@@ -31,7 +31,7 @@ pub struct App { |
| 31 | 31 | should_quit: bool, |
| 32 | 32 | width: u32, |
| 33 | 33 | height: u32, |
| 34 | | - daemon_conn: Option<UnixStream>, |
| 34 | + daemon_available: bool, |
| 35 | 35 | last_refresh: Instant, |
| 36 | 36 | status: Option<StatusInfo>, |
| 37 | 37 | cpu_stats: Option<CpuStats>, |
@@ -74,8 +74,8 @@ impl App { |
| 74 | 74 | // Create header bar |
| 75 | 75 | let header = HeaderBar::new(Rect::new(0, 0, width, HEADER_HEIGHT)); |
| 76 | 76 | |
| 77 | | - // Try to connect to daemon |
| 78 | | - let daemon_conn = Self::connect_daemon(); |
| 77 | + // Check if daemon is available |
| 78 | + let daemon_available = Self::check_daemon(); |
| 79 | 79 | |
| 80 | 80 | Ok(Self { |
| 81 | 81 | window, |
@@ -86,7 +86,7 @@ impl App { |
| 86 | 86 | should_quit: false, |
| 87 | 87 | width, |
| 88 | 88 | height, |
| 89 | | - daemon_conn, |
| 89 | + daemon_available, |
| 90 | 90 | last_refresh: Instant::now() - std::time::Duration::from_secs(10), // force immediate refresh |
| 91 | 91 | status: None, |
| 92 | 92 | cpu_stats: None, |
@@ -94,25 +94,26 @@ impl App { |
| 94 | 94 | }) |
| 95 | 95 | } |
| 96 | 96 | |
| 97 | | - /// Connect to the daemon, returns None if connection fails. |
| 98 | | - fn connect_daemon() -> Option<UnixStream> { |
| 97 | + /// Check if daemon is available by attempting a connection. |
| 98 | + fn check_daemon() -> bool { |
| 99 | 99 | let path = gartop_ipc::socket_path(); |
| 100 | 100 | match UnixStream::connect(&path) { |
| 101 | | - Ok(stream) => { |
| 102 | | - stream.set_nonblocking(false).ok()?; |
| 103 | | - tracing::info!("Connected to daemon at {}", path.display()); |
| 104 | | - Some(stream) |
| 101 | + Ok(_) => { |
| 102 | + tracing::info!("Daemon available at {}", path.display()); |
| 103 | + true |
| 105 | 104 | } |
| 106 | 105 | Err(e) => { |
| 107 | | - tracing::warn!("Failed to connect to daemon: {}", e); |
| 108 | | - None |
| 106 | + tracing::warn!("Daemon not available: {}", e); |
| 107 | + false |
| 109 | 108 | } |
| 110 | 109 | } |
| 111 | 110 | } |
| 112 | 111 | |
| 113 | 112 | /// Send a command to the daemon and get response. |
| 114 | | - fn send_command(&mut self, cmd: &Command) -> Option<Response> { |
| 115 | | - let stream = self.daemon_conn.as_mut()?; |
| 113 | + /// Creates a fresh connection for each command since daemon closes after response. |
| 114 | + fn send_command(&self, cmd: &Command) -> Option<Response> { |
| 115 | + let path = gartop_ipc::socket_path(); |
| 116 | + let mut stream = UnixStream::connect(&path).ok()?; |
| 116 | 117 | |
| 117 | 118 | // Send command |
| 118 | 119 | let json = serde_json::to_string(cmd).ok()?; |
@@ -120,7 +121,7 @@ impl App { |
| 120 | 121 | stream.flush().ok()?; |
| 121 | 122 | |
| 122 | 123 | // Read response |
| 123 | | - let mut reader = BufReader::new(stream.try_clone().ok()?); |
| 124 | + let mut reader = BufReader::new(&stream); |
| 124 | 125 | let mut line = String::new(); |
| 125 | 126 | reader.read_line(&mut line).ok()?; |
| 126 | 127 | |
@@ -129,8 +130,11 @@ impl App { |
| 129 | 130 | |
| 130 | 131 | /// Refresh data from daemon. |
| 131 | 132 | fn refresh_data(&mut self) { |
| 133 | + let mut any_success = false; |
| 134 | + |
| 132 | 135 | // Get status |
| 133 | 136 | if let Some(resp) = self.send_command(&Command::Status) { |
| 137 | + any_success = true; |
| 134 | 138 | if resp.success { |
| 135 | 139 | self.status = resp.data.and_then(|d| serde_json::from_value(d).ok()); |
| 136 | 140 | } |
@@ -138,6 +142,7 @@ impl App { |
| 138 | 142 | |
| 139 | 143 | // Get CPU stats |
| 140 | 144 | if let Some(resp) = self.send_command(&Command::GetCpu) { |
| 145 | + any_success = true; |
| 141 | 146 | if resp.success { |
| 142 | 147 | self.cpu_stats = resp.data.and_then(|d| serde_json::from_value(d).ok()); |
| 143 | 148 | } |
@@ -145,11 +150,14 @@ impl App { |
| 145 | 150 | |
| 146 | 151 | // Get memory stats |
| 147 | 152 | if let Some(resp) = self.send_command(&Command::GetMemory) { |
| 153 | + any_success = true; |
| 148 | 154 | if resp.success { |
| 149 | 155 | self.memory_stats = resp.data.and_then(|d| serde_json::from_value(d).ok()); |
| 150 | 156 | } |
| 151 | 157 | } |
| 152 | 158 | |
| 159 | + // Update daemon availability based on whether any command succeeded |
| 160 | + self.daemon_available = any_success; |
| 153 | 161 | self.last_refresh = Instant::now(); |
| 154 | 162 | } |
| 155 | 163 | |
@@ -206,7 +214,7 @@ impl App { |
| 206 | 214 | let x = 20.0; |
| 207 | 215 | let line_height = 24.0; |
| 208 | 216 | |
| 209 | | - if self.daemon_conn.is_none() { |
| 217 | + if !self.daemon_available { |
| 210 | 218 | self.renderer.text( |
| 211 | 219 | "Not connected to daemon", |
| 212 | 220 | x, |
@@ -394,14 +402,14 @@ impl App { |
| 394 | 402 | InputEvent::Idle => { |
| 395 | 403 | // Periodic refresh |
| 396 | 404 | if self.last_refresh.elapsed().as_secs_f64() >= REFRESH_INTERVAL { |
| 397 | | - if self.daemon_conn.is_some() { |
| 405 | + if self.daemon_available { |
| 398 | 406 | self.refresh_data(); |
| 399 | 407 | self.update_header(); |
| 400 | 408 | ev_loop.request_redraw(); |
| 401 | 409 | } else { |
| 402 | 410 | // Try reconnecting |
| 403 | | - self.daemon_conn = Self::connect_daemon(); |
| 404 | | - if self.daemon_conn.is_some() { |
| 411 | + self.daemon_available = Self::check_daemon(); |
| 412 | + if self.daemon_available { |
| 405 | 413 | ev_loop.request_redraw(); |
| 406 | 414 | } |
| 407 | 415 | self.last_refresh = Instant::now(); |