//go:build windows package main import ( "fmt" "strings" "syscall" "unsafe" "hamlog/internal/applog" ) // GetSystemMetrics indices for the virtual desktop โ€” the rectangle spanning // every attached monitor. const ( smXVirtualScreen = 76 smYVirtualScreen = 77 smCXVirtualScreen = 78 smCYVirtualScreen = 79 ) var ( user32Dll = syscall.NewLazyDLL("user32.dll") procGetSystemMetrics = user32Dll.NewProc("GetSystemMetrics") procEnumDisplayMonitors = user32Dll.NewProc("EnumDisplayMonitors") procGetMonitorInfoW = user32Dll.NewProc("GetMonitorInfoW") ) func systemMetric(index int) int { r, _, _ := procGetSystemMetrics.Call(uintptr(index)) return int(int32(r)) // signed: the virtual desktop origin is negative with a monitor to the left } // virtualScreenBounds returns the rectangle covering all monitors, and whether // it could be determined at all. func virtualScreenBounds() (x, y, w, h int, ok bool) { w, h = systemMetric(smCXVirtualScreen), systemMetric(smCYVirtualScreen) if w <= 0 || h <= 0 { return 0, 0, 0, 0, false } return systemMetric(smXVirtualScreen), systemMetric(smYVirtualScreen), w, h, true } // winRect is Win32's RECT. type winRect struct{ Left, Top, Right, Bottom int32 } // monitorInfo is MONITORINFO: the monitor's whole rectangle and its work area // (what is left once the taskbar is taken out). type monitorInfo struct { CbSize uint32 RcMonitor winRect RcWork winRect DwFlags uint32 } // monitorRects enumerates the attached monitors. // // THE BOUNDING BOX IS NOT THE DESKTOP. The virtual screen is the rectangle that // spans every monitor, and monitors are rarely arranged to fill it: a wide // screen beside a tall one, or one offset vertically, leaves rectangular HOLES // inside the box that belong to no monitor at all. A window placed in a hole // passes a bounding-box test and is invisible โ€” which is exactly what was // reported from a four-monitor station whose window.json held x=-7680. // // So the test has to be against the monitors themselves. func monitorRects() []screenRect { var out []screenRect cb := syscall.NewCallback(func(hMonitor, hdc uintptr, lprc *winRect, data uintptr) uintptr { var mi monitorInfo mi.CbSize = uint32(unsafe.Sizeof(mi)) if r, _, _ := procGetMonitorInfoW.Call(hMonitor, uintptr(unsafe.Pointer(&mi))); r != 0 { // The WORK area, not the full rectangle: a title bar under the // taskbar is a window that cannot be dragged, which is the fault // being guarded against in the first place. out = append(out, screenRect{ X: int(mi.RcWork.Left), Y: int(mi.RcWork.Top), W: int(mi.RcWork.Right - mi.RcWork.Left), H: int(mi.RcWork.Bottom - mi.RcWork.Top), }) } return 1 // keep enumerating }) procEnumDisplayMonitors.Call(0, 0, cb, 0) return out } // describeMonitors renders the layout for the log. A window that opens where // nobody can see it is reported as "OpsLog did not start", and the first // question is what the screens looked like at that moment. func describeMonitors(rects []screenRect) string { if len(rects) == 0 { return "none detected" } parts := make([]string, 0, len(rects)) for _, r := range rects { parts = append(parts, fmt.Sprintf("%dx%d at %d,%d", r.W, r.H, r.X, r.Y)) } return strings.Join(parts, " ยท ") } // onSomeMonitorImpl reports whether a window at these coordinates would land // where it can be seen and grabbed. func onSomeMonitorImpl(x, y, w, h int) bool { rects := monitorRects() if len(rects) == 0 { // Enumeration failed. Fall back to the bounding box rather than refuse // the operator's own saved position on the strength of a failed call. vx, vy, vw, vh, ok := virtualScreenBounds() if !ok { return true } return overlapsEnough(x, y, w, h, vx, vy, vw, vh) } return onAnyScreen(x, y, w, h, rects) } // clampToVisible moves a window rectangle onto the monitor it is closest to. func clampToVisible(x, y, w, h int) (int, int, bool) { return clampRectToScreens(x, y, w, h, monitorRects()) } // logMonitorLayout writes the current screen arrangement once at startup. func logMonitorLayout() { applog.Printf("window: monitors โ€” %s", describeMonitors(monitorRects())) }