From f48520c10ba822e13ef119f626b85fc081f96124 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 25 Aug 2026 19:48:15 -0400 Subject: Raw input + drag and drop on Windows, cleanup Signed-off-by: Andrew Opalach --- src/egl_common.c | 2 +- src/gl_versions.h | 2 +- src/window.c | 39 +++++++++--- src/window.h | 61 ++++++++++++------- src/window_glfw.c | 6 +- src/window_wayland.c | 21 +++++-- src/window_wayland.h | 1 + src/window_win32.c | 168 ++++++++++++++++++++++++++++++++++----------------- src/window_x11.c | 21 ++++--- 9 files changed, 216 insertions(+), 105 deletions(-) (limited to 'src') diff --git a/src/egl_common.c b/src/egl_common.c index 8f02afa..eb01492 100644 --- a/src/egl_common.c +++ b/src/egl_common.c @@ -122,7 +122,7 @@ bool stl_init_egl_context(struct stl_egl *egl, struct stl_egl_global *global, EG STELA_LOG_GL_CONTEXT_CREATION(stl_gl_vers[i]); - const EGLint *context_attr = stl_gl_vers[i].api == STELA_GL_API ? + const EGLint *context_attr = (stl_gl_vers[i].api == STELA_GL_API) ? context_attr_gl : context_attr_gles; eglBindAPI(stl_gl_vers[i].api); diff --git a/src/gl_versions.h b/src/gl_versions.h index eb53794..7b3050c 100644 --- a/src/gl_versions.h +++ b/src/gl_versions.h @@ -12,7 +12,7 @@ #define STELA_LOG_GL_CONTEXT_CREATION(ver) \ log_debug("Attempting to create a %s context of version %d.%d (GLSL %d).", \ - (ver).api == STELA_GL_API ? "GL" : "GLES", (ver).major, (ver).minor, (ver).glsl_ver) + ((ver).api == STELA_GL_API) ? "GL" : "GLES", (ver).major, (ver).minor, (ver).glsl_ver) // https://github.com/haasn/libplacebo/blob/795600a44b03fcd52c055981a403ad60ee5d027a/demos/window_glfw.c#L194 #ifdef STELA_USE_GLES diff --git a/src/window.c b/src/window.c index f85f4fc..ad5ca4d 100644 --- a/src/window.c +++ b/src/window.c @@ -4,9 +4,10 @@ #include "window.h" #include "keys.h" -static bool nop_pointer_pos_callback(void *userdata, f64 x, f64 y) +static bool nop_pointer_callback(void *userdata, u8 type, f64 x, f64 y) { (void)userdata; + (void)type; (void)x; (void)y; return false; @@ -53,6 +54,14 @@ static bool nop_key_immediate_callback(void *userdata, u8 state, u16 button) return false; } +#ifdef STELA_DRAG_AND_DROP +static void nop_drag_and_drop_callback(void *userdata, str *path) +{ + (void)userdata; + (void)path; +} +#endif + static void window_default_close(struct stl_window *window) { window->closed = true; @@ -60,12 +69,15 @@ static void window_default_close(struct stl_window *window) void stl_window_init(struct stl_window *window) { - window->pointer_pos_callback = nop_pointer_pos_callback; + window->pointer_callback = nop_pointer_callback; window->touch_callback = nop_touch_callback; window->scroll_callback = nop_scroll_callback; window->mouse_button_callback = nop_mouse_button_callback; window->key_callback = nop_key_callback; window->key_immediate_callback = nop_key_immediate_callback; +#ifdef STELA_DRAG_AND_DROP + window->drag_and_drop_callback = nop_drag_and_drop_callback; +#endif window->close = window_default_close; #ifdef STELA_EVENT_BUFFER al_ring_buffer_init(&window->events, window->data, STELA_EVENTS_SIZE); @@ -121,18 +133,19 @@ void stl_window_send_resize_event(struct stl_window *window, u32 width, u32 heig #endif } -void stl_window_send_pointer_pos_event(struct stl_window *window, f64 x, f64 y) +void stl_window_send_pointer_event(struct stl_window *window, u8 type, f64 x, f64 y) { #ifdef STELA_EVENT_BUFFER GET_EVENT_CHUNK(STELA_EVENT_POINTER_POS); - ((f64 *)(&ptr[1]))[0] = x; - ((f64 *)(&ptr[9]))[0] = y; + ptr[1] = type; + ((f64 *)(&ptr[2]))[0] = x; + ((f64 *)(&ptr[10]))[0] = y; APPEND_EVENT_CHUNK(); #else #ifdef STELA_PAUSE window->pending_refresh |= #endif - window->pointer_pos_callback(window->userdata, x, y); + window->pointer_callback(window->userdata, type, x, y); #endif } @@ -234,6 +247,13 @@ bool stl_window_send_key_immediate_event(struct stl_window *window, u8 state, u1 return window->key_immediate_callback(window->userdata, state, button * (stl_key_to_mod(button) == STELA_MOD_NONE)); } +#ifdef STELA_DRAG_AND_DROP +void stl_window_send_drag_and_drop_event(struct stl_window *window, str *path) +{ + window->drag_and_drop_callback(window->userdata, path); +} +#endif + void stl_window_drop_modifiers(struct stl_window *window) { u32 *mods = stl_all_modifiers; @@ -282,12 +302,13 @@ bool stl_window_read_events(struct stl_window *window) op = *bc; switch (op) { case STELA_EVENT_POINTER_POS: { - f64 x = ((f64 *)(&bc[1]))[0]; - f64 y = ((f64 *)(&bc[9]))[0]; + u8 type = bc[1]; + f64 x = ((f64 *)(&bc[2]))[0]; + f64 y = ((f64 *)(&bc[10]))[0]; #ifdef STELA_PAUSE window->pending_refresh |= #endif - window->pointer_pos_callback(window->userdata, x, y); + window->pointer_callback(window->userdata, type, x, y); break; } case STELA_EVENT_TOUCH: { diff --git a/src/window.h b/src/window.h index 16b8f2e..66d4f38 100644 --- a/src/window.h +++ b/src/window.h @@ -36,6 +36,18 @@ #include "egl_common.h" #endif +// Drag window by clicking anywhere with MOUSE2. +#define STELA_MOUSE2_DRAG + +#ifdef STELA_WINDOW_WIN32 +#define STELA_DRAG_AND_DROP +#endif + +#define STELA_EVENT_SIZE 24u +#ifndef STELA_EVENTS_SIZE +#define STELA_EVENTS_SIZE (STELA_EVENT_SIZE * 64u) +#endif + #ifndef STELA_MIN_WIDTH #define STELA_MIN_WIDTH 16u #endif @@ -48,21 +60,6 @@ #define STELA_ICON_AT_RUNTIME #endif -// Drag window by clicking anywhere with MOUSE2. -#define STELA_MOUSE2_DRAG - -enum { - STELA_WINDOW_VSYNC = 1, - STELA_WINDOW_WALLPAPER = 1 << 1 -}; - -enum { - STELA_CURSOR_NORMAL = 0, - STELA_CURSOR_MOVE, - STELA_CURSOR_CROSSHAIR, - STELA_CURSOR_HIDDEN -}; - #define STELA_MAX_MONITOR_NAME 255u struct stl_monitor { @@ -73,10 +70,10 @@ struct stl_monitor { char name[STELA_MAX_MONITOR_NAME]; }; -#define STELA_EVENT_SIZE 24u -#ifndef STELA_EVENTS_SIZE -#define STELA_EVENTS_SIZE (STELA_EVENT_SIZE * 64u) -#endif +enum { + STELA_WINDOW_VSYNC = 1, + STELA_WINDOW_WALLPAPER = 1 << 1 +}; struct stl_window { u32 width; @@ -126,7 +123,7 @@ struct stl_window { void (*output_discovered_callback)(void *, struct stl_monitor *); void (*render_callback)(void *); void (*resize_callback)(void *, u32, u32); - bool (*pointer_pos_callback)(void *, f64, f64); + bool (*pointer_callback)(void *, u8, f64, f64); bool (*touch_callback)(void *, s32, u8, f64, f64); bool (*scroll_callback)(void *, f64); bool (*mouse_button_callback)(void *, u8, u8); @@ -136,6 +133,9 @@ struct stl_window { // - Return value decides if the event should "pass through" or not. // True = Consume, False = Passthrough. bool (*key_immediate_callback)(void *, u8, u16); +#ifdef STELA_DRAG_AND_DROP + void (*drag_and_drop_callback)(void *, str *); +#endif void (*should_close_callback)(void *); void *userdata; bool closed; @@ -184,6 +184,11 @@ static u8 stl_all_mouse_buttons[] = { AL_IGNORE_WARNING_END +enum { + STELA_POINTER_POS = 0, + STELA_POINTER_MOVE, +}; + enum { STELA_TOUCH_HOVER = 0, STELA_TOUCH_BEGAN, @@ -192,7 +197,14 @@ enum { STELA_TOUCH_CANCELLED }; -extern s32 stl_platform_main(u32 argc, str *argv); +enum { + STELA_CURSOR_NORMAL = 0, + STELA_CURSOR_MOVE, + STELA_CURSOR_CROSSHAIR, + STELA_CURSOR_HIDDEN +}; + +extern s32 stl_platform_main(u32 argc, str *argv, void *extra); f64 stl_scale_scroll(f64 v, f64 factor); void stl_set_output_discovered_callback(void (*output_discovered)(void *, struct stl_monitor *), void *userdata); @@ -209,15 +221,18 @@ void stl_global_close(void); struct stl_window *stl_window_create(void); void stl_window_send_render_event(struct stl_window *window); void stl_window_send_resize_event(struct stl_window *window, u32 width, u32 height); -void stl_window_send_pointer_pos_event(struct stl_window *window, f64 x, f64 y); +void stl_window_send_pointer_event(struct stl_window *window, u8 type, f64 x, f64 y); void stl_window_send_touch_event(struct stl_window *window, s32 index, u8 phase, f64 x, f64 y); void stl_window_send_scroll_event(struct stl_window *window, f64 y); void stl_window_send_mouse_button_event(struct stl_window *window, u8 state, u8 button); void stl_window_drop_mouse_buttons(struct stl_window *window); void stl_window_send_key_event(struct stl_window *window, u8 state, u16 button); bool stl_window_send_key_immediate_event(struct stl_window *window, u8 state, u16 button); -void stl_window_drop_modifiers(struct stl_window *window); +#ifdef STELA_DRAG_AND_DROP +void stl_window_send_drag_and_drop_event(struct stl_window *window, str *path); +#endif void stl_window_send_should_close_event(struct stl_window *window); +void stl_window_drop_modifiers(struct stl_window *window); #ifdef STELA_EVENT_BUFFER bool stl_window_read_events(struct stl_window *window); #endif diff --git a/src/window_glfw.c b/src/window_glfw.c index 32e0cfb..f42a99c 100644 --- a/src/window_glfw.c +++ b/src/window_glfw.c @@ -119,7 +119,7 @@ static void cursor_position_callback(GLFWwindow *window, f64 xpos, f64 ypos) struct stl_window_glfw *glfw = glfwGetWindowUserPointer(window); xpos *= glfw->scale_x; ypos *= glfw->scale_y; - stl_window_send_pointer_pos_event(&glfw->w, xpos, ypos); + stl_window_send_pointer_event(&glfw->w, STELA_POINTER_POS, xpos, ypos); } // @TODO: Test behavior on x11, Windows? @@ -178,7 +178,7 @@ static void key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, log_warn("Unmapped GLFW key pressed (%hu).", (u16)key); return; } - u8 mapped_state = action == GLFW_PRESS ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED; + u8 mapped_state = (action == GLFW_PRESS) ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED; stl_window_send_key_immediate_event(&glfw->w, mapped_state, (u16)key); stl_window_send_key_event(&glfw->w, mapped_state, (u16)key); } @@ -225,7 +225,7 @@ static bool window_glfw_create_window(struct stl_window *window, u32 width, u32 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); } log_debug("Attempting to create a %s context of version %d.%d (GLSL %d).", - stl_gl_vers[i].api == STELA_GL_API ? "GL" : "GLES", + (stl_gl_vers[i].api == STELA_GL_API) ? "GL" : "GLES", stl_gl_vers[i].major, stl_gl_vers[i].minor, stl_gl_vers[i].glsl_ver); #endif al_assert(width >= STELA_MIN_WIDTH && height >= STELA_MIN_HEIGHT); diff --git a/src/window_wayland.c b/src/window_wayland.c index 2517003..e019002 100644 --- a/src/window_wayland.c +++ b/src/window_wayland.c @@ -118,6 +118,7 @@ static void global_add(void *data, struct wl_registry *registry, u32 name, (void)data; if (al_strscmp(interface, "wl_output") == 0) { struct stl_monitor_wayland *monitor = al_alloc_object(struct stl_monitor_wayland); + monitor->wln = name; al_array_push(global.monitors, (struct stl_monitor *)monitor); monitor->output = (struct wl_output *)wl_registry_bind(registry, name, &wl_output_interface, ASSERT_MIN(4u, version)); wl_output_add_listener(monitor->output, &output_listener, monitor); @@ -128,7 +129,16 @@ static void global_remove(void *data, struct wl_registry *registry, u32 name) { (void)data; (void)registry; - (void)name; + // @TODO: Untested. + for (u32 i = 0; i < global.monitors.count; i++) { + struct stl_monitor_wayland *monitor = (struct stl_monitor_wayland *)al_array_at(global.monitors, i); + if (monitor->wln == name) { + if (monitor->output) wl_output_destroy(monitor->output); + al_free(monitor); + al_array_remove_at_iter(global.monitors, i); + break; + } + } } static const struct wl_registry_listener registry_listener = { @@ -302,7 +312,7 @@ bool stl_global_init(bool skip_graphics) if (!skip_graphics) { #if defined STELA_API_VULKAN - global.vk_extensions = (const char **)al_calloc(2, sizeof(const char *const *)); + global.vk_extensions = (const char **)al_calloc(2, sizeof(const char *)); global.vk_extensions[0] = "VK_KHR_surface"; global.vk_extensions[1] = "VK_KHR_wayland_surface"; #elif defined STELA_API_OPENGL @@ -499,7 +509,7 @@ static void pointer_frame(void *data, struct wl_pointer *pointer) struct stl_window_wayland *wl = (struct stl_window_wayland *)data; (void)pointer; if (wl->frame.pointer_x != FRAME_NO_VALUE && wl->frame.pointer_y != FRAME_NO_VALUE) { - stl_window_send_pointer_pos_event(&wl->w, wl->frame.pointer_x, wl->frame.pointer_y); + stl_window_send_pointer_event(&wl->w, STELA_POINTER_POS, wl->frame.pointer_x, wl->frame.pointer_y); wl->frame.pointer_x = FRAME_NO_VALUE; wl->frame.pointer_y = FRAME_NO_VALUE; } @@ -708,6 +718,7 @@ static void context_global_add(void *data, struct wl_registry *registry, u32 nam static void context_global_remove(void *data, struct wl_registry *registry, u32 name) { + // @TODO: Probably need to handle seat here. (void)data; (void)registry; (void)name; @@ -1299,10 +1310,10 @@ static VkResult window_wayland_vk_destroy_surface(VkInstance inst, VkSurfaceKHR return stl_vk_destroy_surface(inst, surface); } -static const char *const *window_wayland_vk_get_extensions(u32 *num) +static const char * const *window_wayland_vk_get_extensions(u32 *num) { *num = 2; - return global.vk_extensions; + return (const char * const *)global.vk_extensions; } #elif defined STELA_API_OPENGL static bool window_wayland_gl_loader_load(void *window) diff --git a/src/window_wayland.h b/src/window_wayland.h index ad0f572..db20187 100644 --- a/src/window_wayland.h +++ b/src/window_wayland.h @@ -47,6 +47,7 @@ struct stl_wayland_global { struct stl_monitor_wayland { struct stl_monitor m; + u32 wln; struct wl_output *output; struct zxdg_output_v1 *xdg_output; }; 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 diff --git a/src/window_x11.c b/src/window_x11.c index 17de7ae..6d9ef78 100644 --- a/src/window_x11.c +++ b/src/window_x11.c @@ -39,7 +39,7 @@ bool stl_global_init(bool skip_graphics) if (!skip_graphics) { #if defined STELA_API_VULKAN - global.vk_extensions = (const char **)al_calloc(2, sizeof(const char *const *)); + global.vk_extensions = (const char **)al_calloc(2, sizeof(const char *)); global.vk_extensions[0] = "VK_KHR_surface"; global.vk_extensions[1] = "VK_KHR_xlib_surface"; #elif defined STELA_API_OPENGL @@ -60,14 +60,19 @@ bool stl_global_init(bool skip_graphics) } global.cursors[STELA_CURSOR_NORMAL] = None; + // Only cursors also defined by XC_* seem to work on Xwayland. + // https://tronche.com/gui/x/xlib/appendix/b/ + global.cursors[STELA_CURSOR_CROSSHAIR] = XcursorLibraryLoadCursor(global.x11_d, "crosshair"); + global.cursors[STELA_CURSOR_MOVE] = XcursorLibraryLoadCursor(global.x11_d, "hand2"); char *cursor_theme = XcursorGetTheme(global.x11_d); s32 cursor_size = XcursorGetDefaultSize(global.x11_d); - XcursorImage *image; - if ((image = XcursorLibraryLoadImage("crosshair", cursor_theme, cursor_size))) { + XcursorImage *image = XcursorLibraryLoadImage("crosshair", cursor_theme, cursor_size); + if (image) { global.cursors[STELA_CURSOR_CROSSHAIR] = XcursorImageLoadCursor(global.x11_d, image); XcursorImageDestroy(image); } - if ((image = XcursorLibraryLoadImage("pointer", cursor_theme, cursor_size))) { + image = XcursorLibraryLoadImage("move", cursor_theme, cursor_size); + if (image) { global.cursors[STELA_CURSOR_MOVE] = XcursorImageLoadCursor(global.x11_d, image); XcursorImageDestroy(image); } @@ -535,7 +540,7 @@ static void handle_next_xevent(struct stl_window_x11 *xw) case KeyRelease: { KeySym sym = XkbKeycodeToKeysym(global.x11_d, xev.xkey.keycode, 0, 0);//(xev.xkey.state & ShiftMask) ? 1 : 0); if (sym != NoSymbol) { - u8 mapped_state = xev.type == KeyPress ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED; + u8 mapped_state = (xev.type == KeyPress) ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED; u16 mapped_key = keysym_to_key(sym); stl_window_send_key_immediate_event(&xw->w, mapped_state, mapped_key); stl_window_send_key_event(&xw->w, mapped_state, mapped_key); @@ -585,7 +590,7 @@ static void handle_next_xevent(struct stl_window_x11 *xw) } break; case MotionNotify: - stl_window_send_pointer_pos_event(&xw->w, (f64)xev.xmotion.x, (f64)xev.xmotion.y); + stl_window_send_pointer_event(&xw->w, STELA_POINTER_POS, (f64)xev.xmotion.x, (f64)xev.xmotion.y); break; case MapNotify: break; @@ -651,10 +656,10 @@ static VkResult window_x11_vk_destroy_surface(VkInstance inst, VkSurfaceKHR surf return stl_vk_destroy_surface(inst, surface); } -static const char *const *window_x11_vk_get_extensions(u32 *num) +static const char * const *window_x11_vk_get_extensions(u32 *num) { *num = 2; - return global.vk_extensions; + return (const char * const *)global.vk_extensions; } #elif defined STELA_API_OPENGL static bool window_x11_gl_loader_load(void *window) -- cgit v1.2.3-101-g0448