-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes: #55
- Loading branch information
Showing
8 changed files
with
375 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use gtk3::prelude::*; | ||
use gtk3::{gdk, glib}; | ||
|
||
#[derive(Debug)] | ||
pub struct Gtk3ActivationToken { | ||
pub(crate) token: String, | ||
} | ||
|
||
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 => todo!(), | ||
_ => None, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#[cfg(feature = "wayland")] | ||
use super::wayland::WaylandActivationToken; | ||
use gtk4::{gdk, glib, prelude::*}; | ||
use gdk4wayland::prelude::WaylandSurfaceExtManual; | ||
|
||
#[derive(Debug)] | ||
pub struct Gtk4ActivationToken { | ||
pub(crate) wl_token: WaylandActivationToken, | ||
} | ||
|
||
#[cfg(all(feature = "gtk4_wayland", feature = "wayland"))] | ||
impl Gtk4ActivationToken { | ||
pub async fn from_native<N: glib::IsA<gtk4::Native>>(app_id: &str, native: &N) -> Option<Self> { | ||
let surface = native.surface(); | ||
match surface.display().backend() { | ||
gdk::Backend::Wayland => { | ||
let surface = surface.downcast_ref::<gdk4wayland::WaylandSurface>().unwrap(); | ||
if let Some(wl_surface) = surface.wl_surface() { | ||
let wl_token = WaylandActivationToken::from_surface(app_id, &wl_surface).await.unwrap(); | ||
|
||
Some(Self { wl_token }) | ||
} else { | ||
None | ||
} | ||
}, | ||
_ => None, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(feature = "wayland")] | ||
impl From<WaylandActivationToken> for Gtk4ActivationToken { | ||
fn from(wl_token: WaylandActivationToken) -> Self { | ||
Self { wl_token } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#[cfg(all(feature = "gtk3", feature = "wayland"))] | ||
mod gtk3; | ||
#[cfg(all(feature = "gtk3", feature = "wayland"))] | ||
pub use self::gtk3::Gtk3ActivationToken; | ||
|
||
#[cfg(feature = "gtk4_wayland")] | ||
mod gtk4; | ||
#[cfg(feature = "gtk4_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(feature = "gtk4_wayland")] | ||
#[doc(hidden)] | ||
Gtk4(Gtk4ActivationToken), | ||
#[cfg(all(feature = "gtk3", feature = "wayland"))] | ||
#[doc(hidden)] | ||
Gtk3(Gtk3ActivationToken), | ||
#[doc(hidden)] | ||
Raw(String), | ||
#[doc(hidden)] | ||
None, | ||
} | ||
|
||
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 Default for ActivationToken { | ||
fn default() -> Self { | ||
Self::None | ||
} | ||
} | ||
|
||
impl ActivationToken { | ||
#[cfg(feature = "wayland")] | ||
/// Create an instance of [`ActivationToken`] from a Wayland surface and the | ||
/// application's id. | ||
pub async fn from_wayland_surface( | ||
app_id: &str, | ||
surface: &wayland_client::protocol::wl_surface::WlSurface, | ||
) -> Option<Self> { | ||
let token = WaylandActivationToken::from_surface(app_id, surface).await?; | ||
|
||
Some(Self::Wayland(token)) | ||
} | ||
|
||
#[cfg(feature = "wayland")] | ||
/// Create an instance of [`ActivationToken`] from a raw Wayland surface and | ||
/// the application's id. | ||
/// | ||
/// # Safety | ||
/// | ||
/// Both pointers have to be valid surface and display pointers. You must | ||
/// ensure the `display_ptr` lives longer than the returned | ||
/// `ActivationToken`. | ||
pub async unsafe fn from_wayland_raw( | ||
app_id: &str, | ||
surface_ptr: *mut std::ffi::c_void, | ||
display_ptr: *mut std::ffi::c_void, | ||
) -> Option<Self> { | ||
let token = WaylandActivationToken::from_raw(app_id, surface_ptr, display_ptr).await?; | ||
|
||
Some(Self::Wayland(token)) | ||
} | ||
|
||
#[cfg(feature = "gtk4_wayland")] | ||
// TODO Maybe name from_display. | ||
/// Creates a [`ActivationToken`] from a [`gtk4::Native`](https://docs.gtk.org/gtk4/class.Native.html). | ||
pub async fn from_native<N: ::gtk4::glib::IsA<::gtk4::Native>>(app_id: &str, native: &N) -> Option<Self> { | ||
let token = Gtk4ActivationToken::from_native(app_id, native).await?; | ||
|
||
Some(Self::Gtk4(token)) | ||
} | ||
|
||
#[cfg(all(feature = "gtk3", feature = "wayland"))] | ||
/// Creates a [`ActivationToken`] from a [`IsA<gdk3::Window>`](https://gtk-rs.org/gtk3-rs/stable/latest/docs/gdk/struct.Window.html). | ||
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(crate) fn as_str(&self) -> &str { | ||
match self { | ||
#[cfg(feature = "wayland")] | ||
Self::Wayland(activation_token) => activation_token.token.as_str(), | ||
#[cfg(feature = "gtk4_wayland")] | ||
Self::Gtk4(activation_token) => activation_token.wl_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(), | ||
Self::None => "", | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
use wayland_backend::sys::client::Backend; | ||
use wayland_client::{ | ||
protocol::{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(crate) token: String, | ||
wl_activation: Option<XdgActivationV1>, | ||
wl_token: Option<XdgActivationTokenV1>, | ||
} | ||
|
||
impl Drop for WaylandActivationToken { | ||
fn drop(&mut self) { | ||
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 { | ||
// Can be changed to display. | ||
pub async fn from_surface(app_id: &str, surface: &WlSurface) -> Option<Self> { | ||
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: &str, | ||
surface_ptr: *mut std::ffi::c_void, | ||
display_ptr: *mut std::ffi::c_void, | ||
) -> Option<Self> { | ||
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( | ||
WlSurface::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: &str, | ||
conn: wayland_client::Connection, | ||
surface: &WlSurface, | ||
) -> Option<Self> { | ||
let (sender, receiver) = futures_channel::oneshot::channel::<Option<Self>>(); | ||
|
||
// Cheap clone, protocol objects are essentially smart pointers | ||
let surface = surface.clone(); | ||
let app_id = app_id.to_owned(); | ||
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() | ||
} | ||
} | ||
|
||
impl wayland_client::Dispatch<XdgActivationTokenV1, ()> for WaylandActivationToken { | ||
fn event( | ||
state: &mut Self, | ||
_proxy: &XdgActivationTokenV1, | ||
event: <XdgActivationTokenV1 as Proxy>::Event, | ||
_data: &(), | ||
_connhandle: &wayland_client::Connection, | ||
_qhandle: &QueueHandle<Self>, | ||
) { | ||
if let Event::Done { token } = event { | ||
state.token = token; | ||
} | ||
} | ||
} | ||
|
||
impl wayland_client::Dispatch<wl_registry::WlRegistry, ()> for WaylandActivationToken { | ||
fn event( | ||
state: &mut Self, | ||
registry: &wl_registry::WlRegistry, | ||
event: wl_registry::Event, | ||
_data: &(), | ||
_connhandle: &wayland_client::Connection, | ||
qhandle: &QueueHandle<Self>, | ||
) { | ||
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::<XdgActivationV1, (), Self>( | ||
name, | ||
version.min(XDG_ACTIVATION_V1_VERSION), | ||
qhandle, | ||
(), | ||
); | ||
state.wl_activation = Some(activation); | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl wayland_client::Dispatch<XdgActivationV1, ()> for WaylandActivationToken { | ||
fn event( | ||
_state: &mut Self, | ||
_activation: &XdgActivationV1, | ||
_event: wayland_protocols::xdg::activation::v1::client::xdg_activation_v1::Event, | ||
_data: &(), | ||
_connhandle: &wayland_client::Connection, | ||
_qhandle: &QueueHandle<Self>, | ||
) { | ||
} | ||
} | ||
|
||
fn wayland_export_token( | ||
app_id: String, | ||
conn: wayland_client::Connection, | ||
surface: &WlSurface, | ||
) -> Result<WaylandActivationToken, Box<dyn std::error::Error>> { | ||
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.roundtrip(&mut state)?; | ||
|
||
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.roundtrip(&mut state)?; | ||
state.wl_token = Some(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)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.