Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
- run: nim c examples/fixedsize.nim
- run: nim c examples/fullscreen.nim
- run: nim c examples/icon.nim
- run: nim c examples/mouse_inside.nim
- run: nim c examples/opengl_version.nim
- run: nim c examples/openurl.nim
- run: nim c examples/property_changes.nim
Expand Down Expand Up @@ -82,6 +83,8 @@ jobs:
if: matrix.os == 'ubuntu-latest'
- run: nim c -d:emscripten examples/icon.nim
if: matrix.os == 'ubuntu-latest'
- run: nim c -d:emscripten examples/mouse_inside.nim
if: matrix.os == 'ubuntu-latest'
- run: nim c -d:emscripten examples/opengl_version.nim
if: matrix.os == 'ubuntu-latest'
- run: nim c -d:emscripten examples/openurl.nim
Expand Down
29 changes: 29 additions & 0 deletions examples/mouse_inside.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import opengl, os, windy

let window = newWindow("Windy Mouse Inside", ivec2(1280, 800))

window.makeContextCurrent()
loadExtensions()

var wasInside = window.mouseInside

echo "Move the mouse in and out of the window."
echo "Red means inside, black means outside."
echo "mouseInside ", wasInside

window.onFrame = proc() =
let inside = window.mouseInside
if inside != wasInside:
echo "mouseInside ", inside
wasInside = inside
if inside:
glClearColor(1.0f, 0.0f, 0.0f, 1.0f)
else:
glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
glClear(GL_COLOR_BUFFER_BIT)
window.swapBuffers()

