Skip to content

kzheng1929/erl-glfw

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Erlang ↔ GLFW NIF Binding

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.

Environment

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

Project Structure

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

Quick Start

1. Build the NIF DLL

PowerShell (recommended):

.\build.ps1

Command Prompt:

build.bat

Output: priv/glfw_nif.dll and priv/glfw3.dll

2. Start Erlang & Run Tests

> cd("D:/Programming/Erlang/nif_glfw").
> c(glfw).
> c(glfw_test).
> glfw_test:run().

3. Minimal Application

> 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).

NIF Functions

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

Callback Registration

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.

NIF Callback Architecture

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.

What This Demonstrates

1. Complete NIF Project Structure

  • on_load callback to dynamically load the .dll
  • NIF stubs with fallback if the library isn't loaded
  • ERL_NIF_INIT macro for DLL entry point registration
  • Resource type management for C object lifetime

2. Data Type Passing

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

3. Third-Party C Library Linking

  • Linking glfw3.lib alongside the NIF DLL
  • Runtime redistribution via glfw3.dll in priv/
  • GLFW callback system bridged to Erlang message passing

4. Thread-Safe Callbacks

  • enif_mutex_create / enif_mutex_lock for shared state protection
  • enif_alloc_env / enif_send for message delivery from callbacks
  • GLFW user pointer (glfwSetWindowUserPointer) for window identity

MSVC Compilation Details

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.

Troubleshooting

"NIF library not found"

Loading NIF from: D:...\priv\glfw_nif

Run .\build.ps1 first, then verify priv/glfw_nif.dll and priv/glfw3.dll exist.

"invalid library: glfw_nif.dll"

The DLL was compiled with incompatible flags. Ensure:

  • You used x64 (vcvarsall.bat x64) — must match BEAM (64-bit)
  • You used /MD (not /MT or /MTd)
  • OTP versions match the NIF API version in your header

"GLFW3.DLL not found" at runtime

The GLFW runtime DLL (glfw3.dll) must be in the same directory as glfw_nif.dll (the priv/ directory), or on the system PATH.

"NIF version mismatch"

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.

References

About

Erlang glfw绑定

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors