linux observer
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4"""Monitor position assignment based on geometry.
5
6Extracted from solstone's observe/utils.py — the assign_monitor_positions()
7function only. Also remains in solstone core (used by server-side naming).
8"""
9
10from __future__ import annotations
11
12
13def assign_monitor_positions(monitors: list[dict]) -> list[dict]:
14 """
15 Assign position labels to monitors based on relative positions.
16
17 Uses pairwise comparison to determine positions. Vertical labels (top/bottom)
18 are only assigned when monitors actually overlap horizontally, avoiding
19 phantom relationships from offset monitors.
20
21 Parameters
22 ----------
23 monitors : list[dict]
24 List of monitor dicts, each with keys:
25 - id: Monitor identifier (e.g., "DP-3", "HDMI-1")
26 - box: [x1, y1, x2, y2] coordinates
27
28 Returns
29 -------
30 list[dict]
31 Same monitors with "position" key added to each:
32 - "center": No monitors on both sides
33 - "left"/"right": Horizontal position
34 - "top"/"bottom": Vertical position (only with horizontal overlap)
35 - "left-top", "right-bottom", etc.: Corner positions
36 """
37 if not monitors:
38 return []
39
40 if len(monitors) == 1:
41 monitors[0]["position"] = "center"
42 return monitors
43
44 # Tolerance for center classification
45 epsilon = 1
46
47 for m in monitors:
48 x1, y1, x2, y2 = m["box"]
49 center_x = (x1 + x2) / 2
50 center_y = (y1 + y2) / 2
51
52 has_left = False
53 has_right = False
54 has_above = False
55 has_below = False
56
57 for other in monitors:
58 if other is m:
59 continue
60
61 ox1, oy1, ox2, oy2 = other["box"]
62 other_center_x = (ox1 + ox2) / 2
63 other_center_y = (oy1 + oy2) / 2
64
65 # Horizontal relationship (always check)
66 if other_center_x < center_x - epsilon:
67 has_left = True
68 elif other_center_x > center_x + epsilon:
69 has_right = True
70
71 # Vertical relationship only if horizontal overlap exists
72 # Overlap means ranges intersect (not just touch)
73 h_overlap = (x1 < ox2) and (x2 > ox1)
74 if h_overlap:
75 if other_center_y < center_y - epsilon:
76 has_above = True
77 elif other_center_y > center_y + epsilon:
78 has_below = True
79
80 # Determine horizontal label
81 if has_left and has_right:
82 h_pos = "center"
83 elif has_left:
84 h_pos = "right"
85 elif has_right:
86 h_pos = "left"
87 else:
88 h_pos = "center"
89
90 # Determine vertical label (only if monitors above/below with overlap)
91 if has_above and has_below:
92 v_pos = "middle"
93 elif has_above:
94 v_pos = "bottom"
95 elif has_below:
96 v_pos = "top"
97 else:
98 v_pos = None
99
100 # Combine positions
101 if v_pos is None:
102 position = h_pos
103 elif h_pos == "center":
104 position = v_pos
105 else:
106 position = f"{h_pos}-{v_pos}"
107
108 m["position"] = position
109
110 return monitors