Files
OpsLog/screenclamp_test.go
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

73 lines
2.7 KiB
Go

package main
import "testing"
// The layout that produced the report: four monitors in a row, the leftmost at
// x=-7680, and a window.json holding exactly that corner.
func fourAcross() []screenRectOf {
return []screenRectOf{
{X: -7680, Y: 0, W: 2560, H: 1392},
{X: -5120, Y: 0, W: 2560, H: 1392},
{X: -2560, Y: 0, W: 2560, H: 1392},
{X: 0, Y: 0, W: 2560, H: 1392},
}
}
func TestSavedCornerOnTheLeftMostMonitorIsAccepted(t *testing.T) {
if !onAnyScreen(-7680, 0, 2272, 1044, fourAcross()) {
t.Fatal("a window on the left-hand monitor was judged off-screen")
}
}
// The fault the bounding box could not see: monitors do not tile the rectangle
// they span, and a window in the leftover space is invisible while passing a
// bounding-box test.
func TestAHoleBetweenMonitorsIsNotAScreen(t *testing.T) {
screens := []screenRectOf{
{X: 0, Y: 0, W: 1920, H: 1040}, // primary
{X: 1920, Y: -1080, W: 1920, H: 1040}, // second, mounted above and to the right
}
// Inside the bounding box (0..3840, -1080..1040), on neither monitor.
if onAnyScreen(2400, 600, 1200, 800, screens) {
t.Fatal("a window in the gap between two monitors was judged visible")
}
}
func TestAWindowInAHoleIsMovedOntoTheNearestScreen(t *testing.T) {
screens := []screenRectOf{
{X: 0, Y: 0, W: 1920, H: 1040},
{X: 1920, Y: -1080, W: 1920, H: 1040},
}
x, y, moved := clampRectToScreens(2400, 600, 1200, 800, screens)
if !moved {
t.Fatal("the window was left where nobody can see it")
}
if !onAnyScreen(x, y, 1200, 800, screens) {
t.Fatalf("moved to %d,%d, which is still not on a screen", x, y)
}
}
// A window wider than the screen it is moved to must still have its top-left
// corner on that screen — clamping the right edge first would push the corner
// off to the left, which is the same fault wearing a different hat.
func TestAWindowTooBigForTheScreenKeepsItsCornerVisible(t *testing.T) {
screens := []screenRectOf{{X: 0, Y: 0, W: 1280, H: 800}}
x, y, _ := clampRectToScreens(-9000, -9000, 2560, 1440, screens)
if x < 0 || y < 0 {
t.Fatalf("corner at %d,%d is off the screen", x, y)
}
}
// The saved position is only refused when it is genuinely lost. The rule is
// overlapsEnough's: a real slab of title bar — 160x32 — has to be visible, so a
// window hanging well over the edge is kept and a sliver is not.
func TestAWindowMostlyOffTheEdgeKeepsEnoughToGrab(t *testing.T) {
screens := []screenRectOf{{X: 0, Y: 0, W: 1920, H: 1040}}
if !onAnyScreen(1700, 900, 1200, 800, screens) { // 220 px still showing
t.Fatal("a window with 220 px on screen was judged off-screen")
}
if onAnyScreen(1850, 900, 1200, 800, screens) { // 70 px: not enough to grab
t.Fatal("a 70 px sliver was judged grabbable")
}
}