diff --git a/README.md b/README.md index 3bb5af95..f34c6e88 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ information on what to include when reporting a bug. ## Changelog + - Added `glfwDragWindow` function for starting a drag operation on a window - Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958) - Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to diff --git a/examples/windows.c b/examples/windows.c index 1589ffbf..a81196ce 100644 --- a/examples/windows.c +++ b/examples/windows.c @@ -31,6 +31,12 @@ #include #include +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods){ + if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS){ + glfwDragWindow(window); + } +} + int main(int argc, char** argv) { int xpos, ypos, height; @@ -78,6 +84,7 @@ int main(int argc, char** argv) } glfwSetInputMode(windows[i], GLFW_STICKY_KEYS, GLFW_TRUE); + glfwSetMouseButtonCallback(windows[i], mouse_button_callback); glfwMakeContextCurrent(windows[i]); gladLoadGL(glfwGetProcAddress); diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 5e6fad42..8495ce3b 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -3873,6 +3873,23 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); */ GLFWAPI void glfwFocusWindow(GLFWwindow* window); +/*! @brief Starts drag operation to the specified window. + * + * This function starts the drag operation of the specified window. + * + * @param[in] window The window to start the dragging operation. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @since Added in version 3.4. + * + * @ingroup window + */ +GLFWAPI void glfwDragWindow(GLFWwindow* handle); + /*! @brief Requests user attention to the specified window. * * This function requests user attention to the specified window. On diff --git a/src/cocoa_init.m b/src/cocoa_init.m index b3831df1..8ff37bed 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -544,6 +544,7 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) _glfwHideWindowCocoa, _glfwRequestWindowAttentionCocoa, _glfwFocusWindowCocoa, + _glfwDragWindowCocoa, _glfwSetWindowMonitorCocoa, _glfwWindowFocusedCocoa, _glfwWindowIconifiedCocoa, diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 9f7d191d..320a96ca 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -234,6 +234,7 @@ void _glfwShowWindowCocoa(_GLFWwindow* window); void _glfwHideWindowCocoa(_GLFWwindow* window); void _glfwRequestWindowAttentionCocoa(_GLFWwindow* window); void _glfwFocusWindowCocoa(_GLFWwindow* window); +void _glfwDragWindowCocoa(_GLFWwindow* window); void _glfwSetWindowMonitorCocoa(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); GLFWbool _glfwWindowFocusedCocoa(_GLFWwindow* window); GLFWbool _glfwWindowIconifiedCocoa(_GLFWwindow* window); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 9d794940..94f9ec59 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1241,6 +1241,11 @@ void _glfwFocusWindowCocoa(_GLFWwindow* window) } // autoreleasepool } +void _glfwDragWindowCocoa(_GLFWwindow* window) +{ + // TODO +} + void _glfwSetWindowMonitorCocoa(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/internal.h b/src/internal.h index fe0369aa..53da04e8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -725,6 +725,7 @@ struct _GLFWplatform void (*hideWindow)(_GLFWwindow*); void (*requestWindowAttention)(_GLFWwindow*); void (*focusWindow)(_GLFWwindow*); + void (*dragWindow)(_GLFWwindow*); void (*setWindowMonitor)(_GLFWwindow*,_GLFWmonitor*,int,int,int,int,int); GLFWbool (*windowFocused)(_GLFWwindow*); GLFWbool (*windowIconified)(_GLFWwindow*); diff --git a/src/null_init.c b/src/null_init.c index 34ddc04a..b7b818b1 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -88,6 +88,7 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) _glfwHideWindowNull, _glfwRequestWindowAttentionNull, _glfwFocusWindowNull, + _glfwDragWindowNull, _glfwSetWindowMonitorNull, _glfwWindowFocusedNull, _glfwWindowIconifiedNull, diff --git a/src/null_platform.h b/src/null_platform.h index fb9374b4..801eb032 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -241,6 +241,7 @@ void _glfwShowWindowNull(_GLFWwindow* window); void _glfwRequestWindowAttentionNull(_GLFWwindow* window); void _glfwHideWindowNull(_GLFWwindow* window); void _glfwFocusWindowNull(_GLFWwindow* window); +void _glfwDragWindowNull(_GLFWwindow* window); GLFWbool _glfwWindowFocusedNull(_GLFWwindow* window); GLFWbool _glfwWindowIconifiedNull(_GLFWwindow* window); GLFWbool _glfwWindowVisibleNull(_GLFWwindow* window); diff --git a/src/null_window.c b/src/null_window.c index c2aafbad..fb56d315 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -468,6 +468,11 @@ void _glfwFocusWindowNull(_GLFWwindow* window) _glfwInputWindowFocus(window, GLFW_TRUE); } +void _glfwDragWindowNull(_GLFWwindow* window) +{ + // TODO +} + GLFWbool _glfwWindowFocusedNull(_GLFWwindow* window) { return _glfw.null.focusedWindow == window; diff --git a/src/win32_init.c b/src/win32_init.c index 739c9c33..9b59ad7e 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -691,6 +691,7 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) _glfwHideWindowWin32, _glfwRequestWindowAttentionWin32, _glfwFocusWindowWin32, + _glfwDragWindowWin32, _glfwSetWindowMonitorWin32, _glfwWindowFocusedWin32, _glfwWindowIconifiedWin32, diff --git a/src/win32_platform.h b/src/win32_platform.h index 1db31584..bbadb5bd 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -563,6 +563,7 @@ void _glfwShowWindowWin32(_GLFWwindow* window); void _glfwHideWindowWin32(_GLFWwindow* window); void _glfwRequestWindowAttentionWin32(_GLFWwindow* window); void _glfwFocusWindowWin32(_GLFWwindow* window); +void _glfwDragWindowWin32(_GLFWwindow* window); void _glfwSetWindowMonitorWin32(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); GLFWbool _glfwWindowFocusedWin32(_GLFWwindow* window); GLFWbool _glfwWindowIconifiedWin32(_GLFWwindow* window); diff --git a/src/win32_window.c b/src/win32_window.c index db3bed6e..a9a2ed38 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1792,6 +1792,13 @@ void _glfwFocusWindowWin32(_GLFWwindow* window) SetFocus(window->win32.handle); } +void _glfwDragWindowWin32(_GLFWwindow* window) +{ + ReleaseCapture(); + SendMessage(window->win32.handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, getKeyMods()); +} + void _glfwSetWindowMonitorWin32(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/window.c b/src/window.c index 1117df2f..4a94278a 100644 --- a/src/window.c +++ b/src/window.c @@ -843,6 +843,16 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* handle) _glfw.platform.focusWindow(window); } +GLFWAPI void glfwDragWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfw.platform.dragWindow(window); +} + GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/src/wl_init.c b/src/wl_init.c index a4692b52..bb174c68 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -462,6 +462,7 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) _glfwHideWindowWayland, _glfwRequestWindowAttentionWayland, _glfwFocusWindowWayland, + _glfwDragWindowWayland, _glfwSetWindowMonitorWayland, _glfwWindowFocusedWayland, _glfwWindowIconifiedWayland, diff --git a/src/wl_platform.h b/src/wl_platform.h index d00e28fe..64a6b2f8 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -629,6 +629,7 @@ void _glfwShowWindowWayland(_GLFWwindow* window); void _glfwHideWindowWayland(_GLFWwindow* window); void _glfwRequestWindowAttentionWayland(_GLFWwindow* window); void _glfwFocusWindowWayland(_GLFWwindow* window); +void _glfwDragWindowWayland(_GLFWwindow* window); void _glfwSetWindowMonitorWayland(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); GLFWbool _glfwWindowFocusedWayland(_GLFWwindow* window); GLFWbool _glfwWindowIconifiedWayland(_GLFWwindow* window); diff --git a/src/wl_window.c b/src/wl_window.c index 76c6a763..7dc58926 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1310,6 +1310,14 @@ static void pointerHandleLeave(void* userData, _glfw.wl.pointerFocus = NULL; _glfw.wl.cursorPreviousName = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); + + // Would like to silently set mouse button state to undefined without triggering a mouse button release event + // See https://gitlab.freedesktop.org/wayland/wayland/-/issues/280 + // But many underlying systems will keep their own state and only use GLFW events to keep it up to date + // So realistically, they NEED a mouse button event, even if wayland spec would like it to be implicitly undefined + for (int btn = 0; btn < GLFW_MOUSE_BUTTON_LAST+1; btn++) + if (window->mouseButtons[btn] != GLFW_RELEASE) + _glfwInputMouseClick(window, btn, GLFW_RELEASE, 0); } static void pointerHandleMotion(void* userData, @@ -1659,6 +1667,14 @@ static void keyboardHandleLeave(void* userData, _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = NULL; _glfwInputWindowFocus(window, GLFW_FALSE); + + // Would like to silently set key state to undefined without triggering a key release event + // See https://gitlab.freedesktop.org/wayland/wayland/-/issues/280 + // But many underlying systems will keep their own state and only use GLFW events to keep it up to date + // So realistically, they NEED a key event, even if wayland spec would like it to be implicitly undefined + for (int key = 0; key < GLFW_KEY_LAST+1; key++) + if (window->keys[key] != GLFW_RELEASE) + _glfwInputKey(window, key, _glfwGetKeyScancodeWayland(key), GLFW_RELEASE, 0); } static void keyboardHandleKey(void* userData, @@ -2357,6 +2373,11 @@ void _glfwFocusWindowWayland(_GLFWwindow* window) "Wayland: The platform does not support setting the input focus"); } +void _glfwDragWindowWayland(_GLFWwindow* window) +{ + xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.serial); +} + void _glfwSetWindowMonitorWayland(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/x11_init.c b/src/x11_init.c index c90b593c..c38a9329 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1223,6 +1223,7 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) _glfwHideWindowX11, _glfwRequestWindowAttentionX11, _glfwFocusWindowX11, + _glfwDragWindowX11, _glfwSetWindowMonitorX11, _glfwWindowFocusedX11, _glfwWindowIconifiedX11, diff --git a/src/x11_platform.h b/src/x11_platform.h index cdea3957..1bf5059f 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -921,6 +921,7 @@ void _glfwShowWindowX11(_GLFWwindow* window); void _glfwHideWindowX11(_GLFWwindow* window); void _glfwRequestWindowAttentionX11(_GLFWwindow* window); void _glfwFocusWindowX11(_GLFWwindow* window); +void _glfwDragWindowX11(_GLFWwindow* window); void _glfwSetWindowMonitorX11(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); GLFWbool _glfwWindowFocusedX11(_GLFWwindow* window); GLFWbool _glfwWindowIconifiedX11(_GLFWwindow* window); diff --git a/src/x11_window.c b/src/x11_window.c index 3c76d3a9..94b76255 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -45,6 +45,7 @@ #define _NET_WM_STATE_REMOVE 0 #define _NET_WM_STATE_ADD 1 #define _NET_WM_STATE_TOGGLE 2 +#define _NET_WM_MOVERESIZE_MOVE 8 // Additional mouse button names for XButtonEvent #define Button6 6 @@ -2464,6 +2465,28 @@ void _glfwFocusWindowX11(_GLFWwindow* window) XFlush(_glfw.x11.display); } +void _glfwDragWindowX11(_GLFWwindow* window) +{ + int winXpos, winYpos; + double curXpos, curYpos; + XClientMessageEvent xclient; + memset(&xclient, 0, sizeof(XClientMessageEvent)); + XUngrabPointer(_glfw.x11.display, 0); + XFlush(_glfw.x11.display); + _glfwGetCursorPosX11(window, &curXpos, &curYpos); + _glfwGetWindowPosX11(window, &winXpos, &winYpos); + xclient.type = ClientMessage; + xclient.window = window->x11.handle; + xclient.message_type = XInternAtom(_glfw.x11.display, "_NET_WM_MOVERESIZE", False); + xclient.format = 32; + xclient.data.l[0] = winXpos + curXpos; + xclient.data.l[1] = winYpos + curYpos; + xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE; + xclient.data.l[3] = 0; + xclient.data.l[4] = 0; + XSendEvent(_glfw.x11.display, _glfw.x11.root, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient); +} + void _glfwSetWindowMonitorX11(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos,