From 42dcc7d61fc5e278b4ed76bb9720ba4d89266f01 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Tue, 13 Aug 2024 00:52:34 +0200 Subject: [PATCH] Add support for `set_parent` in XDG portals (#209) * Make set_parent require HasWindowHandle + HasDisplayHandle HasDisplayHandle is required for Wayland to work. * Add support for set_parent in XDG portals * Add libwayland-dev as a dependency in CI for Ubuntu XDG --- .github/workflows/rust.yml | 5 +- CHANGELOG.md | 2 + Cargo.lock | 104 ++++++++++++++++++++++++++++++ Cargo.toml | 2 +- src/backend/xdg_desktop_portal.rs | 20 +++++- src/file_dialog.rs | 23 ++++--- src/message_dialog.rs | 23 ++++--- 7 files changed, 159 insertions(+), 20 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6169d52..204fea2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -41,9 +41,12 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - - name: "[Ubuntu] install dependencies" + - name: "[Ubuntu GTK] install dependencies" if: matrix.name == 'Ubuntu GTK' run: sudo apt update && sudo apt install libgtk-3-dev + - name: "[Ubuntu XDG] install dependencies" + if: matrix.name == 'Ubuntu XDG' + run: sudo apt update && sudo apt install libwayland-dev - name: "[WASM] rustup" if: matrix.name == 'WASM32' run: rustup target add wasm32-unknown-unknown diff --git a/CHANGELOG.md b/CHANGELOG.md index ca4a950..0b9a65f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Fix `AsyncFileDialog` blocking the executor on Windows (#191) - Add `TDF_SIZE_TO_CONTENT` to `TaskDialogIndirect` config so that it can display longer text without truncating/wrapping (80 characters instead of 55) (#202) - Fix `xdg-portal` backend not accepting special characters in message dialogs +- Make `set_parent` require `HasWindowHandle + HasDisplayHandle` +- Add support for `set_parent` in XDG Portals ## 0.14.0 - i18n for GTK and XDG Portal diff --git a/Cargo.lock b/Cargo.lock index feba393..24d70e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,10 +38,14 @@ dependencies = [ "futures-channel", "futures-util", "rand", + "raw-window-handle", "serde", "serde_repr", "tokio", "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", "zbus", ] @@ -399,6 +403,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "endi" version = "1.1.0" @@ -769,6 +788,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.48.5", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1007,6 +1036,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.35" @@ -1124,6 +1162,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "serde" version = "1.0.197" @@ -1516,6 +1560,66 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wayland-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943" +dependencies = [ + "bitflags", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.69" diff --git a/Cargo.toml b/Cargo.toml index c9f646f..c7cb044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ windows-sys = { version = "0.48", features = [ [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] # XDG Desktop Portal -ashpd = { version = "0.8", optional = true, default-features = false } +ashpd = { version = "0.8", optional = true, default-features = false, features = ["raw_handle"] } urlencoding = { version = "2.1.0", optional = true } pollster = { version = "0.3", optional = true } # GTK diff --git a/src/backend/xdg_desktop_portal.rs b/src/backend/xdg_desktop_portal.rs index 790cbc6..4d6b358 100644 --- a/src/backend/xdg_desktop_portal.rs +++ b/src/backend/xdg_desktop_portal.rs @@ -7,11 +7,22 @@ use crate::message_dialog::MessageDialog; use crate::{FileDialog, FileHandle, MessageButtons, MessageDialogResult}; use ashpd::desktop::file_chooser::{FileFilter, OpenFileRequest, SaveFileRequest}; -// TODO: convert raw_window_handle::RawWindowHandle to ashpd::WindowIdentifier -// https://github.com/bilelmoussaoui/ashpd/issues/40 +use ashpd::WindowIdentifier; use log::error; use pollster::block_on; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; + +fn to_window_identifier( + window: Option, + display: Option, +) -> Option { + window.map(|window| { + block_on(Box::pin(async move { + WindowIdentifier::from_raw_handle(&window, display.as_ref()).await + })) + }) +} impl From<&Filter> for FileFilter { fn from(filter: &Filter) -> Self { @@ -44,6 +55,7 @@ impl AsyncFilePickerDialogImpl for FileDialog { fn pick_file_async(self) -> DialogFutureType> { Box::pin(async move { let res = OpenFileRequest::default() + .identifier(to_window_identifier(self.parent, self.parent_display)) .multiple(false) .title(self.title.as_deref().or(None)) .filters(self.filters.iter().map(From::from)) @@ -77,6 +89,7 @@ impl AsyncFilePickerDialogImpl for FileDialog { fn pick_files_async(self) -> DialogFutureType>> { Box::pin(async move { let res = OpenFileRequest::default() + .identifier(to_window_identifier(self.parent, self.parent_display)) .multiple(true) .title(self.title.as_deref().or(None)) .filters(self.filters.iter().map(From::from)) @@ -130,6 +143,7 @@ impl AsyncFolderPickerDialogImpl for FileDialog { fn pick_folder_async(self) -> DialogFutureType> { Box::pin(async move { let res = OpenFileRequest::default() + .identifier(to_window_identifier(self.parent, self.parent_display)) .multiple(false) .directory(true) .title(self.title.as_deref().or(None)) @@ -164,6 +178,7 @@ impl AsyncFolderPickerDialogImpl for FileDialog { fn pick_folders_async(self) -> DialogFutureType>> { Box::pin(async move { let res = OpenFileRequest::default() + .identifier(to_window_identifier(self.parent, self.parent_display)) .multiple(true) .directory(true) .title(self.title.as_deref().or(None)) @@ -213,6 +228,7 @@ impl AsyncFileSaveDialogImpl for FileDialog { fn save_file_async(self) -> DialogFutureType> { Box::pin(async move { let res = SaveFileRequest::default() + .identifier(to_window_identifier(self.parent, self.parent_display)) .title(self.title.as_deref().or(None)) .current_name(self.file_name.as_deref()) .filters(self.filters.iter().map(From::from)) diff --git a/src/file_dialog.rs b/src/file_dialog.rs index d1a5692..b6f95e7 100644 --- a/src/file_dialog.rs +++ b/src/file_dialog.rs @@ -3,8 +3,7 @@ use crate::FileHandle; use std::path::Path; use std::path::PathBuf; -use raw_window_handle::HasWindowHandle; -use raw_window_handle::RawWindowHandle; +use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; #[derive(Debug, Clone)] pub(crate) struct Filter { @@ -24,6 +23,7 @@ pub struct FileDialog { pub(crate) file_name: Option, pub(crate) title: Option, pub(crate) parent: Option, + pub(crate) parent_display: Option, pub(crate) can_create_directories: Option, } @@ -88,10 +88,14 @@ impl FileDialog { self } - /// Set parent windows explicitly (optional) - /// Suported in: `macos` and `windows` - pub fn set_parent(mut self, parent: &W) -> Self { + /// Set parent windows explicitly (optional). + /// Supported platforms: + /// * Windows + /// * Mac + /// * Linux (XDG only) + pub fn set_parent(mut self, parent: &W) -> Self { self.parent = parent.window_handle().ok().map(|x| x.as_raw()); + self.parent_display = parent.display_handle().ok().map(|x| x.as_raw()); self } @@ -206,9 +210,12 @@ impl AsyncFileDialog { self } - /// Set parent windows explicitly (optional) - /// Suported in: `macos` and `windows` - pub fn set_parent(mut self, parent: &W) -> Self { + /// Set parent windows explicitly (optional). + /// Supported platforms: + /// * Windows + /// * Mac + /// * Linux (XDG only) + pub fn set_parent(mut self, parent: &W) -> Self { self.file_dialog = self.file_dialog.set_parent(parent); self } diff --git a/src/message_dialog.rs b/src/message_dialog.rs index f56e7f0..d5fbe5e 100644 --- a/src/message_dialog.rs +++ b/src/message_dialog.rs @@ -4,8 +4,7 @@ use std::fmt::{Display, Formatter}; use std::future::Future; -use raw_window_handle::HasWindowHandle; -use raw_window_handle::RawWindowHandle; +use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; /// Synchronous Message Dialog. Supported platforms: /// * Windows @@ -19,6 +18,7 @@ pub struct MessageDialog { pub(crate) level: MessageLevel, pub(crate) buttons: MessageButtons, pub(crate) parent: Option, + pub(crate) parent_display: Option, } // Oh god, I don't like sending RawWindowHandle between threads but here we go anyways... @@ -64,10 +64,14 @@ impl MessageDialog { self } - /// Set parent windows explicitly (optional) - /// Suported in: `macos` and `windows` - pub fn set_parent(mut self, parent: &W) -> Self { + /// Set parent windows explicitly (optional). + /// Supported platforms: + /// * Windows + /// * Mac + /// * Linux (XDG only) + pub fn set_parent(mut self, parent: &W) -> Self { self.parent = parent.window_handle().ok().map(|x| x.as_raw()); + self.parent_display = parent.display_handle().ok().map(|x| x.as_raw()); self } @@ -124,9 +128,12 @@ impl AsyncMessageDialog { self } - /// Set parent windows explicitly (optional) - /// Suported in: `macos` and `windows` - pub fn set_parent(mut self, parent: &W) -> Self { + /// Set parent windows explicitly (optional). + /// Supported platforms: + /// * Windows + /// * Mac + /// * Linux (XDG only) + pub fn set_parent(mut self, parent: &W) -> Self { self.0 = self.0.set_parent(parent); self }