refactor(linux): remove zbus and use unity launcher APIs directly (#880)

* refactor(linux): remove zbus and use unity launcher APIs directly

* change files

* fmt

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Amr Bashir
2024-02-21 14:53:31 +02:00
committed by GitHub
parent 3424f6340a
commit 2af91313b2
8 changed files with 137 additions and 70 deletions

5
.changes/bump-msrv.md Normal file
View File

@@ -0,0 +1,5 @@
---
"tao": minor
---
Updated the minimum supported Rust version to 1.70.

View File

@@ -0,0 +1,5 @@
---
"tao": minor
---
Progress bar on Linux no longer relies on zbus. Changed `ProgressBarState`'s field `unity_uri` to `desktop_filename`.

View File

@@ -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"

View File

@@ -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,
});
}
}

View File

@@ -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)| {
@@ -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!(),
}

View File

@@ -1,56 +1,138 @@
use crate::window::{ProgressBarState, ProgressState};
use zbus::{
blocking::Connection,
fdo::Result,
zvariant::{DeserializeDict, SerializeDict, Type},
Message,
};
use std::ffi::CString;
pub struct TaskbarIndicator {
conn: Connection,
app_uri: String,
use dlopen2::wrapper::{Container, WrapperApi};
use crate::window::{ProgressBarState, ProgressState};
#[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();
if let Some(uri) = progress.unity_uri {
self.app_uri = uri;
fn ensure_lib_load(&mut self) {
if self.attempted_load {
return;
}
if let Some(progress) = progress.progress {
let progress = if progress > 100 { 100 } else { progress };
self.attempted_load = true;
properties.progress = Some(progress as f64 / 100.0);
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(unity_lib) = &self.unity_lib {
let handle = unsafe { unity_lib.unity_inspector_get_default() };
if !handle.is_null() {
self.unity_inspector = Some(handle);
}
}
}
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);
}
}
}
}
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);
}
let signal = Message::signal("/", "com.canonical.Unity.LauncherEntry", "Update")?
.build(&(self.app_uri.clone(), properties))?;
if !self.is_supported {
return;
}
self.conn.send(&signal)?;
Ok(())
self.ensure_lib_load();
if !self.is_unity_running() {
return;
}
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) };
}
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)
}

View File

@@ -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)
}

View File

@@ -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.