@@ -110,6 +110,13 @@ async fn main() -> anyhow::Result<()> { |
| 110 | } | 110 | } |
| 111 | | 111 | |
| 112 | async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { | 112 | async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| | 113 | + use std::collections::HashMap; |
| | 114 | + use std::net::SocketAddr; |
| | 115 | + use std::sync::Arc; |
| | 116 | + use tokio::sync::RwLock; |
| | 117 | + use hyprkvm_common::Direction; |
| | 118 | + use hyprkvm_common::protocol::{Message, HelloPayload, PROTOCOL_VERSION}; |
| | 119 | + |
| 113 | // Load or create default config | 120 | // Load or create default config |
| 114 | let config = match Config::load(config_path) { | 121 | let config = match Config::load(config_path) { |
| 115 | Ok(cfg) => cfg, | 122 | Ok(cfg) => cfg, |
@@ -133,48 +140,314 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 133 | info!(" {} at ({}, {}) {}x{}", mon.name, mon.x, mon.y, mon.width, mon.height); | 140 | info!(" {} at ({}, {}) {}x{}", mon.name, mon.x, mon.y, mon.width, mon.height); |
| 134 | } | 141 | } |
| 135 | | 142 | |
| | 143 | + // Calculate screen bounds |
| | 144 | + let screen_width: u32 = monitors.iter().map(|m| m.x as u32 + m.width).max().unwrap_or(1920); |
| | 145 | + let screen_height: u32 = monitors.iter().map(|m| m.y as u32 + m.height).max().unwrap_or(1080); |
| | 146 | + |
| 136 | // Determine which edges have network neighbors | 147 | // Determine which edges have network neighbors |
| 137 | let mut enabled_edges = Vec::new(); | 148 | let mut enabled_edges = Vec::new(); |
| | 149 | + let mut neighbor_map: HashMap<Direction, SocketAddr> = HashMap::new(); |
| 138 | for neighbor in &config.machines.neighbors { | 150 | for neighbor in &config.machines.neighbors { |
| 139 | enabled_edges.push(neighbor.direction); | 151 | enabled_edges.push(neighbor.direction); |
| 140 | - info!(" Network neighbor: {} ({})", neighbor.name, neighbor.direction); | 152 | + neighbor_map.insert(neighbor.direction, neighbor.address); |
| | 153 | + info!(" Network neighbor: {} ({}) at {}", neighbor.name, neighbor.direction, neighbor.address); |
| 141 | } | 154 | } |
| 142 | | 155 | |
| 143 | - // If no neighbors configured, enable all edges for testing | 156 | + // If no neighbors configured, just run in demo mode |
| 144 | if enabled_edges.is_empty() { | 157 | if enabled_edges.is_empty() { |
| 145 | - info!("No neighbors configured, enabling all edges for testing"); | 158 | + info!("No neighbors configured. Add neighbors in config to enable control transfer."); |
| 146 | - enabled_edges = vec![ | 159 | + enabled_edges = vec![Direction::Left, Direction::Right]; |
| 147 | - hyprkvm_common::Direction::Left, | | |
| 148 | - hyprkvm_common::Direction::Right, | | |
| 149 | - ]; | | |
| 150 | } | 160 | } |
| 151 | | 161 | |
| 152 | // Start edge capture | 162 | // Start edge capture |
| 153 | info!("Starting edge capture for: {:?}", enabled_edges); | 163 | info!("Starting edge capture for: {:?}", enabled_edges); |
| 154 | let edge_capture = input::EdgeCapture::new(input::EdgeCaptureConfig { | 164 | let edge_capture = input::EdgeCapture::new(input::EdgeCaptureConfig { |
| 155 | barrier_size: 1, | 165 | barrier_size: 1, |
| 156 | - enabled_edges, | 166 | + enabled_edges: enabled_edges.clone(), |
| 157 | })?; | 167 | })?; |
| 158 | | 168 | |
| | 169 | + // Create transfer manager |
| | 170 | + let (transfer_manager, mut transfer_events) = transfer::TransferManager::new( |
| | 171 | + config.machines.self_name.clone(), |
| | 172 | + ); |
| | 173 | + let transfer_manager = Arc::new(transfer_manager); |
| | 174 | + |
| | 175 | + // Connection storage: direction -> peer connection |
| | 176 | + let peers: Arc<RwLock<HashMap<Direction, network::FramedConnection>>> = |
| | 177 | + Arc::new(RwLock::new(HashMap::new())); |
| | 178 | + |
| | 179 | + // Start network server |
| | 180 | + let listen_addr: SocketAddr = format!("0.0.0.0:{}", config.network.listen_port).parse()?; |
| | 181 | + let server = network::Server::bind(listen_addr).await?; |
| | 182 | + info!("Listening for connections on {}", server.local_addr()); |
| | 183 | + |
| | 184 | + // Spawn task to accept incoming connections |
| | 185 | + let peers_clone = peers.clone(); |
| | 186 | + let machine_name = config.machines.self_name.clone(); |
| | 187 | + let accept_handle = tokio::spawn(async move { |
| | 188 | + loop { |
| | 189 | + match server.accept().await { |
| | 190 | + Ok(mut conn) => { |
| | 191 | + let addr = conn.remote_addr(); |
| | 192 | + info!("Incoming connection from {}", addr); |
| | 193 | + |
| | 194 | + // Receive Hello |
| | 195 | + match conn.recv().await { |
| | 196 | + Ok(Some(Message::Hello(hello))) => { |
| | 197 | + info!("Peer {} connected (protocol v{})", hello.machine_name, hello.protocol_version); |
| | 198 | + |
| | 199 | + // Send HelloAck |
| | 200 | + let ack = Message::HelloAck(hyprkvm_common::protocol::HelloAckPayload { |
| | 201 | + accepted: true, |
| | 202 | + protocol_version: PROTOCOL_VERSION, |
| | 203 | + machine_name: machine_name.clone(), |
| | 204 | + error: None, |
| | 205 | + }); |
| | 206 | + if let Err(e) = conn.send(&ack).await { |
| | 207 | + tracing::error!("Failed to send HelloAck: {}", e); |
| | 208 | + continue; |
| | 209 | + } |
| | 210 | + |
| | 211 | + // TODO: Determine direction from peer info |
| | 212 | + // For now, assume first connection is from configured neighbor |
| | 213 | + // In production, match by machine name |
| | 214 | + } |
| | 215 | + Ok(Some(other)) => { |
| | 216 | + tracing::warn!("Expected Hello, got {:?}", other); |
| | 217 | + } |
| | 218 | + Ok(None) => { |
| | 219 | + tracing::debug!("Connection closed during handshake"); |
| | 220 | + } |
| | 221 | + Err(e) => { |
| | 222 | + tracing::error!("Handshake error: {}", e); |
| | 223 | + } |
| | 224 | + } |
| | 225 | + } |
| | 226 | + Err(e) => { |
| | 227 | + tracing::error!("Accept error: {}", e); |
| | 228 | + } |
| | 229 | + } |
| | 230 | + } |
| | 231 | + }); |
| | 232 | + |
| | 233 | + // Channel for incoming messages from peers |
| | 234 | + let (peer_msg_tx, mut peer_msg_rx) = tokio::sync::mpsc::channel::<(Direction, Message)>(64); |
| | 235 | + |
| | 236 | + // Connect to configured peers |
| | 237 | + for neighbor in &config.machines.neighbors { |
| | 238 | + let addr = neighbor.address; |
| | 239 | + let direction = neighbor.direction; |
| | 240 | + let peers_clone = peers.clone(); |
| | 241 | + let machine_name = config.machines.self_name.clone(); |
| | 242 | + let msg_tx = peer_msg_tx.clone(); |
| | 243 | + |
| | 244 | + tokio::spawn(async move { |
| | 245 | + info!("Connecting to {} at {}...", direction, addr); |
| | 246 | + match network::connect(addr).await { |
| | 247 | + Ok(mut conn) => { |
| | 248 | + // Send Hello |
| | 249 | + let hello = Message::Hello(HelloPayload { |
| | 250 | + protocol_version: PROTOCOL_VERSION, |
| | 251 | + machine_name: machine_name.clone(), |
| | 252 | + capabilities: vec![], |
| | 253 | + }); |
| | 254 | + |
| | 255 | + if let Err(e) = conn.send(&hello).await { |
| | 256 | + tracing::error!("Failed to send Hello to {}: {}", direction, e); |
| | 257 | + return; |
| | 258 | + } |
| | 259 | + |
| | 260 | + // Wait for HelloAck |
| | 261 | + match conn.recv().await { |
| | 262 | + Ok(Some(Message::HelloAck(ack))) => { |
| | 263 | + if ack.accepted { |
| | 264 | + info!("Connected to {} ({})", ack.machine_name, direction); |
| | 265 | + |
| | 266 | + // Split connection: store for sending, spawn receiver |
| | 267 | + // For now, just store and we'll poll in the main loop |
| | 268 | + let mut peers = peers_clone.write().await; |
| | 269 | + peers.insert(direction, conn); |
| | 270 | + } else { |
| | 271 | + tracing::error!("Connection rejected: {:?}", ack.error); |
| | 272 | + } |
| | 273 | + } |
| | 274 | + Ok(Some(other)) => { |
| | 275 | + tracing::warn!("Expected HelloAck, got {:?}", other); |
| | 276 | + } |
| | 277 | + Ok(None) => { |
| | 278 | + tracing::warn!("Connection closed during handshake"); |
| | 279 | + } |
| | 280 | + Err(e) => { |
| | 281 | + tracing::error!("Handshake error: {}", e); |
| | 282 | + } |
| | 283 | + } |
| | 284 | + } |
| | 285 | + Err(e) => { |
| | 286 | + tracing::warn!("Failed to connect to {} ({}): {}", direction, addr, e); |
| | 287 | + } |
| | 288 | + } |
| | 289 | + }); |
| | 290 | + } |
| | 291 | + |
| 159 | // Listen for Hyprland events | 292 | // Listen for Hyprland events |
| 160 | let mut event_stream = hyprland::events::HyprlandEventStream::connect().await?; | 293 | let mut event_stream = hyprland::events::HyprlandEventStream::connect().await?; |
| 161 | | 294 | |
| 162 | - info!("Daemon running. Move mouse to screen edges to test. Press Ctrl+C to stop."); | 295 | + info!("Daemon running. Move mouse to screen edges to trigger transfer. Press Ctrl+C to stop."); |
| 163 | | 296 | |
| 164 | loop { | 297 | loop { |
| 165 | tokio::select! { | 298 | tokio::select! { |
| 166 | - // Check for edge events (non-blocking via channel) | 299 | + // Check for edge events and poll peer messages |
| 167 | _ = tokio::time::sleep(std::time::Duration::from_millis(10)) => { | 300 | _ = tokio::time::sleep(std::time::Duration::from_millis(10)) => { |
| | 301 | + // Handle edge events |
| 168 | while let Some(edge_event) = edge_capture.try_recv() { | 302 | while let Some(edge_event) = edge_capture.try_recv() { |
| 169 | - info!( | 303 | + let direction = edge_event.direction; |
| 170 | - "EDGE EVENT: {:?} at ({}, {})", | 304 | + |
| 171 | - edge_event.direction, | 305 | + // Check if we have a peer in this direction |
| 172 | - edge_event.position.0, | 306 | + let has_peer = { |
| 173 | - edge_event.position.1 | 307 | + let peers = peers.read().await; |
| 174 | - ); | 308 | + peers.contains_key(&direction) |
| 175 | - | 309 | + }; |
| 176 | - // TODO: Sprint 4 - Trigger network switch | 310 | + |
| 177 | - // For now, just log it | 311 | + if has_peer { |
| | 312 | + info!( |
| | 313 | + "EDGE: {:?} at ({}, {}) - initiating transfer", |
| | 314 | + direction, |
| | 315 | + edge_event.position.0, |
| | 316 | + edge_event.position.1 |
| | 317 | + ); |
| | 318 | + |
| | 319 | + if let Err(e) = transfer_manager.initiate_transfer( |
| | 320 | + direction, |
| | 321 | + edge_event.position, |
| | 322 | + screen_height, |
| | 323 | + screen_width, |
| | 324 | + ).await { |
| | 325 | + tracing::warn!("Failed to initiate transfer: {}", e); |
| | 326 | + } |
| | 327 | + } else { |
| | 328 | + tracing::debug!( |
| | 329 | + "EDGE: {:?} but no peer connected", |
| | 330 | + direction |
| | 331 | + ); |
| | 332 | + } |
| | 333 | + } |
| | 334 | + |
| | 335 | + // Poll for incoming messages from peers (non-blocking) |
| | 336 | + let directions: Vec<Direction> = { |
| | 337 | + let peers = peers.read().await; |
| | 338 | + peers.keys().cloned().collect() |
| | 339 | + }; |
| | 340 | + |
| | 341 | + for direction in directions { |
| | 342 | + let mut peers = peers.write().await; |
| | 343 | + if let Some(peer) = peers.get_mut(&direction) { |
| | 344 | + // Try non-blocking receive using tokio timeout |
| | 345 | + match tokio::time::timeout( |
| | 346 | + std::time::Duration::from_millis(1), |
| | 347 | + peer.recv() |
| | 348 | + ).await { |
| | 349 | + Ok(Ok(Some(msg))) => { |
| | 350 | + tracing::debug!("Received from {:?}: {:?}", direction, msg); |
| | 351 | + // Handle incoming message |
| | 352 | + match msg { |
| | 353 | + Message::Enter(payload) => { |
| | 354 | + info!("Received Enter from {:?}", direction); |
| | 355 | + match transfer_manager.handle_enter( |
| | 356 | + direction, |
| | 357 | + payload, |
| | 358 | + screen_width, |
| | 359 | + screen_height, |
| | 360 | + ).await { |
| | 361 | + Ok(pos) => { |
| | 362 | + info!("Positioned cursor at {:?}", pos); |
| | 363 | + } |
| | 364 | + Err(e) => { |
| | 365 | + tracing::error!("Failed to handle Enter: {}", e); |
| | 366 | + } |
| | 367 | + } |
| | 368 | + } |
| | 369 | + Message::EnterAck(ack) => { |
| | 370 | + info!("Received EnterAck: success={}", ack.success); |
| | 371 | + if let Err(e) = transfer_manager.handle_enter_ack(ack).await { |
| | 372 | + tracing::error!("Failed to handle EnterAck: {}", e); |
| | 373 | + } |
| | 374 | + } |
| | 375 | + Message::Leave(payload) => { |
| | 376 | + info!("Received Leave from {:?}", direction); |
| | 377 | + if let Err(e) = transfer_manager.handle_leave(payload).await { |
| | 378 | + tracing::error!("Failed to handle Leave: {}", e); |
| | 379 | + } |
| | 380 | + } |
| | 381 | + Message::LeaveAck => { |
| | 382 | + info!("Received LeaveAck"); |
| | 383 | + // Transfer complete |
| | 384 | + } |
| | 385 | + Message::InputEvent(input) => { |
| | 386 | + tracing::trace!("Received input event: {:?}", input); |
| | 387 | + // TODO: Inject input via emulation module |
| | 388 | + } |
| | 389 | + Message::Ping { timestamp } => { |
| | 390 | + let _ = peer.send(&Message::Pong { timestamp }).await; |
| | 391 | + } |
| | 392 | + Message::Pong { timestamp } => { |
| | 393 | + tracing::trace!("Pong received, rtt={}ms", |
| | 394 | + std::time::SystemTime::now() |
| | 395 | + .duration_since(std::time::UNIX_EPOCH) |
| | 396 | + .unwrap() |
| | 397 | + .as_millis() as u64 - timestamp |
| | 398 | + ); |
| | 399 | + } |
| | 400 | + _ => { |
| | 401 | + tracing::debug!("Unhandled message: {:?}", msg); |
| | 402 | + } |
| | 403 | + } |
| | 404 | + } |
| | 405 | + Ok(Ok(None)) => { |
| | 406 | + // Connection closed |
| | 407 | + info!("Peer {:?} disconnected", direction); |
| | 408 | + peers.remove(&direction); |
| | 409 | + } |
| | 410 | + Ok(Err(e)) => { |
| | 411 | + tracing::error!("Error receiving from {:?}: {}", direction, e); |
| | 412 | + peers.remove(&direction); |
| | 413 | + } |
| | 414 | + Err(_) => { |
| | 415 | + // Timeout - no message available, that's fine |
| | 416 | + } |
| | 417 | + } |
| | 418 | + } |
| | 419 | + } |
| | 420 | + } |
| | 421 | + |
| | 422 | + // Handle transfer events |
| | 423 | + Some(event) = transfer_events.recv() => { |
| | 424 | + match event { |
| | 425 | + transfer::TransferEvent::SendMessage { direction, message } => { |
| | 426 | + let mut peers = peers.write().await; |
| | 427 | + if let Some(peer) = peers.get_mut(&direction) { |
| | 428 | + tracing::debug!("Sending {:?} to {:?}", message, direction); |
| | 429 | + if let Err(e) = peer.send(&message).await { |
| | 430 | + tracing::error!("Failed to send message: {}", e); |
| | 431 | + } |
| | 432 | + } else { |
| | 433 | + tracing::warn!("No peer for direction {:?}", direction); |
| | 434 | + } |
| | 435 | + } |
| | 436 | + transfer::TransferEvent::StartCapture { direction } => { |
| | 437 | + info!("Starting input capture for {:?}", direction); |
| | 438 | + // TODO: Implement actual input capture |
| | 439 | + // For now, just log |
| | 440 | + } |
| | 441 | + transfer::TransferEvent::StopCapture => { |
| | 442 | + info!("Stopping input capture"); |
| | 443 | + } |
| | 444 | + transfer::TransferEvent::StartInjection { from } => { |
| | 445 | + info!("Starting input injection from {:?}", from); |
| | 446 | + // TODO: Implement actual input injection |
| | 447 | + } |
| | 448 | + transfer::TransferEvent::StopInjection => { |
| | 449 | + info!("Stopping input injection"); |
| | 450 | + } |
| 178 | } | 451 | } |
| 179 | } | 452 | } |
| 180 | | 453 | |
@@ -182,7 +455,7 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 182 | event = event_stream.next_event() => { | 455 | event = event_stream.next_event() => { |
| 183 | match event { | 456 | match event { |
| 184 | Ok(evt) => { | 457 | Ok(evt) => { |
| 185 | - tracing::debug!("Hyprland event: {:?}", evt); | 458 | + tracing::trace!("Hyprland event: {:?}", evt); |
| 186 | } | 459 | } |
| 187 | Err(e) => { | 460 | Err(e) => { |
| 188 | tracing::error!("Event error: {e}"); | 461 | tracing::error!("Event error: {e}"); |
@@ -194,6 +467,7 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> { |
| 194 | // Shutdown | 467 | // Shutdown |
| 195 | _ = tokio::signal::ctrl_c() => { | 468 | _ = tokio::signal::ctrl_c() => { |
| 196 | info!("Shutting down..."); | 469 | info!("Shutting down..."); |
| | 470 | + accept_handle.abort(); |
| 197 | break; | 471 | break; |
| 198 | } | 472 | } |
| 199 | } | 473 | } |