1 | use std::cell::{Cell, RefCell}; |
2 | use std::collections::{HashMap, HashSet, VecDeque}; |
3 | use std::ffi::CStr; |
4 | use std::marker::PhantomData; |
5 | use std::mem::MaybeUninit; |
6 | use std::ops::Deref; |
7 | use std::os::raw::*; |
8 | use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; |
9 | use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; |
10 | use std::sync::{Arc, Weak}; |
11 | use std::time::{Duration, Instant}; |
12 | use std::{fmt, ptr, slice, str}; |
13 | |
14 | use calloop::generic::Generic; |
15 | use calloop::ping::Ping; |
16 | use calloop::{EventLoop as Loop, Readiness}; |
17 | use libc::{setlocale, LC_CTYPE}; |
18 | use tracing::warn; |
19 | |
20 | use x11rb::connection::RequestConnection; |
21 | use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError}; |
22 | use x11rb::protocol::xinput::{self, ConnectionExt as _}; |
23 | use x11rb::protocol::xkb; |
24 | use x11rb::protocol::xproto::{self, ConnectionExt as _}; |
25 | use x11rb::x11_utils::X11Error as LogicalError; |
26 | use x11rb::xcb_ffi::ReplyOrIdError; |
27 | |
28 | use crate::error::{EventLoopError, OsError as RootOsError}; |
29 | use crate::event::{Event, StartCause, WindowEvent}; |
30 | use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed}; |
31 | use crate::platform::pump_events::PumpStatus; |
32 | use crate::platform_impl::common::xkb::Context; |
33 | use crate::platform_impl::platform::{min_timeout, WindowId}; |
34 | use crate::platform_impl::{ |
35 | ActiveEventLoop as PlatformActiveEventLoop, OsError, PlatformCustomCursor, |
36 | }; |
37 | use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, WindowAttributes}; |
38 | |
39 | mod activation; |
40 | mod atoms; |
41 | mod dnd; |
42 | mod event_processor; |
43 | pub mod ffi; |
44 | mod ime; |
45 | mod monitor; |
46 | mod util; |
47 | mod window; |
48 | mod xdisplay; |
49 | mod xsettings; |
50 | |
51 | pub use util::CustomCursor; |
52 | |
53 | use atoms::*; |
54 | use dnd::{Dnd, DndState}; |
55 | use event_processor::{EventProcessor, MAX_MOD_REPLAY_LEN}; |
56 | use ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender}; |
57 | pub(crate) use monitor::{MonitorHandle, VideoModeHandle}; |
58 | use window::UnownedWindow; |
59 | pub(crate) use xdisplay::{XConnection, XError, XNotSupported}; |
60 | |
61 | // Xinput constants not defined in x11rb |
62 | const ALL_DEVICES: u16 = 0; |
63 | const ALL_MASTER_DEVICES: u16 = 1; |
64 | const ICONIC_STATE: u32 = 3; |
65 | |
66 | /// The underlying x11rb connection that we are using. |
67 | type X11rbConnection = x11rb::xcb_ffi::XCBConnection; |
68 | |
69 | type X11Source = Generic<BorrowedFd<'static>>; |
70 | |
71 | struct WakeSender<T> { |
72 | sender: Sender<T>, |
73 | waker: Ping, |
74 | } |
75 | |
76 | impl<T> Clone for WakeSender<T> { |
77 | fn clone(&self) -> Self { |
78 | Self { sender: self.sender.clone(), waker: self.waker.clone() } |
79 | } |
80 | } |
81 | |
82 | impl<T> WakeSender<T> { |
83 | pub fn send(&self, t: T) -> Result<(), EventLoopClosed<T>> { |
84 | let res: Result<(), EventLoopClosed<…>> = self.sender.send(t).map_err(|e: SendError| EventLoopClosed(e.0)); |
85 | if res.is_ok() { |
86 | self.waker.ping(); |
87 | } |
88 | res |
89 | } |
90 | } |
91 | |
92 | struct PeekableReceiver<T> { |
93 | recv: Receiver<T>, |
94 | first: Option<T>, |
95 | } |
96 | |
97 | impl<T> PeekableReceiver<T> { |
98 | pub fn from_recv(recv: Receiver<T>) -> Self { |
99 | Self { recv, first: None } |
100 | } |
101 | |
102 | pub fn has_incoming(&mut self) -> bool { |
103 | if self.first.is_some() { |
104 | return true; |
105 | } |
106 | |
107 | match self.recv.try_recv() { |
108 | Ok(v) => { |
109 | self.first = Some(v); |
110 | true |
111 | }, |
112 | Err(TryRecvError::Empty) => false, |
113 | Err(TryRecvError::Disconnected) => { |
114 | warn!("Channel was disconnected when checking incoming" ); |
115 | false |
116 | }, |
117 | } |
118 | } |
119 | |
120 | pub fn try_recv(&mut self) -> Result<T, TryRecvError> { |
121 | if let Some(first) = self.first.take() { |
122 | return Ok(first); |
123 | } |
124 | self.recv.try_recv() |
125 | } |
126 | } |
127 | |
128 | pub struct ActiveEventLoop { |
129 | xconn: Arc<XConnection>, |
130 | wm_delete_window: xproto::Atom, |
131 | net_wm_ping: xproto::Atom, |
132 | ime_sender: ImeSender, |
133 | control_flow: Cell<ControlFlow>, |
134 | exit: Cell<Option<i32>>, |
135 | root: xproto::Window, |
136 | ime: Option<RefCell<Ime>>, |
137 | windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>, |
138 | redraw_sender: WakeSender<WindowId>, |
139 | activation_sender: WakeSender<ActivationToken>, |
140 | device_events: Cell<DeviceEvents>, |
141 | } |
142 | |
143 | pub struct EventLoop<T: 'static> { |
144 | loop_running: bool, |
145 | event_loop: Loop<'static, EventLoopState>, |
146 | waker: calloop::ping::Ping, |
147 | event_processor: EventProcessor, |
148 | redraw_receiver: PeekableReceiver<WindowId>, |
149 | user_receiver: PeekableReceiver<T>, |
150 | activation_receiver: PeekableReceiver<ActivationToken>, |
151 | user_sender: Sender<T>, |
152 | |
153 | /// The current state of the event loop. |
154 | state: EventLoopState, |
155 | } |
156 | |
157 | type ActivationToken = (WindowId, crate::event_loop::AsyncRequestSerial); |
158 | |
159 | struct EventLoopState { |
160 | /// The latest readiness state for the x11 file descriptor |
161 | x11_readiness: Readiness, |
162 | } |
163 | |
164 | pub struct EventLoopProxy<T: 'static> { |
165 | user_sender: WakeSender<T>, |
166 | } |
167 | |
168 | impl<T: 'static> Clone for EventLoopProxy<T> { |
169 | fn clone(&self) -> Self { |
170 | EventLoopProxy { user_sender: self.user_sender.clone() } |
171 | } |
172 | } |
173 | |
174 | impl<T: 'static> EventLoop<T> { |
175 | pub(crate) fn new(xconn: Arc<XConnection>) -> EventLoop<T> { |
176 | let root = xconn.default_root().root; |
177 | let atoms = xconn.atoms(); |
178 | |
179 | let wm_delete_window = atoms[WM_DELETE_WINDOW]; |
180 | let net_wm_ping = atoms[_NET_WM_PING]; |
181 | |
182 | let dnd = Dnd::new(Arc::clone(&xconn)) |
183 | .expect("Failed to call XInternAtoms when initializing drag and drop" ); |
184 | |
185 | let (ime_sender, ime_receiver) = mpsc::channel(); |
186 | let (ime_event_sender, ime_event_receiver) = mpsc::channel(); |
187 | // Input methods will open successfully without setting the locale, but it won't be |
188 | // possible to actually commit pre-edit sequences. |
189 | unsafe { |
190 | // Remember default locale to restore it if target locale is unsupported |
191 | // by Xlib |
192 | let default_locale = setlocale(LC_CTYPE, ptr::null()); |
193 | setlocale(LC_CTYPE, b" \0" .as_ptr() as *const _); |
194 | |
195 | // Check if set locale is supported by Xlib. |
196 | // If not, calls to some Xlib functions like `XSetLocaleModifiers` |
197 | // will fail. |
198 | let locale_supported = (xconn.xlib.XSupportsLocale)() == 1; |
199 | if !locale_supported { |
200 | let unsupported_locale = setlocale(LC_CTYPE, ptr::null()); |
201 | warn!( |
202 | "Unsupported locale \"{}\". Restoring default locale \"{}\"." , |
203 | CStr::from_ptr(unsupported_locale).to_string_lossy(), |
204 | CStr::from_ptr(default_locale).to_string_lossy() |
205 | ); |
206 | // Restore default locale |
207 | setlocale(LC_CTYPE, default_locale); |
208 | } |
209 | } |
210 | |
211 | let ime = Ime::new(Arc::clone(&xconn), ime_event_sender); |
212 | if let Err(ImeCreationError::OpenFailure(state)) = ime.as_ref() { |
213 | warn!("Failed to open input method: {state:#?}" ); |
214 | } else if let Err(err) = ime.as_ref() { |
215 | warn!("Failed to set input method destruction callback: {err:?}" ); |
216 | } |
217 | |
218 | let ime = ime.ok().map(RefCell::new); |
219 | |
220 | let randr_event_offset = |
221 | xconn.select_xrandr_input(root).expect("Failed to query XRandR extension" ); |
222 | |
223 | let xi2ext = xconn |
224 | .xcb_connection() |
225 | .extension_information(xinput::X11_EXTENSION_NAME) |
226 | .expect("Failed to query XInput extension" ) |
227 | .expect("X server missing XInput extension" ); |
228 | let xkbext = xconn |
229 | .xcb_connection() |
230 | .extension_information(xkb::X11_EXTENSION_NAME) |
231 | .expect("Failed to query XKB extension" ) |
232 | .expect("X server missing XKB extension" ); |
233 | |
234 | // Check for XInput2 support. |
235 | xconn |
236 | .xcb_connection() |
237 | .xinput_xi_query_version(2, 3) |
238 | .expect("Failed to send XInput2 query version request" ) |
239 | .reply() |
240 | .expect("Error while checking for XInput2 query version reply" ); |
241 | |
242 | xconn.update_cached_wm_info(root); |
243 | |
244 | // Create an event loop. |
245 | let event_loop = |
246 | Loop::<EventLoopState>::try_new().expect("Failed to initialize the event loop" ); |
247 | let handle = event_loop.handle(); |
248 | |
249 | // Create the X11 event dispatcher. |
250 | let source = X11Source::new( |
251 | // SAFETY: xcb owns the FD and outlives the source. |
252 | unsafe { BorrowedFd::borrow_raw(xconn.xcb_connection().as_raw_fd()) }, |
253 | calloop::Interest::READ, |
254 | calloop::Mode::Level, |
255 | ); |
256 | handle |
257 | .insert_source(source, |readiness, _, state| { |
258 | state.x11_readiness = readiness; |
259 | Ok(calloop::PostAction::Continue) |
260 | }) |
261 | .expect("Failed to register the X11 event dispatcher" ); |
262 | |
263 | let (waker, waker_source) = |
264 | calloop::ping::make_ping().expect("Failed to create event loop waker" ); |
265 | event_loop |
266 | .handle() |
267 | .insert_source(waker_source, move |_, _, _| { |
268 | // No extra handling is required, we just need to wake-up. |
269 | }) |
270 | .expect("Failed to register the event loop waker source" ); |
271 | |
272 | // Create a channel for handling redraw requests. |
273 | let (redraw_sender, redraw_channel) = mpsc::channel(); |
274 | |
275 | // Create a channel for sending activation tokens. |
276 | let (activation_token_sender, activation_token_channel) = mpsc::channel(); |
277 | |
278 | // Create a channel for sending user events. |
279 | let (user_sender, user_channel) = mpsc::channel(); |
280 | |
281 | let xkb_context = |
282 | Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap(); |
283 | |
284 | let mut xmodmap = util::ModifierKeymap::new(); |
285 | xmodmap.reload_from_x_connection(&xconn); |
286 | |
287 | let window_target = ActiveEventLoop { |
288 | ime, |
289 | root, |
290 | control_flow: Cell::new(ControlFlow::default()), |
291 | exit: Cell::new(None), |
292 | windows: Default::default(), |
293 | ime_sender, |
294 | xconn, |
295 | wm_delete_window, |
296 | net_wm_ping, |
297 | redraw_sender: WakeSender { |
298 | sender: redraw_sender, // not used again so no clone |
299 | waker: waker.clone(), |
300 | }, |
301 | activation_sender: WakeSender { |
302 | sender: activation_token_sender, // not used again so no clone |
303 | waker: waker.clone(), |
304 | }, |
305 | device_events: Default::default(), |
306 | }; |
307 | |
308 | // Set initial device event filter. |
309 | window_target.update_listen_device_events(true); |
310 | |
311 | let root_window_target = |
312 | RootAEL { p: PlatformActiveEventLoop::X(window_target), _marker: PhantomData }; |
313 | |
314 | let event_processor = EventProcessor { |
315 | target: root_window_target, |
316 | dnd, |
317 | devices: Default::default(), |
318 | randr_event_offset, |
319 | ime_receiver, |
320 | ime_event_receiver, |
321 | xi2ext, |
322 | xfiltered_modifiers: VecDeque::with_capacity(MAX_MOD_REPLAY_LEN), |
323 | xmodmap, |
324 | xkbext, |
325 | xkb_context, |
326 | num_touch: 0, |
327 | held_key_press: None, |
328 | first_touch: None, |
329 | active_window: None, |
330 | modifiers: Default::default(), |
331 | is_composing: false, |
332 | }; |
333 | |
334 | // Register for device hotplug events |
335 | // (The request buffer is flushed during `init_device`) |
336 | let xconn = &EventProcessor::window_target(&event_processor.target).xconn; |
337 | |
338 | xconn |
339 | .select_xinput_events( |
340 | root, |
341 | ALL_DEVICES, |
342 | x11rb::protocol::xinput::XIEventMask::HIERARCHY, |
343 | ) |
344 | .expect_then_ignore_error("Failed to register for XInput2 device hotplug events" ); |
345 | |
346 | xconn |
347 | .select_xkb_events( |
348 | 0x100, // Use the "core keyboard device" |
349 | xkb::EventType::NEW_KEYBOARD_NOTIFY |
350 | | xkb::EventType::MAP_NOTIFY |
351 | | xkb::EventType::STATE_NOTIFY, |
352 | ) |
353 | .unwrap(); |
354 | |
355 | event_processor.init_device(ALL_DEVICES); |
356 | |
357 | EventLoop { |
358 | loop_running: false, |
359 | event_loop, |
360 | waker, |
361 | event_processor, |
362 | redraw_receiver: PeekableReceiver::from_recv(redraw_channel), |
363 | activation_receiver: PeekableReceiver::from_recv(activation_token_channel), |
364 | user_receiver: PeekableReceiver::from_recv(user_channel), |
365 | user_sender, |
366 | state: EventLoopState { x11_readiness: Readiness::EMPTY }, |
367 | } |
368 | } |
369 | |
370 | pub fn create_proxy(&self) -> EventLoopProxy<T> { |
371 | EventLoopProxy { |
372 | user_sender: WakeSender { sender: self.user_sender.clone(), waker: self.waker.clone() }, |
373 | } |
374 | } |
375 | |
376 | pub(crate) fn window_target(&self) -> &RootAEL { |
377 | &self.event_processor.target |
378 | } |
379 | |
380 | pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> |
381 | where |
382 | F: FnMut(Event<T>, &RootAEL), |
383 | { |
384 | let exit = loop { |
385 | match self.pump_events(None, &mut event_handler) { |
386 | PumpStatus::Exit(0) => { |
387 | break Ok(()); |
388 | }, |
389 | PumpStatus::Exit(code) => { |
390 | break Err(EventLoopError::ExitFailure(code)); |
391 | }, |
392 | _ => { |
393 | continue; |
394 | }, |
395 | } |
396 | }; |
397 | |
398 | // Applications aren't allowed to carry windows between separate |
399 | // `run_on_demand` calls but if they have only just dropped their |
400 | // windows we need to make sure those last requests are sent to the |
401 | // X Server. |
402 | let wt = EventProcessor::window_target(&self.event_processor.target); |
403 | wt.x_connection().sync_with_server().map_err(|x_err| { |
404 | EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err))))) |
405 | })?; |
406 | |
407 | exit |
408 | } |
409 | |
410 | pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus |
411 | where |
412 | F: FnMut(Event<T>, &RootAEL), |
413 | { |
414 | if !self.loop_running { |
415 | self.loop_running = true; |
416 | |
417 | // run the initial loop iteration |
418 | self.single_iteration(&mut callback, StartCause::Init); |
419 | } |
420 | |
421 | // Consider the possibility that the `StartCause::Init` iteration could |
422 | // request to Exit. |
423 | if !self.exiting() { |
424 | self.poll_events_with_timeout(timeout, &mut callback); |
425 | } |
426 | if let Some(code) = self.exit_code() { |
427 | self.loop_running = false; |
428 | |
429 | callback(Event::LoopExiting, self.window_target()); |
430 | |
431 | PumpStatus::Exit(code) |
432 | } else { |
433 | PumpStatus::Continue |
434 | } |
435 | } |
436 | |
437 | fn has_pending(&mut self) -> bool { |
438 | self.event_processor.poll() |
439 | || self.user_receiver.has_incoming() |
440 | || self.redraw_receiver.has_incoming() |
441 | } |
442 | |
443 | pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) |
444 | where |
445 | F: FnMut(Event<T>, &RootAEL), |
446 | { |
447 | let start = Instant::now(); |
448 | |
449 | let has_pending = self.has_pending(); |
450 | |
451 | timeout = if has_pending { |
452 | // If we already have work to do then we don't want to block on the next poll. |
453 | Some(Duration::ZERO) |
454 | } else { |
455 | let control_flow_timeout = match self.control_flow() { |
456 | ControlFlow::Wait => None, |
457 | ControlFlow::Poll => Some(Duration::ZERO), |
458 | ControlFlow::WaitUntil(wait_deadline) => { |
459 | Some(wait_deadline.saturating_duration_since(start)) |
460 | }, |
461 | }; |
462 | |
463 | min_timeout(control_flow_timeout, timeout) |
464 | }; |
465 | |
466 | self.state.x11_readiness = Readiness::EMPTY; |
467 | if let Err(error) = |
468 | self.event_loop.dispatch(timeout, &mut self.state).map_err(std::io::Error::from) |
469 | { |
470 | tracing::error!("Failed to poll for events: {error:?}" ); |
471 | let exit_code = error.raw_os_error().unwrap_or(1); |
472 | self.set_exit_code(exit_code); |
473 | return; |
474 | } |
475 | |
476 | // NB: `StartCause::Init` is handled as a special case and doesn't need |
477 | // to be considered here |
478 | let cause = match self.control_flow() { |
479 | ControlFlow::Poll => StartCause::Poll, |
480 | ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None }, |
481 | ControlFlow::WaitUntil(deadline) => { |
482 | if Instant::now() < deadline { |
483 | StartCause::WaitCancelled { start, requested_resume: Some(deadline) } |
484 | } else { |
485 | StartCause::ResumeTimeReached { start, requested_resume: deadline } |
486 | } |
487 | }, |
488 | }; |
489 | |
490 | // False positive / spurious wake ups could lead to us spamming |
491 | // redundant iterations of the event loop with no new events to |
492 | // dispatch. |
493 | // |
494 | // If there's no readable event source then we just double check if we |
495 | // have any pending `_receiver` events and if not we return without |
496 | // running a loop iteration. |
497 | // If we don't have any pending `_receiver` |
498 | if !self.has_pending() |
499 | && !matches!(&cause, StartCause::ResumeTimeReached { .. } | StartCause::Poll) |
500 | { |
501 | return; |
502 | } |
503 | |
504 | self.single_iteration(&mut callback, cause); |
505 | } |
506 | |
507 | fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause) |
508 | where |
509 | F: FnMut(Event<T>, &RootAEL), |
510 | { |
511 | callback(Event::NewEvents(cause), &self.event_processor.target); |
512 | |
513 | // NB: For consistency all platforms must emit a 'resumed' event even though X11 |
514 | // applications don't themselves have a formal suspend/resume lifecycle. |
515 | if cause == StartCause::Init { |
516 | callback(Event::Resumed, &self.event_processor.target); |
517 | } |
518 | |
519 | // Process all pending events |
520 | self.drain_events(callback); |
521 | |
522 | // Empty activation tokens. |
523 | while let Ok((window_id, serial)) = self.activation_receiver.try_recv() { |
524 | let token = self.event_processor.with_window(window_id.0 as xproto::Window, |window| { |
525 | window.generate_activation_token() |
526 | }); |
527 | |
528 | match token { |
529 | Some(Ok(token)) => { |
530 | let event = Event::WindowEvent { |
531 | window_id: crate::window::WindowId(window_id), |
532 | event: WindowEvent::ActivationTokenDone { |
533 | serial, |
534 | token: crate::window::ActivationToken::from_raw(token), |
535 | }, |
536 | }; |
537 | callback(event, &self.event_processor.target) |
538 | }, |
539 | Some(Err(e)) => { |
540 | tracing::error!("Failed to get activation token: {}" , e); |
541 | }, |
542 | None => {}, |
543 | } |
544 | } |
545 | |
546 | // Empty the user event buffer |
547 | { |
548 | while let Ok(event) = self.user_receiver.try_recv() { |
549 | callback(Event::UserEvent(event), &self.event_processor.target); |
550 | } |
551 | } |
552 | |
553 | // Empty the redraw requests |
554 | { |
555 | let mut windows = HashSet::new(); |
556 | |
557 | while let Ok(window_id) = self.redraw_receiver.try_recv() { |
558 | windows.insert(window_id); |
559 | } |
560 | |
561 | for window_id in windows { |
562 | let window_id = crate::window::WindowId(window_id); |
563 | callback( |
564 | Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested }, |
565 | &self.event_processor.target, |
566 | ); |
567 | } |
568 | } |
569 | |
570 | // This is always the last event we dispatch before poll again |
571 | { |
572 | callback(Event::AboutToWait, &self.event_processor.target); |
573 | } |
574 | } |
575 | |
576 | fn drain_events<F>(&mut self, callback: &mut F) |
577 | where |
578 | F: FnMut(Event<T>, &RootAEL), |
579 | { |
580 | let mut xev = MaybeUninit::uninit(); |
581 | |
582 | while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { |
583 | let mut xev = unsafe { xev.assume_init() }; |
584 | self.event_processor.process_event(&mut xev, |window_target, event| { |
585 | if let Event::WindowEvent { |
586 | window_id: crate::window::WindowId(wid), |
587 | event: WindowEvent::RedrawRequested, |
588 | } = event |
589 | { |
590 | let window_target = EventProcessor::window_target(window_target); |
591 | window_target.redraw_sender.send(wid).unwrap(); |
592 | } else { |
593 | callback(event, window_target); |
594 | } |
595 | }); |
596 | } |
597 | } |
598 | |
599 | fn control_flow(&self) -> ControlFlow { |
600 | let window_target = EventProcessor::window_target(&self.event_processor.target); |
601 | window_target.control_flow() |
602 | } |
603 | |
604 | fn exiting(&self) -> bool { |
605 | let window_target = EventProcessor::window_target(&self.event_processor.target); |
606 | window_target.exiting() |
607 | } |
608 | |
609 | fn set_exit_code(&self, code: i32) { |
610 | let window_target = EventProcessor::window_target(&self.event_processor.target); |
611 | window_target.set_exit_code(code); |
612 | } |
613 | |
614 | fn exit_code(&self) -> Option<i32> { |
615 | let window_target = EventProcessor::window_target(&self.event_processor.target); |
616 | window_target.exit_code() |
617 | } |
618 | } |
619 | |
620 | impl<T> AsFd for EventLoop<T> { |
621 | fn as_fd(&self) -> BorrowedFd<'_> { |
622 | self.event_loop.as_fd() |
623 | } |
624 | } |
625 | |
626 | impl<T> AsRawFd for EventLoop<T> { |
627 | fn as_raw_fd(&self) -> RawFd { |
628 | self.event_loop.as_raw_fd() |
629 | } |
630 | } |
631 | |
632 | impl ActiveEventLoop { |
633 | /// Returns the `XConnection` of this events loop. |
634 | #[inline ] |
635 | pub(crate) fn x_connection(&self) -> &Arc<XConnection> { |
636 | &self.xconn |
637 | } |
638 | |
639 | pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { |
640 | self.xconn.available_monitors().into_iter().flatten() |
641 | } |
642 | |
643 | pub fn primary_monitor(&self) -> Option<MonitorHandle> { |
644 | self.xconn.primary_monitor().ok() |
645 | } |
646 | |
647 | pub(crate) fn create_custom_cursor(&self, cursor: CustomCursorSource) -> RootCustomCursor { |
648 | RootCustomCursor { inner: PlatformCustomCursor::X(CustomCursor::new(self, cursor.inner)) } |
649 | } |
650 | |
651 | pub fn listen_device_events(&self, allowed: DeviceEvents) { |
652 | self.device_events.set(allowed); |
653 | } |
654 | |
655 | /// Update the device event based on window focus. |
656 | pub fn update_listen_device_events(&self, focus: bool) { |
657 | let device_events = self.device_events.get() == DeviceEvents::Always |
658 | || (focus && self.device_events.get() == DeviceEvents::WhenFocused); |
659 | |
660 | let mut mask = xinput::XIEventMask::from(0u32); |
661 | if device_events { |
662 | mask = xinput::XIEventMask::RAW_MOTION |
663 | | xinput::XIEventMask::RAW_BUTTON_PRESS |
664 | | xinput::XIEventMask::RAW_BUTTON_RELEASE |
665 | | xinput::XIEventMask::RAW_KEY_PRESS |
666 | | xinput::XIEventMask::RAW_KEY_RELEASE; |
667 | } |
668 | |
669 | self.xconn |
670 | .select_xinput_events(self.root, ALL_MASTER_DEVICES, mask) |
671 | .expect_then_ignore_error("Failed to update device event filter" ); |
672 | } |
673 | |
674 | #[cfg (feature = "rwh_05" )] |
675 | pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle { |
676 | let mut display_handle = rwh_05::XlibDisplayHandle::empty(); |
677 | display_handle.display = self.xconn.display as *mut _; |
678 | display_handle.screen = self.xconn.default_screen_index() as c_int; |
679 | display_handle.into() |
680 | } |
681 | |
682 | #[cfg (feature = "rwh_06" )] |
683 | pub fn raw_display_handle_rwh_06( |
684 | &self, |
685 | ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> { |
686 | let display_handle = rwh_06::XlibDisplayHandle::new( |
687 | // SAFETY: display will never be null |
688 | Some( |
689 | std::ptr::NonNull::new(self.xconn.display as *mut _) |
690 | .expect("X11 display should never be null" ), |
691 | ), |
692 | self.xconn.default_screen_index() as c_int, |
693 | ); |
694 | Ok(display_handle.into()) |
695 | } |
696 | |
697 | pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { |
698 | self.control_flow.set(control_flow) |
699 | } |
700 | |
701 | pub(crate) fn control_flow(&self) -> ControlFlow { |
702 | self.control_flow.get() |
703 | } |
704 | |
705 | pub(crate) fn exit(&self) { |
706 | self.exit.set(Some(0)) |
707 | } |
708 | |
709 | pub(crate) fn clear_exit(&self) { |
710 | self.exit.set(None) |
711 | } |
712 | |
713 | pub(crate) fn exiting(&self) -> bool { |
714 | self.exit.get().is_some() |
715 | } |
716 | |
717 | pub(crate) fn set_exit_code(&self, code: i32) { |
718 | self.exit.set(Some(code)) |
719 | } |
720 | |
721 | pub(crate) fn exit_code(&self) -> Option<i32> { |
722 | self.exit.get() |
723 | } |
724 | } |
725 | |
726 | impl<T: 'static> EventLoopProxy<T> { |
727 | pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> { |
728 | self.user_sender.send(event).map_err(|e: EventLoopClosed| EventLoopClosed(e.0)) |
729 | } |
730 | } |
731 | |
732 | struct DeviceInfo<'a> { |
733 | xconn: &'a XConnection, |
734 | info: *const ffi::XIDeviceInfo, |
735 | count: usize, |
736 | } |
737 | |
738 | impl<'a> DeviceInfo<'a> { |
739 | fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> { |
740 | unsafe { |
741 | let mut count: i32 = 0; |
742 | let info: *mut XIDeviceInfo = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count); |
743 | xconn.check_errors().ok()?; |
744 | |
745 | if info.is_null() || count == 0 { |
746 | None |
747 | } else { |
748 | Some(DeviceInfo { xconn, info, count: count as usize }) |
749 | } |
750 | } |
751 | } |
752 | } |
753 | |
754 | impl Drop for DeviceInfo<'_> { |
755 | fn drop(&mut self) { |
756 | assert!(!self.info.is_null()); |
757 | unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; |
758 | } |
759 | } |
760 | |
761 | impl Deref for DeviceInfo<'_> { |
762 | type Target = [ffi::XIDeviceInfo]; |
763 | |
764 | fn deref(&self) -> &Self::Target { |
765 | unsafe { slice::from_raw_parts(self.info, self.count) } |
766 | } |
767 | } |
768 | |
769 | #[derive (Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
770 | pub struct DeviceId(xinput::DeviceId); |
771 | |
772 | impl DeviceId { |
773 | #[allow (unused)] |
774 | pub const fn dummy() -> Self { |
775 | DeviceId(0) |
776 | } |
777 | } |
778 | |
779 | pub(crate) struct Window(Arc<UnownedWindow>); |
780 | |
781 | impl Deref for Window { |
782 | type Target = UnownedWindow; |
783 | |
784 | #[inline ] |
785 | fn deref(&self) -> &UnownedWindow { |
786 | &self.0 |
787 | } |
788 | } |
789 | |
790 | impl Window { |
791 | pub(crate) fn new( |
792 | event_loop: &ActiveEventLoop, |
793 | attribs: WindowAttributes, |
794 | ) -> Result<Self, RootOsError> { |
795 | let window: Arc = Arc::new(data:UnownedWindow::new(event_loop, window_attrs:attribs)?); |
796 | event_loop.windows.borrow_mut().insert(k:window.id(), v:Arc::downgrade(&window)); |
797 | Ok(Window(window)) |
798 | } |
799 | } |
800 | |
801 | impl Drop for Window { |
802 | fn drop(&mut self) { |
803 | let window: &Window = self.deref(); |
804 | let xconn: &Arc = &window.xconn; |
805 | |
806 | if let Ok(c: VoidCookie<'_, XCBConnection>) = xconn.xcb_connection().destroy_window(window.id().0 as xproto::Window) { |
807 | c.ignore_error(); |
808 | } |
809 | } |
810 | } |
811 | |
812 | /// Generic sum error type for X11 errors. |
813 | #[derive (Debug)] |
814 | pub enum X11Error { |
815 | /// An error from the Xlib library. |
816 | Xlib(XError), |
817 | |
818 | /// An error that occurred while trying to connect to the X server. |
819 | Connect(ConnectError), |
820 | |
821 | /// An error that occurred over the connection medium. |
822 | Connection(ConnectionError), |
823 | |
824 | /// An error that occurred logically on the X11 end. |
825 | X11(LogicalError), |
826 | |
827 | /// The XID range has been exhausted. |
828 | XidsExhausted(IdsExhausted), |
829 | |
830 | /// Got `null` from an Xlib function without a reason. |
831 | UnexpectedNull(&'static str), |
832 | |
833 | /// Got an invalid activation token. |
834 | InvalidActivationToken(Vec<u8>), |
835 | |
836 | /// An extension that we rely on is not available. |
837 | MissingExtension(&'static str), |
838 | |
839 | /// Could not find a matching X11 visual for this visualid |
840 | NoSuchVisual(xproto::Visualid), |
841 | |
842 | /// Unable to parse xsettings. |
843 | XsettingsParse(xsettings::ParserError), |
844 | |
845 | /// Failed to get property. |
846 | GetProperty(util::GetPropertyError), |
847 | } |
848 | |
849 | impl fmt::Display for X11Error { |
850 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
851 | match self { |
852 | X11Error::Xlib(e) => write!(f, "Xlib error: {}" , e), |
853 | X11Error::Connect(e) => write!(f, "X11 connection error: {}" , e), |
854 | X11Error::Connection(e) => write!(f, "X11 connection error: {}" , e), |
855 | X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}" , e), |
856 | X11Error::GetProperty(e) => write!(f, "Failed to get X property {}" , e), |
857 | X11Error::X11(e) => write!(f, "X11 error: {:?}" , e), |
858 | X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}" , s), |
859 | X11Error::InvalidActivationToken(s) => write!( |
860 | f, |
861 | "Invalid activation token: {}" , |
862 | std::str::from_utf8(s).unwrap_or("<invalid utf8>" ) |
863 | ), |
864 | X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {}" , s), |
865 | X11Error::NoSuchVisual(visualid) => { |
866 | write!(f, "Could not find a matching X11 visual for ID ` {:x}`" , visualid) |
867 | }, |
868 | X11Error::XsettingsParse(err) => { |
869 | write!(f, "Failed to parse xsettings: {:?}" , err) |
870 | }, |
871 | } |
872 | } |
873 | } |
874 | |
875 | impl std::error::Error for X11Error { |
876 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
877 | match self { |
878 | X11Error::Xlib(e: &XError) => Some(e), |
879 | X11Error::Connect(e: &ConnectError) => Some(e), |
880 | X11Error::Connection(e: &ConnectionError) => Some(e), |
881 | X11Error::XidsExhausted(e: &IdsExhausted) => Some(e), |
882 | _ => None, |
883 | } |
884 | } |
885 | } |
886 | |
887 | impl From<XError> for X11Error { |
888 | fn from(e: XError) -> Self { |
889 | X11Error::Xlib(e) |
890 | } |
891 | } |
892 | |
893 | impl From<ConnectError> for X11Error { |
894 | fn from(e: ConnectError) -> Self { |
895 | X11Error::Connect(e) |
896 | } |
897 | } |
898 | |
899 | impl From<ConnectionError> for X11Error { |
900 | fn from(e: ConnectionError) -> Self { |
901 | X11Error::Connection(e) |
902 | } |
903 | } |
904 | |
905 | impl From<LogicalError> for X11Error { |
906 | fn from(e: LogicalError) -> Self { |
907 | X11Error::X11(e) |
908 | } |
909 | } |
910 | |
911 | impl From<ReplyError> for X11Error { |
912 | fn from(value: ReplyError) -> Self { |
913 | match value { |
914 | ReplyError::ConnectionError(e: ConnectionError) => e.into(), |
915 | ReplyError::X11Error(e: X11Error) => e.into(), |
916 | } |
917 | } |
918 | } |
919 | |
920 | impl From<ime::ImeContextCreationError> for X11Error { |
921 | fn from(value: ime::ImeContextCreationError) -> Self { |
922 | match value { |
923 | ime::ImeContextCreationError::XError(e: XError) => e.into(), |
924 | ime::ImeContextCreationError::Null => Self::UnexpectedNull("XOpenIM" ), |
925 | } |
926 | } |
927 | } |
928 | |
929 | impl From<ReplyOrIdError> for X11Error { |
930 | fn from(value: ReplyOrIdError) -> Self { |
931 | match value { |
932 | ReplyOrIdError::ConnectionError(e: ConnectionError) => e.into(), |
933 | ReplyOrIdError::X11Error(e: X11Error) => e.into(), |
934 | ReplyOrIdError::IdsExhausted => Self::XidsExhausted(IdsExhausted), |
935 | } |
936 | } |
937 | } |
938 | |
939 | impl From<xsettings::ParserError> for X11Error { |
940 | fn from(value: xsettings::ParserError) -> Self { |
941 | Self::XsettingsParse(value) |
942 | } |
943 | } |
944 | |
945 | impl From<util::GetPropertyError> for X11Error { |
946 | fn from(value: util::GetPropertyError) -> Self { |
947 | Self::GetProperty(value) |
948 | } |
949 | } |
950 | |
951 | /// Type alias for a void cookie. |
952 | type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>; |
953 | |
954 | /// Extension trait for `Result<VoidCookie, E>`. |
955 | trait CookieResultExt { |
956 | /// Unwrap the send error and ignore the result. |
957 | fn expect_then_ignore_error(self, msg: &str); |
958 | } |
959 | |
960 | impl<E: fmt::Debug> CookieResultExt for Result<VoidCookie<'_>, E> { |
961 | fn expect_then_ignore_error(self, msg: &str) { |
962 | self.expect(msg).ignore_error() |
963 | } |
964 | } |
965 | |
966 | fn mkwid(w: xproto::Window) -> crate::window::WindowId { |
967 | crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _)) |
968 | } |
969 | fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId { |
970 | crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w))) |
971 | } |
972 | |
973 | #[derive (Debug)] |
974 | pub struct Device { |
975 | _name: String, |
976 | scroll_axes: Vec<(i32, ScrollAxis)>, |
977 | // For master devices, this is the paired device (pointer <-> keyboard). |
978 | // For slave devices, this is the master. |
979 | attachment: c_int, |
980 | } |
981 | |
982 | #[derive (Debug, Copy, Clone)] |
983 | struct ScrollAxis { |
984 | increment: f64, |
985 | orientation: ScrollOrientation, |
986 | position: f64, |
987 | } |
988 | |
989 | #[derive (Debug, Copy, Clone)] |
990 | enum ScrollOrientation { |
991 | Vertical, |
992 | Horizontal, |
993 | } |
994 | |
995 | impl Device { |
996 | fn new(info: &ffi::XIDeviceInfo) -> Self { |
997 | let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() }; |
998 | let mut scroll_axes = Vec::new(); |
999 | |
1000 | if Device::physical_device(info) { |
1001 | // Identify scroll axes |
1002 | for &class_ptr in Device::classes(info) { |
1003 | let ty = unsafe { (*class_ptr)._type }; |
1004 | if ty == ffi::XIScrollClass { |
1005 | let info = unsafe { &*(class_ptr as *const ffi::XIScrollClassInfo) }; |
1006 | scroll_axes.push((info.number, ScrollAxis { |
1007 | increment: info.increment, |
1008 | orientation: match info.scroll_type { |
1009 | ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal, |
1010 | ffi::XIScrollTypeVertical => ScrollOrientation::Vertical, |
1011 | _ => unreachable!(), |
1012 | }, |
1013 | position: 0.0, |
1014 | })); |
1015 | } |
1016 | } |
1017 | } |
1018 | |
1019 | let mut device = |
1020 | Device { _name: name.into_owned(), scroll_axes, attachment: info.attachment }; |
1021 | device.reset_scroll_position(info); |
1022 | device |
1023 | } |
1024 | |
1025 | fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) { |
1026 | if Device::physical_device(info) { |
1027 | for &class_ptr in Device::classes(info) { |
1028 | let ty = unsafe { (*class_ptr)._type }; |
1029 | if ty == ffi::XIValuatorClass { |
1030 | let info = unsafe { &*(class_ptr as *const ffi::XIValuatorClassInfo) }; |
1031 | if let Some(&mut (_, ref mut axis)) = |
1032 | self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number) |
1033 | { |
1034 | axis.position = info.value; |
1035 | } |
1036 | } |
1037 | } |
1038 | } |
1039 | } |
1040 | |
1041 | #[inline ] |
1042 | fn physical_device(info: &ffi::XIDeviceInfo) -> bool { |
1043 | info._use == ffi::XISlaveKeyboard |
1044 | || info._use == ffi::XISlavePointer |
1045 | || info._use == ffi::XIFloatingSlave |
1046 | } |
1047 | |
1048 | #[inline ] |
1049 | fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] { |
1050 | unsafe { |
1051 | slice::from_raw_parts( |
1052 | info.classes as *const *const ffi::XIAnyClassInfo, |
1053 | info.num_classes as usize, |
1054 | ) |
1055 | } |
1056 | } |
1057 | } |
1058 | |
1059 | /// Convert the raw X11 representation for a 32-bit floating point to a double. |
1060 | #[inline ] |
1061 | fn xinput_fp1616_to_float(fp: xinput::Fp1616) -> f64 { |
1062 | (fp as f64) / ((1 << 16) as f64) |
1063 | } |
1064 | |