Files
OpsLog/screenclamp.go
T
rouggy 6536d140ba fix(window): check the saved position against the monitors, not their bounding box
Reported from a multi-monitor station: window.json held x=-7680 and the
window opened where nobody could see it, with no way back short of
editing the file — which nobody knows to do.

The guard existed but asked the wrong question. It tested the position
against the VIRTUAL SCREEN, the rectangle spanning every monitor, and
monitors rarely tile that rectangle: a wide screen beside a tall one, or
one mounted higher, leaves gaps inside the box that belong to no monitor.
A window in a gap passes a bounding-box test and is invisible. The test
now walks the monitors themselves, through EnumDisplayMonitors, and uses
each one's WORK area — a title bar under the taskbar cannot be dragged
either.

A position that is genuinely lost is now MOVED onto the nearest monitor,
keeping the window's size. Handing it back to Windows lost the size too
and dropped the window on the primary screen wherever Windows chose.

The arithmetic is in screenclamp.go with no Win32 in it, and tested
against the reporter's four-monitor layout and against a gap between two:
this fault is invisible by definition and cannot be reproduced without
the reporter's screens, so a table test is the only place it can be held.
The layout is also logged at every start, since the first question after
'OpsLog does not open' is what the screens looked like.
2026-08-25 09:53:34 +02:00

76 lines
2.2 KiB
Go

package main
// Placing a window on a set of monitors — the arithmetic, with no Win32 in it.
//
// Kept apart from the platform code so it can be tested: the fault it guards
// against (a window that opens where nobody can see it) is reported as "OpsLog
// does not start", is invisible by definition, and cannot be reproduced without
// the reporter's screen layout. A table test can hold that layout.
// screenRect is one monitor's work area, in virtual-desktop coordinates.
type screenRect struct{ X, Y, W, H int }
// screenRectOf is the same shape under the name the helpers below read with.
type screenRectOf = screenRect
// onAnyScreen reports whether a window would land where it can be seen and
// grabbed on ONE of the screens.
//
// One of them, not their bounding box: monitors rarely tile the box they span,
// and the leftover rectangles belong to no screen at all. A window in one of
// those holes passes a bounding-box test and is invisible.
func onAnyScreen(x, y, w, h int, screens []screenRectOf) bool {
for _, r := range screens {
if overlapsEnough(x, y, w, h, r.X, r.Y, r.W, r.H) {
return true
}
}
return false
}
// nearestScreen picks the screen whose centre is closest to the window's.
func nearestScreen(x, y, w, h int, screens []screenRectOf) (screenRectOf, bool) {
if len(screens) == 0 {
return screenRectOf{}, false
}
best, bestDist := screens[0], int64(1)<<62
cx, cy := x+w/2, y+h/2
for _, r := range screens {
rcx, rcy := r.X+r.W/2, r.Y+r.H/2
dx, dy := int64(cx-rcx), int64(cy-rcy)
if d := dx*dx + dy*dy; d < bestDist {
best, bestDist = r, d
}
}
return best, true
}
// clampRectToScreens moves a window onto the nearest screen, keeping its size
// where the screen can hold it. Returns the new position and whether it moved.
func clampRectToScreens(x, y, w, h int, screens []screenRectOf) (int, int, bool) {
best, ok := nearestScreen(x, y, w, h, screens)
if !ok {
return x, y, false
}
if w > best.W {
w = best.W
}
if h > best.H {
h = best.H
}
nx, ny := x, y
if nx < best.X {
nx = best.X
}
if ny < best.Y {
ny = best.Y
}
if nx+w > best.X+best.W {
nx = best.X + best.W - w
}
if ny+h > best.Y+best.H {
ny = best.Y + best.H - h
}
return nx, ny, nx != x || ny != y
}