1 #![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
2 
3 pub mod ffi;
4 mod events;
5 mod monitor;
6 mod window;
7 mod xdisplay;
8 mod dnd;
9 mod ime;
10 pub mod util;
11 
12 pub use self::monitor::MonitorId;
13 pub use self::window::UnownedWindow;
14 pub use self::xdisplay::{XConnection, XNotSupported, XError};
15 
16 use std::{mem, ptr, slice};
17 use std::cell::RefCell;
18 use std::collections::HashMap;
19 use std::ffi::CStr;
20 use std::ops::Deref;
21 use std::os::raw::*;
22 use libc::{select, fd_set, FD_SET, FD_ZERO, FD_ISSET, EINTR, EINVAL, ENOMEM, EBADF};
23 #[cfg(target_os = "linux")]
24 use libc::__errno_location;
25 #[cfg(target_os = "freebsd")]
26 use libc::__error as __errno_location;
27 #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
28 use libc::__errno as __errno_location;
29 use std::sync::{Arc, mpsc, Weak};
30 use std::sync::atomic::{self, AtomicBool};
31 
32 use libc::{self, setlocale, LC_CTYPE};
33 
34 use {
35     ControlFlow,
36     CreationError,
37     DeviceEvent,
38     Event,
39     EventsLoopClosed,
40     KeyboardInput,
41     LogicalPosition,
42     LogicalSize,
43     WindowAttributes,
44     WindowEvent,
45 };
46 use events::ModifiersState;
47 use platform::PlatformSpecificWindowBuilderAttributes;
48 use self::dnd::{Dnd, DndState};
49 use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime};
50 
51 pub struct EventsLoop {
52     xconn: Arc<XConnection>,
53     wm_delete_window: ffi::Atom,
54     dnd: Dnd,
55     ime_receiver: ImeReceiver,
56     ime_sender: ImeSender,
57     ime: RefCell<Ime>,
58     randr_event_offset: c_int,
59     windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
60     devices: RefCell<HashMap<DeviceId, Device>>,
61     xi2ext: XExtension,
62     pending_wakeup: Arc<AtomicBool>,
63     root: ffi::Window,
64     // A dummy, `InputOnly` window that we can use to receive wakeup events and interrupt blocking
65     // `XNextEvent` calls.
66     wakeup_dummy_window: ffi::Window,
67 }
68 
69 #[derive(Clone)]
70 pub struct EventsLoopProxy {
71     pending_wakeup: Weak<AtomicBool>,
72     xconn: Weak<XConnection>,
73     wakeup_dummy_window: ffi::Window,
74 }
75 
76 impl EventsLoop {
new(xconn: Arc<XConnection>) -> EventsLoop77     pub fn new(xconn: Arc<XConnection>) -> EventsLoop {
78         let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
79 
80         let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
81 
82         let dnd = Dnd::new(Arc::clone(&xconn))
83             .expect("Failed to call XInternAtoms when initializing drag and drop");
84 
85         let (ime_sender, ime_receiver) = mpsc::channel();
86         // Input methods will open successfully without setting the locale, but it won't be
87         // possible to actually commit pre-edit sequences.
88         unsafe { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); }
89         let ime = RefCell::new({
90             let result = Ime::new(Arc::clone(&xconn));
91             if let Err(ImeCreationError::OpenFailure(ref state)) = result {
92                 panic!(format!("Failed to open input method: {:#?}", state));
93             }
94             result.expect("Failed to set input method destruction callback")
95         });
96 
97         let randr_event_offset = xconn.select_xrandr_input(root)
98             .expect("Failed to query XRandR extension");
99 
100         let xi2ext = unsafe {
101             let mut result = XExtension {
102                 opcode: mem::uninitialized(),
103                 first_event_id: mem::uninitialized(),
104                 first_error_id: mem::uninitialized(),
105             };
106             let res = (xconn.xlib.XQueryExtension)(
107                 xconn.display,
108                 b"XInputExtension\0".as_ptr() as *const c_char,
109                 &mut result.opcode as *mut c_int,
110                 &mut result.first_event_id as *mut c_int,
111                 &mut result.first_error_id as *mut c_int);
112             if res == ffi::False {
113                 panic!("X server missing XInput extension");
114             }
115             result
116         };
117 
118         unsafe {
119             let mut xinput_major_ver = ffi::XI_2_Major;
120             let mut xinput_minor_ver = ffi::XI_2_Minor;
121             if (xconn.xinput2.XIQueryVersion)(
122                 xconn.display,
123                 &mut xinput_major_ver,
124                 &mut xinput_minor_ver,
125             ) != ffi::Success as libc::c_int {
126                 panic!(
127                     "X server has XInput extension {}.{} but does not support XInput2",
128                     xinput_major_ver,
129                     xinput_minor_ver,
130                 );
131             }
132         }
133 
134         xconn.update_cached_wm_info(root);
135 
136         let wakeup_dummy_window = unsafe {
137             let (x, y, w, h) = (10, 10, 10, 10);
138             let (border_w, border_px, background_px) = (0, 0, 0);
139             (xconn.xlib.XCreateSimpleWindow)(
140                 xconn.display,
141                 root,
142                 x,
143                 y,
144                 w,
145                 h,
146                 border_w,
147                 border_px,
148                 background_px,
149             )
150         };
151 
152         let result = EventsLoop {
153             xconn,
154             wm_delete_window,
155             dnd,
156             ime_receiver,
157             ime_sender,
158             ime,
159             randr_event_offset,
160             windows: Default::default(),
161             devices: Default::default(),
162             xi2ext,
163             pending_wakeup: Default::default(),
164             root,
165             wakeup_dummy_window,
166         };
167 
168         // Register for device hotplug events
169         // (The request buffer is flushed during `init_device`)
170         result.xconn.select_xinput_events(
171             root,
172             ffi::XIAllDevices,
173             ffi::XI_HierarchyChangedMask,
174         ).queue();
175 
176         result.init_device(ffi::XIAllDevices);
177 
178         result
179     }
180 
181     /// Returns the `XConnection` of this events loop.
182     #[inline]
x_connection(&self) -> &Arc<XConnection>183     pub fn x_connection(&self) -> &Arc<XConnection> {
184         &self.xconn
185     }
186 
create_proxy(&self) -> EventsLoopProxy187     pub fn create_proxy(&self) -> EventsLoopProxy {
188         EventsLoopProxy {
189             pending_wakeup: Arc::downgrade(&self.pending_wakeup),
190             xconn: Arc::downgrade(&self.xconn),
191             wakeup_dummy_window: self.wakeup_dummy_window,
192         }
193     }
194 
poll_one_event(&mut self, event_ptr : *mut ffi::XEvent) -> bool195     unsafe fn poll_one_event(&mut self, event_ptr : *mut ffi::XEvent) -> bool {
196         // This function is used to poll and remove a single event
197         // from the Xlib event queue in a non-blocking, atomic way.
198         // XCheckIfEvent is non-blocking and removes events from queue.
199         // XNextEvent can't be used because it blocks while holding the
200         // global Xlib mutex.
201         // XPeekEvent does not remove events from the queue.
202         unsafe extern "C" fn predicate(
203             _display: *mut ffi::Display,
204             _event: *mut ffi::XEvent,
205             _arg : *mut c_char)  -> c_int {
206             // This predicate always returns "true" (1) to accept all events
207             1
208         }
209 
210         let result = (self.xconn.xlib.XCheckIfEvent)(
211             self.xconn.display,
212             event_ptr,
213             Some(predicate),
214             std::ptr::null_mut());
215 
216         result != 0
217     }
218 
wait_for_input(&mut self)219     unsafe fn wait_for_input(&mut self) {
220         // XNextEvent can not be used in multi-threaded applications
221         // because it is blocking for input while holding the global
222         // Xlib mutex.
223         // To work around this issue, first flush the X11 display, then
224         // use select(2) to wait for input to arrive
225         loop {
226             // First use XFlush to flush any buffered x11 requests
227             (self.xconn.xlib.XFlush)(self.xconn.display);
228 
229             // Then use select(2) to wait for input data
230             let mut fds : fd_set = mem::uninitialized();
231             FD_ZERO(&mut fds);
232             FD_SET(self.xconn.x11_fd, &mut fds);
233             let err = select(
234                 self.xconn.x11_fd + 1,
235                 &mut fds, // read fds
236                 std::ptr::null_mut(), // write fds
237                 std::ptr::null_mut(), // except fds (could be used to detect errors)
238                 std::ptr::null_mut()); // timeout
239 
240             if err < 0 {
241                 let errno_ptr = __errno_location();
242                 let errno = *errno_ptr;
243 
244                 if errno == EINTR {
245                     // try again if errno is EINTR
246                     continue;
247                 }
248 
249                 assert!(errno == EBADF || errno == EINVAL || errno == ENOMEM);
250                 panic!("select(2) returned fatal error condition");
251             }
252 
253             if FD_ISSET(self.xconn.x11_fd, &mut fds) {
254                 break;
255             }
256         }
257     }
258 
poll_events<F>(&mut self, mut callback: F) where F: FnMut(Event)259     pub fn poll_events<F>(&mut self, mut callback: F)
260         where F: FnMut(Event)
261     {
262         let mut xev = unsafe { mem::uninitialized() };
263         loop {
264             // Get next event
265             unsafe {
266                 if !self.poll_one_event(&mut xev) {
267                     break;
268                 }
269             }
270             self.process_event(&mut xev, &mut callback);
271         }
272     }
273 
run_forever<F>(&mut self, mut callback: F) where F: FnMut(Event) -> ControlFlow274     pub fn run_forever<F>(&mut self, mut callback: F)
275         where F: FnMut(Event) -> ControlFlow
276     {
277         let mut xev = unsafe { mem::uninitialized() };
278 
279         loop {
280             unsafe {
281                 while !self.poll_one_event(&mut xev) {
282                     // block until input is available
283                     self.wait_for_input();
284                 }
285             };
286 
287             let mut control_flow = ControlFlow::Continue;
288 
289             // Track whether or not `Break` was returned when processing the event.
290             {
291                 let mut cb = |event| {
292                     if let ControlFlow::Break = callback(event) {
293                         control_flow = ControlFlow::Break;
294                     }
295                 };
296 
297                 self.process_event(&mut xev, &mut cb);
298             }
299 
300             if let ControlFlow::Break = control_flow {
301                 break;
302             }
303         }
304     }
305 
process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F) where F: FnMut(Event)306     fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
307         where F: FnMut(Event)
308     {
309         // XFilterEvent tells us when an event has been discarded by the input method.
310         // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
311         // along with an extra copy of the KeyRelease events. This also prevents backspace and
312         // arrow keys from being detected twice.
313         if ffi::True == unsafe { (self.xconn.xlib.XFilterEvent)(
314             xev,
315             { let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window }
316         ) } {
317             return;
318         }
319 
320         let event_type = xev.get_type();
321         match event_type {
322             ffi::MappingNotify => {
323                 unsafe { (self.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); }
324                 self.xconn.check_errors().expect("Failed to call XRefreshKeyboardMapping");
325             }
326 
327             ffi::ClientMessage => {
328                 let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
329 
330                 let window = client_msg.window;
331                 let window_id = mkwid(window);
332 
333                 if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window {
334                     callback(Event::WindowEvent { window_id, event: WindowEvent::CloseRequested });
335                 } else if client_msg.message_type == self.dnd.atoms.enter {
336                     let source_window = client_msg.data.get_long(0) as c_ulong;
337                     let flags = client_msg.data.get_long(1);
338                     let version = flags >> 24;
339                     self.dnd.version = Some(version);
340                     let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1;
341                     if !has_more_types {
342                         let type_list = vec![
343                             client_msg.data.get_long(2) as c_ulong,
344                             client_msg.data.get_long(3) as c_ulong,
345                             client_msg.data.get_long(4) as c_ulong
346                         ];
347                         self.dnd.type_list = Some(type_list);
348                     } else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) } {
349                         self.dnd.type_list = Some(more_types);
350                     }
351                 } else if client_msg.message_type == self.dnd.atoms.position {
352                     // This event occurs every time the mouse moves while a file's being dragged
353                     // over our window. We emit HoveredFile in response; while the macOS backend
354                     // does that upon a drag entering, XDND doesn't have access to the actual drop
355                     // data until this event. For parity with other platforms, we only emit
356                     // `HoveredFile` the first time, though if winit's API is later extended to
357                     // supply position updates with `HoveredFile` or another event, implementing
358                     // that here would be trivial.
359 
360                     let source_window = client_msg.data.get_long(0) as c_ulong;
361 
362                     // Equivalent to `(x << shift) | y`
363                     // where `shift = mem::size_of::<c_short>() * 8`
364                     // Note that coordinates are in "desktop space", not "window space"
365                     // (in X11 parlance, they're root window coordinates)
366                     //let packed_coordinates = client_msg.data.get_long(2);
367                     //let shift = mem::size_of::<libc::c_short>() * 8;
368                     //let x = packed_coordinates >> shift;
369                     //let y = packed_coordinates & !(x << shift);
370 
371                     // By our own state flow, `version` should never be `None` at this point.
372                     let version = self.dnd.version.unwrap_or(5);
373 
374                     // Action is specified in versions 2 and up, though we don't need it anyway.
375                     //let action = client_msg.data.get_long(4);
376 
377                     let accepted = if let Some(ref type_list) = self.dnd.type_list {
378                         type_list.contains(&self.dnd.atoms.uri_list)
379                     } else {
380                         false
381                     };
382 
383                     if accepted {
384                         self.dnd.source_window = Some(source_window);
385                         unsafe {
386                             if self.dnd.result.is_none() {
387                                 let time = if version >= 1 {
388                                     client_msg.data.get_long(3) as c_ulong
389                                 } else {
390                                     // In version 0, time isn't specified
391                                     ffi::CurrentTime
392                                 };
393                                 // This results in the `SelectionNotify` event below
394                                 self.dnd.convert_selection(window, time);
395                             }
396                             self.dnd.send_status(window, source_window, DndState::Accepted)
397                                 .expect("Failed to send `XdndStatus` message.");
398                         }
399                     } else {
400                         unsafe {
401                             self.dnd.send_status(window, source_window, DndState::Rejected)
402                                 .expect("Failed to send `XdndStatus` message.");
403                         }
404                         self.dnd.reset();
405                     }
406                 } else if client_msg.message_type == self.dnd.atoms.drop {
407                     let (source_window, state) = if let Some(source_window) = self.dnd.source_window {
408                         if let Some(Ok(ref path_list)) = self.dnd.result {
409                             for path in path_list {
410                                 callback(Event::WindowEvent {
411                                     window_id,
412                                     event: WindowEvent::DroppedFile(path.clone()),
413                                 });
414                             }
415                         }
416                         (source_window, DndState::Accepted)
417                     } else {
418                         // `source_window` won't be part of our DND state if we already rejected the drop in our
419                         // `XdndPosition` handler.
420                         let source_window = client_msg.data.get_long(0) as c_ulong;
421                         (source_window, DndState::Rejected)
422                     };
423                     unsafe {
424                         self.dnd.send_finished(window, source_window, state)
425                             .expect("Failed to send `XdndFinished` message.");
426                     }
427                     self.dnd.reset();
428                 } else if client_msg.message_type == self.dnd.atoms.leave {
429                     self.dnd.reset();
430                     callback(Event::WindowEvent {
431                         window_id,
432                         event: WindowEvent::HoveredFileCancelled,
433                     });
434                 } else if self.pending_wakeup.load(atomic::Ordering::Relaxed) {
435                     self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
436                     callback(Event::Awakened);
437                 }
438             }
439 
440             ffi::SelectionNotify => {
441                 let xsel: &ffi::XSelectionEvent = xev.as_ref();
442 
443                 let window = xsel.requestor;
444                 let window_id = mkwid(window);
445 
446                 if xsel.property == self.dnd.atoms.selection {
447                     let mut result = None;
448 
449                     // This is where we receive data from drag and drop
450                     if let Ok(mut data) = unsafe { self.dnd.read_data(window) } {
451                         let parse_result = self.dnd.parse_data(&mut data);
452                         if let Ok(ref path_list) = parse_result {
453                             for path in path_list {
454                                 callback(Event::WindowEvent {
455                                     window_id,
456                                     event: WindowEvent::HoveredFile(path.clone()),
457                                 });
458                             }
459                         }
460                         result = Some(parse_result);
461                     }
462 
463                     self.dnd.result = result;
464                 }
465             }
466 
467             ffi::ConfigureNotify => {
468                 #[derive(Debug, Default)]
469                 struct Events {
470                     resized: Option<WindowEvent>,
471                     moved: Option<WindowEvent>,
472                     dpi_changed: Option<WindowEvent>,
473                 }
474 
475                 let xev: &ffi::XConfigureEvent = xev.as_ref();
476                 let xwindow = xev.window;
477                 let events = self.with_window(xwindow, |window| {
478                     // So apparently...
479                     // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root
480                     // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent
481                     // https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5
482                     // We don't want to send `Moved` when this is false, since then every `Resized`
483                     // (whether the window moved or not) is accompanied by an extraneous `Moved` event
484                     // that has a position relative to the parent window.
485                     let is_synthetic = xev.send_event == ffi::True;
486 
487                     // These are both in physical space.
488                     let new_inner_size = (xev.width as u32, xev.height as u32);
489                     let new_inner_position = (xev.x as i32, xev.y as i32);
490 
491                     let mut monitor = window.get_current_monitor(); // This must be done *before* locking!
492                     let mut shared_state_lock = window.shared_state.lock();
493 
494                     let (mut resized, moved) = {
495                         let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
496                         let moved = if is_synthetic {
497                             util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
498                         } else {
499                             // Detect when frame extents change.
500                             // Since this isn't synthetic, as per the notes above, this position is relative to the
501                             // parent window.
502                             let rel_parent = new_inner_position;
503                             if util::maybe_change(&mut shared_state_lock.inner_position_rel_parent, rel_parent) {
504                                 // This ensures we process the next `Moved`.
505                                 shared_state_lock.inner_position = None;
506                                 // Extra insurance against stale frame extents.
507                                 shared_state_lock.frame_extents = None;
508                             }
509                             false
510                         };
511                         (resized, moved)
512                     };
513 
514                     let mut events = Events::default();
515 
516                     let new_outer_position = if moved || shared_state_lock.position.is_none() {
517                         // We need to convert client area position to window position.
518                         let frame_extents = shared_state_lock.frame_extents
519                             .as_ref()
520                             .cloned()
521                             .unwrap_or_else(|| {
522                                 let frame_extents = self.xconn.get_frame_extents_heuristic(xwindow, self.root);
523                                 shared_state_lock.frame_extents = Some(frame_extents.clone());
524                                 frame_extents
525                             });
526                         let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
527                         shared_state_lock.position = Some(outer);
528                         if moved {
529                             let logical_position = LogicalPosition::from_physical(outer, monitor.hidpi_factor);
530                             events.moved = Some(WindowEvent::Moved(logical_position));
531                         }
532                         outer
533                     } else {
534                         shared_state_lock.position.unwrap()
535                     };
536 
537                     if is_synthetic {
538                         // If we don't use the existing adjusted value when available, then the user can screw up the
539                         // resizing by dragging across monitors *without* dropping the window.
540                         let (width, height) = shared_state_lock.dpi_adjusted
541                             .unwrap_or_else(|| (xev.width as f64, xev.height as f64));
542                         let last_hidpi_factor = shared_state_lock.guessed_dpi
543                             .take()
544                             .unwrap_or_else(|| {
545                                 shared_state_lock.last_monitor
546                                     .as_ref()
547                                     .map(|last_monitor| last_monitor.hidpi_factor)
548                                     .unwrap_or(1.0)
549                             });
550                         let new_hidpi_factor = {
551                             let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
552                             monitor = self.xconn.get_monitor_for_window(Some(window_rect));
553                             let new_hidpi_factor = monitor.hidpi_factor;
554                             shared_state_lock.last_monitor = Some(monitor.clone());
555                             new_hidpi_factor
556                         };
557                         if last_hidpi_factor != new_hidpi_factor {
558                             events.dpi_changed = Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
559                             let (new_width, new_height, flusher) = window.adjust_for_dpi(
560                                 last_hidpi_factor,
561                                 new_hidpi_factor,
562                                 width,
563                                 height,
564                             );
565                             flusher.queue();
566                             shared_state_lock.dpi_adjusted = Some((new_width, new_height));
567                             // if the DPI factor changed, force a resize event to ensure the logical
568                             // size is computed with the right DPI factor
569                             resized = true;
570                         }
571                     }
572 
573                     // This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
574                     // doesn't need this, but Xfwm does. The hack should not be run on other WMs, since tiling
575                     // WMs constrain the window size, making the resize fail. This would cause an endless stream of
576                     // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU.
577                     if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
578                         let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
579                         if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
580                             // When this finally happens, the event will not be synthetic.
581                             shared_state_lock.dpi_adjusted = None;
582                         } else {
583                             unsafe {
584                                 (self.xconn.xlib.XResizeWindow)(
585                                     self.xconn.display,
586                                     xwindow,
587                                     rounded_size.0 as c_uint,
588                                     rounded_size.1 as c_uint,
589                                 );
590                             }
591                         }
592                     }
593 
594                     if resized {
595                         let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
596                         events.resized = Some(WindowEvent::Resized(logical_size));
597                     }
598 
599                     events
600                 });
601 
602                 if let Some(events) = events {
603                     let window_id = mkwid(xwindow);
604                     if let Some(event) = events.dpi_changed {
605                         callback(Event::WindowEvent { window_id, event });
606                     }
607                     if let Some(event) = events.resized {
608                         callback(Event::WindowEvent { window_id, event });
609                     }
610                     if let Some(event) = events.moved {
611                         callback(Event::WindowEvent { window_id, event });
612                     }
613                 }
614             }
615 
616             ffi::ReparentNotify => {
617                 let xev: &ffi::XReparentEvent = xev.as_ref();
618 
619                 // This is generally a reliable way to detect when the window manager's been
620                 // replaced, though this event is only fired by reparenting window managers
621                 // (which is almost all of them). Failing to correctly update WM info doesn't
622                 // really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only
623                 // effect is that we waste some time trying to query unsupported properties.
624                 self.xconn.update_cached_wm_info(self.root);
625 
626                 self.with_window(xev.window, |window| {
627                     window.invalidate_cached_frame_extents();
628                 });
629             }
630 
631             ffi::DestroyNotify => {
632                 let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
633 
634                 let window = xev.window;
635                 let window_id = mkwid(window);
636 
637                 // In the event that the window's been destroyed without being dropped first, we
638                 // cleanup again here.
639                 self.windows.borrow_mut().remove(&WindowId(window));
640 
641                 // Since all XIM stuff needs to happen from the same thread, we destroy the input
642                 // context here instead of when dropping the window.
643                 self.ime
644                     .borrow_mut()
645                     .remove_context(window)
646                     .expect("Failed to destroy input context");
647 
648                 callback(Event::WindowEvent { window_id, event: WindowEvent::Destroyed });
649             }
650 
651             ffi::Expose => {
652                 let xev: &ffi::XExposeEvent = xev.as_ref();
653 
654                 let window = xev.window;
655                 let window_id = mkwid(window);
656 
657                 callback(Event::WindowEvent { window_id, event: WindowEvent::Refresh });
658             }
659 
660             ffi::KeyPress | ffi::KeyRelease => {
661                 use events::ElementState::{Pressed, Released};
662 
663                 // Note that in compose/pre-edit sequences, this will always be Released.
664                 let state = if xev.get_type() == ffi::KeyPress {
665                     Pressed
666                 } else {
667                     Released
668                 };
669 
670                 let xkev: &mut ffi::XKeyEvent = xev.as_mut();
671 
672                 let window = xkev.window;
673                 let window_id = mkwid(window);
674 
675                 // Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable
676                 // value, though this should only be an issue under multiseat configurations.
677                 let device = util::VIRTUAL_CORE_KEYBOARD;
678                 let device_id = mkdid(device);
679 
680                 // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
681                 // a keycode of 0.
682                 if xkev.keycode != 0 {
683                     let modifiers = ModifiersState {
684                         alt: xkev.state & ffi::Mod1Mask != 0,
685                         shift: xkev.state & ffi::ShiftMask != 0,
686                         ctrl: xkev.state & ffi::ControlMask != 0,
687                         logo: xkev.state & ffi::Mod4Mask != 0,
688                     };
689 
690                     let keysym = unsafe {
691                         let mut keysym = 0;
692                         (self.xconn.xlib.XLookupString)(
693                             xkev,
694                             ptr::null_mut(),
695                             0,
696                             &mut keysym,
697                             ptr::null_mut(),
698                         );
699                         self.xconn.check_errors().expect("Failed to lookup keysym");
700                         keysym
701                     };
702                     let virtual_keycode = events::keysym_to_element(keysym as c_uint);
703 
704                     callback(Event::WindowEvent {
705                         window_id,
706                         event: WindowEvent::KeyboardInput {
707                             device_id,
708                             input: KeyboardInput {
709                                 state,
710                                 scancode: xkev.keycode - 8,
711                                 virtual_keycode,
712                                 modifiers,
713                             },
714                         }
715                     });
716                 }
717 
718                 if state == Pressed {
719                     let written = if let Some(ic) = self.ime.borrow().get_context(window) {
720                         self.xconn.lookup_utf8(ic, xkev)
721                     } else {
722                         return;
723                     };
724 
725                     for chr in written.chars() {
726                         let event = Event::WindowEvent {
727                             window_id,
728                             event: WindowEvent::ReceivedCharacter(chr),
729                         };
730                         callback(event);
731                     }
732                 }
733             }
734 
735             ffi::GenericEvent => {
736                 let guard = if let Some(e) = GenericEventCookie::from_event(&self.xconn, *xev) { e } else { return };
737                 let xev = &guard.cookie;
738                 if self.xi2ext.opcode != xev.extension {
739                     return;
740                 }
741 
742                 use events::WindowEvent::{Focused, CursorEntered, MouseInput, CursorLeft, CursorMoved, MouseWheel, AxisMotion};
743                 use events::ElementState::{Pressed, Released};
744                 use events::MouseButton::{Left, Right, Middle, Other};
745                 use events::MouseScrollDelta::LineDelta;
746                 use events::{Touch, TouchPhase};
747 
748                 match xev.evtype {
749                     ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
750                         let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
751                         let window_id = mkwid(xev.event);
752                         let device_id = mkdid(xev.deviceid);
753                         if (xev.flags & ffi::XIPointerEmulated) != 0 {
754                             // Deliver multi-touch events instead of emulated mouse events.
755                             let return_now = self
756                                 .with_window(xev.event, |window| window.multitouch)
757                                 .unwrap_or(true);
758                             if return_now { return; }
759                         }
760 
761                         let modifiers = ModifiersState::from(xev.mods);
762 
763                         let state = if xev.evtype == ffi::XI_ButtonPress {
764                             Pressed
765                         } else {
766                             Released
767                         };
768                         match xev.detail as u32 {
769                             ffi::Button1 => callback(Event::WindowEvent {
770                                 window_id,
771                                 event: MouseInput {
772                                     device_id,
773                                     state,
774                                     button: Left,
775                                     modifiers,
776                                 },
777                             }),
778                             ffi::Button2 => callback(Event::WindowEvent {
779                                 window_id,
780                                 event: MouseInput {
781                                     device_id,
782                                     state,
783                                     button: Middle,
784                                     modifiers,
785                                 },
786                             }),
787                             ffi::Button3 => callback(Event::WindowEvent {
788                                 window_id,
789                                 event: MouseInput {
790                                     device_id,
791                                     state,
792                                     button: Right,
793                                     modifiers,
794                                 },
795                             }),
796 
797                             // Suppress emulated scroll wheel clicks, since we handle the real motion events for those.
798                             // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in
799                             // turn) as axis motion, so we don't otherwise special-case these button presses.
800                             4 | 5 | 6 | 7 => if xev.flags & ffi::XIPointerEmulated == 0 {
801                                 callback(Event::WindowEvent {
802                                     window_id,
803                                     event: MouseWheel {
804                                         device_id,
805                                         delta: match xev.detail {
806                                             4 => LineDelta(0.0, 1.0),
807                                             5 => LineDelta(0.0, -1.0),
808                                             6 => LineDelta(-1.0, 0.0),
809                                             7 => LineDelta(1.0, 0.0),
810                                             _ => unreachable!(),
811                                         },
812                                         phase: TouchPhase::Moved,
813                                         modifiers,
814                                     },
815                                 });
816                             },
817 
818                             x => callback(Event::WindowEvent {
819                                 window_id,
820                                 event: MouseInput {
821                                     device_id,
822                                     state,
823                                     button: Other(x as u8),
824                                     modifiers,
825                                 },
826                             }),
827                         }
828                     }
829                     ffi::XI_Motion => {
830                         let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
831                         let device_id = mkdid(xev.deviceid);
832                         let window_id = mkwid(xev.event);
833                         let new_cursor_pos = (xev.event_x, xev.event_y);
834 
835                         let modifiers = ModifiersState::from(xev.mods);
836 
837                         let cursor_moved = self.with_window(xev.event, |window| {
838                             let mut shared_state_lock = window.shared_state.lock();
839                             util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
840                         });
841                         if cursor_moved == Some(true) {
842                             let dpi_factor = self.with_window(xev.event, |window| {
843                                 window.get_hidpi_factor()
844                             });
845                             if let Some(dpi_factor) = dpi_factor {
846                                 let position = LogicalPosition::from_physical(
847                                     (xev.event_x as f64, xev.event_y as f64),
848                                     dpi_factor,
849                                 );
850                                 callback(Event::WindowEvent {
851                                     window_id,
852                                     event: CursorMoved {
853                                         device_id,
854                                         position,
855                                         modifiers,
856                                     },
857                                 });
858                             } else {
859                                 return;
860                             }
861                         } else if cursor_moved.is_none() {
862                             return;
863                         }
864 
865                         // More gymnastics, for self.devices
866                         let mut events = Vec::new();
867                         {
868                             let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
869                             let mut devices = self.devices.borrow_mut();
870                             let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
871                                 Some(device) => device,
872                                 None => return,
873                             };
874 
875                             let mut value = xev.valuators.values;
876                             for i in 0..xev.valuators.mask_len*8 {
877                                 if ffi::XIMaskIsSet(mask, i) {
878                                     let x = unsafe { *value };
879                                     if let Some(&mut (_, ref mut info)) = physical_device.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == i) {
880                                         let delta = (x - info.position) / info.increment;
881                                         info.position = x;
882                                         events.push(Event::WindowEvent {
883                                             window_id,
884                                             event: MouseWheel {
885                                                 device_id,
886                                                 delta: match info.orientation {
887                                                     ScrollOrientation::Horizontal => LineDelta(delta as f32, 0.0),
888                                                     // X11 vertical scroll coordinates are opposite to winit's
889                                                     ScrollOrientation::Vertical => LineDelta(0.0, -delta as f32),
890                                                 },
891                                                 phase: TouchPhase::Moved,
892                                                 modifiers,
893                                             },
894                                         });
895                                     } else {
896                                         events.push(Event::WindowEvent {
897                                             window_id,
898                                             event: AxisMotion {
899                                                 device_id,
900                                                 axis: i as u32,
901                                                 value: unsafe { *value },
902                                             },
903                                         });
904                                     }
905                                     value = unsafe { value.offset(1) };
906                                 }
907                             }
908                         }
909                         for event in events {
910                             callback(event);
911                         }
912                     }
913 
914                     ffi::XI_Enter => {
915                         let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
916 
917                         let window_id = mkwid(xev.event);
918                         let device_id = mkdid(xev.deviceid);
919 
920                         if let Some(all_info) = DeviceInfo::get(&self.xconn, ffi::XIAllDevices) {
921                             let mut devices = self.devices.borrow_mut();
922                             for device_info in all_info.iter() {
923                                 if device_info.deviceid == xev.sourceid
924                                 // This is needed for resetting to work correctly on i3, and
925                                 // presumably some other WMs. On those, `XI_Enter` doesn't include
926                                 // the physical device ID, so both `sourceid` and `deviceid` are
927                                 // the virtual device.
928                                 || device_info.attachment == xev.sourceid {
929                                     let device_id = DeviceId(device_info.deviceid);
930                                     if let Some(device) = devices.get_mut(&device_id) {
931                                         device.reset_scroll_position(device_info);
932                                     }
933                                 }
934                             }
935                         }
936                         callback(Event::WindowEvent {
937                             window_id,
938                             event: CursorEntered { device_id },
939                         });
940 
941                         if let Some(dpi_factor) = self.with_window(xev.event, |window| {
942                             window.get_hidpi_factor()
943                         }) {
944                             let position = LogicalPosition::from_physical(
945                                 (xev.event_x as f64, xev.event_y as f64),
946                                 dpi_factor,
947                             );
948 
949                             // The mods field on this event isn't actually populated, so query the
950                             // pointer device. In the future, we can likely remove this round-trip by
951                             // relying on `Xkb` for modifier values.
952                             //
953                             // This needs to only be done after confirming the window still exists,
954                             // since otherwise we risk getting a `BadWindow` error if the window was
955                             // dropped with queued events.
956                             let modifiers = self.xconn
957                                 .query_pointer(xev.event, xev.deviceid)
958                                 .expect("Failed to query pointer device")
959                                 .get_modifier_state();
960 
961                             callback(Event::WindowEvent {
962                                 window_id,
963                                 event: CursorMoved {
964                                     device_id,
965                                     position,
966                                     modifiers,
967                                 },
968                             });
969                         }
970                     }
971                     ffi::XI_Leave => {
972                         let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
973 
974                         // Leave, FocusIn, and FocusOut can be received by a window that's already
975                         // been destroyed, which the user presumably doesn't want to deal with.
976                         let window_closed = !self.window_exists(xev.event);
977                         if !window_closed {
978                             callback(Event::WindowEvent {
979                                 window_id: mkwid(xev.event),
980                                 event: CursorLeft { device_id: mkdid(xev.deviceid) },
981                             });
982                         }
983                     }
984                     ffi::XI_FocusIn => {
985                         let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
986 
987                         let dpi_factor = match self.with_window(xev.event, |window| {
988                             window.get_hidpi_factor()
989                         }) {
990                             Some(dpi_factor) => dpi_factor,
991                             None => return,
992                         };
993                         let window_id = mkwid(xev.event);
994 
995                         self.ime
996                             .borrow_mut()
997                             .focus(xev.event)
998                             .expect("Failed to focus input context");
999 
1000                         callback(Event::WindowEvent { window_id, event: Focused(true) });
1001 
1002                         // The deviceid for this event is for a keyboard instead of a pointer,
1003                         // so we have to do a little extra work.
1004                         let pointer_id = self.devices
1005                             .borrow()
1006                             .get(&DeviceId(xev.deviceid))
1007                             .map(|device| device.attachment)
1008                             .unwrap_or(2);
1009 
1010                         let position = LogicalPosition::from_physical(
1011                             (xev.event_x as f64, xev.event_y as f64),
1012                             dpi_factor,
1013                         );
1014                         callback(Event::WindowEvent {
1015                             window_id,
1016                             event: CursorMoved {
1017                                 device_id: mkdid(pointer_id),
1018                                 position,
1019                                 modifiers: ModifiersState::from(xev.mods),
1020                             }
1021                         });
1022                     }
1023                     ffi::XI_FocusOut => {
1024                         let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
1025                         if !self.window_exists(xev.event) { return; }
1026                         self.ime
1027                             .borrow_mut()
1028                             .unfocus(xev.event)
1029                             .expect("Failed to unfocus input context");
1030                         callback(Event::WindowEvent {
1031                             window_id: mkwid(xev.event),
1032                             event: Focused(false),
1033                         })
1034                     }
1035 
1036                     ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
1037                         let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
1038                         let window_id = mkwid(xev.event);
1039                         let phase = match xev.evtype {
1040                             ffi::XI_TouchBegin => TouchPhase::Started,
1041                             ffi::XI_TouchUpdate => TouchPhase::Moved,
1042                             ffi::XI_TouchEnd => TouchPhase::Ended,
1043                             _ => unreachable!()
1044                         };
1045                          let dpi_factor = self.with_window(xev.event, |window| {
1046                             window.get_hidpi_factor()
1047                         });
1048                         if let Some(dpi_factor) = dpi_factor {
1049                             let location = LogicalPosition::from_physical(
1050                                 (xev.event_x as f64, xev.event_y as f64),
1051                                 dpi_factor,
1052                             );
1053                             callback(Event::WindowEvent {
1054                                 window_id,
1055                                 event: WindowEvent::Touch(Touch {
1056                                     device_id: mkdid(xev.deviceid),
1057                                     phase,
1058                                     location,
1059                                     id: xev.detail as u64,
1060                                 }),
1061                             })
1062                         }
1063                     }
1064 
1065                     ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
1066                         let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1067                         if xev.flags & ffi::XIPointerEmulated == 0 {
1068                             callback(Event::DeviceEvent { device_id: mkdid(xev.deviceid), event: DeviceEvent::Button {
1069                                 button: xev.detail as u32,
1070                                 state: match xev.evtype {
1071                                     ffi::XI_RawButtonPress => Pressed,
1072                                     ffi::XI_RawButtonRelease => Released,
1073                                     _ => unreachable!(),
1074                                 },
1075                             }});
1076                         }
1077                     }
1078 
1079                     ffi::XI_RawMotion => {
1080                         let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1081                         let did = mkdid(xev.deviceid);
1082 
1083                         let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
1084                         let mut value = xev.raw_values;
1085                         let mut mouse_delta = (0.0, 0.0);
1086                         let mut scroll_delta = (0.0, 0.0);
1087                         for i in 0..xev.valuators.mask_len*8 {
1088                             if ffi::XIMaskIsSet(mask, i) {
1089                                 let x = unsafe { *value };
1090                                 // We assume that every XInput2 device with analog axes is a pointing device emitting
1091                                 // relative coordinates.
1092                                 match i {
1093                                     0 => mouse_delta.0 = x,
1094                                     1 => mouse_delta.1 = x,
1095                                     2 => scroll_delta.0 = x as f32,
1096                                     3 => scroll_delta.1 = x as f32,
1097                                     _ => {},
1098                                 }
1099                                 callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::Motion {
1100                                     axis: i as u32,
1101                                     value: x,
1102                                 }});
1103                                 value = unsafe { value.offset(1) };
1104                             }
1105                         }
1106                         if mouse_delta != (0.0, 0.0) {
1107                             callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseMotion {
1108                                 delta: mouse_delta,
1109                             }});
1110                         }
1111                         if scroll_delta != (0.0, 0.0) {
1112                             callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseWheel {
1113                                 delta: LineDelta(scroll_delta.0, scroll_delta.1),
1114                             }});
1115                         }
1116                     }
1117 
1118                     ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
1119                         let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1120 
1121                         let state = match xev.evtype {
1122                             ffi::XI_RawKeyPress => Pressed,
1123                             ffi::XI_RawKeyRelease => Released,
1124                             _ => unreachable!(),
1125                         };
1126 
1127                         let device_id = xev.sourceid;
1128                         let keycode = xev.detail;
1129                         if keycode < 8 { return; }
1130                         let scancode = (keycode - 8) as u32;
1131 
1132                         let keysym = unsafe {
1133                             (self.xconn.xlib.XKeycodeToKeysym)(
1134                                 self.xconn.display,
1135                                 xev.detail as ffi::KeyCode,
1136                                 0,
1137                             )
1138                         };
1139                         self.xconn.check_errors().expect("Failed to lookup raw keysym");
1140 
1141                         let virtual_keycode = events::keysym_to_element(keysym as c_uint);
1142 
1143                         callback(Event::DeviceEvent {
1144                             device_id: mkdid(device_id),
1145                             event: DeviceEvent::Key(KeyboardInput {
1146                                 scancode,
1147                                 virtual_keycode,
1148                                 state,
1149                                 // So, in an ideal world we can use libxkbcommon to get modifiers.
1150                                 // However, libxkbcommon-x11 isn't as commonly installed as one
1151                                 // would hope. We can still use the Xkb extension to get
1152                                 // comprehensive keyboard state updates, but interpreting that
1153                                 // info manually is going to be involved.
1154                                 modifiers: ModifiersState::default(),
1155                             }),
1156                         });
1157                     }
1158 
1159                     ffi::XI_HierarchyChanged => {
1160                         let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
1161                         for info in unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } {
1162                             if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) {
1163                                 self.init_device(info.deviceid);
1164                                 callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Added });
1165                             } else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved) {
1166                                 callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Removed });
1167                                 let mut devices = self.devices.borrow_mut();
1168                                 devices.remove(&DeviceId(info.deviceid));
1169                             }
1170                         }
1171                     }
1172 
1173                     _ => {}
1174                 }
1175             },
1176             _ => {
1177                 if event_type == self.randr_event_offset {
1178                     // In the future, it would be quite easy to emit monitor hotplug events.
1179                     let prev_list = monitor::invalidate_cached_monitor_list();
1180                     if let Some(prev_list) = prev_list {
1181                         let new_list = self.xconn.get_available_monitors();
1182                         for new_monitor in new_list {
1183                             prev_list
1184                                 .iter()
1185                                 .find(|prev_monitor| prev_monitor.name == new_monitor.name)
1186                                 .map(|prev_monitor| {
1187                                     if new_monitor.hidpi_factor != prev_monitor.hidpi_factor {
1188                                         for (window_id, window) in self.windows.borrow().iter() {
1189                                             if let Some(window) = window.upgrade() {
1190                                                 // Check if the window is on this monitor
1191                                                 let monitor = window.get_current_monitor();
1192                                                 if monitor.name == new_monitor.name {
1193                                                     callback(Event::WindowEvent {
1194                                                         window_id: mkwid(window_id.0),
1195                                                         event: WindowEvent::HiDpiFactorChanged(
1196                                                             new_monitor.hidpi_factor
1197                                                         ),
1198                                                     });
1199                                                     let (width, height) = match window.get_inner_size_physical() {
1200                                                         Some(result) => result,
1201                                                         None => continue,
1202                                                     };
1203                                                     let (_, _, flusher) = window.adjust_for_dpi(
1204                                                         prev_monitor.hidpi_factor,
1205                                                         new_monitor.hidpi_factor,
1206                                                         width as f64,
1207                                                         height as f64,
1208                                                     );
1209                                                     flusher.queue();
1210                                                 }
1211                                             }
1212                                         }
1213                                     }
1214                                 });
1215                         }
1216                     }
1217                 }
1218             },
1219         }
1220 
1221         match self.ime_receiver.try_recv() {
1222             Ok((window_id, x, y)) => {
1223                 self.ime.borrow_mut().send_xim_spot(window_id, x, y);
1224             },
1225             Err(_) => (),
1226         }
1227     }
1228 
init_device(&self, device: c_int)1229     fn init_device(&self, device: c_int) {
1230         let mut devices = self.devices.borrow_mut();
1231         if let Some(info) = DeviceInfo::get(&self.xconn, device) {
1232             for info in info.iter() {
1233                 devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
1234             }
1235         }
1236     }
1237 
with_window<F, T>(&self, window_id: ffi::Window, callback: F) -> Option<T> where F: Fn(&UnownedWindow) -> T1238     fn with_window<F, T>(&self, window_id: ffi::Window, callback: F) -> Option<T>
1239         where F: Fn(&UnownedWindow) -> T
1240     {
1241         let mut deleted = false;
1242         let window_id = WindowId(window_id);
1243         let result = self.windows
1244             .borrow()
1245             .get(&window_id)
1246             .and_then(|window| {
1247                 let arc = window.upgrade();
1248                 deleted = arc.is_none();
1249                 arc
1250             })
1251             .map(|window| callback(&*window));
1252         if deleted {
1253             // Garbage collection
1254             self.windows.borrow_mut().remove(&window_id);
1255         }
1256         result
1257     }
1258 
window_exists(&self, window_id: ffi::Window) -> bool1259     fn window_exists(&self, window_id: ffi::Window) -> bool {
1260         self.with_window(window_id, |_| ()).is_some()
1261     }
1262 }
1263 
1264 impl EventsLoopProxy {
wakeup(&self) -> Result<(), EventsLoopClosed>1265     pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
1266         // Update the `EventsLoop`'s `pending_wakeup` flag.
1267         let display = match (self.pending_wakeup.upgrade(), self.xconn.upgrade()) {
1268             (Some(wakeup), Some(display)) => {
1269                 wakeup.store(true, atomic::Ordering::Relaxed);
1270                 display
1271             },
1272             _ => return Err(EventsLoopClosed),
1273         };
1274 
1275         // Push an event on the X event queue so that methods run_forever will advance.
1276         //
1277         // NOTE: This design is taken from the old `WindowProxy::wakeup` implementation. It
1278         // assumes that X11 is thread safe. Is this true?
1279         // (WARNING: it's probably not true)
1280         display.send_client_msg(
1281             self.wakeup_dummy_window,
1282             self.wakeup_dummy_window,
1283             0,
1284             None,
1285             [0, 0, 0, 0, 0],
1286         ).flush().expect("Failed to call XSendEvent after wakeup");
1287 
1288         Ok(())
1289     }
1290 }
1291 
1292 struct DeviceInfo<'a> {
1293     xconn: &'a XConnection,
1294     info: *const ffi::XIDeviceInfo,
1295     count: usize,
1296 }
1297 
1298 impl<'a> DeviceInfo<'a> {
get(xconn: &'a XConnection, device: c_int) -> Option<Self>1299     fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> {
1300         unsafe {
1301             let mut count = mem::uninitialized();
1302             let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
1303             xconn.check_errors()
1304                 .ok()
1305                 .and_then(|_| {
1306                     if info.is_null() || count == 0 {
1307                         None
1308                     } else {
1309                         Some(DeviceInfo {
1310                             xconn,
1311                             info,
1312                             count: count as usize,
1313                         })
1314                     }
1315                 })
1316         }
1317     }
1318 }
1319 
1320 impl<'a> Drop for DeviceInfo<'a> {
drop(&mut self)1321     fn drop(&mut self) {
1322         assert!(!self.info.is_null());
1323         unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
1324     }
1325 }
1326 
1327 impl<'a> Deref for DeviceInfo<'a> {
1328     type Target = [ffi::XIDeviceInfo];
deref(&self) -> &Self::Target1329     fn deref(&self) -> &Self::Target {
1330         unsafe { slice::from_raw_parts(self.info, self.count) }
1331     }
1332 }
1333 
1334 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1335 pub struct WindowId(ffi::Window);
1336 
1337 impl WindowId {
dummy() -> Self1338     pub unsafe fn dummy() -> Self {
1339         WindowId(0)
1340     }
1341 }
1342 
1343 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1344 pub struct DeviceId(c_int);
1345 
1346 impl DeviceId {
dummy() -> Self1347     pub unsafe fn dummy() -> Self {
1348         DeviceId(0)
1349     }
1350 }
1351 
1352 pub struct Window(Arc<UnownedWindow>);
1353 
1354 impl Deref for Window {
1355     type Target = UnownedWindow;
1356     #[inline]
deref(&self) -> &UnownedWindow1357     fn deref(&self) -> &UnownedWindow {
1358         &*self.0
1359     }
1360 }
1361 
1362 impl Window {
new( event_loop: &EventsLoop, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes ) -> Result<Self, CreationError>1363     pub fn new(
1364         event_loop: &EventsLoop,
1365         attribs: WindowAttributes,
1366         pl_attribs: PlatformSpecificWindowBuilderAttributes
1367     ) -> Result<Self, CreationError> {
1368         let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
1369         event_loop.windows
1370             .borrow_mut()
1371             .insert(window.id(), Arc::downgrade(&window));
1372         Ok(Window(window))
1373     }
1374 }
1375 
1376 impl Drop for Window {
drop(&mut self)1377     fn drop(&mut self) {
1378         let window = self.deref();
1379         let xconn = &window.xconn;
1380         unsafe {
1381             (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0);
1382             // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
1383             let _ = xconn.check_errors();
1384         }
1385     }
1386 }
1387 
1388 /// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
1389 /// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
1390 struct GenericEventCookie<'a> {
1391     xconn: &'a XConnection,
1392     cookie: ffi::XGenericEventCookie
1393 }
1394 
1395 impl<'a> GenericEventCookie<'a> {
from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>>1396     fn from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
1397         unsafe {
1398             let mut cookie: ffi::XGenericEventCookie = From::from(event);
1399             if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
1400                 Some(GenericEventCookie { xconn, cookie })
1401             } else {
1402                 None
1403             }
1404         }
1405     }
1406 }
1407 
1408 impl<'a> Drop for GenericEventCookie<'a> {
drop(&mut self)1409     fn drop(&mut self) {
1410         unsafe {
1411             (self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
1412         }
1413     }
1414 }
1415 
1416 #[derive(Debug, Copy, Clone)]
1417 struct XExtension {
1418     opcode: c_int,
1419     first_event_id: c_int,
1420     first_error_id: c_int,
1421 }
1422 
mkwid(w: ffi::Window) -> ::WindowId1423 fn mkwid(w: ffi::Window) -> ::WindowId { ::WindowId(::platform::WindowId::X(WindowId(w))) }
mkdid(w: c_int) -> ::DeviceId1424 fn mkdid(w: c_int) -> ::DeviceId { ::DeviceId(::platform::DeviceId::X(DeviceId(w))) }
1425 
1426 #[derive(Debug)]
1427 struct Device {
1428     name: String,
1429     scroll_axes: Vec<(i32, ScrollAxis)>,
1430     // For master devices, this is the paired device (pointer <-> keyboard).
1431     // For slave devices, this is the master.
1432     attachment: c_int,
1433 }
1434 
1435 #[derive(Debug, Copy, Clone)]
1436 struct ScrollAxis {
1437     increment: f64,
1438     orientation: ScrollOrientation,
1439     position: f64,
1440 }
1441 
1442 #[derive(Debug, Copy, Clone)]
1443 enum ScrollOrientation {
1444     Vertical,
1445     Horizontal,
1446 }
1447 
1448 impl Device {
new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self1449     fn new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self {
1450         let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
1451         let mut scroll_axes = Vec::new();
1452 
1453         if Device::physical_device(info) {
1454             // Register for global raw events
1455             let mask = ffi::XI_RawMotionMask
1456                 | ffi::XI_RawButtonPressMask
1457                 | ffi::XI_RawButtonReleaseMask
1458                 | ffi::XI_RawKeyPressMask
1459                 | ffi::XI_RawKeyReleaseMask;
1460             // The request buffer is flushed when we poll for events
1461             el.xconn.select_xinput_events(el.root, info.deviceid, mask).queue();
1462 
1463             // Identify scroll axes
1464             for class_ptr in Device::classes(info) {
1465                 let class = unsafe { &**class_ptr };
1466                 match class._type {
1467                     ffi::XIScrollClass => {
1468                         let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) };
1469                         scroll_axes.push((info.number, ScrollAxis {
1470                             increment: info.increment,
1471                             orientation: match info.scroll_type {
1472                                 ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
1473                                 ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
1474                                 _ => { unreachable!() }
1475                             },
1476                             position: 0.0,
1477                         }));
1478                     }
1479                     _ => {}
1480                 }
1481             }
1482         }
1483 
1484         let mut device = Device {
1485             name: name.into_owned(),
1486             scroll_axes: scroll_axes,
1487             attachment: info.attachment,
1488         };
1489         device.reset_scroll_position(info);
1490         device
1491     }
1492 
reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo)1493     fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) {
1494         if Device::physical_device(info) {
1495             for class_ptr in Device::classes(info) {
1496                 let class = unsafe { &**class_ptr };
1497                 match class._type {
1498                     ffi::XIValuatorClass => {
1499                         let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class) };
1500                         if let Some(&mut (_, ref mut axis)) = self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number) {
1501                             axis.position = info.value;
1502                         }
1503                     }
1504                     _ => {}
1505                 }
1506             }
1507         }
1508     }
1509 
1510     #[inline]
physical_device(info: &ffi::XIDeviceInfo) -> bool1511     fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
1512         info._use == ffi::XISlaveKeyboard || info._use == ffi::XISlavePointer || info._use == ffi::XIFloatingSlave
1513     }
1514 
1515     #[inline]
classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo]1516     fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
1517         unsafe { slice::from_raw_parts(info.classes as *const *const ffi::XIAnyClassInfo, info.num_classes as usize) }
1518     }
1519 }
1520