use serde::{ser::Serializer, Serialize};
#[cfg(any(feature = "feature_gtk4", feature = "feature_gtk3"))]
use futures::lock::Mutex;
#[cfg(any(feature = "feature_gtk4", feature = "feature_gtk3"))]
use std::sync::Arc;
#[cfg(all(feature = "feature_gtk4", not(feature = "feature_gtk3")))]
use gtk4::glib::{self, clone};
#[cfg(feature = "feature_gtk4")]
use gtk4::prelude::*;
#[cfg(feature = "feature_gtk3")]
use gtk3::{
gdk as gdk3,
glib::{self, clone, translate::*},
prelude::*,
};
#[cfg(feature = "feature_gtk3")]
use std::{ffi::c_void, os::raw::c_char};
pub enum WindowIdentifier {
#[cfg(feature = "feature_gtk4")]
#[doc(hidden)]
Gtk4 {
native: Arc<Mutex<gtk4::Native>>,
handle: String,
},
#[cfg(feature = "feature_gtk3")]
#[doc(hidden)]
Gtk3 {
handle: String,
window: Arc<Mutex<gdk3::Window>>,
},
#[doc(hidden)]
Other(String),
}
unsafe impl Send for WindowIdentifier {}
unsafe impl Sync for WindowIdentifier {}
impl zvariant::Type for WindowIdentifier {
fn signature() -> zvariant::Signature<'static> {
String::signature()
}
}
impl Serialize for WindowIdentifier {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.inner())
}
}
impl std::fmt::Display for WindowIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner())
}
}
impl std::fmt::Debug for WindowIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("WindowIdentifier")
.field(&self.inner())
.finish()
}
}
impl WindowIdentifier {
pub fn new(identifier: &str) -> Self {
Self::Other(identifier.to_string())
}
pub(crate) fn inner(&self) -> &str {
match self {
#[cfg(feature = "feature_gtk4")]
Self::Gtk4 { native: _, handle } => handle,
#[cfg(feature = "feature_gtk3")]
Self::Gtk3 { handle, window: _ } => handle,
Self::Other(handle) => handle,
}
}
}
impl Default for WindowIdentifier {
fn default() -> Self {
Self::new("")
}
}
impl WindowIdentifier {
#[cfg(feature = "feature_gtk4")]
pub async fn from_native<W: glib::IsA<gtk4::Native>>(native: &W) -> Self {
let surface = native.surface().unwrap();
let handle = match surface
.display()
.expect("Surface has to be attached to a display")
.type_()
.name()
{
"GdkWaylandDisplay" => {
let (sender, receiver) = futures::channel::oneshot::channel::<String>();
let sender = Arc::new(Mutex::new(Some(sender)));
let top_level = surface
.downcast_ref::<gdk4wayland::WaylandToplevel>()
.unwrap();
top_level.export_handle(clone!(@strong sender => move |_level, handle| {
let wayland_handle = format!("wayland:{}", handle);
let ctx = glib::MainContext::default();
ctx.spawn_local(clone!(@strong sender, @strong wayland_handle => async move {
if let Some(m) = sender.lock().await.take() {
let _ = m.send(wayland_handle);
}
}));
}));
receiver.await.ok()
}
"GdkX11Display" => surface
.downcast_ref::<gdk4x11::X11Surface>()
.map(|w| format!("x11:{}", w.xid())),
_ => None,
};
match handle {
Some(h) => WindowIdentifier::Gtk4 {
native: Arc::new(Mutex::new(native.clone().upcast())),
handle: h,
},
None => WindowIdentifier::default(),
}
}
#[cfg(feature = "feature_gtk3")]
pub async fn from_window<W: glib::IsA<gdk3::Window>>(win: &W) -> Self {
let handle = match win.as_ref().display().type_().name() {
"GdkWaylandDisplay" => {
let (sender, receiver) = futures::channel::oneshot::channel::<String>();
let sender = Arc::new(Mutex::new(Some(sender)));
export_wayland_handle(
win,
clone!(@strong sender => move |_level, handle| {
let wayland_handle = format!("wayland:{}", handle);
let ctx = glib::MainContext::default();
ctx.spawn_local(clone!(@strong sender, @strong wayland_handle => async move {
if let Some(m) = sender.lock().await.take() {
let _ = m.send(wayland_handle);
}
}));
}),
);
receiver.await.ok()
}
"GdkX11Display" => win
.as_ref()
.downcast_ref::<gdk3x11::X11Window>()
.map(|w| format!("x11:{}", w.xid())),
_ => None,
};
match handle {
Some(h) => WindowIdentifier::Gtk3 {
handle: h,
window: Arc::new(Mutex::new(win.clone().upcast())),
},
None => WindowIdentifier::default(),
}
}
}
impl Drop for WindowIdentifier {
fn drop(&mut self) {
#[cfg(feature = "feature_gtk4")]
if let Self::Gtk4 { native, handle: _ } = self {
let ctx = glib::MainContext::default();
ctx.spawn_local(clone!(@strong native => async move {
let native = native.lock().await;
let surface = native.surface().unwrap();
let name = surface.display()
.unwrap()
.type_()
.name();
if name == "GdkWaylandDisplay"
{
let top_level = surface.downcast_ref::<gdk4wayland::WaylandToplevel>().unwrap();
top_level.unexport_handle();
}
}));
}
#[cfg(feature = "feature_gtk3")]
if let Self::Gtk3 { window, handle: _ } = self {
let ctx = glib::MainContext::default();
ctx.spawn_local(clone!(@strong window => async move {
let window = window.lock().await;
let name = window.display().type_().name();
if name == "GdkWaylandDisplay" {
unexport_wayland_handle(&*window);
}
}));
}
}
}
#[cfg(feature = "feature_gtk3")]
pub(crate) fn unexport_wayland_handle<W: glib::IsA<gdk3::Window>>(win: &W) {
extern "C" {
pub fn gdk_wayland_window_unexport_handle(window: *mut gdk3::ffi::GdkWindow);
}
unsafe {
gdk_wayland_window_unexport_handle(win.as_ptr() as *mut _);
}
}
#[cfg(feature = "feature_gtk3")]
pub(crate) fn export_wayland_handle<
W: glib::IsA<gdk3::Window>,
P: Fn(&gdk3::Window, &str) + 'static,
>(
win: &W,
callback: P,
) -> bool {
extern "C" {
pub fn gdk_wayland_window_export_handle(
window: *mut gdk3::ffi::GdkWindow,
cb: Option<unsafe extern "C" fn(*mut gdk3::ffi::GdkWindow, *const c_char, *mut c_void)>,
user_data: *mut c_void,
destroy_notify: Option<unsafe extern "C" fn(*mut c_void)>,
) -> bool;
}
unsafe extern "C" fn callback_trampoline<P: Fn(&gdk3::Window, &str) + 'static>(
window: *mut gdk3::ffi::GdkWindow,
handle: *const c_char,
user_data: glib::ffi::gpointer,
) {
let window = from_glib_borrow(window);
let handle: Borrowed<glib::GString> = from_glib_borrow(handle);
let callback: &P = &*(user_data as *mut _);
(*callback)(&window, handle.as_str());
}
unsafe extern "C" fn destroy_notify<P: Fn(&gdk3::Window, &str) + 'static>(
data: glib::ffi::gpointer,
) {
Box::from_raw(data as *mut _);
}
unsafe {
gdk_wayland_window_export_handle(
win.as_ref().to_glib_none().0,
Some(callback_trampoline::<P> as _),
Box::into_raw(Box::new(callback)) as *mut _,
Some(destroy_notify::<P> as _),
)
}
}