Skip to content

Commit

Permalink
Add activation_token
Browse files Browse the repository at this point in the history
  • Loading branch information
A6GibKm committed Jun 5, 2022
1 parent 1fea95b commit d03a925
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 18 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ tracing = {version = "0.1", optional = true}
libc = {version = "0.2.94", optional = true}
raw-window-handle = {version = "0.4", optional = true}
wayland-client = {version = "0.30.0-beta.3", optional = true}
wayland-protocols = {version = "0.30.0-beta.3", optional = true, features = ["unstable", "client"]}
wayland-protocols = {version = "0.30.0-beta.3", optional = true, features = ["unstable", "client", "staging"]}
wayland-backend = {version = "0.1.0-beta.3", optional = true, features = ["client_system"]}
async-std = {version = "1.11", optional = true}
tokio = {version = "1.17", features = ["fs", "io-util"], optional = true, default-features = false}
once_cell = "1.12"

[dev-dependencies]
serde_json = "1.0"
Expand Down
12 changes: 12 additions & 0 deletions ashpd-demo/data/resources/ui/open_uri.ui
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Activation Token</property>
<property name="subtitle" translatable="yes">TODO</property>
<property name="activatable-widget">activation_token_switch</property>
<child>
<object class="GtkSwitch" id="activation_token_switch">
<property name="valign">center</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
Expand Down
9 changes: 8 additions & 1 deletion ashpd-demo/src/portals/desktop/open_uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ mod imp {
pub ask_switch: TemplateChild<gtk::Switch>,
#[template_child]
pub uri_entry: TemplateChild<gtk::Entry>,
#[template_child]
pub activation_token_switch: TemplateChild<gtk::Switch>,
}

