Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
94fca06
feat(dbus): start of a new code to split the dbus interface
luytan May 20, 2026
0644e11
feat(daemon): gpu dbus
luytan May 20, 2026
b3c2f2c
refactor(dbus): clean re-implementation
luytan May 21, 2026
f77b830
refactor(gpu-dbus): remove blocked var, get status directly from ebpf
luytan May 21, 2026
e972ac7
feat(mode-dbus): auto apply gpu state on manual mode
luytan May 21, 2026
d45fa49
feat(gpu-dbus): safety check for individual gpu
luytan May 21, 2026
a99be6f
feat(dbus): move mode to root, same for manager
luytan May 21, 2026
6e9d21c
feat(dbus): config API, not finished yet
luytan May 22, 2026
22aa9a1
feat(config-dbus): save config to file
luytan May 23, 2026
e85311c
feat(dbus): Debug interface and pre daemon tasks
luytan May 23, 2026
5fc506a
fix(daemon): use debug gpu_state
luytan May 23, 2026
92ed14c
feat(gpu-dbus): prevent manual block if mode isn't manual
luytan May 23, 2026
4cc3f98
feat(config-dbus): replace Rwlock with atomic for config
luytan May 23, 2026
ebb67b3
feat(daemon): auto battery switch task
luytan May 23, 2026
7bd2cb8
feat(gpu-dbus): lsof function
luytan May 24, 2026
5182363
docs: added comment to some functions
luytan May 24, 2026
02c4feb
feat(gpu-dbus): replace return type of lsof to an Hashmap and add lso…
luytan May 24, 2026
ef63710
feat(daemon): add object manager for gpus
luytan May 24, 2026
751d80a
feat(gpu-dbus): add GetDevice to the gpu api
luytan May 24, 2026
13e1bcc
docs: update the dbus part with the new api
luytan May 24, 2026
0dbc9b1
docs: update titles to be smaller
luytan May 24, 2026
885cc27
feat(cli): implement new api
luytan May 24, 2026
08c95ce
fix(gpu-dbus): handle is_gpu_blocked error
luytan May 24, 2026
358c123
fix(daemon): fix save state to use ebpf block check
luytan May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 63 additions & 4 deletions crates/cardwire-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ pub enum Commands {

#[command(about = "Print the gpu list")]
List {
#[arg(long, help("Print the whole gpu list"), action(ArgAction::SetTrue))]
#[arg(
long,
help("Print the whole pci list"),
action(clap::ArgAction::SetTrue)
)]
full: bool,
#[arg(
long,
help("Print the gpu list in json format"),
action(ArgAction::SetTrue)
action(clap::ArgAction::SetTrue)
)]
json: bool,
},
Expand All @@ -55,12 +59,67 @@ pub enum Commands {
#[command(flatten)]
action: GpuAction,
},

#[command(about = "Manage daemon configuration", arg_required_else_help = true)]
Config {
#[command(subcommand)]
action: ConfigAction,
},

#[command(about = "Manager operations", arg_required_else_help = true)]
Manager {
#[command(subcommand)]
action: ManagerAction,
},

#[command(about = "Debug operations", arg_required_else_help = true)]
Debug {
#[command(subcommand)]
action: DebugAction,
},

#[command(about = "Generate shell completions", hide = true)]
Completion {
#[arg(help = "The shell to generate the completions for")]
shell: Shell,
},
}

#[derive(Subcommand, Debug)]
pub enum ConfigAction {
#[command(about = "Get or set AutoApplyGpuState")]
AutoApplyGpuState {
#[arg(help = "Value to set")]
set: Option<bool>,
},
#[command(about = "Get or set ExperimentalNvidiaBlock")]
ExperimentalNvidiaBlock {
#[arg(help = "Value to set")]
set: Option<bool>,
},
#[command(about = "Get or set BatteryAutoSwitch")]
BatteryAutoSwitch {
#[arg(help = "Value to set")]
set: Option<bool>,
},
#[command(about = "Save current configuration to file")]
Save,
}

