Skip to main content

wingy_x11/
event_loop.rs

1use std::cell::{Cell, RefCell};
2use std::collections::{HashMap, HashSet, VecDeque};
3use std::ffi::CStr;
4use std::mem::MaybeUninit;
5use std::ops::Deref;
6use std::os::raw::*;
7use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
10use std::sync::{Arc, LazyLock, Mutex, Weak};
11use std::time::{Duration, Instant};
12use std::{fmt, mem, ptr, slice, str};
13
14use calloop::generic::Generic;
15use calloop::ping::Ping;
16use calloop::{EventLoop as Loop, Readiness};
17use libc::{LC_CTYPE, setlocale};
18use tracing::warn;
19use wingy_common::xkb::Context;
20use wingy_core::application::ApplicationHandler;
21use wingy_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
22use wingy_core::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType};
23use wingy_core::error::{
24    CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, TransferError,
25};
26use wingy_core::event::{DeviceId, StartCause, WindowEvent};
27use wingy_core::event_loop::primary_selection::PrimarySelectionExt;
28use wingy_core::event_loop::pump_events::PumpStatus;
29use wingy_core::event_loop::{
30    ActiveEventLoop as RootActiveEventLoop, AsyncRequestSerial, ControlFlow, DeviceEvents,
31    DndAction, EventLoopProvider, EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
32    OwnedDisplayHandle as CoreOwnedDisplayHandle,
33};
34use wingy_core::monitor::MonitorHandle as CoreMonitorHandle;
35use wingy_core::window::{Theme, Window as CoreWindow, WindowAttributes, WindowId};
36use x11rb::connection::RequestConnection;
37use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
38use x11rb::protocol::xinput::{self, ConnectionExt as _};
39use x11rb::protocol::{ErrorKind, xkb, xproto};
40use x11rb::x11_utils::X11Error as LogicalError;
41use x11rb::xcb_ffi::ReplyOrIdError;
42
43use crate::atoms::{
44    _NET_WM_PING, _NET_WM_SYNC_REQUEST, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y, Atoms,
45    WM_DELETE_WINDOW,
46};
47use crate::data_transfer::{ClipboardSelectionType, DataTransferState};
48use crate::event_processor::{EventProcessor, MAX_MOD_REPLAY_LEN};
49use crate::ime::{self, Ime, ImeCreationError, ImeSender};
50use crate::util::{self, CustomCursor};
51use crate::window::{UnownedWindow, Window};
52use crate::xdisplay::{XConnection, XError, XNotSupported};
53use crate::{Selection, XlibErrorHook, ffi, xsettings};
54
55// Xinput constants not defined in x11rb
56pub(crate) const ALL_DEVICES: u16 = 0;
57pub(crate) const ALL_MASTER_DEVICES: u16 = 1;
58pub(crate) const ICONIC_STATE: u32 = 3;
59
60/// The underlying x11rb connection that we are using.
61type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
62
63type X11Source = Generic<BorrowedFd<'static>>;
64
65pub(crate) static X11_BACKEND: LazyLock<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
66    LazyLock::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)));
67
68/// Hooks for X11 errors.
69pub(crate) static XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new());
70
71unsafe extern "C" fn x_error_callback(
72    display: *mut ffi::Display,
73    event: *mut ffi::XErrorEvent,
74) -> c_int {
75    let xconn_lock = X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner());
76    if let Ok(ref xconn) = *xconn_lock {
77        // Call all the hooks.
78        let mut error_handled = false;
79        for hook in XLIB_ERROR_HOOKS.lock().unwrap().iter() {
80            error_handled |= hook(display as *mut _, event as *mut _);
81        }
82
83        // `assume_init` is safe here because the array consists of `MaybeUninit` values,
84        // which do not require initialization.
85        let mut buf: [MaybeUninit<c_char>; 1024] = unsafe { MaybeUninit::uninit().assume_init() };
86        unsafe {
87            (xconn.xlib.XGetErrorText)(
88                display,
89                (*event).error_code as c_int,
90                buf.as_mut_ptr() as *mut c_char,
91                buf.len() as c_int,
92            )
93        };
94        let description =
95            unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) }.to_string_lossy();
96
97        let error = unsafe {
98            XError {
99                description: description.into_owned(),
100                error_code: (*event).error_code,
101                request_code: (*event).request_code,
102                minor_code: (*event).minor_code,
103            }
104        };
105
106        // Don't log error.
107        if !error_handled {
108            tracing::error!("X11 error: {:#?}", error);
109            // XXX only update the error, if it wasn't handled by any of the hooks.
110            *xconn.latest_error.lock().unwrap() = Some(error);
111        }
112    }
113    // Fun fact: this return value is completely ignored.
114    0
115}
116
117#[derive(Debug)]
118pub(crate) struct WakeSender<T> {
119    sender: Sender<T>,
120    waker: Ping,
121}
122
123impl<T> Clone for WakeSender<T> {
124    fn clone(&self) -> Self {
125        Self { sender: self.sender.clone(), waker: self.waker.clone() }
126    }
127}
128
129impl<T> WakeSender<T> {
130    pub fn send(&self, t: T) {
131        let res = self.sender.send(t);
132        if res.is_ok() {
133            self.waker.ping();
134        }
135    }
136}
137
138#[derive(Debug)]
139struct PeekableReceiver<T> {
140    recv: Receiver<T>,
141    first: Option<T>,
142}
143
144impl<T> PeekableReceiver<T> {
145    pub fn from_recv(recv: Receiver<T>) -> Self {
146        Self { recv, first: None }
147    }
148
149    pub fn has_incoming(&mut self) -> bool {
150        if self.first.is_some() {
151            return true;
152        }
153
154        match self.recv.try_recv() {
155            Ok(v) => {
156                self.first = Some(v);
157                true
158            },
159            Err(TryRecvError::Empty) => false,
160            Err(TryRecvError::Disconnected) => {
161                warn!("Channel was disconnected when checking incoming");
162                false
163            },
164        }
165    }
166
167    pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
168        if let Some(first) = self.first.take() {
169            return Ok(first);
170        }
171        self.recv.try_recv()
172    }
173}
174
175#[derive(Debug)]
176pub struct ActiveEventLoop {
177    pub(crate) xconn: Arc<XConnection>,
178    pub(crate) data_transfer_state: RefCell<DataTransferState>,
179    pub(crate) wm_delete_window: xproto::Atom,
180    pub(crate) net_wm_ping: xproto::Atom,
181    pub(crate) net_wm_sync_request: xproto::Atom,
182    pub(crate) ime_sender: ImeSender,
183    control_flow: Cell<ControlFlow>,
184    exit: Cell<Option<i32>>,
185    pub(crate) root: xproto::Window,
186    pub(crate) ime: Option<RefCell<Ime>>,
187    pub(crate) windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
188    pub(crate) redraw_sender: WakeSender<WindowId>,
189    pub(crate) activation_sender: WakeSender<ActivationItem>,
190    event_loop_proxy: CoreEventLoopProxy,
191    device_events: Cell<DeviceEvents>,
192}
193
194#[derive(Debug)]
195pub struct EventLoop {
196    loop_running: bool,
197    event_loop: Loop<'static, EventLoopState>,
198    event_processor: EventProcessor,
199    redraw_receiver: PeekableReceiver<WindowId>,
200    activation_receiver: PeekableReceiver<ActivationItem>,
201
202    /// The current state of the event loop.
203    state: EventLoopState,
204}
205
206pub(crate) type ActivationItem = (WindowId, wingy_core::event_loop::AsyncRequestSerial);
207
208#[derive(Debug)]
209struct EventLoopState {
210    /// The latest readiness state for the x11 file descriptor
211    x11_readiness: Readiness,
212
213    /// User requested a wake up.
214    proxy_wake_up: bool,
215}
216
217impl EventLoop {
218    pub fn new() -> Result<EventLoop, EventLoopError> {
219        static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
220        if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
221            // Required?
222            return Err(EventLoopError::RecreationAttempt);
223        }
224
225        let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
226            Ok(xconn) => xconn.clone(),
227            Err(XNotSupported::ExtensionNotSupported(reason)) => {
228                return Err(NotSupportedError::new(reason).into());
229            },
230            Err(err) => return Err(os_error!(err.clone()).into()),
231        };
232
233        let root = xconn.default_root().root;
234        let atoms = xconn.atoms();
235
236        let wm_delete_window = atoms[WM_DELETE_WINDOW];
237        let net_wm_ping = atoms[_NET_WM_PING];
238        let net_wm_sync_request = atoms[_NET_WM_SYNC_REQUEST];
239
240        let (ime_sender, ime_receiver) = mpsc::channel();
241        let (ime_event_sender, ime_event_receiver) = mpsc::channel();
242        // Input methods will open successfully without setting the locale, but it won't be
243        // possible to actually commit pre-edit sequences.
244        unsafe {
245            // Remember default locale to restore it if target locale is unsupported
246            // by Xlib
247            let default_locale = setlocale(LC_CTYPE, ptr::null());
248            setlocale(LC_CTYPE, c"".as_ptr() as *const _);
249
250            // Check if set locale is supported by Xlib.
251            // If not, calls to some Xlib functions like `XSetLocaleModifiers`
252            // will fail.
253            let locale_supported = (xconn.xlib.XSupportsLocale)() == 1;
254            if !locale_supported {
255                let unsupported_locale = setlocale(LC_CTYPE, ptr::null());
256                warn!(
257                    "Unsupported locale \"{}\". Restoring default locale \"{}\".",
258                    CStr::from_ptr(unsupported_locale).to_string_lossy(),
259                    CStr::from_ptr(default_locale).to_string_lossy()
260                );
261                // Restore default locale
262                setlocale(LC_CTYPE, default_locale);
263            }
264        }
265
266        let ime = Ime::new(Arc::clone(&xconn), ime_event_sender);
267        if let Err(ImeCreationError::OpenFailure(state)) = ime.as_ref() {
268            warn!("Failed to open input method: {state:#?}");
269        } else if let Err(err) = ime.as_ref() {
270            warn!("Failed to set input method destruction callback: {err:?}");
271        }
272
273        let ime = ime.ok().map(RefCell::new);
274
275        let randr_event_offset = xconn.select_xrandr_input(root).map_err(|err| match err {
276            X11Error::MissingExtension(_) => EventLoopError::NotSupported(NotSupportedError::new(
277                "the X11 backend requires XRandR 1.2 or newer",
278            )),
279            error => os_error!(error).into(),
280        })?;
281
282        let xi2ext = xconn
283            .xcb_connection()
284            .extension_information(xinput::X11_EXTENSION_NAME)
285            .map_err(|err| os_error!(X11Error::from(err)))?
286            .ok_or_else(|| {
287                NotSupportedError::new("the X11 backend requires XInput 2.0 or newer")
288            })?;
289        let xkbext = xconn
290            .xcb_connection()
291            .extension_information(xkb::X11_EXTENSION_NAME)
292            .map_err(|err| os_error!(X11Error::from(err)))?
293            .ok_or_else(|| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
294        let xfixesext = xconn
295            .xcb_connection()
296            .extension_information(x11rb::protocol::xfixes::X11_EXTENSION_NAME)
297            .map_err(|err| os_error!(X11Error::from(err)))?
298            .ok_or_else(|| {
299                NotSupportedError::new("the X11 backend requires the XFixes extension")
300            })?;
301
302        let data_transfer_state = DataTransferState::new(Arc::clone(&xconn)).into();
303        // Check for XInput2 support.
304        xconn
305            .xcb_connection()
306            .xinput_xi_query_version(2, 3)
307            .map_err(|err| os_error!(X11Error::from(err)))?
308            .reply()
309            .map_err(|err| match err {
310                ReplyError::X11Error(error) if error.error_kind == ErrorKind::Request => {
311                    EventLoopError::NotSupported(NotSupportedError::new(
312                        "the X11 backend requires XInput 2.0 or newer",
313                    ))
314                },
315                error => os_error!(X11Error::from(error)).into(),
316            })?;
317
318        xconn.update_cached_wm_info(root);
319
320        // Create an event loop.
321        let event_loop =
322            Loop::<EventLoopState>::try_new().expect("Failed to initialize the event loop");
323        let handle = event_loop.handle();
324
325        // Create the X11 event dispatcher.
326        let source = X11Source::new(
327            // SAFETY: xcb owns the FD and outlives the source.
328            unsafe { BorrowedFd::borrow_raw(xconn.xcb_connection().as_raw_fd()) },
329            calloop::Interest::READ,
330            calloop::Mode::Level,
331        );
332        handle
333            .insert_source(source, |readiness, _, state| {
334                state.x11_readiness = readiness;
335                Ok(calloop::PostAction::Continue)
336            })
337            .expect("Failed to register the X11 event dispatcher");
338
339        let (waker, waker_source) =
340            calloop::ping::make_ping().expect("Failed to create event loop waker");
341        event_loop
342            .handle()
343            .insert_source(waker_source, move |_, _, _| {
344                // No extra handling is required, we just need to wake-up.
345            })
346            .expect("Failed to register the event loop waker source");
347
348        // Create a channel for handling redraw requests.
349        let (redraw_sender, redraw_channel) = mpsc::channel();
350
351        // Create a channel for sending activation tokens.
352        let (activation_token_sender, activation_token_channel) = mpsc::channel();
353
354        // Create a channel for sending user events.
355        let (user_waker, user_waker_source) =
356            calloop::ping::make_ping().expect("Failed to create user event loop waker.");
357        event_loop
358            .handle()
359            .insert_source(user_waker_source, move |_, _, state| {
360                // No extra handling is required, we just need to wake-up.
361                state.proxy_wake_up = true;
362            })
363            .expect("Failed to register the event loop waker source");
364        let event_loop_proxy = EventLoopProxy::new(user_waker);
365
366        let xkb_context = Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection())
367            .map_err(|_| NotSupportedError::new("the X11 backend requires XKB 1.0 or newer"))?;
368
369        let mut xmodmap = util::ModifierKeymap::new();
370        xmodmap.reload_from_x_connection(&xconn);
371
372        let window_target = ActiveEventLoop {
373            ime,
374            data_transfer_state,
375            root,
376            control_flow: Cell::new(ControlFlow::default()),
377            exit: Cell::new(None),
378            windows: Default::default(),
379            ime_sender,
380            xconn,
381            wm_delete_window,
382            net_wm_ping,
383            net_wm_sync_request,
384            redraw_sender: WakeSender {
385                sender: redraw_sender, // not used again so no clone
386                waker: waker.clone(),
387            },
388            activation_sender: WakeSender {
389                sender: activation_token_sender, // not used again so no clone
390                waker: waker.clone(),
391            },
392            event_loop_proxy: event_loop_proxy.into(),
393            device_events: Default::default(),
394        };
395
396        // Set initial device event filter.
397        window_target.update_listen_device_events(true);
398
399        let event_processor = EventProcessor {
400            target: window_target,
401            devices: Default::default(),
402            randr_event_offset,
403            ime_receiver,
404            ime_event_receiver,
405            xi2ext,
406            xfiltered_modifiers: VecDeque::with_capacity(MAX_MOD_REPLAY_LEN),
407            xmodmap,
408            xkbext,
409            xfixesext,
410            xkb_context,
411            num_touch: 0,
412            held_key_press: None,
413            first_touch: None,
414            active_window: None,
415            modifiers: Default::default(),
416            is_composing: false,
417        };
418
419        // Register for device hotplug events
420        // (The request buffer is flushed during `init_device`)
421        event_processor
422            .target
423            .xconn
424            .select_xinput_events(
425                root,
426                ALL_DEVICES,
427                x11rb::protocol::xinput::XIEventMask::HIERARCHY,
428            )
429            .expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
430
431        event_processor
432            .target
433            .xconn
434            .select_xkb_events(
435                0x100, // Use the "core keyboard device"
436                xkb::EventType::NEW_KEYBOARD_NOTIFY
437                    | xkb::EventType::MAP_NOTIFY
438                    | xkb::EventType::STATE_NOTIFY,
439            )
440            .map_err(|err| os_error!(err))?;
441
442        event_processor.init_device(ALL_DEVICES);
443
444        let event_loop = EventLoop {
445            loop_running: false,
446            event_loop,
447            event_processor,
448            redraw_receiver: PeekableReceiver::from_recv(redraw_channel),
449            activation_receiver: PeekableReceiver::from_recv(activation_token_channel),
450            state: EventLoopState { x11_readiness: Readiness::EMPTY, proxy_wake_up: false },
451        };
452
453        Ok(event_loop)
454    }
455
456    pub fn window_target(&self) -> &dyn RootActiveEventLoop {
457        &self.event_processor.target
458    }
459
460    pub fn run_app_on_demand<A: ApplicationHandler>(
461        &mut self,
462        mut app: A,
463    ) -> Result<(), EventLoopError> {
464        self.event_processor.target.clear_exit();
465        let exit = loop {
466            match self.pump_app_events(None, &mut app) {
467                PumpStatus::Exit(0) => {
468                    break Ok(());
469                },
470                PumpStatus::Exit(code) => {
471                    break Err(EventLoopError::ExitFailure(code));
472                },
473                _ => {
474                    continue;
475                },
476            }
477        };
478
479        // Applications aren't allowed to carry windows between separate
480        // `run_on_demand` calls but if they have only just dropped their
481        // windows we need to make sure those last requests are sent to the
482        // X Server.
483        self.event_processor
484            .target
485            .x_connection()
486            .sync_with_server()
487            .map_err(|x_err| EventLoopError::Os(os_error!(X11Error::Xlib(x_err))))?;
488
489        exit
490    }
491
492    pub fn pump_app_events<A: ApplicationHandler>(
493        &mut self,
494        timeout: Option<Duration>,
495        mut app: A,
496    ) -> PumpStatus {
497        if !self.loop_running {
498            self.loop_running = true;
499
500            // run the initial loop iteration
501            self.single_iteration(&mut app, StartCause::Init);
502        }
503
504        // Consider the possibility that the `StartCause::Init` iteration could
505        // request to Exit.
506        if !self.exiting() {
507            self.poll_events_with_timeout(timeout, &mut app);
508        }
509        if let Some(code) = self.exit_code() {
510            self.loop_running = false;
511
512            PumpStatus::Exit(code)
513        } else {
514            PumpStatus::Continue
515        }
516    }
517
518    fn has_pending(&mut self) -> bool {
519        self.event_processor.poll()
520            || self.state.proxy_wake_up
521            || self.redraw_receiver.has_incoming()
522    }
523
524    fn poll_events_with_timeout<A: ApplicationHandler>(
525        &mut self,
526        mut timeout: Option<Duration>,
527        app: &mut A,
528    ) {
529        let start = Instant::now();
530
531        let has_pending = self.has_pending();
532
533        timeout = if has_pending {
534            // If we already have work to do then we don't want to block on the next poll.
535            Some(Duration::ZERO)
536        } else {
537            let control_flow_timeout = match self.control_flow() {
538                ControlFlow::Wait => None,
539                ControlFlow::Poll => Some(Duration::ZERO),
540                ControlFlow::WaitUntil(wait_deadline) => {
541                    Some(wait_deadline.saturating_duration_since(start))
542                },
543            };
544
545            min_timeout(control_flow_timeout, timeout)
546        };
547
548        self.state.x11_readiness = Readiness::EMPTY;
549        if let Err(error) =
550            self.event_loop.dispatch(timeout, &mut self.state).map_err(std::io::Error::from)
551        {
552            tracing::error!("Failed to poll for events: {error:?}");
553            let exit_code = error.raw_os_error().unwrap_or(1);
554            self.set_exit_code(exit_code);
555            return;
556        }
557
558        // NB: `StartCause::Init` is handled as a special case and doesn't need
559        // to be considered here
560        let cause = match self.control_flow() {
561            ControlFlow::Poll => StartCause::Poll,
562            ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
563            ControlFlow::WaitUntil(deadline) => {
564                if Instant::now() < deadline {
565                    StartCause::WaitCancelled { start, requested_resume: Some(deadline) }
566                } else {
567                    StartCause::ResumeTimeReached { start, requested_resume: deadline }
568                }
569            },
570        };
571
572        // False positive / spurious wake ups could lead to us spamming
573        // redundant iterations of the event loop with no new events to
574        // dispatch.
575        //
576        // If there's no readable event source then we just double check if we
577        // have any pending `_receiver` events and if not we return without
578        // running a loop iteration.
579        // If we don't have any pending `_receiver`
580        if !self.has_pending()
581            && !matches!(&cause, StartCause::ResumeTimeReached { .. } | StartCause::Poll)
582            && timeout.is_none()
583        {
584            return;
585        }
586
587        self.single_iteration(app, cause);
588    }
589
590    fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
591        app.new_events(&self.event_processor.target, cause);
592
593        // NB: For consistency all platforms must call `can_create_surfaces` even though X11
594        // applications don't themselves have a formal surface destroy/create lifecycle.
595        if cause == StartCause::Init {
596            app.can_create_surfaces(&self.event_processor.target)
597        }
598
599        // Process all pending events
600        self.drain_events(app);
601
602        // Empty activation tokens.
603        while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
604            let token = self
605                .event_processor
606                .with_window(window_id.into_raw() as xproto::Window, |window| {
607                    window.generate_activation_token()
608                });
609
610            match token {
611                Some(Ok(token)) => {
612                    let event = WindowEvent::ActivationTokenDone {
613                        serial,
614                        token: wingy_core::window::ActivationToken::from_raw(token),
615                    };
616                    app.window_event(&self.event_processor.target, window_id, event);
617                },
618                Some(Err(e)) => {
619                    tracing::error!("Failed to get activation token: {}", e);
620                },
621                None => {},
622            }
623        }
624
625        // Empty the user event buffer
626        if mem::take(&mut self.state.proxy_wake_up) {
627            app.proxy_wake_up(&self.event_processor.target);
628        }
629
630        // Empty the redraw requests
631        {
632            let mut windows = HashSet::new();
633
634            while let Ok(window_id) = self.redraw_receiver.try_recv() {
635                windows.insert(window_id);
636            }
637
638            for window_id in windows {
639                app.window_event(
640                    &self.event_processor.target,
641                    window_id,
642                    WindowEvent::RedrawRequested,
643                );
644            }
645        }
646
647        // This is always the last event we dispatch before poll again
648        app.about_to_wait(&self.event_processor.target);
649    }
650
651    fn drain_events<A: ApplicationHandler>(&mut self, app: &mut A) {
652        let mut xev = MaybeUninit::uninit();
653
654        while let Some(xev) = self.event_processor.poll_one_event(&mut xev) {
655            self.event_processor.process_event(xev, app);
656        }
657    }
658
659    fn control_flow(&self) -> ControlFlow {
660        self.event_processor.target.control_flow()
661    }
662
663    fn exiting(&self) -> bool {
664        self.event_processor.target.exiting()
665    }
666
667    fn set_exit_code(&self, code: i32) {
668        self.event_processor.target.set_exit_code(code);
669    }
670
671    fn exit_code(&self) -> Option<i32> {
672        self.event_processor.target.exit_code()
673    }
674}
675
676impl EventLoopProvider for EventLoop {
677    fn run_app<A: ApplicationHandler + 'static>(
678        mut self,
679        mut app: A,
680    ) -> Result<(), EventLoopError> {
681        let result = self.run_app_on_demand(&mut app);
682        // SAFETY: unsure that the state is dropped before the exit from the event loop.
683        drop(app);
684        result
685    }
686
687    fn create_proxy(&self) -> CoreEventLoopProxy {
688        self.window_target().create_proxy()
689    }
690
691    fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
692        self.window_target().owned_display_handle()
693    }
694
695    fn listen_device_events(&self, allowed: DeviceEvents) {
696        self.window_target().listen_device_events(allowed);
697    }
698
699    fn set_control_flow(&self, control_flow: ControlFlow) {
700        self.window_target().set_control_flow(control_flow);
701    }
702
703    fn create_custom_cursor(
704        &self,
705        custom_cursor: CustomCursorSource,
706    ) -> Result<CoreCustomCursor, CustomCursorError> {
707        self.window_target().create_custom_cursor(custom_cursor)
708    }
709}
710
711impl AsFd for EventLoop {
712    fn as_fd(&self) -> BorrowedFd<'_> {
713        self.event_loop.as_fd()
714    }
715}
716
717impl AsRawFd for EventLoop {
718    fn as_raw_fd(&self) -> RawFd {
719        self.event_loop.as_raw_fd()
720    }
721}
722
723impl ActiveEventLoop {
724    /// Returns the `XConnection` of this events loop.
725    #[inline]
726    pub(crate) fn x_connection(&self) -> &Arc<XConnection> {
727        &self.xconn
728    }
729
730    /// Update the device event based on window focus.
731    pub fn update_listen_device_events(&self, focus: bool) {
732        let device_events = self.device_events.get() == DeviceEvents::Always
733            || (focus && self.device_events.get() == DeviceEvents::WhenFocused);
734
735        let mut mask = xinput::XIEventMask::from(0u32);
736        if device_events {
737            mask = xinput::XIEventMask::RAW_MOTION
738                | xinput::XIEventMask::RAW_BUTTON_PRESS
739                | xinput::XIEventMask::RAW_BUTTON_RELEASE
740                | xinput::XIEventMask::RAW_KEY_PRESS
741                | xinput::XIEventMask::RAW_KEY_RELEASE;
742        }
743
744        self.xconn
745            .select_xinput_events(self.root, ALL_MASTER_DEVICES, mask)
746            .expect_then_ignore_error("Failed to update device event filter");
747    }
748
749    pub(crate) fn clear_exit(&self) {
750        self.exit.set(None)
751    }
752
753    pub(crate) fn set_exit_code(&self, code: i32) {
754        self.exit.set(Some(code))
755    }
756
757    pub(crate) fn exit_code(&self) -> Option<i32> {
758        self.exit.get()
759    }
760
761    /// We must choose a window to get the clipboard from or attach it to. Currently choose the
762    /// focused window (TODO: better strategy?).
763    pub(crate) fn clipboard_window(&self) -> Option<xproto::Window> {
764        self.windows
765            .borrow()
766            .iter()
767            .find(|(_, window)| window.upgrade().is_some_and(|window| window.has_focus()))
768            .map(|(id, _)| id.into_raw() as _)
769            .or_else(|| {
770                self.windows
771                    .borrow()
772                    .iter()
773                    .find(|(_, window)| window.upgrade().is_some())
774                    .map(|(id, _)| id.into_raw() as _)
775            })
776    }
777
778    fn read_clipboard(
779        &self,
780        clipboard: ClipboardSelectionType,
781    ) -> Result<Option<DataTransferId>, TransferError> {
782        let selection = clipboard.selection_atom(self.xconn.atoms());
783        if self.xconn.selection_owner(selection).map_err(|err| os_error!(err))?.is_none() {
784            return Ok(None);
785        }
786
787        let mut data_transfer = self.data_transfer_state.borrow_mut();
788
789        Ok(Some(data_transfer.request_clipboard_read(clipboard)))
790    }
791
792    fn write_clipboard(
793        &self,
794        clipboard: ClipboardSelectionType,
795        send_data: Box<dyn DataTransferSend>,
796    ) -> Result<(), TransferError> {
797        let Some(xwindow) = self.clipboard_window() else {
798            return Err(os_error!("no active window").into());
799        };
800        let mut data_transfer = self.data_transfer_state.borrow_mut();
801        data_transfer.set_clipboard(xwindow, send_data, clipboard);
802
803        Ok(())
804    }
805}
806
807impl RootActiveEventLoop for ActiveEventLoop {
808    fn create_proxy(&self) -> CoreEventLoopProxy {
809        self.event_loop_proxy.clone()
810    }
811
812    fn create_window(
813        &self,
814        window_attributes: WindowAttributes,
815    ) -> Result<Box<dyn CoreWindow>, CreateWindowError> {
816        Ok(Box::new(Window::new(self, window_attributes)?))
817    }
818
819    fn create_custom_cursor(
820        &self,
821        custom_cursor: CustomCursorSource,
822    ) -> Result<CoreCustomCursor, CustomCursorError> {
823        Ok(CoreCustomCursor(Arc::new(CustomCursor::new(self, custom_cursor)?)))
824    }
825
826    fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
827        Box::new(
828            self.xconn
829                .available_monitors()
830                .into_iter()
831                .flatten()
832                .map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
833        )
834    }
835
836    fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
837        self.xconn.primary_monitor().ok().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
838    }
839
840    fn system_theme(&self) -> Option<Theme> {
841        None
842    }
843
844    fn listen_device_events(&self, allowed: DeviceEvents) {
845        self.device_events.set(allowed);
846    }
847
848    fn set_control_flow(&self, control_flow: ControlFlow) {
849        self.control_flow.set(control_flow)
850    }
851
852    fn control_flow(&self) -> ControlFlow {
853        self.control_flow.get()
854    }
855
856    fn exit(&self) {
857        self.exit.set(Some(0))
858    }
859
860    fn exiting(&self) -> bool {
861        self.exit.get().is_some()
862    }
863
864    fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
865        CoreOwnedDisplayHandle::new(self.x_connection().clone())
866    }
867
868    fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
869        self
870    }
871
872    fn data_transfer(&self, id: DataTransferId) -> Result<Box<dyn DataTransfer>, TransferError> {
873        let data_transfer = self.data_transfer_state.borrow();
874
875        if let Some(state) = data_transfer.state() {
876            if state.transfer_id == id {
877                return Ok(Box::new(Selection::new(state.types.clone())));
878            }
879        }
880
881        if let Some(clipboard) = data_transfer.resolve_clipboard_type(id) {
882            return Ok(Box::new(
883                data_transfer.get_clipboard(clipboard).get_types(self.x_connection().atoms()),
884            ));
885        }
886
887        Err(TransferError::UnknownTransfer(id))
888    }
889
890    fn fetch_data_transfer(
891        &self,
892        id: DataTransferId,
893        type_: &dyn TransferType,
894    ) -> Result<AsyncRequestSerial, TransferError> {
895        let mut data_transfer = self.data_transfer_state.borrow_mut();
896
897        if let Some(state) = data_transfer.state() {
898            if state.transfer_id == id {
899                return data_transfer.fetch_dnd_data_transfer(type_);
900            }
901        }
902
903        if let Some(clipboard) = data_transfer.resolve_clipboard_type(id) {
904            let Some(clip_win) = self.clipboard_window() else {
905                return Err(TransferError::Failed);
906            };
907            data_transfer.fetch_clipboard_data_transfer(clipboard, type_, clip_win)
908        } else {
909            Err(TransferError::UnknownTransfer(id))
910        }
911    }
912
913    fn set_valid_dnd_actions(
914        &self,
915        id: DataTransferId,
916        actions: &[DndAction],
917    ) -> Result<(), TransferError> {
918        let mut data_transfer = self.data_transfer_state.borrow_mut();
919
920        let Some(state) = data_transfer.state_mut() else {
921            return Err(TransferError::UnknownTransfer(id));
922        };
923
924        if state.transfer_id != id {
925            return Err(TransferError::UnknownTransfer(id));
926        }
927
928        state.accepted = !actions.is_empty();
929
930        Ok(())
931    }
932
933    fn clipboard(&self) -> Result<Option<DataTransferId>, TransferError> {
934        self.read_clipboard(ClipboardSelectionType::Clipboard)
935    }
936
937    fn set_clipboard(&self, send_data: Box<dyn DataTransferSend>) -> Result<(), TransferError> {
938        self.write_clipboard(ClipboardSelectionType::Clipboard, send_data)
939    }
940
941    fn primary_selection_ext(&self) -> Option<&dyn PrimarySelectionExt> {
942        Some(self)
943    }
944}
945
946impl PrimarySelectionExt for ActiveEventLoop {
947    fn primary_selection(&self) -> Result<Option<DataTransferId>, TransferError> {
948        self.read_clipboard(ClipboardSelectionType::Primary)
949    }
950
951    fn set_primary_selection(
952        &self,
953        send_data: Box<dyn DataTransferSend>,
954    ) -> Result<(), TransferError> {
955        self.write_clipboard(ClipboardSelectionType::Primary, send_data)
956    }
957}
958
959impl rwh_06::HasDisplayHandle for ActiveEventLoop {
960    fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
961        self.xconn.display_handle()
962    }
963}
964
965pub(crate) struct DeviceInfo<'a> {
966    xconn: &'a XConnection,
967    info: *const ffi::XIDeviceInfo,
968    count: usize,
969}
970
971impl<'a> DeviceInfo<'a> {
972    pub(crate) fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> {
973        unsafe {
974            let mut count = 0;
975            let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
976            xconn.check_errors().ok()?;
977
978            if info.is_null() || count == 0 {
979                None
980            } else {
981                Some(DeviceInfo { xconn, info, count: count as usize })
982            }
983        }
984    }
985}
986
987impl Drop for DeviceInfo<'_> {
988    fn drop(&mut self) {
989        assert!(!self.info.is_null());
990        unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
991    }
992}
993
994impl Deref for DeviceInfo<'_> {
995    type Target = [ffi::XIDeviceInfo];
996
997    fn deref(&self) -> &Self::Target {
998        unsafe { slice::from_raw_parts(self.info, self.count) }
999    }
1000}
1001
1002#[derive(Clone, Debug)]
1003pub struct EventLoopProxy {
1004    ping: Ping,
1005}
1006
1007impl EventLoopProxyProvider for EventLoopProxy {
1008    fn wake_up(&self) {
1009        self.ping.ping();
1010    }
1011}
1012
1013impl EventLoopProxy {
1014    fn new(ping: Ping) -> Self {
1015        Self { ping }
1016    }
1017}
1018
1019impl From<EventLoopProxy> for CoreEventLoopProxy {
1020    fn from(value: EventLoopProxy) -> Self {
1021        CoreEventLoopProxy::new(Arc::new(value))
1022    }
1023}
1024
1025/// Generic sum error type for X11 errors.
1026#[derive(Debug)]
1027pub enum X11Error {
1028    /// An error from the Xlib library.
1029    Xlib(XError),
1030
1031    /// An error that occurred while trying to connect to the X server.
1032    Connect(ConnectError),
1033
1034    /// An error that occurred over the connection medium.
1035    Connection(ConnectionError),
1036
1037    /// An error that occurred logically on the X11 end.
1038    X11(LogicalError),
1039
1040    /// The XID range has been exhausted.
1041    XidsExhausted(IdsExhausted),
1042
1043    /// Got `null` from an Xlib function without a reason.
1044    UnexpectedNull(&'static str),
1045
1046    /// Got an invalid activation token.
1047    InvalidActivationToken(Vec<u8>),
1048
1049    /// An extension that we rely on is not available.
1050    MissingExtension(&'static str),
1051
1052    /// Could not find a matching X11 visual for this visualid
1053    NoSuchVisual(xproto::Visualid),
1054
1055    /// Unable to parse xsettings.
1056    XsettingsParse(xsettings::ParserError),
1057
1058    /// Failed to get property.
1059    GetProperty(util::GetPropertyError),
1060
1061    /// Could not find an ARGB32 pict format.
1062    NoArgb32Format,
1063}
1064
1065impl fmt::Display for X11Error {
1066    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1067        match self {
1068            X11Error::Xlib(e) => write!(f, "Xlib error: {e}"),
1069            X11Error::Connect(e) => write!(f, "X11 connection error: {e}"),
1070            X11Error::Connection(e) => write!(f, "X11 connection error: {e}"),
1071            X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {e}"),
1072            X11Error::GetProperty(e) => write!(f, "Failed to get X property {e}"),
1073            X11Error::X11(e) => write!(f, "X11 error: {e:?}"),
1074            X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {s}"),
1075            X11Error::InvalidActivationToken(s) => write!(
1076                f,
1077                "Invalid activation token: {}",
1078                std::str::from_utf8(s).unwrap_or("<invalid utf8>")
1079            ),
1080            X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {s}"),
1081            X11Error::NoSuchVisual(visualid) => {
1082                write!(f, "Could not find a matching X11 visual for ID `{visualid:x}`")
1083            },
1084            X11Error::XsettingsParse(err) => {
1085                write!(f, "Failed to parse xsettings: {err:?}")
1086            },
1087            X11Error::NoArgb32Format => {
1088                f.write_str("wingy only supports X11 displays with ARGB32 picture formats")
1089            },
1090        }
1091    }
1092}
1093
1094impl std::error::Error for X11Error {
1095    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1096        match self {
1097            X11Error::Xlib(e) => Some(e),
1098            X11Error::Connect(e) => Some(e),
1099            X11Error::Connection(e) => Some(e),
1100            X11Error::XidsExhausted(e) => Some(e),
1101            _ => None,
1102        }
1103    }
1104}
1105
1106impl From<XError> for X11Error {
1107    fn from(e: XError) -> Self {
1108        X11Error::Xlib(e)
1109    }
1110}
1111
1112impl From<ConnectError> for X11Error {
1113    fn from(e: ConnectError) -> Self {
1114        X11Error::Connect(e)
1115    }
1116}
1117
1118impl From<ConnectionError> for X11Error {
1119    fn from(e: ConnectionError) -> Self {
1120        X11Error::Connection(e)
1121    }
1122}
1123
1124impl From<LogicalError> for X11Error {
1125    fn from(e: LogicalError) -> Self {
1126        X11Error::X11(e)
1127    }
1128}
1129
1130impl From<ReplyError> for X11Error {
1131    fn from(value: ReplyError) -> Self {
1132        match value {
1133            ReplyError::ConnectionError(e) => e.into(),
1134            ReplyError::X11Error(e) => e.into(),
1135        }
1136    }
1137}
1138
1139impl From<ime::ImeContextCreationError> for X11Error {
1140    fn from(value: ime::ImeContextCreationError) -> Self {
1141        match value {
1142            ime::ImeContextCreationError::XError(e) => e.into(),
1143            ime::ImeContextCreationError::Null => Self::UnexpectedNull("XOpenIM"),
1144        }
1145    }
1146}
1147
1148impl From<ReplyOrIdError> for X11Error {
1149    fn from(value: ReplyOrIdError) -> Self {
1150        match value {
1151            ReplyOrIdError::ConnectionError(e) => e.into(),
1152            ReplyOrIdError::X11Error(e) => e.into(),
1153            ReplyOrIdError::IdsExhausted => Self::XidsExhausted(IdsExhausted),
1154        }
1155    }
1156}
1157
1158impl From<xsettings::ParserError> for X11Error {
1159    fn from(value: xsettings::ParserError) -> Self {
1160        Self::XsettingsParse(value)
1161    }
1162}
1163
1164impl From<util::GetPropertyError> for X11Error {
1165    fn from(value: util::GetPropertyError) -> Self {
1166        Self::GetProperty(value)
1167    }
1168}
1169
1170/// Type alias for a void cookie.
1171pub(crate) type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;
1172
1173/// Extension trait for `Result<VoidCookie, E>`.
1174pub(crate) trait CookieResultExt {
1175    /// Unwrap the send error and ignore the result.
1176    fn expect_then_ignore_error(self, msg: &str);
1177}
1178
1179impl<E: fmt::Debug> CookieResultExt for Result<VoidCookie<'_>, E> {
1180    fn expect_then_ignore_error(self, msg: &str) {
1181        self.expect(msg).ignore_error()
1182    }
1183}
1184
1185pub(crate) fn mkwid(w: xproto::Window) -> wingy_core::window::WindowId {
1186    wingy_core::window::WindowId::from_raw(w as _)
1187}
1188
1189pub(crate) fn mkdid(w: xinput::DeviceId) -> DeviceId {
1190    DeviceId::from_raw(w as i64)
1191}
1192
1193#[derive(Debug)]
1194pub struct Device {
1195    _name: String,
1196    pub(crate) scroll_axes: Vec<(i32, ScrollAxis)>,
1197    // For master devices, this is the paired device (pointer <-> keyboard).
1198    // For slave devices, this is the master.
1199    pub(crate) attachment: c_int,
1200    pub(crate) r#type: DeviceType,
1201}
1202
1203#[derive(Clone, Copy, Debug)]
1204pub(crate) enum DeviceType {
1205    Mouse,
1206    Touch,
1207    Pen,
1208    Eraser,
1209}
1210
1211#[derive(Debug, Copy, Clone)]
1212pub(crate) struct ScrollAxis {
1213    pub(crate) increment: f64,
1214    pub(crate) orientation: ScrollOrientation,
1215    pub(crate) position: f64,
1216}
1217
1218#[derive(Debug, Copy, Clone)]
1219pub(crate) enum ScrollOrientation {
1220    Vertical,
1221    Horizontal,
1222}
1223
1224impl Device {
1225    pub(crate) fn new(info: &ffi::XIDeviceInfo, atoms: &Atoms) -> Self {
1226        let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
1227        let mut scroll_axes = Vec::new();
1228        let mut r#type = None;
1229
1230        if Device::physical_device(info) {
1231            // Identify scroll axes
1232            for &class_ptr in Device::classes(info) {
1233                let ty = unsafe { (*class_ptr)._type };
1234                if ty == ffi::XIScrollClass {
1235                    let info = unsafe { &*(class_ptr as *const ffi::XIScrollClassInfo) };
1236                    scroll_axes.push((
1237                        info.number,
1238                        ScrollAxis {
1239                            increment: info.increment,
1240                            orientation: match info.scroll_type {
1241                                ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
1242                                ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
1243                                _ => unreachable!(),
1244                            },
1245                            position: 0.0,
1246                        },
1247                    ));
1248                } else if ty == ffi::XITouchClass {
1249                    r#type = Some(DeviceType::Touch);
1250                } else if r#type.is_none() && ty == ffi::XIValuatorClass {
1251                    let info = unsafe { &*(class_ptr as *const ffi::XIValuatorClassInfo) };
1252                    let atom = info.label as xproto::Atom;
1253
1254                    // Absolute X/Y axes alone do not identify a stylus:
1255                    // emulated pointing devices in virtual machines (the
1256                    // QEMU/VMware/VirtualBox USB tablets, and thus any
1257                    // desktop accessed through SPICE or similar viewers)
1258                    // expose Abs X/Y without pressure or tilt. Treating them
1259                    // as pens makes the mouse-only event filters drop all
1260                    // their motion and button input. Only pressure and tilt
1261                    // axes indicate actual stylus hardware.
1262                    if atom == atoms[ABS_PRESSURE]
1263                        || atom == atoms[ABS_TILT_X]
1264                        || atom == atoms[ABS_TILT_Y]
1265                    {
1266                        if name.contains("eraser") {
1267                            r#type = Some(DeviceType::Eraser);
1268                        } else {
1269                            r#type = Some(DeviceType::Pen);
1270                        }
1271                    }
1272                }
1273            }
1274        }
1275
1276        let mut device = Device {
1277            _name: name.into_owned(),
1278            scroll_axes,
1279            attachment: info.attachment,
1280            r#type: r#type.unwrap_or(DeviceType::Mouse),
1281        };
1282        device.reset_scroll_position(info);
1283        device
1284    }
1285
1286    pub(crate) fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) {
1287        if Device::physical_device(info) {
1288            for &class_ptr in Device::classes(info) {
1289                let ty = unsafe { (*class_ptr)._type };
1290                if ty == ffi::XIValuatorClass {
1291                    let info = unsafe { &*(class_ptr as *const ffi::XIValuatorClassInfo) };
1292                    if let Some(&mut (_, ref mut axis)) =
1293                        self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number)
1294                    {
1295                        axis.position = info.value;
1296                    }
1297                }
1298            }
1299        }
1300    }
1301
1302    #[inline]
1303    fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
1304        info._use == ffi::XISlaveKeyboard
1305            || info._use == ffi::XISlavePointer
1306            || info._use == ffi::XIFloatingSlave
1307    }
1308
1309    #[inline]
1310    fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
1311        unsafe {
1312            slice::from_raw_parts(
1313                info.classes as *const *const ffi::XIAnyClassInfo,
1314                info.num_classes as usize,
1315            )
1316        }
1317    }
1318}
1319
1320/// Convert the raw X11 representation for a 32-bit floating point to a double.
1321#[inline]
1322pub(crate) fn xinput_fp1616_to_float(fp: xinput::Fp1616) -> f64 {
1323    (fp as f64) / ((1 << 16) as f64)
1324}
1325
1326/// Returns the minimum `Option<Duration>`, taking into account that `None`
1327/// equates to an infinite timeout, not a zero timeout (so can't just use
1328/// `Option::min`)
1329fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
1330    a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
1331}