Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion crates/cardwire-cli/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use clap::{Args as ClapArgs, Parser, Subcommand, ValueEnum};
use clap_complete::Shell;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, ValueEnum)]
#[derive(
Clone, Copy, Debug, PartialEq, Eq, ValueEnum, Serialize, Deserialize, zbus::zvariant::Type,
)]
#[serde(rename_all = "snake_case")]
pub enum CliMode {
Integrated,
Hybrid,
Expand Down
11 changes: 11 additions & 0 deletions crates/cardwire-cli/src/dbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@ impl<'a> DaemonClient<'a> {
proxy.get_property("Mode").await
}

pub async fn get_available_modes(&self) -> zbus::Result<Vec<crate::args::CliMode>> {
let proxy = zbus::Proxy::new(
self.proxy.connection(),
"org.opengamingcollective.cardwire",
"/org/opengamingcollective/cardwire",
"org.opengamingcollective.cardwire.Mode",
)
.await?;
proxy.call("AvailableModes", &()).await
}

pub async fn set_gpu_block(&self, id: u32, blocked: bool) -> zbus::fdo::Result<()> {
let path = format!("/org/opengamingcollective/cardwire/Gpu/{}", id);
let block_proxy = zbus::Proxy::new(
Expand Down
51 changes: 46 additions & 5 deletions crates/cardwire-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,25 @@ async fn main() -> anyhow::Result<()> {
}

// Connect to D-Bus
let connection: zbus::Connection = zbus::connection::Builder::system()?.build().await?;
let client: DaemonClient<'_> = DaemonClient::connect(&connection).await?;
let connection: zbus::Connection = match zbus::connection::Builder::system() {
Ok(builder) => match builder.build().await {
Ok(conn) => conn,
Err(e) => handle_error(e),
},
Err(e) => handle_error(e),
};
let client: DaemonClient<'_> = match DaemonClient::connect(&connection).await {
Ok(client) => client,
Err(e) => handle_error(e),
};

// Verify the daemon is reachable via Cardwire Manager status before processing args
if let Err(e) = client.manager_status().await {
return Err(anyhow::anyhow!(
"Cannot communicate with cardwired, is the daemon running? - {}",
e
));
}

match args.command {
Commands::Set { mode } => {
Expand All @@ -55,6 +72,15 @@ async fn main() -> anyhow::Result<()> {
};
}
Commands::Get => {
let available_modes = match client.get_available_modes().await {
Ok(modes) => modes,
Err(e) => handle_error(e),
};
let available_modes_str = available_modes
.iter()
.map(|m| m.to_string().to_lowercase())
.collect::<Vec<_>>()
.join(", ");
match client.get_mode().await {
Ok(response) => {
let response: CliMode = match response {
Expand All @@ -66,7 +92,7 @@ async fn main() -> anyhow::Result<()> {
_ => CliMode::Manual,
};
println!("Current Mode: {}", response);
println!("Available Mode: integrated, hybrid, manual, smart");
println!("Available Mode: {}", available_modes_str);
}
Err(e) => handle_error(e),
};
Expand Down Expand Up @@ -223,6 +249,17 @@ async fn main() -> anyhow::Result<()> {
handle_error(e);
} else {
println!("GPU list refreshed");
match client.get_available_modes().await {
Ok(modes) => {
let modes_str = modes
.iter()
.map(|m| m.to_string().to_lowercase())
.collect::<Vec<_>>()
.join(", ");
println!("Available Mode: {}", modes_str);
}
Err(e) => handle_error(e),
}
}
}
},
Expand Down Expand Up @@ -309,7 +346,11 @@ async fn main() -> anyhow::Result<()> {
fn handle_error(err: zbus::Error) -> ! {
match err {
zbus::Error::MethodError(name, description, _) => {
if let Some(msg) = description {
if name.as_str() == "org.freedesktop.DBus.Error.ServiceUnknown"
|| name.as_str() == "org.freedesktop.DBus.Error.NameHasNoOwner"
{
eprintln!("error: cardwired daemon is not running. Is the service up?");
} else if let Some(msg) = description {
eprintln!("{}", msg);
} else {
eprintln!("{}", name);
Expand All @@ -320,7 +361,7 @@ fn handle_error(err: zbus::Error) -> ! {
| zbus::fdo::Error::Failed(msg)
| zbus::fdo::Error::InvalidArgs(msg)
| zbus::fdo::Error::NotSupported(msg) => eprintln!("{}", msg),
zbus::fdo::Error::ServiceUnknown(_) => {
zbus::fdo::Error::ServiceUnknown(_) | zbus::fdo::Error::NameHasNoOwner(_) => {
eprintln!("error: cardwired daemon is not running. Is the service up?");
}
_ => eprintln!("{}", fdo_err),
Expand Down
37 changes: 31 additions & 6 deletions crates/cardwire-gui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl AppState {
&message,
Message::AllDevicesFetched(_)
| Message::FetchedMode(_)
| Message::FetchedAvailableModes(_)
| Message::TrayReady(_)
| Message::UpdateGpuPowerState(..)
| Message::UpdateBlockState(..)
Expand Down Expand Up @@ -104,6 +105,15 @@ impl AppState {
self.error = Some(format!("Error fetching Mode: {}", err));
}
},
// Happen when available modes are received from dbus
Message::FetchedAvailableModes(res) => match res {
Ok(modes) => {
self.main_state.available_modes = modes;
}
Err(err) => {
log::warn!("Failed to fetch available modes: {}", err);
}
},
// Send the new mode to dbus
Message::SetMode(mode) => {
let conn = self.zbus_conn.clone();
Expand Down Expand Up @@ -202,6 +212,7 @@ impl AppState {
PrimaryClickAction::SwitchMode => {
return match configured_primary_click_mode(
self.main_state.current_mode,
&self.main_state.available_modes,
&self.setting_state.gui_config,
) {
Ok(mode) => self.update(Message::SetMode(mode)),
Expand Down Expand Up @@ -364,7 +375,14 @@ impl AppState {
);
}
Message::RefreshGpuResult(res) => match res {
Ok(()) => self.info = Some("GPU list refreshed".to_string()),
Ok(()) => {
self.info = Some("GPU list refreshed".to_string());
let conn = self.zbus_conn.clone();
return Task::perform(
async move { conn.get_available_modes().await.map_err(|e| e.to_string()) },
Message::FetchedAvailableModes,
);
}
Err(err) => self.error = Some(format!("Refresh error: {}", err)),
},
// Fetch the initial blocked process logs from dbus
Expand Down Expand Up @@ -475,8 +493,11 @@ impl AppState {
return Task::none();
};
let mode = self.main_state.current_mode;
let available_modes = self.main_state.available_modes.clone();
let gpus = self.gpu_list.clone();
Task::perform(tray::update(handle, mode, gpus), |_| Message::None)
Task::perform(tray::update(handle, mode, available_modes, gpus), |_| {
Message::None
})
}

fn open_or_focus_window(&mut self) -> Task<Message> {
Expand All @@ -495,7 +516,9 @@ impl AppState {
Page::Main => ui::main_page(&self.main_state, &self.gpu_list),
Page::Pci => pci_page(&self.pci_list),
Page::SmartMode => ui::smart_mode_page(&self.smart_state, self.main_state.current_mode),
Page::CardwireSettings => daemon_setting_page(&self.setting_state),
Page::CardwireSettings => {
daemon_setting_page(&self.setting_state, &self.main_state.available_modes)
}
Page::Logs => ui::logs_page(&self.log_state, &self.gpu_list),
Page::Advanced => ui::advanced_page(),
Page::About => ui::about_page(),
Expand Down Expand Up @@ -536,10 +559,11 @@ impl AppState {

fn configured_primary_click_mode(
current: Option<Mode>,
available_modes: &[Mode],
config: &GuiConfig,
) -> Result<Mode, String> {
current
.map(|mode| config.next_primary_click_mode(mode))
.map(|mode| config.next_primary_click_mode(mode, available_modes))
.ok_or_else(|| "Cardwire daemon is unavailable".to_string())
}

Expand All @@ -554,14 +578,15 @@ mod tests {
#[test]
fn primary_click_uses_the_configured_modes() {
assert_eq!(
configured_primary_click_mode(Some(Mode::Integrated), &GuiConfig::default()).unwrap(),
configured_primary_click_mode(Some(Mode::Integrated), &[], &GuiConfig::default())
.unwrap(),
Mode::Hybrid
);
}

#[test]
fn primary_click_rejects_an_offline_daemon() {
assert!(configured_primary_click_mode(None, &GuiConfig::default()).is_err());
assert!(configured_primary_click_mode(None, &[], &GuiConfig::default()).is_err());
}

#[test]
Expand Down
53 changes: 41 additions & 12 deletions crates/cardwire-gui/src/gui_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ static TEMPORARY_SEQUENCE: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, VariantArray)]
#[serde(rename_all = "kebab-case")]
pub enum PrimaryClickAction {
#[default]
SwitchMode,
#[default]
OpenGui,
}

Expand Down Expand Up @@ -42,7 +42,7 @@ impl Default for GuiConfig {
fn default() -> Self {
Self {
start_in_tray: false,
primary_click_action: PrimaryClickAction::SwitchMode,
primary_click_action: PrimaryClickAction::OpenGui,
primary_click_modes: default_primary_click_modes(),
}
}
Expand Down Expand Up @@ -92,8 +92,12 @@ impl GuiConfig {
result
}

pub fn next_primary_click_mode(&self, current: Mode) -> Mode {
let modes = Mode::VARIANTS;
pub fn next_primary_click_mode(&self, current: Mode, available_modes: &[Mode]) -> Mode {
let modes = if available_modes.is_empty() {
Mode::VARIANTS
} else {
available_modes
};
let current_index = modes.iter().position(|&mode| mode == current);
current_index
.into_iter()
Expand All @@ -106,7 +110,7 @@ impl GuiConfig {
.copied()
.find(|mode| self.primary_click_modes.contains(mode))
})
.expect("validated GUI config must contain a primary-click mode")
.unwrap_or_else(|| modes[0])
}

pub fn with_primary_click_mode(mut self, mode: Mode, enabled: bool) -> Self {
Expand Down Expand Up @@ -174,7 +178,7 @@ mod tests {
);
assert_eq!(
GuiConfig::default().primary_click_action,
PrimaryClickAction::SwitchMode
PrimaryClickAction::OpenGui
);
assert!(!GuiConfig::default().start_in_tray);
}
Expand Down Expand Up @@ -226,15 +230,21 @@ mod tests {
..GuiConfig::default()
};
assert_eq!(
config.next_primary_click_mode(Mode::Integrated),
config.next_primary_click_mode(Mode::Integrated, &[]),
Mode::Manual
);
assert_eq!(config.next_primary_click_mode(Mode::Manual), Mode::Smart);
assert_eq!(
config.next_primary_click_mode(Mode::Smart),
config.next_primary_click_mode(Mode::Manual, &[]),
Mode::Smart
);
assert_eq!(
config.next_primary_click_mode(Mode::Smart, &[]),
Mode::Integrated
);
assert_eq!(config.next_primary_click_mode(Mode::Hybrid), Mode::Manual);
assert_eq!(
config.next_primary_click_mode(Mode::Hybrid, &[]),
Mode::Manual
);
}

#[test]
Expand All @@ -243,8 +253,27 @@ mod tests {
primary_click_modes: vec![Mode::Smart],
..GuiConfig::default()
};
assert_eq!(config.next_primary_click_mode(Mode::Hybrid), Mode::Smart);
assert_eq!(config.next_primary_click_mode(Mode::Smart), Mode::Smart);
assert_eq!(
config.next_primary_click_mode(Mode::Hybrid, &[]),
Mode::Smart
);
assert_eq!(
config.next_primary_click_mode(Mode::Smart, &[]),
Mode::Smart
);
}

#[test]
fn skips_unavailable_configured_modes() {
let config = GuiConfig {
primary_click_modes: vec![Mode::Integrated, Mode::Hybrid, Mode::Smart],
..GuiConfig::default()
};
let available = vec![Mode::Hybrid, Mode::Manual];
assert_eq!(
config.next_primary_click_mode(Mode::Hybrid, &available),
Mode::Hybrid
);
}

#[test]
Expand Down
11 changes: 11 additions & 0 deletions crates/cardwire-gui/src/helpers/dbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ impl CardwireDbus {
.await?;
proxy.get_property("Mode").await
}
pub async fn get_available_modes(&self) -> zbus::Result<Vec<Mode>> {
let connection = Connection::system().await?;
let proxy = zbus::Proxy::new(
&connection,
"org.opengamingcollective.cardwire",
"/org/opengamingcollective/cardwire",
"org.opengamingcollective.cardwire.Mode",
)
.await?;
proxy.call("AvailableModes", &()).await
}
pub async fn set_mode(&self, mode: u32) -> zbus::fdo::Result<()> {
let connection = Connection::system().await?;
let proxy = zbus::Proxy::new(
Expand Down
1 change: 1 addition & 0 deletions crates/cardwire-gui/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::collections::{BTreeMap, VecDeque};
pub enum Message {
SwitchPage(Page),
FetchedMode(Result<Mode, String>),
FetchedAvailableModes(Result<Vec<Mode>, String>),
SetMode(Mode),
AllDevicesFetched(Result<BTreeMap<usize, GpuDevice>, String>),
UpdateNvidiaSetting(bool),
Expand Down
16 changes: 14 additions & 2 deletions crates/cardwire-gui/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use strum::{EnumIter, FromRepr, VariantArray};
serde::Deserialize,
)]
#[repr(u32)]
#[serde(rename_all = "kebab-case")]
#[serde(rename_all = "snake_case")]
pub enum Mode {
Integrated = 0,
Hybrid = 1,
Expand Down Expand Up @@ -69,13 +69,25 @@ impl Display for Page {
}
}

#[derive(Default, Clone, Debug)]
#[derive(Clone, Debug)]
pub struct MainState {
pub current_mode: Option<Mode>,
pub available_modes: Vec<Mode>,
pub open_gpu_menu: Option<usize>,
pub lsof_window: Option<LsofData>,
}

impl Default for MainState {
fn default() -> Self {
Self {
current_mode: None,
available_modes: Mode::VARIANTS.to_vec(),
open_gpu_menu: None,
lsof_window: None,
}
}
}

#[derive(serde::Deserialize, zbus::zvariant::Type, Debug, Clone)]
pub struct LogEntry {
pub timestamp: SystemTime,
Expand Down
Loading