#[derive(Subcommand, Debug)]
pub enum ManagerAction {
#[command(about = "Check if daemon is alive")]
Status,
#[command(about = "Refresh GPU list in daemon")]
RefreshGpu,
}

#[derive(Subcommand, Debug)]
pub enum DebugAction {
#[command(about = "Run GPU diagnostics")]
DiagnosticGpu,
}

#[derive(ClapArgs, Debug)]
#[group(required = true, multiple = false)]
pub struct GpuAction {
Expand All @@ -70,6 +129,6 @@ pub struct GpuAction {
#[arg(long, help = "Unblock a specific gpu")]
pub unblock: bool,

#[arg(long, help = "Get gpu status")]
pub status: bool,
#[arg(long, help = "List open files on the GPU")]
pub lsof: bool,
}
217 changes: 204 additions & 13 deletions crates/cardwire-cli/src/dbus.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
use crate::display::{GpuDevice, PciDevice};
use std::collections::BTreeMap;

use zbus::{Proxy, connection::Connection};

#[derive(serde::Deserialize, serde::Serialize, zbus::zvariant::Type, Debug)]
pub struct DbusGpuDevice {
pub name: String,
pub pci: String,
pub render: u32,
pub card: u32,
pub default: bool,
pub nvidia: bool,
pub nvidia_minor: String,
}

pub struct DaemonClient<'a> {
proxy: Proxy<'a>,
}
Expand All @@ -19,25 +28,207 @@ impl<'a> DaemonClient<'a> {
Ok(Self { proxy })
}

pub async fn get_managed_objects(
&self,
) -> zbus::fdo::Result<
std::collections::HashMap<
zbus::zvariant::OwnedObjectPath,
std::collections::HashMap<
zbus::names::OwnedInterfaceName,
std::collections::HashMap<String, zbus::zvariant::OwnedValue>,
>,
>,
> {
let proxy = zbus::fdo::ObjectManagerProxy::builder(self.proxy.connection())
.destination("com.github.opengamingcollective.cardwire")?
.path("/com/github/opengamingcollective/cardwire")?
.build()
.await?;
proxy.get_managed_objects().await
}

pub async fn get_device(&self, id: u32) -> zbus::Result<DbusGpuDevice> {
let path = format!("/com/github/opengamingcollective/cardwire/Gpu/{}", id);
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
path.as_str(),
"com.github.opengamingcollective.cardwire.Gpu",
)
.await?;
proxy.call("GetDevice", &()).await
}

pub async fn set_mode(&self, mode: &u32) -> zbus::fdo::Result<()> {
self.proxy.set_property("Mode", mode).await
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Mode",
)
.await
.map_err(|e| zbus::fdo::Error::Failed(format!("Failed to create Mode proxy: {}", e)))?;
proxy.set_property("Mode", mode).await
}

pub async fn get_mode(&self) -> zbus::Result<u32> {
self.proxy.get_property("Mode").await
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Mode",
)
.await?;
proxy.get_property("Mode").await
}

pub async fn set_gpu_block(&self, id: u32, blocked: bool) -> zbus::fdo::Result<()> {
let path = format!("/com/github/opengamingcollective/cardwire/Gpu/{}", id);
let block_proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
path.as_str(),
"com.github.opengamingcollective.cardwire.Gpu",
)
.await
.map_err(|e| zbus::fdo::Error::Failed(format!("Failed to create proxy: {}", e)))?;
block_proxy.set_property("Block", &(blocked)).await
}

pub async fn get_status(&self, id: u32) -> zbus::Result<bool> {
let path = format!("/com/github/opengamingcollective/cardwire/Gpu/{}", id);
let block_proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
path.as_str(),
"com.github.opengamingcollective.cardwire.Gpu",
)
.await?;
block_proxy.get_property("Block").await
}

pub async fn lsof(
&self,
id: u32,
) -> zbus::Result<std::collections::HashMap<String, Vec<String>>> {
let path = format!("/com/github/opengamingcollective/cardwire/Gpu/{}", id);
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
path.as_str(),
"com.github.opengamingcollective.cardwire.Gpu",
)
.await?;
proxy.call("Lsof", &()).await
}

