@@ -967,6 +967,10 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 967 | 967 | |
| 968 | 968 | if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { |
| 969 | 969 | info!("RECOVERY HOTKEY: Barrier enabled, blocking transfer"); |
| 970 | + } else if !input_grabber.has_devices() { |
| 971 | + // Should never happen (no devices = no recovery hotkey events) |
| 972 | + // but guard against it anyway |
| 973 | + tracing::debug!("RECOVERY HOTKEY: No input devices, cannot initiate"); |
| 970 | 974 | } else { |
| 971 | 975 | info!("RECOVERY HOTKEY: At edge with peer, initiating transfer to {:?}", direction); |
| 972 | 976 | if let Err(e) = transfer_manager.initiate_transfer( |
@@ -1076,6 +1080,14 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 1076 | 1080 | cursor_pos.0, |
| 1077 | 1081 | cursor_pos.1 |
| 1078 | 1082 | ); |
| 1083 | + } else if !input_grabber.has_devices() { |
| 1084 | + // No input devices = cannot initiate transfers (deviceless machine) |
| 1085 | + tracing::debug!( |
| 1086 | + "EDGE: {:?} at ({}, {}) - no input devices, cannot initiate", |
| 1087 | + direction, |
| 1088 | + cursor_pos.0, |
| 1089 | + cursor_pos.1 |
| 1090 | + ); |
| 1079 | 1091 | } else { |
| 1080 | 1092 | info!( |
| 1081 | 1093 | "EDGE: {:?} at ({}, {}) - initiating transfer", |
@@ -1220,6 +1232,12 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 1220 | 1232 | "CURSOR EDGE: {:?} at ({}, {}) - barrier enabled, blocking", |
| 1221 | 1233 | edge_dir, cx, cy |
| 1222 | 1234 | ); |
| 1235 | + } else if !input_grabber.has_devices() { |
| 1236 | + // No input devices = cannot initiate transfers |
| 1237 | + tracing::debug!( |
| 1238 | + "CURSOR EDGE: {:?} at ({}, {}) - no input devices, cannot initiate", |
| 1239 | + edge_dir, cx, cy |
| 1240 | + ); |
| 1223 | 1241 | } else { |
| 1224 | 1242 | info!( |
| 1225 | 1243 | "CURSOR EDGE: {:?} at ({}, {}) - initiating transfer", |
@@ -1836,6 +1854,8 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 1836 | 1854 | // At edge with peer but received control from different direction |
| 1837 | 1855 | if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { |
| 1838 | 1856 | IpcResponse::Error { message: "Barrier enabled".to_string() } |
| 1857 | + } else if !input_grabber.has_devices() { |
| 1858 | + IpcResponse::Error { message: "No input devices - cannot initiate transfer".to_string() } |
| 1839 | 1859 | } else { |
| 1840 | 1860 | // Initiate new transfer |
| 1841 | 1861 | let cursor_pos = hypr_client.cursor_pos().await |
@@ -1879,6 +1899,8 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 1879 | 1899 | } |
| 1880 | 1900 | } else if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { |
| 1881 | 1901 | IpcResponse::Error { message: "Barrier enabled".to_string() } |
| 1902 | + } else if !input_grabber.has_devices() { |
| 1903 | + IpcResponse::Error { message: "No input devices - cannot initiate transfer".to_string() } |
| 1882 | 1904 | } else { |
| 1883 | 1905 | // Initiate new transfer |
| 1884 | 1906 | let cursor_pos = hypr_client.cursor_pos().await |
@@ -2070,25 +2092,32 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 2070 | 2092 | if peers_guard.get(&dir).is_some() { |
| 2071 | 2093 | drop(peers_guard); |
| 2072 | 2094 | |
| 2073 | | - // Get cursor position (use center of total screen) |
| 2074 | | - let cursor_pos = hypr_client.cursor_pos().await |
| 2075 | | - .map(|c| (c.x, c.y)) |
| 2076 | | - .unwrap_or(((screen_min_x + screen_max_x) / 2, (screen_min_y + screen_max_y) / 2)); |
| 2077 | | - |
| 2078 | | - // Initiate transfer (CLI-initiated, not keyboard) |
| 2079 | | - info!("IPC Switch: calling initiate_transfer"); |
| 2080 | | - match transfer_manager.initiate_transfer(dir, cursor_pos, screen_min_x, screen_min_y, screen_max_x, screen_max_y, false).await { |
| 2081 | | - Ok(()) => { |
| 2082 | | - let machine_name = config.machines.neighbors |
| 2083 | | - .iter() |
| 2084 | | - .find(|n| n.direction == dir) |
| 2085 | | - .map(|n| n.name.clone()) |
| 2086 | | - .unwrap_or_else(|| format!("{:?}", dir)); |
| 2087 | | - info!("IPC Switch: initiate_transfer succeeded, returning response to CLI"); |
| 2088 | | - IpcResponse::Transferred { to_machine: machine_name } |
| 2095 | + // Check if we have input devices before initiating |
| 2096 | + if !input_grabber.has_devices() { |
| 2097 | + IpcResponse::Error { |
| 2098 | + message: "No input devices - cannot initiate transfer".to_string(), |
| 2089 | 2099 | } |
| 2090 | | - Err(e) => IpcResponse::Error { |
| 2091 | | - message: format!("Transfer failed: {}", e), |
| 2100 | + } else { |
| 2101 | + // Get cursor position (use center of total screen) |
| 2102 | + let cursor_pos = hypr_client.cursor_pos().await |
| 2103 | + .map(|c| (c.x, c.y)) |
| 2104 | + .unwrap_or(((screen_min_x + screen_max_x) / 2, (screen_min_y + screen_max_y) / 2)); |
| 2105 | + |
| 2106 | + // Initiate transfer (CLI-initiated, not keyboard) |
| 2107 | + info!("IPC Switch: calling initiate_transfer"); |
| 2108 | + match transfer_manager.initiate_transfer(dir, cursor_pos, screen_min_x, screen_min_y, screen_max_x, screen_max_y, false).await { |
| 2109 | + Ok(()) => { |
| 2110 | + let machine_name = config.machines.neighbors |
| 2111 | + .iter() |
| 2112 | + .find(|n| n.direction == dir) |
| 2113 | + .map(|n| n.name.clone()) |
| 2114 | + .unwrap_or_else(|| format!("{:?}", dir)); |
| 2115 | + info!("IPC Switch: initiate_transfer succeeded, returning response to CLI"); |
| 2116 | + IpcResponse::Transferred { to_machine: machine_name } |
| 2117 | + } |
| 2118 | + Err(e) => IpcResponse::Error { |
| 2119 | + message: format!("Transfer failed: {}", e), |
| 2120 | + } |
| 2092 | 2121 | } |
| 2093 | 2122 | } |
| 2094 | 2123 | } else { |