diff --git a/examples/callbacks.nim b/examples/callbacks.nim index 9843a1a..87f0dd5 100644 --- a/examples/callbacks.nim +++ b/examples/callbacks.nim @@ -37,6 +37,14 @@ window.onResize = proc() = window.onFocusChange = proc() = echo "onFocusChange ", window.focused +window.onMouseMove = proc() = + echo "onMouseMove from ", + window.mousePrevPos, " to ", window.mousePos, + " (delta ", window.mouseDelta, ")" + +window.onScroll = proc(delta: Vec2) = + echo "onScroll ", delta + window.visible = true while running: diff --git a/src/windy/common.nim b/src/windy/common.nim index ecdf69a..036a718 100644 --- a/src/windy/common.nim +++ b/src/windy/common.nim @@ -1,3 +1,5 @@ +import vmath + type WindyError* = object of ValueError @@ -5,3 +7,7 @@ type msaaDisabled = 0, msaa2x = 2, msaa4x = 4, msaa8x = 8 Callback* = proc() + ScrollCallback* = proc(delta: Vec2) + + Mouse* = object + pos*, prevPos*, delta*: IVec2 diff --git a/src/windy/platforms/win32/platform.nim b/src/windy/platforms/win32/platform.nim index 11e5890..e8f18a2 100644 --- a/src/windy/platforms/win32/platform.nim +++ b/src/windy/platforms/win32/platform.nim @@ -3,6 +3,7 @@ import ../../common, utils, vmath, windefs const windowClassName = "WINDY0" defaultScreenDpi = 96 + wheelDelta = 120 decoratedWindowStyle = WS_OVERLAPPEDWINDOW undecoratedWindowStyle = WS_POPUP @@ -33,6 +34,11 @@ type onMove*: Callback onResize*: Callback onFocusChange*: Callback + onMouseMove*: Callback + onScroll*: ScrollCallback + + mouse: Mouse + trackMouseEventRegistered: bool hWnd: HWND hdc: HDC @@ -266,14 +272,6 @@ proc loadLibraries() = GetProcAddress(user32, "AdjustWindowRectExForDpi") ) -proc callCallback(name: string, callback: Callback) {.raises: [].} = - if callback == nil: - return - try: - callback() - except: - quit("Exception during " & name & " callback: " & getCurrentExceptionMsg()) - proc wndProc( hWnd: HWND, uMsg: UINT, @@ -294,16 +292,20 @@ proc wndProc( case uMsg: of WM_CLOSE: - callCallback("onCloseRequest", window.onCloseRequest) + if window.onCloseRequest != nil: + window.onCloseRequest() return 0 of WM_MOVE: - callCallback("onMove", window.onMove) + if window.onMove != nil: + window.onMove() return 0 of WM_SIZE: - callCallback("onResize", window.onResize) + if window.onResize != nil: + window.onResize() return 0 of WM_SETFOCUS, WM_KILLFOCUS: - callCallback("onFocusChange", window.onFocusChange) + if window.onFocusChange != nil: + window.onFocusChange() return 0 of WM_DPICHANGED: # Resize to the suggested size (this triggers WM_SIZE) @@ -318,6 +320,41 @@ proc wndProc( SWP_NOACTIVATE or SWP_NOZORDER ) return 0 + of WM_MOUSEMOVE: + if not window.trackMouseEventRegistered: + var tme: TRACKMOUSEEVENTSTRUCT + tme.cbSize = sizeof(TRACKMOUSEEVENTSTRUCT).DWORD + tme.dwFlags = TME_LEAVE + tme.hWndTrack = window.hWnd + discard TrackMouseEvent(tme.addr) + window.trackMouseEventRegistered = true + + window.mouse.prevPos = window.mouse.pos + var pos: POINT + discard GetCursorPos(pos.addr) + discard ScreenToClient(window.hWnd, pos.addr) + window.mouse.pos = ivec2(pos.x, pos.y) + window.mouse.delta = window.mouse.pos - window.mouse.prevPos + if window.onMouseMove != nil: + window.onMouseMove() + return 0 + of WM_MOUSELEAVE: + window.trackMouseEventRegistered = false + return 0 + of WM_MOUSEWHEEL: + let + hiword = cast[int16]((wParam shr 16)) + delta = vec2(0, hiword.float32 / wheelDelta) + if window.onScroll != nil: + window.onScroll(delta) + return 0 + of WM_MOUSEHWHEEL: + let + hiword = cast[int16]((wParam shr 16)) + delta = vec2(hiword.float32 / wheelDelta, 0) + if window.onScroll != nil: + window.onScroll(delta) + return 0 else: discard @@ -493,6 +530,15 @@ proc contentScale*(window: Window): float32 = proc focused*(window: Window): bool = window.hWnd == GetActiveWindow() +proc mousePos*(window: Window): IVec2 = + window.mouse.pos + +proc mousePrevPos*(window: Window): IVec2 = + window.mouse.prevPos + +proc mouseDelta*(window: Window): IVec2 = + window.mouse.delta + proc `decorated=`*(window: Window, decorated: bool) = var style: LONG if decorated: diff --git a/src/windy/platforms/win32/utils.nim b/src/windy/platforms/win32/utils.nim index 4fb88e3..9826f4f 100644 --- a/src/windy/platforms/win32/utils.nim +++ b/src/windy/platforms/win32/utils.nim @@ -85,5 +85,13 @@ proc wmEventName*(wm: int | UINT): string = "WM_DPICHANGED" of WM_NULL: "WM_NULL" + of WM_MOUSEMOVE: + "WM_MOUSEMOVE" + of WM_MOUSEWHEEL: + "WM_MOUSEWHEEL" + of WM_MOUSEHWHEEL: + "WM_MOUSEHWHEEL" + of WM_MOUSELEAVE: + "WM_MOUSELEAVE" else: "WM " & $wm & " " & $toHex(wm) diff --git a/src/windy/platforms/win32/windefs.nim b/src/windy/platforms/win32/windefs.nim index e6a2e56..67a136b 100644 --- a/src/windy/platforms/win32/windefs.nim +++ b/src/windy/platforms/win32/windefs.nim @@ -110,6 +110,12 @@ type ptMinPosition*: POINT ptMaxPosition*: POINT rcNormalPosition*: RECT + TRACKMOUSEEVENTSTRUCT* {.pure.} = object + cbSize*: DWORD + dwFlags*: DWORD + hWndTrack*: HWND + dwHoverTime*: DWORD + LPTRACKMOUSEEVENTSTRUCT* = ptr TRACKMOUSEEVENTSTRUCT type wglCreateContext* = proc(hdc: HDC): HGLRC {.stdcall, raises: [].} @@ -209,6 +215,9 @@ const WM_NCCALCSIZE* = 0x0083 WM_NCPAINT* = 0x0085 WM_NCACTIVATE* = 0x0086 + WM_MOUSEMOVE* = 0x0200 + WM_MOUSEWHEEL* = 0x020A + WM_MOUSEHWHEEL* = 0x020E WM_IME_SETCONTEXT* = 0x0281 WM_IME_NOTIFY* = 0x0282 WM_IME_CONTROL* = 0x0283 @@ -218,6 +227,7 @@ const WM_IME_REQUEST* = 0x0288 WM_IME_KEYDOWN* = 0x0290 WM_IME_KEYUP* = 0x0291 + WM_MOUSELEAVE* = 0x02A3 WM_DPICHANGED* = 0x02E0 WM_DWMCOMPOSITIONCHANGED* = 0x031e WM_DWMNCRENDERINGCHANGED* = 0x031f @@ -280,6 +290,11 @@ const MONITOR_DEFAULTTONULL* = 0x00000000 MONITOR_DEFAULTTOPRIMARY* = 0x00000001 MONITOR_DEFAULTTONEAREST* = 0x00000002 + TME_HOVER* = 0x00000001 + TME_LEAVE* = 0x00000002 + TME_NONCLIENT* = 0x00000010 + TME_QUERY* = 0x40000000 + TME_CANCEL* = 0x80000000'i32 proc GetLastError*(): DWORD {.importc, stdcall, dynlib: "Kernel32".} @@ -446,6 +461,11 @@ proc ClientToScreen*( lpPoint: LPPOINT ): BOOL {.importc, stdcall, dynlib: "User32".} +proc ScreenToClient*( + hWnd: HWND, + lpPoint: LPPOINT +): BOOL {.importc, stdcall, dynlib: "User32".} + proc SetPropW*( hWnd: HWND, lpString: LPCWSTR, @@ -466,6 +486,14 @@ proc IsIconic*(hWnd: HWND): BOOL {.importc, stdcall, dynlib: "User32".} proc IsZoomed*(hWnd: HWND): BOOL {.importc, stdcall, dynlib: "User32".} +proc GetCursorPos*( + lpPoint: LPPOINT +): BOOL {.importc, stdcall, dynlib: "User32".} + +proc TrackMouseEvent*( + lpEventTrack: LPTRACKMOUSEEVENTSTRUCT +): BOOL {.importc, stdcall, dynlib: "User32".} + proc ChoosePixelFormat*( hdc: HDC, ppfd: ptr PIXELFORMATDESCRIPTOR