@@ -967,6 +967,10 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 967 | | 967 | |
| 968 | if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { | 968 | if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { |
| 969 | info!("RECOVERY HOTKEY: Barrier enabled, blocking transfer"); | 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 | } else { | 974 | } else { |
| 971 | info!("RECOVERY HOTKEY: At edge with peer, initiating transfer to {:?}", direction); | 975 | info!("RECOVERY HOTKEY: At edge with peer, initiating transfer to {:?}", direction); |
| 972 | if let Err(e) = transfer_manager.initiate_transfer( | 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 | cursor_pos.0, | 1080 | cursor_pos.0, |
| 1077 | cursor_pos.1 | 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 | } else { | 1091 | } else { |
| 1080 | info!( | 1092 | info!( |
| 1081 | "EDGE: {:?} at ({}, {}) - initiating transfer", | 1093 | "EDGE: {:?} at ({}, {}) - initiating transfer", |
@@ -1220,6 +1232,12 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 1220 | "CURSOR EDGE: {:?} at ({}, {}) - barrier enabled, blocking", | 1232 | "CURSOR EDGE: {:?} at ({}, {}) - barrier enabled, blocking", |
| 1221 | edge_dir, cx, cy | 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 | } else { | 1241 | } else { |
| 1224 | info!( | 1242 | info!( |
| 1225 | "CURSOR EDGE: {:?} at ({}, {}) - initiating transfer", | 1243 | "CURSOR EDGE: {:?} at ({}, {}) - initiating transfer", |
@@ -1836,6 +1854,8 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 1836 | // At edge with peer but received control from different direction | 1854 | // At edge with peer but received control from different direction |
| 1837 | if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { | 1855 | if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { |
| 1838 | IpcResponse::Error { message: "Barrier enabled".to_string() } | 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 | } else { | 1859 | } else { |
| 1840 | // Initiate new transfer | 1860 | // Initiate new transfer |
| 1841 | let cursor_pos = hypr_client.cursor_pos().await | 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 | } else if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { | 1900 | } else if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) { |
| 1881 | IpcResponse::Error { message: "Barrier enabled".to_string() } | 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 | } else { | 1904 | } else { |
| 1883 | // Initiate new transfer | 1905 | // Initiate new transfer |
| 1884 | let cursor_pos = hypr_client.cursor_pos().await | 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 | if peers_guard.get(&dir).is_some() { | 2092 | if peers_guard.get(&dir).is_some() { |
| 2071 | drop(peers_guard); | 2093 | drop(peers_guard); |
| 2072 | | 2094 | |
| 2073 | - // Get cursor position (use center of total screen) | 2095 | + // Check if we have input devices before initiating |
| 2074 | - let cursor_pos = hypr_client.cursor_pos().await | 2096 | + if !input_grabber.has_devices() { |
| 2075 | - .map(|c| (c.x, c.y)) | 2097 | + IpcResponse::Error { |
| 2076 | - .unwrap_or(((screen_min_x + screen_max_x) / 2, (screen_min_y + screen_max_y) / 2)); | 2098 | + message: "No input devices - cannot initiate transfer".to_string(), |
| 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 } | | |
| 2089 | } | 2099 | } |
| 2090 | - Err(e) => IpcResponse::Error { | 2100 | + } else { |
| 2091 | - message: format!("Transfer failed: {}", e), | 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 | } else { | 2123 | } else { |