From 0dc00ec5c910352f9b8eae3bf45a1f4c4b115c95 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Mon, 23 Mar 2026 16:16:25 +0545 Subject: [PATCH] refactor(src/commands): Refactor battery and touchpad command execution Refactored command flow to separate daemon invocation, added status handling, and improved error handling. - New Features: Added `execute_local` helper and `BatteryCommands::Status` handling. - Enhancements: Simplified daemon checks, removed redundant condition, improved readability and error messages. - Bug Fixes: Fixed missed daemon return path for status command and ensured proper touchpad inhibition logic. --- src/commands/battery.rs | 108 +++++++++++++++------------------- src/commands/cooling.rs | 17 ++++-- src/commands/gpu.rs | 16 +++-- src/commands/inhibit.rs | 8 +-- src/commands/install_rules.rs | 18 ++++-- src/commands/power.rs | 20 ++++--- src/commands/touchpad.rs | 88 ++++++++++++++------------- 7 files changed, 143 insertions(+), 132 deletions(-) diff --git a/src/commands/battery.rs b/src/commands/battery.rs index 9fbdfd9..3df3c75 100644 --- a/src/commands/battery.rs +++ b/src/commands/battery.rs @@ -15,10 +15,6 @@ trait Lapctl { } fn try_call_daemon(command: &BatteryCommands) -> bool { - if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() { - return false; - } - let rt = match tokio::runtime::Runtime::new() { Ok(rt) => rt, Err(_) => return false, @@ -54,20 +50,27 @@ fn try_call_daemon(command: &BatteryCommands) -> bool { } pub fn execute(command: &BatteryCommands) { - if matches!(command, BatteryCommands::Limit { .. }) && try_call_daemon(command) { - println!("Request handled by lapctld daemon."); - return; - } - match command { BatteryCommands::Limit { percent } => { if !(&1..=&100).contains(&percent) { error!("Invalid percentage. Please provide a value between 1 and 100."); std::process::exit(1); } - println!("Setting battery charge limit to {}%", percent); + } + BatteryCommands::Status => {} + } + if matches!(command, BatteryCommands::Limit { .. }) && try_call_daemon(command) { + return; + } + + execute_local(command); +} + +pub fn execute_local(command: &BatteryCommands) { + match command { + BatteryCommands::Limit { percent } => { let mut success_count = 0; let sys_class_power = Path::new("/sys/class/power_supply"); @@ -90,47 +93,34 @@ pub fn execute(command: &BatteryCommands) { } } else { // Ideapad fallback - let mut ideapad_found = false; - if let Ok(ideapad_entries) = - fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi") - { - for ideapad_entry in ideapad_entries.flatten() { - let ideapad_path = ideapad_entry.path(); - let conservation_path = ideapad_path.join("conservation_mode"); - if conservation_path.exists() { - ideapad_found = true; - let val = if *percent < 100 { "1" } else { "0" }; - match fs::write(&conservation_path, val) { - Ok(_) => { - if *percent < 100 { - println!( - "WARNING: Your laptop (Lenovo Ideapad) does NOT support custom charge limits (like {}%).", - percent - ); - println!( - "Instead, lapctl has enabled Lenovo's built-in 'Conservation Mode' via the Ideapad ACPI." - ); - println!( - "This mode is hard-coded into your laptop's firmware to stop charging at ~60%." - ); - } else { - println!( - "Disabled Conservation Mode (charging to 100%)." - ); - } - success_count += 1; - } - Err(e) => debug!( - "Failed to write to conservation_mode: {}", - e - ), + let conservation_path = Path::new( + "/sys/bus/platform/devices/ideapad_acpi/conservation_mode", + ); + if conservation_path.exists() { + let val = if *percent < 100 { "1" } else { "0" }; + match fs::write(conservation_path, val) { + Ok(_) => { + if *percent < 100 { + println!( + "WARNING: Your laptop (Lenovo Ideapad) does NOT support custom charge limits (like {}%).", + percent + ); + println!( + "Instead, lapctl has enabled Lenovo's built-in 'Conservation Mode' via the Ideapad ACPI." + ); + println!( + "This mode is hard-coded into your laptop's firmware to stop charging at ~60%." + ); + } else { + println!( + "Disabled Conservation Mode (charging to 100%)." + ); } + success_count += 1; } + Err(e) => debug!("Failed to write to conservation_mode: {}", e), } } - if !ideapad_found { - debug!("limit not supported for {} (missing thresholds)", name_str); - } } } } @@ -145,7 +135,6 @@ pub fn execute(command: &BatteryCommands) { } } BatteryCommands::Status => { - // Re-use logic from `lapctl status` to show battery-specific details: let sys_class_power = Path::new("/sys/class/power_supply"); if let Ok(entries) = fs::read_dir(sys_class_power) { for entry in entries.flatten() { @@ -168,21 +157,16 @@ pub fn execute(command: &BatteryCommands) { println!(" Charge Limit: {}%", limit.trim()); } else { // Ideapad fallback display - if let Ok(ideapad_entries) = - fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi") + let conservation_path = Path::new( + "/sys/bus/platform/devices/ideapad_acpi/conservation_mode", + ); + if conservation_path.exists() + && let Ok(mode) = fs::read_to_string(conservation_path) { - for ideapad_entry in ideapad_entries.flatten() { - let conservation_path = - ideapad_entry.path().join("conservation_mode"); - if conservation_path.exists() - && let Ok(mode) = fs::read_to_string(&conservation_path) - { - if mode.trim() == "1" { - println!(" Charge Limit: Conservation Mode (~60%)"); - } else { - println!(" Charge Limit: 100%"); - } - } + if mode.trim() == "1" { + println!(" Charge Limit: Conservation Mode (~60%)"); + } else { + println!(" Charge Limit: 100%"); } } } diff --git a/src/commands/cooling.rs b/src/commands/cooling.rs index f75146f..706792f 100644 --- a/src/commands/cooling.rs +++ b/src/commands/cooling.rs @@ -15,11 +15,6 @@ trait Lapctl { } fn try_call_daemon(command: &CoolingCommands) -> bool { - if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() { - debug!("Internal daemon call detected. Skipping D-Bus self-call."); - return false; - } - let rt = match tokio::runtime::Runtime::new() { Ok(rt) => rt, Err(_) => return false, @@ -102,11 +97,21 @@ fn set_asus_throttle_policy(mode: &str) -> bool { } pub fn execute(command: &CoolingCommands) { + let desc = match command { + CoolingCommands::Performance => "Performance", + CoolingCommands::Balanced => "Balanced", + CoolingCommands::Quiet => "Quiet", + }; + println!("Setting thermal/cooling profile to {}", desc); + if try_call_daemon(command) { - println!("Request handled by lapctld daemon."); return; } + execute_local(command); +} + +pub fn execute_local(command: &CoolingCommands) { let (ideapad_mode, asus_mode, desc) = match command { CoolingCommands::Performance => ("1", "1", "Performance"), CoolingCommands::Balanced => ("0", "0", "Balanced"), diff --git a/src/commands/gpu.rs b/src/commands/gpu.rs index 52622b1..5c9c46f 100644 --- a/src/commands/gpu.rs +++ b/src/commands/gpu.rs @@ -40,10 +40,6 @@ trait Lapctl { } fn try_call_daemon(cmd: &GpuCommands) -> bool { - if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() { - return false; - } - let rt = match tokio::runtime::Runtime::new() { Ok(rt) => rt, Err(_) => return false, @@ -748,11 +744,21 @@ fn run_on_dgpu(command: &[String]) { } pub fn execute(cmd: &GpuCommands) { + match cmd { + GpuCommands::Integrated { .. } => println!("Switching to Integrated GPU mode..."), + GpuCommands::Hybrid { .. } => println!("Switching to Hybrid GPU mode..."), + GpuCommands::Nvidia { .. } => println!("Switching to NVIDIA-only GPU mode..."), + _ => {} + } + if try_call_daemon(cmd) { - println!("Request handled by lapctld daemon."); return; } + execute_local(cmd); +} + +pub fn execute_local(cmd: &GpuCommands) { match cmd { GpuCommands::Query => { let mode = get_current_mode(); diff --git a/src/commands/inhibit.rs b/src/commands/inhibit.rs index 4679cc3..0c53371 100644 --- a/src/commands/inhibit.rs +++ b/src/commands/inhibit.rs @@ -18,10 +18,6 @@ trait Lapctl { } fn try_call_daemon_inhibit(active: bool, why: String, who: String) -> bool { - if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() { - return false; - } - let rt = match tokio::runtime::Runtime::new() { Ok(rt) => rt, Err(_) => return false, @@ -62,6 +58,10 @@ pub fn execute(command_args: &[String], why: &str, who: &str, daemon: bool, stop } if daemon && command_args.is_empty() { + println!( + "Attempting to activate system inhibition via lapctld: '{}' by '{}'", + why, who + ); if try_call_daemon_inhibit(true, why.to_string(), who.to_string()) { println!("Persistent system inhibition activated via lapctld."); println!("The system will stay awake until you run: lapctl inhibit --stop"); diff --git a/src/commands/install_rules.rs b/src/commands/install_rules.rs index 6a14def..d319bd3 100644 --- a/src/commands/install_rules.rs +++ b/src/commands/install_rules.rs @@ -20,7 +20,7 @@ const POLKIT_POLICY: &str = r#" auth_admin yes - /usr/bin/lapctl + {EXE_PATH} false @@ -66,7 +66,7 @@ After=dbus.service [Service] Type=simple -ExecStart=/usr/bin/lapctl daemon +ExecStart={EXE_PATH} daemon Restart=on-failure [Install] @@ -76,21 +76,27 @@ WantedBy=multi-user.target pub fn execute() { assert_root(); - println!("Installing lapctl configuration for daemon communication..."); + let current_exe = std::env::current_exe().expect("Failed to get current executable path"); + let exe_path = current_exe.to_str().expect("Invalid executable path"); + + println!("Found lapctl at: {}", exe_path); + println!("Installing lapctl configuration with dynamic paths..."); let dbus_policy_path = "/etc/dbus-1/system.d/org.lapctl.conf"; create_file(dbus_policy_path, DBUS_POLICY, false); + let systemd_content = SYSTEMD_SERVICE.replace("{EXE_PATH}", exe_path); let service_path = "/etc/systemd/system/lapctld.service"; - create_file(service_path, SYSTEMD_SERVICE, false); + create_file(service_path, &systemd_content, false); // Also install the udev rules file (even if empty now) to avoid errors if it was previously there let rules_path = "/etc/udev/rules.d/99-lapctl.rules"; create_file(rules_path, LAPCTL_UDEV_RULES, false); // Polkit fallbacks for rootless execution without the daemon + let polkit_content = POLKIT_POLICY.replace("{EXE_PATH}", exe_path); let polkit_policy_path = "/usr/share/polkit-1/actions/org.lapctl.policy"; - create_file(polkit_policy_path, POLKIT_POLICY, false); + create_file(polkit_policy_path, &polkit_content, false); let polkit_rules_path = "/etc/polkit-1/rules.d/10-lapctl.rules"; create_file(polkit_rules_path, POLKIT_RULES, false); @@ -115,5 +121,5 @@ pub fn execute() { } println!("Installation successful! The lapctld daemon is now running."); - println!("You can now use all lapctl commands without sudo."); + println!("You can now use all lapctl commands from any location."); } diff --git a/src/commands/power.rs b/src/commands/power.rs index c33e265..6365c6b 100644 --- a/src/commands/power.rs +++ b/src/commands/power.rs @@ -16,10 +16,6 @@ trait Lapctl { } fn try_call_daemon(command: &PowerCommands) -> bool { - if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() { - return false; - } - let rt = match tokio::runtime::Runtime::new() { Ok(rt) => rt, Err(_) => return false, @@ -226,11 +222,23 @@ fn set_platform_profile(profile: &str) { } pub fn execute(command: &PowerCommands) { + match command { + PowerCommands::Performance => println!("Setting power profile to Performance"), + PowerCommands::Balanced => println!("Setting power profile to Balanced"), + PowerCommands::BatterySave => println!("Setting power profile to Battery Saver"), + PowerCommands::LimitTdp { watts } => { + println!("Setting CPU Package TDP Limit to {} Watts...", watts) + } + } + if try_call_daemon(command) { - println!("Request handled by lapctld daemon."); return; } + execute_local(command); +} + +pub fn execute_local(command: &PowerCommands) { match command { PowerCommands::Performance => { println!("Setting power profile to Performance"); @@ -242,8 +250,6 @@ pub fn execute(command: &PowerCommands) { PowerCommands::Balanced => { println!("Setting power profile to Balanced"); set_platform_profile("balanced"); - // Schedutil is the modern balanced default. Fallback isn't critical since cpufreq - // usually rejects invalid profiles, and standard Intel/AMD systems use EPP now anyway. set_cpu_governor("schedutil"); set_energy_performance_preference("balance_performance"); println!("Operation completed successfully."); diff --git a/src/commands/touchpad.rs b/src/commands/touchpad.rs index 13ac4bd..f45c557 100644 --- a/src/commands/touchpad.rs +++ b/src/commands/touchpad.rs @@ -1,5 +1,5 @@ use crate::cli::TouchpadCommands; -use log::{error, info}; +use log::{debug, error, info}; use std::fs; use std::path::Path; use zbus::Connection; @@ -15,10 +15,6 @@ trait Lapctl { } fn try_call_daemon(command: &TouchpadCommands) -> bool { - if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() { - return false; - } - let rt = match tokio::runtime::Runtime::new() { Ok(rt) => rt, Err(_) => return false, @@ -60,56 +56,64 @@ fn try_call_daemon(command: &TouchpadCommands) -> bool { } pub fn execute(command: &TouchpadCommands) { + match command { + TouchpadCommands::Enable => println!("Enabling touchpad..."), + TouchpadCommands::Disable => println!("Disabling touchpad..."), + } + if try_call_daemon(command) { - println!("Request handled by lapctld daemon."); return; } - let sys_class_input = Path::new("/sys/class/input"); - if !sys_class_input.exists() { + execute_local(command); +} + +pub fn execute_local(command: &TouchpadCommands) { + match command { + TouchpadCommands::Enable => set_touchpad_inhibition(false), + TouchpadCommands::Disable => set_touchpad_inhibition(true), + } +} + +fn set_touchpad_inhibition(inhibited: bool) { + let dev_input = Path::new("/sys/class/input"); + if !dev_input.exists() { error!("Could not find /sys/class/input. Is this a Linux system?"); return; } - let mut found_touchpads = Vec::new(); - - if let Ok(entries) = fs::read_dir(sys_class_input) { + if let Ok(entries) = fs::read_dir(dev_input) { + let mut found = false; for entry in entries.flatten() { - let path = entry.path(); - let name_path = path.join("name"); - let inhibited_path = path.join("inhibited"); + let name = entry.file_name(); + let name_str = name.to_string_lossy(); - if name_path.exists() - && inhibited_path.exists() - && let Ok(name) = fs::read_to_string(&name_path) - && name.to_lowercase().contains("touchpad") - { - found_touchpads.push((name.trim().to_string(), inhibited_path)); - } - } - } - - if found_touchpads.is_empty() { - error!("No touchpad devices found with 'inhibited' support in /sys/class/input."); - return; - } - - match command { - TouchpadCommands::Enable => { - for (name, path) in found_touchpads { - match fs::write(&path, "0") { - Ok(_) => info!("Enabled touchpad: {}", name), - Err(e) => error!("Failed to enable touchpad {}: {}", name, e), + if name_str.starts_with("input") { + let name_file = entry.path().join("name"); + if let Ok(content) = fs::read_to_string(name_file) { + let content_lower = content.to_lowercase(); + if content_lower.contains("touchpad") || content_lower.contains("trackpad") { + found = true; + let inhibited_file = entry.path().join("inhibited"); + if inhibited_file.exists() { + let val = if inhibited { "1" } else { "0" }; + match fs::write(&inhibited_file, val) { + Ok(_) => info!("Set inhibit={} for {}", inhibited, name_str), + Err(e) => { + debug!("Failed to write to {}: {}", inhibited_file.display(), e) + } + } + } + } } } } - TouchpadCommands::Disable => { - for (name, path) in found_touchpads { - match fs::write(&path, "1") { - Ok(_) => info!("Disabled touchpad: {}", name), - Err(e) => error!("Failed to disable touchpad {}: {}", name, e), - } - } + if found { + println!("Operation completed successfully."); + } else { + error!("No touchpad device found in /sys/class/input."); } + } else { + error!("Failed to read directory /sys/class/input."); } }