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