diff --git a/.changes/bump-msrv.md b/.changes/bump-msrv.md new file mode 100644 index 000000000..d5bda3db2 --- /dev/null +++ b/.changes/bump-msrv.md @@ -0,0 +1,5 @@ +--- +"tao": minor +--- + +Updated the minimum supported Rust version to 1.70. diff --git a/.changes/progress-bar-refactor.md b/.changes/progress-bar-refactor.md new file mode 100644 index 000000000..8e4596bf3 --- /dev/null +++ b/.changes/progress-bar-refactor.md @@ -0,0 +1,5 @@ +--- +"tao": minor +--- + +Progress bar on Linux no longer relies on zbus. Changed `ProgressBarState`'s field `unity_uri` to `desktop_filename`. diff --git a/Cargo.toml b/Cargo.toml index 14e3825df..bfd54674c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ authors = [ "The winit contributors" ] edition = "2021" -rust-version = "1.56" +rust-version = "1.70" keywords = [ "windowing" ] license = "Apache-2.0" readme = "README.md" @@ -32,9 +32,6 @@ default = [ "rwh_06" ] [workspace] members = [ "tao-macros" ] -[build-dependencies] -cc = "1" - [dependencies] instant = "0.1" lazy_static = "1" @@ -120,6 +117,6 @@ gtk = "0.18" gdkx11-sys = "0.18" gdkwayland-sys = "0.18.0" x11-dl = "2.21" -zbus = "4" png = "0.17" parking_lot = "0.12" +dlopen2 = "0.7.0" diff --git a/examples/progress_bar.rs b/examples/progress_bar.rs index f88a825e3..c526a74cc 100644 --- a/examples/progress_bar.rs +++ b/examples/progress_bar.rs @@ -61,7 +61,7 @@ fn main() { window.set_progress_bar(ProgressBarState { progress: Some(progress), state: Some(ProgressState::Normal), - unity_uri: None, + desktop_filename: None, }); } else if modifiers.control_key() { let mut state = ProgressState::None; @@ -77,7 +77,7 @@ fn main() { window.set_progress_bar(ProgressBarState { progress: None, state: Some(state), - unity_uri: None, + desktop_filename: None, }); } } diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index 25cf14d41..8cf68e622 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -245,8 +245,7 @@ impl EventLoop { None }; - let mut taskbar: Option = None; - let supports_unity = util::is_unity(); + let mut taskbar = TaskbarIndicator::new(); // Window Request window_requests_rx.attach(Some(&context), move |(id, request)| { @@ -853,19 +852,7 @@ impl EventLoop { } else if id == WindowId::dummy() { match request { WindowRequest::ProgressBarState(state) => { - if supports_unity { - if taskbar.is_none() { - if let Ok(indicator) = TaskbarIndicator::new() { - taskbar.replace(indicator); - } - } - - if let Some(taskbar) = &mut taskbar { - if let Err(e) = taskbar.update(state) { - log::warn!("Failed to update taskbar progress {}", e); - } - } - } + taskbar.update(state); } _ => unreachable!(), } diff --git a/src/platform_impl/linux/taskbar.rs b/src/platform_impl/linux/taskbar.rs index c086fe920..d4f4778ac 100644 --- a/src/platform_impl/linux/taskbar.rs +++ b/src/platform_impl/linux/taskbar.rs @@ -1,56 +1,138 @@ +use std::ffi::CString; + +use dlopen2::wrapper::{Container, WrapperApi}; + use crate::window::{ProgressBarState, ProgressState}; -use zbus::{ - blocking::Connection, - fdo::Result, - zvariant::{DeserializeDict, SerializeDict, Type}, - Message, -}; -pub struct TaskbarIndicator { - conn: Connection, - app_uri: String, +#[derive(WrapperApi)] +struct UnityLib { + unity_launcher_entry_get_for_desktop_id: unsafe extern "C" fn(id: *const i8) -> *const isize, + unity_inspector_get_default: unsafe extern "C" fn() -> *const isize, + unity_inspector_get_unity_running: unsafe extern "C" fn(inspector: *const isize) -> i32, + unity_launcher_entry_set_progress: unsafe extern "C" fn(entry: *const isize, value: f64) -> i32, + unity_launcher_entry_set_progress_visible: + unsafe extern "C" fn(entry: *const isize, value: i32) -> i32, } -#[derive(Default, SerializeDict, DeserializeDict, Type, PartialEq, Debug)] -#[zvariant(signature = "dict")] -struct Progress { - progress: Option, - #[zvariant(rename = "progress-visible")] - progress_visible: Option, - urgent: Option, +pub struct TaskbarIndicator { + desktop_filename: Option, + desktop_filename_c_str: Option, + + is_supported: bool, + unity_lib: Option>, + attempted_load: bool, + + unity_inspector: Option<*const isize>, + unity_entry: Option<*const isize>, } impl TaskbarIndicator { - pub fn new() -> Result { - let conn = Connection::session()?; + pub fn new() -> Self { + Self { + desktop_filename: None, + desktop_filename_c_str: None, - Ok(Self { - conn, - app_uri: String::new(), - }) + is_supported: is_supported(), + unity_lib: None, + attempted_load: false, + + unity_inspector: None, + unity_entry: None, + } } - pub fn update(&mut self, progress: ProgressBarState) -> Result<()> { - let mut properties = Progress::default(); + fn ensure_lib_load(&mut self) { + if self.attempted_load { + return; + } + + self.attempted_load = true; + + self.unity_lib = unsafe { + Container::load("libunity.so.4") + .or_else(|_| Container::load("libunity.so.6")) + .or_else(|_| Container::load("libunity.so.9")) + .ok() + }; - if let Some(uri) = progress.unity_uri { - self.app_uri = uri; + if let Some(unity_lib) = &self.unity_lib { + let handle = unsafe { unity_lib.unity_inspector_get_default() }; + if !handle.is_null() { + self.unity_inspector = Some(handle); + } } + } - if let Some(progress) = progress.progress { - let progress = if progress > 100 { 100 } else { progress }; + fn ensure_entry_load(&mut self) { + if let Some(unity_lib) = &self.unity_lib { + if let Some(id) = &self.desktop_filename_c_str { + let handle = unsafe { unity_lib.unity_launcher_entry_get_for_desktop_id(id.as_ptr()) }; + if !handle.is_null() { + self.unity_entry = Some(handle); + } + } + } + } - properties.progress = Some(progress as f64 / 100.0); + fn is_unity_running(&self) -> bool { + if let Some(inspector) = self.unity_inspector { + if let Some(unity_lib) = &self.unity_lib { + return unsafe { unity_lib.unity_inspector_get_unity_running(inspector) } == 1; + } } - if let Some(state) = progress.state { - properties.progress_visible = Some(!matches!(state, ProgressState::None)); + false + } + + pub fn update(&mut self, progress: ProgressBarState) { + if let Some(uri) = progress.desktop_filename { + self.desktop_filename = Some(uri); + } + + if !self.is_supported { + return; + } + + self.ensure_lib_load(); + + if !self.is_unity_running() { + return; } - let signal = Message::signal("/", "com.canonical.Unity.LauncherEntry", "Update")? - .build(&(self.app_uri.clone(), properties))?; + if let Some(uri) = &self.desktop_filename { + self.desktop_filename_c_str = Some(CString::new(uri.as_str()).unwrap_or_default()); + } + + if self.unity_entry.is_none() { + self.ensure_entry_load(); + } + if let Some(unity_lib) = &self.unity_lib { + if let Some(unity_entry) = &self.unity_entry { + if let Some(progress) = progress.progress { + let progress = if progress > 100 { 100 } else { progress }; + let progress = progress as f64 / 100.0; + unsafe { (unity_lib.unity_launcher_entry_set_progress)(*unity_entry, progress) }; + } - self.conn.send(&signal)?; - Ok(()) + if let Some(state) = progress.state { + let is_visible = !matches!(state, ProgressState::None); + unsafe { + (unity_lib.unity_launcher_entry_set_progress_visible)( + *unity_entry, + if is_visible { 1 } else { 0 }, + ) + }; + } + } + } } } + +pub fn is_supported() -> bool { + std::env::var("XDG_CURRENT_DESKTOP") + .map(|d| { + let d = d.to_lowercase(); + d.contains("unity") || d.contains("gnome") + }) + .unwrap_or(false) +} diff --git a/src/platform_impl/linux/util.rs b/src/platform_impl/linux/util.rs index 93ccd4e96..5040a5092 100644 --- a/src/platform_impl/linux/util.rs +++ b/src/platform_impl/linux/util.rs @@ -70,12 +70,3 @@ pub fn set_size_constraints( geom_mask, ) } - -pub fn is_unity() -> bool { - std::env::var("XDG_CURRENT_DESKTOP") - .map(|d| { - let d = d.to_lowercase(); - d.contains("unity") || d.contains("gnome") - }) - .unwrap_or(false) -} diff --git a/src/window.rs b/src/window.rs index 95fae97ce..094fa3994 100644 --- a/src/window.rs +++ b/src/window.rs @@ -33,8 +33,8 @@ pub struct ProgressBarState { pub state: Option, /// The progress bar progress. This can be a value ranging from `0` to `100` pub progress: Option, - /// The identifier for your app to communicate with the Unity desktop window manager **Linux Only** - pub unity_uri: Option, + /// The `.desktop` filename with the Unity desktop window manager, for example `myapp.desktop` **Linux Only** + pub desktop_filename: Option, } /// Represents a window.