use std::{fmt, str::FromStr};
#[cfg(all(feature = "raw_handle", feature = "gtk4"))]
use raw_window_handle::{
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
};
#[cfg(feature = "raw_handle")]
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use serde::{ser::Serializer, Deserialize, Serialize};
use zbus::zvariant::Type;
#[derive(Default, Type)]
#[zvariant(signature = "s")]
#[doc(alias = "XdpParent")]
#[non_exhaustive]
pub enum WindowIdentifier {
#[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
#[doc(hidden)]
Gtk4(Gtk4WindowIdentifier),
#[cfg(feature = "wayland")]
#[doc(hidden)]
Wayland(WaylandWindowIdentifier),
#[doc(hidden)]
X11(WindowIdentifierType),
#[doc(hidden)]
#[default]
None,
}
unsafe impl Send for WindowIdentifier {}
unsafe impl Sync for WindowIdentifier {}
impl Serialize for WindowIdentifier {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl std::fmt::Display for WindowIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
Self::Gtk4(identifier) => f.write_str(&format!("{identifier}")),
#[cfg(feature = "wayland")]
Self::Wayland(identifier) => f.write_str(&format!("{identifier}")),
Self::X11(identifier) => f.write_str(&format!("{identifier}")),
Self::None => f.write_str(""),
}
}
}
impl std::fmt::Debug for WindowIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("WindowIdentifier")
.field(&format!("{self}"))
.finish()
}
}
impl WindowIdentifier {
#[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))))]
#[doc(alias = "xdp_parent_new_gtk")]
pub async fn from_native(native: &impl ::gtk4::prelude::IsA<::gtk4::Native>) -> Self {
match Gtk4WindowIdentifier::new(native).await {
Some(identifier) => Self::Gtk4(identifier),
None => Self::default(),
}
}
#[cfg(feature = "raw_handle")]
#[cfg_attr(docsrs, doc(cfg(feature = "raw_handle")))]
pub async fn from_raw_handle(
window_handle: &RawWindowHandle,
display_handle: Option<&RawDisplayHandle>,
) -> Self {
use raw_window_handle::{
RawDisplayHandle::Wayland as DisplayHandle,
RawWindowHandle::{Wayland, Xcb, Xlib},
};
match (window_handle, display_handle) {
(Wayland(wl_handle), Some(DisplayHandle(wl_display))) => unsafe {
Self::from_wayland_raw(wl_handle.surface.as_ptr(), wl_display.display.as_ptr())
.await
},
(Xlib(x_handle), _) => Self::from_xid(x_handle.window),
(Xcb(x_handle), _) => Self::from_xid(x_handle.window.get().into()),
_ => Self::default(), }
}
pub fn from_xid(xid: std::os::raw::c_ulong) -> Self {
Self::X11(WindowIdentifierType::X11(xid))
}
#[cfg(feature = "wayland")]
#[cfg_attr(docsrs, doc(cfg(feature = "wayland")))]
pub async unsafe fn from_wayland_raw(
surface_ptr: *mut std::ffi::c_void,
display_ptr: *mut std::ffi::c_void,
) -> Self {
match WaylandWindowIdentifier::from_raw(surface_ptr, display_ptr).await {
Some(identifier) => Self::Wayland(identifier),
None => Self::default(),
}
}
#[cfg(feature = "wayland")]
#[cfg_attr(docsrs, doc(cfg(feature = "wayland")))]
pub async fn from_wayland(surface: &wayland_client::protocol::wl_surface::WlSurface) -> Self {
match WaylandWindowIdentifier::new(surface).await {
Some(identifier) => Self::Wayland(identifier),
None => Self::default(),
}
}
}
#[cfg(all(feature = "raw_handle", feature = "gtk4"))]
impl HasDisplayHandle for WindowIdentifier {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
match self {
#[cfg(feature = "gtk4")]
Self::Gtk4(identifier) => Ok(identifier.as_raw_display_handle()),
_ => unreachable!(),
}
}
}
#[cfg(all(feature = "raw_handle", feature = "gtk4"))]
impl HasWindowHandle for WindowIdentifier {
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
match self {
#[cfg(feature = "gtk4")]
Self::Gtk4(identifier) => Ok(identifier.as_raw_window_handle()),
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Type)]
#[zvariant(signature = "s")]
pub enum WindowIdentifierType {
X11(std::os::raw::c_ulong),
#[allow(dead_code)]
Wayland(String),
}
impl fmt::Display for WindowIdentifierType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::X11(xid) => f.write_str(&format!("x11:0x{xid:x}")),
Self::Wayland(handle) => f.write_str(&format!("wayland:{handle}")),
}
}
}
impl FromStr for WindowIdentifierType {
type Err = PortalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (kind, handle) = s
.split_once(':')
.ok_or_else(|| PortalError::InvalidArgument("Invalid Window Identifier".to_owned()))?;
match kind {
"x11" => {
let handle = handle.trim_start_matches("0x");
Ok(Self::X11(
std::os::raw::c_ulong::from_str_radix(handle, 16)
.map_err(|_| PortalError::InvalidArgument(format!("Wrong XID {handle}")))?,
))
}
"wayland" => Ok(Self::Wayland(handle.to_owned())),
t => Err(PortalError::InvalidArgument(format!(
"Invalid Window Identifier type {t}",
))),
}
}
}
impl<'de> Deserialize<'de> for WindowIdentifierType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let handle = String::deserialize(deserializer)?;
Self::from_str(&handle)
.map_err(|e| serde::de::Error::custom(format!("Invalid Window identifier {e}")))
}
}
#[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
mod gtk4;
#[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
pub use self::gtk4::Gtk4WindowIdentifier;
use crate::PortalError;
#[cfg(feature = "wayland")]
mod wayland;
#[cfg(feature = "wayland")]
pub use self::wayland::WaylandWindowIdentifier;
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::WindowIdentifier;
use crate::window_identifier::WindowIdentifierType;
#[test]
fn test_serialize() {
let x11 = WindowIdentifier::from_xid(1024);
assert_eq!(x11.to_string(), "x11:0x400");
assert_eq!(WindowIdentifier::default().to_string(), "");
assert_eq!(
WindowIdentifierType::from_str("x11:0x11432").unwrap(),
WindowIdentifierType::X11(70706)
);
assert_eq!(
WindowIdentifierType::from_str("wayland:Somerandomchars").unwrap(),
WindowIdentifierType::Wayland("Somerandomchars".to_owned())
);
assert!(WindowIdentifierType::from_str("some_handle").is_err());
assert!(WindowIdentifierType::from_str("some_type:some_handle").is_err());
}
}