tenseleyflow/hyprkvm / 2b6d82c

Browse files

fix: prevent deviceless machines from initiating transfers from Local state

Machines without physical input devices (like hatake) can still:
- Receive control (ReceivedControl state)
- Relay input to other machines (Relaying state)
- Return control to the source

But they cannot initiate transfers from Local state since there's
nothing to grab. This prevents the deadlock where hatake would
enter RemoteActive and call StartCapture, but with no devices,
no input would be forwarded.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
2b6d82cc722d2013aa54b60aeb45534bee27894b
Parents
92a72cc
Tree
b4eb856

2 changed files

StatusFile+-
M hyprkvm-daemon/src/input/evdev_grab.rs 13 0
M hyprkvm-daemon/src/main.rs 18 0
hyprkvm-daemon/src/input/evdev_grab.rsmodified
@@ -30,6 +30,8 @@ pub struct EvdevGrabber {
3030
     recovery_active: Arc<AtomicU64>,
3131
     /// The direction to watch for in recovery mode (encoded as u8: 1=Up, 2=Down, 3=Left, 4=Right, 0=none)
3232
     recovery_direction: Arc<AtomicU64>,
33
+    /// Whether this machine has physical input devices to grab
34
+    has_devices: bool,
3335
     event_rx: mpsc::Receiver<GrabEvent>,
3436
     _thread: thread::JoinHandle<()>,
3537
 }
@@ -37,6 +39,10 @@ pub struct EvdevGrabber {
3739
 impl EvdevGrabber {
3840
     /// Create a new evdev grabber
3941
     pub fn new() -> Result<Self, EvdevGrabError> {
42
+        // Check if we have devices before spawning the thread
43
+        let device_paths = find_input_devices();
44
+        let has_devices = !device_paths.is_empty();
45
+
4046
         let active = Arc::new(AtomicBool::new(false));
4147
         let active_clone = active.clone();
4248
         let recovery_active = Arc::new(AtomicU64::new(0));
@@ -59,6 +65,7 @@ impl EvdevGrabber {
5965
             active,
6066
             recovery_active,
6167
             recovery_direction,
68
+            has_devices,
6269
             event_rx,
6370
             _thread: thread,
6471
         })
@@ -101,6 +108,12 @@ impl EvdevGrabber {
101108
         self.active.load(Ordering::SeqCst)
102109
     }
103110
 
111
+    /// Check if this machine has physical input devices
112
+    /// Machines without devices can only receive control (and relay), not initiate from Local
113
+    pub fn has_devices(&self) -> bool {
114
+        self.has_devices
115
+    }
116
+
104117
     /// Try to receive a grab event (non-blocking)
105118
     pub fn try_recv(&self) -> Option<GrabEvent> {
106119
         self.event_rx.try_recv().ok()
hyprkvm-daemon/src/main.rsmodified
@@ -1071,6 +1071,9 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
10711071
                             }
10721072
                         }
10731073
 
1074
+                        // Re-check state for the has_devices check
1075
+                        let current_state = transfer_manager.state().await;
1076
+
10741077
                         if barrier_enabled.load(std::sync::atomic::Ordering::SeqCst) {
10751078
                             info!(
10761079
                                 "EDGE: {:?} at ({}, {}) - barrier enabled, blocking",
@@ -1078,6 +1081,14 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
10781081
                                 cursor_pos.0,
10791082
                                 cursor_pos.1
10801083
                             );
1084
+                        } else if !input_grabber.has_devices() && current_state.is_local() {
1085
+                            // No devices and in Local state - can't initiate
1086
+                            tracing::debug!(
1087
+                                "EDGE: {:?} at ({}, {}) - no devices, can't initiate from Local",
1088
+                                direction,
1089
+                                cursor_pos.0,
1090
+                                cursor_pos.1
1091
+                            );
10811092
                         } else {
10821093
                             info!(
10831094
                                 "EDGE: {:?} at ({}, {}) - initiating transfer",
@@ -1222,6 +1233,13 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
12221233
                                                             "CURSOR EDGE: {:?} at ({}, {}) - barrier enabled, blocking",
12231234
                                                             edge_dir, cx, cy
12241235
                                                         );
1236
+                                                    } else if !input_grabber.has_devices() && current_state.is_local() {
1237
+                                                        // No devices and in Local state - can't initiate (no input to grab)
1238
+                                                        // Note: if in ReceivedControl, we can still relay
1239
+                                                        tracing::debug!(
1240
+                                                            "CURSOR EDGE: {:?} at ({}, {}) - no devices, can't initiate from Local",
1241
+                                                            edge_dir, cx, cy
1242
+                                                        );
12251243
                                                     } else {
12261244
                                                         info!(
12271245
                                                             "CURSOR EDGE: {:?} at ({}, {}) - initiating transfer",