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