Skip to content

Commit

Permalink
refactor(linux): remove zbus and use unity launcher APIs directly (#880)
Browse files Browse the repository at this point in the history
* refactor(linux): remove zbus and use unity launcher APIs directly

* change files

* fmt

---------

Co-authored-by: Lucas Nogueira <[email protected]>
  • Loading branch information
amrbashir and lucasfernog authored Feb 21, 2024
1 parent 3424f63 commit 2af9131
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .changes/bump-msrv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": minor
---

Updated the minimum supported Rust version to 1.70.
5 changes: 5 additions & 0 deletions .changes/progress-bar-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": minor
---

Progress bar on Linux no longer relies on zbus. Changed `ProgressBarState`'s field `unity_uri` to `desktop_filename`.
7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -32,9 +32,6 @@ default = [ "rwh_06" ]
[workspace]
members = [ "tao-macros" ]

[build-dependencies]
cc = "1"

[dependencies]
instant = "0.1"
lazy_static = "1"
Expand Down Expand Up @@ -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"
4 changes: 2 additions & 2 deletions examples/progress_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -77,7 +77,7 @@ fn main() {
window.set_progress_bar(ProgressBarState {
progress: None,
state: Some(state),
unity_uri: None,
desktop_filename: None,
});
}
}
Expand Down
17 changes: 2 additions & 15 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,7 @@ impl<T: 'static> EventLoop<T> {
None
};

let mut taskbar: Option<TaskbarIndicator> = None;
let supports_unity = util::is_unity();
let mut taskbar = TaskbarIndicator::new();

// Window Request
window_requests_rx.attach(Some(&context), move |(id, request)| {
Expand Down Expand Up @@ -853,19 +852,7 @@ impl<T: 'static> EventLoop<T> {
} 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!(),
}
Expand Down
152 changes: 117 additions & 35 deletions src/platform_impl/linux/taskbar.rs
Original file line number Diff line number Diff line change
@@ -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<f64>,
#[zvariant(rename = "progress-visible")]
progress_visible: Option<bool>,
urgent: Option<bool>,
pub struct TaskbarIndicator {
desktop_filename: Option<String>,
desktop_filename_c_str: Option<CString>,

is_supported: bool,
unity_lib: Option<Container<UnityLib>>,
attempted_load: bool,

unity_inspector: Option<*const isize>,
unity_entry: Option<*const isize>,
}

impl TaskbarIndicator {
pub fn new() -> Result<Self> {
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)
}
9 changes: 0 additions & 9 deletions src/platform_impl/linux/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,3 @@ pub fn set_size_constraints<W: GtkWindowExt + WidgetExt>(
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)
}
4 changes: 2 additions & 2 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub struct ProgressBarState {
pub state: Option<ProgressState>,
/// The progress bar progress. This can be a value ranging from `0` to `100`
pub progress: Option<u64>,
/// The identifier for your app to communicate with the Unity desktop window manager **Linux Only**
pub unity_uri: Option<String>,
/// The `.desktop` filename with the Unity desktop window manager, for example `myapp.desktop` **Linux Only**
pub desktop_filename: Option<String>,
}

/// Represents a window.
Expand Down

0 comments on commit 2af9131

Please sign in to comment.