diff options
| author | 2026-08-25 19:48:15 -0400 | |
|---|---|---|
| committer | 2026-08-25 19:48:15 -0400 | |
| commit | f48520c10ba822e13ef119f626b85fc081f96124 (patch) | |
| tree | 8672a171b616b82feb74478d29f4205cf06488e9 /src/window_win32.c | |
| parent | a35c47d401335e81f25c2c78611dcb7785f703da (diff) | |
| download | stela-f48520c10ba822e13ef119f626b85fc081f96124.tar.gz stela-f48520c10ba822e13ef119f626b85fc081f96124.tar.bz2 stela-f48520c10ba822e13ef119f626b85fc081f96124.zip | |
Raw input + drag and drop on Windows, cleanup
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/window_win32.c')
| -rw-r--r-- | src/window_win32.c | 168 |
1 files changed, 113 insertions, 55 deletions
diff --git a/src/window_win32.c b/src/window_win32.c index 3d5d2e8..e1cf137 100644 --- a/src/window_win32.c +++ b/src/window_win32.c @@ -108,10 +108,10 @@ static LRESULT CALLBACK ServiceWndProc(HWND Window, UINT Message, WPARAM WParam, } case DESTROY_DANGEROUS_WINDOW: { struct stl_window_win32 *w32 = (struct stl_window_win32 *)WParam; - if (!w32->Closed && !DestroyWindow(w32->Window)) { + al_assert(!w32->Closed); + if (!DestroyWindow(w32->Window)) { log_error("Failed to destroy window (0x%lx).", GetLastError()); } - UnregisterClassW(DangerousClass, Global.ModuleHandle); break; } case EXIT_PROCESS: @@ -660,6 +660,7 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, switch (Message) { case WM_CREATE: case WM_CLOSE: + case WM_DESTROY: // Special case with Window as WParam. PostThreadMessageW(MainThreadID, Message, (WPARAM)Window, LParam); break; @@ -684,55 +685,53 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, goto intercept; case WM_LBUTTONUP: w32->DragCapture = FALSE; - ReleaseCapture(); + if (!w32->WindowMoving) { + ReleaseCapture(); + } goto intercept; case WM_RBUTTONDOWN: case WM_NCRBUTTONDOWN: #ifdef STELA_MOUSE2_DRAG - if (!Global.IsWine) { - POINT Pos; - Pos.x = GET_X_LPARAM(LParam); - Pos.y = GET_Y_LPARAM(LParam); - if (Message == WM_RBUTTONDOWN) { - ClientToScreen(w32->Window, &Pos); - } - w32->LastMousePos.x = Pos.x; - w32->LastMousePos.y = Pos.y; - w32->WindowMoving = TRUE; - // DragCapture is a higher priority. - if (!w32->DragCapture) { - SetCapture(w32->Window); + if (!w32->w.fullscreen) { + if (!Global.IsWine) { + POINT Pos; + Pos.x = GET_X_LPARAM(LParam); + Pos.y = GET_Y_LPARAM(LParam); + if (Message == WM_RBUTTONDOWN) { + ClientToScreen(w32->Window, &Pos); + } + w32->LastMousePos.x = Pos.x; + w32->LastMousePos.y = Pos.y; + w32->WindowMoving = TRUE; + if (!GetCapture()) { + SetCapture(w32->Window); + } + } else { + /* + Message = WM_NCLBUTTONDOWN; + WParam = HTCAPTION; + */ + // https://stackoverflow.com/a/35522584, incredible... + // https://gitlab.winehq.org/agusev/wine/-/blob/wine-971221/windows/nonclient.c#L1773 + // This works on Wine but not Windows. Everything works the same, except on Windows + // it seems there is a special case for LBUTTON. + SendMessageW(w32->Window, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, LParam); } - return Result; - } else { - /* - Message = WM_NCLBUTTONDOWN; - WParam = HTCAPTION; - */ - // https://stackoverflow.com/a/35522584, incredible... - // https://gitlab.winehq.org/agusev/wine/-/blob/wine-971221/windows/nonclient.c#L1773 - // This works on Wine but not Windows. Everything works the same, except on Windows - // it seems there is a special case for LBUTTON. - SendMessageW(w32->Window, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, LParam); } - break; -#else - goto intercept; #endif + goto intercept; case WM_RBUTTONUP: case WM_NCRBUTTONUP: #ifdef STELA_MOUSE2_DRAG - if (!Global.IsWine) { + if (!w32->w.fullscreen && !Global.IsWine) { w32->WindowMoving = FALSE; + // DragCapture is a higher priority. if (!w32->DragCapture) { ReleaseCapture(); } - return Result; } - break; -#else - goto intercept; #endif + goto intercept; case WM_MBUTTONDOWN: case WM_MBUTTONUP: goto intercept; @@ -758,6 +757,8 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, return Result; } goto intercept; + case WM_INPUT: + goto intercept; case WM_MOUSEWHEEL: goto intercept; case WM_ACTIVATE: // Don't use Wine behavior as a reference, it's too broken. @@ -814,6 +815,10 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, ShowCursor(TRUE); } return 0; +#ifdef STELA_DRAG_AND_DROP + case WM_DROPFILES: + goto intercept; +#endif default: break; } @@ -893,15 +898,9 @@ static bool CreateWGLContext(struct stl_window_win32 *w32) w32->GLContext = NULL; if (CreateContextAttribsARB) { for (u32 i = 0; i < ARRAY_SIZE(stl_gl_vers); i++) { - s32 flags = stl_gl_vers[i].major >= 3 ? WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB : 0; - s32 mask = 0; - if (stl_gl_vers[i].api == STELA_GLES_API) { - mask |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT; - } else { - mask |= stl_gl_vers[i].profile == STELA_GL_CORE_PROFILE_BIT ? - WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - } - + s32 flags = (stl_gl_vers[i].major >= 3) ? WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB : 0; + s32 mask = (stl_gl_vers[i].api == STELA_GLES_API) ? WGL_CONTEXT_ES2_PROFILE_BIT_EXT : + ((stl_gl_vers[i].profile == STELA_GL_CORE_PROFILE_BIT) ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); s32 attribs[] = { WGL_CONTEXT_FLAGS_ARB, flags, WGL_CONTEXT_PROFILE_MASK_ARB, mask, @@ -909,15 +908,12 @@ static bool CreateWGLContext(struct stl_window_win32 *w32) WGL_CONTEXT_MINOR_VERSION_ARB, stl_gl_vers[i].minor, 0, 0 }; - STELA_LOG_GL_CONTEXT_CREATION(stl_gl_vers[i]); - - w32->GLContext = CreateContextAttribsARB(w32->DeviceContext, NULL, attribs); - if (w32->GLContext) break; - + if ((w32->GLContext = CreateContextAttribsARB(w32->DeviceContext, NULL, attribs))) { + break; + } log_warn("Failed to create context (0x%lx).", GetLastError()); } - wglDeleteContext(FakeContext); wglMakeCurrent(w32->DeviceContext, w32->GLContext); } else { @@ -988,6 +984,21 @@ static bool window_win32_create_window(struct stl_window *window, u32 width, u32 return false; } +#define HID_USAGE_PAGE_GENERIC ((USHORT)0x01) +#define HID_USAGE_GENERIC_MOUSE ((USHORT)0x02) + RAWINPUTDEVICE Rid[1]; + Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE; + Rid[0].dwFlags = 0; + Rid[0].hwndTarget = w32->Window; + if (!RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]))) { + log_warn("Failed to register raw input mouse."); + } + +#ifdef STELA_DRAG_AND_DROP + DragAcceptFiles(w32->Window, TRUE); +#endif + stl_window_created(&w32->w, width, height, flags); w32->Closed = FALSE; @@ -995,7 +1006,7 @@ static bool window_win32_create_window(struct stl_window *window, u32 width, u32 w32->UserDecorated = TRUE; w32->Prev.Style = GetWindowLong(w32->Window, GWL_STYLE); w32->Prev.ExStyle = GetWindowLong(w32->Window, GWL_EXSTYLE); - w32->DragCapture = TRUE; + w32->DragCapture = FALSE; w32->WindowMoving = FALSE; w32->MouseButtonMask = 0; @@ -1096,6 +1107,12 @@ static void window_win32_toggle_fullscreen(struct stl_window *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; if (!w32->w.fullscreen) { + if (w32->WindowMoving) { + w32->WindowMoving = FALSE; + if (!w32->DragCapture) { + ReleaseCapture(); + } + } RECT Prev; GetWindowRect(w32->Window, &Prev); // https://learn.microsoft.com/en-us/windows/win32/gdi/positioning-objects-on-a-multiple-display-setup @@ -1165,8 +1182,10 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) switch (Message->message) { case WM_CREATE: break; - case WM_CLOSE: + case WM_CLOSE: // This is *not* sent by DestroyWindow(). w32->Closed = TRUE; + break; + case WM_DESTROY: #ifdef STELA_API_OPENGL wglDeleteContext(w32->GLContext); // Makes context not current. #endif @@ -1230,7 +1249,25 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) if (Message->message == WM_NCMOUSEMOVE) { ScreenToClient(w32->Window, &Pos); } - stl_window_send_pointer_pos_event(&w32->w, (f64)Pos.x, (f64)Pos.y); + stl_window_send_pointer_event(&w32->w, STELA_POINTER_POS, (f64)Pos.x, (f64)Pos.y); + break; + } + case WM_INPUT: { + UINT dwSize = sizeof(RAWINPUT); + BYTE lpb[sizeof(RAWINPUT)] = { 0 }; + GetRawInputData((HRAWINPUT)Message->lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)); + RAWINPUT *raw = (RAWINPUT *)lpb; + if (raw->header.dwType == RIM_TYPEMOUSE) { + RAWMOUSE *mouse = &raw->data.mouse; + // This doesn't work from outside the WndProc on Wine (all zeros). + if (mouse->usFlags & MOUSE_MOVE_ABSOLUTE) { + } else { + if (mouse->lLastX != 0 || mouse->lLastY != 0) { + stl_window_send_pointer_event(&w32->w, STELA_POINTER_MOVE, + (f64)mouse->lLastX, (f64)mouse->lLastY); + } + } + } break; } case WM_MOUSEWHEEL: { @@ -1274,6 +1311,22 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) w32->w.pending_refresh = true; #endif break; +#ifdef STELA_DRAG_AND_DROP + case WM_DROPFILES: { + HDROP Drop = (HDROP)Message->wParam; + str path = AL_STR_EMPTY; + u32 count = DragQueryFileA(Drop, 0xffffffff, NULL, 0); + for (u32 i = 0; i < count; i++) { + path.length = DragQueryFileA(Drop, i, NULL, 0); + path.alloc = al_str_reserve(&path, path.length + 1); + DragQueryFileA(Drop, i, path.data, path.alloc); + stl_window_send_drag_and_drop_event(&w32->w, &path); + } + al_str_free(&path); + DragFinish(Drop); + break; + } +#endif default: break; } @@ -1321,7 +1374,12 @@ static bool window_win32_should_refresh(struct stl_window *window) static void window_win32_free(struct stl_window **window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)*window; - SendMessageW(Global.ServiceWindow, DESTROY_DANGEROUS_WINDOW, (WPARAM)w32, 0); + if (!w32->Closed) { + SendMessageW(Global.ServiceWindow, DESTROY_DANGEROUS_WINDOW, (WPARAM)w32, 0); + // Hopefully get the WM_DESTROY event. + window_win32_process_events(&w32->w); + } + UnregisterClassW(DangerousClass, Global.ModuleHandle); al_wstr_free(&w32->WindowName); al_free(w32); *window = NULL; @@ -1387,7 +1445,7 @@ static void window_win32_gl_release_current(void *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; al_assert(w32->GLContext == w32->CurrentContext); -#if 1 +#if 0 // Mimic our EGL behavior. w32->CurrentContext = NULL; wglMakeCurrent(w32->DeviceContext, NULL); #endif |