From 4372f6fdac18ca3883dce7e62185ab973595e9cb Mon Sep 17 00:00:00 2001 From: Francesca Frangipane Date: Tue, 29 May 2018 07:48:47 -0400 Subject: [PATCH] X11: Flatten window model (#536) --- CHANGELOG.md | 3 + src/lib.rs | 12 + src/platform/linux/x11/mod.rs | 434 ++++++++++++----------------- src/platform/linux/x11/util/mod.rs | 10 + src/platform/linux/x11/window.rs | 345 ++++++++++------------- 5 files changed, 360 insertions(+), 444 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1325606..15fc634f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +- On X11, the `Moved` event is no longer sent when the window is resized without changing position. +- `MouseCursor` and `CursorState` now implement `Default`. + # Version 0.15.0 (2018-05-22) - `Icon::to_cardinals` is no longer public, since it was never supposed to be. diff --git a/src/lib.rs b/src/lib.rs index 5b489e0c..b4120d75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -374,6 +374,12 @@ pub enum MouseCursor { RowResize, } +impl Default for MouseCursor { + fn default() -> Self { + MouseCursor::Default + } +} + /// Describes how winit handles the cursor. #[derive(Debug, Copy, Clone, PartialEq)] pub enum CursorState { @@ -391,6 +397,12 @@ pub enum CursorState { Grab, } +impl Default for CursorState { + fn default() -> Self { + CursorState::Normal + } +} + /// Attributes to use when creating a window. #[derive(Clone)] pub struct WindowAttributes { diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index 8e3623d0..85100a78 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -14,17 +14,19 @@ pub use self::monitor::{ get_available_monitors, get_monitor_for_window, get_primary_monitor, + invalidate_cached_monitor_list, }; -pub use self::window::{Window2, XWindow}; +pub use self::window::UnownedWindow; pub use self::xdisplay::{XConnection, XNotSupported, XError}; use std::{mem, ptr, slice}; -use std::sync::{Arc, mpsc, Weak}; -use std::sync::atomic::{self, AtomicBool}; use std::cell::RefCell; use std::collections::HashMap; use std::ffi::CStr; +use std::ops::Deref; use std::os::raw::*; +use std::sync::{Arc, mpsc, Weak}; +use std::sync::atomic::{self, AtomicBool}; use libc::{self, setlocale, LC_CTYPE}; use parking_lot::Mutex; @@ -45,16 +47,14 @@ use self::dnd::{Dnd, DndState}; use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime}; pub struct EventsLoop { - display: Arc, + xconn: Arc, wm_delete_window: ffi::Atom, dnd: Dnd, ime_receiver: ImeReceiver, ime_sender: ImeSender, ime: RefCell, randr_event_offset: c_int, - windows: Arc>>, - // Please don't laugh at this type signature - shared_state: RefCell>>>, + windows: RefCell>>, devices: RefCell>, xi2ext: XExtension, pending_wakeup: Arc, @@ -67,17 +67,17 @@ pub struct EventsLoop { #[derive(Clone)] pub struct EventsLoopProxy { pending_wakeup: Weak, - display: Weak, + xconn: Weak, wakeup_dummy_window: ffi::Window, } impl EventsLoop { - pub fn new(display: Arc) -> EventsLoop { - let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) }; + pub fn new(xconn: Arc) -> EventsLoop { + let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) }; - let wm_delete_window = unsafe { display.get_atom_unchecked(b"WM_DELETE_WINDOW\0") }; + let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") }; - let dnd = Dnd::new(Arc::clone(&display)) + let dnd = Dnd::new(Arc::clone(&xconn)) .expect("Failed to call XInternAtoms when initializing drag and drop"); let (ime_sender, ime_receiver) = mpsc::channel(); @@ -85,14 +85,14 @@ impl EventsLoop { // possible to actually commit pre-edit sequences. unsafe { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); } let ime = RefCell::new({ - let result = Ime::new(Arc::clone(&display)); + let result = Ime::new(Arc::clone(&xconn)); if let Err(ImeCreationError::OpenFailure(ref state)) = result { panic!(format!("Failed to open input method: {:#?}", state)); } result.expect("Failed to set input method destruction callback") }); - let randr_event_offset = monitor::select_input(&display, root) + let randr_event_offset = monitor::select_input(&xconn, root) .expect("Failed to query XRandR extension"); let xi2ext = unsafe { @@ -101,8 +101,8 @@ impl EventsLoop { first_event_id: mem::uninitialized(), first_error_id: mem::uninitialized(), }; - let res = (display.xlib.XQueryExtension)( - display.display, + let res = (xconn.xlib.XQueryExtension)( + xconn.display, b"XInputExtension\0".as_ptr() as *const c_char, &mut result.opcode as *mut c_int, &mut result.first_event_id as *mut c_int, @@ -116,8 +116,8 @@ impl EventsLoop { unsafe { let mut xinput_major_ver = ffi::XI_2_Major; let mut xinput_minor_ver = ffi::XI_2_Minor; - if (display.xinput2.XIQueryVersion)( - display.display, + if (xconn.xinput2.XIQueryVersion)( + xconn.display, &mut xinput_major_ver, &mut xinput_minor_ver, ) != ffi::Success as libc::c_int { @@ -129,13 +129,13 @@ impl EventsLoop { } } - display.update_cached_wm_info(root); + xconn.update_cached_wm_info(root); let wakeup_dummy_window = unsafe { let (x, y, w, h) = (10, 10, 10, 10); let (border_w, border_px, background_px) = (0, 0, 0); - (display.xlib.XCreateSimpleWindow)( - display.display, + (xconn.xlib.XCreateSimpleWindow)( + xconn.display, root, x, y, @@ -148,25 +148,28 @@ impl EventsLoop { }; let result = EventsLoop { - pending_wakeup: Arc::new(AtomicBool::new(false)), - display, + xconn, wm_delete_window, dnd, ime_receiver, ime_sender, ime, randr_event_offset, - windows: Arc::new(Mutex::new(HashMap::new())), - shared_state: RefCell::new(HashMap::new()), - devices: RefCell::new(HashMap::new()), + windows: Default::default(), + devices: Default::default(), xi2ext, + pending_wakeup: Default::default(), root, wakeup_dummy_window, }; // Register for device hotplug events // (The request buffer is flushed during `init_device`) - result.display.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask).queue(); + result.xconn.select_xinput_events( + root, + ffi::XIAllDevices, + ffi::XI_HierarchyChangedMask, + ).queue(); result.init_device(ffi::XIAllDevices); @@ -176,13 +179,13 @@ impl EventsLoop { /// Returns the `XConnection` of this events loop. #[inline] pub fn x_connection(&self) -> &Arc { - &self.display + &self.xconn } pub fn create_proxy(&self) -> EventsLoopProxy { EventsLoopProxy { pending_wakeup: Arc::downgrade(&self.pending_wakeup), - display: Arc::downgrade(&self.display), + xconn: Arc::downgrade(&self.xconn), wakeup_dummy_window: self.wakeup_dummy_window, } } @@ -195,12 +198,12 @@ impl EventsLoop { // Get next event unsafe { // Ensure XNextEvent won't block - let count = (self.display.xlib.XPending)(self.display.display); + let count = (self.xconn.xlib.XPending)(self.xconn.display); if count == 0 { break; } - (self.display.xlib.XNextEvent)(self.display.display, &mut xev); + (self.xconn.xlib.XNextEvent)(self.xconn.display, &mut xev); } self.process_event(&mut xev, &mut callback); } @@ -212,7 +215,7 @@ impl EventsLoop { let mut xev = unsafe { mem::uninitialized() }; loop { - unsafe { (self.display.xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary + unsafe { (self.xconn.xlib.XNextEvent)(self.xconn.display, &mut xev) }; // Blocks as necessary let mut control_flow = ControlFlow::Continue; @@ -236,13 +239,11 @@ impl EventsLoop { fn process_event(&mut self, xev: &mut ffi::XEvent, mut callback: F) where F: FnMut(Event) { - let xlib = &self.display.xlib; - // XFilterEvent tells us when an event has been discarded by the input method. // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences, // along with an extra copy of the KeyRelease events. This also prevents backspace and // arrow keys from being detected twice. - if ffi::True == unsafe { (self.display.xlib.XFilterEvent)( + if ffi::True == unsafe { (self.xconn.xlib.XFilterEvent)( xev, { let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window } ) } { @@ -252,8 +253,8 @@ impl EventsLoop { let event_type = xev.get_type(); match event_type { ffi::MappingNotify => { - unsafe { (xlib.XRefreshKeyboardMapping)(xev.as_mut()); } - self.display.check_errors().expect("Failed to call XRefreshKeyboardMapping"); + unsafe { (self.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); } + self.xconn.check_errors().expect("Failed to call XRefreshKeyboardMapping"); } ffi::ClientMessage => { @@ -394,124 +395,92 @@ impl EventsLoop { ffi::ConfigureNotify => { let xev: &ffi::XConfigureEvent = xev.as_ref(); + let xwindow = xev.window; + let events = self.with_window(xwindow, |window| { + // So apparently... + // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root + // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent + // https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5 + // We don't want to send `Moved` when this is false, since then every `Resized` + // (whether the window moved or not) is accompanied by an extraneous `Moved` event + // that has a position relative to the parent window. + let is_synthetic = xev.send_event == ffi::True; - // So apparently... - // XSendEvent (synthetic ConfigureNotify) -> position relative to root - // XConfigureNotify (real ConfigureNotify) -> position relative to parent - // https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5 - // We don't want to send Moved when this is true, since then every Resized - // (whether the window moved or not) is accompanied by an extraneous Moved event - // that has a position relative to the parent window. - let is_synthetic = xev.send_event == ffi::True; + let new_inner_size = (xev.width as u32, xev.height as u32); + let new_inner_position = (xev.x as i32, xev.y as i32); - let window = xev.window; - let window_id = mkwid(window); + let mut shared_state_lock = window.shared_state.lock(); - let new_size = (xev.width, xev.height); - let new_position = (xev.x, xev.y); - - let (resized, moved) = { - let mut windows = self.windows.lock(); - if let Some(window_data) = windows.get_mut(&WindowId(window)) { - let (mut resized, mut moved) = (false, false); - - if window_data.config.size.is_none() { - window_data.config.size = Some(new_size); - resized = true; - } - if window_data.config.size.is_none() && is_synthetic { - window_data.config.position = Some(new_position); - moved = true; - } - - if !resized { - if window_data.config.size != Some(new_size) { - window_data.config.size = Some(new_size); - resized = true; + let (resized, moved) = { + let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size); + let moved = if is_synthetic { + util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position) + } else { + // Detect when frame extents change. + // Since this isn't synthetic, as per the notes above, this position is relative to the + // parent window. + let rel_parent = new_inner_position; + if util::maybe_change(&mut shared_state_lock.inner_position_rel_parent, rel_parent) { + // This ensures we process the next `Moved`. + shared_state_lock.inner_position = None; + // Extra insurance against stale frame extents. + shared_state_lock.frame_extents = None; } - } - if !moved && is_synthetic { - if window_data.config.position != Some(new_position) { - window_data.config.position = Some(new_position); - moved = true; - } - } - - if !is_synthetic - && window_data.config.inner_position != Some(new_position) { - window_data.config.inner_position = Some(new_position); - // This way, we get sent Moved when the decorations are toggled. - window_data.config.position = None; - self.shared_state.borrow().get(&WindowId(window)).map(|window_state| { - if let Some(window_state) = window_state.upgrade() { - // Extra insurance against stale frame extents - (*window_state.lock()).frame_extents.take(); - } - }); - } - + false + }; (resized, moved) - } else { - return; + }; + + let capacity = resized as usize + moved as usize; + let mut events = Vec::with_capacity(capacity); + + if resized { + events.push(WindowEvent::Resized(new_inner_size.0, new_inner_size.1)); } - }; - if resized { - let (width, height) = (xev.width as u32, xev.height as u32); - callback(Event::WindowEvent { - window_id, - event: WindowEvent::Resized(width, height), - }); - } - - if moved { - // We need to convert client area position to window position. - self.shared_state.borrow().get(&WindowId(window)).map(|window_state| { - if let Some(window_state) = window_state.upgrade() { - let (x, y) = { - let (inner_x, inner_y) = (xev.x as i32, xev.y as i32); - let mut window_state_lock = window_state.lock(); - if (*window_state_lock).frame_extents.is_some() { - (*window_state_lock).frame_extents - .as_ref() - .unwrap() - .inner_pos_to_outer(inner_x, inner_y) - } else { - let extents = self.display.get_frame_extents_heuristic(window, self.root); - let outer_pos = extents.inner_pos_to_outer(inner_x, inner_y); - (*window_state_lock).frame_extents = Some(extents); - outer_pos - } - }; - callback(Event::WindowEvent { - window_id, - event: WindowEvent::Moved(x, y), + if moved || shared_state_lock.position.is_none() { + // We need to convert client area position to window position. + let frame_extents = shared_state_lock.frame_extents + .as_ref() + .cloned() + .unwrap_or_else(|| { + let frame_extents = self.xconn.get_frame_extents_heuristic(xwindow, self.root); + shared_state_lock.frame_extents = Some(frame_extents.clone()); + frame_extents }); + let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1); + shared_state_lock.position = Some(outer); + if moved { + events.push(WindowEvent::Moved(outer.0, outer.1)); } - }); + } + + events + }); + + if let Some(events) = events { + for event in events { + callback(Event::WindowEvent { + window_id: mkwid(xwindow), + event, + }); + } } } ffi::ReparentNotify => { let xev: &ffi::XReparentEvent = xev.as_ref(); - let window = xev.window; - // This is generally a reliable way to detect when the window manager's been // replaced, though this event is only fired by reparenting window managers // (which is almost all of them). Failing to correctly update WM info doesn't // really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only // effect is that we waste some time trying to query unsupported properties. - self.display.update_cached_wm_info(self.root); + self.xconn.update_cached_wm_info(self.root); - self.shared_state - .borrow() - .get(&WindowId(window)) - .map(|window_state| { - if let Some(window_state) = window_state.upgrade() { - (*window_state.lock()).frame_extents.take(); - } - }); + self.with_window(xev.window, |window| { + window.invalidate_cached_frame_extents(); + }); } ffi::DestroyNotify => { @@ -522,7 +491,7 @@ impl EventsLoop { // In the event that the window's been destroyed without being dropped first, we // cleanup again here. - self.windows.lock().remove(&WindowId(window)); + self.windows.borrow_mut().remove(&WindowId(window)); // Since all XIM stuff needs to happen from the same thread, we destroy the input // context here instead of when dropping the window. @@ -575,14 +544,14 @@ impl EventsLoop { let keysym = unsafe { let mut keysym = 0; - (self.display.xlib.XLookupString)( + (self.xconn.xlib.XLookupString)( xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut(), ); - self.display.check_errors().expect("Failed to lookup keysym"); + self.xconn.check_errors().expect("Failed to lookup keysym"); keysym }; let virtual_keycode = events::keysym_to_element(keysym as c_uint); @@ -603,7 +572,7 @@ impl EventsLoop { if state == Pressed { let written = if let Some(ic) = self.ime.borrow().get_context(window) { - self.display.lookup_utf8(ic, xkev) + self.xconn.lookup_utf8(ic, xkev) } else { return; }; @@ -619,7 +588,7 @@ impl EventsLoop { } ffi::GenericEvent => { - let guard = if let Some(e) = GenericEventCookie::from_event(&self.display, *xev) { e } else { return }; + let guard = if let Some(e) = GenericEventCookie::from_event(&self.xconn, *xev) { e } else { return }; let xev = &guard.cookie; if self.xi2ext.opcode != xev.extension { return; @@ -637,15 +606,11 @@ impl EventsLoop { let window_id = mkwid(xev.event); let device_id = mkdid(xev.deviceid); if (xev.flags & ffi::XIPointerEmulated) != 0 { - let windows = self.windows.lock(); - if let Some(window_data) = windows.get(&WindowId(xev.event)) { - if window_data.multitouch { - // Deliver multi-touch events instead of emulated mouse events. - return; - } - } else { - return; - } + // Deliver multi-touch events instead of emulated mouse events. + let return_now = self + .with_window(xev.event, |window| window.multitouch) + .unwrap_or(true); + if return_now { return; } } let modifiers = ModifiersState::from(xev.mods); @@ -724,21 +689,11 @@ impl EventsLoop { let modifiers = ModifiersState::from(xev.mods); - // Gymnastics to ensure self.windows isn't locked when we invoke callback - if { - let mut windows = self.windows.lock(); - let window_data = { - if let Some(window_data) = windows.get_mut(&WindowId(xev.event)) { - window_data - } else { - return; - } - }; - if Some(new_cursor_pos) != window_data.cursor_pos { - window_data.cursor_pos = Some(new_cursor_pos); - true - } else { false } - } { + let cursor_moved = self.with_window(xev.event, |window| { + let mut shared_state_lock = window.shared_state.lock(); + util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos) + }); + if cursor_moved == Some(true) { callback(Event::WindowEvent { window_id, event: CursorMoved { @@ -747,6 +702,8 @@ impl EventsLoop { modifiers, }, }); + } else if cursor_moved.is_none() { + return; } // More gymnastics, for self.devices @@ -804,7 +761,7 @@ impl EventsLoop { let window_id = mkwid(xev.event); let device_id = mkdid(xev.deviceid); - if let Some(all_info) = DeviceInfo::get(&self.display, ffi::XIAllDevices) { + if let Some(all_info) = DeviceInfo::get(&self.xconn, ffi::XIAllDevices) { let mut devices = self.devices.borrow_mut(); for device_info in all_info.iter() { if device_info.deviceid == xev.sourceid @@ -830,7 +787,7 @@ impl EventsLoop { // The mods field on this event isn't actually populated, so query the // pointer device. In the future, we can likely remove this round-trip by // relying on Xkb for modifier values. - let modifiers = self.display.query_pointer(xev.event, xev.deviceid) + let modifiers = self.xconn.query_pointer(xev.event, xev.deviceid) .expect("Failed to query pointer device").get_modifier_state(); callback(Event::WindowEvent { window_id, event: CursorMoved { @@ -844,11 +801,7 @@ impl EventsLoop { // Leave, FocusIn, and FocusOut can be received by a window that's already // been destroyed, which the user presumably doesn't want to deal with. - let window_closed = self.windows - .lock() - .get(&WindowId(xev.event)) - .is_none(); - + let window_closed = !self.window_exists(xev.event); if !window_closed { callback(Event::WindowEvent { window_id: mkwid(xev.event), @@ -859,11 +812,9 @@ impl EventsLoop { ffi::XI_FocusIn => { let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; + if !self.window_exists(xev.event) { return; } let window_id = mkwid(xev.event); - if let None = self.windows.lock().get(&WindowId(xev.event)) { - return; - } self.ime .borrow_mut() .focus(xev.event) @@ -890,15 +841,11 @@ impl EventsLoop { } ffi::XI_FocusOut => { let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; - - if let None = self.windows.lock().get(&WindowId(xev.event)) { - return; - } + if !self.window_exists(xev.event) { return; } self.ime .borrow_mut() .unfocus(xev.event) .expect("Failed to unfocus input context"); - callback(Event::WindowEvent { window_id: mkwid(xev.event), event: Focused(false), @@ -993,13 +940,13 @@ impl EventsLoop { let scancode = (keycode - 8) as u32; let keysym = unsafe { - (self.display.xlib.XKeycodeToKeysym)( - self.display.display, + (self.xconn.xlib.XKeycodeToKeysym)( + self.xconn.display, xev.detail as ffi::KeyCode, 0, ) }; - self.display.check_errors().expect("Failed to lookup raw keysym"); + self.xconn.check_errors().expect("Failed to lookup raw keysym"); let virtual_keycode = events::keysym_to_element(keysym as c_uint); @@ -1054,18 +1001,43 @@ impl EventsLoop { fn init_device(&self, device: c_int) { let mut devices = self.devices.borrow_mut(); - if let Some(info) = DeviceInfo::get(&self.display, device) { + if let Some(info) = DeviceInfo::get(&self.xconn, device) { for info in info.iter() { devices.insert(DeviceId(info.deviceid), Device::new(&self, info)); } } } + + fn with_window(&self, window_id: ffi::Window, callback: F) -> Option + where F: Fn(&UnownedWindow) -> T + { + let mut deleted = false; + let window_id = WindowId(window_id); + let result = self.windows + .borrow() + .get(&window_id) + .and_then(|window| { + let arc = window.upgrade(); + deleted = arc.is_none(); + arc + }) + .map(|window| callback(&*window)); + if deleted { + // Garbage collection + self.windows.borrow_mut().remove(&window_id); + } + result + } + + fn window_exists(&self, window_id: ffi::Window) -> bool { + self.with_window(window_id, |_| ()).is_some() + } } impl EventsLoopProxy { pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { // Update the `EventsLoop`'s `pending_wakeup` flag. - let display = match (self.pending_wakeup.upgrade(), self.display.upgrade()) { + let display = match (self.pending_wakeup.upgrade(), self.xconn.upgrade()) { (Some(wakeup), Some(display)) => { wakeup.store(true, atomic::Ordering::Relaxed); display @@ -1091,24 +1063,24 @@ impl EventsLoopProxy { } struct DeviceInfo<'a> { - display: &'a XConnection, + xconn: &'a XConnection, info: *const ffi::XIDeviceInfo, count: usize, } impl<'a> DeviceInfo<'a> { - fn get(display: &'a XConnection, device: c_int) -> Option { + fn get(xconn: &'a XConnection, device: c_int) -> Option { unsafe { let mut count = mem::uninitialized(); - let info = (display.xinput2.XIQueryDevice)(display.display, device, &mut count); - display.check_errors() + let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count); + xconn.check_errors() .ok() .and_then(|_| { if info.is_null() || count == 0 { None } else { Some(DeviceInfo { - display, + xconn, info, count: count as usize, }) @@ -1121,11 +1093,11 @@ impl<'a> DeviceInfo<'a> { impl<'a> Drop for DeviceInfo<'a> { fn drop(&mut self) { assert!(!self.info.is_null()); - unsafe { (self.display.xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; + unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; } } -impl<'a> ::std::ops::Deref for DeviceInfo<'a> { +impl<'a> Deref for DeviceInfo<'a> { type Target = [ffi::XIDeviceInfo]; fn deref(&self) -> &Self::Target { unsafe { slice::from_raw_parts(self.info, self.count) } @@ -1139,57 +1111,41 @@ pub struct WindowId(ffi::Window); pub struct DeviceId(c_int); pub struct Window { - pub window: Arc, - display: Weak, - windows: Weak>>, + pub window: Arc, ime_sender: Mutex, } -impl ::std::ops::Deref for Window { - type Target = Window2; +impl Deref for Window { + type Target = UnownedWindow; #[inline] - fn deref(&self) -> &Window2 { + fn deref(&self) -> &UnownedWindow { &*self.window } } impl Window { pub fn new( - x_events_loop: &EventsLoop, + event_loop: &EventsLoop, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes ) -> Result { - let multitouch = attribs.multitouch; - let win = Arc::new(Window2::new(&x_events_loop, attribs, pl_attribs)?); + let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?); - x_events_loop.shared_state + event_loop.windows .borrow_mut() - .insert(win.id(), Arc::downgrade(&win.shared_state)); + .insert(window.id(), Arc::downgrade(&window)); - x_events_loop.ime + event_loop.ime .borrow_mut() - .create_context(win.id().0) + .create_context(window.id().0) .expect("Failed to create input context"); - x_events_loop.windows.lock().insert(win.id(), WindowData { - config: Default::default(), - multitouch, - cursor_pos: None, - }); - Ok(Window { - window: win, - windows: Arc::downgrade(&x_events_loop.windows), - display: Arc::downgrade(&x_events_loop.display), - ime_sender: Mutex::new(x_events_loop.ime_sender.clone()), + window, + ime_sender: Mutex::new(event_loop.ime_sender.clone()), }) } - #[inline] - pub fn id(&self) -> WindowId { - self.window.id() - } - #[inline] pub fn send_xim_spot(&self, x: i16, y: i16) { let _ = self.ime_sender @@ -1200,47 +1156,28 @@ impl Window { impl Drop for Window { fn drop(&mut self) { - if let (Some(windows), Some(display)) = (self.windows.upgrade(), self.display.upgrade()) { - if let Some(_) = windows.lock().remove(&self.window.id()) { - unsafe { - (display.xlib.XDestroyWindow)(display.display, self.window.id().0); - } - } + let xconn = &self.window.xconn; + unsafe { + (xconn.xlib.XDestroyWindow)(xconn.display, self.window.id().0); + // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about. + let _ = xconn.check_errors(); } } } -/// State maintained for translating window-related events -#[derive(Debug)] -struct WindowData { - config: WindowConfig, - multitouch: bool, - cursor_pos: Option<(f64, f64)>, -} - -// Required by ffi members -unsafe impl Send for WindowData {} - -#[derive(Debug, Default)] -struct WindowConfig { - pub size: Option<(c_int, c_int)>, - pub position: Option<(c_int, c_int)>, - pub inner_position: Option<(c_int, c_int)>, -} - /// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to /// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed struct GenericEventCookie<'a> { - display: &'a XConnection, + xconn: &'a XConnection, cookie: ffi::XGenericEventCookie } impl<'a> GenericEventCookie<'a> { - fn from_event<'b>(display: &'b XConnection, event: ffi::XEvent) -> Option> { + fn from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option> { unsafe { let mut cookie: ffi::XGenericEventCookie = From::from(event); - if (display.xlib.XGetEventData)(display.display, &mut cookie) == ffi::True { - Some(GenericEventCookie{display: display, cookie: cookie}) + if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True { + Some(GenericEventCookie { xconn, cookie }) } else { None } @@ -1251,8 +1188,7 @@ impl<'a> GenericEventCookie<'a> { impl<'a> Drop for GenericEventCookie<'a> { fn drop(&mut self) { unsafe { - let xlib = &self.display.xlib; - (xlib.XFreeEventData)(self.display.display, &mut self.cookie); + (self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie); } } } @@ -1302,7 +1238,7 @@ impl Device { | ffi::XI_RawKeyPressMask | ffi::XI_RawKeyReleaseMask; // The request buffer is flushed when we poll for events - el.display.select_xinput_events(el.root, info.deviceid, mask).queue(); + el.xconn.select_xinput_events(el.root, info.deviceid, mask).queue(); // Identify scroll axes for class_ptr in Device::classes(info) { diff --git a/src/platform/linux/x11/util/mod.rs b/src/platform/linux/x11/util/mod.rs index 101521f5..a0e3df70 100644 --- a/src/platform/linux/x11/util/mod.rs +++ b/src/platform/linux/x11/util/mod.rs @@ -31,6 +31,16 @@ use std::os::raw::*; use super::{ffi, XConnection, XError}; +pub fn maybe_change(field: &mut Option, value: T) -> bool { + let wrapped = Some(value); + if *field != wrapped { + *field = wrapped; + true + } else { + false + } +} + #[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."] pub struct Flusher<'a> { xconn: &'a XConnection, diff --git a/src/platform/linux/x11/window.rs b/src/platform/linux/x11/window.rs index 75671364..4f8a2632 100644 --- a/src/platform/linux/x11/window.rs +++ b/src/platform/linux/x11/window.rs @@ -1,5 +1,4 @@ use std::{cmp, env, mem}; -use std::borrow::Borrow; use std::ffi::CString; use std::os::raw::*; use std::path::Path; @@ -28,38 +27,41 @@ unsafe extern "C" fn visibility_predicate( (event.window == window && event.type_ == ffi::VisibilityNotify) as _ } -pub struct XWindow { - pub display: Arc, - pub window: ffi::Window, - pub root: ffi::Window, - pub screen_id: i32, -} - -unsafe impl Send for XWindow {} -unsafe impl Sync for XWindow {} - -unsafe impl Send for Window2 {} -unsafe impl Sync for Window2 {} - #[derive(Debug, Default)] pub struct SharedState { + pub multitouch: bool, + pub cursor_pos: Option<(f64, f64)>, + pub size: Option<(u32, u32)>, + pub position: Option<(i32, i32)>, + pub inner_position: Option<(i32, i32)>, + pub inner_position_rel_parent: Option<(i32, i32)>, + pub last_monitor: Option, + pub dpi_adjusted: Option<(f64, f64)>, pub frame_extents: Option, } -pub struct Window2 { - pub x: Arc, +unsafe impl Send for UnownedWindow {} +unsafe impl Sync for UnownedWindow {} + +pub struct UnownedWindow { + pub xconn: Arc, // never changes + xwindow: ffi::Window, // never changes + root: ffi::Window, // never changes + screen_id: i32, // never changes cursor: Mutex, cursor_state: Mutex, - pub shared_state: Arc>, + pub multitouch: bool, // never changes + pub shared_state: Mutex, } -impl Window2 { +impl UnownedWindow { pub fn new( - ctx: &EventsLoop, + event_loop: &EventsLoop, window_attrs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, - ) -> Result { - let xconn = &ctx.display; + ) -> Result { + let xconn = &event_loop.xconn; + let root = event_loop.root; let dimensions = { // x11 only applies constraints when the window is actively resized @@ -81,9 +83,6 @@ impl Window2 { None => unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }, }; - // getting the root window - let root = ctx.root; - // creating let mut set_win_attr = { let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() }; @@ -121,7 +120,7 @@ impl Window2 { } // finally creating the window - let window = unsafe { + let xwindow = unsafe { (xconn.xlib.XCreateWindow)( xconn.display, root, @@ -144,18 +143,15 @@ impl Window2 { ) }; - let x_window = Arc::new(XWindow { - display: Arc::clone(xconn), - window, + let window = UnownedWindow { + xconn: Arc::clone(xconn), + xwindow, root, screen_id, - }); - - let window = Window2 { - x: x_window, - cursor: Mutex::new(MouseCursor::Default), - cursor_state: Mutex::new(CursorState::Normal), - shared_state: Arc::new(Mutex::new(SharedState::default())), + cursor: Default::default(), + cursor_state: Default::default(), + multitouch: window_attrs.multitouch, + shared_state: Default::default(), }; // Title must be set before mapping. Some tiling window managers (i.e. i3) use the window @@ -165,14 +161,12 @@ impl Window2 { window.set_decorations_inner(window_attrs.decorations).queue(); { - let ref x_window: &XWindow = window.x.borrow(); - // Enable drag and drop (TODO: extend API to make this toggleable) unsafe { let dnd_aware_atom = xconn.get_atom_unchecked(b"XdndAware\0"); let version = &[5 as c_ulong]; // Latest version; hasn't changed since 2002 xconn.change_property( - x_window.window, + window.xwindow, dnd_aware_atom, ffi::XA_ATOM, util::PropMode::Replace, @@ -215,21 +209,16 @@ impl Window2 { unsafe { (xconn.xlib.XSetClassHint)( xconn.display, - x_window.window, + window.xwindow, class_hint.ptr, ); }//.queue(); } - Window2::set_pid(xconn, x_window.window) - .map(|flusher| flusher.queue()); + window.set_pid().map(|flusher| flusher.queue()); if pl_attribs.x11_window_type != Default::default() { - Window2::set_window_type( - xconn, - x_window.window, - pl_attribs.x11_window_type, - ).queue(); + window.set_window_type(pl_attribs.x11_window_type).queue(); } // set size hints @@ -261,7 +250,7 @@ impl Window2 { unsafe { (xconn.xlib.XSetWMNormalHints)( xconn.display, - x_window.window, + window.xwindow, size_hints.ptr, ); }//.queue(); @@ -276,8 +265,8 @@ impl Window2 { unsafe { (xconn.xlib.XSetWMProtocols)( xconn.display, - x_window.window, - &ctx.wm_delete_window as *const _ as *mut _, + window.xwindow, + &event_loop.wm_delete_window as *const ffi::Atom as *mut ffi::Atom, 1, ); }//.queue(); @@ -285,7 +274,7 @@ impl Window2 { // Set visibility (map window) if window_attrs.visible { unsafe { - (xconn.xlib.XMapRaised)(xconn.display, x_window.window); + (xconn.xlib.XMapRaised)(xconn.display, window.xwindow); }//.queue(); } @@ -320,7 +309,7 @@ impl Window2 { } mask }; - xconn.select_xinput_events(x_window.window, ffi::XIAllMasterDevices, mask).queue(); + xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask).queue(); // These properties must be set after mapping if window_attrs.maximized { @@ -342,11 +331,11 @@ impl Window2 { xconn.display, &mut event as *mut ffi::XEvent, Some(visibility_predicate), - x_window.window as _, + window.xwindow as _, ); (xconn.xlib.XSetInputFocus)( xconn.display, - x_window.window, + window.xwindow, ffi::RevertToParent, ffi::CurrentTime, ); @@ -362,9 +351,9 @@ impl Window2 { )) } - fn set_pid(xconn: &Arc, window: ffi::Window) -> Option { - let pid_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PID\0") }; - let client_machine_atom = unsafe { xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") }; + fn set_pid(&self) -> Option { + let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") }; + let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") }; unsafe { let (hostname, hostname_length) = { // 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is @@ -377,15 +366,15 @@ impl Window2 { let hostname_length = libc::strlen(hostname.as_ptr()); (hostname, hostname_length as usize) }; - xconn.change_property( - window, + self.xconn.change_property( + self.xwindow, pid_atom, ffi::XA_CARDINAL, util::PropMode::Replace, &[libc::getpid() as util::Cardinal], ).queue(); - let flusher = xconn.change_property( - window, + let flusher = self.xconn.change_property( + self.xwindow, client_machine_atom, ffi::XA_STRING, util::PropMode::Replace, @@ -395,15 +384,11 @@ impl Window2 { } } - fn set_window_type( - xconn: &Arc, - window: ffi::Window, - window_type: util::WindowType, - ) -> util::Flusher { - let hint_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") }; - let window_type_atom = window_type.as_atom(xconn); - xconn.change_property( - window, + fn set_window_type(&self, window_type: util::WindowType) -> util::Flusher { + let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") }; + let window_type_atom = window_type.as_atom(&self.xconn); + self.xconn.change_property( + self.xwindow, hint_atom, ffi::XA_ATOM, util::PropMode::Replace, @@ -412,27 +397,24 @@ impl Window2 { } pub fn set_urgent(&self, is_urgent: bool) { - let xconn = &self.x.display; - let mut wm_hints = xconn.get_wm_hints(self.x.window).expect("`XGetWMHints` failed"); + let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed"); if is_urgent { (*wm_hints).flags |= ffi::XUrgencyHint; } else { (*wm_hints).flags &= !ffi::XUrgencyHint; } - xconn.set_wm_hints(self.x.window, wm_hints).flush().expect("Failed to set urgency hint"); + self.xconn.set_wm_hints(self.xwindow, wm_hints).flush().expect("Failed to set urgency hint"); } fn set_netwm( - xconn: &Arc, - window: ffi::Window, - root: ffi::Window, + &self, + operation: util::StateOperation, properties: (c_long, c_long, c_long, c_long), - operation: util::StateOperation ) -> util::Flusher { - let state_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE\0") }; - xconn.send_client_msg( - window, - root, + let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") }; + self.xconn.send_client_msg( + self.xwindow, + self.root, state_atom, Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask), [ @@ -446,15 +428,8 @@ impl Window2 { } fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher { - let xconn = &self.x.display; - let fullscreen_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") }; - Window2::set_netwm( - xconn, - self.x.window, - self.x.root, - (fullscreen_atom as c_long, 0, 0, 0), - fullscreen.into(), - ) + let fullscreen_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") }; + self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0)) } fn set_fullscreen_inner(&self, monitor: Option) -> util::Flusher { @@ -488,20 +463,13 @@ impl Window2 { } pub fn get_current_monitor(&self) -> X11MonitorId { - get_monitor_for_window(&self.x.display, self.get_rect()).to_owned() + get_monitor_for_window(&self.xconn, self.get_rect()).to_owned() } fn set_maximized_inner(&self, maximized: bool) -> util::Flusher { - let xconn = &self.x.display; - let horz_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0") }; - let vert_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0") }; - Window2::set_netwm( - xconn, - self.x.window, - self.x.root, - (horz_atom as c_long, vert_atom as c_long, 0, 0), - maximized.into(), - ) + let horz_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0") }; + let vert_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0") }; + self.set_netwm(maximized.into(), (horz_atom as c_long, vert_atom as c_long, 0, 0)) } pub fn set_maximized(&self, maximized: bool) { @@ -512,18 +480,17 @@ impl Window2 { } fn set_title_inner(&self, title: &str) -> util::Flusher { - let xconn = &self.x.display; - let wm_name_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_NAME\0") }; - let utf8_atom = unsafe { xconn.get_atom_unchecked(b"UTF8_STRING\0") }; + let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") }; + let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; let title = CString::new(title).expect("Window title contained null byte"); unsafe { - (xconn.xlib.XStoreName)( - xconn.display, - self.x.window, + (self.xconn.xlib.XStoreName)( + self.xconn.display, + self.xwindow, title.as_ptr() as *const c_char, ); - xconn.change_property( - self.x.window, + self.xconn.change_property( + self.xwindow, wm_name_atom, utf8_atom, util::PropMode::Replace, @@ -539,10 +506,9 @@ impl Window2 { } fn set_decorations_inner(&self, decorations: bool) -> util::Flusher { - let xconn = &self.x.display; - let wm_hints = unsafe { xconn.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") }; - xconn.change_property( - self.x.window, + let wm_hints = unsafe { self.xconn.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") }; + self.xconn.change_property( + self.xwindow, wm_hints, wm_hints, util::PropMode::Replace, @@ -564,15 +530,8 @@ impl Window2 { } fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher { - let xconn = &self.x.display; - let above_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") }; - Window2::set_netwm( - xconn, - self.x.window, - self.x.root, - (above_atom as c_long, 0, 0, 0), - always_on_top.into(), - ) + let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") }; + self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0)) } pub fn set_always_on_top(&self, always_on_top: bool) { @@ -582,11 +541,10 @@ impl Window2 { } fn set_icon_inner(&self, icon: Icon) -> util::Flusher { - let xconn = &self.x.display; - let icon_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; + let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; let data = icon.to_cardinals(); - xconn.change_property( - self.x.window, + self.xconn.change_property( + self.xwindow, icon_atom, ffi::XA_CARDINAL, util::PropMode::Replace, @@ -595,11 +553,10 @@ impl Window2 { } fn unset_icon_inner(&self) -> util::Flusher { - let xconn = &self.x.display; - let icon_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; + let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; let empty_data: [util::Cardinal; 0] = []; - xconn.change_property( - self.x.window, + self.xconn.change_property( + self.xwindow, icon_atom, ffi::XA_CARDINAL, util::PropMode::Replace, @@ -616,26 +573,26 @@ impl Window2 { pub fn show(&self) { unsafe { - (self.x.display.xlib.XMapRaised)(self.x.display.display, self.x.window); - self.x.display.flush_requests() + (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow); + self.xconn.flush_requests() .expect("Failed to call XMapRaised"); } } pub fn hide(&self) { unsafe { - (self.x.display.xlib.XUnmapWindow)(self.x.display.display, self.x.window); - self.x.display.flush_requests() + (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); + self.xconn.flush_requests() .expect("Failed to call XUnmapWindow"); } } fn update_cached_frame_extents(&self) { - let extents = self.x.display.get_frame_extents_heuristic(self.x.window, self.x.root); + let extents = self.xconn.get_frame_extents_heuristic(self.xwindow, self.root); (*self.shared_state.lock()).frame_extents = Some(extents); } - fn invalidate_cached_frame_extents(&self) { + pub fn invalidate_cached_frame_extents(&self) { (*self.shared_state.lock()).frame_extents.take(); } @@ -654,7 +611,7 @@ impl Window2 { #[inline] pub fn get_inner_position(&self) -> Option<(i32, i32)> { - self.x.display.translate_coords(self.x.window, self.x.root ) + self.xconn.translate_coords(self.xwindow, self.root ) .ok() .map(|coords| (coords.x_rel_root, coords.y_rel_root)) } @@ -673,19 +630,19 @@ impl Window2 { } } unsafe { - (self.x.display.xlib.XMoveWindow)( - self.x.display.display, - self.x.window, + (self.xconn.xlib.XMoveWindow)( + self.xconn.display, + self.xwindow, x as c_int, y as c_int, ); - self.x.display.flush_requests() + self.xconn.flush_requests() }.expect("Failed to call XMoveWindow"); } #[inline] pub fn get_inner_size(&self) -> Option<(u32, u32)> { - self.x.display.get_geometry(self.x.window) + self.xconn.get_geometry(self.xwindow) .ok() .map(|geo| (geo.width, geo.height)) } @@ -706,39 +663,37 @@ impl Window2 { #[inline] pub fn set_inner_size(&self, width: u32, height: u32) { unsafe { - (self.x.display.xlib.XResizeWindow)( - self.x.display.display, - self.x.window, + (self.xconn.xlib.XResizeWindow)( + self.xconn.display, + self.xwindow, width as c_uint, height as c_uint, ); - self.x.display.flush_requests() + self.xconn.flush_requests() }.expect("Failed to call XResizeWindow"); } unsafe fn update_normal_hints(&self, callback: F) -> Result<(), XError> where F: FnOnce(*mut ffi::XSizeHints) -> () { - let xconn = &self.x.display; - - let size_hints = xconn.alloc_size_hints(); + let size_hints = self.xconn.alloc_size_hints(); let mut flags: c_long = mem::uninitialized(); - (xconn.xlib.XGetWMNormalHints)( - xconn.display, - self.x.window, + (self.xconn.xlib.XGetWMNormalHints)( + self.xconn.display, + self.xwindow, size_hints.ptr, &mut flags, ); - xconn.check_errors()?; + self.xconn.check_errors()?; callback(size_hints.ptr); - (xconn.xlib.XSetWMNormalHints)( - xconn.display, - self.x.window, + (self.xconn.xlib.XSetWMNormalHints)( + self.xconn.display, + self.xwindow, size_hints.ptr, ); - xconn.flush_requests()?; + self.xconn.flush_requests()?; Ok(()) } @@ -773,44 +728,44 @@ impl Window2 { #[inline] pub fn get_xlib_display(&self) -> *mut c_void { - self.x.display.display as _ + self.xconn.display as _ } #[inline] pub fn get_xlib_screen_id(&self) -> c_int { - self.x.screen_id + self.screen_id } #[inline] pub fn get_xlib_xconnection(&self) -> Arc { - self.x.display.clone() + Arc::clone(&self.xconn) } #[inline] pub fn platform_display(&self) -> *mut libc::c_void { - self.x.display.display as _ + self.xconn.display as _ } #[inline] pub fn get_xlib_window(&self) -> c_ulong { - self.x.window + self.xwindow } #[inline] pub fn platform_window(&self) -> *mut libc::c_void { - self.x.window as _ + self.xwindow as _ } pub fn get_xcb_connection(&self) -> *mut c_void { unsafe { - (self.x.display.xlib_xcb.XGetXCBConnection)(self.x.display.display) as *mut _ + (self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _ } } fn load_cursor(&self, name: &[u8]) -> ffi::Cursor { unsafe { - (self.x.display.xcursor.XcursorLibraryLoadCursor)( - self.x.display.display, + (self.xconn.xcursor.XcursorLibraryLoadCursor)( + self.xconn.display, name.as_ptr() as *const c_char, ) } @@ -890,19 +845,18 @@ impl Window2 { fn update_cursor(&self, cursor: ffi::Cursor) { unsafe { - (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, cursor); + (self.xconn.xlib.XDefineCursor)(self.xconn.display, self.xwindow, cursor); if cursor != 0 { - (self.x.display.xlib.XFreeCursor)(self.x.display.display, cursor); + (self.xconn.xlib.XFreeCursor)(self.xconn.display, cursor); } - self.x.display.flush_requests().expect("Failed to set or free the cursor"); + self.xconn.flush_requests().expect("Failed to set or free the cursor"); } } pub fn set_cursor(&self, cursor: MouseCursor) { - let mut current_cursor = self.cursor.lock(); - *current_cursor = cursor; + *self.cursor.lock() = cursor; if *self.cursor_state.lock() != CursorState::Hide { - self.update_cursor(self.get_cursor(*current_cursor)); + self.update_cursor(self.get_cursor(cursor)); } } @@ -912,9 +866,9 @@ impl Window2 { fn create_empty_cursor(&self) -> Option { let data = 0; let pixmap = unsafe { - (self.x.display.xlib.XCreateBitmapFromData)( - self.x.display.display, - self.x.window, + (self.xconn.xlib.XCreateBitmapFromData)( + self.xconn.display, + self.xwindow, &data, 1, 1, @@ -929,8 +883,8 @@ impl Window2 { // We don't care about this color, since it only fills bytes // in the pixmap which are not 0 in the mask. let dummy_color: ffi::XColor = mem::uninitialized(); - let cursor = (self.x.display.xlib.XCreatePixmapCursor)( - self.x.display.display, + let cursor = (self.xconn.xlib.XCreatePixmapCursor)( + self.xconn.display, pixmap, pixmap, &dummy_color as *const _ as *mut _, @@ -938,26 +892,27 @@ impl Window2 { 0, 0, ); - (self.x.display.xlib.XFreePixmap)(self.x.display.display, pixmap); + (self.xconn.xlib.XFreePixmap)(self.xconn.display, pixmap); cursor }; Some(cursor) } pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> { - use CursorState::{ Grab, Normal, Hide }; + use CursorState::*; - let mut cursor_state = self.cursor_state.lock(); - match (state, *cursor_state) { + let mut cursor_state_lock = self.cursor_state.lock(); + + match (state, *cursor_state_lock) { (Normal, Normal) | (Hide, Hide) | (Grab, Grab) => return Ok(()), _ => {}, } - match *cursor_state { + match *cursor_state_lock { Grab => { unsafe { - (self.x.display.xlib.XUngrabPointer)(self.x.display.display, ffi::CurrentTime); - self.x.display.flush_requests().expect("Failed to call XUngrabPointer"); + (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); + self.xconn.flush_requests().expect("Failed to call XUngrabPointer"); } }, Normal => {}, @@ -966,11 +921,11 @@ impl Window2 { match state { Normal => { - *cursor_state = state; + *cursor_state_lock = state; Ok(()) }, Hide => { - *cursor_state = state; + *cursor_state_lock = state; self.update_cursor( self.create_empty_cursor().expect("Failed to create empty cursor") ); @@ -980,20 +935,20 @@ impl Window2 { unsafe { // Ungrab before grabbing to prevent passive grabs // from causing AlreadyGrabbed - (self.x.display.xlib.XUngrabPointer)(self.x.display.display, ffi::CurrentTime); + (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); - match (self.x.display.xlib.XGrabPointer)( - self.x.display.display, self.x.window, ffi::True, + match (self.xconn.xlib.XGrabPointer)( + self.xconn.display, self.xwindow, ffi::True, (ffi::ButtonPressMask | ffi::ButtonReleaseMask | ffi::EnterWindowMask | ffi::LeaveWindowMask | ffi::PointerMotionMask | ffi::PointerMotionHintMask | ffi::Button1MotionMask | ffi::Button2MotionMask | ffi::Button3MotionMask | ffi::Button4MotionMask | ffi::Button5MotionMask | ffi::ButtonMotionMask | ffi::KeymapStateMask) as c_uint, ffi::GrabModeAsync, ffi::GrabModeAsync, - self.x.window, 0, ffi::CurrentTime + self.xwindow, 0, ffi::CurrentTime ) { ffi::GrabSuccess => { - *cursor_state = state; + *cursor_state_lock = state; Ok(()) }, ffi::AlreadyGrabbed | ffi::GrabInvalidTime | @@ -1012,10 +967,10 @@ impl Window2 { pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> { unsafe { - (self.x.display.xlib.XWarpPointer)( - self.x.display.display, + (self.xconn.xlib.XWarpPointer)( + self.xconn.display, 0, - self.x.window, + self.xwindow, 0, 0, 0, @@ -1023,10 +978,10 @@ impl Window2 { x, y, ); - self.x.display.flush_requests().map_err(|_| ()) + self.xconn.flush_requests().map_err(|_| ()) } } #[inline] - pub fn id(&self) -> WindowId { WindowId(self.x.window) } + pub fn id(&self) -> WindowId { WindowId(self.xwindow) } }