Skip to content
Open
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
2 changes: 2 additions & 0 deletions crates/cardwire-gui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ impl AppState {
}
}
Message::TrayShutdownComplete => return iced::exit(),
Message::ShowWindow => return self.open_or_focus_window(),
Message::WindowClosed(id) => {
if self.window_id == Some(id) {
self.window_id = None;
Expand Down Expand Up @@ -536,6 +537,7 @@ impl AppState {
Subscription::batch([
crate::subscription::dbus_sub(),
crate::subscription::tray_sub(),
crate::subscription::show_window_sub(),
window::close_events().map(Message::WindowClosed),
])
}
Expand Down
13 changes: 13 additions & 0 deletions crates/cardwire-gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,32 @@ mod gui_config;
mod helpers;
mod message;
mod models;
mod single_instance;
mod subscription;
mod tray;
mod ui;

use app::AppState;
use env_logger::Env;
use log::info;

fn main() -> iced::Result {
env_logger::Builder::from_env(Env::default().default_filter_or("info"))
.format_target(false)
.format_timestamp(None)
.init();

// Keep alive for the process's lifetime: dropping it releases the D-Bus name and a
// subsequent launch would no longer detect this instance as running.
let _single_instance_guard = match single_instance::acquire() {
single_instance::Acquisition::Acquired(connection) => Some(connection),
single_instance::Acquisition::AlreadyRunning => {
info!("cardwire-gui is already running; exiting");
return Ok(());
}
single_instance::Acquisition::Unchecked => None,
};

unsafe {
// Vulkan wakes the dGPU
std::env::set_var("WGPU_BACKEND", "gl");
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 @@ -20,6 +20,7 @@ pub enum Message {
TrayAction(TrayAction),
TrayUnavailable(String),
TrayShutdownComplete,
ShowWindow,
WindowClosed(iced::window::Id),
UpdateGpuPowerState(usize, String),
UpdateBlockState(usize, bool),
Expand Down
43 changes: 43 additions & 0 deletions crates/cardwire-gui/src/single_instance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use log::warn;
use zbus::{Error, blocking::Connection, fdo::RequestNameFlags};

pub(crate) const BUS_NAME: &str = "org.opengamingcollective.cardwire.Gui";
pub(crate) const OBJECT_PATH: &str = "/org/opengamingcollective/cardwire/Gui";
/// Signal a later launch broadcasts to ask the running instance to raise its window.
pub(crate) const SHOW_SIGNAL: &str = "Show";

pub enum Acquisition {
Acquired(Connection),
AlreadyRunning,
/// Could not be verified (e.g. no session bus available). Callers should fail open rather
/// than block startup on an inconclusive check.
Unchecked,
}

pub fn acquire() -> Acquisition {
let connection = match Connection::session() {
Ok(connection) => connection,
Err(error) => {
warn!(
"could not connect to session bus to check for another running instance: {error}"
);
return Acquisition::Unchecked;
}
};

match connection.request_name_with_flags(BUS_NAME, RequestNameFlags::DoNotQueue.into()) {
Ok(_) => Acquisition::Acquired(connection),
Err(Error::NameTaken) => {
if let Err(error) =
connection.emit_signal(None::<&str>, OBJECT_PATH, BUS_NAME, SHOW_SIGNAL, &())
{
warn!("could not ask the running instance to show its window: {error}");
}
Acquisition::AlreadyRunning
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Err(error) => {
warn!("could not request D-Bus name to check for another running instance: {error}");
Acquisition::Unchecked
}
}
}
50 changes: 49 additions & 1 deletion crates/cardwire-gui/src/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
helpers::CardwireDbus, message::Message, models::{DaemonSettings, LogEntry, Mode, PciDevice}, tray
};
use zbus::{
Connection, Proxy, names::OwnedInterfaceName, proxy, zvariant::{OwnedObjectPath, OwnedValue}
Connection, MatchRule, MessageStream, Proxy, message::Type as MessageType, names::OwnedInterfaceName, proxy, zvariant::{OwnedObjectPath, OwnedValue}
};

pub fn tray_sub() -> Subscription<Message> {
Expand Down Expand Up @@ -53,6 +53,54 @@ pub fn tray_sub() -> Subscription<Message> {
})
}

pub fn show_window_sub() -> Subscription<Message> {
Subscription::run_with("cardwire_show_window_subscription", |_id| {
stream::channel(1, |mut output: Sender<Message>| async move {
let connection = match Connection::session().await {
Ok(conn) => conn,
Err(error) => {
warn!(
"Failed to connect to D-Bus for show-window requests: {}",
error
);
return;
}
};

let rule = match show_window_match_rule() {
Ok(rule) => rule,
Err(error) => {
warn!("Failed to build show-window match rule: {}", error);
return;
}
};

let mut signals = match MessageStream::for_match_rule(rule, &connection, None).await {
Ok(stream) => stream,
Err(error) => {
warn!("Failed to listen for show-window requests: {}", error);
return;
}
};

while signals.next().await.is_some() {
if output.send(Message::ShowWindow).await.is_err() {
return;
}
}
})
})
}

fn show_window_match_rule() -> zbus::Result<MatchRule<'static>> {
Ok(MatchRule::builder()
.msg_type(MessageType::Signal)
.path(crate::single_instance::OBJECT_PATH)?
.interface(crate::single_instance::BUS_NAME)?
.member(crate::single_instance::SHOW_SIGNAL)?
.build())
}

// CardwireMode is used to listen to mode change signals

#[proxy(
Expand Down
Loading