gardesk/garfield / a467a52

Browse files

ui: add free disk space display to status bar

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
a467a5292cc2801d71240ceb6fc0089f4d715458
Parents
2cc95b4
Tree
524f546

5 changed files

StatusFile+-
M Cargo.lock 1 0
M Cargo.toml 3 0
M garfield/Cargo.toml 1 0
M garfield/src/app.rs 3 2
M garfield/src/ui/status_bar.rs 39 3
Cargo.lockmodified
@@ -331,6 +331,7 @@ dependencies = [
331
  "gartk-core",
331
  "gartk-core",
332
  "gartk-render",
332
  "gartk-render",
333
  "gartk-x11",
333
  "gartk-x11",
334
+ "libc",
334
  "serde",
335
  "serde",
335
  "serde_json",
336
  "serde_json",
336
  "thiserror",
337
  "thiserror",
Cargo.tomlmodified
@@ -22,6 +22,9 @@ gartk-render = { path = "../gartk/gartk-render" }
22
 # X11
22
 # X11
23
 x11rb = { version = "0.13", features = ["allow-unsafe-code"] }
23
 x11rb = { version = "0.13", features = ["allow-unsafe-code"] }
24
 
24
 
25
+# System
26
+libc = "0.2"
27
+
25
 # Logging
28
 # Logging
26
 tracing = "0.1"
29
 tracing = "0.1"
27
 tracing-subscriber = { version = "0.3", features = ["env-filter"] }
30
 tracing-subscriber = { version = "0.3", features = ["env-filter"] }
garfield/Cargo.tomlmodified
@@ -33,6 +33,7 @@ serde_json.workspace = true
33
 # File system
33
 # File system
34
 dirs.workspace = true
34
 dirs.workspace = true
35
 chrono.workspace = true
35
 chrono.workspace = true
36
+libc.workspace = true
36
 
37
 
37
 # IPC
38
 # IPC
38
 garfield-ipc.workspace = true
39
 garfield-ipc.workspace = true
garfield/src/app.rsmodified
@@ -1103,11 +1103,12 @@ impl App {
1103
     fn update_status_bar(&mut self) {
1103
     fn update_status_bar(&mut self) {
1104
         let stats = self.focused_pane()
1104
         let stats = self.focused_pane()
1105
             .and_then(|pane| pane.active_tab())
1105
             .and_then(|pane| pane.active_tab())
1106
-            .map(|tab| (tab.visible_count(), tab.selection_count(), tab.selected_size(), tab.view_mode().name()));
1106
+            .map(|tab| (tab.visible_count(), tab.selection_count(), tab.selected_size(), tab.view_mode().name(), tab.current_path().to_path_buf()));
1107
 
1107
 
1108
-        if let Some((visible_count, selected_count, selected_size, view_mode)) = stats {
1108
+        if let Some((visible_count, selected_count, selected_size, view_mode, path)) = stats {
1109
             self.status_bar.update(visible_count, selected_count, selected_size);
1109
             self.status_bar.update(visible_count, selected_count, selected_size);
1110
             self.status_bar.set_view_mode(view_mode);
1110
             self.status_bar.set_view_mode(view_mode);
1111
+            self.status_bar.update_free_space(&path);
1111
         }
1112
         }
1112
     }
1113
     }
1113
 
1114
 
garfield/src/ui/status_bar.rsmodified
@@ -2,6 +2,8 @@
2
 
2
 
3
 use gartk_core::Rect;
3
 use gartk_core::Rect;
4
 use gartk_render::{Renderer, TextStyle};
4
 use gartk_render::{Renderer, TextStyle};
5
+use std::ffi::CString;
6
+use std::path::Path;
5
 
7
 
6
 /// Height of the status bar.
8
 /// Height of the status bar.
7
 pub const STATUS_BAR_HEIGHT: u32 = 24;
9
 pub const STATUS_BAR_HEIGHT: u32 = 24;
@@ -18,6 +20,8 @@ pub struct StatusBar {
18
     selected_size: u64,
20
     selected_size: u64,
19
     /// Current view mode name.
21
     /// Current view mode name.
20
     view_mode: String,
22
     view_mode: String,
23
+    /// Free disk space in bytes.
24
+    free_space: Option<u64>,
21
 }
25
 }
22
 
26
 
23
 impl StatusBar {
27
 impl StatusBar {
@@ -29,6 +33,7 @@ impl StatusBar {
29
             selected_count: 0,
33
             selected_count: 0,
30
             selected_size: 0,
34
             selected_size: 0,
31
             view_mode: "List".to_string(),
35
             view_mode: "List".to_string(),
36
+            free_space: None,
32
         }
37
         }
33
     }
38
     }
34
 
39
 
@@ -49,6 +54,11 @@ impl StatusBar {
49
         self.view_mode = mode.to_string();
54
         self.view_mode = mode.to_string();
50
     }
55
     }
51
 
56
 
57
+    /// Update free disk space for the given path.
58
+    pub fn update_free_space(&mut self, path: &Path) {
59
+        self.free_space = get_free_space(path);
60
+    }
61
+
52
     /// Get status bar height.
62
     /// Get status bar height.
53
     pub fn height(&self) -> u32 {
63
     pub fn height(&self) -> u32 {
54
         STATUS_BAR_HEIGHT
64
         STATUS_BAR_HEIGHT
@@ -104,10 +114,21 @@ impl StatusBar {
104
             &text_style,
114
             &text_style,
105
         )?;
115
         )?;
106
 
116
 
107
-        // Right side: view mode
117
+        // Right side: free space and view mode
118
+        let mut right_x = self.bounds.x + self.bounds.width as i32 - padding;
119
+
120
+        // View mode
108
         let mode_width = renderer.measure_text(&self.view_mode, &text_style)?.width;
121
         let mode_width = renderer.measure_text(&self.view_mode, &text_style)?.width;
109
-        let mode_x = self.bounds.x + self.bounds.width as i32 - mode_width as i32 - padding;
122
+        right_x -= mode_width as i32;
110
-        renderer.text(&self.view_mode, mode_x as f64, text_y as f64, &text_style)?;
123
+        renderer.text(&self.view_mode, right_x as f64, text_y as f64, &text_style)?;
124
+
125
+        // Free space (if available)
126
+        if let Some(free) = self.free_space {
127
+            let free_text = format!("{} free  |  ", format_bytes(free));
128
+            let free_width = renderer.measure_text(&free_text, &text_style)?.width;
129
+            right_x -= free_width as i32;
130
+            renderer.text(&free_text, right_x as f64, text_y as f64, &text_style)?;
131
+        }
111
 
132
 
112
         Ok(())
133
         Ok(())
113
     }
134
     }
@@ -132,3 +153,18 @@ fn format_bytes(bytes: u64) -> String {
132
         format!("{} B", bytes)
153
         format!("{} B", bytes)
133
     }
154
     }
134
 }
155
 }
156
+
157
+/// Get free disk space for the filesystem containing the given path.
158
+fn get_free_space(path: &Path) -> Option<u64> {
159
+    let c_path = CString::new(path.to_string_lossy().as_bytes()).ok()?;
160
+
161
+    unsafe {
162
+        let mut stat: libc::statvfs = std::mem::zeroed();
163
+        if libc::statvfs(c_path.as_ptr(), &mut stat) == 0 {
164
+            // Available blocks * block size = free space for non-privileged users
165
+            Some(stat.f_bavail as u64 * stat.f_bsize as u64)
166
+        } else {
167
+            None
168
+        }
169
+    }
170
+}