diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6741d82f..038ec391 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -279,6 +279,7 @@ video tutorials. - Jonas Ådahl - Lasse Öörni - Leonard König + - Robbin Marcus - All the unmentioned and anonymous contributors in the GLFW community, for bug reports, patches, feedback, testing and encouragement diff --git a/README.md b/README.md index 886ca66f..ea5b60ac 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,9 @@ information on what to include when reporting a bug. ## Changelog + - Added `glfwSetWindowCaptionArea` to define caption for borderless windows (#1420) + - Added `glfwSetWindowResizeBorderSize` to set the resize area on borders for + resizing borderless windows (#1420) - 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/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 5e6fad42..2b5d0e57 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1158,6 +1158,14 @@ 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 #define GLFW_ANGLE_PLATFORM_TYPE_NONE 0x00037001 #define GLFW_ANGLE_PLATFORM_TYPE_OPENGL 0x00037002 #define GLFW_ANGLE_PLATFORM_TYPE_OPENGLES 0x00037003 @@ -3873,6 +3881,89 @@ 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* window); + +/*! @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 Sets the caption area for the specified window. + * + * This function sets the rectangle for the caption to drag the undecorated window. + * + * @param[in] window The window to set the caption area for. + * @param[in] offsetX The x offset from the top left of the window. + * @param[in] offsetY The y offset from the top left of the window. + * @param[in] sizeX The x size of the caption area. + * @param[in] sizeY The y size of the caption area. + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_caption_area + * + * @since Added in version 3.4. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowCaptionArea(GLFWwindow* window, int offsetX, int offsetY, int sizeX, int sizeY); + +/*! @brief Sets the resize border size for the specified window. + * + * This function sets the size of border where to start the resize operation. + * + * @param[in] window The window to set the caption area for. + * @param[in] size The size of the border. + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_border_size + * + * @since Added in version 3.4. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowResizeBorderSize(GLFWwindow* window, int size); + /*! @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..a1fdad78 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -544,6 +544,8 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) _glfwHideWindowCocoa, _glfwRequestWindowAttentionCocoa, _glfwFocusWindowCocoa, + _glfwDragWindowCocoa, + _glfwResizeWindowCocoa, _glfwSetWindowMonitorCocoa, _glfwWindowFocusedCocoa, _glfwWindowIconifiedCocoa, diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 9f7d191d..2fc05278 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -234,6 +234,8 @@ void _glfwShowWindowCocoa(_GLFWwindow* window); void _glfwHideWindowCocoa(_GLFWwindow* window); void _glfwRequestWindowAttentionCocoa(_GLFWwindow* window); void _glfwFocusWindowCocoa(_GLFWwindow* window); +void _glfwDragWindowCocoa(_GLFWwindow* window); +void _glfwResizeWindowCocoa(_GLFWwindow* window, int border); 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..75482762 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1241,6 +1241,15 @@ void _glfwFocusWindowCocoa(_GLFWwindow* window) } // autoreleasepool } +void _glfwDragWindowCocoa(_GLFWwindow* window) +{ + [window->ns.object performWindowDragWithEvent:[NSApp currentEvent]]; +} + +void _glfwResizeWindowCocoa(_GLFWwindow* window, int border) +{ +} + void _glfwSetWindowMonitorCocoa(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/internal.h b/src/internal.h index fe0369aa..e43eda38 100644 --- a/src/internal.h +++ b/src/internal.h @@ -542,6 +542,11 @@ struct _GLFWwindow int maxwidth, maxheight; int numer, denom; + // Caption for undecorated window dragging functionality + int captionOffsetX, captionOffsetY; + int captionSizeX, captionSizeY; + int resizeBorderSize; + GLFWbool stickyKeys; GLFWbool stickyMouseButtons; GLFWbool lockKeyMods; @@ -725,6 +730,8 @@ struct _GLFWplatform void (*hideWindow)(_GLFWwindow*); void (*requestWindowAttention)(_GLFWwindow*); void (*focusWindow)(_GLFWwindow*); + void (*dragWindow)(_GLFWwindow*); + void (*resizeWindow)(_GLFWwindow*,int); 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..f4b61ac2 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -88,6 +88,8 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) _glfwHideWindowNull, _glfwRequestWindowAttentionNull, _glfwFocusWindowNull, + _glfwDragWindowNull, + _glfwResizeWindowNull, _glfwSetWindowMonitorNull, _glfwWindowFocusedNull, _glfwWindowIconifiedNull, diff --git a/src/null_platform.h b/src/null_platform.h index fb9374b4..bf518d55 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -241,6 +241,8 @@ void _glfwShowWindowNull(_GLFWwindow* window); void _glfwRequestWindowAttentionNull(_GLFWwindow* window); void _glfwHideWindowNull(_GLFWwindow* window); void _glfwFocusWindowNull(_GLFWwindow* window); +void _glfwDragWindowNull(_GLFWwindow* window); +void _glfwResizeWindowNull(_GLFWwindow* window, int border); 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..6b89f262 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -468,6 +468,19 @@ void _glfwFocusWindowNull(_GLFWwindow* window) _glfwInputWindowFocus(window, GLFW_TRUE); } +void _glfwDragWindowNull(_GLFWwindow* window) +{ +} + +void _glfwResizeWindowNull(_GLFWwindow* window, int border) +{ +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return _glfw.null.focusedWindow == window; +} + GLFWbool _glfwWindowFocusedNull(_GLFWwindow* window) { return _glfw.null.focusedWindow == window; diff --git a/src/win32_init.c b/src/win32_init.c index 4cb01adb..1521f96b 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -652,6 +652,8 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) _glfwHideWindowWin32, _glfwRequestWindowAttentionWin32, _glfwFocusWindowWin32, + _glfwDragWindowWin32, + _glfwResizeWindowWin32, _glfwSetWindowMonitorWin32, _glfwWindowFocusedWin32, _glfwWindowIconifiedWin32, diff --git a/src/win32_platform.h b/src/win32_platform.h index 82b34bb9..dc3fe50d 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -559,6 +559,8 @@ void _glfwShowWindowWin32(_GLFWwindow* window); void _glfwHideWindowWin32(_GLFWwindow* window); void _glfwRequestWindowAttentionWin32(_GLFWwindow* window); void _glfwFocusWindowWin32(_GLFWwindow* window); +void _glfwDragWindowWin32(_GLFWwindow* window); +void _glfwResizeWindowWin32(_GLFWwindow* window, int border); 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 f7feb32d..287ddf3c 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -34,6 +34,7 @@ #include #include #include +#include // Returns the window style for the specified window // @@ -55,7 +56,12 @@ static DWORD getWindowStyle(const _GLFWwindow* window) style |= WS_MAXIMIZEBOX | WS_THICKFRAME; } else + { style |= WS_POPUP; + + if (window->resizable) + style |= WS_MAXIMIZEBOX | WS_THICKFRAME; + } } return style; @@ -523,6 +529,54 @@ static void maximizeWindowManually(_GLFWwindow* window) SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); } +static int HitTest(_GLFWwindow* window, RECT windowRect, POINT cursor, POINT border) +{ + const int captionOffsetX = window->captionOffsetX != GLFW_DONT_CARE ? window->captionOffsetX : 0; + const int captionOffsetY = window->captionOffsetY != GLFW_DONT_CARE ? window->captionOffsetY : 0; + const int captionSizeX = window->captionSizeX != GLFW_DONT_CARE ? window->captionSizeX : windowRect.right - windowRect.left; + const int captionSizeY = window->captionSizeY; + + const int clientAreaLeft = windowRect.left + border.x; + const int clientAreaRight = windowRect.right - border.x; + const int clientAreaTop = windowRect.top + border.y; + const int clientAreaBottom = windowRect.bottom - border.y; + + const int cursorInCaption = + cursor.x > clientAreaLeft + captionOffsetX && + cursor.x < clientAreaLeft + captionOffsetX + captionSizeX && + cursor.y > clientAreaTop + captionOffsetY && + cursor.y < clientAreaTop + captionOffsetY + captionSizeY; + + enum region_mask + { + client = 0, + left = 1, + right = 2, + top = 4, + bottom = 8, + }; + + const int result = + left * (cursor.x < clientAreaLeft) | + right * (cursor.x >= clientAreaRight) | + top * (cursor.y < clientAreaTop) | + bottom * (cursor.y >= clientAreaBottom); + + switch (result) + { + case left: return HTLEFT; + case right: return HTRIGHT; + case top: return HTTOP; + case bottom: return HTBOTTOM; + case top | left: return HTTOPLEFT; + case top | right: return HTTOPRIGHT; + case bottom | left: return HTBOTTOMLEFT; + case bottom | right: return HTBOTTOMRIGHT; + case client: return cursorInCaption ? HTCAPTION : HTCLIENT; + default: return HTNOWHERE; + } +} + // Window procedure for user-created windows // static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -1240,6 +1294,38 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l DragFinish(drop); return 0; } + + case WM_NCCALCSIZE: + { + if (wParam && !window->decorated) + return 0; + + break; + } + + case WM_NCHITTEST: + { + if (!window->decorated) + { + POINT cursor = + { + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam) + }; + POINT border = + { + window->resizable ? window->resizeBorderSize : 0, + window->resizable ? window->resizeBorderSize : 0, + }; + + RECT rect; + if (!GetWindowRect(hWnd, &rect)) + return HTNOWHERE; + + return HitTest(window, rect, cursor, border); + } + break; + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -1767,6 +1853,55 @@ void _glfwFocusWindowWin32(_GLFWwindow* window) SetFocus(window->win32.handle); } +void _glfwDragWindowWin32(_GLFWwindow* window) +{ + ReleaseCapture(); + SendMessage(window->win32.handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); + + // Mouse button will be released after drag, so prepare released state here already + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_1, GLFW_RELEASE, 0); +} + +void _glfwResizeWindowWin32(_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); + return; + } + ReleaseCapture(); + SendMessage(window->win32.handle, WM_NCLBUTTONDOWN, wBorder, 0); + + // Mouse button will be released after drag, so prepare released state here already + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_1, GLFW_RELEASE, 0); +} + void _glfwSetWindowMonitorWin32(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/window.c b/src/window.c index 1117df2f..5dd8c56e 100644 --- a/src/window.c +++ b/src/window.c @@ -243,6 +243,13 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->numer = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE; + window->captionOffsetX = GLFW_DONT_CARE; + window->captionOffsetY = GLFW_DONT_CARE; + window->captionSizeX = GLFW_DONT_CARE; + window->captionSizeY = 16; + + window->resizeBorderSize = 4; + if (!_glfw.platform.createWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { glfwDestroyWindow((GLFWwindow*) window); @@ -843,6 +850,58 @@ 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 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; + + _glfw.platform.resizeWindow(window, border); +} + +GLFWAPI void glfwSetWindowCaptionArea(GLFWwindow* handle, int offsetX, int offsetY, int sizeX, int sizeY) +{ + _GLFWwindow* window = (_GLFWwindow*)handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (offsetX < 0 || offsetY < 0 || sizeX < 1 || sizeY < 1) + return; + + window->captionOffsetX = offsetX; + window->captionOffsetY = offsetY; + window->captionSizeX = sizeX; + window->captionSizeY = sizeY; +} + +GLFWAPI void glfwSetWindowResizeBorderSize(GLFWwindow* handle, int size) +{ + _GLFWwindow* window = (_GLFWwindow*)handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (size < 1) + return; + + window->resizeBorderSize = size; +} + 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..b70c452b 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -462,6 +462,8 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) _glfwHideWindowWayland, _glfwRequestWindowAttentionWayland, _glfwFocusWindowWayland, + _glfwDragWindowWayland, + _glfwResizeWindowWayland, _glfwSetWindowMonitorWayland, _glfwWindowFocusedWayland, _glfwWindowIconifiedWayland, diff --git a/src/wl_platform.h b/src/wl_platform.h index d00e28fe..5083ae41 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -629,6 +629,8 @@ void _glfwShowWindowWayland(_GLFWwindow* window); void _glfwHideWindowWayland(_GLFWwindow* window); void _glfwRequestWindowAttentionWayland(_GLFWwindow* window); void _glfwFocusWindowWayland(_GLFWwindow* window); +void _glfwDragWindowWayland(_GLFWwindow* window); +void _glfwResizeWindowWayland(_GLFWwindow* window, int border); 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..597ab968 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "wayland-client-protocol.h" #include "xdg-shell-client-protocol.h" @@ -2357,6 +2358,49 @@ 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 _glfwResizeWindowWayland(_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); + } + xdg_toplevel_resize(window->wl.xdg.toplevel, + _glfw.wl.seat, + _glfw.wl.serial, + wlBorder); +} + void _glfwSetWindowMonitorWayland(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, diff --git a/src/x11_init.c b/src/x11_init.c index c90b593c..0985c8eb 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1223,6 +1223,8 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) _glfwHideWindowX11, _glfwRequestWindowAttentionX11, _glfwFocusWindowX11, + _glfwDragWindowX11, + _glfwResizeWindowX11, _glfwSetWindowMonitorX11, _glfwWindowFocusedX11, _glfwWindowIconifiedX11, diff --git a/src/x11_platform.h b/src/x11_platform.h index cdea3957..0ca53936 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -921,6 +921,8 @@ void _glfwShowWindowX11(_GLFWwindow* window); void _glfwHideWindowX11(_GLFWwindow* window); void _glfwRequestWindowAttentionX11(_GLFWwindow* window); void _glfwFocusWindowX11(_GLFWwindow* window); +void _glfwDragWindowX11(_GLFWwindow* window); +void _glfwResizeWindowX11(_GLFWwindow* window, int border); 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..230c4f01 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -45,6 +45,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 @@ -2464,6 +2473,78 @@ 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 _glfwResizeWindowX11(_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); + _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; + 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 _glfwSetWindowMonitorX11(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos,