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