pub async fn get_auto_apply_gpu_state(&self) -> zbus::Result<bool> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Config",
)
.await?;
proxy.get_property("AutoApplyGpuState").await
}
pub async fn set_auto_apply_gpu_state(&self, state: bool) -> zbus::fdo::Result<()> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Config",
)
.await
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))?;
proxy.set_property("AutoApplyGpuState", state).await
}

pub async fn list_devices(&self) -> zbus::Result<BTreeMap<usize, GpuDevice>> {
self.proxy.call("ListDevices", &()).await
pub async fn get_experimental_nvidia_block(&self) -> zbus::Result<bool> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Config",
)
.await?;
proxy.get_property("ExperimentalNvidiaBlock").await
}
pub async fn list_devices_pci(&self) -> zbus::Result<BTreeMap<String, PciDevice>> {
self.proxy.call("ListDevicesPci", &()).await
pub async fn set_experimental_nvidia_block(&self, state: bool) -> zbus::fdo::Result<()> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Config",
)
.await
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))?;
proxy.set_property("ExperimentalNvidiaBlock", state).await
}

pub async fn set_gpu_block(&self, id: u32, blocked: bool) -> zbus::Result<()> {
self.proxy.call("SetGpuBlock", &(id, blocked)).await
pub async fn get_battery_auto_switch(&self) -> zbus::Result<bool> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Config",
)
.await?;
proxy.get_property("BatteryAutoSwitch").await
}
pub async fn set_battery_auto_switch(&self, state: bool) -> zbus::fdo::Result<()> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Config",
)
.await
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))?;
proxy.set_property("BatteryAutoSwitch", state).await
}
pub async fn get_status(&self, id: u32) -> zbus::Result<String> {
self.proxy.call("GetStatus", &id).await

pub async fn save_to_file(&self) -> zbus::Result<()> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Config",
)
.await?;
proxy.call("SaveToFile", &()).await
}

pub async fn refresh_gpu(&self) -> zbus::Result<()> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Manager",
)
.await?;
proxy.call("RefreshGpu", &()).await
}

pub async fn manager_status(&self) -> zbus::Result<()> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Manager",
)
.await?;
proxy.call("Status", &()).await
}

pub async fn diagnostic_gpu(&self) -> zbus::Result<()> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"com.github.opengamingcollective.cardwire",
"/com/github/opengamingcollective/cardwire",
"com.github.opengamingcollective.cardwire.Debug",
)
.await?;
proxy.call("DiagnosticGpu", &()).await
}
}
22 changes: 11 additions & 11 deletions crates/cardwire-cli/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ use anyhow::{Ok, Result};
// Here the struct are used to parse the json
#[derive(serde::Deserialize, serde::Serialize, zbus::zvariant::Type, Debug)]
pub struct GpuDevice {
id: u32,
name: String,
pci: String,
render: u32,
card: u32,
default: bool,
blocked: bool,
nvidia: bool,
nvidia_minor: String,
pub id: u32,
pub name: String,
pub pci: String,
pub render: u32,
pub card: u32,
pub default: bool,
pub blocked: bool,
pub nvidia: bool,
pub nvidia_minor: String,
}
#[derive(serde::Deserialize, serde::Serialize, zbus::zvariant::Type)]
pub struct PciDevice {
Expand Down Expand Up @@ -99,12 +99,12 @@ fn pretty_print_gpu(gpu_list: BTreeMap<usize, GpuDevice>) {
"-".repeat(default_w),
"-".repeat(blocked_w),
);
for (_, gpu) in gpu_list {
for (id, gpu) in gpu_list {
let render_full = format!("renderD{}", gpu.render);
let card_full = format!("card{}", gpu.card);
println!(
"{:<id_w$} {:<name_w$} {:<pci_w$} {:<render_w$} {:<card_w$} {:<default_w$} {:<blocked_w$}",
gpu.id,
id,
gpu.name,
gpu.pci,
render_full,
Expand Down
Loading