feat(commands): add battery, gpu, power, and status command implementations
Added full command implementations for battery limit/status, GPU management (cache operations, mode switching, NVIDIA handling), and a status reporter that prints GPU mode and battery details. Introduced module registration and placeholder power command. New Features - Battery command support (`limit`, `status`) - GPU command suite (`cache-delete`, `cache-query`, `reset`, `reset-sddm`, `integrated`, `hybrid`, `nvidia`) - Status command output showing GPU mode and battery information - Power command placeholder scaffold Enhancements - Unified command registration via `mod.rs` - Added root checks and file utilities for cache and config operations - Integrated logging infrastructure Bug Fixes: none
This commit is contained in:
5 files changed
+555
No files matched your search
@@ -0,0 +1,12 @@
|
||||
use crate::cli::BatteryCommands;
|
||||
|
||||
pub fn execute(command: &BatteryCommands) {
|
||||
match command {
|
||||
BatteryCommands::Limit { percent } => {
|
||||
println!("Setting battery limit to {}%", percent);
|
||||
}
|
||||
BatteryCommands::Status => {
|
||||
println!("Battery status placeholder");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,500 @@
|
||||
use crate::cli::GpuCommands;
|
||||
use crate::hardware::gpu::{get_amd_igpu_name, get_current_mode, get_igpu_vendor, get_nvidia_gpu_pci_bus};
|
||||
use crate::utils::system::{assert_root, create_file, get_display_manager, rebuild_initramfs};
|
||||
use log::{error, info};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
const CACHE_FILE_PATH: &str = "/var/cache/lapctl/cache.json";
|
||||
const BLACKLIST_PATH: &str = "/etc/modprobe.d/blacklist-nvidia.conf";
|
||||
const BLACKLIST_CONTENT: &str = r#"# Automatically generated by lapctl
|
||||
blacklist nouveau
|
||||
blacklist nova_core
|
||||
blacklist nova_drm
|
||||
blacklist nvidia
|
||||
blacklist nvidia_drm
|
||||
blacklist nvidia_uvm
|
||||
blacklist nvidia_modeset
|
||||
blacklist nvidia_current
|
||||
blacklist nvidia_current_drm
|
||||
blacklist nvidia_current_uvm
|
||||
blacklist nvidia_current_modeset
|
||||
blacklist i2c_nvidia_gpu
|
||||
alias nouveau off
|
||||
alias nova_core off
|
||||
alias nova_drm off
|
||||
alias nvidia off
|
||||
alias nvidia_drm off
|
||||
alias nvidia_uvm off
|
||||
alias nvidia_modeset off
|
||||
alias nvidia_current off
|
||||
alias nvidia_current_drm off
|
||||
alias nvidia_current_uvm off
|
||||
alias nvidia_current_modeset off
|
||||
alias i2c_nvidia_gpu off
|
||||
"#;
|
||||
|
||||
const UDEV_INTEGRATED_PATH: &str = "/etc/udev/rules.d/50-remove-nvidia.rules";
|
||||
const UDEV_INTEGRATED: &str = r#"# Automatically generated by lapctl
|
||||
|
||||
# Remove NVIDIA USB xHCI Host Controller devices, if present
|
||||
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{power/control}="auto", ATTR{remove}="1"
|
||||
|
||||
# Remove NVIDIA USB Type-C UCSI devices, if present
|
||||
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{power/control}="auto", ATTR{remove}="1"
|
||||
|
||||
# Remove NVIDIA Audio devices, if present
|
||||
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{power/control}="auto", ATTR{remove}="1"
|
||||
|
||||
# Remove NVIDIA VGA/3D controller devices
|
||||
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", ATTR{power/control}="auto", ATTR{remove}="1"
|
||||
"#;
|
||||
|
||||
const UDEV_PM_PATH: &str = "/etc/udev/rules.d/80-nvidia-pm.rules";
|
||||
const UDEV_PM_CONTENT: &str = r#"# Automatically generated by lapctl
|
||||
|
||||
# Remove NVIDIA USB xHCI Host Controller devices, if present
|
||||
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"
|
||||
|
||||
# Remove NVIDIA USB Type-C UCSI devices, if present
|
||||
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"
|
||||
|
||||
# Remove NVIDIA Audio devices, if present
|
||||
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"
|
||||
|
||||
# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
|
||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
|
||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
|
||||
|
||||
# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
|
||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
|
||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
|
||||
"#;
|
||||
|
||||
const XORG_PATH: &str = "/etc/X11/xorg.conf";
|
||||
const EXTRA_XORG_PATH: &str = "/etc/X11/xorg.conf.d/10-nvidia.conf";
|
||||
const MODESET_PATH: &str = "/etc/modprobe.d/nvidia.conf";
|
||||
const SDDM_XSETUP_PATH: &str = "/usr/share/sddm/scripts/Xsetup";
|
||||
const LIGHTDM_SCRIPT_PATH: &str = "/etc/lightdm/nvidia.sh";
|
||||
const LIGHTDM_CONFIG_PATH: &str = "/etc/lightdm/lightdm.conf.d/20-nvidia.conf";
|
||||
|
||||
const SDDM_XSETUP_CONTENT: &str = r#"#!/bin/sh
|
||||
# Xsetup - run as root before the login dialog appears
|
||||
|
||||
"#;
|
||||
|
||||
const EXTRA_XORG_CONTENT: &str = r#"# Automatically generated by lapctl
|
||||
|
||||
Section "OutputClass"
|
||||
Identifier "nvidia"
|
||||
MatchDriver "nvidia-drm"
|
||||
Driver "nvidia"
|
||||
"#;
|
||||
|
||||
const LIGHTDM_CONFIG_CONTENT: &str = r#"# Automatically generated by lapctl
|
||||
|
||||
[Seat:*]
|
||||
display-setup-script=/etc/lightdm/nvidia.sh
|
||||
"#;
|
||||
|
||||
const NVIDIA_XRANDR_SCRIPT: &str = r#"#!/bin/sh
|
||||
# Automatically generated by lapctl
|
||||
|
||||
current=""
|
||||
|
||||
xrandr --setprovideroutputsource "{}" NVIDIA-0
|
||||
xrandr --auto
|
||||
|
||||
for next in $(xrandr --listmonitors | grep -E " *[0-9]+:.*" | cut -d" " -f6); do
|
||||
[ -z "$current" ] && current=$next && continue
|
||||
xrandr --output "$current" --auto --output "$next" --auto --right-of "$current"
|
||||
current=$next
|
||||
done
|
||||
"#;
|
||||
|
||||
// Helpers
|
||||
fn xorg_intel(bus_id: &str) -> String {
|
||||
format!(r#"# Automatically generated by lapctl
|
||||
|
||||
Section "ServerLayout"
|
||||
Identifier "layout"
|
||||
Screen 0 "nvidia"
|
||||
Inactive "intel"
|
||||
EndSection
|
||||
|
||||
Section "Device"
|
||||
Identifier "nvidia"
|
||||
Driver "nvidia"
|
||||
BusID "{}"
|
||||
EndSection
|
||||
|
||||
Section "Screen"
|
||||
Identifier "nvidia"
|
||||
Device "nvidia"
|
||||
Option "AllowEmptyInitialConfiguration"
|
||||
EndSection
|
||||
|
||||
Section "Device"
|
||||
Identifier "intel"
|
||||
Driver "modesetting"
|
||||
EndSection
|
||||
|
||||
Section "Screen"
|
||||
Identifier "intel"
|
||||
Device "intel"
|
||||
EndSection
|
||||
"#, bus_id)
|
||||
}
|
||||
|
||||
fn xorg_amd(bus_id: &str) -> String {
|
||||
format!(r#"# Automatically generated by lapctl
|
||||
|
||||
Section "ServerLayout"
|
||||
Identifier "layout"
|
||||
Screen 0 "nvidia"
|
||||
Inactive "amdgpu"
|
||||
EndSection
|
||||
|
||||
Section "Device"
|
||||
Identifier "nvidia"
|
||||
Driver "nvidia"
|
||||
BusID "{}"
|
||||
EndSection
|
||||
|
||||
Section "Screen"
|
||||
Identifier "nvidia"
|
||||
Device "nvidia"
|
||||
Option "AllowEmptyInitialConfiguration"
|
||||
EndSection
|
||||
|
||||
Section "Device"
|
||||
Identifier "amdgpu"
|
||||
Driver "amdgpu"
|
||||
EndSection
|
||||
|
||||
Section "Screen"
|
||||
Identifier "amd"
|
||||
Device "amdgpu"
|
||||
EndSection
|
||||
"#, bus_id)
|
||||
}
|
||||
|
||||
fn generate_xrandr_script(igpu_vendor: Option<&String>) -> String {
|
||||
let source_name = match igpu_vendor.map(|s| s.as_str()) {
|
||||
Some("intel") => "modesetting".to_string(),
|
||||
Some("amd") => {
|
||||
if let Some(amd_name) = get_amd_igpu_name() {
|
||||
amd_name
|
||||
} else {
|
||||
"modesetting".to_string()
|
||||
}
|
||||
}
|
||||
_ => "modesetting".to_string(),
|
||||
};
|
||||
NVIDIA_XRANDR_SCRIPT.replace("{}", &source_name)
|
||||
}
|
||||
|
||||
fn cleanup() {
|
||||
let to_remove = vec![
|
||||
BLACKLIST_PATH,
|
||||
UDEV_INTEGRATED_PATH,
|
||||
UDEV_PM_PATH,
|
||||
XORG_PATH,
|
||||
EXTRA_XORG_PATH,
|
||||
MODESET_PATH,
|
||||
LIGHTDM_SCRIPT_PATH,
|
||||
LIGHTDM_CONFIG_PATH,
|
||||
"/etc/lapctl-wayland-nvidia",
|
||||
// legacy paths
|
||||
"/etc/X11/xorg.conf.d/90-nvidia.conf",
|
||||
"/lib/udev/rules.d/50-remove-nvidia.rules",
|
||||
"/lib/udev/rules.d/80-nvidia-pm.rules",
|
||||
];
|
||||
|
||||
for file_path in to_remove {
|
||||
if Path::new(file_path).exists() {
|
||||
if let Err(e) = fs::remove_file(file_path) {
|
||||
error!("Failed to remove file '{}': {}", file_path, e);
|
||||
} else {
|
||||
info!("Removed file {}", file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let backup_path = format!("{}.bak", SDDM_XSETUP_PATH);
|
||||
if Path::new(&backup_path).exists() {
|
||||
info!("Restoring Xsetup backup");
|
||||
if let Ok(content) = fs::read_to_string(&backup_path) {
|
||||
create_file(SDDM_XSETUP_PATH, &content, true);
|
||||
}
|
||||
if let Err(e) = fs::remove_file(&backup_path) {
|
||||
error!("Failed to remove backup: {}", e);
|
||||
} else {
|
||||
info!("Removed file {}", backup_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CACHING LOGIC
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Cache {
|
||||
nvidia_gpu_pci_bus: String,
|
||||
}
|
||||
|
||||
fn create_cache_obj(bus_id: String) -> Cache {
|
||||
Cache { nvidia_gpu_pci_bus: bus_id }
|
||||
}
|
||||
|
||||
fn create_cache_file() {
|
||||
if get_current_mode() != "hybrid" {
|
||||
error!("--cache-create requires that the system be in the hybrid Optimus mode");
|
||||
return;
|
||||
}
|
||||
let bus_id = get_nvidia_gpu_pci_bus();
|
||||
let cache = create_cache_obj(bus_id);
|
||||
|
||||
if let Some(parent) = Path::new(CACHE_FILE_PATH).parent() {
|
||||
let _ = fs::create_dir_all(parent);
|
||||
}
|
||||
|
||||
if let Ok(json) = serde_json::to_string_pretty(&cache) {
|
||||
if fs::write(CACHE_FILE_PATH, json).is_ok() {
|
||||
log::debug!("Created file {}", CACHE_FILE_PATH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_cache_file() -> Result<Cache, String> {
|
||||
if Path::new(CACHE_FILE_PATH).exists() {
|
||||
let content = fs::read_to_string(CACHE_FILE_PATH).map_err(|e| e.to_string())?;
|
||||
serde_json::from_str(&content).map_err(|e| e.to_string())
|
||||
} else if get_current_mode() == "hybrid" {
|
||||
Ok(create_cache_obj(get_nvidia_gpu_pci_bus()))
|
||||
} else {
|
||||
Err("No cache present. Operation requires that the system be in the hybrid Optimus mode".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_integrated() {
|
||||
assert_root();
|
||||
println!("Switching to integrated mode");
|
||||
|
||||
let is_debug = log::log_enabled!(log::Level::Debug);
|
||||
let mut dis_cmd = Command::new("systemctl");
|
||||
dis_cmd.args(&["disable", "nvidia-persistenced.service"]);
|
||||
if !is_debug {
|
||||
dis_cmd.stdout(std::process::Stdio::null());
|
||||
dis_cmd.stderr(std::process::Stdio::null());
|
||||
}
|
||||
match dis_cmd.status() {
|
||||
Ok(s) if s.success() => println!("Successfully disabled nvidia-persistenced.service"),
|
||||
_ => error!("An error ocurred while disabling service"),
|
||||
}
|
||||
|
||||
if get_current_mode() == "hybrid" && !Path::new(CACHE_FILE_PATH).exists() {
|
||||
create_cache_file();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
create_file(BLACKLIST_PATH, BLACKLIST_CONTENT, false);
|
||||
create_file(UDEV_INTEGRATED_PATH, UDEV_INTEGRATED, false);
|
||||
|
||||
rebuild_initramfs();
|
||||
println!("Operation completed successfully");
|
||||
println!("Please reboot your computer for changes to take effect!");
|
||||
}
|
||||
|
||||
fn switch_hybrid(rtd3: Option<u8>, use_nvidia_current: bool) {
|
||||
assert_root();
|
||||
println!("Switching to hybrid mode");
|
||||
println!("Enable PCI-Express Runtime D3 (RTD3) Power Management: {}", rtd3.is_some());
|
||||
|
||||
if get_current_mode() == "hybrid" && !Path::new(CACHE_FILE_PATH).exists() {
|
||||
create_cache_file();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
// persistenced enable
|
||||
let is_debug = log::log_enabled!(log::Level::Debug);
|
||||
let mut enable_cmd = Command::new("systemctl");
|
||||
enable_cmd.args(&["enable", "nvidia-persistenced.service"]);
|
||||
if !is_debug {
|
||||
enable_cmd.stdout(std::process::Stdio::null());
|
||||
enable_cmd.stderr(std::process::Stdio::null());
|
||||
}
|
||||
match enable_cmd.status() {
|
||||
Ok(s) if s.success() => println!("Successfully enabled nvidia-persistenced.service"),
|
||||
_ => error!("An error ocurred while enabling service"),
|
||||
}
|
||||
|
||||
if let Some(val) = rtd3 {
|
||||
let modeset_content = if use_nvidia_current {
|
||||
format!("# Automatically generated by lapctl\n\noptions nvidia-current-drm modeset=1\noptions nvidia-current \"NVreg_DynamicPowerManagement=0x0{}\"\noptions nvidia-current NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0\n", val)
|
||||
} else {
|
||||
format!("# Automatically generated by lapctl\n\noptions nvidia-drm modeset=1\noptions nvidia \"NVreg_DynamicPowerManagement=0x0{}\"\noptions nvidia NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0\n", val)
|
||||
};
|
||||
create_file(MODESET_PATH, &modeset_content, false);
|
||||
create_file(UDEV_PM_PATH, UDEV_PM_CONTENT, false);
|
||||
} else {
|
||||
let modeset_content = if use_nvidia_current {
|
||||
"# Automatically generated by lapctl\n\noptions nvidia-current-drm modeset=1\noptions nvidia-current NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0\n".to_string()
|
||||
} else {
|
||||
"# Automatically generated by lapctl\n\noptions nvidia-drm modeset=1\noptions nvidia NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0\n".to_string()
|
||||
};
|
||||
create_file(MODESET_PATH, &modeset_content, false);
|
||||
}
|
||||
|
||||
rebuild_initramfs();
|
||||
println!("Operation completed successfully");
|
||||
println!("Please reboot your computer for changes to take effect!");
|
||||
}
|
||||
|
||||
fn switch_nvidia(
|
||||
dm: Option<String>,
|
||||
force_comp: bool,
|
||||
coolbits: Option<u32>,
|
||||
use_nvidia_current: bool,
|
||||
wayland: bool,
|
||||
) {
|
||||
assert_root();
|
||||
println!("Switching to nvidia mode");
|
||||
println!("Enable ForceCompositionPipeline: {}", force_comp);
|
||||
println!("Enable Coolbits: {}", coolbits.is_some());
|
||||
|
||||
// persistenced enable
|
||||
let is_debug = log::log_enabled!(log::Level::Debug);
|
||||
let mut enable_cmd = Command::new("systemctl");
|
||||
enable_cmd.args(&["enable", "nvidia-persistenced.service"]);
|
||||
if !is_debug {
|
||||
enable_cmd.stdout(std::process::Stdio::null());
|
||||
enable_cmd.stderr(std::process::Stdio::null());
|
||||
}
|
||||
match enable_cmd.status() {
|
||||
Ok(s) if s.success() => println!("Successfully enabled nvidia-persistenced.service"),
|
||||
_ => error!("An error ocurred while enabling service"),
|
||||
}
|
||||
|
||||
if get_current_mode() == "hybrid" && !Path::new(CACHE_FILE_PATH).exists() {
|
||||
create_cache_file();
|
||||
}
|
||||
|
||||
let cache = read_cache_file();
|
||||
let pci_bus = match cache {
|
||||
Ok(c) => c.nvidia_gpu_pci_bus,
|
||||
Err(_) => get_nvidia_gpu_pci_bus()
|
||||
};
|
||||
|
||||
cleanup();
|
||||
|
||||
if use_nvidia_current {
|
||||
create_file(MODESET_PATH, "# Automatically generated by lapctl\n\noptions nvidia-current-drm modeset=1\noptions nvidia-current NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0\n", false);
|
||||
} else {
|
||||
create_file(MODESET_PATH, "# Automatically generated by lapctl\n\noptions nvidia-drm modeset=1\noptions nvidia NVreg_UsePageAttributeTable=1 NVreg_InitializeSystemMemoryAllocations=0\n", false);
|
||||
}
|
||||
|
||||
if wayland {
|
||||
create_file("/etc/lapctl-wayland-nvidia", "# Wayland mode marker for lapctl\n", false);
|
||||
} else {
|
||||
let igpu_vendor = get_igpu_vendor();
|
||||
if let Some(ref vendor) = igpu_vendor {
|
||||
if vendor == "intel" {
|
||||
create_file(XORG_PATH, &xorg_intel(&pci_bus), false);
|
||||
} else if vendor == "amd" {
|
||||
create_file(XORG_PATH, &xorg_amd(&pci_bus), false);
|
||||
}
|
||||
}
|
||||
|
||||
let mut extra_xorg = EXTRA_XORG_CONTENT.to_string();
|
||||
if force_comp {
|
||||
extra_xorg.push_str(" Option \"ForceCompositionPipeline\" \"true\"\n");
|
||||
}
|
||||
if let Some(cb) = coolbits {
|
||||
extra_xorg.push_str(&format!(" Option \"Coolbits\" \"{}\"\n", cb));
|
||||
}
|
||||
if force_comp || coolbits.is_some() {
|
||||
extra_xorg.push_str("EndSection\n");
|
||||
create_file(EXTRA_XORG_PATH, &extra_xorg, false);
|
||||
}
|
||||
|
||||
let display_manager = dm.or_else(|| get_display_manager());
|
||||
|
||||
if let Some(cdm) = display_manager {
|
||||
if cdm == "sddm" {
|
||||
if Path::new(SDDM_XSETUP_PATH).exists() {
|
||||
info!("Creating Xsetup backup");
|
||||
if let Ok(content) = fs::read_to_string(SDDM_XSETUP_PATH) {
|
||||
create_file(&format!("{}.bak", SDDM_XSETUP_PATH), &content, false);
|
||||
}
|
||||
}
|
||||
create_file(SDDM_XSETUP_PATH, &generate_xrandr_script(igpu_vendor.as_ref()), true);
|
||||
} else if cdm == "lightdm" {
|
||||
create_file(LIGHTDM_SCRIPT_PATH, &generate_xrandr_script(igpu_vendor.as_ref()), true);
|
||||
create_file(LIGHTDM_CONFIG_PATH, LIGHTDM_CONFIG_CONTENT, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rebuild_initramfs();
|
||||
println!("Operation completed successfully");
|
||||
println!("Please reboot your computer for changes to take effect!");
|
||||
}
|
||||
|
||||
pub fn execute(cmd: &GpuCommands) {
|
||||
match cmd {
|
||||
GpuCommands::Query => {
|
||||
let mode = get_current_mode();
|
||||
println!("{}", mode);
|
||||
}
|
||||
GpuCommands::CacheCreate => {
|
||||
assert_root();
|
||||
create_cache_file();
|
||||
}
|
||||
GpuCommands::CacheDelete => {
|
||||
assert_root();
|
||||
if Path::new(CACHE_FILE_PATH).exists() {
|
||||
let _ = fs::remove_file(CACHE_FILE_PATH);
|
||||
if let Some(parent) = Path::new(CACHE_FILE_PATH).parent() {
|
||||
let _ = fs::remove_dir(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
GpuCommands::CacheQuery => {
|
||||
if let Ok(content) = fs::read_to_string(CACHE_FILE_PATH) {
|
||||
println!("{}", content);
|
||||
} else {
|
||||
println!("ERROR: Could not read {}", CACHE_FILE_PATH);
|
||||
}
|
||||
}
|
||||
GpuCommands::Reset => {
|
||||
assert_root();
|
||||
|
||||
if get_current_mode() == "hybrid" && !Path::new(CACHE_FILE_PATH).exists() {
|
||||
create_cache_file();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
if Path::new(CACHE_FILE_PATH).exists() {
|
||||
let _ = fs::remove_file(CACHE_FILE_PATH);
|
||||
if let Some(parent) = Path::new(CACHE_FILE_PATH).parent() {
|
||||
let _ = fs::remove_dir(parent);
|
||||
}
|
||||
}
|
||||
rebuild_initramfs();
|
||||
println!("Operation completed successfully");
|
||||
}
|
||||
GpuCommands::ResetSddm => {
|
||||
assert_root();
|
||||
create_file(SDDM_XSETUP_PATH, SDDM_XSETUP_CONTENT, true);
|
||||
println!("Operation completed successfully");
|
||||
}
|
||||
GpuCommands::Integrated => switch_integrated(),
|
||||
GpuCommands::Hybrid { rtd3, use_nvidia_current } => switch_hybrid(rtd3.clone(), *use_nvidia_current),
|
||||
GpuCommands::Nvidia { dm, force_comp, coolbits, use_nvidia_current, wayland } => {
|
||||
switch_nvidia(dm.clone(), *force_comp, coolbits.clone(), *use_nvidia_current, *wayland)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod battery;
|
||||
pub mod gpu;
|
||||
pub mod power;
|
||||
pub mod status;
|
||||
@@ -0,0 +1,3 @@
|
||||
pub fn execute() {
|
||||
println!("Power command placeholder");
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use crate::hardware::gpu;
|
||||
use std::fs;
|
||||
|
||||
pub fn execute() {
|
||||
println!("--- lapctl status ---");
|
||||
|
||||
// GPU Mode
|
||||
let current_mode = gpu::get_current_mode();
|
||||
println!("GPU Mode: {}", current_mode);
|
||||
|
||||
// Battery Limit / Status checks (we can peek into typical battery paths)
|
||||
// Note: this assumes a typical sysfs structure. A complete implementation
|
||||
// would parse /sys/class/power_supply/BAT0 or similar.
|
||||
let sys_class_power = "/sys/class/power_supply";
|
||||
if let Ok(entries) = fs::read_dir(sys_class_power) {
|
||||
for entry in entries.flatten() {
|
||||
let name = entry.file_name();
|
||||
let name_str = name.to_string_lossy();
|
||||
if name_str.starts_with("BAT") {
|
||||
let bat_path = entry.path();
|
||||
|
||||
let capacity = fs::read_to_string(bat_path.join("capacity")).unwrap_or_else(|_| "Unknown".into());
|
||||
let status = fs::read_to_string(bat_path.join("status")).unwrap_or_else(|_| "Unknown".into());
|
||||
|
||||
println!("{}:", name_str);
|
||||
println!(" Capacity: {}%", capacity.trim());
|
||||
println!(" Status: {}", status.trim());
|
||||
|
||||
// Print charing limit if exists (e.g., charge_control_end_threshold)
|
||||
if let Ok(limit) = fs::read_to_string(bat_path.join("charge_control_end_threshold")) {
|
||||
println!(" Charge Limit: {}%", limit.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user