Provides an Erlang interface to GLFW 3.x for window creation, OpenGL context management, and input event handling, using the NIF (Native Implemented Function) API via MSVC on Windows.
| Component | Path |
|---|---|
| Erlang/OTP | D:\Erlang_OTP (OTP 28.1, 64-bit) |
| NIF headers | D:\Erlang_OTP\usr\include\erl_nif.h |
| MSVC | VS2022 (D:\VSBundle\VS2022, also in Program Files) |
| GLFW 3.4 | D:\Libs\glfw-3.4.bin.WIN64 |
nif_glfw/
├── c_src/
│ ├── glfw_nif.c # C NIF implementations for GLFW
│ └── nif_utils.h # Helper macros (argument extraction, atoms, etc.)
├── src/
│ ├── glfw.erl # High-level Erlang API
│ ├── glfw_nif.erl # NIF module (on_load + stubs)
│ └── glfw_test.erl # Test suite
├── priv/ # Built DLLs land here
├── build.bat # Batch build script (cmd.exe)
├── build.ps1 # PowerShell build script (recommended)
└── README.md
PowerShell (recommended):
.\build.ps1Command Prompt:
build.batOutput: priv/glfw_nif.dll and priv/glfw3.dll
> cd("D:/Programming/Erlang/nif_glfw").
> c(glfw).
> c(glfw_test).
> glfw_test:run().> cd("D:/Programming/Erlang/nif_glfw").
> c(glfw).
> glfw:run(800, 600, "Hello from Erlang!", fun(Win, Time) ->
%% Your OpenGL rendering code here
io:format("frame at ~.2fs~n", [Time])
end).| Function | Args | Description |
|---|---|---|
init/0 |
— | Initialize GLFW |
terminate/0 |
— | Terminate GLFW |
create_window/3 |
Width, Height, Title | Create a GLFW window → {ok, Ref} |
destroy_window/1 |
WinRef | Destroy window |
window_should_close/1 |
WinRef | Check if close requested → true/false |
set_window_should_close/2 |
WinRef, Value | Set close flag |
make_context_current/1 |
WinRef | Make OpenGL context current |
swap_buffers/1 |
WinRef | Swap front/back buffers |
swap_interval/1 |
Interval | Set VSync interval (0=off, 1=on) |
get_framebuffer_size/1 |
WinRef | Get framebuffer size → {W, H} |
poll_events/0 |
— | Poll for pending events |
wait_events/0 |
— | Wait for events (blocks) |
wait_events_timeout/1 |
Timeout | Wait with timeout |
get_key/2 |
WinRef, Key | Get key state → release/press/repeat |
get_mouse_button/2 |
WinRef, Button | Get mouse button state → release/press |
get_cursor_pos/1 |
WinRef | Get cursor position → {X, Y} |
set_cursor_pos/3 |
WinRef, X, Y | Set cursor position |
get_time/0 |
— | Get GLFW timer (seconds) |
set_time/1 |
Time | Set GLFW timer |
get_version/0 |
— | Get GLFW version → {Maj, Min, Rev} |
get_platform/0 |
— | Get platform atom (win32, etc.) |
window_hint/2 |
Hint, Value | Set window creation hint |
| Function | Args | Sends |
|---|---|---|
set_error_callback/1 |
Pid | {glfw_event, error, Code, Desc} |
set_key_callback/2 |
WinRef, Pid | {glfw_event, key, WinIdx, Key, Scan, Action, Mods} |
set_window_close_callback/2 |
WinRef, Pid | {glfw_event, close, WinIdx} |
set_cursor_pos_callback/2 |
WinRef, Pid | {glfw_event, cursor_pos, WinIdx, X, Y} |
set_mouse_button_callback/2 |
WinRef, Pid | {glfw_event, mouse_button, WinIdx, Btn, Action, Mods} |
set_scroll_callback/2 |
WinRef, Pid | {glfw_event, scroll, WinIdx, XOff, YOff} |
set_char_callback/2 |
WinRef, Pid | {glfw_event, char, WinIdx, Codepoint} |
Pass undefined for the PID to unregister a callback.
Erlang Process C DLL (.dll)
┌──────────────────┐ ┌──────────────────────┐
│ │ │ │
│ glfw:poll_events()│─────────▶│ glfwPollEvents() │
│ │ │ │ │
│ receive │ │ ├─▶ key_callback() │──enif_send──▶ PID
│ {glfw_event, │ │ ├─▶ cursor_cb() │──enif_send──▶ PID
│ key, ...} │ │ └─▶ close_cb() │──enif_send──▶ PID
│ end │ │ │
└──────────────────┘ └──────────────────────┘
GLFW callbacks fire during glfw:poll_events() or glfw:wait_events(). Each
callback sends an Erlang message to the registered PID using enif_send(),
which is thread-safe and can deliver messages from C callbacks on any thread.
on_loadcallback to dynamically load the.dll- NIF stubs with fallback if the library isn't loaded
ERL_NIF_INITmacro for DLL entry point registration- Resource type management for C object lifetime
| Erlang → C | C → Erlang |
|---|---|
| Integer | enif_make_int / enif_make_uint |
| Double | enif_make_double |
| String | enif_make_string (Latin-1) |
| Atom | enif_make_existing_atom |
| Resource | enif_make_resource / enif_get_resource |
| Tuple | enif_make_tuple2 ... enif_make_tuple7 |
| PID | enif_get_local_pid / enif_send |
- Linking
glfw3.libalongside the NIF DLL - Runtime redistribution via
glfw3.dllinpriv/ - GLFW callback system bridged to Erlang message passing
enif_mutex_create/enif_mutex_lockfor shared state protectionenif_alloc_env/enif_sendfor message delivery from callbacks- GLFW user pointer (
glfwSetWindowUserPointer) for window identity
cl /LD /MD /O2 /I"D:\Erlang_OTP\usr\include" /I"D:\Libs\glfw-3.4.bin.WIN64\include" ^
/Fe:"priv\glfw_nif.dll" c_src\glfw_nif.c ^
/link /NODEFAULTLIB:libcmt "D:\Libs\glfw-3.4.bin.WIN64\lib-vc2022\glfw3.lib" ^
opengl32.lib gdi32.lib| Flag | Purpose |
|---|---|
/LD |
Build a DLL |
/MD |
Use multithreaded DLL runtime (must match BEAM) |
/O2 |
Optimize for speed |
/I |
Add include directories (Erlang + GLFW) |
/NODEFAULTLIB:libcmt |
Avoid static CRT linkage conflict with /MD |
glfw3.lib |
GLFW import library |
opengl32.lib |
Windows OpenGL (required by GLFW) |
gdi32.lib |
Windows GDI (required by GLFW) |
No import library is needed for the NIF itself — ERL_NIF_INIT macro
handles the __declspec(dllexport) automatically.
Loading NIF from: D:...\priv\glfw_nifRun .\build.ps1 first, then verify priv/glfw_nif.dll and priv/glfw3.dll exist.
The DLL was compiled with incompatible flags. Ensure:
- You used x64 (
vcvarsall.bat x64) — must match BEAM (64-bit) - You used
/MD(not/MTor/MTd) - OTP versions match the NIF API version in your header
The GLFW runtime DLL (glfw3.dll) must be in the same directory as
glfw_nif.dll (the priv/ directory), or on the system PATH.
The erl_nif.h header version must match the running OTP. If you have multiple OTP installations, ensure the include path points to the correct one.