while not window.closeRequested:
if window.minimized or not window.visible:
sleep(10)
pollEvents()
1 change: 1 addition & 0 deletions src/windy/internal.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type
closeRequested*, closed*: bool
hasPrevMouse*: bool
mouseCaptured*: bool
mouseInside*: bool
mousePos*, mousePrevPos*: IVec2
buttonDown*, buttonToggle*: set[Button]
perFrame*: PerFrame
Expand Down
2 changes: 2 additions & 0 deletions src/windy/platforms/emscripten/emdefs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,8 @@ type
proc emscripten_set_mousedown_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
proc emscripten_set_mouseup_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
proc emscripten_set_mousemove_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
proc emscripten_set_mouseenter_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
proc emscripten_set_mouseleave_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenMouseEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
proc emscripten_set_wheel_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenWheelEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
proc emscripten_set_keydown_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenKeyboardEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
proc emscripten_set_keyup_callback_on_thread*(target: cstring, userData: pointer, useCapture: EM_BOOL, callback: EmscriptenKeyboardEventCallback, targetThread: pointer): EMSCRIPTEN_RESULT {.importc, header: "<emscripten/html5.h>".}
Expand Down
17 changes: 17 additions & 0 deletions src/windy/platforms/emscripten/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ proc newWindow*(
proc mousePos*(window: Window): IVec2 =
(window.state.mousePos.vec2 * window.contentScale).ivec2

proc mouseInside*(window: Window): bool =
## True while the cursor is over this window's content.
window.state.mouseInside

proc mousePrevPos*(window: Window): IVec2 =
(window.state.mousePrevPos.vec2 * window.contentScale).ivec2

Expand Down Expand Up @@ -618,11 +622,22 @@ proc onMouseMove(eventType: cint, mouseEvent: ptr EmscriptenMouseEvent, userData
window.state.mousePrevPos = window.state.mousePos
# Use clientX/clientY as they are more reliably populated
window.state.mousePos = ivec2(mouseEvent.clientX.int32, mouseEvent.clientY.int32)
window.state.mouseInside = true
window.state.perFrame.mouseDelta += window.state.mousePos - window.state.mousePrevPos
if window.onMouseMove != nil:
window.onMouseMove()
return 1

proc onMouseEnter(eventType: cint, mouseEvent: ptr EmscriptenMouseEvent, userData: pointer): EM_BOOL {.cdecl.} =
let window = cast[Window](userData)
window.state.mouseInside = true
return 1

proc onMouseLeave(eventType: cint, mouseEvent: ptr EmscriptenMouseEvent, userData: pointer): EM_BOOL {.cdecl.} =
let window = cast[Window](userData)
window.state.mouseInside = false
return 1

proc onWheel(eventType: cint, wheelEvent: ptr EmscriptenWheelEvent, userData: pointer): EM_BOOL {.cdecl.} =
## Handle browser wheel events with OS-specific normalization.
let window = cast[Window](userData)
Expand Down Expand Up @@ -707,6 +722,8 @@ proc setupEventHandlers(window: Window) =
discard emscripten_set_mousedown_callback_on_thread(window.canvas, cast[pointer](window), 1, onMouseDown, EM_CALLBACK_THREAD_CONTEXT)
discard emscripten_set_mouseup_callback_on_thread(window.canvas, cast[pointer](window), 1, onMouseUp, EM_CALLBACK_THREAD_CONTEXT)
discard emscripten_set_mousemove_callback_on_thread(window.canvas, cast[pointer](window), 1, onMouseMove, EM_CALLBACK_THREAD_CONTEXT)
discard emscripten_set_mouseenter_callback_on_thread(window.canvas, cast[pointer](window), 1, onMouseEnter, EM_CALLBACK_THREAD_CONTEXT)
discard emscripten_set_mouseleave_callback_on_thread(window.canvas, cast[pointer](window), 1, onMouseLeave, EM_CALLBACK_THREAD_CONTEXT)

# Wheel event
discard emscripten_set_wheel_callback_on_thread(window.canvas, cast[pointer](window), 1, onWheel, EM_CALLBACK_THREAD_CONTEXT)
Expand Down
12 changes: 12 additions & 0 deletions src/windy/platforms/linux/x11.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1038,13 +1038,21 @@ proc pollEvents(window: Window) =
of xeMotion:
window.mousePrevPos = window.mousePos
window.mousePos = ev.motion.pos
window.state.mouseInside = true
window.perFrame.mouseDelta += window.mousePos - window.mousePrevPos
if (window.mousePos - window.lastClickPosition).vec2.length > multiClickRadius:
window.buttonClicking = {}
window.clickSeqLen = 0
if window.onMouseMove != nil:
window.onMouseMove()

of xeEnter:
if ev.crossing.mode == 0:
window.state.mouseInside = true
of xeLeave:
if ev.crossing.mode == 0:
window.state.mouseInside = false

of xeButtonPress, xeButtonRelease:

template pushScrollEvent(delta: Vec2) =
Expand Down Expand Up @@ -1151,6 +1159,10 @@ proc pollEvents(window: Window) =
proc mousePos*(window: Window): IVec2 =
window.mousePos

proc mouseInside*(window: Window): bool =
## True while the cursor is over this window's content.
window.state.mouseInside

proc mousePrevPos*(window: Window): IVec2 =
window.mousePrevPos

Expand Down
23 changes: 23 additions & 0 deletions src/windy/platforms/macos/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ proc handleMouseMove(window: Window, location: NSPoint) =

window.state.mousePrevPos = window.state.mousePos
window.state.mousePos = (vec2(x, y) * window.contentScale).ivec2
window.state.mouseInside = true

# Prevent a jump in the mouse delta when focusing a window.
if window.state.hasPrevMouse:
Expand Down Expand Up @@ -615,6 +616,20 @@ proc updateTrackingAreas(self: ID, cmd: SEL): ID {.cdecl.} =

callSuper(self, cmd)

proc mouseEntered(self: ID, cmd: SEL, event: NSEvent): ID {.cdecl.} =
let window = windows.forNSWindow(self.NSView.window)
if window == nil:
return
window.state.mouseInside = true
if not window.state.mouseCaptured:
handleMouseMove(window, event.locationInWindow)

proc mouseExited(self: ID, cmd: SEL, event: NSEvent): ID {.cdecl.} =
let window = windows.forNSWindow(self.NSView.window)
if window == nil:
return
window.state.mouseInside = false

proc mouseMoved(self: ID, cmd: SEL, event: NSEvent): ID {.cdecl.} =
let window = windows.forNSWindow(self.NSView.window)
if window == nil:
Expand Down Expand Up @@ -978,6 +993,8 @@ proc init() {.raises: [].} =
addMethod "acceptsFirstMouse:", acceptsFirstMouse
addMethod "viewDidChangeBackingProperties", viewDidChangeBackingProperties
addMethod "updateTrackingAreas", updateTrackingAreas
addMethod "mouseEntered:", mouseEntered
addMethod "mouseExited:", mouseExited
addMethod "mouseMoved:", mouseMoved
addMethod "mouseDragged:", mouseDragged
addMethod "rightMouseDragged:", rightMouseDragged
Expand Down Expand Up @@ -1013,6 +1030,8 @@ proc init() {.raises: [].} =
addMethod "acceptsFirstMouse:", acceptsFirstMouse
addMethod "viewDidChangeBackingProperties", viewDidChangeBackingProperties
addMethod "updateTrackingAreas", updateTrackingAreas
addMethod "mouseEntered:", mouseEntered
addMethod "mouseExited:", mouseExited
addMethod "mouseMoved:", mouseMoved
addMethod "mouseDragged:", mouseDragged
addMethod "rightMouseDragged:", rightMouseDragged
Expand Down Expand Up @@ -1338,6 +1357,10 @@ proc nativeView*(window: Window): NSView =
proc mousePos*(window: Window): IVec2 =
window.state.mousePos

proc mouseInside*(window: Window): bool =
## True while the cursor is over this window's content.
window.state.mouseInside

proc mousePrevPos*(window: Window): IVec2 =
window.state.mousePrevPos

Expand Down
6 changes: 6 additions & 0 deletions src/windy/platforms/win32/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,7 @@ proc wndProc(
discard GetCursorPos(pos.addr)
discard ScreenToClient(window.hWnd, pos.addr)
window.state.mousePos = ivec2(pos.x, pos.y)
window.state.mouseInside = true
window.state.perFrame.mouseDelta +=
window.state.mousePos - window.state.mousePrevPos
if window.onMouseMove != nil:
Expand All @@ -982,6 +983,7 @@ proc wndProc(
return 0
of WM_MOUSELEAVE:
window.trackMouseEventRegistered = false
window.state.mouseInside = false
return 0
of WM_SETCURSOR:
if LOWORD(lParam) == HTCLIENT:
Expand Down Expand Up @@ -1374,6 +1376,10 @@ proc icon*(window: Window): Image =
proc mousePos*(window: Window): IVec2 =
window.state.mousePos

proc mouseInside*(window: Window): bool =
## True while the cursor is over this window's content.
window.state.mouseInside

proc mousePrevPos*(window: Window): IVec2 =
window.state.mousePrevPos

Expand Down
1 change: 1 addition & 0 deletions tests/run_all.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const Examples = [
"fixedsize",
"fullscreen",
"icon",
"mouse_inside",
"opengl_version",
"openurl",
"property_changes",
Expand Down
1 change: 1 addition & 0 deletions tests/run_all_emscripten.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const Examples = [
"fixedsize",
"fullscreen",
"icon",
"mouse_inside",
"opengl_version",
"openurl",
"property_changes",
Expand Down
Loading