@@ -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 | +} |