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