#[glib::object_subclass]
Expand Down Expand Up @@ -67,7 +69,12 @@ impl OpenUriPage {
let root = self.native().unwrap();
let identifier = WindowIdentifier::from_native(&root).await;
let uri = imp.uri_entry.text();
match open_uri::open_uri(&identifier, &uri, writable, ask).await {
let activation_token = if imp.activation_token_switch.is_active() {
ashpd::ActivationToken::from_native(&root)
} else {
None
};
match open_uri::open_uri(&identifier, &uri, writable, ask, activation_token).await {
Ok(_) => {
self.send_notification(
"Open URI request was successful",
Expand Down
60 changes: 60 additions & 0 deletions src/activation_token/gtk3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use super::wayland::WaylandActivationToken;
use gtk3::glib::translate::ToGlibPtr;
use gtk3::prelude::*;
use gtk3::{gdk, glib};
use wayland_client::{
backend::ObjectId,
protocol::{__interfaces::WL_SURFACE_INTERFACE, wl_surface::WlSurface},
Proxy,
};
use wayland_protocols::xdg::activation::v1::client::xdg_activation_token_v1::XdgActivationTokenV1;

#[derive(Debug)]
pub struct Gtk3ActivationToken {
pub token: String,
inner: XdgActivationTokenV1,
}

impl Gtk3ActivationToken {
pub fn from_window(window: &impl glib::IsA<gdk::Window>) -> Option<Self> {
let display = window.as_ref().display();
match display.backend() {
gdk::Backend::Wayland => {
let cnx = wayland_client::Connection::connect_to_env().unwrap();
let wayland_window = window
.as_ref()
.downcast_ref::<gdk3wayland::WaylandWindow>()
.unwrap();
let surface_id = unsafe {
let ptr = gdk3wayland::ffi::gdk_wayland_window_get_wl_surface(
wayland_window.to_glib_none().0,
);

ObjectId::from_ptr(&WL_SURFACE_INTERFACE, ptr as *mut _).unwrap()
};
let wl_surface = WlSurface::from_id(&cnx, surface_id).unwrap();
let wl = WaylandActivationToken::from_surface(&wl_surface).ok()?;

Some(Self::from(wl))
}
// TODO Can this be implemented for X11?
_ => None,
}
}
}

impl From<WaylandActivationToken> for Gtk3ActivationToken {
fn from(mut wl_token: WaylandActivationToken) -> Self {
let token = std::mem::take(&mut wl_token.token);
// NOTE Safe unwrap, WlActivationToken has a inner set at construction.
let inner = wl_token.inner.take().unwrap();

Self { inner, token }
}
}

impl Drop for Gtk3ActivationToken {
fn drop(&mut self) {
self.inner.destroy();
}
}
61 changes: 61 additions & 0 deletions src/activation_token/gtk4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use super::wayland::WaylandActivationToken;
use gtk4::{gdk, glib::translate::ToGlibPtr, prelude::*};
use wayland_client::{
backend::ObjectId,
protocol::{__interfaces::WL_SURFACE_INTERFACE, wl_surface::WlSurface},
Proxy,
};
use wayland_protocols::xdg::activation::v1::client::xdg_activation_token_v1::XdgActivationTokenV1;

#[derive(Debug)]
pub struct Gtk4ActivationToken {
pub token: String,
inner: XdgActivationTokenV1,
}

impl Gtk4ActivationToken {
pub fn from_native(native: &impl ::gtk4::glib::IsA<::gtk4::Native>) -> Option<Self> {
let surface = native.surface();
match surface.display().backend() {
gdk::Backend::Wayland => {
// TODO use WaylandSurface::wl_surface(), see
// https://github.com/gtk-rs/gtk4-rs/pull/1053
let cnx = wayland_client::Connection::connect_to_env().unwrap();
let surface_id = unsafe {
let ptr = gdk4wayland::ffi::gdk_wayland_surface_get_wl_surface(
surface
.downcast_ref::<gdk4wayland::WaylandSurface>()
.unwrap()
.to_glib_none()
.0,
);

ObjectId::from_ptr(&WL_SURFACE_INTERFACE, ptr as *mut _).unwrap()
};
let wl_surface = WlSurface::from_id(&cnx, surface_id).unwrap();

let wl = WaylandActivationToken::from_surface(&wl_surface).ok()?;

Some(Self::from(wl))
}
// TODO Can this be implemented for X11?
_ => None,
}
}
}

impl From<WaylandActivationToken> for Gtk4ActivationToken {
fn from(mut wl_token: WaylandActivationToken) -> Self {
let token = std::mem::take(&mut wl_token.token);
// NOTE Safe unwrap, WlActivationToken has a inner set at construction.
let inner = wl_token.inner.take().unwrap();

Self { inner, token }
}
}

impl Drop for Gtk4ActivationToken {
fn drop(&mut self) {
self.inner.destroy();
}
}
82 changes: 82 additions & 0 deletions src/activation_token/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#[cfg(all(feature = "gtk3", feature = "wayland"))]
mod gtk3;
#[cfg(all(feature = "gtk3", feature = "wayland"))]
pub use self::gtk3::Gtk3ActivationToken;

#[cfg(all(feature = "gtk4", feature = "wayland"))]
mod gtk4;
#[cfg(all(feature = "gtk4", feature = "wayland"))]
pub use self::gtk4::Gtk4ActivationToken;

#[cfg(any(feature = "wayland"))]
mod wayland;
#[cfg(feature = "wayland")]
pub use wayland::WaylandActivationToken;

use serde::{ser::Serializer, Serialize};
use zbus::zvariant::Type;

// TODO
/// See https://wayland.app/protocols/xdg-activation-v1
#[derive(Type)]
#[zvariant(signature = "s")]
#[derive(Debug)]
pub enum ActivationToken {
#[cfg(feature = "wayland")]
#[doc(hidden)]
Wayland(WaylandActivationToken),
#[cfg(all(feature = "gtk4", feature = "wayland"))]
#[doc(hidden)]
Gtk4(Gtk4ActivationToken),
#[cfg(all(feature = "gtk3", feature = "wayland"))]
#[doc(hidden)]
Gtk3(Gtk3ActivationToken),
#[doc(hidden)]
Raw(String),
}

impl Serialize for ActivationToken {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}

impl ActivationToken {
#[cfg(feature = "wayland")]
pub fn from_surface(
surface: &wayland_client::protocol::wl_surface::WlSurface,
) -> Result<Self, Box<dyn std::error::Error>> {
let token = WaylandActivationToken::from_surface(surface)?;

Ok(Self::Wayland(token))
}

#[cfg(all(feature = "gtk4", feature = "wayland"))]
pub fn from_native(native: &impl ::gtk4::glib::IsA<::gtk4::Native>) -> Option<Self> {
let token = Gtk4ActivationToken::from_native(native)?;

Some(Self::Gtk4(token))
}

#[cfg(all(feature = "gtk3", feature = "wayland"))]
pub fn from_window(window: &impl ::gtk3::glib::IsA<::gtk3::gdk::Window>) -> Option<Self> {
let token = Gtk3ActivationToken::from_window(window)?;

Some(Self::Gtk3(token))
}

pub fn as_str(&self) -> &str {
match self {
#[cfg(feature = "wayland")]
Self::Wayland(activation_token) => activation_token.token.as_str(),
#[cfg(all(feature = "gtk4", feature = "wayland"))]
Self::Gtk4(activation_token) => activation_token.token.as_str(),
#[cfg(all(feature = "gtk3", feature = "wayland"))]
Self::Gtk3(activation_token) => activation_token.token.as_str(),
Self::Raw(string) => string.as_str(),
}
}
}
58 changes: 58 additions & 0 deletions src/activation_token/wayland.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use once_cell::sync::OnceCell;
use wayland_client::{protocol::wl_surface::WlSurface, Proxy, QueueHandle};
use wayland_protocols::xdg::activation::v1::client::{
xdg_activation_token_v1::{Event, XdgActivationTokenV1},
xdg_activation_v1::XdgActivationV1,
};

#[derive(Debug, Default)]
pub struct WaylandActivationToken {
pub token: String,
// We use a cell to transform Self into Gtk4ActivationToken.
pub(super) inner: OnceCell<XdgActivationTokenV1>,
}

impl Drop for WaylandActivationToken {
fn drop(&mut self) {
if let Some(wl_token) = self.inner.take() {
wl_token.destroy();
}
}
}

impl WaylandActivationToken {
pub fn from_surface(surface: &WlSurface) -> Result<Self, Box<dyn std::error::Error>> {
let cnx = wayland_client::Connection::connect_to_env()?;
let wl_activation = XdgActivationV1::from_id(&cnx, surface.id())?;
let mut queue = cnx.new_event_queue();
let queue_handle = queue.handle();
let inner = wl_activation.get_activation_token(&queue_handle, ())?;
let mut exported_token = ExportedActivationToken::default();
queue.blocking_dispatch(&mut exported_token)?;

Ok(Self {
token: exported_token.0,
inner: OnceCell::with_value(inner),
})
}
}

#[derive(Default)]
struct ExportedActivationToken(String);
impl wayland_client::Dispatch<XdgActivationTokenV1, ()> for ExportedActivationToken {
fn event(
&mut self,
_proxy: &XdgActivationTokenV1,
event: <XdgActivationTokenV1 as Proxy>::Event,
_data: &(),
_connhandle: &wayland_client::Connection,
_qhandle: &QueueHandle<Self>,
) {
match event {
Event::Done { token } => {
self.0 = token;
}
_ => unreachable!(),
}
}
}
14 changes: 11 additions & 3 deletions src/desktop/dynamic_launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use zbus::zvariant::{self, SerializeDict, Type};

use super::{HandleToken, Icon, DESTINATION, PATH};
use crate::{
activation_token::ActivationToken,
helpers::{call_method, call_request_method},
Error, WindowIdentifier,
};
Expand Down Expand Up @@ -254,9 +255,16 @@ impl<'a> DynamicLauncherProxy<'a> {
/// See also [`Launch`](https://flatpak.github.io/xdg-desktop-portal/index.html#gdbus-method-org-freedesktop-portal-DynamicLauncher.Launch).
#[doc(alias = "Launch")]
#[doc(alias = "xdp_portal_dynamic_launcher_launch")]
pub async fn launch(&self, desktop_file_id: &str) -> Result<(), Error> {
// TODO: handle activation_token
let options: HashMap<&str, zvariant::Value<'_>> = HashMap::new();
pub async fn launch(
&self,
desktop_file_id: &str,
activation_token: Option<ActivationToken>,
) -> Result<(), Error> {
let options: HashMap<&str, zvariant::Value<'_>> = if let Some(token) = activation_token {
HashMap::from([("activation_token", token.as_str().to_owned().into())])
} else {
HashMap::new()
};
call_method(self.inner(), "Launch", &(desktop_file_id, &options)).await
}

Expand Down
Loading

0 comments on commit d03a925

Please sign in to comment.