Wayland: guess the origin (0,0) output for a fresh window's fractional scale

When there is no focused or recently-focused window to key off of (the
typical first-window-at-startup case), glfwGetWaylandCurrentMonitorFractionalScale
fell back to the registry-first output (monitors[0]). Registry/connector
order is semi-arbitrary and uncorrelated with where a compositor actually
places a new window, so on a multi-monitor setup with mixed fractional
scales the first window could be born at the wrong size.

Prefer the output at the logical origin (0, 0) instead: the compositor's
top-left/primary output is a better guess for initial placement than
registry order. Fall back to monitors[0] when no output reports (0, 0).

Since xdg_output geometry (including position) arrives asynchronously and
x/y read zero until it does, wait for every monitor's logical geometry
before trusting the reported positions, so an as-yet-unpositioned output
cannot spuriously match (0, 0).

Discussed in #10268.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VjwP6hQjA6GMuiM7Mf9VAx
This commit is contained in:
Dan Egnor
2026-07-23 12:34:19 -07:00
parent 51116e5df3
commit 17d5103652

27
glfw/wl_monitor.c vendored
View File

@@ -351,8 +351,31 @@ GLFWAPI double glfwGetWaylandCurrentMonitorFractionalScale(void)
if (window && window->wl.monitorsCount > 0)
monitor = window->wl.monitors[0];
}
if (!monitor)
monitor = _glfw.monitors[0];
if (!monitor) {
// No focused window to key off of (typically the first window at
// startup). Prefer the output at the logical origin (0, 0): the
// compositor's top-left/primary output is a better guess for where a
// new window will be placed than registry order. Fall back to the
// first output if no output reports position (0, 0).
//
// xdg_output geometry arrives asynchronously and x/y remain zero until
// it does, so wait for every monitor's logical geometry before trusting
// the reported positions (otherwise an as-yet-unpositioned output would
// spuriously match (0, 0)).
for (int i = 0; i < _glfw.monitorCount; i++) {
_GLFWmonitor *m = _glfw.monitors[i];
while (m->wl.xdg_output && m->wl.xdg_logical_width <= 0) {
if (wl_display_roundtrip(_glfw.wl.display) < 0) break;
}
}
for (int i = 0; i < _glfw.monitorCount; i++) {
_GLFWmonitor *m = _glfw.monitors[i];
if (m->wl.xdg_output && m->wl.xdg_logical_width > 0 &&
m->wl.x == 0 && m->wl.y == 0) { monitor = m; break; }
}
if (!monitor)
monitor = _glfw.monitors[0];
}
if (!monitor->wl.xdg_output)
return monitor->wl.scale > 0 ? (double)monitor->wl.scale : 1.0;