@@ -916,22 +916,38 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 916 | }; | 916 | }; |
| 917 | | 917 | |
| 918 | // Check if any window is further in the requested direction on same monitor | 918 | // Check if any window is further in the requested direction on same monitor |
| 919 | - let has_window_in_direction = clients.iter().any(|client| { | 919 | + // For Up/Down: use monitor proximity instead of window detection (bars/panels cause false positives) |
| 920 | - let mon = client.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32; | 920 | + let mon_logical_h = (focused_monitor.height as f32 / focused_monitor.scale).round() as i32; |
| 921 | - if mon != focused_monitor.id { return false; } | 921 | + let has_window_in_direction = match direction { |
| 922 | - | 922 | + Direction::Up => { |
| 923 | - let cx = client.get("at").and_then(|a| a.get(0)).and_then(|x| x.as_i64()).unwrap_or(0) as i32; | 923 | + // Window is at top edge if its top is within 100px of monitor top |
| 924 | - let cy = client.get("at").and_then(|a| a.get(1)).and_then(|y| y.as_i64()).unwrap_or(0) as i32; | 924 | + let near_top = win_y <= focused_monitor.y + 100; |
| 925 | - let cw = client.get("size").and_then(|s| s.get(0)).and_then(|w| w.as_i64()).unwrap_or(0) as i32; | 925 | + !near_top |
| 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, | | |
| 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 | info!(" RECOVERY edge_check: has_window_in_direction={} -> at_edge={}", has_window_in_direction, !has_window_in_direction); | 952 | info!(" RECOVERY edge_check: has_window_in_direction={} -> at_edge={}", has_window_in_direction, !has_window_in_direction); |
| 937 | !has_window_in_direction | 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", | 1761 | + // Calculate monitor bounds in logical coordinates |
| 1746 | - win_x, win_y, win_w, win_h, | 1762 | + let mon_logical_h = (focused_monitor.height as f32 / focused_monitor.scale).round() as i32; |
| 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; } | | |
| 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; | 1764 | + info!(" edge_check: active window at ({},{}) size {}x{}, {} clients on monitor, mon_y={}, mon_h={}", |
| 1755 | - let cy = client.get("at").and_then(|a| a.get(1)).and_then(|y| y.as_i64()).unwrap_or(0) as i32; | 1765 | + win_x, win_y, win_w, win_h, |
| 1756 | - let cw = client.get("size").and_then(|s| s.get(0)).and_then(|w| w.as_i64()).unwrap_or(0) as i32; | 1766 | + clients.iter().filter(|c| c.get("monitor").and_then(|m| m.as_i64()).unwrap_or(-1) as i32 == focused_monitor.id).count(), |
| 1757 | - let ch = client.get("size").and_then(|s| s.get(1)).and_then(|h| h.as_i64()).unwrap_or(0) as i32; | 1767 | + focused_monitor.y, mon_logical_h); |
| 1758 | - | 1768 | + |
| 1759 | - match direction { | 1769 | + // For Up/Down: check if window is near monitor edge (accounts for bars/panels) |
| 1760 | - Direction::Left => cx + cw < win_x + 10, // Window is to the left | 1770 | + // For Left/Right: check if any window is further in that direction |
| 1761 | - Direction::Right => cx > win_x + win_w - 10, // Window is to the right | 1771 | + let has_window_in_direction = match direction { |
| 1762 | - Direction::Up => cy + ch < win_y + 10, | 1772 | + Direction::Up => { |
| 1763 | - Direction::Down => cy > win_y + win_h - 10, | 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 | info!(" edge_check: has_window_in_direction={} -> at_edge={}", has_window_in_direction, !has_window_in_direction); | 1802 | info!(" edge_check: has_window_in_direction={} -> at_edge={}", has_window_in_direction, !has_window_in_direction); |
| 1768 | !has_window_in_direction | 1803 | !has_window_in_direction |