@@ -49,6 +49,7 @@ pub struct MonitorInfo { |
| 49 | 49 | pub y: i32, |
| 50 | 50 | pub width: u32, |
| 51 | 51 | pub height: u32, |
| 52 | + pub scale: f32, |
| 52 | 53 | } |
| 53 | 54 | |
| 54 | 55 | /// Configuration for edge barriers |
@@ -130,27 +131,28 @@ impl EdgeCaptureState { |
| 130 | 131 | let mut used_monitors: Vec<bool> = vec![false; self.config.monitors.len()]; |
| 131 | 132 | let mut assigned_outputs: Vec<bool> = vec![false; self.outputs.len()]; |
| 132 | 133 | |
| 134 | + // Helper to check if a monitor's logical size matches an output |
| 135 | + let sizes_match = |mon: &MonitorInfo, out_w: u32, out_h: u32| -> bool { |
| 136 | + // Use the monitor's actual scale from Hyprland |
| 137 | + let scale = mon.scale as f64; |
| 138 | + let logical_w = (mon.width as f64 / scale).round() as u32; |
| 139 | + let logical_h = (mon.height as f64 / scale).round() as u32; |
| 140 | + |
| 141 | + // Allow 1 pixel tolerance for rounding differences |
| 142 | + let w_match = (logical_w as i32 - out_w as i32).abs() <= 1; |
| 143 | + let h_match = (logical_h as i32 - out_h as i32).abs() <= 1; |
| 144 | + w_match && h_match |
| 145 | + }; |
| 146 | + |
| 133 | 147 | // First pass: try to find unique matches |
| 134 | 148 | for (out_idx, out) in self.outputs.iter_mut().enumerate() { |
| 135 | | - // Find monitors that could match this output size (considering possible scales) |
| 136 | 149 | let mut candidates: Vec<usize> = Vec::new(); |
| 137 | 150 | for (i, mon) in self.config.monitors.iter().enumerate() { |
| 138 | 151 | if used_monitors[i] { |
| 139 | 152 | continue; |
| 140 | 153 | } |
| 141 | | - // Check if sizes match directly |
| 142 | | - if mon.width == out.width && mon.height == out.height { |
| 154 | + if sizes_match(mon, out.width, out.height) { |
| 143 | 155 | candidates.push(i); |
| 144 | | - continue; |
| 145 | | - } |
| 146 | | - // Check common scale factors (1.5, 2.0) |
| 147 | | - for scale in [1.5_f64, 2.0_f64] { |
| 148 | | - let scaled_w = (mon.width as f64 / scale).round() as u32; |
| 149 | | - let scaled_h = (mon.height as f64 / scale).round() as u32; |
| 150 | | - if scaled_w == out.width && scaled_h == out.height { |
| 151 | | - candidates.push(i); |
| 152 | | - break; |
| 153 | | - } |
| 154 | 156 | } |
| 155 | 157 | } |
| 156 | 158 | |
@@ -158,8 +160,8 @@ impl EdgeCaptureState { |
| 158 | 160 | if candidates.len() == 1 { |
| 159 | 161 | let i = candidates[0]; |
| 160 | 162 | let mon = &self.config.monitors[i]; |
| 161 | | - tracing::info!("Matched output {}x{} to monitor {} at ({}, {})", |
| 162 | | - out.width, out.height, mon.name, mon.x, mon.y); |
| 163 | + tracing::info!("Matched output {}x{} to monitor {} at ({}, {}) scale={}", |
| 164 | + out.width, out.height, mon.name, mon.x, mon.y, mon.scale); |
| 163 | 165 | out.x = mon.x; |
| 164 | 166 | out.y = mon.y; |
| 165 | 167 | used_monitors[i] = true; |
@@ -176,21 +178,14 @@ impl EdgeCaptureState { |
| 176 | 178 | if assigned_outputs[out_idx] { |
| 177 | 179 | continue; // Already assigned in first pass |
| 178 | 180 | } |
| 179 | | - // Find first unused monitor that could match |
| 181 | + // Find first unused monitor that matches |
| 180 | 182 | for (i, mon) in self.config.monitors.iter().enumerate() { |
| 181 | 183 | if used_monitors[i] { |
| 182 | 184 | continue; |
| 183 | 185 | } |
| 184 | | - // Check all possible scales |
| 185 | | - let matches = (mon.width == out.width && mon.height == out.height) |
| 186 | | - || [1.5_f64, 2.0_f64].iter().any(|&scale| { |
| 187 | | - let scaled_w = (mon.width as f64 / scale).round() as u32; |
| 188 | | - let scaled_h = (mon.height as f64 / scale).round() as u32; |
| 189 | | - scaled_w == out.width && scaled_h == out.height |
| 190 | | - }); |
| 191 | | - if matches { |
| 192 | | - tracing::info!("Assigned remaining output {}x{} to monitor {} at ({}, {})", |
| 193 | | - out.width, out.height, mon.name, mon.x, mon.y); |
| 186 | + if sizes_match(mon, out.width, out.height) { |
| 187 | + tracing::info!("Assigned remaining output {}x{} to monitor {} at ({}, {}) scale={}", |
| 188 | + out.width, out.height, mon.name, mon.x, mon.y, mon.scale); |
| 194 | 189 | out.x = mon.x; |
| 195 | 190 | out.y = mon.y; |
| 196 | 191 | used_monitors[i] = true; |
@@ -615,26 +610,18 @@ impl PointerHandler for EdgeCaptureState { |
| 615 | 610 | if should_trigger { |
| 616 | 611 | self.last_trigger_time.insert(barrier.direction, now); |
| 617 | 612 | |
| 618 | | - // Calculate screen position |
| 619 | | - let (min_x, min_y, max_x, max_y) = self.screen_bounds(); |
| 620 | | - let screen_pos = match barrier.direction { |
| 621 | | - Direction::Left => (min_x, min_y + y as i32), |
| 622 | | - Direction::Right => (max_x - 1, min_y + y as i32), |
| 623 | | - Direction::Up => (min_x + x as i32, min_y), |
| 624 | | - Direction::Down => (min_x + x as i32, max_y - 1), |
| 625 | | - }; |
| 626 | | - |
| 613 | + // Send edge event with direction only |
| 614 | + // Main daemon will query Hyprland for actual cursor position |
| 615 | + // and verify we're at screen boundary before triggering transfer |
| 627 | 616 | let edge_event = EdgeEvent { |
| 628 | 617 | direction: barrier.direction, |
| 629 | | - position: screen_pos, |
| 618 | + position: (0, 0), // Placeholder - main daemon uses Hyprland cursor pos |
| 630 | 619 | timestamp: now, |
| 631 | 620 | }; |
| 632 | 621 | |
| 633 | 622 | tracing::info!( |
| 634 | | - "Edge trigger: {:?} at screen ({}, {})", |
| 635 | | - barrier.direction, |
| 636 | | - screen_pos.0, |
| 637 | | - screen_pos.1 |
| 623 | + "Edge event: {:?} barrier triggered", |
| 624 | + barrier.direction |
| 638 | 625 | ); |
| 639 | 626 | |
| 640 | 627 | let _ = self.event_tx.send(edge_event); |