1use std::ffi::CString;
2use std::mem::replace;
3use std::num::NonZeroU32;
4use std::ops::Deref;
5use std::os::raw::*;
6use std::path::Path;
7use std::sync::{Arc, Mutex, MutexGuard};
8use std::{cmp, env};
9
10use tracing::{debug, info, warn};
11use x11rb::connection::{Connection, RequestConnection};
12use x11rb::properties::{WmHints, WmSizeHints, WmSizeHintsSpecification};
13use x11rb::protocol::shape::SK;
14use x11rb::protocol::sync::{ConnectionExt as _, Int64};
15use x11rb::protocol::xfixes::{ConnectionExt, RegionWrapper};
16use x11rb::protocol::xproto::{self, ConnectionExt as _, Rectangle};
17use x11rb::protocol::{randr, xinput};
18
19use super::util::{self, SelectedCursor};
20use super::{
21 ffi, ActiveEventLoop, CookieResultExt, ImeRequest, ImeSender, VoidCookie, WindowId, XConnection,
22};
23use crate::cursor::{Cursor, CustomCursor as RootCustomCursor};
24use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
25use crate::error::{NotSupportedError, RequestError};
26use crate::event::{Event, SurfaceSizeWriter, WindowEvent};
27use crate::event_loop::AsyncRequestSerial;
28use crate::platform::x11::WindowType;
29use crate::platform_impl::x11::atoms::*;
30use crate::platform_impl::x11::{
31 xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error,
32};
33use crate::platform_impl::{
34 common, Fullscreen, MonitorHandle as PlatformMonitorHandle, PlatformCustomCursor, PlatformIcon,
35 VideoModeHandle as PlatformVideoModeHandle,
36};
37use crate::window::{
38 CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
39 WindowAttributes, WindowButtons, WindowLevel,
40};
41
42pub(crate) struct Window(Arc<UnownedWindow>);
43
44impl Deref for Window {
45 type Target = UnownedWindow;
46
47 #[inline]
48 fn deref(&self) -> &UnownedWindow {
49 &self.0
50 }
51}
52
53impl Window {
54 pub(crate) fn new(
55 event_loop: &ActiveEventLoop,
56 attribs: WindowAttributes,
57 ) -> Result<Self, RequestError> {
58 let window = Arc::new(UnownedWindow::new(event_loop, attribs)?);
59 event_loop.windows.borrow_mut().insert(window.id(), Arc::downgrade(&window));
60 Ok(Window(window))
61 }
62}
63
64impl CoreWindow for Window {
65 fn id(&self) -> crate::window::WindowId {
66 crate::window::WindowId(self.0.id())
67 }
68
69 fn scale_factor(&self) -> f64 {
70 self.0.scale_factor()
71 }
72
73 fn request_redraw(&self) {
74 self.0.request_redraw()
75 }
76
77 fn pre_present_notify(&self) {
78 self.0.pre_present_notify()
79 }
80
81 fn reset_dead_keys(&self) {
82 common::xkb::reset_dead_keys();
83 }
84
85 fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
86 self.0.inner_position()
87 }
88
89 fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
90 self.0.outer_position()
91 }
92
93 fn set_outer_position(&self, position: Position) {
94 self.0.set_outer_position(position)
95 }
96
97 fn surface_size(&self) -> PhysicalSize<u32> {
98 self.0.surface_size()
99 }
100
101 fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
102 self.0.request_surface_size(size)
103 }
104
105 fn outer_size(&self) -> PhysicalSize<u32> {
106 self.0.outer_size()
107 }
108
109 fn set_min_surface_size(&self, min_size: Option<Size>) {
110 self.0.set_min_surface_size(min_size)
111 }
112
113 fn set_max_surface_size(&self, max_size: Option<Size>) {
114 self.0.set_max_surface_size(max_size)
115 }
116
117 fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
118 self.0.surface_resize_increments()
119 }
120
121 fn set_surface_resize_increments(&self, increments: Option<Size>) {
122 self.0.set_surface_resize_increments(increments)
123 }
124
125 fn set_title(&self, title: &str) {
126 self.0.set_title(title);
127 }
128
129 fn set_transparent(&self, transparent: bool) {
130 self.0.set_transparent(transparent);
131 }
132
133 fn set_blur(&self, blur: bool) {
134 self.0.set_blur(blur);
135 }
136
137 fn set_visible(&self, visible: bool) {
138 self.0.set_visible(visible);
139 }
140
141 fn is_visible(&self) -> Option<bool> {
142 self.0.is_visible()
143 }
144
145 fn set_resizable(&self, resizable: bool) {
146 self.0.set_resizable(resizable);
147 }
148
149 fn is_resizable(&self) -> bool {
150 self.0.is_resizable()
151 }
152
153 fn set_enabled_buttons(&self, buttons: WindowButtons) {
154 self.0.set_enabled_buttons(buttons)
155 }
156
157 fn enabled_buttons(&self) -> WindowButtons {
158 self.0.enabled_buttons()
159 }
160
161 fn set_minimized(&self, minimized: bool) {
162 self.0.set_minimized(minimized)
163 }
164
165 fn is_minimized(&self) -> Option<bool> {
166 self.0.is_minimized()
167 }
168
169 fn set_maximized(&self, maximized: bool) {
170 self.0.set_maximized(maximized)
171 }
172
173 fn is_maximized(&self) -> bool {
174 self.0.is_maximized()
175 }
176
177 fn set_fullscreen(&self, fullscreen: Option<crate::window::Fullscreen>) {
178 self.0.set_fullscreen(fullscreen.map(Into::into))
179 }
180
181 fn fullscreen(&self) -> Option<crate::window::Fullscreen> {
182 self.0.fullscreen().map(Into::into)
183 }
184
185 fn set_decorations(&self, decorations: bool) {
186 self.0.set_decorations(decorations);
187 }
188
189 fn is_decorated(&self) -> bool {
190 self.0.is_decorated()
191 }
192
193 fn set_window_level(&self, level: WindowLevel) {
194 self.0.set_window_level(level);
195 }
196
197 fn set_window_icon(&self, window_icon: Option<crate::window::Icon>) {
198 self.0.set_window_icon(window_icon.map(|inner| inner.inner))
199 }
200
201 fn set_ime_cursor_area(&self, position: Position, size: Size) {
202 self.0.set_ime_cursor_area(position, size);
203 }
204
205 fn set_ime_allowed(&self, allowed: bool) {
206 self.0.set_ime_allowed(allowed);
207 }
208
209 fn set_ime_purpose(&self, purpose: ImePurpose) {
210 self.0.set_ime_purpose(purpose);
211 }
212
213 fn focus_window(&self) {
214 self.0.focus_window();
215 }
216
217 fn has_focus(&self) -> bool {
218 self.0.has_focus()
219 }
220
221 fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
222 self.0.request_user_attention(request_type);
223 }
224
225 fn set_theme(&self, theme: Option<Theme>) {
226 self.0.set_theme(theme);
227 }
228
229 fn theme(&self) -> Option<Theme> {
230 self.0.theme()
231 }
232
233 fn set_content_protected(&self, protected: bool) {
234 self.0.set_content_protected(protected);
235 }
236
237 fn title(&self) -> String {
238 self.0.title()
239 }
240
241 fn set_cursor(&self, cursor: Cursor) {
242 self.0.set_cursor(cursor);
243 }
244
245 fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
246 self.0.set_cursor_position(position)
247 }
248
249 fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
250 self.0.set_cursor_grab(mode)
251 }
252
253 fn set_cursor_visible(&self, visible: bool) {
254 self.0.set_cursor_visible(visible);
255 }
256
257 fn drag_window(&self) -> Result<(), RequestError> {
258 self.0.drag_window()
259 }
260
261 fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
262 self.0.drag_resize_window(direction)
263 }
264
265 fn show_window_menu(&self, position: Position) {
266 self.0.show_window_menu(position);
267 }
268
269 fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
270 self.0.set_cursor_hittest(hittest)
271 }
272
273 fn current_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
274 self.0
275 .current_monitor()
276 .map(crate::platform_impl::MonitorHandle::X)
277 .map(|inner| crate::monitor::MonitorHandle { inner })
278 }
279
280 fn available_monitors(&self) -> Box<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
281 Box::new(
282 self.0
283 .available_monitors()
284 .into_iter()
285 .map(crate::platform_impl::MonitorHandle::X)
286 .map(|inner| crate::monitor::MonitorHandle { inner }),
287 )
288 }
289
290 fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
291 self.0
292 .primary_monitor()
293 .map(crate::platform_impl::MonitorHandle::X)
294 .map(|inner| crate::monitor::MonitorHandle { inner })
295 }
296
297 #[cfg(feature = "rwh_06")]
298 fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
299 self
300 }
301
302 #[cfg(feature = "rwh_06")]
303 fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
304 self
305 }
306}
307
308#[cfg(feature = "rwh_06")]
309impl rwh_06::HasDisplayHandle for Window {
310 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
311 let raw = self.0.raw_display_handle_rwh_06()?;
312 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
313 }
314}
315
316#[cfg(feature = "rwh_06")]
317impl rwh_06::HasWindowHandle for Window {
318 fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
319 let raw = self.0.raw_window_handle_rwh_06()?;
320 unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
321 }
322}
323
324impl Drop for Window {
325 fn drop(&mut self) {
326 let window = &self.0;
327 let xconn = &window.xconn;
328
329 if let Some(Fullscreen::Exclusive(_)) = window.fullscreen() {
331 window.set_fullscreen(None);
332 }
333
334 if let Ok(c) = xconn.xcb_connection().destroy_window(window.id().0 as xproto::Window) {
335 c.ignore_error();
336 }
337 }
338}
339
340#[derive(Debug)]
341pub struct SharedState {
342 pub cursor_pos: Option<(f64, f64)>,
343 pub size: Option<(u32, u32)>,
344 pub position: Option<(i32, i32)>,
345 pub inner_position: Option<(i32, i32)>,
346 pub inner_position_rel_parent: Option<(i32, i32)>,
347 pub is_resizable: bool,
348 pub is_decorated: bool,
349 pub last_monitor: X11MonitorHandle,
350 pub dpi_adjusted: Option<(u32, u32)>,
351 pub(crate) fullscreen: Option<Fullscreen>,
352 pub(crate) desired_fullscreen: Option<Option<Fullscreen>>,
354 pub restore_position: Option<(i32, i32)>,
356 pub desktop_video_mode: Option<(randr::Crtc, randr::Mode)>,
358 pub frame_extents: Option<util::FrameExtentsHeuristic>,
359 pub min_surface_size: Option<Size>,
360 pub max_surface_size: Option<Size>,
361 pub surface_resize_increments: Option<Size>,
362 pub base_size: Option<Size>,
363 pub visibility: Visibility,
364 pub has_focus: bool,
365 pub cursor_hittest: Option<bool>,
367}
368
369#[derive(Copy, Clone, Debug, Eq, PartialEq)]
370pub enum Visibility {
371 No,
372 Yes,
373 YesWait,
375}
376
377impl SharedState {
378 fn new(last_monitor: X11MonitorHandle, window_attributes: &WindowAttributes) -> Mutex<Self> {
379 let visibility =
380 if window_attributes.visible { Visibility::YesWait } else { Visibility::No };
381
382 Mutex::new(SharedState {
383 last_monitor,
384 visibility,
385
386 is_resizable: window_attributes.resizable,
387 is_decorated: window_attributes.decorations,
388 cursor_pos: None,
389 size: None,
390 position: None,
391 inner_position: None,
392 inner_position_rel_parent: None,
393 dpi_adjusted: None,
394 fullscreen: None,
395 desired_fullscreen: None,
396 restore_position: None,
397 desktop_video_mode: None,
398 frame_extents: None,
399 min_surface_size: None,
400 max_surface_size: None,
401 surface_resize_increments: None,
402 base_size: None,
403 has_focus: false,
404 cursor_hittest: None,
405 })
406 }
407}
408
409unsafe impl Send for UnownedWindow {}
410unsafe impl Sync for UnownedWindow {}
411
412pub struct UnownedWindow {
413 pub(crate) xconn: Arc<XConnection>, xwindow: xproto::Window, #[allow(dead_code)]
416 visual: u32, root: xproto::Window, #[allow(dead_code)]
419 screen_id: i32, sync_counter_id: Option<NonZeroU32>, selected_cursor: Mutex<SelectedCursor>,
422 cursor_grabbed_mode: Mutex<CursorGrabMode>,
423 #[allow(clippy::mutex_atomic)]
424 cursor_visible: Mutex<bool>,
425 ime_sender: Mutex<ImeSender>,
426 pub shared_state: Mutex<SharedState>,
427 redraw_sender: WakeSender<WindowId>,
428 activation_sender: WakeSender<super::ActivationToken>,
429}
430macro_rules! leap {
431 ($e:expr) => {
432 $e.map_err(|err| os_error!(err))?
433 };
434}
435
436impl UnownedWindow {
437 #[allow(clippy::unnecessary_cast)]
438 pub(crate) fn new(
439 event_loop: &ActiveEventLoop,
440 window_attrs: WindowAttributes,
441 ) -> Result<UnownedWindow, RequestError> {
442 let xconn = &event_loop.xconn;
443 let atoms = xconn.atoms();
444 #[cfg(feature = "rwh_06")]
445 let root = match window_attrs.parent_window.as_ref().map(|handle| handle.0) {
446 Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
447 Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(),
448 Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
449 None => event_loop.root,
450 };
451 #[cfg(not(feature = "rwh_06"))]
452 let root = event_loop.root;
453
454 let mut monitors = leap!(xconn.available_monitors());
455 let guessed_monitor = if monitors.is_empty() {
456 X11MonitorHandle::dummy()
457 } else {
458 xconn
459 .query_pointer(root, util::VIRTUAL_CORE_POINTER)
460 .ok()
461 .and_then(|pointer_state| {
462 let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
463
464 for i in 0..monitors.len() {
465 if monitors[i].rect.contains_point(x, y) {
466 return Some(monitors.swap_remove(i));
467 }
468 }
469
470 None
471 })
472 .unwrap_or_else(|| monitors.swap_remove(0))
473 };
474 let scale_factor = guessed_monitor.scale_factor();
475
476 info!("Guessed window scale factor: {}", scale_factor);
477
478 let max_surface_size: Option<(u32, u32)> =
479 window_attrs.max_surface_size.map(|size| size.to_physical::<u32>(scale_factor).into());
480 let min_surface_size: Option<(u32, u32)> =
481 window_attrs.min_surface_size.map(|size| size.to_physical::<u32>(scale_factor).into());
482
483 let position =
484 window_attrs.position.map(|position| position.to_physical::<i32>(scale_factor));
485
486 let dimensions = {
487 let mut dimensions: (u32, u32) = window_attrs
490 .surface_size
491 .map(|size| size.to_physical::<u32>(scale_factor))
492 .or_else(|| Some((800, 600).into()))
493 .map(Into::into)
494 .unwrap();
495 if let Some(max) = max_surface_size {
496 dimensions.0 = cmp::min(dimensions.0, max.0);
497 dimensions.1 = cmp::min(dimensions.1, max.1);
498 }
499 if let Some(min) = min_surface_size {
500 dimensions.0 = cmp::max(dimensions.0, min.0);
501 dimensions.1 = cmp::max(dimensions.1, min.1);
502 }
503 debug!("Calculated physical dimensions: {}x{}", dimensions.0, dimensions.1);
504 dimensions
505 };
506
507 let screen_id = match window_attrs.platform_specific.x11.screen_id {
508 Some(id) => id,
509 None => xconn.default_screen_index() as c_int,
510 };
511
512 let mut all_visuals = xconn
514 .xcb_connection()
515 .setup()
516 .roots
517 .iter()
518 .flat_map(|root| &root.allowed_depths)
519 .flat_map(|depth| depth.visuals.iter().map(move |visual| (visual, depth.depth)));
520
521 let (visualtype, depth, require_colormap) =
523 match window_attrs.platform_specific.x11.visual_id {
524 Some(vi) => {
525 let (visualtype, depth) = all_visuals
527 .find(|(visual, _)| visual.visual_id == vi)
528 .ok_or_else(|| os_error!(X11Error::NoSuchVisual(vi)))?;
529
530 (Some(visualtype), depth, true)
531 },
532 None if window_attrs.transparent => {
533 all_visuals
535 .find_map(|(visual, depth)| {
536 (depth == 32 && visual.class == xproto::VisualClass::TRUE_COLOR)
537 .then_some((Some(visual), depth, true))
538 })
539 .unwrap_or_else(|| {
540 debug!(
541 "Could not set transparency, because XMatchVisualInfo returned \
542 zero for the required parameters"
543 );
544 (None as _, x11rb::COPY_FROM_PARENT as _, false)
545 })
546 },
547 _ => (None, x11rb::COPY_FROM_PARENT as _, false),
548 };
549 let mut visual = visualtype.map_or(x11rb::COPY_FROM_PARENT, |v| v.visual_id);
550
551 let window_attributes = {
552 use xproto::EventMask;
553
554 let mut aux = xproto::CreateWindowAux::new();
555 let event_mask = EventMask::EXPOSURE
556 | EventMask::STRUCTURE_NOTIFY
557 | EventMask::VISIBILITY_CHANGE
558 | EventMask::KEY_PRESS
559 | EventMask::KEY_RELEASE
560 | EventMask::KEYMAP_STATE
561 | EventMask::BUTTON_PRESS
562 | EventMask::BUTTON_RELEASE
563 | EventMask::POINTER_MOTION
564 | EventMask::PROPERTY_CHANGE;
565
566 aux = aux.event_mask(event_mask).border_pixel(0);
567
568 if window_attrs.platform_specific.x11.override_redirect {
569 aux = aux.override_redirect(true as u32);
570 }
571
572 let colormap_visual = match window_attrs.platform_specific.x11.visual_id {
574 Some(vi) => Some(vi),
575 None if require_colormap => Some(visual),
576 _ => None,
577 };
578
579 if let Some(visual) = colormap_visual {
580 let colormap = leap!(xconn.xcb_connection().generate_id());
581 leap!(xconn.xcb_connection().create_colormap(
582 xproto::ColormapAlloc::NONE,
583 colormap,
584 root,
585 visual,
586 ));
587 aux = aux.colormap(colormap);
588 } else {
589 aux = aux.colormap(0);
590 }
591
592 aux
593 };
594
595 let parent = window_attrs.platform_specific.x11.embed_window.unwrap_or(root);
597
598 let xwindow = {
600 let (x, y) = position.map_or((0, 0), Into::into);
601 let wid = leap!(xconn.xcb_connection().generate_id());
602 let result = xconn.xcb_connection().create_window(
603 depth,
604 wid,
605 parent,
606 x,
607 y,
608 dimensions.0.try_into().unwrap(),
609 dimensions.1.try_into().unwrap(),
610 0,
611 xproto::WindowClass::INPUT_OUTPUT,
612 visual,
613 &window_attributes,
614 );
615 leap!(leap!(result).check());
616
617 wid
618 };
619
620 if visual == x11rb::COPY_FROM_PARENT {
624 visual = leap!(leap!(xconn
625 .xcb_connection()
626 .get_window_attributes(xwindow as xproto::Window))
627 .reply())
628 .visual;
629 }
630
631 #[allow(clippy::mutex_atomic)]
632 let mut window = UnownedWindow {
633 xconn: Arc::clone(xconn),
634 xwindow: xwindow as xproto::Window,
635 visual,
636 root,
637 screen_id,
638 sync_counter_id: None,
639 selected_cursor: Default::default(),
640 cursor_grabbed_mode: Mutex::new(CursorGrabMode::None),
641 cursor_visible: Mutex::new(true),
642 ime_sender: Mutex::new(event_loop.ime_sender.clone()),
643 shared_state: SharedState::new(guessed_monitor, &window_attrs),
644 redraw_sender: event_loop.redraw_sender.clone(),
645 activation_sender: event_loop.activation_sender.clone(),
646 };
647
648 leap!(window.set_title_inner(&window_attrs.title)).ignore_error();
652 leap!(window.set_decorations_inner(window_attrs.decorations)).ignore_error();
653
654 if let Some(theme) = window_attrs.preferred_theme {
655 leap!(window.set_theme_inner(Some(theme))).ignore_error();
656 }
657
658 if window_attrs.platform_specific.x11.embed_window.is_some() {
660 window.embed_window()?;
661 }
662
663 {
664 {
666 let dnd_aware_atom = atoms[XdndAware];
667 let version = &[5u32]; leap!(xconn.change_property(
669 window.xwindow,
670 dnd_aware_atom,
671 u32::from(xproto::AtomEnum::ATOM),
672 xproto::PropMode::REPLACE,
673 version,
674 ))
675 .ignore_error();
676 }
677
678 {
680 let (instance, class) = if let Some(name) = window_attrs.platform_specific.name {
681 (name.instance, name.general)
682 } else {
683 let class = env::args_os()
684 .next()
685 .as_ref()
686 .and_then(|path| Path::new(path).file_name())
688 .and_then(|bin_name| bin_name.to_str())
689 .map(|bin_name| bin_name.to_owned())
690 .unwrap_or_else(|| window_attrs.title.clone());
691 let instance = env::var("RESOURCE_NAME").ok().unwrap_or_else(|| class.clone());
693 (instance, class)
694 };
695
696 let class = format!("{instance}\0{class}\0");
697 leap!(xconn.change_property(
698 window.xwindow,
699 xproto::Atom::from(xproto::AtomEnum::WM_CLASS),
700 xproto::Atom::from(xproto::AtomEnum::STRING),
701 xproto::PropMode::REPLACE,
702 class.as_bytes(),
703 ))
704 .ignore_error();
705 }
706
707 if let Some(flusher) = leap!(window.set_pid()) {
708 flusher.ignore_error()
709 }
710
711 leap!(window.set_window_types(window_attrs.platform_specific.x11.x11_window_types))
712 .ignore_error();
713
714 let mut min_surface_size =
716 window_attrs.min_surface_size.map(|size| size.to_physical::<u32>(scale_factor));
717 let mut max_surface_size =
718 window_attrs.max_surface_size.map(|size| size.to_physical::<u32>(scale_factor));
719
720 if !window_attrs.resizable {
721 if util::wm_name_is_one_of(&["Xfwm4"]) {
722 warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
723 } else {
724 max_surface_size = Some(dimensions.into());
725 min_surface_size = Some(dimensions.into());
726 }
727 }
728
729 let shared_state = window.shared_state.get_mut().unwrap();
730 shared_state.min_surface_size = min_surface_size.map(Into::into);
731 shared_state.max_surface_size = max_surface_size.map(Into::into);
732 shared_state.surface_resize_increments = window_attrs.surface_resize_increments;
733 shared_state.base_size = window_attrs.platform_specific.x11.base_size;
734
735 let normal_hints = WmSizeHints {
736 position: position.map(|PhysicalPosition { x, y }| {
737 (WmSizeHintsSpecification::UserSpecified, x, y)
738 }),
739 size: Some((
740 WmSizeHintsSpecification::UserSpecified,
741 cast_dimension_to_hint(dimensions.0),
742 cast_dimension_to_hint(dimensions.1),
743 )),
744 max_size: max_surface_size.map(cast_physical_size_to_hint),
745 min_size: min_surface_size.map(cast_physical_size_to_hint),
746 size_increment: window_attrs
747 .surface_resize_increments
748 .map(|size| cast_size_to_hint(size, scale_factor)),
749 base_size: window_attrs
750 .platform_specific
751 .x11
752 .base_size
753 .map(|size| cast_size_to_hint(size, scale_factor)),
754 aspect: None,
755 win_gravity: None,
756 };
757 leap!(leap!(normal_hints.set(
758 xconn.xcb_connection(),
759 window.xwindow as xproto::Window,
760 xproto::AtomEnum::WM_NORMAL_HINTS,
761 ))
762 .check());
763
764 if let Some(icon) = window_attrs.window_icon {
766 leap!(window.set_icon_inner(icon.inner)).ignore_error();
767 }
768
769 let result = xconn.xcb_connection().change_property(
771 xproto::PropMode::REPLACE,
772 window.xwindow,
773 atoms[WM_PROTOCOLS],
774 xproto::AtomEnum::ATOM,
775 32,
776 3,
777 bytemuck::cast_slice::<xproto::Atom, u8>(&[
778 atoms[WM_DELETE_WINDOW],
779 atoms[_NET_WM_PING],
780 atoms[_NET_WM_SYNC_REQUEST],
781 ]),
782 );
783 leap!(result).ignore_error();
784
785 if leap!(xconn.xcb_connection().extension_information("SYNC")).is_some() {
787 let sync_counter_id = leap!(xconn.xcb_connection().generate_id());
788 window.sync_counter_id = NonZeroU32::new(sync_counter_id);
789
790 leap!(xconn
791 .xcb_connection()
792 .sync_create_counter(sync_counter_id, Int64::default()))
793 .ignore_error();
794
795 let result = xconn.xcb_connection().change_property(
796 xproto::PropMode::REPLACE,
797 window.xwindow,
798 atoms[_NET_WM_SYNC_REQUEST_COUNTER],
799 xproto::AtomEnum::CARDINAL,
800 32,
801 1,
802 bytemuck::cast_slice::<u32, u8>(&[sync_counter_id]),
803 );
804 leap!(result).ignore_error();
805 }
806
807 if window_attrs.visible {
809 leap!(xconn.xcb_connection().map_window(window.xwindow)).ignore_error();
810 leap!(xconn.xcb_connection().configure_window(
811 xwindow,
812 &xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE)
813 ))
814 .ignore_error();
815 }
816
817 unsafe {
819 let mut supported_ptr = ffi::False;
820 (xconn.xlib.XkbSetDetectableAutoRepeat)(
821 xconn.display,
822 ffi::True,
823 &mut supported_ptr,
824 );
825 if supported_ptr == ffi::False {
826 return Err(os_error!("`XkbSetDetectableAutoRepeat` failed").into());
827 }
828 }
829
830 let mask = xinput::XIEventMask::MOTION
832 | xinput::XIEventMask::BUTTON_PRESS
833 | xinput::XIEventMask::BUTTON_RELEASE
834 | xinput::XIEventMask::ENTER
835 | xinput::XIEventMask::LEAVE
836 | xinput::XIEventMask::FOCUS_IN
837 | xinput::XIEventMask::FOCUS_OUT
838 | xinput::XIEventMask::TOUCH_BEGIN
839 | xinput::XIEventMask::TOUCH_UPDATE
840 | xinput::XIEventMask::TOUCH_END;
841 leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
842 .ignore_error();
843
844 if let Some(ime) = event_loop.ime.as_ref() {
846 ime.borrow_mut()
847 .create_context(window.xwindow as ffi::Window, false)
848 .map_err(|err| os_error!(err))?;
849 }
850
851 if window_attrs.maximized {
853 leap!(window.set_maximized_inner(window_attrs.maximized)).ignore_error();
854 }
855
856 if window_attrs.fullscreen.is_some() {
857 if let Some(flusher) =
858 leap!(window
859 .set_fullscreen_inner(window_attrs.fullscreen.clone().map(Into::into)))
860 {
861 flusher.ignore_error()
862 }
863
864 if let Some(PhysicalPosition { x, y }) = position {
865 let shared_state = window.shared_state.get_mut().unwrap();
866
867 shared_state.restore_position = Some((x, y));
868 }
869 }
870
871 leap!(window.set_window_level_inner(window_attrs.window_level)).ignore_error();
872 }
873
874 window.set_cursor(window_attrs.cursor);
875
876 if let Some(startup) = window_attrs.platform_specific.activation_token.as_ref() {
878 leap!(xconn.remove_activation_token(xwindow, &startup._token));
879 }
880
881 let window = leap!(xconn.sync_with_server().map(|_| window));
883
884 Ok(window)
885 }
886
887 pub(super) fn embed_window(&self) -> Result<(), RequestError> {
889 let atoms = self.xconn.atoms();
890 leap!(leap!(self.xconn.change_property(
891 self.xwindow,
892 atoms[_XEMBED],
893 atoms[_XEMBED],
894 xproto::PropMode::REPLACE,
895 &[0u32, 1u32],
896 ))
897 .check());
898
899 Ok(())
900 }
901
902 pub(super) fn shared_state_lock(&self) -> MutexGuard<'_, SharedState> {
903 self.shared_state.lock().unwrap()
904 }
905
906 fn set_pid(&self) -> Result<Option<VoidCookie<'_>>, X11Error> {
907 let atoms = self.xconn.atoms();
908 let pid_atom = atoms[_NET_WM_PID];
909 let client_machine_atom = atoms[WM_CLIENT_MACHINE];
910
911 let uname = rustix::system::uname();
913 let pid = rustix::process::getpid();
914
915 self.xconn
916 .change_property(
917 self.xwindow,
918 pid_atom,
919 xproto::Atom::from(xproto::AtomEnum::CARDINAL),
920 xproto::PropMode::REPLACE,
921 &[pid.as_raw_nonzero().get() as util::Cardinal],
922 )?
923 .ignore_error();
924 let flusher = self.xconn.change_property(
925 self.xwindow,
926 client_machine_atom,
927 xproto::Atom::from(xproto::AtomEnum::STRING),
928 xproto::PropMode::REPLACE,
929 uname.nodename().to_bytes(),
930 );
931 flusher.map(Some)
932 }
933
934 fn set_window_types(&self, window_types: Vec<WindowType>) -> Result<VoidCookie<'_>, X11Error> {
935 let atoms = self.xconn.atoms();
936 let hint_atom = atoms[_NET_WM_WINDOW_TYPE];
937 let atoms: Vec<_> = window_types.iter().map(|t| t.as_atom(&self.xconn)).collect();
938
939 self.xconn.change_property(
940 self.xwindow,
941 hint_atom,
942 xproto::Atom::from(xproto::AtomEnum::ATOM),
943 xproto::PropMode::REPLACE,
944 &atoms,
945 )
946 }
947
948 pub fn set_theme_inner(&self, theme: Option<Theme>) -> Result<VoidCookie<'_>, X11Error> {
949 let atoms = self.xconn.atoms();
950 let hint_atom = atoms[_GTK_THEME_VARIANT];
951 let utf8_atom = atoms[UTF8_STRING];
952 let variant = match theme {
953 Some(Theme::Dark) => "dark",
954 Some(Theme::Light) => "light",
955 None => "dark",
956 };
957 let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
958 self.xconn.change_property(
959 self.xwindow,
960 hint_atom,
961 utf8_atom,
962 xproto::PropMode::REPLACE,
963 variant.as_bytes(),
964 )
965 }
966
967 #[inline]
968 pub fn set_theme(&self, theme: Option<Theme>) {
969 self.set_theme_inner(theme).expect("Failed to change window theme").ignore_error();
970
971 self.xconn.flush_requests().expect("Failed to change window theme");
972 }
973
974 fn set_netwm(
975 &self,
976 operation: util::StateOperation,
977 properties: (u32, u32, u32, u32),
978 ) -> Result<VoidCookie<'_>, X11Error> {
979 let atoms = self.xconn.atoms();
980 let state_atom = atoms[_NET_WM_STATE];
981 self.xconn.send_client_msg(
982 self.xwindow,
983 self.root,
984 state_atom,
985 Some(xproto::EventMask::SUBSTRUCTURE_REDIRECT | xproto::EventMask::SUBSTRUCTURE_NOTIFY),
986 [operation as u32, properties.0, properties.1, properties.2, properties.3],
987 )
988 }
989
990 fn set_fullscreen_hint(&self, fullscreen: bool) -> Result<VoidCookie<'_>, X11Error> {
991 let atoms = self.xconn.atoms();
992 let fullscreen_atom = atoms[_NET_WM_STATE_FULLSCREEN];
993 let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom, 0, 0, 0));
994
995 if fullscreen {
996 self.xconn
999 .xcb_connection()
1000 .set_input_focus(xproto::InputFocus::PARENT, self.xwindow, x11rb::CURRENT_TIME)?
1001 .ignore_error();
1002 }
1003
1004 flusher
1005 }
1006
1007 fn set_fullscreen_inner(
1008 &self,
1009 fullscreen: Option<Fullscreen>,
1010 ) -> Result<Option<VoidCookie<'_>>, X11Error> {
1011 let mut shared_state_lock = self.shared_state_lock();
1012
1013 match shared_state_lock.visibility {
1014 Visibility::No | Visibility::YesWait => {
1016 shared_state_lock.desired_fullscreen = Some(fullscreen);
1017 return Ok(None);
1018 },
1019 Visibility::Yes => (),
1020 }
1021
1022 let old_fullscreen = shared_state_lock.fullscreen.clone();
1023 if old_fullscreen == fullscreen {
1024 return Ok(None);
1025 }
1026 shared_state_lock.fullscreen.clone_from(&fullscreen);
1027
1028 match (&old_fullscreen, &fullscreen) {
1029 (&None, &Some(Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode))))
1034 | (
1035 &Some(Fullscreen::Borderless(_)),
1036 &Some(Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode))),
1037 ) => {
1038 let monitor = video_mode.monitor.as_ref().unwrap();
1039 shared_state_lock.desktop_video_mode = Some((
1040 monitor.id,
1041 self.xconn.get_crtc_mode(monitor.id).expect("Failed to get desktop video mode"),
1042 ));
1043 },
1044 (&Some(Fullscreen::Exclusive(_)), &None)
1046 | (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => {
1047 let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap();
1048 self.xconn
1049 .set_crtc_config(monitor_id, mode_id)
1050 .expect("failed to restore desktop video mode");
1051 },
1052 _ => (),
1053 }
1054
1055 drop(shared_state_lock);
1056
1057 match fullscreen {
1058 None => {
1059 let flusher = self.set_fullscreen_hint(false);
1060 let mut shared_state_lock = self.shared_state_lock();
1061 if let Some(position) = shared_state_lock.restore_position.take() {
1062 drop(shared_state_lock);
1063 self.set_position_inner(position.0, position.1)
1064 .expect_then_ignore_error("Failed to restore window position");
1065 }
1066 flusher.map(Some)
1067 },
1068 Some(fullscreen) => {
1069 let (video_mode, monitor) = match fullscreen {
1070 Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode)) => {
1071 (Some(video_mode), video_mode.monitor.clone().unwrap())
1072 },
1073 Fullscreen::Borderless(Some(PlatformMonitorHandle::X(monitor))) => {
1074 (None, monitor)
1075 },
1076 Fullscreen::Borderless(None) => {
1077 (None, self.shared_state_lock().last_monitor.clone())
1078 },
1079 #[cfg(wayland_platform)]
1080 _ => unreachable!(),
1081 };
1082
1083 if monitor.is_dummy() {
1085 return Ok(None);
1086 }
1087
1088 if let Some(video_mode) = video_mode {
1089 self.xconn
1115 .set_crtc_config(monitor.id, video_mode.native_mode)
1116 .expect("failed to set video mode");
1117 }
1118
1119 let window_position = self.outer_position_physical();
1120 self.shared_state_lock().restore_position = Some(window_position);
1121 let monitor_origin: (i32, i32) = monitor.position;
1122 self.set_position_inner(monitor_origin.0, monitor_origin.1)
1123 .expect_then_ignore_error("Failed to set window position");
1124 self.set_fullscreen_hint(true).map(Some)
1125 },
1126 }
1127 }
1128
1129 #[inline]
1130 pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
1131 let shared_state = self.shared_state_lock();
1132
1133 shared_state.desired_fullscreen.clone().unwrap_or_else(|| shared_state.fullscreen.clone())
1134 }
1135
1136 #[inline]
1137 pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
1138 if let Some(flusher) =
1139 self.set_fullscreen_inner(fullscreen).expect("Failed to change window fullscreen state")
1140 {
1141 flusher.check().expect("Failed to change window fullscreen state");
1142 self.invalidate_cached_frame_extents();
1143 }
1144 }
1145
1146 pub(crate) fn visibility_notify(&self) {
1148 let mut shared_state = self.shared_state_lock();
1149
1150 match shared_state.visibility {
1151 Visibility::No => self
1152 .xconn
1153 .xcb_connection()
1154 .unmap_window(self.xwindow)
1155 .expect_then_ignore_error("Failed to unmap window"),
1156 Visibility::Yes => (),
1157 Visibility::YesWait => {
1158 shared_state.visibility = Visibility::Yes;
1159
1160 if let Some(fullscreen) = shared_state.desired_fullscreen.take() {
1161 drop(shared_state);
1162 self.set_fullscreen(fullscreen);
1163 }
1164 },
1165 }
1166 }
1167
1168 pub fn current_monitor(&self) -> Option<X11MonitorHandle> {
1169 Some(self.shared_state_lock().last_monitor.clone())
1170 }
1171
1172 pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
1173 self.xconn.available_monitors().expect("Failed to get available monitors")
1174 }
1175
1176 pub fn primary_monitor(&self) -> Option<X11MonitorHandle> {
1177 Some(self.xconn.primary_monitor().expect("Failed to get primary monitor"))
1178 }
1179
1180 #[inline]
1181 pub fn is_minimized(&self) -> Option<bool> {
1182 let atoms = self.xconn.atoms();
1183 let state_atom = atoms[_NET_WM_STATE];
1184 let state = self.xconn.get_property(
1185 self.xwindow,
1186 state_atom,
1187 xproto::Atom::from(xproto::AtomEnum::ATOM),
1188 );
1189 let hidden_atom = atoms[_NET_WM_STATE_HIDDEN];
1190
1191 Some(match state {
1192 Ok(atoms) => {
1193 atoms.iter().any(|atom: &xproto::Atom| *atom as xproto::Atom == hidden_atom)
1194 },
1195 _ => false,
1196 })
1197 }
1198
1199 #[inline]
1201 pub(super) fn refresh_dpi_for_monitor(
1202 &self,
1203 new_monitor: &X11MonitorHandle,
1204 maybe_prev_scale_factor: Option<f64>,
1205 mut callback: impl FnMut(Event),
1206 ) {
1207 let monitor = self.shared_state_lock().last_monitor.clone();
1209 if monitor.name == new_monitor.name {
1210 let (width, height) = self.surface_size_physical();
1211 let (new_width, new_height) = self.adjust_for_dpi(
1212 maybe_prev_scale_factor.unwrap_or(monitor.scale_factor),
1217 new_monitor.scale_factor,
1218 width,
1219 height,
1220 &self.shared_state_lock(),
1221 );
1222
1223 let window_id = crate::window::WindowId(self.id());
1224 let old_surface_size = PhysicalSize::new(width, height);
1225 let surface_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height)));
1226 callback(Event::WindowEvent {
1227 window_id,
1228 event: WindowEvent::ScaleFactorChanged {
1229 scale_factor: new_monitor.scale_factor,
1230 surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&surface_size)),
1231 },
1232 });
1233
1234 let new_surface_size = *surface_size.lock().unwrap();
1235 drop(surface_size);
1236
1237 if new_surface_size != old_surface_size {
1238 let (new_width, new_height) = new_surface_size.into();
1239 self.request_surface_size_physical(new_width, new_height);
1240 }
1241 }
1242 }
1243
1244 fn set_minimized_inner(&self, minimized: bool) -> Result<VoidCookie<'_>, X11Error> {
1245 let atoms = self.xconn.atoms();
1246
1247 if minimized {
1248 let root_window = self.xconn.default_root().root;
1249
1250 self.xconn.send_client_msg(
1251 self.xwindow,
1252 root_window,
1253 atoms[WM_CHANGE_STATE],
1254 Some(
1255 xproto::EventMask::SUBSTRUCTURE_REDIRECT
1256 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
1257 ),
1258 [3u32, 0, 0, 0, 0],
1259 )
1260 } else {
1261 self.xconn.send_client_msg(
1262 self.xwindow,
1263 self.root,
1264 atoms[_NET_ACTIVE_WINDOW],
1265 Some(
1266 xproto::EventMask::SUBSTRUCTURE_REDIRECT
1267 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
1268 ),
1269 [1, x11rb::CURRENT_TIME, 0, 0, 0],
1270 )
1271 }
1272 }
1273
1274 #[inline]
1275 pub fn set_minimized(&self, minimized: bool) {
1276 self.set_minimized_inner(minimized)
1277 .expect_then_ignore_error("Failed to change window minimization");
1278
1279 self.xconn.flush_requests().expect("Failed to change window minimization");
1280 }
1281
1282 #[inline]
1283 pub fn is_maximized(&self) -> bool {
1284 let atoms = self.xconn.atoms();
1285 let state_atom = atoms[_NET_WM_STATE];
1286 let state = self.xconn.get_property(
1287 self.xwindow,
1288 state_atom,
1289 xproto::Atom::from(xproto::AtomEnum::ATOM),
1290 );
1291 let horz_atom = atoms[_NET_WM_STATE_MAXIMIZED_HORZ];
1292 let vert_atom = atoms[_NET_WM_STATE_MAXIMIZED_VERT];
1293 match state {
1294 Ok(atoms) => {
1295 let horz_maximized = atoms.iter().any(|atom: &xproto::Atom| *atom == horz_atom);
1296 let vert_maximized = atoms.iter().any(|atom: &xproto::Atom| *atom == vert_atom);
1297 horz_maximized && vert_maximized
1298 },
1299 _ => false,
1300 }
1301 }
1302
1303 fn set_maximized_inner(&self, maximized: bool) -> Result<VoidCookie<'_>, X11Error> {
1304 let atoms = self.xconn.atoms();
1305 let horz_atom = atoms[_NET_WM_STATE_MAXIMIZED_HORZ];
1306 let vert_atom = atoms[_NET_WM_STATE_MAXIMIZED_VERT];
1307
1308 self.set_netwm(maximized.into(), (horz_atom, vert_atom, 0, 0))
1309 }
1310
1311 #[inline]
1312 pub fn set_maximized(&self, maximized: bool) {
1313 self.set_maximized_inner(maximized)
1314 .expect_then_ignore_error("Failed to change window maximization");
1315 self.xconn.flush_requests().expect("Failed to change window maximization");
1316 self.invalidate_cached_frame_extents();
1317 }
1318
1319 fn set_title_inner(&self, title: &str) -> Result<VoidCookie<'_>, X11Error> {
1320 let atoms = self.xconn.atoms();
1321
1322 let title = CString::new(title).expect("Window title contained null byte");
1323 self.xconn
1324 .change_property(
1325 self.xwindow,
1326 xproto::Atom::from(xproto::AtomEnum::WM_NAME),
1327 xproto::Atom::from(xproto::AtomEnum::STRING),
1328 xproto::PropMode::REPLACE,
1329 title.as_bytes(),
1330 )?
1331 .ignore_error();
1332 self.xconn.change_property(
1333 self.xwindow,
1334 atoms[_NET_WM_NAME],
1335 atoms[UTF8_STRING],
1336 xproto::PropMode::REPLACE,
1337 title.as_bytes(),
1338 )
1339 }
1340
1341 #[inline]
1342 pub fn set_title(&self, title: &str) {
1343 self.set_title_inner(title).expect_then_ignore_error("Failed to set window title");
1344
1345 self.xconn.flush_requests().expect("Failed to set window title");
1346 }
1347
1348 #[inline]
1349 pub fn set_transparent(&self, _transparent: bool) {}
1350
1351 #[inline]
1352 pub fn set_blur(&self, _blur: bool) {}
1353
1354 fn set_decorations_inner(&self, decorations: bool) -> Result<VoidCookie<'_>, X11Error> {
1355 self.shared_state_lock().is_decorated = decorations;
1356 let mut hints = self.xconn.get_motif_hints(self.xwindow);
1357
1358 hints.set_decorations(decorations);
1359
1360 self.xconn.set_motif_hints(self.xwindow, &hints)
1361 }
1362
1363 #[inline]
1364 pub fn set_decorations(&self, decorations: bool) {
1365 self.set_decorations_inner(decorations)
1366 .expect_then_ignore_error("Failed to set decoration state");
1367 self.xconn.flush_requests().expect("Failed to set decoration state");
1368 self.invalidate_cached_frame_extents();
1369 }
1370
1371 #[inline]
1372 pub fn is_decorated(&self) -> bool {
1373 self.shared_state_lock().is_decorated
1374 }
1375
1376 fn set_maximizable_inner(&self, maximizable: bool) -> Result<VoidCookie<'_>, X11Error> {
1377 let mut hints = self.xconn.get_motif_hints(self.xwindow);
1378
1379 hints.set_maximizable(maximizable);
1380
1381 self.xconn.set_motif_hints(self.xwindow, &hints)
1382 }
1383
1384 fn toggle_atom(&self, atom_name: AtomName, enable: bool) -> Result<VoidCookie<'_>, X11Error> {
1385 let atoms = self.xconn.atoms();
1386 let atom = atoms[atom_name];
1387 self.set_netwm(enable.into(), (atom, 0, 0, 0))
1388 }
1389
1390 fn set_window_level_inner(&self, level: WindowLevel) -> Result<VoidCookie<'_>, X11Error> {
1391 self.toggle_atom(_NET_WM_STATE_ABOVE, level == WindowLevel::AlwaysOnTop)?.ignore_error();
1392 self.toggle_atom(_NET_WM_STATE_BELOW, level == WindowLevel::AlwaysOnBottom)
1393 }
1394
1395 #[inline]
1396 pub fn set_window_level(&self, level: WindowLevel) {
1397 self.set_window_level_inner(level)
1398 .expect_then_ignore_error("Failed to set window-level state");
1399 self.xconn.flush_requests().expect("Failed to set window-level state");
1400 }
1401
1402 fn set_icon_inner(&self, icon: PlatformIcon) -> Result<VoidCookie<'_>, X11Error> {
1403 let atoms = self.xconn.atoms();
1404 let icon_atom = atoms[_NET_WM_ICON];
1405 let data = icon.to_cardinals();
1406 self.xconn.change_property(
1407 self.xwindow,
1408 icon_atom,
1409 xproto::Atom::from(xproto::AtomEnum::CARDINAL),
1410 xproto::PropMode::REPLACE,
1411 data.as_slice(),
1412 )
1413 }
1414
1415 fn unset_icon_inner(&self) -> Result<VoidCookie<'_>, X11Error> {
1416 let atoms = self.xconn.atoms();
1417 let icon_atom = atoms[_NET_WM_ICON];
1418 let empty_data: [util::Cardinal; 0] = [];
1419 self.xconn.change_property(
1420 self.xwindow,
1421 icon_atom,
1422 xproto::Atom::from(xproto::AtomEnum::CARDINAL),
1423 xproto::PropMode::REPLACE,
1424 &empty_data,
1425 )
1426 }
1427
1428 #[inline]
1429 pub(crate) fn set_window_icon(&self, icon: Option<PlatformIcon>) {
1430 match icon {
1431 Some(icon) => self.set_icon_inner(icon),
1432 None => self.unset_icon_inner(),
1433 }
1434 .expect_then_ignore_error("Failed to set icons");
1435
1436 self.xconn.flush_requests().expect("Failed to set icons");
1437 }
1438
1439 #[inline]
1440 pub fn set_visible(&self, visible: bool) {
1441 let mut shared_state = self.shared_state_lock();
1442
1443 match (visible, shared_state.visibility) {
1444 (true, Visibility::Yes) | (true, Visibility::YesWait) | (false, Visibility::No) => {
1445 return
1446 },
1447 _ => (),
1448 }
1449
1450 if visible {
1451 self.xconn
1452 .xcb_connection()
1453 .map_window(self.xwindow)
1454 .expect_then_ignore_error("Failed to call `xcb_map_window`");
1455 self.xconn
1456 .xcb_connection()
1457 .configure_window(
1458 self.xwindow,
1459 &xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE),
1460 )
1461 .expect_then_ignore_error("Failed to call `xcb_configure_window`");
1462 self.xconn.flush_requests().expect("Failed to call XMapRaised");
1463 shared_state.visibility = Visibility::YesWait;
1464 } else {
1465 self.xconn
1466 .xcb_connection()
1467 .unmap_window(self.xwindow)
1468 .expect_then_ignore_error("Failed to call `xcb_unmap_window`");
1469 self.xconn.flush_requests().expect("Failed to call XUnmapWindow");
1470 shared_state.visibility = Visibility::No;
1471 }
1472 }
1473
1474 #[inline]
1475 pub fn is_visible(&self) -> Option<bool> {
1476 Some(self.shared_state_lock().visibility == Visibility::Yes)
1477 }
1478
1479 fn update_cached_frame_extents(&self) {
1480 let extents = self.xconn.get_frame_extents_heuristic(self.xwindow, self.root);
1481 self.shared_state_lock().frame_extents = Some(extents);
1482 }
1483
1484 pub(crate) fn invalidate_cached_frame_extents(&self) {
1485 self.shared_state_lock().frame_extents.take();
1486 }
1487
1488 pub(crate) fn outer_position_physical(&self) -> (i32, i32) {
1489 let extents = self.shared_state_lock().frame_extents.clone();
1490 if let Some(extents) = extents {
1491 let (x, y) = self.inner_position_physical();
1492 extents.inner_pos_to_outer(x, y)
1493 } else {
1494 self.update_cached_frame_extents();
1495 self.outer_position_physical()
1496 }
1497 }
1498
1499 #[inline]
1500 pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
1501 let extents = self.shared_state_lock().frame_extents.clone();
1502 if let Some(extents) = extents {
1503 let (x, y) = self.inner_position_physical();
1504 Ok(extents.inner_pos_to_outer(x, y).into())
1505 } else {
1506 self.update_cached_frame_extents();
1507 self.outer_position()
1508 }
1509 }
1510
1511 pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
1512 self.xconn
1515 .translate_coords(self.xwindow, self.root)
1516 .map(|coords| (coords.dst_x.into(), coords.dst_y.into()))
1517 .unwrap()
1518 }
1519
1520 #[inline]
1521 pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
1522 Ok(self.inner_position_physical().into())
1523 }
1524
1525 pub(crate) fn set_position_inner(
1526 &self,
1527 mut x: i32,
1528 mut y: i32,
1529 ) -> Result<VoidCookie<'_>, X11Error> {
1530 if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
1533 let extents = self.shared_state_lock().frame_extents.clone();
1534 if let Some(extents) = extents {
1535 x += cast_dimension_to_hint(extents.frame_extents.left);
1536 y += cast_dimension_to_hint(extents.frame_extents.top);
1537 } else {
1538 self.update_cached_frame_extents();
1539 return self.set_position_inner(x, y);
1540 }
1541 }
1542
1543 self.xconn
1544 .xcb_connection()
1545 .configure_window(self.xwindow, &xproto::ConfigureWindowAux::new().x(x).y(y))
1546 .map_err(Into::into)
1547 }
1548
1549 pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
1550 self.set_position_inner(x, y).expect_then_ignore_error("Failed to call `XMoveWindow`");
1551 }
1552
1553 #[inline]
1554 pub fn set_outer_position(&self, position: Position) {
1555 let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
1556 self.set_position_physical(x, y);
1557 }
1558
1559 pub(crate) fn surface_size_physical(&self) -> (u32, u32) {
1560 self.xconn
1563 .get_geometry(self.xwindow)
1564 .map(|geo| (geo.width.into(), geo.height.into()))
1565 .unwrap()
1566 }
1567
1568 #[inline]
1569 pub fn surface_size(&self) -> PhysicalSize<u32> {
1570 self.surface_size_physical().into()
1571 }
1572
1573 #[inline]
1574 pub fn outer_size(&self) -> PhysicalSize<u32> {
1575 let extents = self.shared_state_lock().frame_extents.clone();
1576 if let Some(extents) = extents {
1577 let (width, height) = self.surface_size_physical();
1578 extents.surface_size_to_outer(width, height).into()
1579 } else {
1580 self.update_cached_frame_extents();
1581 self.outer_size()
1582 }
1583 }
1584
1585 pub(crate) fn request_surface_size_physical(&self, width: u32, height: u32) {
1586 self.xconn
1587 .xcb_connection()
1588 .configure_window(
1589 self.xwindow,
1590 &xproto::ConfigureWindowAux::new().width(width).height(height),
1591 )
1592 .expect_then_ignore_error("Failed to call `xcb_configure_window`");
1593 self.xconn.flush_requests().expect("Failed to call XResizeWindow");
1594 if self.shared_state_lock().cursor_hittest.unwrap_or(false) {
1596 let _ = self.set_cursor_hittest(true);
1597 }
1598 }
1599
1600 #[inline]
1601 pub fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
1602 let scale_factor = self.scale_factor();
1603 let size = size.to_physical::<u32>(scale_factor).into();
1604 if !self.shared_state_lock().is_resizable {
1605 self.update_normal_hints(|normal_hints| {
1606 normal_hints.min_size = Some(size);
1607 normal_hints.max_size = Some(size);
1608 })
1609 .expect("Failed to call `XSetWMNormalHints`");
1610 }
1611 self.request_surface_size_physical(size.0 as u32, size.1 as u32);
1612
1613 None
1614 }
1615
1616 fn update_normal_hints<F>(&self, callback: F) -> Result<(), X11Error>
1617 where
1618 F: FnOnce(&mut WmSizeHints),
1619 {
1620 let mut normal_hints = WmSizeHints::get(
1621 self.xconn.xcb_connection(),
1622 self.xwindow as xproto::Window,
1623 xproto::AtomEnum::WM_NORMAL_HINTS,
1624 )?
1625 .reply()?
1626 .unwrap_or_default();
1627 callback(&mut normal_hints);
1628 normal_hints
1629 .set(
1630 self.xconn.xcb_connection(),
1631 self.xwindow as xproto::Window,
1632 xproto::AtomEnum::WM_NORMAL_HINTS,
1633 )?
1634 .ignore_error();
1635 Ok(())
1636 }
1637
1638 pub(crate) fn set_min_surface_size_physical(&self, dimensions: Option<(u32, u32)>) {
1639 self.update_normal_hints(|normal_hints| {
1640 normal_hints.min_size =
1641 dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
1642 })
1643 .expect("Failed to call `XSetWMNormalHints`");
1644 }
1645
1646 #[inline]
1647 pub fn set_min_surface_size(&self, dimensions: Option<Size>) {
1648 self.shared_state_lock().min_surface_size = dimensions;
1649 let physical_dimensions =
1650 dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
1651 self.set_min_surface_size_physical(physical_dimensions);
1652 }
1653
1654 pub(crate) fn set_max_surface_size_physical(&self, dimensions: Option<(u32, u32)>) {
1655 self.update_normal_hints(|normal_hints| {
1656 normal_hints.max_size =
1657 dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
1658 })
1659 .expect("Failed to call `XSetWMNormalHints`");
1660 }
1661
1662 #[inline]
1663 pub fn set_max_surface_size(&self, dimensions: Option<Size>) {
1664 self.shared_state_lock().max_surface_size = dimensions;
1665 let physical_dimensions =
1666 dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
1667 self.set_max_surface_size_physical(physical_dimensions);
1668 }
1669
1670 #[inline]
1671 pub fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
1672 WmSizeHints::get(
1673 self.xconn.xcb_connection(),
1674 self.xwindow as xproto::Window,
1675 xproto::AtomEnum::WM_NORMAL_HINTS,
1676 )
1677 .ok()
1678 .and_then(|cookie| cookie.reply().ok())
1679 .flatten()
1680 .and_then(|hints| hints.size_increment)
1681 .map(|(width, height)| (width as u32, height as u32).into())
1682 }
1683
1684 #[inline]
1685 pub fn set_surface_resize_increments(&self, increments: Option<Size>) {
1686 self.shared_state_lock().surface_resize_increments = increments;
1687 let physical_increments =
1688 increments.map(|increments| cast_size_to_hint(increments, self.scale_factor()));
1689 self.update_normal_hints(|hints| hints.size_increment = physical_increments)
1690 .expect("Failed to call `XSetWMNormalHints`");
1691 }
1692
1693 pub(crate) fn adjust_for_dpi(
1694 &self,
1695 old_scale_factor: f64,
1696 new_scale_factor: f64,
1697 width: u32,
1698 height: u32,
1699 shared_state: &SharedState,
1700 ) -> (u32, u32) {
1701 let scale_factor = new_scale_factor / old_scale_factor;
1702 self.update_normal_hints(|normal_hints| {
1703 let dpi_adjuster = |size: Size| -> (i32, i32) { cast_size_to_hint(size, scale_factor) };
1704 let max_size = shared_state.max_surface_size.map(dpi_adjuster);
1705 let min_size = shared_state.min_surface_size.map(dpi_adjuster);
1706 let surface_resize_increments =
1707 shared_state.surface_resize_increments.map(dpi_adjuster);
1708 let base_size = shared_state.base_size.map(dpi_adjuster);
1709
1710 normal_hints.max_size = max_size;
1711 normal_hints.min_size = min_size;
1712 normal_hints.size_increment = surface_resize_increments;
1713 normal_hints.base_size = base_size;
1714 })
1715 .expect("Failed to update normal hints");
1716
1717 let new_width = (width as f64 * scale_factor).round() as u32;
1718 let new_height = (height as f64 * scale_factor).round() as u32;
1719
1720 (new_width, new_height)
1721 }
1722
1723 pub fn set_resizable(&self, resizable: bool) {
1724 if util::wm_name_is_one_of(&["Xfwm4"]) {
1725 warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
1730 return;
1731 }
1732
1733 let (min_size, max_size) = if resizable {
1734 let shared_state_lock = self.shared_state_lock();
1735 (shared_state_lock.min_surface_size, shared_state_lock.max_surface_size)
1736 } else {
1737 let window_size = Some(Size::from(self.surface_size()));
1738 (window_size, window_size)
1739 };
1740 self.shared_state_lock().is_resizable = resizable;
1741
1742 self.set_maximizable_inner(resizable)
1743 .expect_then_ignore_error("Failed to call `XSetWMNormalHints`");
1744
1745 let scale_factor = self.scale_factor();
1746 let min_surface_size = min_size.map(|size| cast_size_to_hint(size, scale_factor));
1747 let max_surface_size = max_size.map(|size| cast_size_to_hint(size, scale_factor));
1748 self.update_normal_hints(|normal_hints| {
1749 normal_hints.min_size = min_surface_size;
1750 normal_hints.max_size = max_surface_size;
1751 })
1752 .expect("Failed to call `XSetWMNormalHints`");
1753 }
1754
1755 #[inline]
1756 pub fn is_resizable(&self) -> bool {
1757 self.shared_state_lock().is_resizable
1758 }
1759
1760 #[inline]
1761 pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
1762
1763 #[inline]
1764 pub fn enabled_buttons(&self) -> WindowButtons {
1765 WindowButtons::all()
1766 }
1767
1768 #[allow(dead_code)]
1769 #[inline]
1770 pub fn xlib_display(&self) -> *mut c_void {
1771 self.xconn.display as _
1772 }
1773
1774 #[allow(dead_code)]
1775 #[inline]
1776 pub fn xlib_window(&self) -> c_ulong {
1777 self.xwindow as ffi::Window
1778 }
1779
1780 #[inline]
1781 pub fn set_cursor(&self, cursor: Cursor) {
1782 match cursor {
1783 Cursor::Icon(icon) => {
1784 let old_cursor = replace(
1785 &mut *self.selected_cursor.lock().unwrap(),
1786 SelectedCursor::Named(icon),
1787 );
1788
1789 #[allow(clippy::mutex_atomic)]
1790 if SelectedCursor::Named(icon) != old_cursor && *self.cursor_visible.lock().unwrap()
1791 {
1792 if let Err(err) = self.xconn.set_cursor_icon(self.xwindow, Some(icon)) {
1793 tracing::error!("failed to set cursor icon: {err}");
1794 }
1795 }
1796 },
1797 Cursor::Custom(RootCustomCursor { inner: PlatformCustomCursor::X(cursor) }) => {
1798 #[allow(clippy::mutex_atomic)]
1799 if *self.cursor_visible.lock().unwrap() {
1800 if let Err(err) = self.xconn.set_custom_cursor(self.xwindow, &cursor) {
1801 tracing::error!("failed to set window icon: {err}");
1802 }
1803 }
1804
1805 *self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(cursor);
1806 },
1807 #[cfg(wayland_platform)]
1808 Cursor::Custom(RootCustomCursor { inner: PlatformCustomCursor::Wayland(_) }) => {
1809 tracing::error!("passed a Wayland cursor to X11 backend")
1810 },
1811 }
1812 }
1813
1814 #[inline]
1815 pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
1816 let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
1817 if mode == *grabbed_lock {
1818 return Ok(());
1819 }
1820
1821 self.xconn
1824 .xcb_connection()
1825 .ungrab_pointer(x11rb::CURRENT_TIME)
1826 .expect_then_ignore_error("Failed to call `xcb_ungrab_pointer`");
1827
1828 let result = match mode {
1829 CursorGrabMode::None => self
1830 .xconn
1831 .flush_requests()
1832 .map_err(|err| RequestError::Os(os_error!(X11Error::Xlib(err)))),
1833 CursorGrabMode::Confined => {
1834 let result = {
1835 self.xconn
1836 .xcb_connection()
1837 .grab_pointer(
1838 true as _,
1839 self.xwindow,
1840 xproto::EventMask::BUTTON_PRESS
1841 | xproto::EventMask::BUTTON_RELEASE
1842 | xproto::EventMask::ENTER_WINDOW
1843 | xproto::EventMask::LEAVE_WINDOW
1844 | xproto::EventMask::POINTER_MOTION
1845 | xproto::EventMask::POINTER_MOTION_HINT
1846 | xproto::EventMask::BUTTON1_MOTION
1847 | xproto::EventMask::BUTTON2_MOTION
1848 | xproto::EventMask::BUTTON3_MOTION
1849 | xproto::EventMask::BUTTON4_MOTION
1850 | xproto::EventMask::BUTTON5_MOTION
1851 | xproto::EventMask::KEYMAP_STATE,
1852 xproto::GrabMode::ASYNC,
1853 xproto::GrabMode::ASYNC,
1854 self.xwindow,
1855 0u32,
1856 x11rb::CURRENT_TIME,
1857 )
1858 .expect("Failed to call `grab_pointer`")
1859 .reply()
1860 .expect("Failed to receive reply from `grab_pointer`")
1861 };
1862
1863 match result.status {
1864 xproto::GrabStatus::SUCCESS => Ok(()),
1865 xproto::GrabStatus::ALREADY_GRABBED => {
1866 Err("Cursor could not be confined: already confined by another client")
1867 },
1868 xproto::GrabStatus::INVALID_TIME => {
1869 Err("Cursor could not be confined: invalid time")
1870 },
1871 xproto::GrabStatus::NOT_VIEWABLE => {
1872 Err("Cursor could not be confined: confine location not viewable")
1873 },
1874 xproto::GrabStatus::FROZEN => {
1875 Err("Cursor could not be confined: frozen by another client")
1876 },
1877 _ => unreachable!(),
1878 }
1879 .map_err(|err| RequestError::Os(os_error!(err)))
1880 },
1881 CursorGrabMode::Locked => {
1882 return Err(
1883 NotSupportedError::new("locked cursor is not implemented on X11").into()
1884 );
1885 },
1886 };
1887
1888 if result.is_ok() {
1889 *grabbed_lock = mode;
1890 }
1891
1892 result
1893 }
1894
1895 #[inline]
1896 pub fn set_cursor_visible(&self, visible: bool) {
1897 #[allow(clippy::mutex_atomic)]
1898 let mut visible_lock = self.cursor_visible.lock().unwrap();
1899 if visible == *visible_lock {
1900 return;
1901 }
1902 let cursor =
1903 if visible { Some((*self.selected_cursor.lock().unwrap()).clone()) } else { None };
1904 *visible_lock = visible;
1905 drop(visible_lock);
1906 let result = match cursor {
1907 Some(SelectedCursor::Custom(cursor)) => {
1908 self.xconn.set_custom_cursor(self.xwindow, &cursor)
1909 },
1910 Some(SelectedCursor::Named(cursor)) => {
1911 self.xconn.set_cursor_icon(self.xwindow, Some(cursor))
1912 },
1913 None => self.xconn.set_cursor_icon(self.xwindow, None),
1914 };
1915
1916 if let Err(err) = result {
1917 tracing::error!("failed to set cursor icon: {err}");
1918 }
1919 }
1920
1921 #[inline]
1922 pub fn scale_factor(&self) -> f64 {
1923 self.shared_state_lock().last_monitor.scale_factor
1924 }
1925
1926 pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), RequestError> {
1927 self.xconn
1928 .xcb_connection()
1929 .warp_pointer(x11rb::NONE, self.xwindow, 0, 0, 0, 0, x as _, y as _)
1930 .map_err(|err| os_error!(X11Error::from(err)))?;
1931 self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
1932 Ok(())
1933 }
1934
1935 #[inline]
1936 pub fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
1937 let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
1938 self.set_cursor_position_physical(x, y)
1939 }
1940
1941 #[inline]
1942 pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
1943 let mut rectangles: Vec<Rectangle> = Vec::new();
1944 if hittest {
1945 let size = self.surface_size();
1946 rectangles.push(Rectangle {
1947 x: 0,
1948 y: 0,
1949 width: size.width as u16,
1950 height: size.height as u16,
1951 })
1952 }
1953 let region = RegionWrapper::create_region(self.xconn.xcb_connection(), &rectangles)
1954 .map_err(|_e| RequestError::Ignored)?;
1955 self.xconn
1956 .xcb_connection()
1957 .xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
1958 .map_err(|_e| RequestError::Ignored)?;
1959 self.shared_state_lock().cursor_hittest = Some(hittest);
1960 Ok(())
1961 }
1962
1963 pub fn drag_window(&self) -> Result<(), RequestError> {
1965 self.drag_initiate(util::MOVERESIZE_MOVE)
1966 }
1967
1968 #[inline]
1969 pub fn show_window_menu(&self, _position: Position) {}
1970
1971 pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
1973 self.drag_initiate(match direction {
1974 ResizeDirection::East => util::MOVERESIZE_RIGHT,
1975 ResizeDirection::North => util::MOVERESIZE_TOP,
1976 ResizeDirection::NorthEast => util::MOVERESIZE_TOPRIGHT,
1977 ResizeDirection::NorthWest => util::MOVERESIZE_TOPLEFT,
1978 ResizeDirection::South => util::MOVERESIZE_BOTTOM,
1979 ResizeDirection::SouthEast => util::MOVERESIZE_BOTTOMRIGHT,
1980 ResizeDirection::SouthWest => util::MOVERESIZE_BOTTOMLEFT,
1981 ResizeDirection::West => util::MOVERESIZE_LEFT,
1982 })
1983 }
1984
1985 fn drag_initiate(&self, action: isize) -> Result<(), RequestError> {
1987 let pointer = self
1988 .xconn
1989 .query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
1990 .map_err(|err| os_error!(err))?;
1991
1992 let window_position = self.inner_position()?;
1993
1994 let atoms = self.xconn.atoms();
1995 let message = atoms[_NET_WM_MOVERESIZE];
1996
1997 let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
2000 self.xconn
2001 .xcb_connection()
2002 .ungrab_pointer(x11rb::CURRENT_TIME)
2003 .map_err(|err| os_error!(X11Error::from(err)))?
2004 .ignore_error();
2005 self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
2006 *grabbed_lock = CursorGrabMode::None;
2007
2008 self.xconn
2010 .send_client_msg(
2011 self.xwindow,
2012 self.root,
2013 message,
2014 Some(
2015 xproto::EventMask::SUBSTRUCTURE_REDIRECT
2016 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
2017 ),
2018 [
2019 (window_position.x + xinput_fp1616_to_float(pointer.win_x) as i32) as u32,
2020 (window_position.y + xinput_fp1616_to_float(pointer.win_y) as i32) as u32,
2021 action.try_into().unwrap(),
2022 1, 1,
2024 ],
2025 )
2026 .map_err(|err| os_error!(err))?;
2027
2028 self.xconn.flush_requests().map_err(|err| os_error!(X11Error::Xlib(err)))?;
2029
2030 Ok(())
2031 }
2032
2033 #[inline]
2034 pub fn set_ime_cursor_area(&self, spot: Position, _size: Size) {
2035 let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
2036 let _ = self.ime_sender.lock().unwrap().send(ImeRequest::Position(
2037 self.xwindow as ffi::Window,
2038 x,
2039 y,
2040 ));
2041 }
2042
2043 #[inline]
2044 pub fn set_ime_allowed(&self, allowed: bool) {
2045 let _ = self
2046 .ime_sender
2047 .lock()
2048 .unwrap()
2049 .send(ImeRequest::Allow(self.xwindow as ffi::Window, allowed));
2050 }
2051
2052 #[inline]
2053 pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}
2054
2055 #[inline]
2056 pub fn focus_window(&self) {
2057 let atoms = self.xconn.atoms();
2058 let state_atom = atoms[WM_STATE];
2059 let state_type_atom = atoms[CARD32];
2060 let is_minimized = if let Ok(state) =
2061 self.xconn.get_property::<u32>(self.xwindow, state_atom, state_type_atom)
2062 {
2063 state.contains(&super::ICONIC_STATE)
2064 } else {
2065 false
2066 };
2067 let is_visible = match self.shared_state_lock().visibility {
2068 Visibility::Yes => true,
2069 Visibility::YesWait | Visibility::No => false,
2070 };
2071
2072 if is_visible && !is_minimized {
2073 self.xconn
2074 .send_client_msg(
2075 self.xwindow,
2076 self.root,
2077 atoms[_NET_ACTIVE_WINDOW],
2078 Some(
2079 xproto::EventMask::SUBSTRUCTURE_REDIRECT
2080 | xproto::EventMask::SUBSTRUCTURE_NOTIFY,
2081 ),
2082 [1, x11rb::CURRENT_TIME, 0, 0, 0],
2083 )
2084 .expect_then_ignore_error("Failed to send client message");
2085 if let Err(e) = self.xconn.flush_requests() {
2086 tracing::error!(
2087 "`flush` returned an error when focusing the window. Error was: {}",
2088 e
2089 );
2090 }
2091 }
2092 }
2093
2094 #[inline]
2095 pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
2096 let mut wm_hints =
2097 WmHints::get(self.xconn.xcb_connection(), self.xwindow as xproto::Window)
2098 .ok()
2099 .and_then(|cookie| cookie.reply().ok())
2100 .flatten()
2101 .unwrap_or_default();
2102
2103 wm_hints.urgent = request_type.is_some();
2104 wm_hints
2105 .set(self.xconn.xcb_connection(), self.xwindow as xproto::Window)
2106 .expect_then_ignore_error("Failed to set WM hints");
2107 }
2108
2109 #[inline]
2110 pub(crate) fn generate_activation_token(&self) -> Result<String, X11Error> {
2111 let atoms = self.xconn.atoms();
2113 let title = {
2114 let title_bytes = self
2115 .xconn
2116 .get_property(self.xwindow, atoms[_NET_WM_NAME], atoms[UTF8_STRING])
2117 .expect("Failed to get title");
2118
2119 String::from_utf8(title_bytes).expect("Bad title")
2120 };
2121
2122 let token = self.xconn.request_activation_token(&title)?;
2124
2125 Ok(token)
2126 }
2127
2128 #[inline]
2129 pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
2130 let serial = AsyncRequestSerial::get();
2131 self.activation_sender.send((self.id(), serial));
2132 Ok(serial)
2133 }
2134
2135 #[inline]
2136 pub fn id(&self) -> WindowId {
2137 WindowId(self.xwindow as _)
2138 }
2139
2140 pub(super) fn sync_counter_id(&self) -> Option<NonZeroU32> {
2141 self.sync_counter_id
2142 }
2143
2144 #[inline]
2145 pub fn request_redraw(&self) {
2146 self.redraw_sender.send(WindowId(self.xwindow as _));
2147 }
2148
2149 #[inline]
2150 pub fn pre_present_notify(&self) {
2151 }
2153
2154 #[cfg(feature = "rwh_06")]
2155 #[inline]
2156 pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
2157 let mut window_handle = rwh_06::XlibWindowHandle::new(self.xlib_window());
2158 window_handle.visual_id = self.visual as c_ulong;
2159 Ok(window_handle.into())
2160 }
2161
2162 #[cfg(feature = "rwh_06")]
2163 #[inline]
2164 pub fn raw_display_handle_rwh_06(
2165 &self,
2166 ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
2167 Ok(rwh_06::XlibDisplayHandle::new(
2168 Some(
2169 std::ptr::NonNull::new(self.xlib_display())
2170 .expect("display pointer should never be null"),
2171 ),
2172 self.screen_id,
2173 )
2174 .into())
2175 }
2176
2177 #[inline]
2178 pub fn theme(&self) -> Option<Theme> {
2179 None
2180 }
2181
2182 pub fn set_content_protected(&self, _protected: bool) {}
2183
2184 #[inline]
2185 pub fn has_focus(&self) -> bool {
2186 self.shared_state_lock().has_focus
2187 }
2188
2189 pub fn title(&self) -> String {
2190 String::new()
2191 }
2192}
2193
2194fn cast_dimension_to_hint(val: u32) -> i32 {
2196 val.try_into().unwrap_or(i32::MAX)
2197}
2198
2199fn cast_physical_size_to_hint(size: PhysicalSize<u32>) -> (i32, i32) {
2201 let PhysicalSize { width, height } = size;
2202 (cast_dimension_to_hint(width), cast_dimension_to_hint(height))
2203}
2204
2205fn cast_size_to_hint(size: Size, scale_factor: f64) -> (i32, i32) {
2207 match size {
2208 Size::Physical(size) => cast_physical_size_to_hint(size),
2209 Size::Logical(size) => size.to_physical::<i32>(scale_factor).into(),
2210 }
2211}