@@ -916,22 +916,38 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 916 | 916 | }; |
| 917 | 917 | |
| 918 | 918 | // Check if any window is further in the requested direction on same monitor |
| 919 | | - let has_window_in_direction = clients.iter().any(|client| { |
| 920 | | - let mon = client.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32; |
| 921 | | - if mon != focused_monitor.id { return false; } |
| 922 | | - |
| 923 | | - let cx = client.get("at").and_then(|a| a.get(0)).and_then(|x| x.as_i64()).unwrap_or(0) as i32; |
| 924 | | - let cy = client.get("at").and_then(|a| a.get(1)).and_then(|y| y.as_i64()).unwrap_or(0) as i32; |
| 925 | | - let cw = client.get("size").and_then(|s| s.get(0)).and_then(|w| w.as_i64()).unwrap_or(0) as i32; |
| 926 | | - let ch = client.get("size").and_then(|s| s.get(1)).and_then(|h| h.as_i64()).unwrap_or(0) as i32; |
| 927 | | - |
| 928 | | - match direction { |
| 929 | | - Direction::Left => cx + cw < win_x + 10, |
| 930 | | - Direction::Right => cx > win_x + win_w - 10, |
| 931 | | - Direction::Up => cy + ch < win_y + 10, |
| 932 | | - Direction::Down => cy > win_y + win_h - 10, |
| 919 | + // For Up/Down: use monitor proximity instead of window detection (bars/panels cause false positives) |
| 920 | + let mon_logical_h = (focused_monitor.height as f32 / focused_monitor.scale).round() as i32; |
| 921 | + let has_window_in_direction = match direction { |
| 922 | + Direction::Up => { |
| 923 | + // Window is at top edge if its top is within 100px of monitor top |
| 924 | + let near_top = win_y <= focused_monitor.y + 100; |
| 925 | + !near_top |
| 933 | 926 | } |
| 934 | | - }); |
| 927 | + Direction::Down => { |
| 928 | + // Window is at bottom edge if its bottom is within 100px of monitor bottom |
| 929 | + let win_bottom = win_y + win_h; |
| 930 | + let mon_bottom = focused_monitor.y + mon_logical_h; |
| 931 | + let near_bottom = win_bottom >= mon_bottom - 100; |
| 932 | + !near_bottom |
| 933 | + } |
| 934 | + Direction::Left | Direction::Right => { |
| 935 | + // Window-based detection for horizontal directions |
| 936 | + clients.iter().any(|client| { |
| 937 | + let mon = client.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32; |
| 938 | + if mon != focused_monitor.id { return false; } |
| 939 | + |
| 940 | + let cx = client.get("at").and_then(|a| a.get(0)).and_then(|x| x.as_i64()).unwrap_or(0) as i32; |
| 941 | + let cw = client.get("size").and_then(|s| s.get(0)).and_then(|w| w.as_i64()).unwrap_or(0) as i32; |
| 942 | + |
| 943 | + match direction { |
| 944 | + Direction::Left => cx + cw < win_x + 10, |
| 945 | + Direction::Right => cx > win_x + win_w - 10, |
| 946 | + _ => false, |
| 947 | + } |
| 948 | + }) |
| 949 | + } |
| 950 | + }; |
| 935 | 951 | |
| 936 | 952 | info!(" RECOVERY edge_check: has_window_in_direction={} -> at_edge={}", has_window_in_direction, !has_window_in_direction); |
| 937 | 953 | !has_window_in_direction |
@@ -1742,27 +1758,46 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 1742 | 1758 | } |
| 1743 | 1759 | }; |
| 1744 | 1760 | |
| 1745 | | - info!(" edge_check: active window at ({},{}) size {}x{}, {} clients on monitor", |
| 1746 | | - win_x, win_y, win_w, win_h, |
| 1747 | | - clients.iter().filter(|c| c.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32 == focused_monitor.id).count()); |
| 1748 | | - |
| 1749 | | - // Check if any window is further in the requested direction on same monitor |
| 1750 | | - let has_window_in_direction = clients.iter().any(|client| { |
| 1751 | | - let mon = client.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32; |
| 1752 | | - if mon != focused_monitor.id { return false; } |
| 1761 | + // Calculate monitor bounds in logical coordinates |
| 1762 | + let mon_logical_h = (focused_monitor.height as f32 / focused_monitor.scale).round() as i32; |
| 1753 | 1763 | |
| 1754 | | - let cx = client.get("at").and_then(|a| a.get(0)).and_then(|x| x.as_i64()).unwrap_or(0) as i32; |
| 1755 | | - let cy = client.get("at").and_then(|a| a.get(1)).and_then(|y| y.as_i64()).unwrap_or(0) as i32; |
| 1756 | | - let cw = client.get("size").and_then(|s| s.get(0)).and_then(|w| w.as_i64()).unwrap_or(0) as i32; |
| 1757 | | - let ch = client.get("size").and_then(|s| s.get(1)).and_then(|h| h.as_i64()).unwrap_or(0) as i32; |
| 1758 | | - |
| 1759 | | - match direction { |
| 1760 | | - Direction::Left => cx + cw < win_x + 10, // Window is to the left |
| 1761 | | - Direction::Right => cx > win_x + win_w - 10, // Window is to the right |
| 1762 | | - Direction::Up => cy + ch < win_y + 10, |
| 1763 | | - Direction::Down => cy > win_y + win_h - 10, |
| 1764 | + info!(" edge_check: active window at ({},{}) size {}x{}, {} clients on monitor, mon_y={}, mon_h={}", |
| 1765 | + win_x, win_y, win_w, win_h, |
| 1766 | + clients.iter().filter(|c| c.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32 == focused_monitor.id).count(), |
| 1767 | + focused_monitor.y, mon_logical_h); |
| 1768 | + |
| 1769 | + // For Up/Down: check if window is near monitor edge (accounts for bars/panels) |
| 1770 | + // For Left/Right: check if any window is further in that direction |
| 1771 | + let has_window_in_direction = match direction { |
| 1772 | + Direction::Up => { |
| 1773 | + // Window is at top edge if its top is within 100px of monitor top (allows for bars) |
| 1774 | + let near_top = win_y <= focused_monitor.y + 100; |
| 1775 | + !near_top // has_window_in_direction = !near_top, so at_edge = near_top |
| 1764 | 1776 | } |
| 1765 | | - }); |
| 1777 | + Direction::Down => { |
| 1778 | + // Window is at bottom edge if its bottom is within 100px of monitor bottom |
| 1779 | + let win_bottom = win_y + win_h; |
| 1780 | + let mon_bottom = focused_monitor.y + mon_logical_h; |
| 1781 | + let near_bottom = win_bottom >= mon_bottom - 100; |
| 1782 | + !near_bottom // has_window_in_direction = !near_bottom, so at_edge = near_bottom |
| 1783 | + } |
| 1784 | + Direction::Left | Direction::Right => { |
| 1785 | + // For Left/Right, use window-based detection |
| 1786 | + clients.iter().any(|client| { |
| 1787 | + let mon = client.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32; |
| 1788 | + if mon != focused_monitor.id { return false; } |
| 1789 | + |
| 1790 | + let cx = client.get("at").and_then(|a| a.get(0)).and_then(|x| x.as_i64()).unwrap_or(0) as i32; |
| 1791 | + let cw = client.get("size").and_then(|s| s.get(0)).and_then(|w| w.as_i64()).unwrap_or(0) as i32; |
| 1792 | + |
| 1793 | + match direction { |
| 1794 | + Direction::Left => cx + cw < win_x + 10, |
| 1795 | + Direction::Right => cx > win_x + win_w - 10, |
| 1796 | + _ => false, |
| 1797 | + } |
| 1798 | + }) |
| 1799 | + } |
| 1800 | + }; |
| 1766 | 1801 | |
| 1767 | 1802 | info!(" edge_check: has_window_in_direction={} -> at_edge={}", has_window_in_direction, !has_window_in_direction); |
| 1768 | 1803 | !has_window_in_direction |