1use std::{fmt, str::FromStr};
2
3#[cfg(all(
4 any(feature = "gtk4_wayland", feature = "gtk4_x11"),
5 feature = "backend"
6))]
7use ::gtk4 as gtk;
8#[cfg(all(feature = "raw_handle", feature = "gtk4"))]
9use raw_window_handle::{
10 DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
11};
12#[cfg(feature = "raw_handle")]
13use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
14use serde::{ser::Serializer, Deserialize, Serialize};
15use zbus::zvariant::Type;
16#[derive(Type)]
92#[zvariant(signature = "s")]
93#[doc(alias = "XdpParent")]
94#[non_exhaustive]
95pub enum WindowIdentifier {
96 #[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
98 #[doc(hidden)]
99 Gtk4(Gtk4WindowIdentifier),
100 #[cfg(feature = "wayland")]
101 #[doc(hidden)]
102 Wayland(WaylandWindowIdentifier),
103 #[doc(hidden)]
104 X11(WindowIdentifierType),
105}
106
107unsafe impl Send for WindowIdentifier {}
108unsafe impl Sync for WindowIdentifier {}
109
110impl Serialize for WindowIdentifier {
111 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112 where
113 S: Serializer,
114 {
115 serializer.serialize_str(&self.to_string())
116 }
117}
118
119impl std::fmt::Display for WindowIdentifier {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 match self {
122 #[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
123 Self::Gtk4(identifier) => f.write_str(&format!("{identifier}")),
124 #[cfg(feature = "wayland")]
125 Self::Wayland(identifier) => f.write_str(&format!("{identifier}")),
126 Self::X11(identifier) => f.write_str(&format!("{identifier}")),
127 }
128 }
129}
130
131impl std::fmt::Debug for WindowIdentifier {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 f.debug_tuple("WindowIdentifier")
134 .field(&format!("{self}"))
135 .finish()
136 }
137}
138
139impl WindowIdentifier {
140 #[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
141 #[cfg_attr(docsrs, doc(cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))))]
142 #[doc(alias = "xdp_parent_new_gtk")]
149 pub async fn from_native(native: &impl ::gtk4::prelude::IsA<::gtk4::Native>) -> Option<Self> {
150 Gtk4WindowIdentifier::new(native).await.map(Self::Gtk4)
151 }
152
153 #[cfg(feature = "raw_handle")]
154 #[cfg_attr(docsrs, doc(cfg(feature = "raw_handle")))]
155 pub async fn from_raw_handle(
163 window_handle: &RawWindowHandle,
164 display_handle: Option<&RawDisplayHandle>,
165 ) -> Option<Self> {
166 use raw_window_handle::RawWindowHandle::{Xcb, Xlib};
167 #[cfg(feature = "wayland")]
168 use raw_window_handle::{
169 RawDisplayHandle::Wayland as DisplayHandle, RawWindowHandle::Wayland,
170 };
171 match (window_handle, display_handle) {
172 #[cfg(feature = "wayland")]
173 (Wayland(wl_handle), Some(DisplayHandle(wl_display))) => unsafe {
174 Self::from_wayland_raw(wl_handle.surface.as_ptr(), wl_display.display.as_ptr())
175 .await
176 },
177 (Xlib(x_handle), _) => Some(Self::from_xid(x_handle.window)),
178 (Xcb(x_handle), _) => Some(Self::from_xid(x_handle.window.get().into())),
179 _ => None,
180 }
181 }
182
183 pub fn from_xid(xid: std::os::raw::c_ulong) -> Self {
185 Self::X11(WindowIdentifierType::X11(xid))
186 }
187
188 #[cfg(feature = "wayland")]
189 #[cfg_attr(docsrs, doc(cfg(feature = "wayland")))]
190 pub async unsafe fn from_wayland_raw(
198 surface_ptr: *mut std::ffi::c_void,
199 display_ptr: *mut std::ffi::c_void,
200 ) -> Option<Self> {
201 WaylandWindowIdentifier::from_raw(surface_ptr, display_ptr)
202 .await
203 .map(Self::Wayland)
204 }
205
206 #[cfg(feature = "wayland")]
207 #[cfg_attr(docsrs, doc(cfg(feature = "wayland")))]
208 pub async fn from_wayland(
210 surface: &wayland_client::protocol::wl_surface::WlSurface,
211 ) -> Option<Self> {
212 WaylandWindowIdentifier::new(surface)
213 .await
214 .map(Self::Wayland)
215 }
216}
217
218#[cfg(all(feature = "raw_handle", feature = "gtk4"))]
219impl HasDisplayHandle for WindowIdentifier {
220 fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
229 match self {
230 #[cfg(feature = "gtk4")]
231 Self::Gtk4(identifier) => Ok(identifier.as_raw_display_handle()),
232 _ => unreachable!(),
233 }
234 }
235}
236
237#[cfg(all(feature = "raw_handle", feature = "gtk4"))]
238impl HasWindowHandle for WindowIdentifier {
239 fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
248 match self {
249 #[cfg(feature = "gtk4")]
250 Self::Gtk4(identifier) => Ok(identifier.as_raw_window_handle()),
251 _ => unreachable!(),
252 }
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq, Type)]
258#[zvariant(signature = "s")]
259pub enum WindowIdentifierType {
260 X11(std::os::raw::c_ulong),
262 #[allow(dead_code)]
263 Wayland(String),
265}
266
267impl fmt::Display for WindowIdentifierType {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 match self {
270 Self::X11(xid) => f.write_str(&format!("x11:{xid:x}")),
271 Self::Wayland(handle) => f.write_str(&format!("wayland:{handle}")),
272 }
273 }
274}
275
276impl FromStr for WindowIdentifierType {
277 type Err = PortalError;
278 fn from_str(s: &str) -> Result<Self, Self::Err> {
279 let (kind, handle) = s
280 .split_once(':')
281 .ok_or_else(|| PortalError::InvalidArgument("Invalid Window Identifier".to_owned()))?;
282 match kind {
283 "x11" => {
284 let handle = handle.trim_start_matches("0x");
285 Ok(Self::X11(
286 std::os::raw::c_ulong::from_str_radix(handle, 16)
287 .map_err(|_| PortalError::InvalidArgument(format!("Wrong XID {handle}")))?,
288 ))
289 }
290 "wayland" => Ok(Self::Wayland(handle.to_owned())),
291 t => Err(PortalError::InvalidArgument(format!(
292 "Invalid Window Identifier type {t}",
293 ))),
294 }
295 }
296}
297
298impl<'de> Deserialize<'de> for WindowIdentifierType {
299 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
300 where
301 D: serde::Deserializer<'de>,
302 {
303 let handle = String::deserialize(deserializer)?;
304 Self::from_str(&handle)
305 .map_err(|e| serde::de::Error::custom(format!("Invalid Window identifier {e}")))
306 }
307}
308
309impl Serialize for WindowIdentifierType {
310 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
311 where
312 S: Serializer,
313 {
314 self.to_string().serialize(serializer)
315 }
316}
317
318impl WindowIdentifierType {
319 #[cfg(all(
337 any(feature = "gtk4_wayland", feature = "gtk4_x11"),
338 feature = "backend"
339 ))]
340 #[cfg_attr(
341 docsrs,
342 doc(cfg(all(
343 any(feature = "gtk4_wayland", feature = "gtk4_x11"),
344 feature = "backend"
345 )))
346 )]
347 pub fn set_parent_of(&self, window: &impl gtk::prelude::IsA<gtk::Window>) -> bool {
348 use gtk::prelude::*;
349
350 let window = window.as_ref();
351
352 let surface = match window.surface() {
353 Some(surface) => surface,
354 None => {
355 WidgetExt::realize(window);
356 window.surface().unwrap()
357 }
358 };
359
360 window.set_modal(true);
361
362 match self {
363 #[cfg(feature = "gtk4_x11")]
364 WindowIdentifierType::X11(xid) => {
365 use gdk4x11::{x11::xlib, X11Display, X11Surface};
366
367 let display = match WidgetExt::display(window).dynamic_cast::<X11Display>() {
368 Ok(display) => display,
369 Err(_) => {
370 #[cfg(feature = "tracing")]
371 tracing::warn!("Failed to get X11 display");
372 return false;
373 }
374 };
375 let surface = match surface.dynamic_cast::<X11Surface>() {
376 Ok(surface) => surface,
377 Err(_) => {
378 #[cfg(feature = "tracing")]
379 tracing::warn!("Failed to get X11 surface");
380 return false;
381 }
382 };
383 unsafe {
384 let xdisplay = display.xdisplay();
387 xlib::XSetTransientForHint(xdisplay, surface.xid(), *xid);
388 let net_wm_window_type_atom =
389 gdk4x11::x11_get_xatom_by_name_for_display(&display, "_NET_WM_WINDOW_TYPE");
390 let net_wm_window_type_dialog_atom = gdk4x11::x11_get_xatom_by_name_for_display(
391 &display,
392 "_NET_WM_WINDOW_DIALOG_TYPE",
393 );
394 let data: *const u8 = &(net_wm_window_type_dialog_atom as u8);
395 xlib::XChangeProperty(
396 xdisplay,
397 surface.xid(),
398 net_wm_window_type_atom,
399 xlib::XA_ATOM,
400 32,
401 xlib::PropModeReplace,
402 data,
403 1,
404 );
405 true
406 }
407 }
408 #[cfg(feature = "gtk4_wayland")]
409 WindowIdentifierType::Wayland(handle) => {
410 use gdk4wayland::WaylandToplevel;
411
412 let toplevel = match surface.dynamic_cast::<WaylandToplevel>() {
413 Ok(toplevel) => toplevel,
414 Err(_) => {
415 #[cfg(feature = "tracing")]
416 tracing::warn!("Failed to get toplevel from surface");
417 return false;
418 }
419 };
420 toplevel.set_transient_for_exported(handle)
421 }
422 #[cfg(not(all(feature = "gtk4_x11", feature = "gtk4_wayland")))]
423 _ => false,
424 }
425 }
426}
427
428#[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
429mod gtk4;
430
431#[cfg(any(feature = "gtk4_wayland", feature = "gtk4_x11"))]
432pub use self::gtk4::Gtk4WindowIdentifier;
433use crate::PortalError;
434
435#[cfg(feature = "wayland")]
436mod wayland;
437
438#[cfg(feature = "wayland")]
439pub use self::wayland::WaylandWindowIdentifier;
440
441#[cfg(test)]
442mod tests {
443 use std::str::FromStr;
444
445 use super::WindowIdentifier;
446 use crate::window_identifier::WindowIdentifierType;
447
448 #[test]
449 fn test_serialize() {
450 let x11 = WindowIdentifier::from_xid(1024);
451 assert_eq!(x11.to_string(), "x11:400");
452
453 assert_eq!(
454 WindowIdentifierType::from_str("x11:11432").unwrap(),
455 WindowIdentifierType::X11(70706)
456 );
457
458 assert_eq!(
462 WindowIdentifierType::from_str("x11:0x502a").unwrap(),
463 WindowIdentifierType::X11(20522)
464 );
465
466 assert_eq!(
467 WindowIdentifierType::from_str("wayland:Somerandomchars").unwrap(),
468 WindowIdentifierType::Wayland("Somerandomchars".to_owned())
469 );
470 assert!(WindowIdentifierType::from_str("some_handle").is_err());
471 assert!(WindowIdentifierType::from_str("some_type:some_handle").is_err());
472 }
473}