| 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 | |