1 use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc};
2 
3 use libc::{c_char, c_int, c_long, c_uint, c_ulong};
4 
5 use parking_lot::MutexGuard;
6 
7 use super::{
8     events, ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd,
9     DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
10     XExtension,
11 };
12 
13 use util::modifiers::{ModifierKeyState, ModifierKeymap};
14 
15 use crate::{
16     dpi::{PhysicalPosition, PhysicalSize},
17     event::{
18         DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent,
19     },
20     event_loop::EventLoopWindowTarget as RootELW,
21 };
22 
23 pub(super) struct EventProcessor<T: 'static> {
24     pub(super) dnd: Dnd,
25     pub(super) ime_receiver: ImeReceiver,
26     pub(super) randr_event_offset: c_int,
27     pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
28     pub(super) xi2ext: XExtension,
29     pub(super) target: Rc<RootELW<T>>,
30     pub(super) mod_keymap: ModifierKeymap,
31     pub(super) device_mod_state: ModifierKeyState,
32     // Number of touch events currently in progress
33     pub(super) num_touch: u32,
34     pub(super) first_touch: Option<u64>,
35     // Currently focused window belonging to this process
36     pub(super) active_window: Option<ffi::Window>,
37 }
38 
39 impl<T: 'static> EventProcessor<T> {
init_device(&self, device: c_int)40     pub(super) fn init_device(&self, device: c_int) {
41         let wt = get_xtarget(&self.target);
42         let mut devices = self.devices.borrow_mut();
43         if let Some(info) = DeviceInfo::get(&wt.xconn, device) {
44             for info in info.iter() {
45                 devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
46             }
47         }
48     }
49 
with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret> where F: Fn(&Arc<UnownedWindow>) -> Ret,50     fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret>
51     where
52         F: Fn(&Arc<UnownedWindow>) -> Ret,
53     {
54         let mut deleted = false;
55         let window_id = WindowId(window_id);
56         let wt = get_xtarget(&self.target);
57         let result = wt
58             .windows
59             .borrow()
60             .get(&window_id)
61             .and_then(|window| {
62                 let arc = window.upgrade();
63                 deleted = arc.is_none();
64                 arc
65             })
66             .map(|window| callback(&window));
67         if deleted {
68             // Garbage collection
69             wt.windows.borrow_mut().remove(&window_id);
70         }
71         result
72     }
73 
window_exists(&self, window_id: ffi::Window) -> bool74     fn window_exists(&self, window_id: ffi::Window) -> bool {
75         self.with_window(window_id, |_| ()).is_some()
76     }
77 
poll(&self) -> bool78     pub(super) fn poll(&self) -> bool {
79         let wt = get_xtarget(&self.target);
80         let result = unsafe { (wt.xconn.xlib.XPending)(wt.xconn.display) };
81 
82         result != 0
83     }
84 
poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool85     pub(super) unsafe fn poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool {
86         let wt = get_xtarget(&self.target);
87         // This function is used to poll and remove a single event
88         // from the Xlib event queue in a non-blocking, atomic way.
89         // XCheckIfEvent is non-blocking and removes events from queue.
90         // XNextEvent can't be used because it blocks while holding the
91         // global Xlib mutex.
92         // XPeekEvent does not remove events from the queue.
93         unsafe extern "C" fn predicate(
94             _display: *mut ffi::Display,
95             _event: *mut ffi::XEvent,
96             _arg: *mut c_char,
97         ) -> c_int {
98             // This predicate always returns "true" (1) to accept all events
99             1
100         }
101 
102         let result = (wt.xconn.xlib.XCheckIfEvent)(
103             wt.xconn.display,
104             event_ptr,
105             Some(predicate),
106             std::ptr::null_mut(),
107         );
108 
109         result != 0
110     }
111 
process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F) where F: FnMut(Event<'_, T>),112     pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
113     where
114         F: FnMut(Event<'_, T>),
115     {
116         let wt = get_xtarget(&self.target);
117         // XFilterEvent tells us when an event has been discarded by the input method.
118         // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
119         // along with an extra copy of the KeyRelease events. This also prevents backspace and
120         // arrow keys from being detected twice.
121         if ffi::True
122             == unsafe {
123                 (wt.xconn.xlib.XFilterEvent)(xev, {
124                     let xev: &ffi::XAnyEvent = xev.as_ref();
125                     xev.window
126                 })
127             }
128         {
129             return;
130         }
131 
132         // We can't call a `&mut self` method because of the above borrow,
133         // so we use this macro for repeated modifier state updates.
134         macro_rules! update_modifiers {
135             ( $state:expr , $modifier:expr ) => {{
136                 match ($state, $modifier) {
137                     (state, modifier) => {
138                         if let Some(modifiers) =
139                             self.device_mod_state.update_state(&state, modifier)
140                         {
141                             if let Some(window_id) = self.active_window {
142                                 callback(Event::WindowEvent {
143                                     window_id: mkwid(window_id),
144                                     event: WindowEvent::ModifiersChanged(modifiers),
145                                 });
146                             }
147                         }
148                     }
149                 }
150             }};
151         }
152 
153         let event_type = xev.get_type();
154         match event_type {
155             ffi::MappingNotify => {
156                 let mapping: &ffi::XMappingEvent = xev.as_ref();
157 
158                 if mapping.request == ffi::MappingModifier
159                     || mapping.request == ffi::MappingKeyboard
160                 {
161                     unsafe {
162                         (wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut());
163                     }
164                     wt.xconn
165                         .check_errors()
166                         .expect("Failed to call XRefreshKeyboardMapping");
167 
168                     self.mod_keymap.reset_from_x_connection(&wt.xconn);
169                     self.device_mod_state.update_keymap(&self.mod_keymap);
170                 }
171             }
172 
173             ffi::ClientMessage => {
174                 let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
175 
176                 let window = client_msg.window;
177                 let window_id = mkwid(window);
178 
179                 if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window {
180                     callback(Event::WindowEvent {
181                         window_id,
182                         event: WindowEvent::CloseRequested,
183                     });
184                 } else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping {
185                     let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut();
186                     response_msg.window = wt.root;
187                     wt.xconn
188                         .send_event(
189                             wt.root,
190                             Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask),
191                             *response_msg,
192                         )
193                         .queue();
194                 } else if client_msg.message_type == self.dnd.atoms.enter {
195                     let source_window = client_msg.data.get_long(0) as c_ulong;
196                     let flags = client_msg.data.get_long(1);
197                     let version = flags >> 24;
198                     self.dnd.version = Some(version);
199                     let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1;
200                     if !has_more_types {
201                         let type_list = vec![
202                             client_msg.data.get_long(2) as c_ulong,
203                             client_msg.data.get_long(3) as c_ulong,
204                             client_msg.data.get_long(4) as c_ulong,
205                         ];
206                         self.dnd.type_list = Some(type_list);
207                     } else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) }
208                     {
209                         self.dnd.type_list = Some(more_types);
210                     }
211                 } else if client_msg.message_type == self.dnd.atoms.position {
212                     // This event occurs every time the mouse moves while a file's being dragged
213                     // over our window. We emit HoveredFile in response; while the macOS backend
214                     // does that upon a drag entering, XDND doesn't have access to the actual drop
215                     // data until this event. For parity with other platforms, we only emit
216                     // `HoveredFile` the first time, though if winit's API is later extended to
217                     // supply position updates with `HoveredFile` or another event, implementing
218                     // that here would be trivial.
219 
220                     let source_window = client_msg.data.get_long(0) as c_ulong;
221 
222                     // Equivalent to `(x << shift) | y`
223                     // where `shift = mem::size_of::<c_short>() * 8`
224                     // Note that coordinates are in "desktop space", not "window space"
225                     // (in X11 parlance, they're root window coordinates)
226                     //let packed_coordinates = client_msg.data.get_long(2);
227                     //let shift = mem::size_of::<libc::c_short>() * 8;
228                     //let x = packed_coordinates >> shift;
229                     //let y = packed_coordinates & !(x << shift);
230 
231                     // By our own state flow, `version` should never be `None` at this point.
232                     let version = self.dnd.version.unwrap_or(5);
233 
234                     // Action is specified in versions 2 and up, though we don't need it anyway.
235                     //let action = client_msg.data.get_long(4);
236 
237                     let accepted = if let Some(ref type_list) = self.dnd.type_list {
238                         type_list.contains(&self.dnd.atoms.uri_list)
239                     } else {
240                         false
241                     };
242 
243                     if accepted {
244                         self.dnd.source_window = Some(source_window);
245                         unsafe {
246                             if self.dnd.result.is_none() {
247                                 let time = if version >= 1 {
248                                     client_msg.data.get_long(3) as c_ulong
249                                 } else {
250                                     // In version 0, time isn't specified
251                                     ffi::CurrentTime
252                                 };
253                                 // This results in the `SelectionNotify` event below
254                                 self.dnd.convert_selection(window, time);
255                             }
256                             self.dnd
257                                 .send_status(window, source_window, DndState::Accepted)
258                                 .expect("Failed to send `XdndStatus` message.");
259                         }
260                     } else {
261                         unsafe {
262                             self.dnd
263                                 .send_status(window, source_window, DndState::Rejected)
264                                 .expect("Failed to send `XdndStatus` message.");
265                         }
266                         self.dnd.reset();
267                     }
268                 } else if client_msg.message_type == self.dnd.atoms.drop {
269                     let (source_window, state) = if let Some(source_window) = self.dnd.source_window
270                     {
271                         if let Some(Ok(ref path_list)) = self.dnd.result {
272                             for path in path_list {
273                                 callback(Event::WindowEvent {
274                                     window_id,
275                                     event: WindowEvent::DroppedFile(path.clone()),
276                                 });
277                             }
278                         }
279                         (source_window, DndState::Accepted)
280                     } else {
281                         // `source_window` won't be part of our DND state if we already rejected the drop in our
282                         // `XdndPosition` handler.
283                         let source_window = client_msg.data.get_long(0) as c_ulong;
284                         (source_window, DndState::Rejected)
285                     };
286                     unsafe {
287                         self.dnd
288                             .send_finished(window, source_window, state)
289                             .expect("Failed to send `XdndFinished` message.");
290                     }
291                     self.dnd.reset();
292                 } else if client_msg.message_type == self.dnd.atoms.leave {
293                     self.dnd.reset();
294                     callback(Event::WindowEvent {
295                         window_id,
296                         event: WindowEvent::HoveredFileCancelled,
297                     });
298                 }
299             }
300 
301             ffi::SelectionNotify => {
302                 let xsel: &ffi::XSelectionEvent = xev.as_ref();
303 
304                 let window = xsel.requestor;
305                 let window_id = mkwid(window);
306 
307                 if xsel.property == self.dnd.atoms.selection {
308                     let mut result = None;
309 
310                     // This is where we receive data from drag and drop
311                     if let Ok(mut data) = unsafe { self.dnd.read_data(window) } {
312                         let parse_result = self.dnd.parse_data(&mut data);
313                         if let Ok(ref path_list) = parse_result {
314                             for path in path_list {
315                                 callback(Event::WindowEvent {
316                                     window_id,
317                                     event: WindowEvent::HoveredFile(path.clone()),
318                                 });
319                             }
320                         }
321                         result = Some(parse_result);
322                     }
323 
324                     self.dnd.result = result;
325                 }
326             }
327 
328             ffi::ConfigureNotify => {
329                 let xev: &ffi::XConfigureEvent = xev.as_ref();
330                 let xwindow = xev.window;
331                 let window_id = mkwid(xwindow);
332 
333                 if let Some(window) = self.with_window(xwindow, Arc::clone) {
334                     // So apparently...
335                     // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root
336                     // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent
337                     // https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5
338                     // We don't want to send `Moved` when this is false, since then every `Resized`
339                     // (whether the window moved or not) is accompanied by an extraneous `Moved` event
340                     // that has a position relative to the parent window.
341                     let is_synthetic = xev.send_event == ffi::True;
342 
343                     // These are both in physical space.
344                     let new_inner_size = (xev.width as u32, xev.height as u32);
345                     let new_inner_position = (xev.x as i32, xev.y as i32);
346 
347                     let mut shared_state_lock = window.shared_state.lock();
348 
349                     let (mut resized, moved) = {
350                         let resized =
351                             util::maybe_change(&mut shared_state_lock.size, new_inner_size);
352                         let moved = if is_synthetic {
353                             util::maybe_change(
354                                 &mut shared_state_lock.inner_position,
355                                 new_inner_position,
356                             )
357                         } else {
358                             // Detect when frame extents change.
359                             // Since this isn't synthetic, as per the notes above, this position is relative to the
360                             // parent window.
361                             let rel_parent = new_inner_position;
362                             if util::maybe_change(
363                                 &mut shared_state_lock.inner_position_rel_parent,
364                                 rel_parent,
365                             ) {
366                                 // This ensures we process the next `Moved`.
367                                 shared_state_lock.inner_position = None;
368                                 // Extra insurance against stale frame extents.
369                                 shared_state_lock.frame_extents = None;
370                             }
371                             false
372                         };
373                         (resized, moved)
374                     };
375 
376                     let new_outer_position = if moved || shared_state_lock.position.is_none() {
377                         // We need to convert client area position to window position.
378                         let frame_extents = shared_state_lock
379                             .frame_extents
380                             .as_ref()
381                             .cloned()
382                             .unwrap_or_else(|| {
383                                 let frame_extents =
384                                     wt.xconn.get_frame_extents_heuristic(xwindow, wt.root);
385                                 shared_state_lock.frame_extents = Some(frame_extents.clone());
386                                 frame_extents
387                             });
388                         let outer = frame_extents
389                             .inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
390                         shared_state_lock.position = Some(outer);
391                         if moved {
392                             // Temporarily unlock shared state to prevent deadlock
393                             MutexGuard::unlocked(&mut shared_state_lock, || {
394                                 callback(Event::WindowEvent {
395                                     window_id,
396                                     event: WindowEvent::Moved(outer.into()),
397                                 });
398                             });
399                         }
400                         outer
401                     } else {
402                         shared_state_lock.position.unwrap()
403                     };
404 
405                     if is_synthetic {
406                         // If we don't use the existing adjusted value when available, then the user can screw up the
407                         // resizing by dragging across monitors *without* dropping the window.
408                         let (width, height) = shared_state_lock
409                             .dpi_adjusted
410                             .unwrap_or_else(|| (xev.width as u32, xev.height as u32));
411 
412                         let last_scale_factor = shared_state_lock.last_monitor.scale_factor;
413                         let new_scale_factor = {
414                             let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
415                             let monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
416 
417                             if monitor.is_dummy() {
418                                 // Avoid updating monitor using a dummy monitor handle
419                                 last_scale_factor
420                             } else {
421                                 shared_state_lock.last_monitor = monitor.clone();
422                                 monitor.scale_factor
423                             }
424                         };
425                         if last_scale_factor != new_scale_factor {
426                             let (new_width, new_height) = window.adjust_for_dpi(
427                                 last_scale_factor,
428                                 new_scale_factor,
429                                 width,
430                                 height,
431                                 &shared_state_lock,
432                             );
433 
434                             let old_inner_size = PhysicalSize::new(width, height);
435                             let mut new_inner_size = PhysicalSize::new(new_width, new_height);
436 
437                             // Temporarily unlock shared state to prevent deadlock
438                             MutexGuard::unlocked(&mut shared_state_lock, || {
439                                 callback(Event::WindowEvent {
440                                     window_id,
441                                     event: WindowEvent::ScaleFactorChanged {
442                                         scale_factor: new_scale_factor,
443                                         new_inner_size: &mut new_inner_size,
444                                     },
445                                 });
446                             });
447 
448                             if new_inner_size != old_inner_size {
449                                 window.set_inner_size_physical(
450                                     new_inner_size.width,
451                                     new_inner_size.height,
452                                 );
453                                 shared_state_lock.dpi_adjusted = Some(new_inner_size.into());
454                                 // if the DPI factor changed, force a resize event to ensure the logical
455                                 // size is computed with the right DPI factor
456                                 resized = true;
457                             }
458                         }
459                     }
460 
461                     // This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
462                     // doesn't need this, but Xfwm does. The hack should not be run on other WMs, since tiling
463                     // WMs constrain the window size, making the resize fail. This would cause an endless stream of
464                     // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU.
465                     if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
466                         if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
467                             // When this finally happens, the event will not be synthetic.
468                             shared_state_lock.dpi_adjusted = None;
469                         } else {
470                             window.set_inner_size_physical(adjusted_size.0, adjusted_size.1);
471                         }
472                     }
473 
474                     if resized {
475                         // Drop the shared state lock to prevent deadlock
476                         drop(shared_state_lock);
477 
478                         callback(Event::WindowEvent {
479                             window_id,
480                             event: WindowEvent::Resized(new_inner_size.into()),
481                         });
482                     }
483                 }
484             }
485 
486             ffi::ReparentNotify => {
487                 let xev: &ffi::XReparentEvent = xev.as_ref();
488 
489                 // This is generally a reliable way to detect when the window manager's been
490                 // replaced, though this event is only fired by reparenting window managers
491                 // (which is almost all of them). Failing to correctly update WM info doesn't
492                 // really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only
493                 // effect is that we waste some time trying to query unsupported properties.
494                 wt.xconn.update_cached_wm_info(wt.root);
495 
496                 self.with_window(xev.window, |window| {
497                     window.invalidate_cached_frame_extents();
498                 });
499             }
500 
501             ffi::DestroyNotify => {
502                 let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
503 
504                 let window = xev.window;
505                 let window_id = mkwid(window);
506 
507                 // In the event that the window's been destroyed without being dropped first, we
508                 // cleanup again here.
509                 wt.windows.borrow_mut().remove(&WindowId(window));
510 
511                 // Since all XIM stuff needs to happen from the same thread, we destroy the input
512                 // context here instead of when dropping the window.
513                 wt.ime
514                     .borrow_mut()
515                     .remove_context(window)
516                     .expect("Failed to destroy input context");
517 
518                 callback(Event::WindowEvent {
519                     window_id,
520                     event: WindowEvent::Destroyed,
521                 });
522             }
523 
524             ffi::VisibilityNotify => {
525                 let xev: &ffi::XVisibilityEvent = xev.as_ref();
526                 let xwindow = xev.window;
527 
528                 self.with_window(xwindow, |window| window.visibility_notify());
529             }
530 
531             ffi::Expose => {
532                 let xev: &ffi::XExposeEvent = xev.as_ref();
533 
534                 // Multiple Expose events may be received for subareas of a window.
535                 // We issue `RedrawRequested` only for the last event of such a series.
536                 if xev.count == 0 {
537                     let window = xev.window;
538                     let window_id = mkwid(window);
539 
540                     callback(Event::RedrawRequested(window_id));
541                 }
542             }
543 
544             ffi::KeyPress | ffi::KeyRelease => {
545                 use crate::event::ElementState::{Pressed, Released};
546 
547                 // Note that in compose/pre-edit sequences, this will always be Released.
548                 let state = if xev.get_type() == ffi::KeyPress {
549                     Pressed
550                 } else {
551                     Released
552                 };
553 
554                 let xkev: &mut ffi::XKeyEvent = xev.as_mut();
555 
556                 let window = xkev.window;
557                 let window_id = mkwid(window);
558 
559                 // Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable
560                 // value, though this should only be an issue under multiseat configurations.
561                 let device = util::VIRTUAL_CORE_KEYBOARD;
562                 let device_id = mkdid(device);
563                 let keycode = xkev.keycode;
564 
565                 // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
566                 // a keycode of 0.
567                 if keycode != 0 {
568                     let scancode = keycode - 8;
569                     let keysym = wt.xconn.lookup_keysym(xkev);
570                     let virtual_keycode = events::keysym_to_element(keysym as c_uint);
571 
572                     update_modifiers!(
573                         ModifiersState::from_x11_mask(xkev.state),
574                         self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode)
575                     );
576 
577                     let modifiers = self.device_mod_state.modifiers();
578 
579                     #[allow(deprecated)]
580                     callback(Event::WindowEvent {
581                         window_id,
582                         event: WindowEvent::KeyboardInput {
583                             device_id,
584                             input: KeyboardInput {
585                                 state,
586                                 scancode,
587                                 virtual_keycode,
588                                 modifiers,
589                             },
590                             is_synthetic: false,
591                         },
592                     });
593                 }
594 
595                 if state == Pressed {
596                     let written = if let Some(ic) = wt.ime.borrow().get_context(window) {
597                         wt.xconn.lookup_utf8(ic, xkev)
598                     } else {
599                         return;
600                     };
601 
602                     for chr in written.chars() {
603                         let event = Event::WindowEvent {
604                             window_id,
605                             event: WindowEvent::ReceivedCharacter(chr),
606                         };
607                         callback(event);
608                     }
609                 }
610             }
611 
612             ffi::GenericEvent => {
613                 let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) {
614                     e
615                 } else {
616                     return;
617                 };
618                 let xev = &guard.cookie;
619                 if self.xi2ext.opcode != xev.extension {
620                     return;
621                 }
622 
623                 use crate::event::{
624                     ElementState::{Pressed, Released},
625                     MouseButton::{Left, Middle, Other, Right},
626                     MouseScrollDelta::LineDelta,
627                     Touch,
628                     WindowEvent::{
629                         AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput,
630                         MouseWheel,
631                     },
632                 };
633 
634                 match xev.evtype {
635                     ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
636                         let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
637                         let window_id = mkwid(xev.event);
638                         let device_id = mkdid(xev.deviceid);
639                         if (xev.flags & ffi::XIPointerEmulated) != 0 {
640                             // Deliver multi-touch events instead of emulated mouse events.
641                             return;
642                         }
643 
644                         let modifiers = ModifiersState::from_x11(&xev.mods);
645                         update_modifiers!(modifiers, None);
646 
647                         let state = if xev.evtype == ffi::XI_ButtonPress {
648                             Pressed
649                         } else {
650                             Released
651                         };
652                         match xev.detail as u32 {
653                             ffi::Button1 => callback(Event::WindowEvent {
654                                 window_id,
655                                 event: MouseInput {
656                                     device_id,
657                                     state,
658                                     button: Left,
659                                     modifiers,
660                                 },
661                             }),
662                             ffi::Button2 => callback(Event::WindowEvent {
663                                 window_id,
664                                 event: MouseInput {
665                                     device_id,
666                                     state,
667                                     button: Middle,
668                                     modifiers,
669                                 },
670                             }),
671                             ffi::Button3 => callback(Event::WindowEvent {
672                                 window_id,
673                                 event: MouseInput {
674                                     device_id,
675                                     state,
676                                     button: Right,
677                                     modifiers,
678                                 },
679                             }),
680 
681                             // Suppress emulated scroll wheel clicks, since we handle the real motion events for those.
682                             // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in
683                             // turn) as axis motion, so we don't otherwise special-case these button presses.
684                             4 | 5 | 6 | 7 => {
685                                 if xev.flags & ffi::XIPointerEmulated == 0 {
686                                     callback(Event::WindowEvent {
687                                         window_id,
688                                         event: MouseWheel {
689                                             device_id,
690                                             delta: match xev.detail {
691                                                 4 => LineDelta(0.0, 1.0),
692                                                 5 => LineDelta(0.0, -1.0),
693                                                 6 => LineDelta(-1.0, 0.0),
694                                                 7 => LineDelta(1.0, 0.0),
695                                                 _ => unreachable!(),
696                                             },
697                                             phase: TouchPhase::Moved,
698                                             modifiers,
699                                         },
700                                     });
701                                 }
702                             }
703 
704                             x => callback(Event::WindowEvent {
705                                 window_id,
706                                 event: MouseInput {
707                                     device_id,
708                                     state,
709                                     button: Other(x as u8),
710                                     modifiers,
711                                 },
712                             }),
713                         }
714                     }
715                     ffi::XI_Motion => {
716                         let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
717                         let device_id = mkdid(xev.deviceid);
718                         let window_id = mkwid(xev.event);
719                         let new_cursor_pos = (xev.event_x, xev.event_y);
720 
721                         let modifiers = ModifiersState::from_x11(&xev.mods);
722                         update_modifiers!(modifiers, None);
723 
724                         let cursor_moved = self.with_window(xev.event, |window| {
725                             let mut shared_state_lock = window.shared_state.lock();
726                             util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
727                         });
728                         if cursor_moved == Some(true) {
729                             let position = PhysicalPosition::new(xev.event_x, xev.event_y);
730 
731                             callback(Event::WindowEvent {
732                                 window_id,
733                                 event: CursorMoved {
734                                     device_id,
735                                     position,
736                                     modifiers,
737                                 },
738                             });
739                         } else if cursor_moved.is_none() {
740                             return;
741                         }
742 
743                         // More gymnastics, for self.devices
744                         let mut events = Vec::new();
745                         {
746                             let mask = unsafe {
747                                 slice::from_raw_parts(
748                                     xev.valuators.mask,
749                                     xev.valuators.mask_len as usize,
750                                 )
751                             };
752                             let mut devices = self.devices.borrow_mut();
753                             let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
754                                 Some(device) => device,
755                                 None => return,
756                             };
757 
758                             let mut value = xev.valuators.values;
759                             for i in 0..xev.valuators.mask_len * 8 {
760                                 if ffi::XIMaskIsSet(mask, i) {
761                                     let x = unsafe { *value };
762                                     if let Some(&mut (_, ref mut info)) = physical_device
763                                         .scroll_axes
764                                         .iter_mut()
765                                         .find(|&&mut (axis, _)| axis == i)
766                                     {
767                                         let delta = (x - info.position) / info.increment;
768                                         info.position = x;
769                                         events.push(Event::WindowEvent {
770                                             window_id,
771                                             event: MouseWheel {
772                                                 device_id,
773                                                 delta: match info.orientation {
774                                                     ScrollOrientation::Horizontal => {
775                                                         LineDelta(delta as f32, 0.0)
776                                                     }
777                                                     // X11 vertical scroll coordinates are opposite to winit's
778                                                     ScrollOrientation::Vertical => {
779                                                         LineDelta(0.0, -delta as f32)
780                                                     }
781                                                 },
782                                                 phase: TouchPhase::Moved,
783                                                 modifiers,
784                                             },
785                                         });
786                                     } else {
787                                         events.push(Event::WindowEvent {
788                                             window_id,
789                                             event: AxisMotion {
790                                                 device_id,
791                                                 axis: i as u32,
792                                                 value: unsafe { *value },
793                                             },
794                                         });
795                                     }
796                                     value = unsafe { value.offset(1) };
797                                 }
798                             }
799                         }
800                         for event in events {
801                             callback(event);
802                         }
803                     }
804 
805                     ffi::XI_Enter => {
806                         let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
807 
808                         let window_id = mkwid(xev.event);
809                         let device_id = mkdid(xev.deviceid);
810 
811                         if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) {
812                             let mut devices = self.devices.borrow_mut();
813                             for device_info in all_info.iter() {
814                                 if device_info.deviceid == xev.sourceid
815                                 // This is needed for resetting to work correctly on i3, and
816                                 // presumably some other WMs. On those, `XI_Enter` doesn't include
817                                 // the physical device ID, so both `sourceid` and `deviceid` are
818                                 // the virtual device.
819                                 || device_info.attachment == xev.sourceid
820                                 {
821                                     let device_id = DeviceId(device_info.deviceid);
822                                     if let Some(device) = devices.get_mut(&device_id) {
823                                         device.reset_scroll_position(device_info);
824                                     }
825                                 }
826                             }
827                         }
828 
829                         if self.window_exists(xev.event) {
830                             callback(Event::WindowEvent {
831                                 window_id,
832                                 event: CursorEntered { device_id },
833                             });
834 
835                             let position = PhysicalPosition::new(xev.event_x, xev.event_y);
836 
837                             // The mods field on this event isn't actually populated, so query the
838                             // pointer device. In the future, we can likely remove this round-trip by
839                             // relying on `Xkb` for modifier values.
840                             //
841                             // This needs to only be done after confirming the window still exists,
842                             // since otherwise we risk getting a `BadWindow` error if the window was
843                             // dropped with queued events.
844                             let modifiers = wt
845                                 .xconn
846                                 .query_pointer(xev.event, xev.deviceid)
847                                 .expect("Failed to query pointer device")
848                                 .get_modifier_state();
849 
850                             callback(Event::WindowEvent {
851                                 window_id,
852                                 event: CursorMoved {
853                                     device_id,
854                                     position,
855                                     modifiers,
856                                 },
857                             });
858                         }
859                     }
860                     ffi::XI_Leave => {
861                         let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
862 
863                         // Leave, FocusIn, and FocusOut can be received by a window that's already
864                         // been destroyed, which the user presumably doesn't want to deal with.
865                         let window_closed = !self.window_exists(xev.event);
866                         if !window_closed {
867                             callback(Event::WindowEvent {
868                                 window_id: mkwid(xev.event),
869                                 event: CursorLeft {
870                                     device_id: mkdid(xev.deviceid),
871                                 },
872                             });
873                         }
874                     }
875                     ffi::XI_FocusIn => {
876                         let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
877 
878                         wt.ime
879                             .borrow_mut()
880                             .focus(xev.event)
881                             .expect("Failed to focus input context");
882 
883                         let modifiers = ModifiersState::from_x11(&xev.mods);
884 
885                         self.device_mod_state.update_state(&modifiers, None);
886 
887                         if self.active_window != Some(xev.event) {
888                             self.active_window = Some(xev.event);
889 
890                             let window_id = mkwid(xev.event);
891                             let position = PhysicalPosition::new(xev.event_x, xev.event_y);
892 
893                             callback(Event::WindowEvent {
894                                 window_id,
895                                 event: Focused(true),
896                             });
897 
898                             if !modifiers.is_empty() {
899                                 callback(Event::WindowEvent {
900                                     window_id,
901                                     event: WindowEvent::ModifiersChanged(modifiers),
902                                 });
903                             }
904 
905                             // The deviceid for this event is for a keyboard instead of a pointer,
906                             // so we have to do a little extra work.
907                             let pointer_id = self
908                                 .devices
909                                 .borrow()
910                                 .get(&DeviceId(xev.deviceid))
911                                 .map(|device| device.attachment)
912                                 .unwrap_or(2);
913 
914                             callback(Event::WindowEvent {
915                                 window_id,
916                                 event: CursorMoved {
917                                     device_id: mkdid(pointer_id),
918                                     position,
919                                     modifiers,
920                                 },
921                             });
922 
923                             // Issue key press events for all pressed keys
924                             self.handle_pressed_keys(
925                                 window_id,
926                                 ElementState::Pressed,
927                                 &mut callback,
928                             );
929                         }
930                     }
931                     ffi::XI_FocusOut => {
932                         let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
933                         if !self.window_exists(xev.event) {
934                             return;
935                         }
936                         wt.ime
937                             .borrow_mut()
938                             .unfocus(xev.event)
939                             .expect("Failed to unfocus input context");
940 
941                         if self.active_window.take() == Some(xev.event) {
942                             let window_id = mkwid(xev.event);
943 
944                             // Issue key release events for all pressed keys
945                             self.handle_pressed_keys(
946                                 window_id,
947                                 ElementState::Released,
948                                 &mut callback,
949                             );
950 
951                             callback(Event::WindowEvent {
952                                 window_id,
953                                 event: WindowEvent::ModifiersChanged(ModifiersState::empty()),
954                             });
955 
956                             callback(Event::WindowEvent {
957                                 window_id,
958                                 event: Focused(false),
959                             })
960                         }
961                     }
962 
963                     ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
964                         let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
965                         let window_id = mkwid(xev.event);
966                         let phase = match xev.evtype {
967                             ffi::XI_TouchBegin => TouchPhase::Started,
968                             ffi::XI_TouchUpdate => TouchPhase::Moved,
969                             ffi::XI_TouchEnd => TouchPhase::Ended,
970                             _ => unreachable!(),
971                         };
972                         if self.window_exists(xev.event) {
973                             let id = xev.detail as u64;
974                             let modifiers = self.device_mod_state.modifiers();
975                             let location =
976                                 PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64);
977 
978                             // Mouse cursor position changes when touch events are received.
979                             // Only the first concurrently active touch ID moves the mouse cursor.
980                             if is_first_touch(&mut self.first_touch, &mut self.num_touch, id, phase)
981                             {
982                                 callback(Event::WindowEvent {
983                                     window_id,
984                                     event: WindowEvent::CursorMoved {
985                                         device_id: mkdid(util::VIRTUAL_CORE_POINTER),
986                                         position: location.cast(),
987                                         modifiers,
988                                     },
989                                 });
990                             }
991 
992                             callback(Event::WindowEvent {
993                                 window_id,
994                                 event: WindowEvent::Touch(Touch {
995                                     device_id: mkdid(xev.deviceid),
996                                     phase,
997                                     location,
998                                     force: None, // TODO
999                                     id,
1000                                 }),
1001                             })
1002                         }
1003                     }
1004 
1005                     ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
1006                         let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1007                         if xev.flags & ffi::XIPointerEmulated == 0 {
1008                             callback(Event::DeviceEvent {
1009                                 device_id: mkdid(xev.deviceid),
1010                                 event: DeviceEvent::Button {
1011                                     button: xev.detail as u32,
1012                                     state: match xev.evtype {
1013                                         ffi::XI_RawButtonPress => Pressed,
1014                                         ffi::XI_RawButtonRelease => Released,
1015                                         _ => unreachable!(),
1016                                     },
1017                                 },
1018                             });
1019                         }
1020                     }
1021 
1022                     ffi::XI_RawMotion => {
1023                         let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1024                         let did = mkdid(xev.deviceid);
1025 
1026                         let mask = unsafe {
1027                             slice::from_raw_parts(
1028                                 xev.valuators.mask,
1029                                 xev.valuators.mask_len as usize,
1030                             )
1031                         };
1032                         let mut value = xev.raw_values;
1033                         let mut mouse_delta = (0.0, 0.0);
1034                         let mut scroll_delta = (0.0, 0.0);
1035                         for i in 0..xev.valuators.mask_len * 8 {
1036                             if ffi::XIMaskIsSet(mask, i) {
1037                                 let x = unsafe { *value };
1038                                 // We assume that every XInput2 device with analog axes is a pointing device emitting
1039                                 // relative coordinates.
1040                                 match i {
1041                                     0 => mouse_delta.0 = x,
1042                                     1 => mouse_delta.1 = x,
1043                                     2 => scroll_delta.0 = x as f32,
1044                                     3 => scroll_delta.1 = x as f32,
1045                                     _ => {}
1046                                 }
1047                                 callback(Event::DeviceEvent {
1048                                     device_id: did,
1049                                     event: DeviceEvent::Motion {
1050                                         axis: i as u32,
1051                                         value: x,
1052                                     },
1053                                 });
1054                                 value = unsafe { value.offset(1) };
1055                             }
1056                         }
1057                         if mouse_delta != (0.0, 0.0) {
1058                             callback(Event::DeviceEvent {
1059                                 device_id: did,
1060                                 event: DeviceEvent::MouseMotion { delta: mouse_delta },
1061                             });
1062                         }
1063                         if scroll_delta != (0.0, 0.0) {
1064                             callback(Event::DeviceEvent {
1065                                 device_id: did,
1066                                 event: DeviceEvent::MouseWheel {
1067                                     delta: LineDelta(scroll_delta.0, scroll_delta.1),
1068                                 },
1069                             });
1070                         }
1071                     }
1072 
1073                     ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
1074                         let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
1075 
1076                         let state = match xev.evtype {
1077                             ffi::XI_RawKeyPress => Pressed,
1078                             ffi::XI_RawKeyRelease => Released,
1079                             _ => unreachable!(),
1080                         };
1081 
1082                         let device_id = mkdid(xev.sourceid);
1083                         let keycode = xev.detail;
1084                         if keycode < 8 {
1085                             return;
1086                         }
1087                         let scancode = (keycode - 8) as u32;
1088                         let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode);
1089                         let virtual_keycode = events::keysym_to_element(keysym as c_uint);
1090                         let modifiers = self.device_mod_state.modifiers();
1091 
1092                         #[allow(deprecated)]
1093                         callback(Event::DeviceEvent {
1094                             device_id,
1095                             event: DeviceEvent::Key(KeyboardInput {
1096                                 scancode,
1097                                 virtual_keycode,
1098                                 state,
1099                                 modifiers,
1100                             }),
1101                         });
1102 
1103                         if let Some(modifier) =
1104                             self.mod_keymap.get_modifier(keycode as ffi::KeyCode)
1105                         {
1106                             self.device_mod_state.key_event(
1107                                 state,
1108                                 keycode as ffi::KeyCode,
1109                                 modifier,
1110                             );
1111 
1112                             let new_modifiers = self.device_mod_state.modifiers();
1113 
1114                             if modifiers != new_modifiers {
1115                                 if let Some(window_id) = self.active_window {
1116                                     callback(Event::WindowEvent {
1117                                         window_id: mkwid(window_id),
1118                                         event: WindowEvent::ModifiersChanged(new_modifiers),
1119                                     });
1120                                 }
1121                             }
1122                         }
1123                     }
1124 
1125                     ffi::XI_HierarchyChanged => {
1126                         let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
1127                         for info in
1128                             unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) }
1129                         {
1130                             if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) {
1131                                 self.init_device(info.deviceid);
1132                                 callback(Event::DeviceEvent {
1133                                     device_id: mkdid(info.deviceid),
1134                                     event: DeviceEvent::Added,
1135                                 });
1136                             } else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved)
1137                             {
1138                                 callback(Event::DeviceEvent {
1139                                     device_id: mkdid(info.deviceid),
1140                                     event: DeviceEvent::Removed,
1141                                 });
1142                                 let mut devices = self.devices.borrow_mut();
1143                                 devices.remove(&DeviceId(info.deviceid));
1144                             }
1145                         }
1146                     }
1147 
1148                     _ => {}
1149                 }
1150             }
1151             _ => {
1152                 if event_type == self.randr_event_offset {
1153                     // In the future, it would be quite easy to emit monitor hotplug events.
1154                     let prev_list = monitor::invalidate_cached_monitor_list();
1155                     if let Some(prev_list) = prev_list {
1156                         let new_list = wt.xconn.available_monitors();
1157                         for new_monitor in new_list {
1158                             prev_list
1159                                 .iter()
1160                                 .find(|prev_monitor| prev_monitor.name == new_monitor.name)
1161                                 .map(|prev_monitor| {
1162                                     if new_monitor.scale_factor != prev_monitor.scale_factor {
1163                                         for (window_id, window) in wt.windows.borrow().iter() {
1164                                             if let Some(window) = window.upgrade() {
1165                                                 // Check if the window is on this monitor
1166                                                 let monitor = window.current_monitor();
1167                                                 if monitor.name == new_monitor.name {
1168                                                     let (width, height) =
1169                                                         window.inner_size_physical();
1170                                                     let (new_width, new_height) = window
1171                                                         .adjust_for_dpi(
1172                                                             prev_monitor.scale_factor,
1173                                                             new_monitor.scale_factor,
1174                                                             width,
1175                                                             height,
1176                                                             &*window.shared_state.lock(),
1177                                                         );
1178 
1179                                                     let window_id = crate::window::WindowId(
1180                                                         crate::platform_impl::platform::WindowId::X(
1181                                                             *window_id,
1182                                                         ),
1183                                                     );
1184                                                     let old_inner_size =
1185                                                         PhysicalSize::new(width, height);
1186                                                     let mut new_inner_size =
1187                                                         PhysicalSize::new(new_width, new_height);
1188 
1189                                                     callback(Event::WindowEvent {
1190                                                         window_id,
1191                                                         event: WindowEvent::ScaleFactorChanged {
1192                                                             scale_factor: new_monitor.scale_factor,
1193                                                             new_inner_size: &mut new_inner_size,
1194                                                         },
1195                                                     });
1196 
1197                                                     if new_inner_size != old_inner_size {
1198                                                         let (new_width, new_height) =
1199                                                             new_inner_size.into();
1200                                                         window.set_inner_size_physical(
1201                                                             new_width, new_height,
1202                                                         );
1203                                                     }
1204                                                 }
1205                                             }
1206                                         }
1207                                     }
1208                                 });
1209                         }
1210                     }
1211                 }
1212             }
1213         }
1214 
1215         match self.ime_receiver.try_recv() {
1216             Ok((window_id, x, y)) => {
1217                 wt.ime.borrow_mut().send_xim_spot(window_id, x, y);
1218             }
1219             Err(_) => (),
1220         }
1221     }
1222 
handle_pressed_keys<F>( &self, window_id: crate::window::WindowId, state: ElementState, callback: &mut F, ) where F: FnMut(Event<'_, T>),1223     fn handle_pressed_keys<F>(
1224         &self,
1225         window_id: crate::window::WindowId,
1226         state: ElementState,
1227         callback: &mut F,
1228     ) where
1229         F: FnMut(Event<'_, T>),
1230     {
1231         let wt = get_xtarget(&self.target);
1232 
1233         let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
1234         let modifiers = self.device_mod_state.modifiers();
1235 
1236         // Get the set of keys currently pressed and apply Key events to each
1237         let keys = wt.xconn.query_keymap();
1238 
1239         for keycode in &keys {
1240             if keycode < 8 {
1241                 continue;
1242             }
1243 
1244             let scancode = (keycode - 8) as u32;
1245             let keysym = wt.xconn.keycode_to_keysym(keycode);
1246             let virtual_keycode = events::keysym_to_element(keysym as c_uint);
1247 
1248             #[allow(deprecated)]
1249             callback(Event::WindowEvent {
1250                 window_id,
1251                 event: WindowEvent::KeyboardInput {
1252                     device_id,
1253                     input: KeyboardInput {
1254                         scancode,
1255                         state,
1256                         virtual_keycode,
1257                         modifiers,
1258                     },
1259                     is_synthetic: true,
1260                 },
1261             });
1262         }
1263     }
1264 }
1265 
is_first_touch(first: &mut Option<u64>, num: &mut u32, id: u64, phase: TouchPhase) -> bool1266 fn is_first_touch(first: &mut Option<u64>, num: &mut u32, id: u64, phase: TouchPhase) -> bool {
1267     match phase {
1268         TouchPhase::Started => {
1269             if *num == 0 {
1270                 *first = Some(id);
1271             }
1272             *num += 1;
1273         }
1274         TouchPhase::Cancelled | TouchPhase::Ended => {
1275             if *first == Some(id) {
1276                 *first = None;
1277             }
1278             *num = num.saturating_sub(1);
1279         }
1280         _ => (),
1281     }
1282 
1283     *first == Some(id)
1284 }
1285