|
| 1 | +Multi-Monitor / Virtual-Desktop Geometry |
| 2 | +======================================== |
| 3 | + |
| 4 | +``snap_window``, ``arrange_grid`` and the layout planner all take a single primary |
| 5 | +``(width, height)`` — they are monitor-blind: they cannot tile on the second display |
| 6 | +or cope with a negative-origin virtual desktop, and ``coordinate_space`` only rescales |
| 7 | +a model grid. This adds the missing physical layer: enumerate the monitors, compute |
| 8 | +the union virtual bounds, ask which monitor contains a point or a window, convert |
| 9 | +between virtual and per-monitor-local coordinates, and remap a point to the equivalent |
| 10 | +spot on another display. |
| 11 | + |
| 12 | +The geometry is pure arithmetic over plain ``Monitor`` dataclasses, so it is fully |
| 13 | +unit-testable; only ``enumerate_monitors``' default provider touches the OS (via |
| 14 | +``mss``) and it is injectable. Imports no ``PySide6``. |
| 15 | + |
| 16 | +Headless API |
| 17 | +------------ |
| 18 | + |
| 19 | +.. code-block:: python |
| 20 | +
|
| 21 | + from je_auto_control import (enumerate_monitors, monitor_at_point, |
| 22 | + virtual_bounds, to_local, remap_point) |
| 23 | +
|
| 24 | + monitors = enumerate_monitors() |
| 25 | + print(virtual_bounds(monitors)) # (x, y, w, h) spanning all displays |
| 26 | +
|
| 27 | + here = monitor_at_point(monitors, x, y) # which monitor owns this point |
| 28 | + idx, lx, ly = to_local(monitors, x, y) # virtual -> (monitor, local x, local y) |
| 29 | +
|
| 30 | + # Move a point to the equivalent relative spot on another monitor. |
| 31 | + second = remap_point(monitors[0], monitors[1], 960, 540) |
| 32 | +
|
| 33 | +``Monitor`` carries ``index, x, y, width, height, scale, primary`` and a ``work`` |
| 34 | +area (``.bounds`` / ``.contains(x, y)`` / ``.to_dict()``). ``virtual_bounds`` returns |
| 35 | +the union box (origin may be negative); ``primary_monitor`` picks the primary; |
| 36 | +``monitor_for_window(rect, monitors)`` returns the display a window mostly occupies |
| 37 | +(max overlap); ``to_virtual`` is the inverse of ``to_local``; ``remap_point`` |
| 38 | +preserves the fractional position so it works across differing resolutions and DPI. |
| 39 | + |
| 40 | +Executor commands |
| 41 | +----------------- |
| 42 | + |
| 43 | +``AC_enumerate_monitors`` → ``{count, monitors, virtual_bounds}`` and |
| 44 | +``AC_monitor_at_point`` (``x`` / ``y``) → ``{found, monitor}``. They are exposed as |
| 45 | +the MCP tools ``ac_enumerate_monitors`` / ``ac_monitor_at_point`` and as Script |
| 46 | +Builder commands under **Window**. |
0 commit comments