diff --git a/README.md b/README.md index 26f92a92..87b85864 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ does not find Doxygen, the documentation will not be generated. - Added `glfwVulkanSupported`, `glfwGetRequiredInstanceExtensions`, `glfwGetInstanceProcAddress`, `glfwGetPhysicalDevicePresentationSupport` and `glfwCreateWindowSurface` for platform independent Vulkan support + - Added `glfwMaximizeWindow` for maximizing windows - Added `glfwSetWindowSizeLimits` and `glfwSetWindowAspectRatio` for setting absolute and relative window size limits - Added `glfwGetKeyName` for querying the layout-specific name of printable diff --git a/docs/news.dox b/docs/news.dox index ed13fb7f..601eee48 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -26,6 +26,12 @@ Vulkan header inclusion can be selected with [GLFW_INCLUDE_VULKAN](@ref build_macros). +@subsection news_32_maximize Window maxmimization supprot + +GLFW now supports window maximization with @ref glfwMaximizeWindow and the +[GLFW_MAXIMIZED](@ref window_attribs_wnd) window hint and attribute. + + @section news_31 New features in 3.1 These are the release highlights. For a full list of changes see the diff --git a/docs/window.dox b/docs/window.dox index 1612e274..b7c7b731 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -175,6 +175,9 @@ above other regular windows, also called topmost or always-on-top. This is intended primarily for debugging purposes and cannot be used to implement proper full screen windows. This hint is ignored for full screen windows. +`GLFW_MAXIMIZED` specifies whether the windowed mode window will be maximized +when created. This hint is ignored for full screen windows. + @subsubsection window_hints_fb Framebuffer related hints @@ -323,6 +326,7 @@ Window hint | Default value | Supported values `GLFW_FOCUSED` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_AUTO_ICONIFY` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_FLOATING` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_MAXIMIZED` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` `GLFW_RED_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` `GLFW_GREEN_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` `GLFW_BLUE_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` @@ -791,6 +795,9 @@ same name. `GLFW_ICONIFIED` indicates whether the specified window is iconified, whether by the user or with @ref glfwIconifyWindow. +`GLFW_MAXIMIZED` indicates whether the specified window is maximized, whether by +the user or with @ref glfwMaximizeWindow. + `GLFW_VISIBLE` indicates whether the specified window is visible. Window visibility can be controlled with @ref glfwShowWindow and @ref glfwHideWindow and initial visibility is controlled by the [window hint](@ref window_hints_wnd) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 43b33f14..78346714 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -626,6 +626,7 @@ extern "C" { #define GLFW_DECORATED 0x00020005 #define GLFW_AUTO_ICONIFY 0x00020006 #define GLFW_FLOATING 0x00020007 +#define GLFW_MAXIMIZED 0x00020008 #define GLFW_RED_BITS 0x00021001 #define GLFW_GREEN_BITS 0x00021002 @@ -2180,6 +2181,7 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * * @sa @ref window_iconify * @sa glfwRestoreWindow + * @sa glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2191,7 +2193,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); /*! @brief Restores the specified window. * * This function restores the specified window if it was previously iconified - * (minimized). If the window is already restored, this function does nothing. + * (minimized) or maximized. If the window is already restored, this function + * does nothing. * * If the specified window is a full screen window, the resolution chosen for * the window is restored on the selected monitor. @@ -2205,6 +2208,7 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * * @sa @ref window_iconify * @sa glfwIconifyWindow + * @sa glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2213,6 +2217,28 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); */ GLFWAPI void glfwRestoreWindow(GLFWwindow* window); +/*! @brief Maximizes the specified window. + * + * This function maximizes the specified window if it was previously not + * maximized. If the window is already maximized, this function does nothing. + * + * If the specified window is a full screen window, this function does nothing. + * + * @param[in] window The window to maximize. + * + * @par Thread Safety + * This function may only be called from the main thread. + * + * @sa @ref window_iconify + * @sa glfwIconifyWindow + * @sa glfwRestoreWindow + * + * @since Added in GLFW 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); + /*! @brief Makes the specified window visible. * * This function makes the specified window visible if it was previously diff --git a/src/cocoa_window.m b/src/cocoa_window.m index fb03419b..a3243750 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -851,6 +851,9 @@ static GLFWbool createWindow(_GLFWwindow* window, if (wndconfig->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; + + if (wndconfig->maximized) + [window->ns.object zoom:nil]; } window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; @@ -1023,7 +1026,16 @@ void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformRestoreWindow(_GLFWwindow* window) { - [window->ns.object deminiaturize:nil]; + if ([window->ns.object isMiniaturized]) + [window->ns.object deminiaturize:nil]; + else if ([window->ns.object isZoomed]) + [window->ns.object zoom:nil]; +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (![window->ns.object isZoomed]) + [window->ns.object zoom:nil]; } void _glfwPlatformShowWindow(_GLFWwindow* window) @@ -1062,6 +1074,11 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return [window->ns.object isVisible]; } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return [window->ns.object isZoomed]; +} + void _glfwPlatformPollEvents(void) { for (;;) diff --git a/src/internal.h b/src/internal.h index 2fe2aee4..3d8edc88 100644 --- a/src/internal.h +++ b/src/internal.h @@ -255,6 +255,7 @@ struct _GLFWwndconfig GLFWbool focused; GLFWbool autoIconify; GLFWbool floating; + GLFWbool maximized; _GLFWmonitor* monitor; }; @@ -673,6 +674,11 @@ void _glfwPlatformIconifyWindow(_GLFWwindow* window); */ void _glfwPlatformRestoreWindow(_GLFWwindow* window); +/*! @copydoc glfwMaximizeWindow + * @ingroup platform + */ +void _glfwPlatformMaximizeWindow(_GLFWwindow* window); + /*! @copydoc glfwShowWindow * @ingroup platform */ @@ -702,6 +708,11 @@ int _glfwPlatformWindowIconified(_GLFWwindow* window); */ int _glfwPlatformWindowVisible(_GLFWwindow* window); +/*! @brief Returns whether the window is maximized. + * @ingroup platform + */ +int _glfwPlatformWindowMaximized(_GLFWwindow* window); + /*! @copydoc glfwPollEvents * @ingroup platform */ diff --git a/src/mir_window.c b/src/mir_window.c index acc0a2cc..6e0ee16b 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -467,6 +467,12 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) mir_surface_set_state(window->mir.surface, mir_surface_state_restored); } +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + void _glfwPlatformHideWindow(_GLFWwindow* window) { MirSurfaceSpec* spec; @@ -514,6 +520,13 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return mir_surface_get_visibility(window->mir.surface) == mir_surface_visibility_exposed; } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + return GLFW_FALSE; +} + void _glfwPlatformPollEvents(void) { EventNode* node = NULL; diff --git a/src/win32_window.c b/src/win32_window.c index 0de246db..55d98b9a 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -653,6 +653,8 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { int xpos, ypos, fullWidth, fullHeight; WCHAR* wideTitle; + DWORD style = getWindowStyle(window); + DWORD exStyle = getWindowExStyle(window); if (wndconfig->monitor) { @@ -671,7 +673,10 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) xpos = CW_USEDEFAULT; ypos = CW_USEDEFAULT; - getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), + if (wndconfig->maximized) + style |= WS_MAXIMIZE; + + getFullWindowSize(style, exStyle, wndconfig->width, wndconfig->height, &fullWidth, &fullHeight); } @@ -684,10 +689,10 @@ static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) return GLFW_FALSE; } - window->win32.handle = CreateWindowExW(getWindowExStyle(window), + window->win32.handle = CreateWindowExW(exStyle, _GLFW_WNDCLASSNAME, wideTitle, - getWindowStyle(window), + style, xpos, ypos, fullWidth, fullHeight, NULL, // No parent window @@ -1026,6 +1031,11 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) ShowWindow(window->win32.handle, SW_RESTORE); } +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_MAXIMIZE); +} + void _glfwPlatformShowWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_SHOW); @@ -1059,6 +1069,11 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return IsWindowVisible(window->win32.handle); } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return IsZoomed(window->win32.handle); +} + void _glfwPlatformPollEvents(void) { MSG msg; diff --git a/src/window.c b/src/window.c index f0063e35..f051370e 100644 --- a/src/window.c +++ b/src/window.c @@ -339,6 +339,9 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_FLOATING: _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; break; + case GLFW_MAXIMIZED: + _glfw.hints.window.maximized = hint ? GLFW_TRUE : GLFW_FALSE; + break; case GLFW_VISIBLE: _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; break; @@ -593,6 +596,13 @@ GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) _glfwPlatformRestoreWindow(window); } +GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT(); + _glfwPlatformMaximizeWindow(window); +} + GLFWAPI void glfwShowWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; @@ -634,6 +644,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowIconified(window); case GLFW_VISIBLE: return _glfwPlatformWindowVisible(window); + case GLFW_MAXIMIZED: + return _glfwPlatformWindowMaximized(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: diff --git a/src/wl_window.c b/src/wl_window.c index ea301434..c3303d70 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -459,6 +459,12 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) fprintf(stderr, "_glfwPlatformRestoreWindow not implemented yet\n"); } +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + // TODO + fprintf(stderr, "_glfwPlatformMaximizeWindow not implemented yet\n"); +} + void _glfwPlatformShowWindow(_GLFWwindow* window) { wl_shell_surface_set_toplevel(window->wl.shell_surface); @@ -494,6 +500,12 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return GLFW_FALSE; } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + // TODO + return GLFW_FALSE; +} + void _glfwPlatformPollEvents(void) { handleEvents(0); diff --git a/src/x11_init.c b/src/x11_init.c index e17a7170..bf9253b0 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -444,6 +444,10 @@ static void detectEWMH(void) getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); _glfw.x11.NET_WM_STATE_FULLSCREEN = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); _glfw.x11.NET_WM_NAME = diff --git a/src/x11_platform.h b/src/x11_platform.h index 65aba0ed..6d0f43a1 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -175,6 +175,8 @@ typedef struct _GLFWlibraryX11 Atom NET_WM_STATE; Atom NET_WM_STATE_ABOVE; Atom NET_WM_STATE_FULLSCREEN; + Atom NET_WM_STATE_MAXIMIZED_VERT; + Atom NET_WM_STATE_MAXIMIZED_HORZ; Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_WM_FULLSCREEN_MONITORS; Atom NET_ACTIVE_WINDOW; diff --git a/src/x11_window.c b/src/x11_window.c index 46e2d32b..beba43cc 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -355,6 +355,24 @@ static GLFWbool createWindow(_GLFWwindow* window, 0, 1, 0); } } + + if (wndconfig->maximized) + { + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + const Atom states[2] = + { + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ + }; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &states, 2); + } + } } @@ -1820,10 +1838,42 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) return; } - XMapWindow(_glfw.x11.display, window->x11.handle); + if (_glfwPlatformWindowIconified(window)) + XMapWindow(_glfw.x11.display, window->x11.handle); + else + { + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); + } + } + XFlush(_glfw.x11.display); } +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); + XFlush(_glfw.x11.display); + } +} + void _glfwPlatformShowWindow(_GLFWwindow* window) { XMapRaised(_glfw.x11.display, window->x11.handle); @@ -1863,6 +1913,31 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return wa.map_state == IsViewable; } +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + Atom* states; + unsigned long i; + GLFWbool maximized = GLFW_FALSE; + const unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + maximized = GLFW_TRUE; + break; + } + } + + XFree(states); + return maximized; +} + void _glfwPlatformPollEvents(void) { int count = XPending(_glfw.x11.display); diff --git a/tests/iconify.c b/tests/iconify.c index a5675574..3eeaf749 100644 --- a/tests/iconify.c +++ b/tests/iconify.c @@ -62,9 +62,15 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, switch (key) { - case GLFW_KEY_SPACE: + case GLFW_KEY_I: glfwIconifyWindow(window); break; + case GLFW_KEY_M: + glfwMaximizeWindow(window); + break; + case GLFW_KEY_R: + glfwRestoreWindow(window); + break; case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GLFW_TRUE); break; @@ -240,7 +246,7 @@ int main(int argc, char** argv) for (;;) { - glfwPollEvents(); + glfwWaitEvents(); for (i = 0; i < window_count; i++) {