1 use raw_window_handle::unix::XlibHandle;
2 use std::{
3     cmp, env,
4     ffi::CString,
5     mem::{self, replace, MaybeUninit},
6     os::raw::*,
7     path::Path,
8     ptr, slice,
9     sync::Arc,
10 };
11 
12 use libc;
13 use mio_extras::channel::Sender;
14 use parking_lot::Mutex;
15 
16 use crate::{
17     dpi::{PhysicalPosition, PhysicalSize, Position, Size},
18     error::{ExternalError, NotSupportedError, OsError as RootOsError},
19     monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
20     platform_impl::{
21         x11::{ime::ImeContextCreationError, MonitorHandle as X11MonitorHandle},
22         MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
23         VideoMode as PlatformVideoMode,
24     },
25     window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
26 };
27 
28 use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
29 
30 #[derive(Debug)]
31 pub struct SharedState {
32     pub cursor_pos: Option<(f64, f64)>,
33     pub size: Option<(u32, u32)>,
34     pub position: Option<(i32, i32)>,
35     pub inner_position: Option<(i32, i32)>,
36     pub inner_position_rel_parent: Option<(i32, i32)>,
37     pub last_monitor: X11MonitorHandle,
38     pub dpi_adjusted: Option<(u32, u32)>,
39     pub fullscreen: Option<Fullscreen>,
40     // Set when application calls `set_fullscreen` when window is not visible
41     pub desired_fullscreen: Option<Option<Fullscreen>>,
42     // Used to restore position after exiting fullscreen
43     pub restore_position: Option<(i32, i32)>,
44     // Used to restore video mode after exiting fullscreen
45     pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>,
46     pub frame_extents: Option<util::FrameExtentsHeuristic>,
47     pub min_inner_size: Option<Size>,
48     pub max_inner_size: Option<Size>,
49     pub resize_increments: Option<Size>,
50     pub base_size: Option<Size>,
51     pub visibility: Visibility,
52 }
53 
54 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
55 pub enum Visibility {
56     No,
57     Yes,
58     // Waiting for VisibilityNotify
59     YesWait,
60 }
61 
62 impl SharedState {
new(last_monitor: X11MonitorHandle, is_visible: bool) -> Mutex<Self>63     fn new(last_monitor: X11MonitorHandle, is_visible: bool) -> Mutex<Self> {
64         let visibility = if is_visible {
65             Visibility::YesWait
66         } else {
67             Visibility::No
68         };
69 
70         Mutex::new(SharedState {
71             last_monitor,
72             visibility,
73 
74             cursor_pos: None,
75             size: None,
76             position: None,
77             inner_position: None,
78             inner_position_rel_parent: None,
79             dpi_adjusted: None,
80             fullscreen: None,
81             desired_fullscreen: None,
82             restore_position: None,
83             desktop_video_mode: None,
84             frame_extents: None,
85             min_inner_size: None,
86             max_inner_size: None,
87             resize_increments: None,
88             base_size: None,
89         })
90     }
91 }
92 
93 unsafe impl Send for UnownedWindow {}
94 unsafe impl Sync for UnownedWindow {}
95 
96 pub struct UnownedWindow {
97     pub xconn: Arc<XConnection>, // never changes
98     xwindow: ffi::Window,        // never changes
99     root: ffi::Window,           // never changes
100     screen_id: i32,              // never changes
101     cursor: Mutex<CursorIcon>,
102     cursor_grabbed: Mutex<bool>,
103     cursor_visible: Mutex<bool>,
104     ime_sender: Mutex<ImeSender>,
105     pub shared_state: Mutex<SharedState>,
106     redraw_sender: Sender<WindowId>,
107 }
108 
109 impl UnownedWindow {
new<T>( event_loop: &EventLoopWindowTarget<T>, window_attrs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result<UnownedWindow, RootOsError>110     pub fn new<T>(
111         event_loop: &EventLoopWindowTarget<T>,
112         window_attrs: WindowAttributes,
113         pl_attribs: PlatformSpecificWindowBuilderAttributes,
114     ) -> Result<UnownedWindow, RootOsError> {
115         let xconn = &event_loop.xconn;
116         let root = event_loop.root;
117 
118         let mut monitors = xconn.available_monitors();
119         let guessed_monitor = if monitors.is_empty() {
120             X11MonitorHandle::dummy()
121         } else {
122             xconn
123                 .query_pointer(root, util::VIRTUAL_CORE_POINTER)
124                 .ok()
125                 .and_then(|pointer_state| {
126                     let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
127 
128                     for i in 0..monitors.len() {
129                         if monitors[i].rect.contains_point(x, y) {
130                             return Some(monitors.swap_remove(i));
131                         }
132                     }
133 
134                     None
135                 })
136                 .unwrap_or_else(|| monitors.swap_remove(0))
137         };
138         let scale_factor = guessed_monitor.scale_factor();
139 
140         info!("Guessed window scale factor: {}", scale_factor);
141 
142         let max_inner_size: Option<(u32, u32)> = window_attrs
143             .max_inner_size
144             .map(|size| size.to_physical::<u32>(scale_factor).into());
145         let min_inner_size: Option<(u32, u32)> = window_attrs
146             .min_inner_size
147             .map(|size| size.to_physical::<u32>(scale_factor).into());
148 
149         let dimensions = {
150             // x11 only applies constraints when the window is actively resized
151             // by the user, so we have to manually apply the initial constraints
152             let mut dimensions: (u32, u32) = window_attrs
153                 .inner_size
154                 .map(|size| size.to_physical::<u32>(scale_factor))
155                 .or_else(|| Some((800, 600).into()))
156                 .map(Into::into)
157                 .unwrap();
158             if let Some(max) = max_inner_size {
159                 dimensions.0 = cmp::min(dimensions.0, max.0);
160                 dimensions.1 = cmp::min(dimensions.1, max.1);
161             }
162             if let Some(min) = min_inner_size {
163                 dimensions.0 = cmp::max(dimensions.0, min.0);
164                 dimensions.1 = cmp::max(dimensions.1, min.1);
165             }
166             debug!(
167                 "Calculated physical dimensions: {}x{}",
168                 dimensions.0, dimensions.1
169             );
170             dimensions
171         };
172 
173         let screen_id = match pl_attribs.screen_id {
174             Some(id) => id,
175             None => unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) },
176         };
177 
178         // creating
179         let mut set_win_attr = {
180             let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
181             swa.colormap = if let Some(vi) = pl_attribs.visual_infos {
182                 unsafe {
183                     let visual = vi.visual;
184                     (xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone)
185                 }
186             } else {
187                 0
188             };
189             swa.event_mask = ffi::ExposureMask
190                 | ffi::StructureNotifyMask
191                 | ffi::VisibilityChangeMask
192                 | ffi::KeyPressMask
193                 | ffi::KeyReleaseMask
194                 | ffi::KeymapStateMask
195                 | ffi::ButtonPressMask
196                 | ffi::ButtonReleaseMask
197                 | ffi::PointerMotionMask;
198             swa.border_pixel = 0;
199             swa.override_redirect = pl_attribs.override_redirect as c_int;
200             swa
201         };
202 
203         let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi::CWEventMask;
204 
205         if pl_attribs.override_redirect {
206             window_attributes |= ffi::CWOverrideRedirect;
207         }
208 
209         // finally creating the window
210         let xwindow = unsafe {
211             (xconn.xlib.XCreateWindow)(
212                 xconn.display,
213                 root,
214                 0,
215                 0,
216                 dimensions.0 as c_uint,
217                 dimensions.1 as c_uint,
218                 0,
219                 match pl_attribs.visual_infos {
220                     Some(vi) => vi.depth,
221                     None => ffi::CopyFromParent,
222                 },
223                 ffi::InputOutput as c_uint,
224                 // TODO: If window wants transparency and `visual_infos` is None,
225                 // we need to find our own visual which has an `alphaMask` which
226                 // is > 0, like we do in glutin.
227                 //
228                 // It is non obvious which masks, if any, we should pass to
229                 // `XGetVisualInfo`. winit doesn't receive any info about what
230                 // properties the user wants. Users should consider choosing the
231                 // visual themselves as glutin does.
232                 match pl_attribs.visual_infos {
233                     Some(vi) => vi.visual,
234                     None => ffi::CopyFromParent as *mut ffi::Visual,
235                 },
236                 window_attributes,
237                 &mut set_win_attr,
238             )
239         };
240 
241         let mut window = UnownedWindow {
242             xconn: Arc::clone(xconn),
243             xwindow,
244             root,
245             screen_id,
246             cursor: Default::default(),
247             cursor_grabbed: Mutex::new(false),
248             cursor_visible: Mutex::new(true),
249             ime_sender: Mutex::new(event_loop.ime_sender.clone()),
250             shared_state: SharedState::new(guessed_monitor, window_attrs.visible),
251             redraw_sender: event_loop.redraw_sender.clone(),
252         };
253 
254         // Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
255         // title to determine placement/etc., so doing this after mapping would cause the WM to
256         // act on the wrong title state.
257         window.set_title_inner(&window_attrs.title).queue();
258         window
259             .set_decorations_inner(window_attrs.decorations)
260             .queue();
261 
262         {
263             // Enable drag and drop (TODO: extend API to make this toggleable)
264             unsafe {
265                 let dnd_aware_atom = xconn.get_atom_unchecked(b"XdndAware\0");
266                 let version = &[5 as c_ulong]; // Latest version; hasn't changed since 2002
267                 xconn.change_property(
268                     window.xwindow,
269                     dnd_aware_atom,
270                     ffi::XA_ATOM,
271                     util::PropMode::Replace,
272                     version,
273                 )
274             }
275             .queue();
276 
277             // WM_CLASS must be set *before* mapping the window, as per ICCCM!
278             {
279                 let (class, instance) = if let Some((instance, class)) = pl_attribs.class {
280                     let instance = CString::new(instance.as_str())
281                         .expect("`WM_CLASS` instance contained null byte");
282                     let class =
283                         CString::new(class.as_str()).expect("`WM_CLASS` class contained null byte");
284                     (instance, class)
285                 } else {
286                     let class = env::args()
287                         .next()
288                         .as_ref()
289                         // Default to the name of the binary (via argv[0])
290                         .and_then(|path| Path::new(path).file_name())
291                         .and_then(|bin_name| bin_name.to_str())
292                         .map(|bin_name| bin_name.to_owned())
293                         .or_else(|| Some(window_attrs.title.clone()))
294                         .and_then(|string| CString::new(string.as_str()).ok())
295                         .expect("Default `WM_CLASS` class contained null byte");
296                     // This environment variable is extraordinarily unlikely to actually be used...
297                     let instance = env::var("RESOURCE_NAME")
298                         .ok()
299                         .and_then(|instance| CString::new(instance.as_str()).ok())
300                         .or_else(|| Some(class.clone()))
301                         .expect("Default `WM_CLASS` instance contained null byte");
302                     (instance, class)
303                 };
304 
305                 let mut class_hint = xconn.alloc_class_hint();
306                 (*class_hint).res_name = class.as_ptr() as *mut c_char;
307                 (*class_hint).res_class = instance.as_ptr() as *mut c_char;
308 
309                 unsafe {
310                     (xconn.xlib.XSetClassHint)(xconn.display, window.xwindow, class_hint.ptr);
311                 } //.queue();
312             }
313 
314             window.set_pid().map(|flusher| flusher.queue());
315 
316             window.set_window_types(pl_attribs.x11_window_types).queue();
317 
318             if let Some(variant) = pl_attribs.gtk_theme_variant {
319                 window.set_gtk_theme_variant(variant).queue();
320             }
321 
322             // set size hints
323             {
324                 let mut min_inner_size = window_attrs
325                     .min_inner_size
326                     .map(|size| size.to_physical::<u32>(scale_factor));
327                 let mut max_inner_size = window_attrs
328                     .max_inner_size
329                     .map(|size| size.to_physical::<u32>(scale_factor));
330 
331                 if !window_attrs.resizable {
332                     if util::wm_name_is_one_of(&["Xfwm4"]) {
333                         warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
334                     } else {
335                         max_inner_size = Some(dimensions.into());
336                         min_inner_size = Some(dimensions.into());
337 
338                         let mut shared_state = window.shared_state.get_mut();
339                         shared_state.min_inner_size = window_attrs.min_inner_size;
340                         shared_state.max_inner_size = window_attrs.max_inner_size;
341                         shared_state.resize_increments = pl_attribs.resize_increments;
342                         shared_state.base_size = pl_attribs.base_size;
343                     }
344                 }
345 
346                 let mut normal_hints = util::NormalHints::new(xconn);
347                 normal_hints.set_size(Some(dimensions));
348                 normal_hints.set_min_size(min_inner_size.map(Into::into));
349                 normal_hints.set_max_size(max_inner_size.map(Into::into));
350                 normal_hints.set_resize_increments(
351                     pl_attribs
352                         .resize_increments
353                         .map(|size| size.to_physical::<u32>(scale_factor).into()),
354                 );
355                 normal_hints.set_base_size(
356                     pl_attribs
357                         .base_size
358                         .map(|size| size.to_physical::<u32>(scale_factor).into()),
359                 );
360                 xconn.set_normal_hints(window.xwindow, normal_hints).queue();
361             }
362 
363             // Set window icons
364             if let Some(icon) = window_attrs.window_icon {
365                 window.set_icon_inner(icon).queue();
366             }
367 
368             // Opt into handling window close
369             unsafe {
370                 (xconn.xlib.XSetWMProtocols)(
371                     xconn.display,
372                     window.xwindow,
373                     &[event_loop.wm_delete_window, event_loop.net_wm_ping] as *const ffi::Atom
374                         as *mut ffi::Atom,
375                     2,
376                 );
377             } //.queue();
378 
379             // Set visibility (map window)
380             if window_attrs.visible {
381                 unsafe {
382                     (xconn.xlib.XMapRaised)(xconn.display, window.xwindow);
383                 } //.queue();
384             }
385 
386             // Attempt to make keyboard input repeat detectable
387             unsafe {
388                 let mut supported_ptr = ffi::False;
389                 (xconn.xlib.XkbSetDetectableAutoRepeat)(
390                     xconn.display,
391                     ffi::True,
392                     &mut supported_ptr,
393                 );
394                 if supported_ptr == ffi::False {
395                     return Err(os_error!(OsError::XMisc(
396                         "`XkbSetDetectableAutoRepeat` failed"
397                     )));
398                 }
399             }
400 
401             // Select XInput2 events
402             let mask = {
403                 let mask = ffi::XI_MotionMask
404                     | ffi::XI_ButtonPressMask
405                     | ffi::XI_ButtonReleaseMask
406                     //| ffi::XI_KeyPressMask
407                     //| ffi::XI_KeyReleaseMask
408                     | ffi::XI_EnterMask
409                     | ffi::XI_LeaveMask
410                     | ffi::XI_FocusInMask
411                     | ffi::XI_FocusOutMask
412                     | ffi::XI_TouchBeginMask
413                     | ffi::XI_TouchUpdateMask
414                     | ffi::XI_TouchEndMask;
415                 mask
416             };
417             xconn
418                 .select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask)
419                 .queue();
420 
421             {
422                 let result = event_loop.ime.borrow_mut().create_context(window.xwindow);
423                 if let Err(err) = result {
424                     let e = match err {
425                         ImeContextCreationError::XError(err) => OsError::XError(err),
426                         ImeContextCreationError::Null => {
427                             OsError::XMisc("IME Context creation failed")
428                         }
429                     };
430                     return Err(os_error!(e));
431                 }
432             }
433 
434             // These properties must be set after mapping
435             if window_attrs.maximized {
436                 window.set_maximized_inner(window_attrs.maximized).queue();
437             }
438             if window_attrs.fullscreen.is_some() {
439                 window
440                     .set_fullscreen_inner(window_attrs.fullscreen.clone())
441                     .map(|flusher| flusher.queue());
442             }
443             if window_attrs.always_on_top {
444                 window
445                     .set_always_on_top_inner(window_attrs.always_on_top)
446                     .queue();
447             }
448         }
449 
450         // We never want to give the user a broken window, since by then, it's too late to handle.
451         xconn
452             .sync_with_server()
453             .map(|_| window)
454             .map_err(|x_err| os_error!(OsError::XError(x_err)))
455     }
456 
set_pid(&self) -> Option<util::Flusher<'_>>457     fn set_pid(&self) -> Option<util::Flusher<'_>> {
458         let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
459         let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
460         unsafe {
461             // 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is
462             // the limit defined by OpenBSD.
463             const MAXHOSTNAMELEN: usize = 256;
464             // `assume_init` is safe here because the array consists of `MaybeUninit` values,
465             // which do not require initialization.
466             let mut buffer: [MaybeUninit<c_char>; MAXHOSTNAMELEN] =
467                 MaybeUninit::uninit().assume_init();
468             let status = libc::gethostname(buffer.as_mut_ptr() as *mut c_char, buffer.len());
469             if status != 0 {
470                 return None;
471             }
472             ptr::write(buffer[MAXHOSTNAMELEN - 1].as_mut_ptr() as *mut u8, b'\0'); // a little extra safety
473             let hostname_length = libc::strlen(buffer.as_ptr() as *const c_char);
474 
475             let hostname = slice::from_raw_parts(buffer.as_ptr() as *const c_char, hostname_length);
476 
477             self.xconn
478                 .change_property(
479                     self.xwindow,
480                     pid_atom,
481                     ffi::XA_CARDINAL,
482                     util::PropMode::Replace,
483                     &[libc::getpid() as util::Cardinal],
484                 )
485                 .queue();
486             let flusher = self.xconn.change_property(
487                 self.xwindow,
488                 client_machine_atom,
489                 ffi::XA_STRING,
490                 util::PropMode::Replace,
491                 &hostname[0..hostname_length],
492             );
493             Some(flusher)
494         }
495     }
496 
set_window_types(&self, window_types: Vec<util::WindowType>) -> util::Flusher<'_>497     fn set_window_types(&self, window_types: Vec<util::WindowType>) -> util::Flusher<'_> {
498         let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") };
499         let atoms: Vec<_> = window_types
500             .iter()
501             .map(|t| t.as_atom(&self.xconn))
502             .collect();
503 
504         self.xconn.change_property(
505             self.xwindow,
506             hint_atom,
507             ffi::XA_ATOM,
508             util::PropMode::Replace,
509             &atoms,
510         )
511     }
512 
set_gtk_theme_variant(&self, variant: String) -> util::Flusher<'_>513     fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher<'_> {
514         let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") };
515         let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
516         let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
517         self.xconn.change_property(
518             self.xwindow,
519             hint_atom,
520             utf8_atom,
521             util::PropMode::Replace,
522             variant.as_bytes(),
523         )
524     }
525 
set_netwm( &self, operation: util::StateOperation, properties: (c_long, c_long, c_long, c_long), ) -> util::Flusher<'_>526     fn set_netwm(
527         &self,
528         operation: util::StateOperation,
529         properties: (c_long, c_long, c_long, c_long),
530     ) -> util::Flusher<'_> {
531         let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") };
532         self.xconn.send_client_msg(
533             self.xwindow,
534             self.root,
535             state_atom,
536             Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask),
537             [
538                 operation as c_long,
539                 properties.0,
540                 properties.1,
541                 properties.2,
542                 properties.3,
543             ],
544         )
545     }
546 
set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_>547     fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> {
548         let fullscreen_atom =
549             unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") };
550         let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0));
551 
552         if fullscreen {
553             // Ensure that the fullscreen window receives input focus to prevent
554             // locking up the user's display.
555             unsafe {
556                 (self.xconn.xlib.XSetInputFocus)(
557                     self.xconn.display,
558                     self.xwindow,
559                     ffi::RevertToParent,
560                     ffi::CurrentTime,
561                 );
562             }
563         }
564 
565         flusher
566     }
567 
set_fullscreen_inner(&self, fullscreen: Option<Fullscreen>) -> Option<util::Flusher<'_>>568     fn set_fullscreen_inner(&self, fullscreen: Option<Fullscreen>) -> Option<util::Flusher<'_>> {
569         let mut shared_state_lock = self.shared_state.lock();
570 
571         match shared_state_lock.visibility {
572             // Setting fullscreen on a window that is not visible will generate an error.
573             Visibility::No | Visibility::YesWait => {
574                 shared_state_lock.desired_fullscreen = Some(fullscreen);
575                 return None;
576             }
577             Visibility::Yes => (),
578         }
579 
580         let old_fullscreen = shared_state_lock.fullscreen.clone();
581         if old_fullscreen == fullscreen {
582             return None;
583         }
584         shared_state_lock.fullscreen = fullscreen.clone();
585 
586         match (&old_fullscreen, &fullscreen) {
587             // Store the desktop video mode before entering exclusive
588             // fullscreen, so we can restore it upon exit, as XRandR does not
589             // provide a mechanism to set this per app-session or restore this
590             // to the desktop video mode as macOS and Windows do
591             (
592                 &None,
593                 &Some(Fullscreen::Exclusive(RootVideoMode {
594                     video_mode: PlatformVideoMode::X(ref video_mode),
595                 })),
596             )
597             | (
598                 &Some(Fullscreen::Borderless(_)),
599                 &Some(Fullscreen::Exclusive(RootVideoMode {
600                     video_mode: PlatformVideoMode::X(ref video_mode),
601                 })),
602             ) => {
603                 let monitor = video_mode.monitor.as_ref().unwrap();
604                 shared_state_lock.desktop_video_mode =
605                     Some((monitor.id, self.xconn.get_crtc_mode(monitor.id)));
606             }
607             // Restore desktop video mode upon exiting exclusive fullscreen
608             (&Some(Fullscreen::Exclusive(_)), &None)
609             | (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => {
610                 let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap();
611                 self.xconn
612                     .set_crtc_config(monitor_id, mode_id)
613                     .expect("failed to restore desktop video mode");
614             }
615             _ => (),
616         }
617 
618         drop(shared_state_lock);
619 
620         match fullscreen {
621             None => {
622                 let flusher = self.set_fullscreen_hint(false);
623                 let mut shared_state_lock = self.shared_state.lock();
624                 if let Some(position) = shared_state_lock.restore_position.take() {
625                     drop(shared_state_lock);
626                     self.set_position_inner(position.0, position.1).queue();
627                 }
628                 Some(flusher)
629             }
630             Some(fullscreen) => {
631                 let (video_mode, monitor) = match fullscreen {
632                     Fullscreen::Exclusive(RootVideoMode {
633                         video_mode: PlatformVideoMode::X(ref video_mode),
634                     }) => (Some(video_mode), video_mode.monitor.clone().unwrap()),
635                     Fullscreen::Borderless(Some(RootMonitorHandle {
636                         inner: PlatformMonitorHandle::X(monitor),
637                     })) => (None, monitor),
638                     Fullscreen::Borderless(None) => (None, self.current_monitor()),
639                     #[cfg(feature = "wayland")]
640                     _ => unreachable!(),
641                 };
642 
643                 // Don't set fullscreen on an invalid dummy monitor handle
644                 if monitor.is_dummy() {
645                     return None;
646                 }
647 
648                 if let Some(video_mode) = video_mode {
649                     // FIXME: this is actually not correct if we're setting the
650                     // video mode to a resolution higher than the current
651                     // desktop resolution, because XRandR does not automatically
652                     // reposition the monitors to the right and below this
653                     // monitor.
654                     //
655                     // What ends up happening is we will get the fullscreen
656                     // window showing up on those monitors as well, because
657                     // their virtual position now overlaps with the monitor that
658                     // we just made larger..
659                     //
660                     // It'd be quite a bit of work to handle this correctly (and
661                     // nobody else seems to bother doing this correctly either),
662                     // so we're just leaving this broken. Fixing this would
663                     // involve storing all CRTCs upon entering fullscreen,
664                     // restoring them upon exit, and after entering fullscreen,
665                     // repositioning displays to the right and below this
666                     // display. I think there would still be edge cases that are
667                     // difficult or impossible to handle correctly, e.g. what if
668                     // a new monitor was plugged in while in fullscreen?
669                     //
670                     // I think we might just want to disallow setting the video
671                     // mode higher than the current desktop video mode (I'm sure
672                     // this will make someone unhappy, but it's very unusual for
673                     // games to want to do this anyway).
674                     self.xconn
675                         .set_crtc_config(monitor.id, video_mode.native_mode)
676                         .expect("failed to set video mode");
677                 }
678 
679                 let window_position = self.outer_position_physical();
680                 self.shared_state.lock().restore_position = Some(window_position);
681                 let monitor_origin: (i32, i32) = monitor.position().into();
682                 self.set_position_inner(monitor_origin.0, monitor_origin.1)
683                     .queue();
684                 Some(self.set_fullscreen_hint(true))
685             }
686         }
687     }
688 
689     #[inline]
fullscreen(&self) -> Option<Fullscreen>690     pub fn fullscreen(&self) -> Option<Fullscreen> {
691         let shared_state = self.shared_state.lock();
692 
693         shared_state
694             .desired_fullscreen
695             .clone()
696             .unwrap_or_else(|| shared_state.fullscreen.clone())
697     }
698 
699     #[inline]
set_fullscreen(&self, fullscreen: Option<Fullscreen>)700     pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
701         if let Some(flusher) = self.set_fullscreen_inner(fullscreen) {
702             flusher
703                 .sync()
704                 .expect("Failed to change window fullscreen state");
705             self.invalidate_cached_frame_extents();
706         }
707     }
708 
709     // Called by EventProcessor when a VisibilityNotify event is received
visibility_notify(&self)710     pub(crate) fn visibility_notify(&self) {
711         let mut shared_state = self.shared_state.lock();
712 
713         match shared_state.visibility {
714             Visibility::No => unsafe {
715                 (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
716             },
717             Visibility::Yes => (),
718             Visibility::YesWait => {
719                 shared_state.visibility = Visibility::Yes;
720 
721                 if let Some(fullscreen) = shared_state.desired_fullscreen.take() {
722                     drop(shared_state);
723                     self.set_fullscreen(fullscreen);
724                 }
725             }
726         }
727     }
728 
729     #[inline]
current_monitor(&self) -> X11MonitorHandle730     pub fn current_monitor(&self) -> X11MonitorHandle {
731         self.shared_state.lock().last_monitor.clone()
732     }
733 
available_monitors(&self) -> Vec<X11MonitorHandle>734     pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
735         self.xconn.available_monitors()
736     }
737 
primary_monitor(&self) -> X11MonitorHandle738     pub fn primary_monitor(&self) -> X11MonitorHandle {
739         self.xconn.primary_monitor()
740     }
741 
set_minimized_inner(&self, minimized: bool) -> util::Flusher<'_>742     fn set_minimized_inner(&self, minimized: bool) -> util::Flusher<'_> {
743         unsafe {
744             if minimized {
745                 let screen = (self.xconn.xlib.XDefaultScreen)(self.xconn.display);
746 
747                 (self.xconn.xlib.XIconifyWindow)(self.xconn.display, self.xwindow, screen);
748 
749                 util::Flusher::new(&self.xconn)
750             } else {
751                 let atom = self.xconn.get_atom_unchecked(b"_NET_ACTIVE_WINDOW\0");
752 
753                 self.xconn.send_client_msg(
754                     self.xwindow,
755                     self.root,
756                     atom,
757                     Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask),
758                     [1, ffi::CurrentTime as c_long, 0, 0, 0],
759                 )
760             }
761         }
762     }
763 
764     #[inline]
set_minimized(&self, minimized: bool)765     pub fn set_minimized(&self, minimized: bool) {
766         self.set_minimized_inner(minimized)
767             .flush()
768             .expect("Failed to change window minimization");
769     }
770 
set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_>771     fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> {
772         let horz_atom = unsafe {
773             self.xconn
774                 .get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0")
775         };
776         let vert_atom = unsafe {
777             self.xconn
778                 .get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0")
779         };
780         self.set_netwm(
781             maximized.into(),
782             (horz_atom as c_long, vert_atom as c_long, 0, 0),
783         )
784     }
785 
786     #[inline]
set_maximized(&self, maximized: bool)787     pub fn set_maximized(&self, maximized: bool) {
788         self.set_maximized_inner(maximized)
789             .flush()
790             .expect("Failed to change window maximization");
791         self.invalidate_cached_frame_extents();
792     }
793 
set_title_inner(&self, title: &str) -> util::Flusher<'_>794     fn set_title_inner(&self, title: &str) -> util::Flusher<'_> {
795         let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") };
796         let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
797         let title = CString::new(title).expect("Window title contained null byte");
798         unsafe {
799             (self.xconn.xlib.XStoreName)(
800                 self.xconn.display,
801                 self.xwindow,
802                 title.as_ptr() as *const c_char,
803             );
804             self.xconn.change_property(
805                 self.xwindow,
806                 wm_name_atom,
807                 utf8_atom,
808                 util::PropMode::Replace,
809                 title.as_bytes(),
810             )
811         }
812     }
813 
814     #[inline]
set_title(&self, title: &str)815     pub fn set_title(&self, title: &str) {
816         self.set_title_inner(title)
817             .flush()
818             .expect("Failed to set window title");
819     }
820 
set_decorations_inner(&self, decorations: bool) -> util::Flusher<'_>821     fn set_decorations_inner(&self, decorations: bool) -> util::Flusher<'_> {
822         let mut hints = self.xconn.get_motif_hints(self.xwindow);
823 
824         hints.set_decorations(decorations);
825 
826         self.xconn.set_motif_hints(self.xwindow, &hints)
827     }
828 
829     #[inline]
set_decorations(&self, decorations: bool)830     pub fn set_decorations(&self, decorations: bool) {
831         self.set_decorations_inner(decorations)
832             .flush()
833             .expect("Failed to set decoration state");
834         self.invalidate_cached_frame_extents();
835     }
836 
set_maximizable_inner(&self, maximizable: bool) -> util::Flusher<'_>837     fn set_maximizable_inner(&self, maximizable: bool) -> util::Flusher<'_> {
838         let mut hints = self.xconn.get_motif_hints(self.xwindow);
839 
840         hints.set_maximizable(maximizable);
841 
842         self.xconn.set_motif_hints(self.xwindow, &hints)
843     }
844 
set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher<'_>845     fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher<'_> {
846         let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") };
847         self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0))
848     }
849 
850     #[inline]
set_always_on_top(&self, always_on_top: bool)851     pub fn set_always_on_top(&self, always_on_top: bool) {
852         self.set_always_on_top_inner(always_on_top)
853             .flush()
854             .expect("Failed to set always-on-top state");
855     }
856 
set_icon_inner(&self, icon: Icon) -> util::Flusher<'_>857     fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> {
858         let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
859         let data = icon.to_cardinals();
860         self.xconn.change_property(
861             self.xwindow,
862             icon_atom,
863             ffi::XA_CARDINAL,
864             util::PropMode::Replace,
865             data.as_slice(),
866         )
867     }
868 
unset_icon_inner(&self) -> util::Flusher<'_>869     fn unset_icon_inner(&self) -> util::Flusher<'_> {
870         let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
871         let empty_data: [util::Cardinal; 0] = [];
872         self.xconn.change_property(
873             self.xwindow,
874             icon_atom,
875             ffi::XA_CARDINAL,
876             util::PropMode::Replace,
877             &empty_data,
878         )
879     }
880 
881     #[inline]
set_window_icon(&self, icon: Option<Icon>)882     pub fn set_window_icon(&self, icon: Option<Icon>) {
883         match icon {
884             Some(icon) => self.set_icon_inner(icon),
885             None => self.unset_icon_inner(),
886         }
887         .flush()
888         .expect("Failed to set icons");
889     }
890 
891     #[inline]
set_visible(&self, visible: bool)892     pub fn set_visible(&self, visible: bool) {
893         let mut shared_state = self.shared_state.lock();
894 
895         match (visible, shared_state.visibility) {
896             (true, Visibility::Yes) | (true, Visibility::YesWait) | (false, Visibility::No) => {
897                 return
898             }
899             _ => (),
900         }
901 
902         if visible {
903             unsafe {
904                 (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
905             }
906             self.xconn
907                 .flush_requests()
908                 .expect("Failed to call XMapRaised");
909             shared_state.visibility = Visibility::YesWait;
910         } else {
911             unsafe {
912                 (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
913             }
914             self.xconn
915                 .flush_requests()
916                 .expect("Failed to call XUnmapWindow");
917             shared_state.visibility = Visibility::No;
918         }
919     }
920 
update_cached_frame_extents(&self)921     fn update_cached_frame_extents(&self) {
922         let extents = self
923             .xconn
924             .get_frame_extents_heuristic(self.xwindow, self.root);
925         (*self.shared_state.lock()).frame_extents = Some(extents);
926     }
927 
invalidate_cached_frame_extents(&self)928     pub(crate) fn invalidate_cached_frame_extents(&self) {
929         (*self.shared_state.lock()).frame_extents.take();
930     }
931 
outer_position_physical(&self) -> (i32, i32)932     pub(crate) fn outer_position_physical(&self) -> (i32, i32) {
933         let extents = (*self.shared_state.lock()).frame_extents.clone();
934         if let Some(extents) = extents {
935             let (x, y) = self.inner_position_physical();
936             extents.inner_pos_to_outer(x, y)
937         } else {
938             self.update_cached_frame_extents();
939             self.outer_position_physical()
940         }
941     }
942 
943     #[inline]
outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError>944     pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
945         let extents = (*self.shared_state.lock()).frame_extents.clone();
946         if let Some(extents) = extents {
947             let (x, y) = self.inner_position_physical();
948             Ok(extents.inner_pos_to_outer(x, y).into())
949         } else {
950             self.update_cached_frame_extents();
951             self.outer_position()
952         }
953     }
954 
inner_position_physical(&self) -> (i32, i32)955     pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
956         // This should be okay to unwrap since the only error XTranslateCoordinates can return
957         // is BadWindow, and if the window handle is bad we have bigger problems.
958         self.xconn
959             .translate_coords(self.xwindow, self.root)
960             .map(|coords| (coords.x_rel_root, coords.y_rel_root))
961             .unwrap()
962     }
963 
964     #[inline]
inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError>965     pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
966         Ok(self.inner_position_physical().into())
967     }
968 
set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_>969     pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> {
970         // There are a few WMs that set client area position rather than window position, so
971         // we'll translate for consistency.
972         if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
973             let extents = (*self.shared_state.lock()).frame_extents.clone();
974             if let Some(extents) = extents {
975                 x += extents.frame_extents.left as i32;
976                 y += extents.frame_extents.top as i32;
977             } else {
978                 self.update_cached_frame_extents();
979                 return self.set_position_inner(x, y);
980             }
981         }
982         unsafe {
983             (self.xconn.xlib.XMoveWindow)(self.xconn.display, self.xwindow, x as c_int, y as c_int);
984         }
985         util::Flusher::new(&self.xconn)
986     }
987 
set_position_physical(&self, x: i32, y: i32)988     pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
989         self.set_position_inner(x, y)
990             .flush()
991             .expect("Failed to call `XMoveWindow`");
992     }
993 
994     #[inline]
set_outer_position(&self, position: Position)995     pub fn set_outer_position(&self, position: Position) {
996         let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
997         self.set_position_physical(x, y);
998     }
999 
inner_size_physical(&self) -> (u32, u32)1000     pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
1001         // This should be okay to unwrap since the only error XGetGeometry can return
1002         // is BadWindow, and if the window handle is bad we have bigger problems.
1003         self.xconn
1004             .get_geometry(self.xwindow)
1005             .map(|geo| (geo.width, geo.height))
1006             .unwrap()
1007     }
1008 
1009     #[inline]
inner_size(&self) -> PhysicalSize<u32>1010     pub fn inner_size(&self) -> PhysicalSize<u32> {
1011         self.inner_size_physical().into()
1012     }
1013 
1014     #[inline]
outer_size(&self) -> PhysicalSize<u32>1015     pub fn outer_size(&self) -> PhysicalSize<u32> {
1016         let extents = self.shared_state.lock().frame_extents.clone();
1017         if let Some(extents) = extents {
1018             let (width, height) = self.inner_size_physical();
1019             extents.inner_size_to_outer(width, height).into()
1020         } else {
1021             self.update_cached_frame_extents();
1022             self.outer_size()
1023         }
1024     }
1025 
set_inner_size_physical(&self, width: u32, height: u32)1026     pub(crate) fn set_inner_size_physical(&self, width: u32, height: u32) {
1027         unsafe {
1028             (self.xconn.xlib.XResizeWindow)(
1029                 self.xconn.display,
1030                 self.xwindow,
1031                 width as c_uint,
1032                 height as c_uint,
1033             );
1034             self.xconn.flush_requests()
1035         }
1036         .expect("Failed to call `XResizeWindow`");
1037     }
1038 
1039     #[inline]
set_inner_size(&self, size: Size)1040     pub fn set_inner_size(&self, size: Size) {
1041         let scale_factor = self.scale_factor();
1042         let (width, height) = size.to_physical::<u32>(scale_factor).into();
1043         self.set_inner_size_physical(width, height);
1044     }
1045 
update_normal_hints<F>(&self, callback: F) -> Result<(), XError> where F: FnOnce(&mut util::NormalHints<'_>) -> (),1046     fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
1047     where
1048         F: FnOnce(&mut util::NormalHints<'_>) -> (),
1049     {
1050         let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?;
1051         callback(&mut normal_hints);
1052         self.xconn
1053             .set_normal_hints(self.xwindow, normal_hints)
1054             .flush()
1055     }
1056 
set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>)1057     pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
1058         self.update_normal_hints(|normal_hints| normal_hints.set_min_size(dimensions))
1059             .expect("Failed to call `XSetWMNormalHints`");
1060     }
1061 
1062     #[inline]
set_min_inner_size(&self, dimensions: Option<Size>)1063     pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
1064         self.shared_state.lock().min_inner_size = dimensions;
1065         let physical_dimensions =
1066             dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
1067         self.set_min_inner_size_physical(physical_dimensions);
1068     }
1069 
set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>)1070     pub(crate) fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
1071         self.update_normal_hints(|normal_hints| normal_hints.set_max_size(dimensions))
1072             .expect("Failed to call `XSetWMNormalHints`");
1073     }
1074 
1075     #[inline]
set_max_inner_size(&self, dimensions: Option<Size>)1076     pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
1077         self.shared_state.lock().max_inner_size = dimensions;
1078         let physical_dimensions =
1079             dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
1080         self.set_max_inner_size_physical(physical_dimensions);
1081     }
1082 
adjust_for_dpi( &self, old_scale_factor: f64, new_scale_factor: f64, width: u32, height: u32, shared_state: &SharedState, ) -> (u32, u32)1083     pub(crate) fn adjust_for_dpi(
1084         &self,
1085         old_scale_factor: f64,
1086         new_scale_factor: f64,
1087         width: u32,
1088         height: u32,
1089         shared_state: &SharedState,
1090     ) -> (u32, u32) {
1091         let scale_factor = new_scale_factor / old_scale_factor;
1092         self.update_normal_hints(|normal_hints| {
1093             let dpi_adjuster =
1094                 |size: Size| -> (u32, u32) { size.to_physical::<u32>(new_scale_factor).into() };
1095             let max_size = shared_state.max_inner_size.map(&dpi_adjuster);
1096             let min_size = shared_state.min_inner_size.map(&dpi_adjuster);
1097             let resize_increments = shared_state.resize_increments.map(&dpi_adjuster);
1098             let base_size = shared_state.base_size.map(&dpi_adjuster);
1099             normal_hints.set_max_size(max_size);
1100             normal_hints.set_min_size(min_size);
1101             normal_hints.set_resize_increments(resize_increments);
1102             normal_hints.set_base_size(base_size);
1103         })
1104         .expect("Failed to update normal hints");
1105 
1106         let new_width = (width as f64 * scale_factor).round() as u32;
1107         let new_height = (height as f64 * scale_factor).round() as u32;
1108 
1109         (new_width, new_height)
1110     }
1111 
set_resizable(&self, resizable: bool)1112     pub fn set_resizable(&self, resizable: bool) {
1113         if util::wm_name_is_one_of(&["Xfwm4"]) {
1114             // Making the window unresizable on Xfwm prevents further changes to `WM_NORMAL_HINTS` from being detected.
1115             // This makes it impossible for resizing to be re-enabled, and also breaks DPI scaling. As such, we choose
1116             // the lesser of two evils and do nothing.
1117             warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
1118             return;
1119         }
1120 
1121         let (min_size, max_size) = if resizable {
1122             let shared_state_lock = self.shared_state.lock();
1123             (
1124                 shared_state_lock.min_inner_size,
1125                 shared_state_lock.max_inner_size,
1126             )
1127         } else {
1128             let window_size = Some(Size::from(self.inner_size()));
1129             (window_size.clone(), window_size)
1130         };
1131 
1132         self.set_maximizable_inner(resizable).queue();
1133 
1134         let scale_factor = self.scale_factor();
1135         let min_inner_size = min_size
1136             .map(|size| size.to_physical::<u32>(scale_factor))
1137             .map(Into::into);
1138         let max_inner_size = max_size
1139             .map(|size| size.to_physical::<u32>(scale_factor))
1140             .map(Into::into);
1141         self.update_normal_hints(|normal_hints| {
1142             normal_hints.set_min_size(min_inner_size);
1143             normal_hints.set_max_size(max_inner_size);
1144         })
1145         .expect("Failed to call `XSetWMNormalHints`");
1146     }
1147 
1148     #[inline]
xlib_display(&self) -> *mut c_void1149     pub fn xlib_display(&self) -> *mut c_void {
1150         self.xconn.display as _
1151     }
1152 
1153     #[inline]
xlib_screen_id(&self) -> c_int1154     pub fn xlib_screen_id(&self) -> c_int {
1155         self.screen_id
1156     }
1157 
1158     #[inline]
xlib_xconnection(&self) -> Arc<XConnection>1159     pub fn xlib_xconnection(&self) -> Arc<XConnection> {
1160         Arc::clone(&self.xconn)
1161     }
1162 
1163     #[inline]
xlib_window(&self) -> c_ulong1164     pub fn xlib_window(&self) -> c_ulong {
1165         self.xwindow
1166     }
1167 
1168     #[inline]
xcb_connection(&self) -> *mut c_void1169     pub fn xcb_connection(&self) -> *mut c_void {
1170         unsafe { (self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _ }
1171     }
1172 
1173     #[inline]
set_cursor_icon(&self, cursor: CursorIcon)1174     pub fn set_cursor_icon(&self, cursor: CursorIcon) {
1175         let old_cursor = replace(&mut *self.cursor.lock(), cursor);
1176         if cursor != old_cursor && *self.cursor_visible.lock() {
1177             self.xconn.set_cursor_icon(self.xwindow, Some(cursor));
1178         }
1179     }
1180 
1181     #[inline]
set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError>1182     pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
1183         let mut grabbed_lock = self.cursor_grabbed.lock();
1184         if grab == *grabbed_lock {
1185             return Ok(());
1186         }
1187         unsafe {
1188             // We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
1189             // Therefore, this is common to both codepaths.
1190             (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime);
1191         }
1192         let result = if grab {
1193             let result = unsafe {
1194                 (self.xconn.xlib.XGrabPointer)(
1195                     self.xconn.display,
1196                     self.xwindow,
1197                     ffi::True,
1198                     (ffi::ButtonPressMask
1199                         | ffi::ButtonReleaseMask
1200                         | ffi::EnterWindowMask
1201                         | ffi::LeaveWindowMask
1202                         | ffi::PointerMotionMask
1203                         | ffi::PointerMotionHintMask
1204                         | ffi::Button1MotionMask
1205                         | ffi::Button2MotionMask
1206                         | ffi::Button3MotionMask
1207                         | ffi::Button4MotionMask
1208                         | ffi::Button5MotionMask
1209                         | ffi::ButtonMotionMask
1210                         | ffi::KeymapStateMask) as c_uint,
1211                     ffi::GrabModeAsync,
1212                     ffi::GrabModeAsync,
1213                     self.xwindow,
1214                     0,
1215                     ffi::CurrentTime,
1216                 )
1217             };
1218 
1219             match result {
1220                 ffi::GrabSuccess => Ok(()),
1221                 ffi::AlreadyGrabbed => {
1222                     Err("Cursor could not be grabbed: already grabbed by another client")
1223                 }
1224                 ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"),
1225                 ffi::GrabNotViewable => {
1226                     Err("Cursor could not be grabbed: grab location not viewable")
1227                 }
1228                 ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"),
1229                 _ => unreachable!(),
1230             }
1231             .map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
1232         } else {
1233             self.xconn
1234                 .flush_requests()
1235                 .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))
1236         };
1237         if result.is_ok() {
1238             *grabbed_lock = grab;
1239         }
1240         result
1241     }
1242 
1243     #[inline]
set_cursor_visible(&self, visible: bool)1244     pub fn set_cursor_visible(&self, visible: bool) {
1245         let mut visible_lock = self.cursor_visible.lock();
1246         if visible == *visible_lock {
1247             return;
1248         }
1249         let cursor = if visible {
1250             Some(*self.cursor.lock())
1251         } else {
1252             None
1253         };
1254         *visible_lock = visible;
1255         drop(visible_lock);
1256         self.xconn.set_cursor_icon(self.xwindow, cursor);
1257     }
1258 
1259     #[inline]
scale_factor(&self) -> f641260     pub fn scale_factor(&self) -> f64 {
1261         self.current_monitor().scale_factor
1262     }
1263 
set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError>1264     pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
1265         unsafe {
1266             (self.xconn.xlib.XWarpPointer)(self.xconn.display, 0, self.xwindow, 0, 0, 0, 0, x, y);
1267             self.xconn
1268                 .flush_requests()
1269                 .map_err(|e| ExternalError::Os(os_error!(OsError::XError(e))))
1270         }
1271     }
1272 
1273     #[inline]
set_cursor_position(&self, position: Position) -> Result<(), ExternalError>1274     pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
1275         let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
1276         self.set_cursor_position_physical(x, y)
1277     }
1278 
set_ime_position_physical(&self, x: i32, y: i32)1279     pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) {
1280         let _ = self
1281             .ime_sender
1282             .lock()
1283             .send((self.xwindow, x as i16, y as i16));
1284     }
1285 
1286     #[inline]
set_ime_position(&self, spot: Position)1287     pub fn set_ime_position(&self, spot: Position) {
1288         let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
1289         self.set_ime_position_physical(x, y);
1290     }
1291 
1292     #[inline]
request_user_attention(&self, request_type: Option<UserAttentionType>)1293     pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
1294         let mut wm_hints = self
1295             .xconn
1296             .get_wm_hints(self.xwindow)
1297             .expect("`XGetWMHints` failed");
1298         if request_type.is_some() {
1299             (*wm_hints).flags |= ffi::XUrgencyHint;
1300         } else {
1301             (*wm_hints).flags &= !ffi::XUrgencyHint;
1302         }
1303         self.xconn
1304             .set_wm_hints(self.xwindow, wm_hints)
1305             .flush()
1306             .expect("Failed to set urgency hint");
1307     }
1308 
1309     #[inline]
id(&self) -> WindowId1310     pub fn id(&self) -> WindowId {
1311         WindowId(self.xwindow)
1312     }
1313 
1314     #[inline]
request_redraw(&self)1315     pub fn request_redraw(&self) {
1316         self.redraw_sender.send(WindowId(self.xwindow)).unwrap();
1317     }
1318 
1319     #[inline]
raw_window_handle(&self) -> XlibHandle1320     pub fn raw_window_handle(&self) -> XlibHandle {
1321         XlibHandle {
1322             window: self.xwindow,
1323             display: self.xconn.display as _,
1324             ..XlibHandle::empty()
1325         }
1326     }
1327 }
1328