Commit 6570d0c4 authored by Camilla Berglund's avatar Camilla Berglund
Browse files

Add glfwSetWindowMonitor

This adds the ability to switch between windowed and full screen modes,
move a full screen window between monitors and update its desired
resolution and refresh rate.

Fixes #43.
parent fb8a31ba
......@@ -77,6 +77,8 @@ does not find Doxygen, the documentation will not be generated.
- Added `glfwVulkanSupported`, `glfwGetRequiredInstanceExtensions`,
`glfwGetInstanceProcAddress`, `glfwGetPhysicalDevicePresentationSupport` and
`glfwCreateWindowSurface` for platform independent Vulkan support
- Added `glfwSetWindowMonitor` for switching between windowed and full screen
modes and updating the monitor and desired video mode of full screen windows
- Added `glfwMaximizeWindow` and `GLFW_MAXIMIZED` for window maximization
- Added `glfwFocusWindow` for giving windows input focus
- Added `glfwSetWindowSizeLimits` and `glfwSetWindowAspectRatio` for setting
......
......@@ -95,8 +95,9 @@ a gamma ramp.
@subsection monitor_modes Video modes
GLFW generally does a good job selecting a suitable video mode when you create
a full screen window, but it is sometimes useful to know exactly which video
modes are supported.
a full screen window, change its video mode or or make a windowed one full
screen, but it is sometimes useful to know exactly which video modes are
supported.
Video modes are represented as @ref GLFWvidmode structures. You can get an
array of the video modes supported by a monitor with @ref glfwGetVideoModes.
......
......@@ -26,6 +26,13 @@ Vulkan header inclusion can be selected with
[GLFW_INCLUDE_VULKAN](@ref build_macros).
@subsection news_32_setwindowmonitor Window mode switching
GLFW now supports switching between windowed and full screen modes and updating
the monitor and desired resolution and refresh rate of full screen windows with
@ref glfwSetWindowMonitor.
@subsection news_32_maximize Window maxmimization support
GLFW now supports window maximization with @ref glfwMaximizeWindow and the
......
......@@ -56,6 +56,10 @@ GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", glfwGetPrimaryMonito
Full screen windows cover the entire display area of a monitor, have no border
or decorations.
Windowed mode windows can be made full screen by setting a monitor with @ref
glfwSetWindowMonitor, and full screen ones can be made windowed by unsetting it
with the same function.
Each field of the @ref GLFWvidmode structure corresponds to a function parameter
or window hint and combine to form the _desired video mode_ for that window.
The supported video mode most closely matching the desired video mode will be
......@@ -71,9 +75,11 @@ GLFWvidmode.greenBits | `GLFW_GREEN_BITS` hint
GLFWvidmode.blueBits | `GLFW_BLUE_BITS` hint
GLFWvidmode.refreshRate | `GLFW_REFRESH_RATE` hint
Once you have a full screen window, you can change its resolution with @ref
glfwSetWindowSize. The new video mode will be selected and set the same way as
the video mode chosen by @ref glfwCreateWindow.
Once you have a full screen window, you can change its resolution, refresh rate
and monitor with @ref glfwSetWindowMonitor. If you just need change its
resolution you can also call @ref glfwSetWindowSize. In all cases, the new
video mode will be selected the same way as the video mode chosen by @ref
glfwCreateWindow.
By default, the original video mode of the monitor will be restored and the
window iconified if it loses input focus, to allow the user to switch back to
......@@ -101,6 +107,18 @@ glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
GLFWwindow* window = glfwCreateWindow(mode->width, mode->height, "My Title", monitor, NULL);
@endcode
This also works for windowed mode windows that are made full screen.
@code
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
@endcode
Note that @ref glfwGetVideoMode returns the _current_ video mode of a monitor,
so if you already have a full screen window on that monitor that you want to
make windowed full screen, you need to have saved the desktop resolution before.
@subsection window_destruction Window destruction
......@@ -423,7 +441,7 @@ glfwSetWindowSize(window, 640, 480);
@endcode
For full screen windows, the specified size becomes the new resolution of the
window's *desired video mode*. The video mode most closely matching the new
window's desired video mode. The video mode most closely matching the new
desired video mode is set immediately. The window is resized to fit the
resolution of the set video mode.
......@@ -648,8 +666,31 @@ GLFWmonitor* monitor = glfwGetWindowMonitor(window);
This monitor handle is one of those returned by @ref glfwGetMonitors.
For windowed mode windows, this function returns `NULL`. This is the
recommended way to tell full screen windows from windowed mode windows.
For windowed mode windows, this function returns `NULL`. This is how to tell
full screen windows from windowed mode windows.
You can move windows between monitors or between full screen and windowed mode
with @ref glfwSetWindowMonitor. When making a window full screen on the same or
on a different monitor, specify the desired monitor, resolution and refresh
rate. The position arguments are ignored.
@code
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
@endcode
When making the window windowed, specify the desired position and size. The
refresh rate argument is ignored.
@code
glfwSetWindowMonitor(window, NULL, xpos, ypos, width, height, 0);
@endcode
This restores any previous window settings such as whether it is decorated,
floating, resizable, has size or aspect ratio limits, etc.. To restore a window
that was originally windowed to its original size and position, save these
before making it full screen and then pass them in as above.
@subsection window_iconify Window iconification
......
......@@ -89,6 +89,7 @@ typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM;
typedef struct {float x; float y; float z;} vertex_t;
/* Global vars */
int windowed_xpos, windowed_ypos, windowed_width, windowed_height;
int width, height;
GLfloat deg_rot_y = 0.f;
GLfloat deg_rot_y_inc = 2.f;
......@@ -238,6 +239,26 @@ void key_callback( GLFWwindow* window, int key, int scancode, int action, int mo
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
if (key == GLFW_KEY_ENTER && action == GLFW_PRESS && mods == GLFW_MOD_ALT)
{
if (glfwGetWindowMonitor(window))
{
glfwSetWindowMonitor(window, NULL,
windowed_xpos, windowed_ypos,
windowed_width, windowed_height, 0);
}
else
{
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
if (monitor)
{
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos);
glfwGetWindowSize(window, &windowed_width, &windowed_height);
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
}
}
}
}
static void set_ball_pos ( GLfloat x, GLfloat y )
......
......@@ -1718,17 +1718,17 @@ GLFWAPI void glfwWindowHint(int hint, int value);
* glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize.
*
* To create a full screen window, you need to specify the monitor the window
* will cover. If no monitor is specified, windowed mode will be used. Unless
* you have a way for the user to choose a specific monitor, it is recommended
* that you pick the primary monitor. For more information on how to query
* connected monitors, see @ref monitor_monitors.
* will cover. If no monitor is specified, the window will be windowed mode.
* Unless you have a way for the user to choose a specific monitor, it is
* recommended that you pick the primary monitor. For more information on how
* to query connected monitors, see @ref monitor_monitors.
*
* For full screen windows, the specified size becomes the resolution of the
* window's _desired video mode_. As long as a full screen window has input
* focus, the supported video mode most closely matching the desired video mode
* is set for the specified monitor. For more information about full screen
* windows, including the creation of so called _windowed full screen_ or
* _borderless full screen_ windows, see @ref window_windowed_full_screen.
* window's _desired video mode_. As long as a full screen window is not
* iconified, the supported video mode most closely matching the desired video
* mode is set for the specified monitor. For more information about full
* screen windows, including the creation of so called _windowed full screen_
* or _borderless full screen_ windows, see @ref window_windowed_full_screen.
*
* By default, newly created windows use the placement recommended by the
* window system. To create the window at a specific position, make it
......@@ -1736,8 +1736,8 @@ GLFWAPI void glfwWindowHint(int hint, int value);
* hint, set its [position](@ref window_pos) and then [show](@ref window_hide)
* it.
*
* If a full screen window has input focus, the screensaver is prohibited from
* starting.
* As long as at least one full screen window is not iconified, the screensaver
* is prohibited from starting.
*
* Window systems put limits on window sizes. Very large or very small window
* dimensions may be overridden by the window system on creation. Check the
......@@ -1751,7 +1751,7 @@ GLFWAPI void glfwWindowHint(int hint, int value);
* @param[in] height The desired height, in screen coordinates, of the window.
* This must be greater than zero.
* @param[in] title The initial, UTF-8 encoded window title.
* @param[in] monitor The monitor to use for full screen mode, or `NULL` to use
* @param[in] monitor The monitor to use for full screen mode, or `NULL` for
* windowed mode.
* @param[in] share The window whose context to share resources with, or `NULL`
* to not share resources.
......@@ -2044,11 +2044,12 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height);
/*! @brief Sets the size limits of the specified window.
*
* This function sets the size limits of the client area of the specified
* window. If the window is full screen or not resizable, this function does
* nothing.
* window. If the window is full screen, the size limits only take effect if
* once it is made windowed. If the window is not resizable, this function
* does nothing.
*
* The size limits are applied immediately and may cause the window to be
* resized.
* The size limits are applied immediately to a windowed mode window and may
* cause it to be resized.
*
* @param[in] window The window to set limits for.
* @param[in] minwidth The minimum width, in screen coordinates, of the client
......@@ -2080,7 +2081,8 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe
/*! @brief Sets the aspect ratio of the specified window.
*
* This function sets the required aspect ratio of the client area of the
* specified window. If the window is full screen or not resizable, this
* specified window. If the window is full screen, the aspect ratio only takes
* effect once it is made windowed. If the window is not resizable, this
* function does nothing.
*
* The aspect ratio is specified as a numerator and a denominator and both
......@@ -2090,8 +2092,8 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe
* If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect
* ratio limit is disabled.
*
* The aspect ratio is applied immediately and may cause the window to be
* resized.
* The aspect ratio is applied immediately to a windowed mode window and may
* cause it to be resized.
*
* @param[in] window The window to set limits for.
* @param[in] numer The numerator of the desired aspect ratio, or
......@@ -2121,17 +2123,22 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom);
* This function sets the size, in screen coordinates, of the client area of
* the specified window.
*
* For full screen windows, this function selects and switches to the resolution
* closest to the specified size, without affecting the window's context. As
* the context is unaffected, the bit depths of the framebuffer remain
* unchanged.
* For full screen windows, this function updates the resolution of its desired
* video mode and switches to the video mode closest to it, without affecting
* the window's context. As the context is unaffected, the bit depths of the
* framebuffer remain unchanged.
*
* If you wish to update the refresh rate of the desired video mode in addition
* to its resolution, see @ref glfwSetWindowMonitor.
*
* The window manager may put limits on what sizes are allowed. GLFW cannot
* and should not override these limits.
*
* @param[in] window The window to resize.
* @param[in] width The desired width of the specified window.
* @param[in] height The desired height of the specified window.
* @param[in] width The desired width, in screen coordinates, of the window
* client area.
* @param[in] height The desired height, in screen coordinates, of the window
* client area.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
......@@ -2140,6 +2147,7 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom);
*
* @sa @ref window_size
* @sa glfwGetWindowSize
* @sa glfwSetWindowMonitor
*
* @since Added in version 1.0.
* @glfw3 Added window handle parameter.
......@@ -2376,6 +2384,7 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* window);
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_monitor
* @sa glfwSetWindowMonitor
*
* @since Added in version 3.0.
*
......@@ -2383,6 +2392,54 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* window);
*/
GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window);
/*! @brief Sets the mode, monitor, video mode and placement of a window.
*
* This function sets the monitor that the window uses for full screen mode or,
* if the monitor is `NULL`, makes it windowed mode.
*
* When setting a monitor, this function updates the width, height and refresh
* rate of the desired video mode and switches to the video mode closest to it.
* The window position is ignored when setting a monitor.
*
* When the monitor is `NULL`, the position, width and height are used to
* place the window client area. The refresh rate is ignored when no monitor
* is specified.
*
* If you only wish to update the resolution of a full screen window or the
* size of a windowed mode window, see @ref glfwSetWindowSize.
*
* When a window transitions from full screen to windowed mode, this function
* restores any previous window settings such as whether it is decorated,
* floating, resizable, has size or aspect ratio limits, etc..
*
* @param[in] window The window whose monitor, size or video mode to set.
* @param[in] monitor The desired monitor, or `NULL` to set windowed mode.
* @param[in] xpos The desired x-coordinate of the upper-left corner of the
* client area.
* @param[in] ypos The desired y-coordinate of the upper-left corner of the
* client area.
* @param[in] width The desired with, in screen coordinates, of the client area
* or video mode.
* @param[in] height The desired height, in screen coordinates, of the client
* area or video mode.
* @param[in] refreshRate The desired refresh rate, in Hz, of the video mode.
*
* @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_monitor
* @sa @ref window_full_screen
* @sa glfwGetWindowMonitor
* @sa glfwSetWindowSize
*
* @since Added in version 3.2.
*
* @ingroup window
*/
GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
/*! @brief Returns an attribute of the specified window.
*
* This function returns the value of an attribute of the specified window or
......
......@@ -56,6 +56,26 @@ static NSCursor* getStandardCursor(int shape)
return nil;
}
// Returns the style mask corresponding to the window settings
//
static NSUInteger getStyleMask(_GLFWwindow* window)
{
NSUInteger styleMask = 0;
if (window->monitor || !window->decorated)
styleMask |= NSBorderlessWindowMask;
else
{
styleMask |= NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask;
if (window->resizable)
styleMask |= NSResizableWindowMask;
}
return styleMask;
}
// Center the cursor in the view of the window
//
static void centerCursor(_GLFWwindow *window)
......@@ -86,7 +106,6 @@ static GLFWbool acquireMonitor(_GLFWwindow* window)
[window->ns.object setFrame:frame display:YES];
_glfwPlatformFocusWindow(window);
_glfwInputMonitorWindowChange(window->monitor, window);
return status;
}
......@@ -908,19 +927,6 @@ static GLFWbool createWindow(_GLFWwindow* window,
return GLFW_FALSE;
}
unsigned int styleMask = 0;
if (window->monitor || !wndconfig->decorated)
styleMask = NSBorderlessWindowMask;
else
{
styleMask = NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask;
if (wndconfig->resizable)
styleMask |= NSResizableWindowMask;
}
NSRect contentRect;
if (window->monitor)
......@@ -938,7 +944,7 @@ static GLFWbool createWindow(_GLFWwindow* window,
window->ns.object = [[GLFWWindow alloc]
initWithContentRect:contentRect
styleMask:styleMask
styleMask:getStyleMask(window)
backing:NSBackingStoreBuffered
defer:NO];
......@@ -948,15 +954,15 @@ static GLFWbool createWindow(_GLFWwindow* window,
return GLFW_FALSE;
}
if (wndconfig->resizable)
[window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
if (window->monitor)
[window->ns.object setLevel:NSMainMenuWindowLevel + 1];
else
{
[window->ns.object center];
if (wndconfig->resizable)
[window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
if (wndconfig->floating)
[window->ns.object setLevel:NSFloatingWindowLevel];
......@@ -1005,6 +1011,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
if (window->monitor)
{
_glfwPlatformShowWindow(window);
_glfwPlatformFocusWindow(window);
if (!acquireMonitor(window))
return GLFW_FALSE;
}
......@@ -1076,7 +1083,10 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
if (window->monitor)
acquireMonitor(window);
{
if (window->monitor->window == window)
acquireMonitor(window);
}
else
[window->ns.object setContentSize:NSMakeSize(width, height)];
}
......@@ -1174,6 +1184,103 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window)
[window->ns.object makeKeyAndOrderFront:nil];
}
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
_GLFWmonitor* monitor,
int xpos, int ypos,
int width, int height,
int refreshRate)
{
if (window->monitor == monitor)
{
if (monitor)
{
if (monitor->window == window)
acquireMonitor(window);
}
else
{
const NSRect contentRect =
NSMakeRect(xpos, transformY(ypos + height), width, height);
const NSRect frameRect =
[window->ns.object frameRectForContentRect:contentRect
styleMask:getStyleMask(window)];
[window->ns.object setFrame:frameRect display:YES];
}
return;
}
if (window->monitor)
releaseMonitor(window);
_glfwInputWindowMonitorChange(window, monitor);
const NSUInteger styleMask = getStyleMask(window);
[window->ns.object setStyleMask:styleMask];
[window->ns.object makeFirstResponder:window->ns.view];
NSRect contentRect;
if (monitor)
{
GLFWvidmode mode;
_glfwPlatformGetVideoMode(window->monitor, &mode);
_glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
contentRect = NSMakeRect(xpos, transformY(ypos + mode.height),
mode.width, mode.height);
}
else
{
contentRect = NSMakeRect(xpos, transformY(ypos + height),
width, height);
}
NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect
styleMask:styleMask];
[window->ns.object setFrame:frameRect display:YES];
if (monitor)
{
[window->ns.object setLevel:NSMainMenuWindowLevel + 1];
[window->ns.object setHasShadow:NO];
acquireMonitor(window);
}
else
{
if (window->numer != GLFW_DONT_CARE &&
window->denom != GLFW_DONT_CARE)
{
[window->ns.object setContentAspectRatio:NSMakeSize(window->numer,
window->denom)];
}
if (window->minwidth != GLFW_DONT_CARE &&
window->minheight != GLFW_DONT_CARE)
{
[window->ns.object setContentMinSize:NSMakeSize(window->minwidth,
window->minheight)];
}
if (window->maxwidth != GLFW_DONT_CARE &&
window->maxheight != GLFW_DONT_CARE)
{
[window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth,
window->maxheight)];
}
if (window->floating)
[window->ns.object setLevel:NSFloatingWindowLevel];
else
[window->ns.object setLevel:NSNormalWindowLevel];
[window->ns.object setHasShadow:YES];
}
}
int _glfwPlatformWindowFocused(_GLFWwindow* window)
{
return [window->ns.object isKeyWindow];
......
......@@ -339,6 +339,10 @@ struct _GLFWwindow
_GLFWmonitor* monitor;
_GLFWcursor* cursor;
int minwidth, minheight;
int maxwidth, maxheight;
int numer, denom;
// Window input state
GLFWbool stickyKeys;
GLFWbool stickyMouseButtons;
......@@ -694,6 +698,11 @@ void _glfwPlatformHideWindow(_GLFWwindow* window);
*/
void _glfwPlatformFocusWindow(_GLFWwindow* window);
/*! @copydoc glfwSetWindowMonitor
* @ingroup platform
*/
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
/*! @brief Returns whether the window is focused.
* @ingroup platform
*/
......@@ -856,6 +865,8 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
*/
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor);
/*! @brief Notifies shared code of a physical key event.
* @param[in] window The window that received the event.
* @param[in] key The key that was pressed or released.
......
......@@ -508,6 +508,16 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window)
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
}
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
_GLFWmonitor* monitor,
int xpos, int ypos,
int width, int height,
int refreshRate)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
}
int _glfwPlatformWindowFocused(_GLFWwindow* window)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
......
......@@ -126,7 +126,11 @@ void _glfwInputMonitorChange(void)
for (window = _glfw.windowListHead; window; window = window->next)
{
if (window->monitor == monitors[i])
window->monitor = NULL;
{
int width, height;
_glfwPlatformGetWindowSize(window, &width, &height);
_glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0);
}
}
if (_glfw.callbacks.monitor)
......
......@@ -197,10 +197,6 @@ typedef struct _GLFWwindowWin32
GLFWbool cursorTracked;
GLFWbool iconified;
int minwidth, minheight;
int maxwidth, maxheight;
int numer, denom;
// The last received cursor position, regardless of source
int cursorPosX, cursorPosY;
......
......@@ -42,15 +42,20 @@ static DWORD getWindowStyle(const _GLFWwindow* window)
{
DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
if (window->decorated && !window->monitor)
if (window->monitor)
style |= WS_POPUP;
else
{
style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
if (window->decorated)
{
style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
if (window->resizable)
style |= WS_MAXIMIZEBOX | WS_SIZEBOX;
if (window->resizable)