diff --git a/src/config.rs b/src/config.rs index ed86b04..94cba54 100644 --- a/src/config.rs +++ b/src/config.rs @@ -397,39 +397,27 @@ impl Config { /// Check if a module is enabled in the configuration. /// Returns false if the module is explicitly disabled; true if enabled or unknown. pub fn is_module_enabled(&self, module_name: &str) -> bool { - match module_name { - #[cfg(feature = "mod-network")] - "net" | "network" => self.network.enabled, - #[cfg(feature = "mod-hardware")] - "cpu" => self.cpu.enabled, - #[cfg(feature = "mod-hardware")] - "mem" | "memory" => self.memory.enabled, - #[cfg(feature = "mod-hardware")] - "gpu" => self.gpu.enabled, - #[cfg(feature = "mod-hardware")] - "sys" => self.sys.enabled, - #[cfg(feature = "mod-hardware")] - "disk" => self.disk.enabled, - #[cfg(feature = "mod-hardware")] - "pool" | "btrfs" => self.pool.enabled, - #[cfg(feature = "mod-hardware")] - "power" => self.power.enabled, - #[cfg(feature = "mod-hardware")] - "game" => self.game.enabled, - #[cfg(feature = "mod-audio")] - "vol" | "audio" | "mic" => self.audio.enabled, - #[cfg(feature = "mod-bt")] - "bt" | "bluetooth" => self.bt.enabled, - #[cfg(feature = "mod-dbus")] - "mpris" => self.mpris.enabled, - #[cfg(feature = "mod-dbus")] - "backlight" => self.backlight.enabled, - #[cfg(feature = "mod-dbus")] - "kbd" | "keyboard" => self.keyboard.enabled, - #[cfg(feature = "mod-dbus")] - "dnd" => self.dnd.enabled, - _ => true, + macro_rules! gen_enabled_match { + ($( { $feature:literal, $field:ident, $state:ty, [$($name:literal),+], [$($sig_name:literal),+], $module:path, $signal:ident, [$($default_arg:literal),*], $config:ident } )*) => { + match module_name { + $( + #[cfg(feature = $feature)] + $($name)|+ => self.$config.enabled, + )* + // Dispatch-only modules (no watch channel) + #[cfg(feature = "mod-audio")] + "mic" => self.audio.enabled, + #[cfg(feature = "mod-hardware")] + "pool" | "btrfs" => self.pool.enabled, + #[cfg(feature = "mod-hardware")] + "power" => self.power.enabled, + #[cfg(feature = "mod-hardware")] + "game" => self.game.enabled, + _ => true, + } + }; } + for_each_watched_module!(gen_enabled_match) } pub fn validate(&self) { diff --git a/src/modules/hardware.rs b/src/modules/hardware.rs index ffa5d8b..22152a9 100644 --- a/src/modules/hardware.rs +++ b/src/modules/hardware.rs @@ -143,122 +143,139 @@ impl HardwareDaemon { async fn poll_gpu(&mut self, gpu: &mut crate::state::GpuState) { gpu.active = false; - if (self.gpu_vendor.as_deref() == Some("NVIDIA") || self.gpu_vendor.is_none()) - && let Ok(output) = tokio::process::Command::new("nvidia-smi") - .args([ - "--query-gpu=utilization.gpu,memory.used,memory.total,temperature.gpu,name", - "--format=csv,noheader,nounits", - ]) - .output() - .await - && output.status.success() - { - let stdout = String::from_utf8_lossy(&output.stdout); - if let Some(line) = stdout.lines().next() { - let parts: Vec<&str> = line.split(',').collect(); - if parts.len() >= 5 { - gpu.active = true; - gpu.vendor = "NVIDIA".to_string(); - gpu.usage = parts[0].trim().parse().unwrap_or(0.0); - gpu.vram_used = parts[1].trim().parse::().unwrap_or(0.0) / 1024.0; - gpu.vram_total = parts[2].trim().parse::().unwrap_or(0.0) / 1024.0; - gpu.temp = parts[3].trim().parse().unwrap_or(0.0); - gpu.model = parts[4].trim().to_string(); + match self.gpu_vendor.as_deref() { + Some("NVIDIA") => { + Self::poll_nvidia(gpu).await; + } + Some("AMD") => { + Self::poll_amd(gpu); + } + Some("Intel") => { + Self::poll_intel(gpu); + } + _ => { + // Detection pass: try each vendor, cache the first that responds. + Self::poll_nvidia(gpu).await; + if gpu.active { self.gpu_vendor = Some("NVIDIA".to_string()); return; } - } - } - - if self.gpu_vendor.as_deref() == Some("AMD") - || self.gpu_vendor.as_deref() == Some("Intel") - || self.gpu_vendor.is_none() - { - for i in 0..=3 { - let base = format!("/sys/class/drm/card{}/device", i); - - if (self.gpu_vendor.as_deref() == Some("AMD") || self.gpu_vendor.is_none()) - && let Ok(usage_str) = - std::fs::read_to_string(format!("{}/gpu_busy_percent", base)) - { - gpu.active = true; - gpu.vendor = "AMD".to_string(); - gpu.usage = usage_str.trim().parse().unwrap_or(0.0); - - if let Ok(mem_used) = - std::fs::read_to_string(format!("{}/mem_info_vram_used", base)) - { - gpu.vram_used = mem_used.trim().parse::().unwrap_or(0.0) - / 1024.0 - / 1024.0 - / 1024.0; - } - if let Ok(mem_total) = - std::fs::read_to_string(format!("{}/mem_info_vram_total", base)) - { - gpu.vram_total = mem_total.trim().parse::().unwrap_or(0.0) - / 1024.0 - / 1024.0 - / 1024.0; - } - - if let Ok(entries) = std::fs::read_dir(format!("{}/hwmon", base)) { - for entry in entries.flatten() { - let temp_path = entry.path().join("temp1_input"); - if let Ok(temp_str) = std::fs::read_to_string(temp_path) { - gpu.temp = temp_str.trim().parse::().unwrap_or(0.0) / 1000.0; - break; - } - } - } - gpu.model = "AMD GPU".to_string(); + Self::poll_amd(gpu); + if gpu.active { self.gpu_vendor = Some("AMD".to_string()); return; } - - if self.gpu_vendor.as_deref() == Some("Intel") || self.gpu_vendor.is_none() { - let freq_path = - if std::path::Path::new(&format!("{}/gt_cur_freq_mhz", base)).exists() { - Some(format!("{}/gt_cur_freq_mhz", base)) - } else if std::path::Path::new(&format!( - "/sys/class/drm/card{}/gt_cur_freq_mhz", - i - )) - .exists() - { - Some(format!("/sys/class/drm/card{}/gt_cur_freq_mhz", i)) - } else { - None - }; - - if let Some(path) = freq_path - && let Ok(freq_str) = std::fs::read_to_string(&path) - { - gpu.active = true; - gpu.vendor = "Intel".to_string(); - - let cur_freq = freq_str.trim().parse::().unwrap_or(0.0); - let mut max_freq = 0.0; - - let max_path = path.replace("gt_cur_freq_mhz", "gt_max_freq_mhz"); - if let Ok(max_str) = std::fs::read_to_string(max_path) { - max_freq = max_str.trim().parse::().unwrap_or(0.0); - } - - gpu.usage = if max_freq > 0.0 { - (cur_freq / max_freq) * 100.0 - } else { - 0.0 - }; - gpu.temp = 0.0; - gpu.vram_used = 0.0; - gpu.vram_total = 0.0; - gpu.model = format!("Intel iGPU ({}MHz)", cur_freq); - self.gpu_vendor = Some("Intel".to_string()); - return; - } + Self::poll_intel(gpu); + if gpu.active { + self.gpu_vendor = Some("Intel".to_string()); } } } } + + async fn poll_nvidia(gpu: &mut crate::state::GpuState) { + let Ok(output) = tokio::process::Command::new("nvidia-smi") + .args([ + "--query-gpu=utilization.gpu,memory.used,memory.total,temperature.gpu,name", + "--format=csv,noheader,nounits", + ]) + .output() + .await + else { + return; + }; + if !output.status.success() { + return; + } + let stdout = String::from_utf8_lossy(&output.stdout); + if let Some(line) = stdout.lines().next() { + let parts: Vec<&str> = line.split(',').collect(); + if parts.len() >= 5 { + gpu.active = true; + gpu.vendor = "NVIDIA".to_string(); + gpu.usage = parts[0].trim().parse().unwrap_or(0.0); + gpu.vram_used = parts[1].trim().parse::().unwrap_or(0.0) / 1024.0; + gpu.vram_total = parts[2].trim().parse::().unwrap_or(0.0) / 1024.0; + gpu.temp = parts[3].trim().parse().unwrap_or(0.0); + gpu.model = parts[4].trim().to_string(); + } + } + } + + fn poll_amd(gpu: &mut crate::state::GpuState) { + for i in 0..=3 { + let base = format!("/sys/class/drm/card{}/device", i); + let Ok(usage_str) = std::fs::read_to_string(format!("{}/gpu_busy_percent", base)) + else { + continue; + }; + + gpu.active = true; + gpu.vendor = "AMD".to_string(); + gpu.usage = usage_str.trim().parse().unwrap_or(0.0); + + if let Ok(mem_used) = std::fs::read_to_string(format!("{}/mem_info_vram_used", base)) { + gpu.vram_used = + mem_used.trim().parse::().unwrap_or(0.0) / 1024.0 / 1024.0 / 1024.0; + } + if let Ok(mem_total) = std::fs::read_to_string(format!("{}/mem_info_vram_total", base)) + { + gpu.vram_total = + mem_total.trim().parse::().unwrap_or(0.0) / 1024.0 / 1024.0 / 1024.0; + } + + if let Ok(entries) = std::fs::read_dir(format!("{}/hwmon", base)) { + for entry in entries.flatten() { + let temp_path = entry.path().join("temp1_input"); + if let Ok(temp_str) = std::fs::read_to_string(temp_path) { + gpu.temp = temp_str.trim().parse::().unwrap_or(0.0) / 1000.0; + break; + } + } + } + gpu.model = "AMD GPU".to_string(); + return; + } + } + + fn poll_intel(gpu: &mut crate::state::GpuState) { + for i in 0..=3 { + let base = format!("/sys/class/drm/card{}/device", i); + let freq_path = if std::path::Path::new(&format!("{}/gt_cur_freq_mhz", base)).exists() { + Some(format!("{}/gt_cur_freq_mhz", base)) + } else if std::path::Path::new(&format!("/sys/class/drm/card{}/gt_cur_freq_mhz", i)) + .exists() + { + Some(format!("/sys/class/drm/card{}/gt_cur_freq_mhz", i)) + } else { + None + }; + + if let Some(path) = freq_path + && let Ok(freq_str) = std::fs::read_to_string(&path) + { + gpu.active = true; + gpu.vendor = "Intel".to_string(); + + let cur_freq = freq_str.trim().parse::().unwrap_or(0.0); + let mut max_freq = 0.0; + + let max_path = path.replace("gt_cur_freq_mhz", "gt_max_freq_mhz"); + if let Ok(max_str) = std::fs::read_to_string(max_path) { + max_freq = max_str.trim().parse::().unwrap_or(0.0); + } + + gpu.usage = if max_freq > 0.0 { + (cur_freq / max_freq) * 100.0 + } else { + 0.0 + }; + gpu.temp = 0.0; + gpu.vram_used = 0.0; + gpu.vram_total = 0.0; + gpu.model = format!("Intel iGPU ({}MHz)", cur_freq); + return; + } + } + } }