From ffdb1f4ee07e3f5a5cf6a8bb2dcc6d39f856bf0a Mon Sep 17 00:00:00 2001 From: Jeremie Pelletier Date: Tue, 23 Sep 2025 11:39:05 -0400 Subject: [PATCH 01/12] [wip] gamepad macos still need to finish other platforms --- src/windy/common.nim | 32 +++++ src/windy/internal.nim | 20 +++ src/windy/platforms/macos/macdefs.nim | 67 +++++++++- src/windy/platforms/macos/objc.nim | 2 + src/windy/platforms/macos/platform.nim | 161 ++++++++++++++++++++++++- 5 files changed, 277 insertions(+), 5 deletions(-) diff --git a/src/windy/common.nim b/src/windy/common.nim index 5bc4cef..d5d1ed1 100644 --- a/src/windy/common.nim +++ b/src/windy/common.nim @@ -69,6 +69,7 @@ type DecoratedResizable, Decorated, Undecorated, Transparent Callback* = proc() + GamepadCallback* = proc(gamepadId: int) {.raises: [].} ButtonCallback* = proc(button: Button) RuneCallback* = proc(rune: Rune) HttpErrorCallback* = proc(msg: string) @@ -194,7 +195,38 @@ type ButtonView* = distinct set[Button] + # A button is an input whose value is a boolean with an optional pressure value between 0 and 1 + # For buttons without pressure, the pressure value is 1 if the button is pressed, 0 otherwise + GamepadButton* = enum + GamepadDown + GamepadRight + GamepadLeft + GamepadUp + GamepadA + GamepadB + GamepadX + GamepadY + GamepadL1 + GamepadR1 + GamepadL2 + GamepadR2 + GamepadL3 + GamepadR3 + GamepadStart + GamepadSelect + GamepadHome + GamepadButtonCount + + # An axis is an input whose value is a float between -1 and 1 + GamepadAxis* = enum + GamepadLStickX + GamepadLStickY + GamepadRStickX + GamepadRStickY + GamepadAxisCount + const + maxGamepads* = 4 # GCController, XInput and other native APIs come with a limit of 4 gamepads defaultHttpDeadline*: float32 = -1 proc `==`*(a, b: HttpRequestHandle): bool = diff --git a/src/windy/internal.nim b/src/windy/internal.nim index 65bd33d..b16a81b 100644 --- a/src/windy/internal.nim +++ b/src/windy/internal.nim @@ -5,6 +5,15 @@ const CRLF* = "\r\n" type + GamepadState* = object + numButtons*: int8 + numAxes*: int8 + buttons*: uint32 # One bit per button, 32 buttons max + pressed*: uint32 # Buttons pressed this frame + released*: uint32 # Buttons released this frame + pressures*: array[GamepadButtonCount.int, float32] + axes*: array[GamepadAxisCount.int, float32] + WindowState* = object title*: string icon*: Image @@ -162,3 +171,14 @@ proc addDefaultHeaders*(headers: var seq[HttpHeader]) = if headers["accept-encoding"].len == 0: # If there isn't a specific accept-encoding specified, enable gzip headers["accept-encoding"] = "gzip" + +proc resetGamepadState*(state: var GamepadState) = + state.numButtons = 0.int8 + state.numAxes = 0.int8 + state.buttons = 0.uint32 + state.pressed = 0.uint32 + state.released = 0.uint32 + for i in 0.. Date: Tue, 23 Sep 2025 11:47:29 -0400 Subject: [PATCH 02/12] Add example --- examples/gamepad.nim | 35 +++++++++++++++++++++++++++++++++++ src/windy/internal.nim | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 examples/gamepad.nim diff --git a/examples/gamepad.nim b/examples/gamepad.nim new file mode 100644 index 0000000..5830c08 --- /dev/null +++ b/examples/gamepad.nim @@ -0,0 +1,35 @@ +import opengl, windy + +let window = newWindow("Windy Basic", ivec2(1280, 800)) +var color = vec4(0, 0, 0, 1) + +window.makeContextCurrent() +loadExtensions() + +proc gamepad() = + onGamepadConnected = proc(gamepadId: int) = + echo "Gamepad ", gamepadId, " connected: ", gamepadName(gamepadId) + onGamepadDisconnected = proc(gamepadId: int) = + echo "Gamepad ", gamepadId, " disconnected" + + for i in 0.. 0 and gamepadButtonPressure(i, btn) < 1: + echo "Gamepad ", i, " button ", btn, " pressure ", gamepadButtonPressure(i, btn) + for axis in 0.GamepadAxis.. Date: Tue, 23 Sep 2025 17:01:58 -0400 Subject: [PATCH 03/12] update --- src/windy/common.nim | 4 ++++ src/windy/internal.nim | 16 ++++++++++++++++ src/windy/platforms/macos/platform.nim | 18 +----------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/windy/common.nim b/src/windy/common.nim index d5d1ed1..f58d2b0 100644 --- a/src/windy/common.nim +++ b/src/windy/common.nim @@ -229,6 +229,10 @@ const maxGamepads* = 4 # GCController, XInput and other native APIs come with a limit of 4 gamepads defaultHttpDeadline*: float32 = -1 +var + onGamepadConnected*: GamepadCallback + onGamepadDisconnected*: GamepadCallback + proc `==`*(a, b: HttpRequestHandle): bool = a.int == b.int diff --git a/src/windy/internal.nim b/src/windy/internal.nim index 147886c..bc52bc0 100644 --- a/src/windy/internal.nim +++ b/src/windy/internal.nim @@ -172,6 +172,22 @@ proc addDefaultHeaders*(headers: var seq[HttpHeader]) = # If there isn't a specific accept-encoding specified, enable gzip headers["accept-encoding"] = "gzip" +template handleGamepadTemplate*() = + proc gamepadButton*(gamepadId: int, button: GamepadButton): bool = + (gamepadStates[gamepadId].buttons and (1.uint32 shl button.int8)) != 0 + + proc gamepadButtonPressed*(gamepadId: int, button: GamepadButton): bool = + (gamepadStates[gamepadId].pressed and (1.uint32 shl button.int8)) != 0 + + proc gamepadButtonReleased*(gamepadId: int, button: GamepadButton): bool = + (gamepadStates[gamepadId].released and (1.uint32 shl button.int8)) != 0 + + proc gamepadButtonPressure*(gamepadId: int, button: GamepadButton): float = + gamepadStates[gamepadId].pressures[button.int8] + + proc gamepadAxis*(gamepadId: int, axis: GamepadAxis): float = + gamepadStates[gamepadId].axes[axis.int8] + proc resetGamepadState*(state: var GamepadState) = state.numButtons = 0.int8 state.numAxes = 0.int8 diff --git a/src/windy/platforms/macos/platform.nim b/src/windy/platforms/macos/platform.nim index 5bed26e..01e04bf 100644 --- a/src/windy/platforms/macos/platform.nim +++ b/src/windy/platforms/macos/platform.nim @@ -49,9 +49,6 @@ var gamepadButtonLookup: array[maxGamepads, array[GamepadButtonCount.int, int8]] gamepadButtonInputs: array[maxGamepads, array[GamepadButtonCount.int, GCControllerButtonInput]] - onGamepadConnected*: GamepadCallback - onGamepadDisconnected*: GamepadCallback - proc indexForNSWindow(windows: seq[Window], inner: NSWindow): int = ## Returns the window for this handle, else -1 for i, window in windows: @@ -1128,20 +1125,7 @@ proc gamepadConnected*(gamepadId: int): bool = proc gamepadName*(gamepadId: int): string = $gamepadProfiles[gamepadId].device().vendorName() -proc gamepadButton*(gamepadId: int, button: GamepadButton): bool = - (gamepadStates[gamepadId].buttons and (1.uint32 shl button.int8)) != 0 - -proc gamepadButtonPressed*(gamepadId: int, button: GamepadButton): bool = - (gamepadStates[gamepadId].pressed and (1.uint32 shl button.int8)) != 0 - -proc gamepadButtonReleased*(gamepadId: int, button: GamepadButton): bool = - (gamepadStates[gamepadId].released and (1.uint32 shl button.int8)) != 0 - -proc gamepadButtonPressure*(gamepadId: int, button: GamepadButton): float = - gamepadStates[gamepadId].pressures[button.int8] - -proc gamepadAxis*(gamepadId: int, axis: GamepadAxis): float = - gamepadStates[gamepadId].axes[axis.int8] +handleGamepadTemplate() proc getClipboardContentKinds*(): set[ClipboardContentKind] = init() From b50ad3575300f1aedc624685ff5e8091eea497b6 Mon Sep 17 00:00:00 2001 From: Jeremie Pelletier Date: Tue, 23 Sep 2025 17:12:36 -0400 Subject: [PATCH 04/12] update --- examples/gamepad.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/gamepad.nim b/examples/gamepad.nim index 5830c08..73c7d39 100644 --- a/examples/gamepad.nim +++ b/examples/gamepad.nim @@ -6,12 +6,12 @@ var color = vec4(0, 0, 0, 1) window.makeContextCurrent() loadExtensions() -proc gamepad() = - onGamepadConnected = proc(gamepadId: int) = - echo "Gamepad ", gamepadId, " connected: ", gamepadName(gamepadId) - onGamepadDisconnected = proc(gamepadId: int) = - echo "Gamepad ", gamepadId, " disconnected" +onGamepadConnected = proc(gamepadId: int) = + echo "Gamepad ", gamepadId, " connected: ", gamepadName(gamepadId) +onGamepadDisconnected = proc(gamepadId: int) = + echo "Gamepad ", gamepadId, " disconnected" +proc gamepad() = for i in 0.. Date: Wed, 24 Sep 2025 18:35:40 -0400 Subject: [PATCH 05/12] Emscripten gamepad + window.run proc + html5 shell --- config.nims | 51 ++++++++++ examples/basic.nim | 4 +- examples/basic_textured_quad.nim | 4 +- examples/basic_triangle.nim | 4 +- examples/callbacks.nim | 4 +- examples/fixedsize.nim | 5 +- examples/fullscreen.nim | 5 +- examples/gamepad.nim | 6 +- examples/highdpi.nim | 3 +- examples/icon.nim | 3 +- examples/opengl_version.nim | 4 +- examples/property_changes.nim | 4 +- examples/tray.nim | 7 +- examples/websocket.nim | 4 +- src/windy.nim | 6 ++ src/windy/common.nim | 14 +-- src/windy/internal.nim | 4 +- src/windy/platforms/emscripten/emdefs.nim | 21 ++++ src/windy/platforms/emscripten/platform.nim | 69 +++++++++++++ src/windy/shell.html | 101 ++++++++++++++++++++ 20 files changed, 284 insertions(+), 39 deletions(-) create mode 100644 config.nims create mode 100644 src/windy/shell.html diff --git a/config.nims b/config.nims new file mode 100644 index 0000000..d429c74 --- /dev/null +++ b/config.nims @@ -0,0 +1,51 @@ +import strutils, os + +if defined(emscripten): + + # This path will only run if -d:emscripten is passed to nim. + + --nimcache:tmp # Store intermediate files close by in the ./tmp dir. + + --os:linux # Emscripten pretends to be linux. + --cpu:wasm32 # Emscripten is 32bits. + --cc:clang # Emscripten is very close to clang, so we will replace it. + when defined(windows): + --clang.exe:emcc.bat # Replace C + --clang.linkerexe:emcc.bat # Replace C linker + --clang.cpp.exe:emcc.bat # Replace C++ + --clang.cpp.linkerexe:emcc.bat # Replace C++ linker. + else: + --clang.exe:emcc # Replace C + --clang.linkerexe:emcc # Replace C linker + --clang.cpp.exe:emcc # Replace C++ + --clang.cpp.linkerexe:emcc # Replace C++ linker. + --listCmd # List what commands we are running so that we can debug them. + + --gc:arc # GC:arc is friendlier with crazy platforms. + --exceptions:goto # Goto exceptions are friendlier with crazy platforms. + --define:noSignalHandler # Emscripten doesn't support signal handlers. + + # Create the dist directory if it doesn't exist. + if not dirExists("dist"): + mkDir("dist") + + # Pass this to Emscripten linker to generate html file scaffold for us. + switch( + "passL", + """ + -o dist/windy.html + --shell-file src/windy/shell.html + -s USE_WEBGL2=1 + -s MAX_WEBGL_VERSION=2 + -s MIN_WEBGL_VERSION=1 + -s FULL_ES3=1 + -s GL_ENABLE_GET_PROC_ADDRESS=1 + -s ALLOW_MEMORY_GROWTH + --profiling + """.replace("\n", " ") + ) + +--gc:arc # GC:arc is friendlier with crazy platforms. +--exceptions:goto # Goto exceptions are friendlier with crazy platforms. +--define:noSignalHandler # Emscripten doesn't support signal handlers. +--define:noAutoGLerrorCheck diff --git a/examples/basic.nim b/examples/basic.nim index a0d3558..5e1a696 100644 --- a/examples/basic.nim +++ b/examples/basic.nim @@ -10,6 +10,6 @@ proc display() = # Your OpenGL display code here window.swapBuffers() -while not window.closeRequested: +window.run(proc() = display() - pollEvents() + pollEvents()) diff --git a/examples/basic_textured_quad.nim b/examples/basic_textured_quad.nim index e885876..eafa82f 100644 --- a/examples/basic_textured_quad.nim +++ b/examples/basic_textured_quad.nim @@ -175,6 +175,6 @@ proc display() = glDrawArrays(GL_TRIANGLES, 0, 3*2) swapBuffers(window) -while not window.closeRequested: +window.run(proc() = pollEvents() - display() + display()) diff --git a/examples/basic_triangle.nim b/examples/basic_triangle.nim index 7ab4637..49387d2 100644 --- a/examples/basic_triangle.nim +++ b/examples/basic_triangle.nim @@ -118,6 +118,6 @@ proc display() = # Your OpenGL display code here window.swapBuffers() -while not window.closeRequested: +window.run(proc() = display() - pollEvents() + pollEvents()) diff --git a/examples/callbacks.nim b/examples/callbacks.nim index 77c4300..0467ccb 100644 --- a/examples/callbacks.nim +++ b/examples/callbacks.nim @@ -52,7 +52,7 @@ window.onButtonRelease = proc(button: Button) = window.onRune = proc(rune: Rune) = echo "onRune ", rune -while not window.closeRequested: +window.run(proc() = if window.minimized or not window.visible: sleep(10) - pollEvents() + pollEvents()) diff --git a/examples/fixedsize.nim b/examples/fixedsize.nim index 7369443..1bba51b 100644 --- a/examples/fixedsize.nim +++ b/examples/fixedsize.nim @@ -1,6 +1,6 @@ import opengl, windy -let window = newWindow("Windy Basic", ivec2(500, 500)) +let window = newWindow("Windy Fixed Size", ivec2(500, 500)) window.style = Decorated window.makeContextCurrent() @@ -11,5 +11,4 @@ window.onFrame = proc() = # Your OpenGL display code here window.swapBuffers() -while not window.closeRequested: - pollEvents() +window.run(pollEvents) diff --git a/examples/fullscreen.nim b/examples/fullscreen.nim index 06f19d2..7954de2 100644 --- a/examples/fullscreen.nim +++ b/examples/fullscreen.nim @@ -1,6 +1,6 @@ import boxy, opengl, windy -let window = newWindow("Toggle Fullscreen", ivec2(1280, 800)) +let window = newWindow("Windy Toggle Fullscreen", ivec2(1280, 800)) window.makeContextCurrent() loadExtensions() @@ -21,5 +21,4 @@ window.onButtonPress = proc(button: Button) = if button == MouseLeft: window.fullscreen = window.buttonToggle[MouseLeft] -while not window.closeRequested: - pollEvents() +window.run(pollEvents) diff --git a/examples/gamepad.nim b/examples/gamepad.nim index 73c7d39..f16c9e0 100644 --- a/examples/gamepad.nim +++ b/examples/gamepad.nim @@ -1,6 +1,6 @@ import opengl, windy -let window = newWindow("Windy Basic", ivec2(1280, 800)) +let window = newWindow("Windy Gamepad", ivec2(1280, 800)) var color = vec4(0, 0, 0, 1) window.makeContextCurrent() @@ -29,7 +29,7 @@ proc display() = glClear(GL_COLOR_BUFFER_BIT) window.swapBuffers() -while not window.closeRequested: +window.run(proc() = gamepad() display() - pollEvents() + pollEvents()) diff --git a/examples/highdpi.nim b/examples/highdpi.nim index a2d26ee..e870d0e 100644 --- a/examples/highdpi.nim +++ b/examples/highdpi.nim @@ -22,5 +22,4 @@ window.onFrame = proc() = # Your OpenGL display code here window.swapBuffers() -while not window.closeRequested: - pollEvents() +window.run(pollEvents) diff --git a/examples/icon.nim b/examples/icon.nim index f0d917a..c881ade 100644 --- a/examples/icon.nim +++ b/examples/icon.nim @@ -14,5 +14,4 @@ when defined(windows) or defined(linux): window.icon = icon - while not window.closeRequested: - pollEvents() + window.run(pollEvents) diff --git a/examples/opengl_version.nim b/examples/opengl_version.nim index 9592c3e..e844950 100644 --- a/examples/opengl_version.nim +++ b/examples/opengl_version.nim @@ -15,6 +15,6 @@ echo "GL_VERSION: ", cast[cstring](glGetString(GL_VERSION)) echo "GL_VENDOR: ", cast[cstring](glGetString(GL_VENDOR)) echo "GL_RENDERER: ", cast[cstring](glGetString(GL_RENDERER)) -while not window.closeRequested: +window.run(proc() = pollEvents() - sleep(10) + sleep(10)) diff --git a/examples/property_changes.nim b/examples/property_changes.nim index 56e9969..db3b43f 100644 --- a/examples/property_changes.nim +++ b/examples/property_changes.nim @@ -10,7 +10,7 @@ window.onFrame = proc() = # Your OpenGL display code here window.swapBuffers() -while not window.closeRequested: +window.run(proc() = sleep(100) pollEvents() @@ -74,4 +74,4 @@ while not window.closeRequested: doAssert not window.fullscreen echo "SUCCESS!" - quit() + quit()) diff --git a/examples/tray.nim b/examples/tray.nim index 2700161..c143108 100644 --- a/examples/tray.nim +++ b/examples/tray.nim @@ -38,7 +38,6 @@ when defined(windows): showTrayIcon(icon, "Demo", onTrayIconClick, menu) - while not window.closeRequested: - pollEvents() - - hideTrayIcon() + window.run( + pollEvents, + hideTrayIcon) diff --git a/examples/websocket.nim b/examples/websocket.nim index 798cc30..c3fcded 100644 --- a/examples/websocket.nim +++ b/examples/websocket.nim @@ -25,6 +25,4 @@ ws.onClose = proc() = echo "onClose" # Closing the window exits the demo -let window = newWindow("Windy Basic", ivec2(1280, 800)) -while not window.closeRequested: - pollEvents() +newWindow("Windy WebSocket", ivec2(1280, 800)).run(pollEvents) diff --git a/src/windy.nim b/src/windy.nim index 1af0991..4598cc7 100644 --- a/src/windy.nim +++ b/src/windy.nim @@ -10,3 +10,9 @@ elif defined(linux): import windy/platforms/linux/platform export common, platform, unicode, vmath + +when not defined(emscripten): + proc run*(window: Window, mainLoop: proc(), onExit: proc() = empty) = + while not window.closeRequested: + mainLoop() + onExit() \ No newline at end of file diff --git a/src/windy/common.nim b/src/windy/common.nim index f58d2b0..3f7a2db 100644 --- a/src/windy/common.nim +++ b/src/windy/common.nim @@ -197,11 +197,8 @@ type # A button is an input whose value is a boolean with an optional pressure value between 0 and 1 # For buttons without pressure, the pressure value is 1 if the button is pressed, 0 otherwise + # NOTE These are the same as the HTML5 standard mapping, making the definition order important GamepadButton* = enum - GamepadDown - GamepadRight - GamepadLeft - GamepadUp GamepadA GamepadB GamepadX @@ -210,11 +207,16 @@ type GamepadR1 GamepadL2 GamepadR2 + GamepadSelect + GamepadStart GamepadL3 GamepadR3 - GamepadStart - GamepadSelect + GamepadUp + GamepadDown + GamepadLeft + GamepadRight GamepadHome + GamepadTouchpad GamepadButtonCount # An axis is an input whose value is a float between -1 and 1 diff --git a/src/windy/internal.nim b/src/windy/internal.nim index bc52bc0..3b41639 100644 --- a/src/windy/internal.nim +++ b/src/windy/internal.nim @@ -1,6 +1,7 @@ import common, pixie, std/random const + gamepadDeadzone = 0.1 multiClickRadius = 4 CRLF* = "\r\n" @@ -186,7 +187,8 @@ template handleGamepadTemplate*() = gamepadStates[gamepadId].pressures[button.int8] proc gamepadAxis*(gamepadId: int, axis: GamepadAxis): float = - gamepadStates[gamepadId].axes[axis.int8] + let value = gamepadStates[gamepadId].axes[axis.int8] + if abs(value) < gamepadDeadzone: 0 else: value proc resetGamepadState*(state: var GamepadState) = state.numButtons = 0.int8 diff --git a/src/windy/platforms/emscripten/emdefs.nim b/src/windy/platforms/emscripten/emdefs.nim index 4763811..c17b151 100644 --- a/src/windy/platforms/emscripten/emdefs.nim +++ b/src/windy/platforms/emscripten/emdefs.nim @@ -128,8 +128,22 @@ proc emscripten_webgl_init_context_attributes*(attrs: ptr EmscriptenWebGLContext proc emscripten_webgl_create_context*(target: cstring, attrs: ptr EmscriptenWebGLContextAttributes): EMSCRIPTEN_WEBGL_CONTEXT_HANDLE {.importc, header: "".} proc emscripten_webgl_make_context_current*(context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE): EMSCRIPTEN_RESULT {.importc, header: "".} +const EM_HTML5_MEDIUM_STRING_LEN_BYTES* = 64 + # Mouse event handling type + EmscriptenGamepadEvent* {.importc: "EmscriptenGamepadEvent", header: "".} = object + timestamp*: cdouble + numAxes*: cint + numButtons*: cint + axis*: array[64, cdouble] + analogButton*: array[64, cdouble] + digitalButton*: array[64, bool] + connected*: bool + index*: cint + id*: array[EM_HTML5_MEDIUM_STRING_LEN_BYTES, char] + mapping*: array[EM_HTML5_MEDIUM_STRING_LEN_BYTES, char] + EmscriptenMouseEvent* {.importc: "EmscriptenMouseEvent", header: "".} = object timestamp*: cdouble screenX*, screenY*: clong @@ -181,7 +195,11 @@ type EmscriptenKeyboardEventCallback* = proc(eventType: cint, keyEvent: ptr EmscriptenKeyboardEvent, userData: pointer): EM_BOOL {.cdecl.} EmscriptenFocusEventCallback* = proc(eventType: cint, focusEvent: ptr EmscriptenFocusEvent, userData: pointer): EM_BOOL {.cdecl.} EmscriptenUiEventCallback* = proc(eventType: cint, uiEvent: ptr EmscriptenUiEvent, userData: pointer): EM_BOOL {.cdecl.} + EmscriptenGamepadEventCallback* = proc(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEvent, userData: pointer): EM_BOOL {.cdecl.} + EmscriptenGamepadDisconnectedEventCallback* = proc(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEvent, userData: pointer): EM_BOOL {.cdecl.} +proc emscripten_set_gamepadconnected_callback_on_thread*(userData: pointer, useCapture: EM_BOOL, callback: EmscriptenGamepadEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "".} +proc emscripten_set_gamepaddisconnected_callback_on_thread*(userData: pointer, useCapture: EM_BOOL, callback: EmscriptenGamepadDisconnectedEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "".} proc emscripten_set_mousedown_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "".} proc emscripten_set_mouseup_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "".} proc emscripten_set_mousemove_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "".} @@ -193,6 +211,9 @@ proc emscripten_set_blur_callback_on_thread*(target: cstring, userData: pointer, proc emscripten_set_focus_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenFocusEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "".} proc emscripten_set_resize_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenUiEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "".} +proc emscripten_sample_gamepad_data*(): EMSCRIPTEN_RESULT {.importc, header: "".} +proc emscripten_get_gamepad_status*(index: cint, status: ptr EmscriptenGamepadEvent): EMSCRIPTEN_RESULT {.importc, header: "".} + const EMSCRIPTEN_EVENT_KEYPRESS* = 1 EMSCRIPTEN_EVENT_KEYDOWN* = 2 diff --git a/src/windy/platforms/emscripten/platform.nim b/src/windy/platforms/emscripten/platform.nim index 017196c..d9e8868 100644 --- a/src/windy/platforms/emscripten/platform.nim +++ b/src/windy/platforms/emscripten/platform.nim @@ -54,10 +54,15 @@ var mainWindow: Window # Track the main window for events httpRequests: Table[HttpRequestHandle, EmsHttpRequestState] + gamepadsConnectedMask: uint8 + gamepadNames: array[maxGamepads, string] + gamepadStates: array[maxGamepads, GamepadState] + proc handleButtonPress(window: Window, button: Button) proc handleButtonRelease(window: Window, button: Button) proc handleRune(window: Window, rune: Rune) proc setupEventHandlers(window: Window) # Forward declaration +proc setupGamepads() proc init = if initialized: @@ -242,6 +247,7 @@ proc newWindow*( # Setup event handlers setupEventHandlers(result) + setupGamepads() result.title = title if pos != ivec2(0, 0): @@ -279,6 +285,14 @@ proc buttonReleased*(window: Window): ButtonView = proc buttonToggle*(window: Window): ButtonView = window.state.buttonToggle.ButtonView +proc gamepadConnected*(gamepadId: int): bool = + (gamepadsConnectedMask and (1.uint8 shl gamepadId)) != 0 + +proc gamepadName*(gamepadId: int): string = + gamepadNames[gamepadId] + +handleGamepadTemplate() + # Clipboard functions proc clipboardContent*(): string = "" # Would need JavaScript interop @@ -462,6 +476,57 @@ proc keyCodeToButton(keyCode: culong): Button = of 222: KeyApostrophe else: ButtonUnknown +proc strcmp(a: cstring, b: cstring): cint {.importc, header: "".} + +proc onGamepadConnected(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEvent, userData: pointer): EM_BOOL {.cdecl.} = + # We can only ensure known stable mappings if the gamepad reports the standard mapping + if strcmp(cast[cstring](addr gamepadEvent.mapping), "standard".cstring) == 0: + gamepadsConnectedMask = gamepadsConnectedMask or (1.uint8 shl gamepadEvent.index) + gamepadNames[gamepadEvent.index] = $gamepadEvent.id + if common.onGamepadConnected != nil: + common.onGamepadConnected(gamepadEvent.index) + return 1 + +proc onGamepadDisconnected(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEvent, userData: pointer): EM_BOOL {.cdecl.} = + let wasConnected = (gamepadsConnectedMask and (1.uint8 shl gamepadEvent.index)) != 0 + gamepadsConnectedMask = gamepadsConnectedMask and (not (1.uint8 shl gamepadEvent.index)) + gamepadNames[gamepadEvent.index] = "" + if common.onGamepadDisconnected != nil and wasConnected: # Don't notify disconnects if we ignored the connect + common.onGamepadDisconnected(gamepadEvent.index) + return 1 + +proc setupGamepads() = + discard emscripten_sample_gamepad_data() + + var gp: EmscriptenGamepadEvent + for i in 0.. + + + + + Emscripten-Generated Code + + + + + + + + {{{ SCRIPT }}} + + From 3c3af2953ea9dfff6ebe758a671848ca5e2a78cf Mon Sep 17 00:00:00 2001 From: Jeremie Pelletier Date: Thu, 25 Sep 2025 15:27:35 -0400 Subject: [PATCH 06/12] Win32 gamepad + com bindings + internal updates --- examples/gamepad.nim | 12 +- src/windy/internal.nim | 37 ++-- src/windy/platforms/emscripten/platform.nim | 28 ++- src/windy/platforms/macos/platform.nim | 82 ++++---- src/windy/platforms/win32/platform.nim | 108 +++++++++++ src/windy/platforms/win32/windefs.nim | 205 +++++++++++++++++++- 6 files changed, 395 insertions(+), 77 deletions(-) diff --git a/examples/gamepad.nim b/examples/gamepad.nim index f16c9e0..fb3f3a0 100644 --- a/examples/gamepad.nim +++ b/examples/gamepad.nim @@ -1,16 +1,18 @@ import opengl, windy +# Global event handlers must be registered before the first window is created, +# this enables them to receive events from gamepads that are already connected +onGamepadConnected = proc(gamepadId: int) = + echo "Gamepad ", gamepadId, " connected: ", gamepadName(gamepadId) +onGamepadDisconnected = proc(gamepadId: int) = + echo "Gamepad ", gamepadId, " disconnected" + let window = newWindow("Windy Gamepad", ivec2(1280, 800)) var color = vec4(0, 0, 0, 1) window.makeContextCurrent() loadExtensions() -onGamepadConnected = proc(gamepadId: int) = - echo "Gamepad ", gamepadId, " connected: ", gamepadName(gamepadId) -onGamepadDisconnected = proc(gamepadId: int) = - echo "Gamepad ", gamepadId, " disconnected" - proc gamepad() = for i in 0..".} proc onGamepadConnected(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEvent, userData: pointer): EM_BOOL {.cdecl.} = # We can only ensure known stable mappings if the gamepad reports the standard mapping - if strcmp(cast[cstring](addr gamepadEvent.mapping), "standard".cstring) == 0: + if strcmp(cast[cstring](addr gamepadEvent.mapping), cstring "standard") == 0: gamepadsConnectedMask = gamepadsConnectedMask or (1.uint8 shl gamepadEvent.index) - gamepadNames[gamepadEvent.index] = $gamepadEvent.id + gamepadStates[gamepadEvent.index].name = $gamepadEvent.id if common.onGamepadConnected != nil: common.onGamepadConnected(gamepadEvent.index) return 1 @@ -490,13 +487,13 @@ proc onGamepadConnected(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEven proc onGamepadDisconnected(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEvent, userData: pointer): EM_BOOL {.cdecl.} = let wasConnected = (gamepadsConnectedMask and (1.uint8 shl gamepadEvent.index)) != 0 gamepadsConnectedMask = gamepadsConnectedMask and (not (1.uint8 shl gamepadEvent.index)) - gamepadNames[gamepadEvent.index] = "" + resetGamepadState(gamepadStates[gamepadEvent.index]) if common.onGamepadDisconnected != nil and wasConnected: # Don't notify disconnects if we ignored the connect common.onGamepadDisconnected(gamepadEvent.index) return 1 proc setupGamepads() = - discard emscripten_sample_gamepad_data() + discard emscripten_sample_gamepad_data() # Populate gamepad status data we're about to read var gp: EmscriptenGamepadEvent for i in 0.. 0: + buttons = buttons or (uint32 1 shl dst.int) + + template axis(src: float, dst: GamepadAxis) = + state.axes[dst.int] = gamepadFilterDeadZone(src) + + axis(gamepad.leftThumbstickX, GamepadLStickX) + axis(gamepad.leftThumbstickY, GamepadLStickY) + axis(gamepad.rightThumbstickX, GamepadRStickX) + axis(gamepad.rightThumbstickY, GamepadRStickY) + remap(gamepad.leftTrigger, GamepadL2) + remap(gamepad.rightTrigger, GamepadR2) + button(GameInputGamepadMenu, GamepadStart) + button(GameInputGamepadView, GamepadSelect) + button(GameInputGamepadA, GamepadA) + button(GameInputGamepadB, GamepadB) + button(GameInputGamepadX, GamepadX) + button(GameInputGamepadY, GamepadY) + button(GameInputGamepadDPadUp, GamepadUp) + button(GameInputGamepadDPadDown, GamepadDown) + button(GameInputGamepadDPadLeft, GamepadLeft) + button(GameInputGamepadDPadRight, GamepadRight) + button(GameInputGamepadLeftShoulder, GamepadL1) + button(GameInputGamepadRightShoulder, GamepadR1) + button(GameInputGamepadLeftThumbstick, GamepadL3) + button(GameInputGamepadRightThumbstick, GamepadR3) + + gamepadUpdateButtons() + comDispose(reading) + +proc gameInputDeviceCallback(callbackToken: GameInputCallbackToken, context: pointer, device: ptr IGameInputDevice, timestamp: uint64, currentStatus: GameInputDeviceStatus, previousStatus: GameInputDeviceStatus) {.stdcall.} = + if (currentStatus and GameInputDeviceConnected) == 0: + for i in 0.. Date: Sat, 27 Sep 2025 00:55:27 -0400 Subject: [PATCH 07/12] linux gamepad support through epoll/udev/evdev --- config.nims | 10 +- src/windy/internal.nim | 2 +- src/windy/platforms/emscripten/platform.nim | 10 +- src/windy/platforms/linux/gamepad.nim | 217 +++++++++++++++++++ src/windy/platforms/linux/lindefs.nim | 218 ++++++++++++++++++++ src/windy/platforms/linux/x11.nim | 5 + src/windy/platforms/macos/platform.nim | 2 +- src/windy/platforms/win32/platform.nim | 2 +- 8 files changed, 457 insertions(+), 9 deletions(-) create mode 100644 src/windy/platforms/linux/gamepad.nim create mode 100644 src/windy/platforms/linux/lindefs.nim diff --git a/config.nims b/config.nims index d429c74..bf882d8 100644 --- a/config.nims +++ b/config.nims @@ -1,4 +1,12 @@ -import strutils, os +import strutils + +if defined(linux): + switch( + "passL", + """ + -ludev + -levdev + """.replace("\n", " ")) if defined(emscripten): diff --git a/src/windy/internal.nim b/src/windy/internal.nim index 53770cc..26495d1 100644 --- a/src/windy/internal.nim +++ b/src/windy/internal.nim @@ -200,7 +200,7 @@ template gamepadUpdateButtons*() = state.pressed = buttons and (not prevButtons) state.released = prevButtons and (not buttons) -proc resetGamepadState*(state: var GamepadState) = +proc gamepadResetState*(state: var GamepadState) = state.buttons = 0.uint32 state.pressed = 0.uint32 state.released = 0.uint32 diff --git a/src/windy/platforms/emscripten/platform.nim b/src/windy/platforms/emscripten/platform.nim index e9dc629..35537d9 100644 --- a/src/windy/platforms/emscripten/platform.nim +++ b/src/windy/platforms/emscripten/platform.nim @@ -485,11 +485,11 @@ proc onGamepadConnected(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEven return 1 proc onGamepadDisconnected(eventType: cint, gamepadEvent: ptr EmscriptenGamepadEvent, userData: pointer): EM_BOOL {.cdecl.} = - let wasConnected = (gamepadsConnectedMask and (1.uint8 shl gamepadEvent.index)) != 0 - gamepadsConnectedMask = gamepadsConnectedMask and (not (1.uint8 shl gamepadEvent.index)) - resetGamepadState(gamepadStates[gamepadEvent.index]) - if common.onGamepadDisconnected != nil and wasConnected: # Don't notify disconnects if we ignored the connect - common.onGamepadDisconnected(gamepadEvent.index) + if (gamepadsConnectedMask and (1.uint8 shl gamepadEvent.index)) != 0: + gamepadsConnectedMask = gamepadsConnectedMask and (not (1.uint8 shl gamepadEvent.index)) + gamepadResetState(gamepadStates[gamepadEvent.index]) + if common.onGamepadDisconnected != nil: + common.onGamepadDisconnected(gamepadEvent.index) return 1 proc setupGamepads() = diff --git a/src/windy/platforms/linux/gamepad.nim b/src/windy/platforms/linux/gamepad.nim new file mode 100644 index 0000000..480d67d --- /dev/null +++ b/src/windy/platforms/linux/gamepad.nim @@ -0,0 +1,217 @@ +import std/[os, posix], lindefs, ../../[common, internal] + +# NOTE udev gets us userspace devices for initial discovery and hotplug events. +# +# Beyond that, there are 3 different ways to get device events on linux: +# 1. the joystick subsystem, it's deprecated and we don't use it. +# 2. the evdev subsystem, with standardized names at the kernel level. +# 3. the hidraw subsystem, needing to parse raw vendor frames, but supporting features beyond axes and buttons. +# +# We only use the evdev subsystem as of now. + +var + epoll: cint + udev: ptr udev + udevMonitor: ptr udev_monitor + udevMonitorFd: cint + devices: array[maxGamepads, ptr libevdev] + devicePaths: array[maxGamepads, cstring] + deviceAbsInfo: array[maxGamepads, array[6, ptr input_absinfo]] + gamepadStates: array[maxGamepads, GamepadState] + defaultAbsInfo: input_absinfo = input_absinfo(minimum: -32768, maximum: 32767) + +gamepadPlatform() + +proc gamepadConnected*(gamepadId: int): bool = + devices[gamepadId] != nil + +proc gamepadResetAbsInfo*(gamepadId: int) = + for j in 0..<6: + deviceAbsInfo[gamepadId][j] = addr defaultAbsInfo + +proc strncmp(a: cstring, b: cstring, n: cint): cint {.importc, header: "".} + +proc gamepadEvent(device: ptr udev_device, added: bool) = + # Final filtering and registration of gamepad devices, + # silently treating open errors as unavailable devices. + # The rare possible cases of failure, beyond code errors, are dire conditions like out of memory. + let devnode = udev_device_get_devnode(device) + if devnode != nil and strncmp(devnode, "/dev/input/event", 16) == 0: + let syspath = udev_device_get_syspath(device) + if added: + for i in 0.. info.minimum: + deviceAbsInfo[i][j] = info + + if onGamepadConnected != nil: onGamepadConnected(i) + break + else: + for i in 0.. 0, pos) + case event.code + of ABS_X: state.axes[GamepadLStickX.int] = axis() + of ABS_Y: state.axes[GamepadLStickY.int] = axis() + of ABS_RX: state.axes[GamepadRStickX.int] = axis() + of ABS_RY: state.axes[GamepadRStickY.int] = axis() + of ABS_Z: pressure(GamepadL2) + of ABS_RZ: pressure(GamepadR2) + of ABS_HAT0X: dpad(GamepadLeft, GamepadRight) + of ABS_HAT0Y: dpad(GamepadUp, GamepadDown) + else: discard + else: discard + gamepadUpdateButtons() diff --git a/src/windy/platforms/linux/lindefs.nim b/src/windy/platforms/linux/lindefs.nim new file mode 100644 index 0000000..022b740 --- /dev/null +++ b/src/windy/platforms/linux/lindefs.nim @@ -0,0 +1,218 @@ +{.push importc, cdecl.} + +{.push header: "".} + +type + epoll_data_t* {.union.} = object + `ptr`*: pointer + fd*: cint + u32*: uint32 + u64*: uint64 + epoll_event* = object + events*: uint32 + data*: epoll_data_t + +const + EPOLL_CTL_ADD* = 1 + EPOLL_CTL_DEL* = 2 + EPOLL_CTL_MOD* = 3 + EPOLLIN* = 0x001 + +proc epoll_create1*(flags: cint): cint +proc epoll_ctl*(epfd: cint, op: cint, fd: cint, event: ptr epoll_event): cint +proc epoll_wait*(epfd: cint, events: ptr epoll_event, maxevents: cint, timeout: cint): cint + +{.pop.} + +{.push header: "".} + +type + dev_t* = uint64 + + udev* = object + udev_list_entry* = object + udev_device* = object + udev_monitor* = object + udev_enumerate* = object + +proc udev_ref*(udev: ptr udev): ptr udev +proc udev_unref*(udev: ptr udev): ptr udev +proc udev_new*(): ptr udev + +proc udev_list_entry_get_next*(list_entry: ptr udev_list_entry): ptr udev_list_entry +proc udev_list_entry_get_by_name*(list_entry: ptr udev_list_entry, name: cstring): ptr udev_list_entry +proc udev_list_entry_get_name*(list_entry: ptr udev_list_entry): cstring +proc udev_list_entry_get_value*(list_entry: ptr udev_list_entry): cstring + +template udev_list_entry_foreach*(first_entry: ptr udev_list_entry, body: untyped) = + entry = first_entry + while entry != nil: + body + entry = udev_list_entry_get_next(entry) + +proc udev_device_ref*(udev_device: ptr udev_device): ptr udev_device +proc udev_device_unref*(udev_device: ptr udev_device): ptr udev_device +proc udev_device_get_udev*(udev_device: ptr udev_device): ptr udev +proc udev_device_new_from_syspath*(udev: ptr udev, syspath: cstring): ptr udev_device +proc udev_device_new_from_devnum*(udev: ptr udev, `type`: cchar, devnum: dev_t): ptr udev_device +proc udev_device_new_from_subsystem_sysname*(udev: ptr udev, subsystem: cstring, sysname: cstring): ptr udev_device +proc udev_device_new_from_device_id*(udev: ptr udev, id: cstring): ptr udev_device +proc udev_device_new_from_environment*(udev: ptr udev): ptr udev_device +proc udev_device_get_parent*(udev_device: ptr udev_device): ptr udev_device +proc udev_device_get_parent_with_subsystem_devtype*(udev_device: ptr udev_device, subsystem: cstring, devtype: cstring): ptr udev_device +proc udev_device_get_devpath*(udev_device: ptr udev_device): cstring +proc udev_device_get_subsystem*(udev_device: ptr udev_device): cstring +proc udev_device_get_devtype*(udev_device: ptr udev_device): cstring +proc udev_device_get_syspath*(udev_device: ptr udev_device): cstring +proc udev_device_get_sysname*(udev_device: ptr udev_device): cstring +proc udev_device_get_sysnum*(udev_device: ptr udev_device): cstring +proc udev_device_get_devnode*(udev_device: ptr udev_device): cstring +proc udev_device_get_is_initialized*(udev_device: ptr udev_device): cint +proc udev_device_get_devlinks_list_entry*(udev_device: ptr udev_device): ptr udev_list_entry +proc udev_device_get_properties_list_entry*(udev_device: ptr udev_device): ptr udev_list_entry +proc udev_device_get_tags_list_entry*(udev_device: ptr udev_device): ptr udev_list_entry +proc udev_device_get_current_tags_list_entry*(udev_device: ptr udev_device): ptr udev_list_entry +proc udev_device_get_sysattr_list_entry*(udev_device: ptr udev_device): ptr udev_list_entry +proc udev_device_get_property_value*(udev_device: ptr udev_device, key: cstring): cstring +proc udev_device_get_driver*(udev_device: ptr udev_device): cstring +proc udev_device_get_devnum*(udev_device: ptr udev_device): dev_t +proc udev_device_get_action*(udev_device: ptr udev_device): cstring +proc udev_device_get_seqnum*(udev_device: ptr udev_device): culong +proc udev_device_get_usec_since_initialized*(udev_device: ptr udev_device): culong +proc udev_device_get_sysattr_value*(udev_device: ptr udev_device, sysattr: cstring): cstring +proc udev_device_set_sysattr_value*(udev_device: ptr udev_device, sysattr: cstring, value: cstring): cint +proc udev_device_has_tag*(udev_device: ptr udev_device, tag: cstring): cint +proc udev_device_has_current_tag*(udev_device: ptr udev_device, tag: cstring): cint + +proc udev_monitor_ref*(udev_monitor: ptr udev_monitor): ptr udev_monitor +proc udev_monitor_unref*(udev_monitor: ptr udev_monitor): ptr udev_monitor +proc udev_monitor_get_udev*(udev_monitor: ptr udev_monitor): ptr udev +proc udev_monitor_new_from_netlink*(udev: ptr udev, name: cstring): ptr udev_monitor +proc udev_monitor_enable_receiving*(udev_monitor: ptr udev_monitor): cint +proc udev_monitor_set_receive_buffer_size*(udev_monitor: ptr udev_monitor, size: cint): cint +proc udev_monitor_get_fd*(udev_monitor: ptr udev_monitor): cint +proc udev_monitor_receive_device*(udev_monitor: ptr udev_monitor): ptr udev_device +proc udev_monitor_filter_add_match_subsystem_devtype*(udev_monitor: ptr udev_monitor, subsystem: cstring, devtype: cstring): cint +proc udev_monitor_filter_add_match_tag*(udev_monitor: ptr udev_monitor, tag: cstring): cint +proc udev_monitor_filter_update*(udev_monitor: ptr udev_monitor): cint +proc udev_monitor_filter_remove*(udev_monitor: ptr udev_monitor): cint + +proc udev_enumerate_ref*(udev_enumerate: ptr udev_enumerate): ptr udev_enumerate +proc udev_enumerate_unref*(udev_enumerate: ptr udev_enumerate): ptr udev_enumerate +proc udev_enumerate_get_udev*(udev_enumerate: ptr udev_enumerate): ptr udev +proc udev_enumerate_new*(udev: ptr udev): ptr udev_enumerate +proc udev_enumerate_add_match_subsystem*(udev_enumerate: ptr udev_enumerate, subsystem: cstring): cint +proc udev_enumerate_add_match_sysname*(udev_enumerate: ptr udev_enumerate, sysname: cstring): cint +proc udev_enumerate_add_match_property*(udev_enumerate: ptr udev_enumerate, property: cstring, value: cstring): cint +proc udev_enumerate_add_match_tag*(udev_enumerate: ptr udev_enumerate, tag: cstring): cint +proc udev_enumerate_add_match_parent*(udev_enumerate: ptr udev_enumerate, parent: ptr udev_device): cint +proc udev_enumerate_add_match_is_initialized*(udev_enumerate: ptr udev_enumerate): cint +proc udev_enumerate_add_syspath*(udev_enumerate: ptr udev_enumerate, syspath: cstring): cint +proc udev_enumerate_scan_devices*(udev_enumerate: ptr udev_enumerate): cint +proc udev_enumerate_scan_subsystems*(udev_enumerate: ptr udev_enumerate): cint +proc udev_enumerate_get_list_entry*(udev_enumerate: ptr udev_enumerate): ptr udev_list_entry + +{.pop.} + +# +type + timeval* = object + tv_sec*: clong + tv_usec*: clong + +# +type + input_event* = object + time*: timeval + `type`*: uint16 + code*: uint16 + value*: int32 + input_absinfo* = object + value*: int32 + minimum*: int32 + maximum*: int32 + fuzz*: int32 + flat*: int32 + resolution*: int32 + +# +const + EV_SYN* = 0 + EV_KEY* = 1 + EV_ABS* = 3 + + SYN_DROPPED* = 3 + + BTN_A* = 0x130 + BTN_B* = 0x131 + BTN_C* = 0x132 + BTN_X* = 0x133 + BTN_Y* = 0x134 + BTN_Z* = 0x135 + BTN_TL* = 0x136 + BTN_TR* = 0x137 + BTN_TL2* = 0x138 + BTN_TR2* = 0x139 + BTN_SELECT* = 0x13a + BTN_START* = 0x13b + BTN_MODE* = 0x13c + BTN_THUMBL* = 0x13d + BTN_THUMBR* = 0x13e + + ABS_X* = 0x00 + ABS_Y* = 0x01 + ABS_Z* = 0x02 + ABS_RX* = 0x03 + ABS_RY* = 0x04 + ABS_RZ* = 0x05 + ABS_THROTTLE* = 0x06 + ABS_RUDDER* = 0x07 + ABS_WHEEL* = 0x08 + ABS_GAS* = 0x09 + ABS_BRAKE* = 0x0a + ABS_HAT0X* = 0x10 + ABS_HAT0Y* = 0x11 + ABS_HAT1X* = 0x12 + ABS_HAT1Y* = 0x13 + ABS_HAT2X* = 0x14 + ABS_HAT2Y* = 0x15 + ABS_HAT3X* = 0x16 + ABS_HAT3Y* = 0x17 + ABS_PRESSURE* = 0x18 + ABS_DISTANCE* = 0x19 + ABS_TILT_X* = 0x1a + ABS_TILT_Y* = 0x1b + ABS_TOOL_WIDTH* = 0x1c + + ABS_VOLUME* = 0x20 + ABS_PROFILE* = 0x21 + + ABS_MISC* = 0x28 + +{.push header: "".} + +type + libevdev* = object + +const + LIBEVDEV_READ_FLAG_SYNC* = 1 + LIBEVDEV_READ_FLAG_NORMAL* = 2 + LIBEVDEV_READ_STATUS_SYNC* = -1 + +proc libevdev_new_from_fd*(fd: cint, dev: ptr ptr libevdev): cint +proc libevdev_free*(dev: ptr libevdev) +proc libevdev_get_fd*(dev: ptr libevdev): cint +proc libevdev_get_name*(dev: ptr libevdev): cstring +proc libevdev_get_phys*(dev: ptr libevdev): cstring +proc libevdev_get_uniq*(dev: ptr libevdev): cstring +proc libevdev_get_id_product*(dev: ptr libevdev): cint +proc libevdev_get_id_vendor*(dev: ptr libevdev): cint +proc libevdev_get_id_bustype*(dev: ptr libevdev): cint +proc libevdev_get_id_version*(dev: ptr libevdev): cint +proc libevdev_has_event_code*(dev: ptr libevdev, `type`: uint16, code: uint16): bool +proc libevdev_get_abs_info*(dev: ptr libevdev, code: uint16): ptr input_absinfo +proc libevdev_next_event*(dev: ptr libevdev, flags: cint, event: ptr input_event): cint + +{.pop.} + +{.pop.} diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index 2e0ab35..49a444f 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -7,6 +7,8 @@ import import ../../http export http +include gamepad + type XWindow = x.Window @@ -118,6 +120,8 @@ proc init = else: WmForDecoratedKind.unsupported + gamepadSetup() + initialized = true proc property( @@ -786,6 +790,7 @@ proc newWindow*( windows.add result proc pollEvents(window: Window) = + gamepadPoll() # Clear all per-frame data window.perFrame = PerFrame() diff --git a/src/windy/platforms/macos/platform.nim b/src/windy/platforms/macos/platform.nim index c7cc676..0141b79 100644 --- a/src/windy/platforms/macos/platform.nim +++ b/src/windy/platforms/macos/platform.nim @@ -416,7 +416,7 @@ proc controllerDidDisconnect(self: ID, cmd: SEL, notification: NSNotification): let index = ctrl.playerIndex gamepadProfiles[index] = 0.GCPhysicalInputProfile gamepadTimestamps[index] = 0.float64 - resetGamepadState(gamepadStates[index]) + gamepadResetState(gamepadStates[index]) if onGamepadDisconnected != nil: onGamepadDisconnected(index) diff --git a/src/windy/platforms/win32/platform.nim b/src/windy/platforms/win32/platform.nim index 8982db5..e40fb60 100644 --- a/src/windy/platforms/win32/platform.nim +++ b/src/windy/platforms/win32/platform.nim @@ -1096,7 +1096,7 @@ proc gameInputDeviceCallback(callbackToken: GameInputCallbackToken, context: poi for i in 0.. Date: Mon, 29 Sep 2025 13:38:16 -0400 Subject: [PATCH 08/12] cleanup --- src/windy.nim | 2 +- src/windy/internal.nim | 8 +++++++- src/windy/platforms/emscripten/platform.nim | 3 ++- src/windy/platforms/linux/x11.nim | 2 ++ src/windy/platforms/macos/platform.nim | 2 ++ src/windy/platforms/win32/platform.nim | 2 ++ 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/windy.nim b/src/windy.nim index 4598cc7..2e383aa 100644 --- a/src/windy.nim +++ b/src/windy.nim @@ -15,4 +15,4 @@ when not defined(emscripten): proc run*(window: Window, mainLoop: proc(), onExit: proc() = empty) = while not window.closeRequested: mainLoop() - onExit() \ No newline at end of file + onExit() diff --git a/src/windy/internal.nim b/src/windy/internal.nim index 26495d1..e41efd9 100644 --- a/src/windy/internal.nim +++ b/src/windy/internal.nim @@ -208,4 +208,10 @@ proc gamepadResetState*(state: var GamepadState) = state.pressures[i] = 0.float32 for i in 0.. 0: + mainLoop() + onExit() diff --git a/src/windy/platforms/emscripten/platform.nim b/src/windy/platforms/emscripten/platform.nim index 35537d9..67f6baf 100644 --- a/src/windy/platforms/emscripten/platform.nim +++ b/src/windy/platforms/emscripten/platform.nim @@ -55,9 +55,10 @@ var httpRequests: Table[HttpRequestHandle, EmsHttpRequestState] gamepadsConnectedMask: uint8 - gamepadNames: array[maxGamepads, string] gamepadStates: array[maxGamepads, GamepadState] +runPlatform() # Only one possible window, ends up equivalent to window.run + proc handleButtonPress(window: Window, button: Button) proc handleButtonRelease(window: Window, button: Button) proc handleRune(window: Window, rune: Rune) diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index 49a444f..e8c0e89 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -72,6 +72,8 @@ var clipboardWindow: XWindow clipboardContent: string +runPlatform() + proc initConstants(display: Display) = xaNetWMState = display.XInternAtom("_NET_WM_STATE", 0) xaNetWMStateMaximizedHorz = display.XInternAtom("_NET_WM_STATE_MAXIMIZED_HORZ", 0) diff --git a/src/windy/platforms/macos/platform.nim b/src/windy/platforms/macos/platform.nim index 0141b79..8e8f5a7 100644 --- a/src/windy/platforms/macos/platform.nim +++ b/src/windy/platforms/macos/platform.nim @@ -54,6 +54,8 @@ var gamepadButtonLookup: array[maxGamepads, array[GamepadButtonCount.int, int8]] gamepadButtonInputs: array[maxGamepads, array[GamepadButtonCount.int, GCControllerButtonInput]] +runPlatform() + proc indexForNSWindow(windows: seq[Window], inner: NSWindow): int = ## Returns the window for this handle, else -1 for i, window in windows: diff --git a/src/windy/platforms/win32/platform.nim b/src/windy/platforms/win32/platform.nim index e40fb60..26e3700 100644 --- a/src/windy/platforms/win32/platform.nim +++ b/src/windy/platforms/win32/platform.nim @@ -157,6 +157,8 @@ var gamepadStates: array[maxGamepads, GamepadState] gameInputDevices: array[maxGamepads, ptr IGameInputDevice] # NOTE GameInput's max is 8, but other platforms limit to 4 +runPlatform() + proc indexForHandle(windows: seq[Window], hWnd: HWND): int = ## Returns the window for this handle, else -1 for i, window in windows: From a2322e89680635aaa201be8958f24a00fc54ed9b Mon Sep 17 00:00:00 2001 From: Jeremie Pelletier Date: Thu, 16 Oct 2025 16:03:09 -0400 Subject: [PATCH 09/12] Linux compilation fix --- src/windy/platforms/linux/lindefs.nim | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/windy/platforms/linux/lindefs.nim b/src/windy/platforms/linux/lindefs.nim index 022b740..18bc08c 100644 --- a/src/windy/platforms/linux/lindefs.nim +++ b/src/windy/platforms/linux/lindefs.nim @@ -1,7 +1,4 @@ -{.push importc, cdecl.} - -{.push header: "".} - +# type epoll_data_t* {.union.} = object `ptr`*: pointer @@ -18,14 +15,13 @@ const EPOLL_CTL_MOD* = 3 EPOLLIN* = 0x001 +{.push importc, cdecl.} + proc epoll_create1*(flags: cint): cint proc epoll_ctl*(epfd: cint, op: cint, fd: cint, event: ptr epoll_event): cint proc epoll_wait*(epfd: cint, events: ptr epoll_event, maxevents: cint, timeout: cint): cint -{.pop.} - -{.push header: "".} - +# type dev_t* = uint64 @@ -112,8 +108,6 @@ proc udev_enumerate_scan_devices*(udev_enumerate: ptr udev_enumerate): cint proc udev_enumerate_scan_subsystems*(udev_enumerate: ptr udev_enumerate): cint proc udev_enumerate_get_list_entry*(udev_enumerate: ptr udev_enumerate): ptr udev_list_entry -{.pop.} - # type timeval* = object @@ -189,8 +183,7 @@ const ABS_MISC* = 0x28 -{.push header: "".} - +# type libevdev* = object @@ -214,5 +207,3 @@ proc libevdev_get_abs_info*(dev: ptr libevdev, code: uint16): ptr input_absinfo proc libevdev_next_event*(dev: ptr libevdev, flags: cint, event: ptr input_event): cint {.pop.} - -{.pop.} From 553433a426c013b1b0d56f2f7a0c280b61a6ef97 Mon Sep 17 00:00:00 2001 From: Jeremie Pelletier Date: Thu, 16 Oct 2025 16:17:36 -0400 Subject: [PATCH 10/12] Remove window.run from examples --- examples/basic.nim | 4 ++-- examples/basic_textured_quad.nim | 4 ++-- examples/basic_triangle.nim | 4 ++-- examples/callbacks.nim | 4 ++-- examples/fixedsize.nim | 3 ++- examples/fullscreen.nim | 3 ++- examples/gamepad.nim | 4 ++-- examples/highdpi.nim | 3 ++- examples/httprequest.nim | 2 +- examples/icon.nim | 3 ++- examples/opengl_version.nim | 4 ++-- examples/property_changes.nim | 4 ++-- examples/tray.nim | 7 ++++--- examples/websocket.nim | 4 +++- 14 files changed, 30 insertions(+), 23 deletions(-) diff --git a/examples/basic.nim b/examples/basic.nim index 5e1a696..a0d3558 100644 --- a/examples/basic.nim +++ b/examples/basic.nim @@ -10,6 +10,6 @@ proc display() = # Your OpenGL display code here window.swapBuffers() -window.run(proc() = +while not window.closeRequested: display() - pollEvents()) + pollEvents() diff --git a/examples/basic_textured_quad.nim b/examples/basic_textured_quad.nim index eafa82f..e885876 100644 --- a/examples/basic_textured_quad.nim +++ b/examples/basic_textured_quad.nim @@ -175,6 +175,6 @@ proc display() = glDrawArrays(GL_TRIANGLES, 0, 3*2) swapBuffers(window) -window.run(proc() = +while not window.closeRequested: pollEvents() - display()) + display() diff --git a/examples/basic_triangle.nim b/examples/basic_triangle.nim index 49387d2..7ab4637 100644 --- a/examples/basic_triangle.nim +++ b/examples/basic_triangle.nim @@ -118,6 +118,6 @@ proc display() = # Your OpenGL display code here window.swapBuffers() -window.run(proc() = +while not window.closeRequested: display() - pollEvents()) + pollEvents() diff --git a/examples/callbacks.nim b/examples/callbacks.nim index 0467ccb..646c6c1 100644 --- a/examples/callbacks.nim +++ b/examples/callbacks.nim @@ -52,7 +52,7 @@ window.onButtonRelease = proc(button: Button) = window.onRune = proc(rune: Rune) = echo "onRune ", rune -window.run(proc() = +while not window.closeRequested: if window.minimized or not window.visible: sleep(10) - pollEvents()) + pollEvents() \ No newline at end of file diff --git a/examples/fixedsize.nim b/examples/fixedsize.nim index 1bba51b..81cbc1b 100644 --- a/examples/fixedsize.nim +++ b/examples/fixedsize.nim @@ -11,4 +11,5 @@ window.onFrame = proc() = # Your OpenGL display code here window.swapBuffers() -window.run(pollEvents) +while not window.closeRequested: + pollEvents() \ No newline at end of file diff --git a/examples/fullscreen.nim b/examples/fullscreen.nim index 7954de2..72a1a6a 100644 --- a/examples/fullscreen.nim +++ b/examples/fullscreen.nim @@ -21,4 +21,5 @@ window.onButtonPress = proc(button: Button) = if button == MouseLeft: window.fullscreen = window.buttonToggle[MouseLeft] -window.run(pollEvents) +while not window.closeRequested: + pollEvents() \ No newline at end of file diff --git a/examples/gamepad.nim b/examples/gamepad.nim index fb3f3a0..b0699cf 100644 --- a/examples/gamepad.nim +++ b/examples/gamepad.nim @@ -31,7 +31,7 @@ proc display() = glClear(GL_COLOR_BUFFER_BIT) window.swapBuffers() -window.run(proc() = +while not window.closeRequested: gamepad() display() - pollEvents()) + pollEvents() diff --git a/examples/highdpi.nim b/examples/highdpi.nim index e870d0e..e9dd838 100644 --- a/examples/highdpi.nim +++ b/examples/highdpi.nim @@ -22,4 +22,5 @@ window.onFrame = proc() = # Your OpenGL display code here window.swapBuffers() -window.run(pollEvents) +while not window.closeRequested: + pollEvents() \ No newline at end of file diff --git a/examples/httprequest.nim b/examples/httprequest.nim index 0941e10..55a3803 100644 --- a/examples/httprequest.nim +++ b/examples/httprequest.nim @@ -23,6 +23,6 @@ req.onResponse = proc(response: HttpResponse) = echo "onResponse: code=", $response.code, ", len=", response.body.len # Closing the window exits the demo -let window = newWindow("Windy Basic", ivec2(1280, 800)) +let window = newWindow("Windy HttpRequest", ivec2(1280, 800)) while not window.closeRequested: pollEvents() diff --git a/examples/icon.nim b/examples/icon.nim index c881ade..5fa1c8a 100644 --- a/examples/icon.nim +++ b/examples/icon.nim @@ -14,4 +14,5 @@ when defined(windows) or defined(linux): window.icon = icon - window.run(pollEvents) + while not window.closeRequested: + pollEvents() \ No newline at end of file diff --git a/examples/opengl_version.nim b/examples/opengl_version.nim index e844950..1e1bb9c 100644 --- a/examples/opengl_version.nim +++ b/examples/opengl_version.nim @@ -15,6 +15,6 @@ echo "GL_VERSION: ", cast[cstring](glGetString(GL_VERSION)) echo "GL_VENDOR: ", cast[cstring](glGetString(GL_VENDOR)) echo "GL_RENDERER: ", cast[cstring](glGetString(GL_RENDERER)) -window.run(proc() = +while not window.closeRequested: pollEvents() - sleep(10)) + sleep(10) \ No newline at end of file diff --git a/examples/property_changes.nim b/examples/property_changes.nim index db3b43f..56e9969 100644 --- a/examples/property_changes.nim +++ b/examples/property_changes.nim @@ -10,7 +10,7 @@ window.onFrame = proc() = # Your OpenGL display code here window.swapBuffers() -window.run(proc() = +while not window.closeRequested: sleep(100) pollEvents() @@ -74,4 +74,4 @@ window.run(proc() = doAssert not window.fullscreen echo "SUCCESS!" - quit()) + quit() diff --git a/examples/tray.nim b/examples/tray.nim index c143108..2700161 100644 --- a/examples/tray.nim +++ b/examples/tray.nim @@ -38,6 +38,7 @@ when defined(windows): showTrayIcon(icon, "Demo", onTrayIconClick, menu) - window.run( - pollEvents, - hideTrayIcon) + while not window.closeRequested: + pollEvents() + + hideTrayIcon() diff --git a/examples/websocket.nim b/examples/websocket.nim index c3fcded..de26b37 100644 --- a/examples/websocket.nim +++ b/examples/websocket.nim @@ -25,4 +25,6 @@ ws.onClose = proc() = echo "onClose" # Closing the window exits the demo -newWindow("Windy WebSocket", ivec2(1280, 800)).run(pollEvents) +let window = newWindow("Windy WebSocket", ivec2(1280, 800)) +while not window.closeRequested: + pollEvents() \ No newline at end of file From ffd58eb142c0dfb05e45b73cab52bcd70176df10 Mon Sep 17 00:00:00 2001 From: Jeremie Pelletier Date: Thu, 16 Oct 2025 16:25:50 -0400 Subject: [PATCH 11/12] cleanup and removing extras --- config.nims | 59 --------------- examples/callbacks.nim | 2 +- examples/fixedsize.nim | 2 +- examples/fullscreen.nim | 2 +- examples/highdpi.nim | 2 +- examples/icon.nim | 2 +- examples/opengl_version.nim | 2 +- examples/websocket.nim | 2 +- src/windy.nim | 6 -- src/windy/internal.nim | 6 -- src/windy/platforms/linux/lindefs.nim | 2 + src/windy/shell.html | 101 -------------------------- 12 files changed, 9 insertions(+), 179 deletions(-) delete mode 100644 config.nims delete mode 100644 src/windy/shell.html diff --git a/config.nims b/config.nims deleted file mode 100644 index bf882d8..0000000 --- a/config.nims +++ /dev/null @@ -1,59 +0,0 @@ -import strutils - -if defined(linux): - switch( - "passL", - """ - -ludev - -levdev - """.replace("\n", " ")) - -if defined(emscripten): - - # This path will only run if -d:emscripten is passed to nim. - - --nimcache:tmp # Store intermediate files close by in the ./tmp dir. - - --os:linux # Emscripten pretends to be linux. - --cpu:wasm32 # Emscripten is 32bits. - --cc:clang # Emscripten is very close to clang, so we will replace it. - when defined(windows): - --clang.exe:emcc.bat # Replace C - --clang.linkerexe:emcc.bat # Replace C linker - --clang.cpp.exe:emcc.bat # Replace C++ - --clang.cpp.linkerexe:emcc.bat # Replace C++ linker. - else: - --clang.exe:emcc # Replace C - --clang.linkerexe:emcc # Replace C linker - --clang.cpp.exe:emcc # Replace C++ - --clang.cpp.linkerexe:emcc # Replace C++ linker. - --listCmd # List what commands we are running so that we can debug them. - - --gc:arc # GC:arc is friendlier with crazy platforms. - --exceptions:goto # Goto exceptions are friendlier with crazy platforms. - --define:noSignalHandler # Emscripten doesn't support signal handlers. - - # Create the dist directory if it doesn't exist. - if not dirExists("dist"): - mkDir("dist") - - # Pass this to Emscripten linker to generate html file scaffold for us. - switch( - "passL", - """ - -o dist/windy.html - --shell-file src/windy/shell.html - -s USE_WEBGL2=1 - -s MAX_WEBGL_VERSION=2 - -s MIN_WEBGL_VERSION=1 - -s FULL_ES3=1 - -s GL_ENABLE_GET_PROC_ADDRESS=1 - -s ALLOW_MEMORY_GROWTH - --profiling - """.replace("\n", " ") - ) - ---gc:arc # GC:arc is friendlier with crazy platforms. ---exceptions:goto # Goto exceptions are friendlier with crazy platforms. ---define:noSignalHandler # Emscripten doesn't support signal handlers. ---define:noAutoGLerrorCheck diff --git a/examples/callbacks.nim b/examples/callbacks.nim index 646c6c1..77c4300 100644 --- a/examples/callbacks.nim +++ b/examples/callbacks.nim @@ -55,4 +55,4 @@ window.onRune = proc(rune: Rune) = while not window.closeRequested: if window.minimized or not window.visible: sleep(10) - pollEvents() \ No newline at end of file + pollEvents() diff --git a/examples/fixedsize.nim b/examples/fixedsize.nim index 81cbc1b..96eda91 100644 --- a/examples/fixedsize.nim +++ b/examples/fixedsize.nim @@ -12,4 +12,4 @@ window.onFrame = proc() = window.swapBuffers() while not window.closeRequested: - pollEvents() \ No newline at end of file + pollEvents() diff --git a/examples/fullscreen.nim b/examples/fullscreen.nim index 72a1a6a..6099a95 100644 --- a/examples/fullscreen.nim +++ b/examples/fullscreen.nim @@ -22,4 +22,4 @@ window.onButtonPress = proc(button: Button) = window.fullscreen = window.buttonToggle[MouseLeft] while not window.closeRequested: - pollEvents() \ No newline at end of file + pollEvents() diff --git a/examples/highdpi.nim b/examples/highdpi.nim index e9dd838..a2d26ee 100644 --- a/examples/highdpi.nim +++ b/examples/highdpi.nim @@ -23,4 +23,4 @@ window.onFrame = proc() = window.swapBuffers() while not window.closeRequested: - pollEvents() \ No newline at end of file + pollEvents() diff --git a/examples/icon.nim b/examples/icon.nim index 5fa1c8a..f0d917a 100644 --- a/examples/icon.nim +++ b/examples/icon.nim @@ -15,4 +15,4 @@ when defined(windows) or defined(linux): window.icon = icon while not window.closeRequested: - pollEvents() \ No newline at end of file + pollEvents() diff --git a/examples/opengl_version.nim b/examples/opengl_version.nim index 1e1bb9c..9592c3e 100644 --- a/examples/opengl_version.nim +++ b/examples/opengl_version.nim @@ -17,4 +17,4 @@ echo "GL_RENDERER: ", cast[cstring](glGetString(GL_RENDERER)) while not window.closeRequested: pollEvents() - sleep(10) \ No newline at end of file + sleep(10) diff --git a/examples/websocket.nim b/examples/websocket.nim index de26b37..5850e7c 100644 --- a/examples/websocket.nim +++ b/examples/websocket.nim @@ -27,4 +27,4 @@ ws.onClose = proc() = # Closing the window exits the demo let window = newWindow("Windy WebSocket", ivec2(1280, 800)) while not window.closeRequested: - pollEvents() \ No newline at end of file + pollEvents() diff --git a/src/windy.nim b/src/windy.nim index 2e383aa..1af0991 100644 --- a/src/windy.nim +++ b/src/windy.nim @@ -10,9 +10,3 @@ elif defined(linux): import windy/platforms/linux/platform export common, platform, unicode, vmath - -when not defined(emscripten): - proc run*(window: Window, mainLoop: proc(), onExit: proc() = empty) = - while not window.closeRequested: - mainLoop() - onExit() diff --git a/src/windy/internal.nim b/src/windy/internal.nim index e41efd9..a80d232 100644 --- a/src/windy/internal.nim +++ b/src/windy/internal.nim @@ -209,9 +209,3 @@ proc gamepadResetState*(state: var GamepadState) = for i in 0.. 0: - mainLoop() - onExit() diff --git a/src/windy/platforms/linux/lindefs.nim b/src/windy/platforms/linux/lindefs.nim index 18bc08c..76a8e68 100644 --- a/src/windy/platforms/linux/lindefs.nim +++ b/src/windy/platforms/linux/lindefs.nim @@ -1,3 +1,5 @@ +{.passL: "-ludev -levdev."} + # type epoll_data_t* {.union.} = object diff --git a/src/windy/shell.html b/src/windy/shell.html deleted file mode 100644 index f4bc37b..0000000 --- a/src/windy/shell.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - Emscripten-Generated Code - - - - - - - - {{{ SCRIPT }}} - - From df6d471f60cb3c25650ba33bc7f2b4abe579583c Mon Sep 17 00:00:00 2001 From: Jeremie Pelletier Date: Thu, 16 Oct 2025 16:27:49 -0400 Subject: [PATCH 12/12] fix compilation issue --- src/windy/platforms/emscripten/platform.nim | 2 -- src/windy/platforms/linux/x11.nim | 2 -- src/windy/platforms/macos/platform.nim | 2 -- src/windy/platforms/win32/platform.nim | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/windy/platforms/emscripten/platform.nim b/src/windy/platforms/emscripten/platform.nim index 67f6baf..5f41d06 100644 --- a/src/windy/platforms/emscripten/platform.nim +++ b/src/windy/platforms/emscripten/platform.nim @@ -57,8 +57,6 @@ var gamepadsConnectedMask: uint8 gamepadStates: array[maxGamepads, GamepadState] -runPlatform() # Only one possible window, ends up equivalent to window.run - proc handleButtonPress(window: Window, button: Button) proc handleButtonRelease(window: Window, button: Button) proc handleRune(window: Window, rune: Rune) diff --git a/src/windy/platforms/linux/x11.nim b/src/windy/platforms/linux/x11.nim index e8c0e89..49a444f 100644 --- a/src/windy/platforms/linux/x11.nim +++ b/src/windy/platforms/linux/x11.nim @@ -72,8 +72,6 @@ var clipboardWindow: XWindow clipboardContent: string -runPlatform() - proc initConstants(display: Display) = xaNetWMState = display.XInternAtom("_NET_WM_STATE", 0) xaNetWMStateMaximizedHorz = display.XInternAtom("_NET_WM_STATE_MAXIMIZED_HORZ", 0) diff --git a/src/windy/platforms/macos/platform.nim b/src/windy/platforms/macos/platform.nim index 8e8f5a7..0141b79 100644 --- a/src/windy/platforms/macos/platform.nim +++ b/src/windy/platforms/macos/platform.nim @@ -54,8 +54,6 @@ var gamepadButtonLookup: array[maxGamepads, array[GamepadButtonCount.int, int8]] gamepadButtonInputs: array[maxGamepads, array[GamepadButtonCount.int, GCControllerButtonInput]] -runPlatform() - proc indexForNSWindow(windows: seq[Window], inner: NSWindow): int = ## Returns the window for this handle, else -1 for i, window in windows: diff --git a/src/windy/platforms/win32/platform.nim b/src/windy/platforms/win32/platform.nim index 26e3700..e40fb60 100644 --- a/src/windy/platforms/win32/platform.nim +++ b/src/windy/platforms/win32/platform.nim @@ -157,8 +157,6 @@ var gamepadStates: array[maxGamepads, GamepadState] gameInputDevices: array[maxGamepads, ptr IGameInputDevice] # NOTE GameInput's max is 8, but other platforms limit to 4 -runPlatform() - proc indexForHandle(windows: seq[Window], hWnd: HWND): int = ## Returns the window for this handle, else -1 for i, window in windows: