diff --git a/README.md b/README.md index d50e5ddb..742f8073 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,10 @@ information on what to include when reporting a bug. (#235,#439,#677,#845,#898) - Added `glfwRequestWindowAttention` function for requesting attention from the user (#732,#988) +- Added `glfwDragWindow` function for starting a drag operation on a window + (#987) +- Added `glfwResizeWindow` function for starting a resize operation on a window + (#923) - Added `glfwGetKeyScancode` function that allows retrieving platform dependent scancodes for keys (#830) - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 8606edc1..d4491e5e 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1015,6 +1015,15 @@ extern "C" { #define GLFW_EGL_CONTEXT_API 0x00036002 #define GLFW_OSMESA_CONTEXT_API 0x00036003 +#define GLFW_WINDOW_LEFT 0 +#define GLFW_WINDOW_TOP 1 +#define GLFW_WINDOW_RIGHT 2 +#define GLFW_WINDOW_BOTTOM 3 +#define GLFW_WINDOW_TOPLEFT 4 +#define GLFW_WINDOW_TOPRIGHT 5 +#define GLFW_WINDOW_BOTTOMLEFT 6 +#define GLFW_WINDOW_BOTTOMRIGHT 7 + /*! @defgroup shapes Standard cursor shapes * @brief Standard system cursor shapes. * @@ -3192,6 +3201,54 @@ 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. + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_drag + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwDragWindow(GLFWwindow* handle); + +/*! @brief Starts a resize operation with the specified window. + * + * This function starts a resize operation on one of the borders of the + * specified window. + * + * this function must be called from a pointer or touch event callback, + * otherwise it risks reacting to a different event. + * + * The borders are [GLFW_WINDOW_LEFT](@ref GLFW_GLFW_WINDOW_LEFT), + * [GLFW_WINDOW_TOP](@ref GLFW_WINDOW_TOP), + * [GLFW_WINDOW_RIGHT](@ref GLFW_WINDOW_RIGHT), + * [GLFW_WINDOW_BOTTOM](@ref GLFW_WINDOW_BOTTOM), + * [GLFW_WINDOW_TOPLEFT](@ref GLFW_WINDOW_TOPLEFT), + * [GLFW_WINDOW_TOPRIGHT](@ref GLFW_WINDOW_TOPRIGHT), + * [GLFW_WINDOW_BOTTOMLEFT](@ref GLFW_WINDOW_BOTTOMLEFT) and + * [GLFW_WINDOW_BOTTOMRIGHT](@ref GLFW_WINDOW_BOTTOMRIGHT). + * + * @param[in] window The window to start the resize operation. + * @param[in] border One of the window borders. + * + * @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. + * + * @sa @ref window_resize + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwResizeWindow(GLFWwindow* window, int border); + /*! @brief Requests user attention to the specified window. * * This function requests user attention to the specified window. On diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 7011a797..098e6cf3 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1146,6 +1146,15 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) [window->ns.object makeKeyAndOrderFront:nil]; } +void _glfwPlatformDragWindow(_GLFWwindow* window) +{ + [window->ns.object performWindowDragWithEvent:[NSApp currentEvent]]; +} + +void _glfwPlatformResizeWindow(_GLFWwindow* window, int border) +{ +} + void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/internal.h b/src/internal.h index c7c5bf8f..2e27d4f3 100644 --- a/src/internal.h +++ b/src/internal.h @@ -655,6 +655,8 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window); void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); +void _glfwPlatformDragWindow(_GLFWwindow* window); +void _glfwPlatformResizeWindow(_GLFWwindow* window, int border); int _glfwPlatformWindowFocused(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window); int _glfwPlatformWindowVisible(_GLFWwindow* window); diff --git a/src/null_window.c b/src/null_window.c index 6a54cfe5..b9f168da 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -217,6 +217,14 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) { } +void _glfwPlatformDragWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformResizeWindow(_GLFWwindow* window, int border) +{ +} + int _glfwPlatformWindowFocused(_GLFWwindow* window) { return GLFW_FALSE; diff --git a/src/win32_window.c b/src/win32_window.c index a0abca06..883afb2e 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -33,6 +33,7 @@ #include #include #include +#include #define _GLFW_KEY_INVALID -2 @@ -1656,6 +1657,48 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) SetFocus(window->win32.handle); } +void _glfwPlatformDragWindow(_GLFWwindow* window) +{ + ReleaseCapture(); + SendMessage(window->win32.handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); +} + +void _glfwPlatformResizeWindow(_GLFWwindow* window, int border) +{ + WPARAM wBorder; + switch (border) + { + case GLFW_WINDOW_LEFT: + wBorder = HTLEFT; + break; + case GLFW_WINDOW_TOP: + wBorder = HTTOP; + break; + case GLFW_WINDOW_RIGHT: + wBorder = HTRIGHT; + break; + case GLFW_WINDOW_BOTTOM: + wBorder = HTBOTTOM; + break; + case GLFW_WINDOW_TOPLEFT: + wBorder = HTTOPLEFT; + break; + case GLFW_WINDOW_TOPRIGHT: + wBorder = HTTOPRIGHT; + break; + case GLFW_WINDOW_BOTTOMLEFT: + wBorder = HTBOTTOMLEFT; + break; + case GLFW_WINDOW_BOTTOMRIGHT: + wBorder = HTBOTTOMRIGHT; + break; + default: + assert(GLFW_FALSE); + } + ReleaseCapture(); + SendMessage(window->win32.handle, WM_NCLBUTTONDOWN, wBorder, 0); +} + void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/window.c b/src/window.c index 2c98d4cb..dd30e859 100644 --- a/src/window.c +++ b/src/window.c @@ -801,6 +801,29 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* handle) _glfwPlatformFocusWindow(window); } +GLFWAPI void glfwDragWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformDragWindow(window); +} + +GLFWAPI void glfwResizeWindow(GLFWwindow* handle, int border) +{ + _GLFWwindow* window = (_GLFWwindow*)handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (border < GLFW_WINDOW_LEFT || border > GLFW_WINDOW_BOTTOMRIGHT) + return; + + _glfwPlatformResizeWindow(window, border); +} + GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/src/wl_window.c b/src/wl_window.c index 98a64659..1a7fffbc 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1215,6 +1215,49 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) "Wayland: Focusing a window requires user interaction"); } +void _glfwPlatformDragWindow(_GLFWwindow* window) +{ + wl_shell_surface_move(window->wl.shellSurface, _glfw.wl.seat, _glfw.wl.pointerSerial); +} + +void _glfwPlatformResizeWindow(_GLFWwindow* window, int border) +{ + int wlBorder; + switch (border) + { + case GLFW_WINDOW_LEFT: + wlBorder = WL_SHELL_SURFACE_RESIZE_LEFT; + break; + case GLFW_WINDOW_TOP: + wlBorder = WL_SHELL_SURFACE_RESIZE_TOP; + break; + case GLFW_WINDOW_RIGHT: + wlBorder = WL_SHELL_SURFACE_RESIZE_RIGHT; + break; + case GLFW_WINDOW_BOTTOM: + wlBorder = WL_SHELL_SURFACE_RESIZE_BOTTOM; + break; + case GLFW_WINDOW_TOPLEFT: + wlBorder = WL_SHELL_SURFACE_RESIZE_TOP_LEFT; + break; + case GLFW_WINDOW_TOPRIGHT: + wlBorder = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT; + break; + case GLFW_WINDOW_BOTTOMLEFT: + wlBorder = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT; + break; + case GLFW_WINDOW_BOTTOMRIGHT: + wlBorder = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; + break; + default: + assert(GLFW_FALSE); + } + wl_shell_surface_resize(window->wl.shellSurface, + _glfw.wl.seat, + _glfw.wl.pointerSerial, + wlBorder); +} + void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/x11_window.c b/src/x11_window.c index 19ed7372..1c573f83 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -43,6 +43,15 @@ #define _NET_WM_STATE_REMOVE 0 #define _NET_WM_STATE_ADD 1 #define _NET_WM_STATE_TOGGLE 2 +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 // Additional mouse button names for XButtonEvent #define Button6 6 @@ -2388,6 +2397,78 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } +void _glfwPlatformDragWindow(_GLFWwindow* window) +{ + int winXpos, winYpos; + double curXpos, curYpos; + XClientMessageEvent xclient; + memset(&xclient, 0, sizeof(XClientMessageEvent)); + XUngrabPointer(_glfw.x11.display, 0); + XFlush(_glfw.x11.display); + _glfwPlatformGetCursorPos(window, &curXpos, &curYpos); + _glfwPlatformGetWindowPos(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 _glfwPlatformResizeWindow(_GLFWwindow* window, int border) +{ + int winXpos, winYpos; + double curXpos, curYpos; + XClientMessageEvent xclient; + memset(&xclient, 0, sizeof(XClientMessageEvent)); + XUngrabPointer(_glfw.x11.display, 0); + XFlush(_glfw.x11.display); + _glfwPlatformGetCursorPos(window, &curXpos, &curYpos); + _glfwPlatformGetWindowPos(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; + switch (border) + { + case GLFW_WINDOW_LEFT: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_LEFT; + break; + case GLFW_WINDOW_TOP: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_TOP; + break; + case GLFW_WINDOW_RIGHT: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_RIGHT; + break; + case GLFW_WINDOW_BOTTOM: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_BOTTOM; + break; + case GLFW_WINDOW_TOPLEFT: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; + break; + case GLFW_WINDOW_TOPRIGHT: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + break; + case GLFW_WINDOW_BOTTOMLEFT: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + break; + case GLFW_WINDOW_BOTTOMRIGHT: + xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + break; + default: + assert(GLFW_FALSE); + } + xclient.data.l[3] = 0; + xclient.data.l[4] = 0; + XSendEvent(_glfw.x11.display, _glfw.x11.root, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient); +} + void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/tests/windows.c b/tests/windows.c index 5fed58f3..da67108b 100644 --- a/tests/windows.c +++ b/tests/windows.c @@ -89,6 +89,12 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, } } +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 i, ch; @@ -138,6 +144,7 @@ int main(int argc, char** argv) } glfwSetKeyCallback(windows[i], key_callback); + glfwSetMouseButtonCallback(windows[i], mouse_button_callback); glfwMakeContextCurrent(windows[i]); gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);