diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index e5b9b6c6..bdc2bebe 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -842,6 +842,12 @@ extern "C" { * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib). */ #define GLFW_FOCUS_ON_SHOW 0x0002000C + /*! @brief Window has titlebar window hint and attribute + * + * Window has titlebar [window hint](@ref GLFW_TITLEBAR_hint) and + * [window attribute](@ref GLFW_TITLEBAR_attrib). + */ +#define GLFW_TITLEBAR 0x0002000D /*! @brief Framebuffer bit depth hint. * @@ -1291,8 +1297,30 @@ typedef void (* GLFWerrorfun)(int,const char*); * * @ingroup window */ + typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); +/*! @brief The function pointer type for window titlebar hittest callbacks. + * + * This is the function pointer type for window titelebar hittest callbacks. + * A window titlebar hittest callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int xpos, int ypos, int* hit) + * @endcode + * + * @param[in] window The window that was moved. + * @param[in] xpos The x-coordinate of mouse, in screen coordinates. + * @param[in] ypos The y-coordinate of mouse, in screen coordinates. + * @param[out] hit 'true' or '1' if mouse hovering titlebar. + * + * @sa @ref window_pos + * @sa @ref glfwSetTitlebarHitTestCallback + * + * @ingroup window + */ +typedef void (*GLFWtitlebarhittestfun)(GLFWwindow*, int, int, int*); + + /*! @brief The function pointer type for window size callbacks. * * This is the function pointer type for window size callbacks. A window size @@ -3688,6 +3716,37 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); */ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); +/*! @brief Sets the titlebar hittest callback for the specified window. + * + * This function sets the titlebar hittest callback of the specified window, + * which is called when the mouse hoveres the window to ask client if it's + * hovering over custom titlebar area which needs to be handles as a native + * titlebar. The callback is provided with the x and y coordinates of the mouse + * cursor in screen coordinates. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int xpos, int ypos, int* hit) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWtitlebarhittestfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * + * @ingroup window + */ +GLFWAPI GLFWtitlebarhittestfun glfwSetTitlebarHitTestCallback(GLFWwindow* window, GLFWtitlebarhittestfun callback); + /*! @brief Sets the size callback for the specified window. * * This function sets the size callback of the specified window, which is diff --git a/premake5.lua b/premake5.lua index c1a9a134..65452ab4 100644 --- a/premake5.lua +++ b/premake5.lua @@ -64,6 +64,11 @@ project "GLFW" "_CRT_SECURE_NO_WARNINGS" } + links + { + "Dwmapi.lib" + } + filter "configurations:Debug" runtime "Debug" symbols "on" diff --git a/src/cocoa_window.m b/src/cocoa_window.m index c50bf21a..69adbc84 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1337,6 +1337,13 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) } // autoreleasepool } +void _glfwPlatformSetWindowTitlebar(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Window attribute setting not implemented yet"); +} + void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { diff --git a/src/internal.h b/src/internal.h index 6d7587c8..ba456b9a 100644 --- a/src/internal.h +++ b/src/internal.h @@ -260,6 +260,7 @@ struct _GLFWwndconfig GLFWbool resizable; GLFWbool visible; GLFWbool decorated; + GLFWbool titlebar; GLFWbool focused; GLFWbool autoIconify; GLFWbool floating; @@ -373,6 +374,7 @@ struct _GLFWwindow // Window settings and state GLFWbool resizable; GLFWbool decorated; + GLFWbool titlebar; GLFWbool autoIconify; GLFWbool floating; GLFWbool focusOnShow; @@ -400,6 +402,7 @@ struct _GLFWwindow struct { GLFWwindowposfun pos; + GLFWtitlebarhittestfun tbhittest; GLFWwindowsizefun size; GLFWwindowclosefun close; GLFWwindowrefreshfun refresh; @@ -673,6 +676,7 @@ int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowTitlebar(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); @@ -708,6 +712,7 @@ void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); +void _glfwInputTitleBarHitTest(_GLFWwindow* window, int posX, int posY, int* hit); void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale); diff --git a/src/null_window.c b/src/null_window.c index 936400d3..7129c9f9 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -185,6 +185,10 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) { } +void _glfwPlatformSetWindowTitlebar(_GLFWwindow* window, GLFWbool enabled) +{ +} + void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { } diff --git a/src/win32_window.c b/src/win32_window.c index 0ae0998a..fb4d1a25 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -35,6 +35,7 @@ #include #include #include +#include // Returns the window style for the specified window // @@ -508,6 +509,8 @@ static void releaseMonitor(_GLFWwindow* window) static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + static RECT border_thickness; + _GLFWwindow* window = GetPropW(hWnd, L"GLFW"); if (!window) { @@ -545,6 +548,48 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; } + + case WM_CREATE: + { + if (_glfw.hints.window.titlebar) + break; + + //find border thickness + SetRectEmpty(&border_thickness); + if (GetWindowLongPtr(hWnd, GWL_STYLE) & WS_THICKFRAME) + { + AdjustWindowRectEx(&border_thickness, GetWindowLongPtr(hWnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL); + border_thickness.left *= -1; + border_thickness.top *= -1; + } + else// if (GetWindowLongPtr(hWnd, GWL_STYLE) & WS_BORDER) + { + SetRect(&border_thickness, 4, 4, 4, 4); + } + + MARGINS margins = { 0 }; + DwmExtendFrameIntoClientArea(hWnd, &margins); + SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + + break; + } + + case WM_ACTIVATE: + { + if (_glfw.hints.window.titlebar) + break; + + // Extend the frame into the client area. + MARGINS margins = { 0 }; + auto hr = DwmExtendFrameIntoClientArea(hWnd, &margins); + + if (!SUCCEEDED(hr)) + { + // Handle the error. + } + + break; + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -951,6 +996,16 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; } + case WM_NCCALCSIZE: + { + if (window->titlebar) + break; + + if (lParam) + return 0; + + break; + } case WM_SIZE: { const GLFWbool iconified = wParam == SIZE_MINIMIZED; @@ -1182,6 +1237,61 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, DragFinish(drop); return 0; } + + case WM_ACTIVATE: + { + if (window->titlebar) + break; + + // Extend the frame into the client area. + MARGINS margins = { 0 }; + auto hr = DwmExtendFrameIntoClientArea(hWnd, &margins); + + if (!SUCCEEDED(hr)) + { + // Handle the error. + } + + break; + } + case WM_NCHITTEST: + { + if (window->titlebar) + break; + + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ScreenToClient(hWnd, &pt); + RECT rc; + GetClientRect(hWnd, &rc); + + int titlebarHittest = 0; + _glfwInputTitleBarHitTest(window, pt.x, pt.y, &titlebarHittest); + + if (titlebarHittest) + { + return HTCAPTION; + } + else + { + enum { left = 1, top = 2, right = 4, bottom = 8 }; + int hit = 0; + if (pt.x < border_thickness.left) hit |= left; + if (pt.x > rc.right - border_thickness.right) hit |= right; + if (pt.y < border_thickness.top) hit |= top; + if (pt.y > rc.bottom - border_thickness.bottom) hit |= bottom; + + if (hit & top && hit & left) return HTTOPLEFT; + if (hit & top && hit & right) return HTTOPRIGHT; + if (hit & bottom && hit & left) return HTBOTTOMLEFT; + if (hit & bottom && hit & right) return HTBOTTOMRIGHT; + if (hit & left) return HTLEFT; + if (hit & top) return HTTOP; + if (hit & right) return HTRIGHT; + if (hit & bottom) return HTBOTTOM; + + return HTCLIENT; + } + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -1828,6 +1938,11 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) updateWindowStyles(window); } +void _glfwPlatformSetWindowTitlebar(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; diff --git a/src/window.c b/src/window.c index bb5ba956..74b9a6ea 100644 --- a/src/window.c +++ b/src/window.c @@ -86,6 +86,14 @@ void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) window->callbacks.size((GLFWwindow*) window, width, height); } +// Notifies shared code that mouse hittest needs to be resolved +// +void _glfwInputTitleBarHitTest(_GLFWwindow* window, int posX, int posY, int* hit) +{ + if (window->callbacks.tbhittest) + window->callbacks.tbhittest((GLFWwindow*)window, posX, posY, hit); +} + // Notifies shared code that a window has been iconified or restored // void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) @@ -200,6 +208,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->monitor = (_GLFWmonitor*) monitor; window->resizable = wndconfig.resizable; window->decorated = wndconfig.decorated; + window->titlebar = wndconfig.titlebar; window->autoIconify = wndconfig.autoIconify; window->floating = wndconfig.floating; window->focusOnShow = wndconfig.focusOnShow; @@ -262,6 +271,7 @@ void glfwDefaultWindowHints(void) _glfw.hints.window.resizable = GLFW_TRUE; _glfw.hints.window.visible = GLFW_TRUE; _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.titlebar = GLFW_TRUE; _glfw.hints.window.focused = GLFW_TRUE; _glfw.hints.window.autoIconify = GLFW_TRUE; _glfw.hints.window.centerCursor = GLFW_TRUE; @@ -345,6 +355,9 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_DECORATED: _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_TITLEBAR: + _glfw.hints.window.titlebar = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_FOCUSED: _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; return; @@ -828,6 +841,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->resizable; case GLFW_DECORATED: return window->decorated; + case GLFW_TITLEBAR: + return window->titlebar; case GLFW_FLOATING: return window->floating; case GLFW_AUTO_ICONIFY: @@ -889,6 +904,15 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) if (!window->monitor) _glfwPlatformSetWindowDecorated(window, value); } + else if (attrib == GLFW_TITLEBAR) + { + if (window->titlebar == value) + return; + + window->titlebar = value; + if (!window->monitor) + _glfwPlatformSetWindowTitlebar(window, value); + } else if (attrib == GLFW_FLOATING) { if (window->floating == value) @@ -981,6 +1005,16 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, return cbfun; } +GLFWAPI GLFWtitlebarhittestfun glfwSetTitlebarHitTestCallback(GLFWwindow* handle, GLFWtitlebarhittestfun tbhtfun) +{ + _GLFWwindow* window = (_GLFWwindow*)handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.tbhittest, tbhtfun); + return tbhtfun; +} + GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, GLFWwindowsizefun cbfun) { diff --git a/src/wl_window.c b/src/wl_window.c index c8dde30a..1d2012a6 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1111,6 +1111,13 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) } } +void _glfwPlatformSetWindowTitlebar(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { // TODO diff --git a/src/x11_window.c b/src/x11_window.c index d6e6bf37..5838ddf9 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2658,6 +2658,13 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) sizeof(hints) / sizeof(long)); } +void _glfwPlatformSetWindowTitlebar(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Window attribute setting not implemented yet"); +} + void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)