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