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