Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d12d0a0916 | ||
|
|
30dadec637 | ||
|
|
dba3995608 | ||
|
|
c8a688f44f | ||
|
|
7de76d0365 | ||
|
|
055fdcd500 | ||
|
|
934aeda6aa | ||
|
|
8947bd643a | ||
|
|
5ac2aed516 | ||
|
|
4020f65e28 | ||
|
|
d52b3992e3 |
No files matched your search
Generated
+1
-1
@@ -596,7 +596,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lapctl"
|
name = "lapctl"
|
||||||
version = "0.3.1"
|
version = "0.3.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lapctl"
|
name = "lapctl"
|
||||||
version = "0.3.1"
|
version = "0.3.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ Built with performance and simplicity in mind, it talks directly to your system'
|
|||||||
- **Intelligent Cooling**: Force your fans into Performance, Balanced, or Quiet modes (supporting ASUS and Lenovo laptops).
|
- **Intelligent Cooling**: Force your fans into Performance, Balanced, or Quiet modes (supporting ASUS and Lenovo laptops).
|
||||||
- **Display Refresh Rate**: Easily query available refresh rates and change your active display's Hz on-the-fly (100% native Rust Wayland implementation using `zwlr_output_manager_v1` for wlroots compositors like Sway and Hyprland).
|
- **Display Refresh Rate**: Easily query available refresh rates and change your active display's Hz on-the-fly (100% native Rust Wayland implementation using `zwlr_output_manager_v1` for wlroots compositors like Sway and Hyprland).
|
||||||
- **Touchpad Toggle**: Quickly enable or disable your touchpad from the terminal when using an external mouse.
|
- **Touchpad Toggle**: Quickly enable or disable your touchpad from the terminal when using an external mouse.
|
||||||
- **Sleep Inhibitor**: Running a long compile or a critical download? Use the inhibitor to stop your laptop from falling asleep mid task.
|
|
||||||
- **Instant Status**: Get a bird's eye view of your hardware state, battery health, and current limits with one simple command.
|
- **Instant Status**: Get a bird's eye view of your hardware state, battery health, and current limits with one simple command.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -59,7 +58,6 @@ sudo lapctl install-rules
|
|||||||
|
|
||||||
#### Requirements
|
#### Requirements
|
||||||
|
|
||||||
- **systemd**: For sleep inhibitor (`systemd-inhibit`)
|
|
||||||
- **GPU Switching (Optional)**: `xrandr` and `nvidia-settings` are strictly required **ONLY** when using the `lapctl gpu` command on X11 (to route proprietary NVIDIA Optimus drivers).
|
- **GPU Switching (Optional)**: `xrandr` and `nvidia-settings` are strictly required **ONLY** when using the `lapctl gpu` command on X11 (to route proprietary NVIDIA Optimus drivers).
|
||||||
- **Wayland Display**: Built entirely natively using `wayland-client` and `wayland-protocols-wlr` (no `wlr-randr` required!)
|
- **Wayland Display**: Built entirely natively using `wayland-client` and `wayland-protocols-wlr` (no `wlr-randr` required!)
|
||||||
|
|
||||||
@@ -104,11 +102,6 @@ lapctl touchpad enable
|
|||||||
lapctl display rates
|
lapctl display rates
|
||||||
lapctl display set-rate 144
|
lapctl display set-rate 144
|
||||||
|
|
||||||
# Keep it awake (requires lapctld)
|
|
||||||
lapctl inhibit --daemon # Stay awake persistently (via daemon)
|
|
||||||
lapctl inhibit --stop # Allow sleep again
|
|
||||||
lapctl inhibit --why "Keeping awake" -- sleep 60 # One-shot (local)
|
|
||||||
|
|
||||||
# Check everything
|
# Check everything
|
||||||
lapctl status
|
lapctl status
|
||||||
```
|
```
|
||||||
|
|||||||
-17
@@ -51,23 +51,6 @@ pub enum Commands {
|
|||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: TouchpadCommands,
|
command: TouchpadCommands,
|
||||||
},
|
},
|
||||||
/// Inhibit system sleep/suspend
|
|
||||||
Inhibit {
|
|
||||||
/// The command to run while inhibiting (optional)
|
|
||||||
command: Vec<String>,
|
|
||||||
/// Why the system is being inhibited
|
|
||||||
#[arg(long, default_value = "lapctl inhibiting sleep")]
|
|
||||||
why: String,
|
|
||||||
/// Who is inhibiting the system
|
|
||||||
#[arg(long, default_value = "lapctl")]
|
|
||||||
who: String,
|
|
||||||
/// Run the inhibitor in the background (daemon mode)
|
|
||||||
#[arg(long)]
|
|
||||||
daemon: bool,
|
|
||||||
/// Stop any active persistent inhibition managed by the daemon
|
|
||||||
#[arg(long)]
|
|
||||||
stop: bool,
|
|
||||||
},
|
|
||||||
/// Start the lapctl background daemon (requires root)
|
/// Start the lapctl background daemon (requires root)
|
||||||
Daemon,
|
Daemon,
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-45
@@ -14,6 +14,10 @@ trait Lapctl {
|
|||||||
async fn set_battery_limit(&self, percent: u32) -> zbus::Result<()>;
|
async fn set_battery_limit(&self, percent: u32) -> zbus::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_ideapad() -> bool {
|
||||||
|
Path::new("/sys/bus/platform/drivers/ideapad_acpi").exists()
|
||||||
|
}
|
||||||
|
|
||||||
fn try_call_daemon(command: &BatteryCommands) -> bool {
|
fn try_call_daemon(command: &BatteryCommands) -> bool {
|
||||||
let rt = match tokio::runtime::Runtime::new() {
|
let rt = match tokio::runtime::Runtime::new() {
|
||||||
Ok(rt) => rt,
|
Ok(rt) => rt,
|
||||||
@@ -56,7 +60,16 @@ pub fn execute(command: &BatteryCommands) {
|
|||||||
error!("Invalid percentage. Please provide a value between 1 and 100.");
|
error!("Invalid percentage. Please provide a value between 1 and 100.");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
println!("Setting battery charge limit to {}%", percent);
|
|
||||||
|
if is_ideapad() {
|
||||||
|
if *percent < 100 {
|
||||||
|
println!("Setting battery charge limit (Lenovo Conservation Mode ~60%)...");
|
||||||
|
} else {
|
||||||
|
println!("Disabling battery charge limit (charging to 100%)...");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Setting battery charge limit to {}%...", percent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BatteryCommands::Status => {}
|
BatteryCommands::Status => {}
|
||||||
}
|
}
|
||||||
@@ -72,6 +85,27 @@ pub fn execute_local(command: &BatteryCommands) {
|
|||||||
match command {
|
match command {
|
||||||
BatteryCommands::Limit { percent } => {
|
BatteryCommands::Limit { percent } => {
|
||||||
let mut success_count = 0;
|
let mut success_count = 0;
|
||||||
|
|
||||||
|
// Ideapad fallback - search for conservation_mode in ideapad_acpi driver directories
|
||||||
|
if let Ok(drivers) = fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi") {
|
||||||
|
for entry in drivers.flatten() {
|
||||||
|
let conservation_path = entry.path().join("conservation_mode");
|
||||||
|
if conservation_path.exists() {
|
||||||
|
let val = if *percent < 100 { "1" } else { "0" };
|
||||||
|
match fs::write(&conservation_path, val) {
|
||||||
|
Ok(_) => {
|
||||||
|
success_count += 1;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Failed to write to {}: {}", conservation_path.display(), e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only try standard thresholds if we haven't already succeeded with Ideapad fallback
|
||||||
|
// or if we want to ensure all bases are covered. For now, let's do both but prioritize the message.
|
||||||
let sys_class_power = Path::new("/sys/class/power_supply");
|
let sys_class_power = Path::new("/sys/class/power_supply");
|
||||||
|
|
||||||
if let Ok(entries) = fs::read_dir(sys_class_power) {
|
if let Ok(entries) = fs::read_dir(sys_class_power) {
|
||||||
@@ -82,6 +116,10 @@ pub fn execute_local(command: &BatteryCommands) {
|
|||||||
if name_str.starts_with("BAT") {
|
if name_str.starts_with("BAT") {
|
||||||
let threshold_path = entry.path().join("charge_control_end_threshold");
|
let threshold_path = entry.path().join("charge_control_end_threshold");
|
||||||
if threshold_path.exists() {
|
if threshold_path.exists() {
|
||||||
|
println!(
|
||||||
|
"Setting battery charge limit for {} to {}%",
|
||||||
|
name_str, percent
|
||||||
|
);
|
||||||
match fs::write(&threshold_path, percent.to_string()) {
|
match fs::write(&threshold_path, percent.to_string()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Successfully set limit for {}", name_str);
|
info!("Successfully set limit for {}", name_str);
|
||||||
@@ -91,36 +129,6 @@ pub fn execute_local(command: &BatteryCommands) {
|
|||||||
debug!("Failed to write to {}: {}", threshold_path.display(), e)
|
debug!("Failed to write to {}: {}", threshold_path.display(), e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Ideapad fallback
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,22 +159,41 @@ pub fn execute_local(command: &BatteryCommands) {
|
|||||||
println!(" Capacity: {}%", capacity.trim());
|
println!(" Capacity: {}%", capacity.trim());
|
||||||
println!(" Status: {}", status.trim());
|
println!(" Status: {}", status.trim());
|
||||||
|
|
||||||
if let Ok(limit) =
|
// Check for Ideapad conservation mode first using robust detection
|
||||||
fs::read_to_string(bat_path.join("charge_control_end_threshold"))
|
let mut limit_shown = false;
|
||||||
|
if let Ok(drivers) = fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi")
|
||||||
{
|
{
|
||||||
println!(" Charge Limit: {}%", limit.trim());
|
for entry in drivers.flatten() {
|
||||||
} else {
|
let conservation_path = entry.path().join("conservation_mode");
|
||||||
// Ideapad fallback display
|
if conservation_path.exists()
|
||||||
let conservation_path = Path::new(
|
&& let Ok(mode) = fs::read_to_string(&conservation_path)
|
||||||
"/sys/bus/platform/devices/ideapad_acpi/conservation_mode",
|
{
|
||||||
);
|
if mode.trim() == "1" {
|
||||||
if conservation_path.exists()
|
println!(" Charge Limit: Conservation Mode (~60%)");
|
||||||
&& let Ok(mode) = fs::read_to_string(conservation_path)
|
limit_shown = true;
|
||||||
|
} else if mode.trim() == "0" {
|
||||||
|
// On Ideapads 0 means 100% or off.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !limit_shown {
|
||||||
|
if let Ok(limit) =
|
||||||
|
fs::read_to_string(bat_path.join("charge_control_end_threshold"))
|
||||||
{
|
{
|
||||||
if mode.trim() == "1" {
|
println!(" Charge Limit: {}%", limit.trim());
|
||||||
println!(" Charge Limit: Conservation Mode (~60%)");
|
} else {
|
||||||
} else {
|
// Re-check if it's an ideapad and we haven't shown a limit (likely 100%)
|
||||||
println!(" Charge Limit: 100%");
|
if let Ok(drivers) =
|
||||||
|
fs::read_dir("/sys/bus/platform/drivers/ideapad_acpi")
|
||||||
|
{
|
||||||
|
for entry in drivers.flatten() {
|
||||||
|
if entry.path().join("conservation_mode").exists() {
|
||||||
|
println!(" Charge Limit: 100%");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
use log::{error, info};
|
|
||||||
use std::process::Command;
|
|
||||||
use zbus::Connection;
|
|
||||||
use zbus::proxy;
|
|
||||||
|
|
||||||
#[proxy(
|
|
||||||
interface = "org.lapctl1",
|
|
||||||
default_service = "org.lapctl",
|
|
||||||
default_path = "/org/lapctl"
|
|
||||||
)]
|
|
||||||
trait Lapctl {
|
|
||||||
async fn set_system_inhibition(
|
|
||||||
&self,
|
|
||||||
active: bool,
|
|
||||||
why: String,
|
|
||||||
who: String,
|
|
||||||
) -> zbus::Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_call_daemon_inhibit(active: bool, why: String, who: String) -> bool {
|
|
||||||
let rt = match tokio::runtime::Runtime::new() {
|
|
||||||
Ok(rt) => rt,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
let connection =
|
|
||||||
match tokio::time::timeout(std::time::Duration::from_secs(2), Connection::system())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(Ok(conn)) => conn,
|
|
||||||
_ => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let proxy = match LapctlProxy::new(&connection).await {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = tokio::time::timeout(
|
|
||||||
std::time::Duration::from_secs(2),
|
|
||||||
proxy.set_system_inhibition(active, why, who),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
matches!(res, Ok(Ok(_)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute(command_args: &[String], why: &str, who: &str, daemon: bool, stop: bool) {
|
|
||||||
if stop {
|
|
||||||
if try_call_daemon_inhibit(false, why.to_string(), who.to_string()) {
|
|
||||||
println!("Persistent system inhibition deactivated via lapctld.");
|
|
||||||
} else {
|
|
||||||
error!("Failed to deactivate inhibition via daemon. Is lapctld running?");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
error!("Failed to activate inhibition via daemon. Is lapctld running?");
|
|
||||||
// Fallback to local systemd-inhibit if daemon call fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let args = build_args(command_args, why, who);
|
|
||||||
|
|
||||||
if command_args.is_empty() {
|
|
||||||
if !daemon {
|
|
||||||
info!("No command provided. Inhibiting sleep indefinitely. Press Ctrl+C to stop.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info!(
|
|
||||||
"Running command while inhibiting sleep: {}",
|
|
||||||
command_args.join(" ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if daemon {
|
|
||||||
match Command::new("systemd-inhibit").args(&args).spawn() {
|
|
||||||
Ok(child) => {
|
|
||||||
info!("Inhibitor started in background with PID: {}", child.id());
|
|
||||||
info!("To stop it, use: pkill -P {} systemd-inhibit", child.id());
|
|
||||||
// In background mode, we just let the child run and exit lapctl
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to spawn systemd-inhibit: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match Command::new("systemd-inhibit").args(&args).status() {
|
|
||||||
Ok(status) => {
|
|
||||||
if !status.success() {
|
|
||||||
error!("systemd-inhibit exited with status: {}", status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to execute systemd-inhibit: {}", e);
|
|
||||||
eprintln!(
|
|
||||||
"Error: make sure 'systemd-inhibit' is installed and available in your PATH."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn build_args(command_args: &[String], why: &str, who: &str) -> Vec<String> {
|
|
||||||
let mut args = vec![
|
|
||||||
"--why".to_string(),
|
|
||||||
why.to_string(),
|
|
||||||
"--who".to_string(),
|
|
||||||
who.to_string(),
|
|
||||||
"--what".to_string(),
|
|
||||||
"sleep:idle".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
if command_args.is_empty() {
|
|
||||||
args.push("sleep".to_string());
|
|
||||||
args.push("infinity".to_string());
|
|
||||||
} else {
|
|
||||||
args.extend(command_args.iter().cloned());
|
|
||||||
}
|
|
||||||
args
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_build_args_no_command() {
|
|
||||||
let args = build_args(&[], "testing", "lapctl");
|
|
||||||
assert_eq!(
|
|
||||||
args,
|
|
||||||
vec![
|
|
||||||
"--why",
|
|
||||||
"testing",
|
|
||||||
"--who",
|
|
||||||
"lapctl",
|
|
||||||
"--what",
|
|
||||||
"sleep:idle",
|
|
||||||
"sleep",
|
|
||||||
"infinity"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_build_args_with_command() {
|
|
||||||
let command_args = vec!["ls".to_string(), "-l".to_string()];
|
|
||||||
let args = build_args(&command_args, "testing", "lapctl");
|
|
||||||
assert_eq!(
|
|
||||||
args,
|
|
||||||
vec![
|
|
||||||
"--why",
|
|
||||||
"testing",
|
|
||||||
"--who",
|
|
||||||
"lapctl",
|
|
||||||
"--what",
|
|
||||||
"sleep:idle",
|
|
||||||
"ls",
|
|
||||||
"-l"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ pub mod battery;
|
|||||||
pub mod cooling;
|
pub mod cooling;
|
||||||
pub mod display;
|
pub mod display;
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
pub mod inhibit;
|
|
||||||
pub mod install_rules;
|
pub mod install_rules;
|
||||||
pub mod power;
|
pub mod power;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
use crate::cli::GpuCommands;
|
use crate::cli::GpuCommands;
|
||||||
use crate::commands::gpu;
|
use crate::commands::gpu;
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use zbus::interface;
|
use zbus::interface;
|
||||||
use zbus::zvariant::OwnedFd;
|
|
||||||
|
|
||||||
pub struct LapctlInterface {
|
#[derive(Default)]
|
||||||
inhibit_fd: Arc<Mutex<Option<OwnedFd>>>,
|
pub struct LapctlInterface {}
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for LapctlInterface {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
inhibit_fd: Arc::new(Mutex::new(None)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[interface(name = "org.lapctl1")]
|
#[interface(name = "org.lapctl1")]
|
||||||
impl LapctlInterface {
|
impl LapctlInterface {
|
||||||
@@ -118,47 +106,4 @@ impl LapctlInterface {
|
|||||||
crate::commands::touchpad::execute_local(&cmd);
|
crate::commands::touchpad::execute_local(&cmd);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_system_inhibition(
|
|
||||||
&self,
|
|
||||||
active: bool,
|
|
||||||
why: String,
|
|
||||||
who: String,
|
|
||||||
) -> zbus::fdo::Result<()> {
|
|
||||||
let mut fd_lock = self.inhibit_fd.lock().await;
|
|
||||||
|
|
||||||
if active {
|
|
||||||
if fd_lock.is_some() {
|
|
||||||
return Ok(()); // Already inhibiting
|
|
||||||
}
|
|
||||||
|
|
||||||
let conn = zbus::Connection::system().await.map_err(|e| {
|
|
||||||
zbus::fdo::Error::Failed(format!("Failed to connect to system bus: {}", e))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let msg = conn
|
|
||||||
.call_method(
|
|
||||||
Some("org.freedesktop.login1"),
|
|
||||||
"/org/freedesktop/login1",
|
|
||||||
Some("org.freedesktop.login1.Manager"),
|
|
||||||
"Inhibit",
|
|
||||||
&("sleep:idle", who, why, "block"),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| zbus::fdo::Error::Failed(format!("Failed to inhibit: {}", e)))?;
|
|
||||||
|
|
||||||
let fd: OwnedFd = msg
|
|
||||||
.body()
|
|
||||||
.deserialize()
|
|
||||||
.map_err(|e| zbus::fdo::Error::Failed(format!("Failed to parse FD: {}", e)))?;
|
|
||||||
|
|
||||||
*fd_lock = Some(fd);
|
|
||||||
log::info!("System inhibition activated via lapctld.");
|
|
||||||
} else {
|
|
||||||
*fd_lock = None;
|
|
||||||
log::info!("System inhibition deactivated via lapctld.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,6 @@ fn main() {
|
|||||||
Commands::Status => commands::status::execute(),
|
Commands::Status => commands::status::execute(),
|
||||||
Commands::InstallRules => commands::install_rules::execute(),
|
Commands::InstallRules => commands::install_rules::execute(),
|
||||||
Commands::Touchpad { command } => commands::touchpad::execute(command),
|
Commands::Touchpad { command } => commands::touchpad::execute(command),
|
||||||
Commands::Inhibit {
|
|
||||||
command,
|
|
||||||
why,
|
|
||||||
who,
|
|
||||||
daemon,
|
|
||||||
stop,
|
|
||||||
} => commands::inhibit::execute(command, why, who, *daemon, *stop),
|
|
||||||
Commands::Daemon => {
|
Commands::Daemon => {
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
|
|||||||
@@ -148,20 +148,6 @@ pub fn rebuild_initramfs() {
|
|||||||
command = vec!["mkinitcpio", "-P"];
|
command = vec!["mkinitcpio", "-P"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(which) = Command::new("which").arg("systemd-inhibit").output()
|
|
||||||
&& which.status.success()
|
|
||||||
{
|
|
||||||
let mut new_cmd = vec![
|
|
||||||
"systemd-inhibit",
|
|
||||||
"--who=lapctl",
|
|
||||||
"--why",
|
|
||||||
"Rebuilding initramfs",
|
|
||||||
"--",
|
|
||||||
];
|
|
||||||
new_cmd.extend(command.iter());
|
|
||||||
command = new_cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !command.is_empty() {
|
if !command.is_empty() {
|
||||||
println!("Rebuilding the initramfs...");
|
println!("Rebuilding the initramfs...");
|
||||||
let is_debug = log::log_enabled!(log::Level::Debug);
|
let is_debug = log::log_enabled!(log::Level::Debug);
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
use clap::Parser;
|
|
||||||
use lapctl::cli::{Cli, Commands};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inhibit_cli_parsing() {
|
|
||||||
let args = vec!["lapctl", "inhibit", "sleep", "10", "--why", "testing"];
|
|
||||||
let cli = Cli::parse_from(args);
|
|
||||||
|
|
||||||
if let Commands::Inhibit {
|
|
||||||
command,
|
|
||||||
why,
|
|
||||||
who,
|
|
||||||
daemon,
|
|
||||||
stop: _,
|
|
||||||
} = cli.command
|
|
||||||
{
|
|
||||||
assert_eq!(command, vec!["sleep".to_string(), "10".to_string()]);
|
|
||||||
assert_eq!(why, "testing");
|
|
||||||
assert_eq!(who, "lapctl"); // default value
|
|
||||||
assert!(!daemon);
|
|
||||||
} else {
|
|
||||||
panic!("Command should be Inhibit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inhibit_daemon_parsing() {
|
|
||||||
let args = vec!["lapctl", "inhibit", "--daemon"];
|
|
||||||
let cli = Cli::parse_from(args);
|
|
||||||
|
|
||||||
if let Commands::Inhibit {
|
|
||||||
command,
|
|
||||||
why,
|
|
||||||
who: _,
|
|
||||||
daemon,
|
|
||||||
stop: _,
|
|
||||||
} = cli.command
|
|
||||||
{
|
|
||||||
assert!(command.is_empty());
|
|
||||||
assert_eq!(why, "lapctl inhibiting sleep"); // default value
|
|
||||||
assert!(daemon);
|
|
||||||
} else {
|
|
||||||
panic!("Command should be Inhibit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user