Add window hints for initial position

This adds window hints for the initial position, in screen coordinates,
of a window.  The special value GLFW_ANY_POSITION means the window
manager will be allowed to position the window.

It is not possible to set window positions on Wayland and GLFW will
always behave as if these hints are set to GLFW_ANY_POSITION.

Fixes #1603
Fixes #1747
This commit is contained in:
Camilla Löwy 2021-10-26 14:25:03 +02:00
parent 7d73629e50
commit 0f9a9578f3
12 changed files with 138 additions and 31 deletions

View File

@ -141,6 +141,9 @@ information on what to include when reporting a bug.
through the window (#1236,#1568) through the window (#1236,#1568)
- Added `GLFW_CURSOR_CAPTURED` cursor mode to confine the cursor to the window - Added `GLFW_CURSOR_CAPTURED` cursor mode to confine the cursor to the window
content area (#58) content area (#58)
- Added `GLFW_POSITION_X` and `GLFW_POSITION_Y` window hints for initial position
(#1603,#1747)
- Added `GLFW_ANY_POSITION` hint value for letting the window manager choose (#1603,#1747)
- Added `GLFW_PLATFORM_UNAVAILABLE` error for platform detection failures (#1958) - Added `GLFW_PLATFORM_UNAVAILABLE` error for platform detection failures (#1958)
- Added `GLFW_FEATURE_UNAVAILABLE` error for platform limitations (#1692) - Added `GLFW_FEATURE_UNAVAILABLE` error for platform limitations (#1692)
- Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692) - Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692)

View File

@ -76,6 +76,14 @@ function pointers corresponding to the standard library functions `malloc`,
For more information see @ref init_allocator. For more information see @ref init_allocator.
@subsubsection features_34_position_hint Window hints for initial position
GLFW now provides the @ref GLFW_POSITION_X and @ref GLFW_POSITION_Y window hints for
specifying the initial position of the window. This removes the need to create a hidden
window, move it and then show it. The default value of these hints is
`GLFW_ANY_POSITION`, which selects the previous behavior.
@subsubsection features_34_win32_keymenu Support for keyboard access to Windows window menu @subsubsection features_34_win32_keymenu Support for keyboard access to Windows window menu
GLFW now provides the GLFW now provides the
@ -243,6 +251,9 @@ then GLFW will fail to initialize.
- @ref GLFW_ANGLE_PLATFORM_TYPE_METAL - @ref GLFW_ANGLE_PLATFORM_TYPE_METAL
- @ref GLFW_X11_XCB_VULKAN_SURFACE - @ref GLFW_X11_XCB_VULKAN_SURFACE
- @ref GLFW_CURSOR_CAPTURED - @ref GLFW_CURSOR_CAPTURED
- @ref GLFW_POSITION_X
- @ref GLFW_POSITION_Y
- @ref GLFW_ANY_POSITION
@section news_archive Release notes for earlier versions @section news_archive Release notes for earlier versions

View File

@ -256,6 +256,14 @@ This is only supported for undecorated windows. Decorated windows with this
enabled will behave differently between platforms. Possible values are enabled will behave differently between platforms. Possible values are
`GLFW_TRUE` and `GLFW_FALSE`. `GLFW_TRUE` and `GLFW_FALSE`.
@anchor GLFW_POSITION_X
@anchor GLFW_POSITION_Y
__GLFW_POSITION_X__ and __GLFW_POSITION_Y__ specify the desired initial position
of the window. The window manager may modify or ignore these coordinates. If
either or both of these hints are set to `GLFW_ANY_POSITION` then the window
manager will position the window where it thinks the user will prefer it.
Possible values are any valid screen coordinates and `GLFW_ANY_POSITION`.
@subsubsection window_hints_fb Framebuffer related hints @subsubsection window_hints_fb Framebuffer related hints
@ -516,6 +524,8 @@ GLFW_TRANSPARENT_FRAMEBUFFER | `GLFW_FALSE` | `GLFW_TRUE` or `GL
GLFW_FOCUS_ON_SHOW | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_FOCUS_ON_SHOW | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_SCALE_TO_MONITOR | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_SCALE_TO_MONITOR | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_MOUSE_PASSTHROUGH | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` GLFW_MOUSE_PASSTHROUGH | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
GLFW_POSITION_X | `GLFW_ANY_POSITION` | Any valid screen x-coordinate or `GLFW_ANY_POSITION`
GLFW_POSITION_Y | `GLFW_ANY_POSITION` | Any valid screen y-coordinate or `GLFW_ANY_POSITION`
GLFW_RED_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` 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_GREEN_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE`
GLFW_BLUE_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` GLFW_BLUE_BITS | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE`
@ -798,7 +808,20 @@ are undefined if they conflict.
@subsection window_pos Window position @subsection window_pos Window position
The position of a windowed-mode window can be changed with @ref By default, the window manager chooses the position of new windowed mode
windows, based on its size and which monitor the user appears to be working on.
This is most often the right choice. If you need to create a window at
a specific position, you can set the desired position with the @ref
GLFW_POSITION_X and @ref GLFW_POSITION_Y window hints.
@code
glfwWindowHint(GLFW_POSITION_X, 70);
glfwWindowHint(GLFW_POSITION_Y, 83);
@endcode
To restore the previous behavior, set these hints to `GLFW_ANY_POSITION`.
The position of a windowed mode window can be changed with @ref
glfwSetWindowPos. This moves the window so that the upper-left corner of its glfwSetWindowPos. This moves the window so that the upper-left corner of its
content area has the specified [screen coordinates](@ref coordinate_systems). content area has the specified [screen coordinates](@ref coordinate_systems).
The window system may put limitations on window placement. The window system may put limitations on window placement.

View File

@ -44,7 +44,6 @@ int main(int argc, char** argv)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
glfwGetMonitorWorkarea(glfwGetPrimaryMonitor(), &xpos, &ypos, NULL, &height); glfwGetMonitorWorkarea(glfwGetPrimaryMonitor(), &xpos, &ypos, NULL, &height);
@ -66,6 +65,9 @@ int main(int argc, char** argv)
if (i > 0) if (i > 0)
glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
glfwWindowHint(GLFW_POSITION_X, xpos + size * (1 + (i & 1)));
glfwWindowHint(GLFW_POSITION_Y, ypos + size * (1 + (i >> 1)));
windows[i] = glfwCreateWindow(size, size, "Multi-Window Example", NULL, NULL); windows[i] = glfwCreateWindow(size, size, "Multi-Window Example", NULL, NULL);
if (!windows[i]) if (!windows[i])
{ {
@ -75,9 +77,6 @@ int main(int argc, char** argv)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
glfwSetWindowPos(windows[i],
xpos + size * (1 + (i & 1)),
ypos + size * (1 + (i >> 1)));
glfwSetInputMode(windows[i], GLFW_STICKY_KEYS, GLFW_TRUE); glfwSetInputMode(windows[i], GLFW_STICKY_KEYS, GLFW_TRUE);
glfwMakeContextCurrent(windows[i]); glfwMakeContextCurrent(windows[i]);
@ -85,9 +84,6 @@ int main(int argc, char** argv)
glClearColor(colors[i].r, colors[i].g, colors[i].b, 1.f); glClearColor(colors[i].r, colors[i].g, colors[i].b, 1.f);
} }
for (int i = 0; i < 4; i++)
glfwShowWindow(windows[i]);
for (;;) for (;;)
{ {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)

View File

@ -927,6 +927,18 @@ extern "C" {
*/ */
#define GLFW_MOUSE_PASSTHROUGH 0x0002000D #define GLFW_MOUSE_PASSTHROUGH 0x0002000D
/*! @brief Initial position x-coordinate window hint.
*
* Initial position x-coordinate [window hint](@ref GLFW_POSITION_X).
*/
#define GLFW_POSITION_X 0x0002000E
/*! @brief Initial position y-coordinate window hint.
*
* Initial position y-coordinate [window hint](@ref GLFW_POSITION_Y).
*/
#define GLFW_POSITION_Y 0x0002000F
/*! @brief Framebuffer bit depth hint. /*! @brief Framebuffer bit depth hint.
* *
* Framebuffer bit depth [hint](@ref GLFW_RED_BITS). * Framebuffer bit depth [hint](@ref GLFW_RED_BITS).
@ -1152,6 +1164,8 @@ extern "C" {
#define GLFW_ANGLE_PLATFORM_TYPE_VULKAN 0x00037007 #define GLFW_ANGLE_PLATFORM_TYPE_VULKAN 0x00037007
#define GLFW_ANGLE_PLATFORM_TYPE_METAL 0x00037008 #define GLFW_ANGLE_PLATFORM_TYPE_METAL 0x00037008
#define GLFW_ANY_POSITION 0x80000000
/*! @defgroup shapes Standard cursor shapes /*! @defgroup shapes Standard cursor shapes
* @brief Standard system cursor shapes. * @brief Standard system cursor shapes.
* *
@ -3036,10 +3050,10 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
* OpenGL or OpenGL ES context. * OpenGL or OpenGL ES context.
* *
* By default, newly created windows use the placement recommended by the * By default, newly created windows use the placement recommended by the
* window system. To create the window at a specific position, make it * window system. To create the window at a specific position, set the @ref
* initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window * GLFW_POSITION_X and @ref GLFW_POSITION_Y window hints before creation. To
* hint, set its [position](@ref window_pos) and then [show](@ref window_hide) * restore the default behavior, set either or both hints back to
* it. * `GLFW_ANY_POSITION`.
* *
* As long as at least one full screen window is not iconified, the screensaver * As long as at least one full screen window is not iconified, the screensaver
* is prohibited from starting. * is prohibited from starting.

View File

@ -791,7 +791,19 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height); contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height);
} }
else else
{
if (wndconfig->xpos == GLFW_ANY_POSITION ||
wndconfig->ypos == GLFW_ANY_POSITION)
{
contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height); contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height);
}
else
{
const int xpos = wndconfig->xpos;
const int ypos = _glfwTransformYCocoa(wndconfig->ypos + wndconfig->height - 1);
contentRect = NSMakeRect(xpos, ypos, wndconfig->width, wndconfig->height);
}
}
NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; NSUInteger styleMask = NSWindowStyleMaskMiniaturizable;
@ -820,11 +832,15 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
if (window->monitor) if (window->monitor)
[window->ns.object setLevel:NSMainMenuWindowLevel + 1]; [window->ns.object setLevel:NSMainMenuWindowLevel + 1];
else else
{
if (wndconfig->xpos == GLFW_ANY_POSITION ||
wndconfig->ypos == GLFW_ANY_POSITION)
{ {
[(NSWindow*) window->ns.object center]; [(NSWindow*) window->ns.object center];
_glfw.ns.cascadePoint = _glfw.ns.cascadePoint =
NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint:
NSPointFromCGPoint(_glfw.ns.cascadePoint)]); NSPointFromCGPoint(_glfw.ns.cascadePoint)]);
}
if (wndconfig->resizable) if (wndconfig->resizable)
{ {

View File

@ -396,6 +396,8 @@ struct _GLFWinitconfig
// //
struct _GLFWwndconfig struct _GLFWwndconfig
{ {
int xpos;
int ypos;
int width; int width;
int height; int height;
const char* title; const char* title;

View File

@ -81,9 +81,18 @@ static int createNativeWindow(_GLFWwindow* window,
if (window->monitor) if (window->monitor)
fitToMonitor(window); fitToMonitor(window);
else else
{
if (wndconfig->xpos == GLFW_ANY_POSITION && wndconfig->ypos == GLFW_ANY_POSITION)
{ {
window->null.xpos = 17; window->null.xpos = 17;
window->null.ypos = 17; window->null.ypos = 17;
}
else
{
window->null.xpos = wndconfig->xpos;
window->null.ypos = wndconfig->ypos;
}
window->null.width = wndconfig->width; window->null.width = wndconfig->width;
window->null.height = wndconfig->height; window->null.height = wndconfig->height;
} }

View File

@ -1316,17 +1316,27 @@ static int createNativeWindow(_GLFWwindow* window,
} }
else else
{ {
xpos = CW_USEDEFAULT; RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
ypos = CW_USEDEFAULT;
window->win32.maximized = wndconfig->maximized; window->win32.maximized = wndconfig->maximized;
if (wndconfig->maximized) if (wndconfig->maximized)
style |= WS_MAXIMIZE; style |= WS_MAXIMIZE;
getFullWindowSize(style, exStyle, AdjustWindowRectEx(&rect, style, FALSE, exStyle);
wndconfig->width, wndconfig->height,
&fullWidth, &fullHeight, if (wndconfig->xpos == GLFW_ANY_POSITION && wndconfig->ypos == GLFW_ANY_POSITION)
USER_DEFAULT_SCREEN_DPI); {
xpos = CW_USEDEFAULT;
ypos = CW_USEDEFAULT;
}
else
{
xpos = wndconfig->xpos + rect.left;
ypos = wndconfig->ypos + rect.top;
}
fullWidth = rect.right - rect.left;
fullHeight = rect.bottom - rect.top;
} }
wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);

View File

@ -274,6 +274,8 @@ void glfwDefaultWindowHints(void)
_glfw.hints.window.autoIconify = GLFW_TRUE; _glfw.hints.window.autoIconify = GLFW_TRUE;
_glfw.hints.window.centerCursor = GLFW_TRUE; _glfw.hints.window.centerCursor = GLFW_TRUE;
_glfw.hints.window.focusOnShow = GLFW_TRUE; _glfw.hints.window.focusOnShow = GLFW_TRUE;
_glfw.hints.window.xpos = GLFW_ANY_POSITION;
_glfw.hints.window.ypos = GLFW_ANY_POSITION;
// The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil,
// double buffered // double buffered
@ -368,6 +370,12 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_VISIBLE: case GLFW_VISIBLE:
_glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE;
return; return;
case GLFW_POSITION_X:
_glfw.hints.window.xpos = value;
return;
case GLFW_POSITION_Y:
_glfw.hints.window.ypos = value;
return;
case GLFW_COCOA_RETINA_FRAMEBUFFER: case GLFW_COCOA_RETINA_FRAMEBUFFER:
_glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE;
return; return;

View File

@ -575,6 +575,14 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
height *= _glfw.x11.contentScaleY; height *= _glfw.x11.contentScaleY;
} }
int xpos = 0, ypos = 0;
if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION)
{
xpos = wndconfig->xpos;
ypos = wndconfig->ypos;
}
// Create a colormap based on the visual used by the current context // Create a colormap based on the visual used by the current context
window->x11.colormap = XCreateColormap(_glfw.x11.display, window->x11.colormap = XCreateColormap(_glfw.x11.display,
_glfw.x11.root, _glfw.x11.root,
@ -595,7 +603,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
window->x11.parent = _glfw.x11.root; window->x11.parent = _glfw.x11.root;
window->x11.handle = XCreateWindow(_glfw.x11.display, window->x11.handle = XCreateWindow(_glfw.x11.display,
_glfw.x11.root, _glfw.x11.root,
0, 0, // Position xpos, ypos,
width, height, width, height,
0, // Border width 0, // Border width
depth, // Color depth depth, // Color depth
@ -714,6 +722,15 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
hints->min_height = hints->max_height = height; hints->min_height = hints->max_height = height;
} }
// HACK: Explicitly setting PPosition to any value causes some WMs, notably
// Compiz and Metacity, to honor the position of unmapped windows
if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION)
{
hints->flags |= PPosition;
hints->x = 0;
hints->y = 0;
}
hints->flags |= PWinGravity; hints->flags |= PWinGravity;
hints->win_gravity = StaticGravity; hints->win_gravity = StaticGravity;

View File

@ -96,10 +96,11 @@ int main(void)
if (!glfwInit()) if (!glfwInit())
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
glfwWindowHint(GLFW_POSITION_X, 200 + 250 * i);
glfwWindowHint(GLFW_POSITION_Y, 200);
threads[i].window = glfwCreateWindow(200, 200, threads[i].window = glfwCreateWindow(200, 200,
threads[i].title, threads[i].title,
NULL, NULL); NULL, NULL);
@ -110,9 +111,6 @@ int main(void)
} }
glfwSetKeyCallback(threads[i].window, key_callback); glfwSetKeyCallback(threads[i].window, key_callback);
glfwSetWindowPos(threads[i].window, 200 + 250 * i, 200);
glfwShowWindow(threads[i].window);
} }
glfwMakeContextCurrent(threads[0].window); glfwMakeContextCurrent(threads[0].window);