diff --git a/README.md b/README.md index 62e6365b..b92d1646 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ information on what to include when reporting a bug. scancodes for keys (#830) - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for receiving window maximization events (#778) +- Added `glfwSetWindowAttrib` function for changing window attributes (#537) - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#281,#850) - Added definition of `GLAPIENTRY` to public header - Bugfix: Calling `glfwMaximizeWindow` on a full screen window was not ignored diff --git a/docs/news.dox b/docs/news.dox index ed4555bb..b262ffee 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -16,6 +16,15 @@ GLFW now supports querying the platform dependent scancode of any key with @ref glfwGetKeyScancode. +@subsection news_33_setwindowattrib Support for updating window attributes + +GLFW now supports changing the [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), +[GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), +[GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and +[GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) attributes for existing +windows with @ref glfwSetWindowAttrib. + + @subsection news_33_moltenvk Support for Vulkan on macOS via MoltenVK GLFW now supports the `VK_MVK_macos_surface` window surface creation extension diff --git a/docs/window.dox b/docs/window.dox index 8f7f85ac..67fe81d8 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -980,10 +980,10 @@ the window or framebuffer is resized. @subsection window_attribs Window attributes Windows have a number of attributes that can be returned using @ref -glfwGetWindowAttrib. Some reflect state that may change during the lifetime of -the window, while others reflect the corresponding hints and are fixed at the -time of creation. Some are related to the actual window and others to its -context. +glfwGetWindowAttrib. Some reflect state that may change as a result of user +interaction, (e.g. whether it has input focus), while others reflect inherent +properties of the window (e.g. what kind of border it has). Some are related to +the window and others to its OpenGL or OpenGL ES context. @code if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) @@ -992,6 +992,17 @@ if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) } @endcode +The [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), +[GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), +[GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and +[GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) window attributes can be +changed with @ref glfwSetWindowAttrib. + +@code +glfwSetWindowAttrib(window, GLFW_RESIZABLE, GLFW_FALSE); +@endcode + + @subsubsection window_attribs_wnd Window related attributes @@ -1013,18 +1024,27 @@ window_hide for details. @anchor GLFW_RESIZABLE_attrib __GLFW_RESIZABLE__ indicates whether the specified window is resizable _by the -user_. This is set before creation with the -[GLFW_RESIZABLE](@ref GLFW_RESIZABLE_hint) window hint. +user_. This can be set before creation with the +[GLFW_RESIZABLE](@ref GLFW_RESIZABLE_hint) window hint or after with @ref +glfwSetWindowAttrib. @anchor GLFW_DECORATED_attrib -__GLFW_DECORATED__ indicates whether the specified window has decorations such as -a border, a close widget, etc. This is set before creation with the -[GLFW_DECORATED](@ref GLFW_DECORATED_hint) window hint. +__GLFW_DECORATED__ indicates whether the specified window has decorations such +as a border, a close widget, etc. This can be set before creation with the +[GLFW_DECORATED](@ref GLFW_DECORATED_hint) window hint or after with @ref +glfwSetWindowAttrib. + +@anchor GLFW_AUTO_ICONIFY_attrib +__GLFW_AUTO_ICONIFY__ indicates whether the specified full screen window is +iconified on focus loss, a close widget, etc. This can be set before creation +with the [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_hint) window hint or after +with @ref glfwSetWindowAttrib. @anchor GLFW_FLOATING_attrib __GLFW_FLOATING__ indicates whether the specified window is floating, also -called topmost or always-on-top. This is controlled by the -[GLFW_FLOATING](@ref GLFW_FLOATING_hint) window hint. +called topmost or always-on-top. This can be set before creation with the +[GLFW_FLOATING](@ref GLFW_FLOATING_hint) window hint or after with @ref +glfwSetWindowAttrib. @subsubsection window_attribs_ctx Context related attributes diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 959d0c02..310b5e49 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -663,9 +663,10 @@ extern "C" { * [window attribute](@ref GLFW_DECORATED_attrib). */ #define GLFW_DECORATED 0x00020005 -/*! @brief Window auto-iconification window hint +/*! @brief Window auto-iconification window hint and attribute * - * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint). + * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) or + * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). */ #define GLFW_AUTO_ICONIFY 0x00020006 /*! @brief Window decoration window hint and attribute @@ -2710,6 +2711,7 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs + * @sa @ref glfwSetWindowAttrib * * @since Added in version 3.0. Replaces `glfwGetWindowParam` and * `glfwGetGLVersion`. @@ -2718,6 +2720,42 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int */ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); +/*! @brief Sets an attribute of the specified window. + * + * This function sets the value of an attribute of the specified window. + * + * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), + * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), + * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and + * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib). + * + * Some of these attributes are ignored for full screen windows. The new + * value will take effect if the window is later made windowed. + * + * Some of these attributes are ignored for windowed mode windows. The new + * value will take effect if the window is later made full screen. + * + * @param[in] window The window to set the attribute for. + * @param[in] attrib A supported window attribute. + * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark Calling @ref glfwGetWindowAttrib will always return the latest + * value, even if that value is ignored by the current mode of the window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwGetWindowAttrib + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); + /*! @brief Sets the user pointer of the specified window. * * This function sets the user-defined pointer of the specified window. The diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 93f1a203..4d7e51bb 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1385,6 +1385,25 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return [window->ns.object isZoomed]; } +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + [window->ns.object setStyleMask:getStyleMask(window)]; +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + [window->ns.object setStyleMask:getStyleMask(window)]; + [window->ns.object makeFirstResponder:window->ns.view]; +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + if (enabled) + [window->ns.object setLevel:NSFloatingWindowLevel]; + else + [window->ns.object setLevel:NSNormalWindowLevel]; +} + void _glfwPlatformPollEvents(void) { for (;;) diff --git a/src/internal.h b/src/internal.h index 42e8d539..1aae35c9 100644 --- a/src/internal.h +++ b/src/internal.h @@ -757,6 +757,21 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window); */ int _glfwPlatformWindowMaximized(_GLFWwindow* window); +/*! @brief Sets whether the window is resizable by the user. + * @ingroup platform + */ +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); + +/*! @brief Sets whether the window is decorated. + * @ingroup platform + */ +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); + +/*! @brief Sets whether the window is floating. + * @ingroup platform + */ +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); + /*! @copydoc glfwPollEvents * @ingroup platform */ diff --git a/src/mir_window.c b/src/mir_window.c index 7d2c276a..368b633e 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -595,6 +595,24 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return mir_surface_get_state(window->mir.surface) == mir_surface_state_maximized; } +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + void _glfwPlatformPollEvents(void) { EventNode* node = NULL; diff --git a/src/win32_window.c b/src/win32_window.c index 9214a66c..70cb09ad 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -280,6 +280,26 @@ static void updateClipRect(_GLFWwindow* window) ClipCursor(NULL); } +// Update native window styles to match attributes +// +static void updateWindowStyles(const _GLFWwindow* window) +{ + RECT rect; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP); + style |= getWindowStyle(window); + + GetClientRect(window->win32.handle, &rect); + AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); + ClientToScreen(window->win32.handle, (POINT*) &rect.left); + ClientToScreen(window->win32.handle, (POINT*) &rect.right); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); +} + // Translates a GLFW standard cursor to a resource ID // static LPWSTR translateCursorShape(int shape) @@ -1346,6 +1366,23 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return IsZoomed(window->win32.handle); } +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); +} + void _glfwPlatformPollEvents(void) { MSG msg; diff --git a/src/window.c b/src/window.c index ece8d75b..d572ecfa 100644 --- a/src/window.c +++ b/src/window.c @@ -704,6 +704,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->decorated; case GLFW_FLOATING: return window->floating; + case GLFW_AUTO_ICONIFY: + return window->autoIconify; case GLFW_CLIENT_API: return window->context.client; case GLFW_CONTEXT_CREATION_API: @@ -732,6 +734,52 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return 0; } +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + value = value ? GLFW_TRUE : GLFW_FALSE; + + switch (attrib) + { + case GLFW_RESIZABLE: + if (window->resizable != value) + { + window->resizable = value; + if (!window->monitor) + _glfwPlatformSetWindowResizable(window, value); + } + return; + + case GLFW_DECORATED: + if (window->decorated != value) + { + window->decorated = value; + if (!window->monitor) + _glfwPlatformSetWindowDecorated(window, value); + } + return; + + case GLFW_FLOATING: + if (window->floating != value) + { + window->floating = value; + if (!window->monitor) + _glfwPlatformSetWindowFloating(window, value); + } + return; + + case GLFW_AUTO_ICONIFY: + window->autoIconify = value; + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute %i", attrib); +} + GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/src/wl_window.c b/src/wl_window.c index a2bae373..be6ca912 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -634,6 +634,27 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return window->wl.maximized; } +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + void _glfwPlatformPollEvents(void) { handleEvents(0); diff --git a/src/x11_window.c b/src/x11_window.c index d552e269..487098a2 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -520,26 +520,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, } if (!wndconfig->decorated) - { - struct - { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long input_mode; - unsigned long status; - } hints; - - hints.flags = 2; // Set decorations - hints.decorations = 0; // No decorations - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.MOTIF_WM_HINTS, - _glfw.x11.MOTIF_WM_HINTS, 32, - PropModeReplace, - (unsigned char*) &hints, - sizeof(hints) / sizeof(long)); - } + _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); if (_glfw.x11.NET_WM_STATE && !window->monitor) { @@ -2065,6 +2046,107 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return maximized; } +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + if (enabled) + { + XDeleteProperty(_glfw.x11.display, + window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS); + } + else + { + struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } hints; + + hints.flags = 2; // Set decorations + hints.decorations = 0; // No decorations + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS, + _glfw.x11.MOTIF_WM_HINTS, 32, + PropModeReplace, + (unsigned char*) &hints, + sizeof(hints) / sizeof(long)); + } +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) + return; + + if (_glfwPlatformWindowVisible(window)) + { + const Atom action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + action, + _glfw.x11.NET_WM_STATE_ABOVE, + 0, 1, 0); + } + else + { + Atom* states; + unsigned long i, count; + + count = _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + if (enabled) + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + break; + } + + if (i == count) + { + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, + 1); + } + } + else + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + { + states[i] = states[count - 1]; + count--; + } + } + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &states, count); + } + + XFree(states); + } + + XFlush(_glfw.x11.display); +} + void _glfwPlatformPollEvents(void) { _glfwPollJoystickEvents(); diff --git a/tests/iconify.c b/tests/iconify.c index efacc0cd..8de892e3 100644 --- a/tests/iconify.c +++ b/tests/iconify.c @@ -45,7 +45,6 @@ static void usage(void) printf(" -a create windows for all monitors\n"); printf(" -f create full screen window(s)\n"); printf(" -h show this help\n"); - printf(" -n no automatic iconification of full screen windows\n"); } static void error_callback(int error, const char* description) @@ -76,6 +75,18 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GLFW_TRUE); break; + case GLFW_KEY_A: + glfwSetWindowAttrib(window, GLFW_AUTO_ICONIFY, !glfwGetWindowAttrib(window, GLFW_AUTO_ICONIFY)); + break; + case GLFW_KEY_B: + glfwSetWindowAttrib(window, GLFW_RESIZABLE, !glfwGetWindowAttrib(window, GLFW_RESIZABLE)); + break; + case GLFW_KEY_D: + glfwSetWindowAttrib(window, GLFW_DECORATED, !glfwGetWindowAttrib(window, GLFW_DECORATED)); + break; + case GLFW_KEY_F: + glfwSetWindowAttrib(window, GLFW_FLOATING, !glfwGetWindowAttrib(window, GLFW_FLOATING)); + break; case GLFW_KEY_F11: case GLFW_KEY_ENTER: { @@ -143,24 +154,11 @@ static void window_maximize_callback(GLFWwindow* window, int maximized) static void window_refresh_callback(GLFWwindow* window) { - int width, height; - printf("%0.2f Window refresh\n", glfwGetTime()); - glfwGetFramebufferSize(window, &width, &height); - glfwMakeContextCurrent(window); - glEnable(GL_SCISSOR_TEST); - - glScissor(0, 0, width, height); - glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); - - glScissor(0, 0, 640, 480); - glClearColor(1, 1, 1, 0); - glClear(GL_COLOR_BUFFER_BIT); - glfwSwapBuffers(window); } @@ -203,7 +201,7 @@ static GLFWwindow* create_window(GLFWmonitor* monitor) int main(int argc, char** argv) { int ch, i, window_count; - int auto_iconify = GLFW_TRUE, fullscreen = GLFW_FALSE, all_monitors = GLFW_FALSE; + int fullscreen = GLFW_FALSE, all_monitors = GLFW_FALSE; GLFWwindow** windows; while ((ch = getopt(argc, argv, "afhn")) != -1) @@ -222,10 +220,6 @@ int main(int argc, char** argv) fullscreen = GLFW_TRUE; break; - case 'n': - auto_iconify = GLFW_FALSE; - break; - default: usage(); exit(EXIT_FAILURE); @@ -237,8 +231,6 @@ int main(int argc, char** argv) if (!glfwInit()) exit(EXIT_FAILURE); - glfwWindowHint(GLFW_AUTO_ICONIFY, auto_iconify); - if (fullscreen && all_monitors) { int monitor_count;