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