From d2c5fe18fe5eda7249796bd42c48c701f37a8a24 Mon Sep 17 00:00:00 2001 From: Maximiliano Sandoval R Date: Thu, 9 Jun 2022 13:28:18 +0200 Subject: [PATCH] Update wayland stuff --- ashpd-demo/Cargo.lock | 28 ++-- ashpd-demo/Cargo.toml | 4 +- ashpd-demo/src/application.rs | 4 +- ashpd-demo/src/portals/desktop/open_uri.rs | 68 +++++--- ashpd-demo/src/window.rs | 4 +- src/activation_token/gtk4.rs | 8 +- src/activation_token/mod.rs | 25 ++- src/activation_token/wayland.rs | 176 +++++++++++++++++---- 8 files changed, 236 insertions(+), 81 deletions(-) diff --git a/ashpd-demo/Cargo.lock b/ashpd-demo/Cargo.lock index e324c8026..5462c7f73 100644 --- a/ashpd-demo/Cargo.lock +++ b/ashpd-demo/Cargo.lock @@ -29,7 +29,6 @@ checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "ashpd" version = "0.4.0" -source = "git+https://github.com/A6GibKm/ashpd?branch=wayland2#6979e7e12ac74f934064ad383c853f5c61c57350" dependencies = [ "async-std", "enumflags2", @@ -38,7 +37,6 @@ dependencies = [ "gdk4-x11", "gtk4", "libc", - "once_cell", "pipewire", "rand", "serde", @@ -57,6 +55,7 @@ dependencies = [ "ashpd", "chrono", "futures", + "gdk4-wayland", "gettext-rs", "gst-plugin-gtk4", "gstreamer", @@ -2247,9 +2246,8 @@ checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "wayland-backend" -version = "0.1.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee0502f3c3c258b4afa4af0a23a37022e39f3b19473ba8577e1e8eebd4a8175c" +version = "0.1.0-beta.4" +source = "git+https://github.com/Smithay/wayland-rs?branch=vberger/backend-from_foreign#b00ff3ba66fa61f6e3aff6cd25b150fd360bc8b5" dependencies = [ "cc", "downcast-rs", @@ -2262,9 +2260,8 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.30.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9817d458e9eaadf8cd279e1bd8d8dbebecf1226ba7dbc503d7a1b5d1850a8ec5" +version = "0.30.0-beta.4" +source = "git+https://github.com/Smithay/wayland-rs?branch=vberger/backend-from_foreign#b00ff3ba66fa61f6e3aff6cd25b150fd360bc8b5" dependencies = [ "bitflags", "futures-channel", @@ -2277,9 +2274,8 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.30.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec20013f911ee1f30329666acd02a9bf0950b2041a5643b5b0d78524f689f40e" +version = "0.30.0-beta.4" +source = "git+https://github.com/Smithay/wayland-rs?branch=vberger/backend-from_foreign#b00ff3ba66fa61f6e3aff6cd25b150fd360bc8b5" dependencies = [ "bitflags", "wayland-backend", @@ -2289,9 +2285,8 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.30.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47fa94bcf5700de28c9849ce9d8442ff686ff0f4314e3fecac1412a4fb576e44" +version = "0.30.0-beta.4" +source = "git+https://github.com/Smithay/wayland-rs?branch=vberger/backend-from_foreign#b00ff3ba66fa61f6e3aff6cd25b150fd360bc8b5" dependencies = [ "proc-macro2", "quote", @@ -2301,9 +2296,8 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.30.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f68ccbd50692c338be609b568286b9567ad35b4fd0b1ea3e8bb7a14a7b9bc2" +version = "0.30.0-beta.4" +source = "git+https://github.com/Smithay/wayland-rs?branch=vberger/backend-from_foreign#b00ff3ba66fa61f6e3aff6cd25b150fd360bc8b5" dependencies = [ "dlib", "log", diff --git a/ashpd-demo/Cargo.toml b/ashpd-demo/Cargo.toml index 151bcfb3e..266671514 100644 --- a/ashpd-demo/Cargo.toml +++ b/ashpd-demo/Cargo.toml @@ -17,12 +17,12 @@ gtk = {package = "gtk4", version = "0.4"} adw = {version = "0.1", package = "libadwaita"} serde = {version = "1.0", features = ["derive"]} chrono = {version = "0.4", default-features = false, features = ["clock"]} +gdk4wayland = {package = "gdk4-wayland"} [dependencies.shumate] package = "libshumate" version = "0.1.0-alpha.4" [dependencies.ashpd] -git = "https://github.com/A6GibKm/ashpd" -branch = "wayland2" +path = "../" features = ["gtk4", "wayland", "pipewire", "tracing"] diff --git a/ashpd-demo/src/application.rs b/ashpd-demo/src/application.rs index e8dff8629..3fd8d08b4 100644 --- a/ashpd-demo/src/application.rs +++ b/ashpd-demo/src/application.rs @@ -144,9 +144,7 @@ impl Application { })); }) ); - let is_sandboxed = futures::executor::block_on(async { - ashpd::is_sandboxed().await - }); + let is_sandboxed = futures::executor::block_on(async { ashpd::is_sandboxed().await }); // The restart app requires the Flatpak portal gtk_macros::get_action!(self, @restart).set_enabled(is_sandboxed); diff --git a/ashpd-demo/src/portals/desktop/open_uri.rs b/ashpd-demo/src/portals/desktop/open_uri.rs index d023e6a6c..cd55301ac 100644 --- a/ashpd-demo/src/portals/desktop/open_uri.rs +++ b/ashpd-demo/src/portals/desktop/open_uri.rs @@ -63,26 +63,54 @@ impl OpenUriPage { } async fn open_uri(&self) { - let imp = self.imp(); - let writable = imp.writeable_switch.is_active(); - let ask = imp.ask_switch.is_active(); - let root = self.native().unwrap(); - let identifier = WindowIdentifier::from_native(&root).await; - let uri = imp.uri_entry.text(); - let activation_token = if imp.activation_token_switch.is_active() { - Some(ashpd::ActivationToken::from_native(&root).unwrap()) - } else { - None - }; - match open_uri::open_uri(&identifier, &uri, writable, ask, activation_token).await { - Ok(_) => { - self.send_notification( - "Open URI request was successful", - NotificationKind::Success, - ); - } - Err(_err) => { - self.send_notification("Request to open URI failed", NotificationKind::Error); + unsafe { + let imp = self.imp(); + let writable = imp.writeable_switch.is_active(); + let ask = imp.ask_switch.is_active(); + let root = self.native().unwrap(); + use gtk::glib::translate::ToGlibPtr; + + let surface = root + .surface() + .downcast::() + .unwrap(); + let display = root + .display() + .downcast::() + .unwrap(); + + let surface_ptr = + gdk4wayland::ffi::gdk_wayland_surface_get_wl_surface(surface.to_glib_none().0); + let display_ptr = + gdk4wayland::ffi::gdk_wayland_display_get_wl_display(display.to_glib_none().0); + + let identifier = + WindowIdentifier::from_wayland_raw(surface_ptr as *mut _, display_ptr as *mut _) + .await; + let app_id = String::from(crate::config::APP_ID); + let token = + ashpd::ActivationToken::from_wayland_raw(app_id, surface_ptr as *mut _, display_ptr as *mut _) + .await.unwrap(); + + tracing::debug!("Handle {identifier}"); + tracing::debug!("Activation Token {}", token.as_str()); + + let uri = imp.uri_entry.text(); + let activation_token = if imp.activation_token_switch.is_active() { + Some(token) + } else { + None + }; + match open_uri::open_uri(&identifier, &uri, writable, ask, activation_token).await { + Ok(_) => { + self.send_notification( + "Open URI request was successful", + NotificationKind::Success, + ); + } + Err(_err) => { + self.send_notification("Request to open URI failed", NotificationKind::Error); + } } } } diff --git a/ashpd-demo/src/window.rs b/ashpd-demo/src/window.rs index c69ed41b1..2c5be7e75 100644 --- a/ashpd-demo/src/window.rs +++ b/ashpd-demo/src/window.rs @@ -127,9 +127,7 @@ mod imp { if config::PROFILE == "Devel" { obj.add_css_class("devel"); } - let is_sandboxed = futures::executor::block_on(async { - ashpd::is_sandboxed().await - }); + let is_sandboxed = futures::executor::block_on(async { ashpd::is_sandboxed().await }); // Add pages based on whether the app is sandboxed if is_sandboxed { self.sidebar diff --git a/src/activation_token/gtk4.rs b/src/activation_token/gtk4.rs index 1948752f6..98b7bb0bb 100644 --- a/src/activation_token/gtk4.rs +++ b/src/activation_token/gtk4.rs @@ -8,11 +8,9 @@ pub struct Gtk4ActivationToken { impl Gtk4ActivationToken { pub fn from_native>(native: &N) -> Option { match native.backend() { - gdk::Backend::Wayland => native - .startup_notification_id() - .map(|token| Self { - token: token.to_string(), - }), + gdk::Backend::Wayland => native.startup_notification_id().map(|token| Self { + token: token.to_string(), + }), gdk::Backend::X11 => todo!(), _ => None, } diff --git a/src/activation_token/mod.rs b/src/activation_token/mod.rs index eb6ee395e..3294d0dda 100644 --- a/src/activation_token/mod.rs +++ b/src/activation_token/mod.rs @@ -46,12 +46,29 @@ impl Serialize for ActivationToken { impl ActivationToken { #[cfg(feature = "wayland")] - pub fn from_surface( + pub async fn from_wayland_surface( + app_id: String, surface: &wayland_client::protocol::wl_surface::WlSurface, - ) -> Result> { - let token = WaylandActivationToken::from_surface(surface)?; + ) -> Option { + let token = WaylandActivationToken::from_surface(app_id, surface).await?; - Ok(Self::Wayland(token)) + Some(Self::Wayland(token)) + } + + #[cfg(feature = "wayland")] + /// Create an instance of [`ActivationToken`] from a Wayland surface. + /// + /// ## Safety + /// + /// The surface and display have to be valid Wayland pointers. + pub async unsafe fn from_wayland_raw( + app_id: String, + surface_ptr: *mut std::ffi::c_void, + display_ptr: *mut std::ffi::c_void, + ) -> Option { + let token = WaylandActivationToken::from_raw(app_id, surface_ptr, display_ptr).await?; + + Some(Self::Wayland(token)) } #[cfg(feature = "gtk4")] diff --git a/src/activation_token/wayland.rs b/src/activation_token/wayland.rs index 2b30c6eed..0d2429f9d 100644 --- a/src/activation_token/wayland.rs +++ b/src/activation_token/wayland.rs @@ -1,46 +1,90 @@ -use wayland_client::{protocol::wl_surface::WlSurface, Proxy, QueueHandle}; +use wayland_backend::sys::client::Backend; +use wayland_client::{ + protocol::{__interfaces::WL_SURFACE_INTERFACE, wl_registry, wl_surface::WlSurface}, + Proxy, QueueHandle, +}; use wayland_protocols::xdg::activation::v1::client::{ xdg_activation_token_v1::{Event, XdgActivationTokenV1}, xdg_activation_v1::XdgActivationV1, }; +// Supported versions. +const XDG_ACTIVATION_V1_VERSION: u32 = 1; + #[derive(Debug, Default)] pub struct WaylandActivationToken { - pub token: String, - // We use a option to transform Self into Gtk4ActivationToken. - pub(super) inner: Option, + pub(crate) token: String, + wl_activation: Option, + wl_token: Option, } +// Is this ok? impl Drop for WaylandActivationToken { fn drop(&mut self) { - if let Some(wl_token) = self.inner.take() { + if let Some(wl_token) = self.wl_token.take() { wl_token.destroy(); } + + if let Some(wl_activation) = self.wl_activation.take() { + wl_activation.destroy(); + } } } impl WaylandActivationToken { - pub fn from_surface(surface: &WlSurface) -> Result> { - let cnx = wayland_client::Connection::connect_to_env().unwrap(); - let wl_activation = XdgActivationV1::from_id(&cnx, surface.id()).unwrap(); - let mut queue = cnx.new_event_queue(); - let queue_handle = queue.handle(); - let inner = wl_activation - .get_activation_token(&queue_handle, ()) - .unwrap(); - let mut exported_token = ExportedActivationToken::default(); - queue.blocking_dispatch(&mut exported_token).unwrap(); - - Ok(Self { - token: exported_token.0, - inner: Some(inner), - }) + // Can be changed to display. + pub async fn from_surface(app_id: String, surface: &WlSurface) -> Option { + let backend = surface.backend().upgrade()?; + let conn = wayland_client::Connection::from_backend(backend); + + Self::new_inner(app_id, conn, surface).await + } + + pub async unsafe fn from_raw( + app_id: String, + surface_ptr: *mut std::ffi::c_void, + display_ptr: *mut std::ffi::c_void, + ) -> Option { + if surface_ptr.is_null() || display_ptr.is_null() { + return None; + } + + let backend = Backend::from_foreign_display(display_ptr as *mut _); + let conn = wayland_client::Connection::from_backend(backend); + let obj_id = wayland_backend::sys::client::ObjectId::from_ptr( + &WL_SURFACE_INTERFACE, + surface_ptr as *mut _, + ) + .ok()?; + + let surface = WlSurface::from_id(&conn, obj_id).ok()?; + + Self::new_inner(app_id, conn, &surface).await + } + + async fn new_inner( + app_id: String, + conn: wayland_client::Connection, + surface: &WlSurface, + ) -> Option { + let (sender, receiver) = futures::channel::oneshot::channel::>(); + + // Cheap clone, protocol objects are essentially smart pointers + let surface = surface.clone(); + std::thread::spawn(move || match wayland_export_token(app_id, conn, &surface) { + Ok(window_handle) => sender.send(Some(window_handle)).unwrap(), + Err(_err) => { + #[cfg(feature = "tracing")] + tracing::info!("Could not get wayland window identifier: {_err}"); + sender.send(None).unwrap(); + } + }); + + receiver.await.unwrap() } } -#[derive(Default)] -struct ExportedActivationToken(String); -impl wayland_client::Dispatch for ExportedActivationToken { +impl wayland_client::Dispatch for WaylandActivationToken { fn event( &mut self, _proxy: &XdgActivationTokenV1, @@ -49,11 +93,89 @@ impl wayland_client::Dispatch for ExportedActivationTo _connhandle: &wayland_client::Connection, _qhandle: &QueueHandle, ) { - match event { - Event::Done { token } => { - self.0 = token; + if let Event::Done { token } = event { + self.token = token; + } + } +} + +impl wayland_client::Dispatch for WaylandActivationToken { + fn event( + &mut self, + registry: &wl_registry::WlRegistry, + event: wl_registry::Event, + _: &(), + _: &wayland_client::Connection, + qhandle: &QueueHandle, + ) { + if let wl_registry::Event::Global { + name, + interface, + version, + } = event + { + if &interface == "xdg_activation_v1" { + #[cfg(feature = "tracing")] + tracing::info!("Found wayland interface {interface} v{version}"); + let activation = registry + .bind::( + name, + version.min(XDG_ACTIVATION_V1_VERSION), + qhandle, + (), + ) + .unwrap(); + self.wl_activation = Some(activation); } - _ => unreachable!(), } } } + +impl wayland_client::Dispatch for WaylandActivationToken { + fn event( + &mut self, + _activation: &XdgActivationV1, + _event: wayland_protocols::xdg::activation::v1::client::xdg_activation_v1::Event, + _: &(), + _: &wayland_client::Connection, + _qhandle: &QueueHandle, + ) { + } +} + +fn wayland_export_token( + app_id: String, + conn: wayland_client::Connection, + surface: &WlSurface, +) -> Result> { + let display = conn.display(); + let mut event_queue = conn.new_event_queue(); + let mut state = WaylandActivationToken::default(); + let qhandle = event_queue.handle(); + display.get_registry(&qhandle, ())?; + event_queue.sync_roundtrip(&mut state)?; + + let wl_token = if let Some(ref activation) = state.wl_activation { + let wl_token = activation.get_activation_token(&qhandle, ())?; + // TODO is this an APP ID in the traditional sense? + wl_token.set_app_id(app_id); + wl_token.set_surface(surface); + // TODO wl_token.set_serial(serial, &seat); + wl_token.commit(); + event_queue.sync_roundtrip(&mut state)?; + + Some(wl_token) + } else { + None + }; + + state.wl_token = wl_token; + + if !state.token.is_empty() { + Ok(state) + } else { + #[cfg(feature = "tracing")] + tracing::error!("Failed to get a response from the wayland server"); + Err(Box::new(crate::Error::NoResponse)) + } +}