@@ -168,6 +168,41 @@ pub fn discover_displays() -> Vec<crate::core::monitor::Monitor> { |
| 168 | 168 | }); |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | + // macOS reserves the menu-bar zone globally in CG y-coords, even on |
| 172 | + // non-primary displays whose NSScreen.visibleFrame falsely reports |
| 173 | + // the full frame as usable. Symptoms: AX position requests above the |
| 174 | + // primary's menu-bar bottom get clamped on the secondary, leaving |
| 175 | + // visible top gap missing and pushing the bottom past the requested |
| 176 | + // rect (observed on a 3440×1440 widescreen positioned to the left of |
| 177 | + // a Retina laptop, where the OS clamped any requested y<30 to y=30). |
| 178 | + // Reservation: clamp every non-primary display whose usable_frame |
| 179 | + // top is above the primary's usable_frame top (in CG coords) — but |
| 180 | + // only when their CG regions overlap on the y-axis at all. |
| 181 | + if let Some(primary_top) = monitors |
| 182 | + .iter() |
| 183 | + .find(|m| m.is_primary) |
| 184 | + .map(|m| m.usable_frame.y) |
| 185 | + { |
| 186 | + for m in monitors.iter_mut() { |
| 187 | + if m.is_primary { |
| 188 | + continue; |
| 189 | + } |
| 190 | + let bottom = m.usable_frame.y + m.usable_frame.height; |
| 191 | + if m.usable_frame.y < primary_top && bottom > primary_top { |
| 192 | + let dy = primary_top - m.usable_frame.y; |
| 193 | + m.usable_frame.y = primary_top; |
| 194 | + m.usable_frame.height = (m.usable_frame.height - dy).max(0.0); |
| 195 | + tracing::debug!( |
| 196 | + id = m.id, |
| 197 | + reserved_top = dy, |
| 198 | + new_y = m.usable_frame.y, |
| 199 | + new_h = m.usable_frame.height, |
| 200 | + "applied global menu-bar reservation to secondary display" |
| 201 | + ); |
| 202 | + } |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 171 | 206 | tracing::info!(count = monitors.len(), "displays discovered"); |
| 172 | 207 | for m in &monitors { |
| 173 | 208 | tracing::debug!( |