gardesk/gartop / 9daf2bb

Browse files

Fix tab switching lag and add network metrics to process list

- Use local process sorting instead of forcing daemon refresh on tab switch
- Network tab now shows Sock/Listen/Estab columns instead of TCP/UDP
- Highlight listening sockets and high connection counts
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
9daf2bb79c0bc25ad29074d1e91ea7ce56387549
Parents
66abe14
Tree
fc60688

2 changed files

StatusFile+-
M gartop/src/gui/app.rs 45 12
M gartop/src/gui/process_list.rs 25 10
gartop/src/gui/app.rsmodified
@@ -631,16 +631,14 @@ impl App {
631631
         if let Some(tab) = self.tab_bar.on_click(pos) {
632632
             if tab != self.tab_bar.active() {
633633
                 self.tab_bar.set_active(tab);
634
-                // Re-sort processes for new tab
634
+                // Re-sort cached processes locally (no daemon call)
635635
                 let sort_field = match tab {
636636
                     Tab::Cpu => SortField::Cpu,
637637
                     Tab::Memory => SortField::Memory,
638638
                     Tab::Network => SortField::NetConnections,
639639
                     Tab::Disk => SortField::DiskTotal,
640640
                 };
641
-                self.process_list.set_sort(sort_field);
642
-                // Force refresh to get re-sorted processes
643
-                self.last_refresh = Instant::now() - std::time::Duration::from_secs(10);
641
+                self.sort_processes(sort_field);
644642
                 return true;
645643
             }
646644
         }
@@ -653,6 +651,44 @@ impl App {
653651
         false
654652
     }
655653
 
654
+    /// Sort cached processes by the given field and update process list.
655
+    fn sort_processes(&mut self, sort_field: SortField) {
656
+        match sort_field {
657
+            SortField::Cpu => self.processes.sort_by(|a, b| {
658
+                b.cpu_percent.partial_cmp(&a.cpu_percent).unwrap_or(std::cmp::Ordering::Equal)
659
+            }),
660
+            SortField::Memory => self.processes.sort_by(|a, b| {
661
+                b.memory_percent.partial_cmp(&a.memory_percent).unwrap_or(std::cmp::Ordering::Equal)
662
+            }),
663
+            SortField::DiskRead => self.processes.sort_by(|a, b| {
664
+                b.io_read_rate.partial_cmp(&a.io_read_rate).unwrap_or(std::cmp::Ordering::Equal)
665
+            }),
666
+            SortField::DiskWrite => self.processes.sort_by(|a, b| {
667
+                b.io_write_rate.partial_cmp(&a.io_write_rate).unwrap_or(std::cmp::Ordering::Equal)
668
+            }),
669
+            SortField::DiskTotal => self.processes.sort_by(|a, b| {
670
+                let a_total = a.io_read_rate + a.io_write_rate;
671
+                let b_total = b.io_read_rate + b.io_write_rate;
672
+                b_total.partial_cmp(&a_total).unwrap_or(std::cmp::Ordering::Equal)
673
+            }),
674
+            SortField::NetConnections => self.processes.sort_by(|a, b| {
675
+                b.net_connections.cmp(&a.net_connections)
676
+            }),
677
+            SortField::NetTcp => self.processes.sort_by(|a, b| {
678
+                b.net_tcp.cmp(&a.net_tcp)
679
+            }),
680
+            SortField::NetBandwidth => self.processes.sort_by(|a, b| {
681
+                let a_total = a.net_rx_rate + a.net_tx_rate;
682
+                let b_total = b.net_rx_rate + b.net_tx_rate;
683
+                b_total.partial_cmp(&a_total).unwrap_or(std::cmp::Ordering::Equal)
684
+            }),
685
+            SortField::Pid => self.processes.sort_by_key(|p| p.pid),
686
+            SortField::Name => self.processes.sort_by(|a, b| a.name.cmp(&b.name)),
687
+        }
688
+        self.process_list.set_processes(self.processes.clone());
689
+        self.process_list.set_sort(sort_field);
690
+    }
691
+
656692
     /// Handle scroll.
657693
     fn handle_scroll(&mut self, delta: i32) -> bool {
658694
         self.process_list.on_scroll(delta);
@@ -711,24 +747,22 @@ impl App {
711747
                         }
712748
                         Key::Char('1') => {
713749
                             self.tab_bar.set_active(Tab::Cpu);
714
-                            self.process_list.set_sort(SortField::Cpu);
715
-                            self.last_refresh = Instant::now() - std::time::Duration::from_secs(10);
750
+                            self.sort_processes(SortField::Cpu);
716751
                             ev_loop.request_redraw();
717752
                         }
718753
                         Key::Char('2') => {
719754
                             self.tab_bar.set_active(Tab::Memory);
720
-                            self.process_list.set_sort(SortField::Memory);
721
-                            self.last_refresh = Instant::now() - std::time::Duration::from_secs(10);
755
+                            self.sort_processes(SortField::Memory);
722756
                             ev_loop.request_redraw();
723757
                         }
724758
                         Key::Char('3') => {
725759
                             self.tab_bar.set_active(Tab::Network);
726
-                            self.last_refresh = Instant::now() - std::time::Duration::from_secs(10);
760
+                            self.sort_processes(SortField::NetConnections);
727761
                             ev_loop.request_redraw();
728762
                         }
729763
                         Key::Char('4') => {
730764
                             self.tab_bar.set_active(Tab::Disk);
731
-                            self.last_refresh = Instant::now() - std::time::Duration::from_secs(10);
765
+                            self.sort_processes(SortField::DiskTotal);
732766
                             ev_loop.request_redraw();
733767
                         }
734768
                         Key::Tab => {
@@ -745,8 +779,7 @@ impl App {
745779
                                 Tab::Network => SortField::NetConnections,
746780
                                 Tab::Disk => SortField::DiskTotal,
747781
                             };
748
-                            self.process_list.set_sort(sort_field);
749
-                            self.last_refresh = Instant::now() - std::time::Duration::from_secs(10);
782
+                            self.sort_processes(sort_field);
750783
                             ev_loop.request_redraw();
751784
                         }
752785
                         _ => {}
gartop/src/gui/process_list.rsmodified
@@ -171,7 +171,7 @@ impl ProcessList {
171171
                 SortField::Cpu => theme.cpu_color,
172172
                 SortField::Memory => theme.memory_color,
173173
                 SortField::DiskRead | SortField::DiskWrite | SortField::DiskTotal => theme.disk_color,
174
-                SortField::NetConnections => theme.network_color,
174
+                SortField::NetConnections | SortField::NetTcp | SortField::NetBandwidth => theme.network_color,
175175
                 _ => theme.text_secondary,
176176
             },
177177
             ..header_style.clone()
@@ -181,20 +181,27 @@ impl ProcessList {
181181
         let is_disk_sort = matches!(self.sort_field, SortField::DiskRead | SortField::DiskWrite | SortField::DiskTotal);
182182
         let is_net_sort = matches!(self.sort_field, SortField::NetConnections | SortField::NetTcp | SortField::NetBandwidth);
183183
 
184
+        // Extra column position for network mode
185
+        let col_extra = x + 350.0;
186
+
184187
         if is_disk_sort {
185188
             renderer.text("Read/s", col_cpu, header_y, &sort_style)?;
186189
             renderer.text("Write/s", col_mem, header_y, &sort_style)?;
190
+            renderer.text("User", col_user, header_y, &header_style)?;
187191
         } else if is_net_sort {
188
-            renderer.text("TCP", col_cpu, header_y, &sort_style)?;
189
-            renderer.text("UDP", col_mem, header_y, &sort_style)?;
192
+            renderer.text("Sock", col_cpu, header_y, &sort_style)?;
193
+            renderer.text("Listen", col_mem, header_y, &sort_style)?;
194
+            renderer.text("Estab", col_extra, header_y, &sort_style)?;
195
+            renderer.text("User", col_user, header_y, &header_style)?;
190196
         } else if self.sort_field == SortField::Cpu {
191197
             renderer.text("CPU%", col_cpu, header_y, &sort_style)?;
192198
             renderer.text("Mem%", col_mem, header_y, &header_style)?;
199
+            renderer.text("User", col_user, header_y, &header_style)?;
193200
         } else {
194201
             renderer.text("CPU%", col_cpu, header_y, &header_style)?;
195202
             renderer.text("Mem%", col_mem, header_y, &sort_style)?;
203
+            renderer.text("User", col_user, header_y, &header_style)?;
196204
         }
197
-        renderer.text("User", col_user, header_y, &header_style)?;
198205
 
199206
         // Header separator
200207
         let sep_y = (self.bounds.y + HEADER_HEIGHT as i32) as f64;
@@ -258,21 +265,29 @@ impl ProcessList {
258265
                 };
259266
                 renderer.text(&format_rate(process.io_write_rate), col_mem, text_y, &write_style)?;
260267
             } else if is_net_sort {
261
-                // TCP count
262
-                let tcp_style = if process.net_tcp > 5 {
268
+                // Total sockets
269
+                let sock_style = if process.net_connections > 10 {
270
+                    TextStyle { color: theme.network_color, ..text_style.clone() }
271
+                } else {
272
+                    dim_style.clone()
273
+                };
274
+                renderer.text(&process.net_connections.to_string(), col_cpu, text_y, &sock_style)?;
275
+
276
+                // Listen count (servers)
277
+                let listen_style = if process.net_listen > 0 {
263278
                     TextStyle { color: theme.network_color, ..text_style.clone() }
264279
                 } else {
265280
                     dim_style.clone()
266281
                 };
267
-                renderer.text(&process.net_tcp.to_string(), col_cpu, text_y, &tcp_style)?;
282
+                renderer.text(&process.net_listen.to_string(), col_mem, text_y, &listen_style)?;
268283
 
269
-                // UDP count
270
-                let udp_style = if process.net_udp > 5 {
284
+                // Established count (active connections)
285
+                let estab_style = if process.net_established > 5 {
271286
                     TextStyle { color: theme.network_color, ..text_style.clone() }
272287
                 } else {
273288
                     dim_style.clone()
274289
                 };
275
-                renderer.text(&process.net_udp.to_string(), col_mem, text_y, &udp_style)?;
290
+                renderer.text(&process.net_established.to_string(), col_extra, text_y, &estab_style)?;
276291
             } else {
277292
                 // CPU %
278293
                 let cpu_style = if process.cpu_percent > 50.0 {