winit/platform_impl/linux/
mod.rs

1#![cfg(free_unix)]
2
3#[cfg(all(not(x11_platform), not(wayland_platform)))]
4compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
5
6use std::collections::VecDeque;
7use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
8use std::sync::Arc;
9use std::time::Duration;
10use std::{env, fmt};
11#[cfg(x11_platform)]
12use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
13
14#[cfg(x11_platform)]
15use crate::utils::Lazy;
16use smol_str::SmolStr;
17
18#[cfg(x11_platform)]
19use self::x11::{X11Error, XConnection, XError, XNotSupported};
20use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
21use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError};
22use crate::event_loop::{
23    ActiveEventLoop as RootELW, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
24};
25use crate::icon::Icon;
26use crate::keyboard::Key;
27use crate::platform::pump_events::PumpStatus;
28#[cfg(x11_platform)]
29use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
30use crate::window::{
31    ActivationToken, Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose,
32    ResizeDirection, Theme, UserAttentionType, WindowAttributes, WindowButtons, WindowLevel,
33};
34
35pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
36pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
37pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
38pub(crate) use crate::platform_impl::Fullscreen;
39
40pub(crate) mod common;
41#[cfg(wayland_platform)]
42pub(crate) mod wayland;
43#[cfg(x11_platform)]
44pub(crate) mod x11;
45
46#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
47pub(crate) enum Backend {
48    #[cfg(x11_platform)]
49    X,
50    #[cfg(wayland_platform)]
51    Wayland,
52}
53
54#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
55pub(crate) struct PlatformSpecificEventLoopAttributes {
56    pub(crate) forced_backend: Option<Backend>,
57    pub(crate) any_thread: bool,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct ApplicationName {
62    pub general: String,
63    pub instance: String,
64}
65
66impl ApplicationName {
67    pub fn new(general: String, instance: String) -> Self {
68        Self { general, instance }
69    }
70}
71
72#[derive(Clone, Debug)]
73pub struct PlatformSpecificWindowAttributes {
74    pub name: Option<ApplicationName>,
75    pub activation_token: Option<ActivationToken>,
76    #[cfg(x11_platform)]
77    pub x11: X11WindowAttributes,
78}
79
80#[derive(Clone, Debug)]
81#[cfg(x11_platform)]
82pub struct X11WindowAttributes {
83    pub visual_id: Option<x11rb::protocol::xproto::Visualid>,
84    pub screen_id: Option<i32>,
85    pub base_size: Option<Size>,
86    pub override_redirect: bool,
87    pub x11_window_types: Vec<XWindowType>,
88
89    /// The parent window to embed this window into.
90    pub embed_window: Option<x11rb::protocol::xproto::Window>,
91}
92
93#[cfg_attr(not(x11_platform), allow(clippy::derivable_impls))]
94impl Default for PlatformSpecificWindowAttributes {
95    fn default() -> Self {
96        Self {
97            name: None,
98            activation_token: None,
99            #[cfg(x11_platform)]
100            x11: X11WindowAttributes {
101                visual_id: None,
102                screen_id: None,
103                base_size: None,
104                override_redirect: false,
105                x11_window_types: vec![XWindowType::Normal],
106                embed_window: None,
107            },
108        }
109    }
110}
111
112#[cfg(x11_platform)]
113pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> =
114    Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)));
115
116#[derive(Debug, Clone)]
117pub enum OsError {
118    Misc(&'static str),
119    #[cfg(x11_platform)]
120    XNotSupported(XNotSupported),
121    #[cfg(x11_platform)]
122    XError(Arc<X11Error>),
123    #[cfg(wayland_platform)]
124    WaylandError(Arc<wayland::WaylandError>),
125}
126
127impl fmt::Display for OsError {
128    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
129        match *self {
130            OsError::Misc(e) => _f.pad(e),
131            #[cfg(x11_platform)]
132            OsError::XNotSupported(ref e) => fmt::Display::fmt(e, _f),
133            #[cfg(x11_platform)]
134            OsError::XError(ref e) => fmt::Display::fmt(e, _f),
135            #[cfg(wayland_platform)]
136            OsError::WaylandError(ref e) => fmt::Display::fmt(e, _f),
137        }
138    }
139}
140
141pub(crate) enum Window {
142    #[cfg(x11_platform)]
143    X(x11::Window),
144    #[cfg(wayland_platform)]
145    Wayland(wayland::Window),
146}
147
148#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
149pub struct WindowId(u64);
150
151impl From<WindowId> for u64 {
152    fn from(window_id: WindowId) -> Self {
153        window_id.0
154    }
155}
156
157impl From<u64> for WindowId {
158    fn from(raw_id: u64) -> Self {
159        Self(raw_id)
160    }
161}
162
163impl WindowId {
164    pub const fn dummy() -> Self {
165        Self(0)
166    }
167}
168
169#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub enum DeviceId {
171    #[cfg(x11_platform)]
172    X(x11::DeviceId),
173    #[cfg(wayland_platform)]
174    Wayland(wayland::DeviceId),
175}
176
177impl DeviceId {
178    pub const fn dummy() -> Self {
179        #[cfg(wayland_platform)]
180        return DeviceId::Wayland(wayland::DeviceId::dummy());
181        #[cfg(all(not(wayland_platform), x11_platform))]
182        return DeviceId::X(x11::DeviceId::dummy());
183    }
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
187pub enum MonitorHandle {
188    #[cfg(x11_platform)]
189    X(x11::MonitorHandle),
190    #[cfg(wayland_platform)]
191    Wayland(wayland::MonitorHandle),
192}
193
194/// `x11_or_wayland!(match expr; Enum(foo) => foo.something())`
195/// expands to the equivalent of
196/// ```ignore
197/// match self {
198///    Enum::X(foo) => foo.something(),
199///    Enum::Wayland(foo) => foo.something(),
200/// }
201/// ```
202/// The result can be converted to another enum by adding `; as AnotherEnum`
203macro_rules! x11_or_wayland {
204    (match $what:expr; $enum:ident ( $($c1:tt)* ) => $x:expr; as $enum2:ident ) => {
205        match $what {
206            #[cfg(x11_platform)]
207            $enum::X($($c1)*) => $enum2::X($x),
208            #[cfg(wayland_platform)]
209            $enum::Wayland($($c1)*) => $enum2::Wayland($x),
210        }
211    };
212    (match $what:expr; $enum:ident ( $($c1:tt)* ) => $x:expr) => {
213        match $what {
214            #[cfg(x11_platform)]
215            $enum::X($($c1)*) => $x,
216            #[cfg(wayland_platform)]
217            $enum::Wayland($($c1)*) => $x,
218        }
219    };
220}
221
222impl MonitorHandle {
223    #[inline]
224    pub fn name(&self) -> Option<String> {
225        x11_or_wayland!(match self; MonitorHandle(m) => m.name())
226    }
227
228    #[inline]
229    pub fn native_identifier(&self) -> u32 {
230        x11_or_wayland!(match self; MonitorHandle(m) => m.native_identifier())
231    }
232
233    #[inline]
234    pub fn size(&self) -> PhysicalSize<u32> {
235        x11_or_wayland!(match self; MonitorHandle(m) => m.size())
236    }
237
238    #[inline]
239    pub fn position(&self) -> PhysicalPosition<i32> {
240        x11_or_wayland!(match self; MonitorHandle(m) => m.position())
241    }
242
243    #[inline]
244    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
245        x11_or_wayland!(match self; MonitorHandle(m) => m.refresh_rate_millihertz())
246    }
247
248    #[inline]
249    pub fn scale_factor(&self) -> f64 {
250        x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as _)
251    }
252
253    #[inline]
254    pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> {
255        x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes()))
256    }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq, Hash)]
260pub enum VideoModeHandle {
261    #[cfg(x11_platform)]
262    X(x11::VideoModeHandle),
263    #[cfg(wayland_platform)]
264    Wayland(wayland::VideoModeHandle),
265}
266
267impl VideoModeHandle {
268    #[inline]
269    pub fn size(&self) -> PhysicalSize<u32> {
270        x11_or_wayland!(match self; VideoModeHandle(m) => m.size())
271    }
272
273    #[inline]
274    pub fn bit_depth(&self) -> u16 {
275        x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth())
276    }
277
278    #[inline]
279    pub fn refresh_rate_millihertz(&self) -> u32 {
280        x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz())
281    }
282
283    #[inline]
284    pub fn monitor(&self) -> MonitorHandle {
285        x11_or_wayland!(match self; VideoModeHandle(m) => m.monitor(); as MonitorHandle)
286    }
287}
288
289impl Window {
290    #[inline]
291    pub(crate) fn new(
292        window_target: &ActiveEventLoop,
293        attribs: WindowAttributes,
294    ) -> Result<Self, RootOsError> {
295        match *window_target {
296            #[cfg(wayland_platform)]
297            ActiveEventLoop::Wayland(ref window_target) => {
298                wayland::Window::new(window_target, attribs).map(Window::Wayland)
299            },
300            #[cfg(x11_platform)]
301            ActiveEventLoop::X(ref window_target) => {
302                x11::Window::new(window_target, attribs).map(Window::X)
303            },
304        }
305    }
306
307    pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Self) + Send + 'static) {
308        f(self)
309    }
310
311    pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Self) -> R + Send) -> R {
312        f(self)
313    }
314
315    #[inline]
316    pub fn id(&self) -> WindowId {
317        x11_or_wayland!(match self; Window(w) => w.id())
318    }
319
320    #[inline]
321    pub fn set_title(&self, title: &str) {
322        x11_or_wayland!(match self; Window(w) => w.set_title(title));
323    }
324
325    #[inline]
326    pub fn set_transparent(&self, transparent: bool) {
327        x11_or_wayland!(match self; Window(w) => w.set_transparent(transparent));
328    }
329
330    #[inline]
331    pub fn set_blur(&self, blur: bool) {
332        x11_or_wayland!(match self; Window(w) => w.set_blur(blur));
333    }
334
335    #[inline]
336    pub fn set_visible(&self, visible: bool) {
337        x11_or_wayland!(match self; Window(w) => w.set_visible(visible))
338    }
339
340    #[inline]
341    pub fn is_visible(&self) -> Option<bool> {
342        x11_or_wayland!(match self; Window(w) => w.is_visible())
343    }
344
345    #[inline]
346    pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
347        x11_or_wayland!(match self; Window(w) => w.outer_position())
348    }
349
350    #[inline]
351    pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
352        x11_or_wayland!(match self; Window(w) => w.inner_position())
353    }
354
355    #[inline]
356    pub fn set_outer_position(&self, position: Position) {
357        x11_or_wayland!(match self; Window(w) => w.set_outer_position(position))
358    }
359
360    #[inline]
361    pub fn inner_size(&self) -> PhysicalSize<u32> {
362        x11_or_wayland!(match self; Window(w) => w.inner_size())
363    }
364
365    #[inline]
366    pub fn outer_size(&self) -> PhysicalSize<u32> {
367        x11_or_wayland!(match self; Window(w) => w.outer_size())
368    }
369
370    #[inline]
371    pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
372        x11_or_wayland!(match self; Window(w) => w.request_inner_size(size))
373    }
374
375    #[inline]
376    pub(crate) fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
377        x11_or_wayland!(match self; Window(w) => w.request_activation_token())
378    }
379
380    #[inline]
381    pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
382        x11_or_wayland!(match self; Window(w) => w.set_min_inner_size(dimensions))
383    }
384
385    #[inline]
386    pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
387        x11_or_wayland!(match self; Window(w) => w.set_max_inner_size(dimensions))
388    }
389
390    #[inline]
391    pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
392        x11_or_wayland!(match self; Window(w) => w.resize_increments())
393    }
394
395    #[inline]
396    pub fn set_resize_increments(&self, increments: Option<Size>) {
397        x11_or_wayland!(match self; Window(w) => w.set_resize_increments(increments))
398    }
399
400    #[inline]
401    pub fn set_resizable(&self, resizable: bool) {
402        x11_or_wayland!(match self; Window(w) => w.set_resizable(resizable))
403    }
404
405    #[inline]
406    pub fn is_resizable(&self) -> bool {
407        x11_or_wayland!(match self; Window(w) => w.is_resizable())
408    }
409
410    #[inline]
411    pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
412        x11_or_wayland!(match self; Window(w) => w.set_enabled_buttons(buttons))
413    }
414
415    #[inline]
416    pub fn enabled_buttons(&self) -> WindowButtons {
417        x11_or_wayland!(match self; Window(w) => w.enabled_buttons())
418    }
419
420    #[inline]
421    pub fn set_cursor(&self, cursor: Cursor) {
422        x11_or_wayland!(match self; Window(w) => w.set_cursor(cursor))
423    }
424
425    #[inline]
426    pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
427        x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(mode))
428    }
429
430    #[inline]
431    pub fn set_cursor_visible(&self, visible: bool) {
432        x11_or_wayland!(match self; Window(window) => window.set_cursor_visible(visible))
433    }
434
435    #[inline]
436    pub fn drag_window(&self) -> Result<(), ExternalError> {
437        x11_or_wayland!(match self; Window(window) => window.drag_window())
438    }
439
440    #[inline]
441    pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
442        x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
443    }
444
445    #[inline]
446    pub fn show_window_menu(&self, position: Position) {
447        x11_or_wayland!(match self; Window(w) => w.show_window_menu(position))
448    }
449
450    #[inline]
451    pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
452        x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))
453    }
454
455    #[inline]
456    pub fn scale_factor(&self) -> f64 {
457        x11_or_wayland!(match self; Window(w) => w.scale_factor())
458    }
459
460    #[inline]
461    pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
462        x11_or_wayland!(match self; Window(w) => w.set_cursor_position(position))
463    }
464
465    #[inline]
466    pub fn set_maximized(&self, maximized: bool) {
467        x11_or_wayland!(match self; Window(w) => w.set_maximized(maximized))
468    }
469
470    #[inline]
471    pub fn is_maximized(&self) -> bool {
472        x11_or_wayland!(match self; Window(w) => w.is_maximized())
473    }
474
475    #[inline]
476    pub fn set_minimized(&self, minimized: bool) {
477        x11_or_wayland!(match self; Window(w) => w.set_minimized(minimized))
478    }
479
480    #[inline]
481    pub fn is_minimized(&self) -> Option<bool> {
482        x11_or_wayland!(match self; Window(w) => w.is_minimized())
483    }
484
485    #[inline]
486    pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
487        x11_or_wayland!(match self; Window(w) => w.fullscreen())
488    }
489
490    #[inline]
491    pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
492        x11_or_wayland!(match self; Window(w) => w.set_fullscreen(monitor))
493    }
494
495    #[inline]
496    pub fn set_decorations(&self, decorations: bool) {
497        x11_or_wayland!(match self; Window(w) => w.set_decorations(decorations))
498    }
499
500    #[inline]
501    pub fn is_decorated(&self) -> bool {
502        x11_or_wayland!(match self; Window(w) => w.is_decorated())
503    }
504
505    #[inline]
506    pub fn set_window_level(&self, level: WindowLevel) {
507        x11_or_wayland!(match self; Window(w) => w.set_window_level(level))
508    }
509
510    #[inline]
511    pub fn set_window_icon(&self, window_icon: Option<Icon>) {
512        x11_or_wayland!(match self; Window(w) => w.set_window_icon(window_icon.map(|icon| icon.inner)))
513    }
514
515    #[inline]
516    pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
517        x11_or_wayland!(match self; Window(w) => w.set_ime_cursor_area(position, size))
518    }
519
520    #[inline]
521    pub fn reset_dead_keys(&self) {
522        common::xkb::reset_dead_keys()
523    }
524
525    #[inline]
526    pub fn set_ime_allowed(&self, allowed: bool) {
527        x11_or_wayland!(match self; Window(w) => w.set_ime_allowed(allowed))
528    }
529
530    #[inline]
531    pub fn set_ime_purpose(&self, purpose: ImePurpose) {
532        x11_or_wayland!(match self; Window(w) => w.set_ime_purpose(purpose))
533    }
534
535    #[inline]
536    pub fn focus_window(&self) {
537        x11_or_wayland!(match self; Window(w) => w.focus_window())
538    }
539
540    pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
541        x11_or_wayland!(match self; Window(w) => w.request_user_attention(request_type))
542    }
543
544    #[inline]
545    pub fn request_redraw(&self) {
546        x11_or_wayland!(match self; Window(w) => w.request_redraw())
547    }
548
549    #[inline]
550    pub fn pre_present_notify(&self) {
551        x11_or_wayland!(match self; Window(w) => w.pre_present_notify())
552    }
553
554    #[inline]
555    pub fn current_monitor(&self) -> Option<MonitorHandle> {
556        Some(x11_or_wayland!(match self; Window(w) => w.current_monitor()?; as MonitorHandle))
557    }
558
559    #[inline]
560    pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
561        match self {
562            #[cfg(x11_platform)]
563            Window::X(ref window) => {
564                window.available_monitors().into_iter().map(MonitorHandle::X).collect()
565            },
566            #[cfg(wayland_platform)]
567            Window::Wayland(ref window) => {
568                window.available_monitors().into_iter().map(MonitorHandle::Wayland).collect()
569            },
570        }
571    }
572
573    #[inline]
574    pub fn primary_monitor(&self) -> Option<MonitorHandle> {
575        Some(x11_or_wayland!(match self; Window(w) => w.primary_monitor()?; as MonitorHandle))
576    }
577
578    #[cfg(feature = "rwh_04")]
579    #[inline]
580    pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
581        x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_04())
582    }
583
584    #[cfg(feature = "rwh_05")]
585    #[inline]
586    pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
587        x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_05())
588    }
589
590    #[cfg(feature = "rwh_05")]
591    #[inline]
592    pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
593        x11_or_wayland!(match self; Window(window) => window.raw_display_handle_rwh_05())
594    }
595
596    #[cfg(feature = "rwh_06")]
597    #[inline]
598    pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
599        x11_or_wayland!(match self; Window(window) => window.raw_window_handle_rwh_06())
600    }
601
602    #[cfg(feature = "rwh_06")]
603    #[inline]
604    pub fn raw_display_handle_rwh_06(
605        &self,
606    ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
607        x11_or_wayland!(match self; Window(window) => window.raw_display_handle_rwh_06())
608    }
609
610    #[inline]
611    pub fn set_theme(&self, theme: Option<Theme>) {
612        x11_or_wayland!(match self; Window(window) => window.set_theme(theme))
613    }
614
615    #[inline]
616    pub fn theme(&self) -> Option<Theme> {
617        x11_or_wayland!(match self; Window(window) => window.theme())
618    }
619
620    pub fn set_content_protected(&self, protected: bool) {
621        x11_or_wayland!(match self; Window(window) => window.set_content_protected(protected))
622    }
623
624    #[inline]
625    pub fn has_focus(&self) -> bool {
626        x11_or_wayland!(match self; Window(window) => window.has_focus())
627    }
628
629    pub fn title(&self) -> String {
630        x11_or_wayland!(match self; Window(window) => window.title())
631    }
632}
633
634#[derive(Debug, Clone, Eq, PartialEq, Hash)]
635pub struct KeyEventExtra {
636    pub text_with_all_modifiers: Option<SmolStr>,
637    pub key_without_modifiers: Key,
638}
639
640#[derive(Clone, Debug, Eq, Hash, PartialEq)]
641pub(crate) enum PlatformCustomCursor {
642    #[cfg(wayland_platform)]
643    Wayland(wayland::CustomCursor),
644    #[cfg(x11_platform)]
645    X(x11::CustomCursor),
646}
647
648/// Hooks for X11 errors.
649#[cfg(x11_platform)]
650pub(crate) static XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new());
651
652#[cfg(x11_platform)]
653unsafe extern "C" fn x_error_callback(
654    display: *mut x11::ffi::Display,
655    event: *mut x11::ffi::XErrorEvent,
656) -> c_int {
657    let xconn_lock = X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner());
658    if let Ok(ref xconn) = *xconn_lock {
659        // Call all the hooks.
660        let mut error_handled = false;
661        for hook in XLIB_ERROR_HOOKS.lock().unwrap().iter() {
662            error_handled |= hook(display as *mut _, event as *mut _);
663        }
664
665        // `assume_init` is safe here because the array consists of `MaybeUninit` values,
666        // which do not require initialization.
667        let mut buf: [MaybeUninit<c_char>; 1024] = unsafe { MaybeUninit::uninit().assume_init() };
668        unsafe {
669            (xconn.xlib.XGetErrorText)(
670                display,
671                (*event).error_code as c_int,
672                buf.as_mut_ptr() as *mut c_char,
673                buf.len() as c_int,
674            )
675        };
676        let description =
677            unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) }.to_string_lossy();
678
679        let error = unsafe {
680            XError {
681                description: description.into_owned(),
682                error_code: (*event).error_code,
683                request_code: (*event).request_code,
684                minor_code: (*event).minor_code,
685            }
686        };
687
688        // Don't log error.
689        if !error_handled {
690            tracing::error!("X11 error: {:#?}", error);
691            // XXX only update the error, if it wasn't handled by any of the hooks.
692            *xconn.latest_error.lock().unwrap() = Some(error);
693        }
694    }
695    // Fun fact: this return value is completely ignored.
696    0
697}
698
699pub enum EventLoop<T: 'static> {
700    #[cfg(wayland_platform)]
701    Wayland(Box<wayland::EventLoop<T>>),
702    #[cfg(x11_platform)]
703    X(x11::EventLoop<T>),
704}
705
706pub enum EventLoopProxy<T: 'static> {
707    #[cfg(x11_platform)]
708    X(x11::EventLoopProxy<T>),
709    #[cfg(wayland_platform)]
710    Wayland(wayland::EventLoopProxy<T>),
711}
712
713impl<T: 'static> Clone for EventLoopProxy<T> {
714    fn clone(&self) -> Self {
715        x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.clone(); as EventLoopProxy)
716    }
717}
718
719impl<T: 'static> EventLoop<T> {
720    pub(crate) fn new(
721        attributes: &PlatformSpecificEventLoopAttributes,
722    ) -> Result<Self, EventLoopError> {
723        if !attributes.any_thread && !is_main_thread() {
724            panic!(
725                "Initializing the event loop outside of the main thread is a significant \
726                 cross-platform compatibility hazard. If you absolutely need to create an \
727                 EventLoop on a different thread, you can use the \
728                 `EventLoopBuilderExtX11::any_thread` or `EventLoopBuilderExtWayland::any_thread` \
729                 functions."
730            );
731        }
732
733        // NOTE: Wayland first because of X11 could be present under Wayland as well. Empty
734        // variables are also treated as not set.
735        let backend = match (
736            attributes.forced_backend,
737            env::var("WAYLAND_DISPLAY")
738                .ok()
739                .filter(|var| !var.is_empty())
740                .or_else(|| env::var("WAYLAND_SOCKET").ok())
741                .filter(|var| !var.is_empty())
742                .is_some(),
743            env::var("DISPLAY").map(|var| !var.is_empty()).unwrap_or(false),
744        ) {
745            // User is forcing a backend.
746            (Some(backend), ..) => backend,
747            // Wayland is present.
748            #[cfg(wayland_platform)]
749            (None, true, _) => Backend::Wayland,
750            // X11 is present.
751            #[cfg(x11_platform)]
752            (None, _, true) => Backend::X,
753            // No backend is present.
754            (_, wayland_display, x11_display) => {
755                let msg = if wayland_display && !cfg!(wayland_platform) {
756                    "DISPLAY is not set; note: enable the `winit/wayland` feature to support \
757                     Wayland"
758                } else if x11_display && !cfg!(x11_platform) {
759                    "neither WAYLAND_DISPLAY nor WAYLAND_SOCKET is set; note: enable the \
760                     `winit/x11` feature to support X11"
761                } else {
762                    "neither WAYLAND_DISPLAY nor WAYLAND_SOCKET nor DISPLAY is set."
763                };
764                return Err(EventLoopError::Os(os_error!(OsError::Misc(msg))));
765            },
766        };
767
768        // Create the display based on the backend.
769        match backend {
770            #[cfg(wayland_platform)]
771            Backend::Wayland => EventLoop::new_wayland_any_thread().map_err(Into::into),
772            #[cfg(x11_platform)]
773            Backend::X => EventLoop::new_x11_any_thread().map_err(Into::into),
774        }
775    }
776
777    #[cfg(wayland_platform)]
778    fn new_wayland_any_thread() -> Result<EventLoop<T>, EventLoopError> {
779        wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
780    }
781
782    #[cfg(x11_platform)]
783    fn new_x11_any_thread() -> Result<EventLoop<T>, EventLoopError> {
784        let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
785            Ok(xconn) => xconn.clone(),
786            Err(err) => {
787                return Err(EventLoopError::Os(os_error!(OsError::XNotSupported(err.clone()))))
788            },
789        };
790
791        Ok(EventLoop::X(x11::EventLoop::new(xconn)))
792    }
793
794    #[inline]
795    pub fn is_wayland(&self) -> bool {
796        match *self {
797            #[cfg(wayland_platform)]
798            EventLoop::Wayland(_) => true,
799            #[cfg(x11_platform)]
800            _ => false,
801        }
802    }
803
804    pub fn create_proxy(&self) -> EventLoopProxy<T> {
805        x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
806    }
807
808    pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
809    where
810        F: FnMut(crate::event::Event<T>, &RootELW),
811    {
812        self.run_on_demand(callback)
813    }
814
815    pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
816    where
817        F: FnMut(crate::event::Event<T>, &RootELW),
818    {
819        x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback))
820    }
821
822    pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
823    where
824        F: FnMut(crate::event::Event<T>, &RootELW),
825    {
826        x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
827    }
828
829    pub fn window_target(&self) -> &crate::event_loop::ActiveEventLoop {
830        x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
831    }
832}
833
834impl<T> AsFd for EventLoop<T> {
835    fn as_fd(&self) -> BorrowedFd<'_> {
836        x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_fd())
837    }
838}
839
840impl<T> AsRawFd for EventLoop<T> {
841    fn as_raw_fd(&self) -> RawFd {
842        x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_raw_fd())
843    }
844}
845
846impl<T: 'static> EventLoopProxy<T> {
847    pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
848        x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event))
849    }
850}
851
852pub enum ActiveEventLoop {
853    #[cfg(wayland_platform)]
854    Wayland(wayland::ActiveEventLoop),
855    #[cfg(x11_platform)]
856    X(x11::ActiveEventLoop),
857}
858
859impl ActiveEventLoop {
860    #[inline]
861    pub fn is_wayland(&self) -> bool {
862        match *self {
863            #[cfg(wayland_platform)]
864            ActiveEventLoop::Wayland(_) => true,
865            #[cfg(x11_platform)]
866            _ => false,
867        }
868    }
869
870    pub fn create_custom_cursor(&self, cursor: CustomCursorSource) -> CustomCursor {
871        x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.create_custom_cursor(cursor))
872    }
873
874    #[inline]
875    pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
876        match *self {
877            #[cfg(wayland_platform)]
878            ActiveEventLoop::Wayland(ref evlp) => {
879                evlp.available_monitors().map(MonitorHandle::Wayland).collect()
880            },
881            #[cfg(x11_platform)]
882            ActiveEventLoop::X(ref evlp) => {
883                evlp.available_monitors().map(MonitorHandle::X).collect()
884            },
885        }
886    }
887
888    #[inline]
889    pub fn primary_monitor(&self) -> Option<MonitorHandle> {
890        Some(
891            x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.primary_monitor()?; as MonitorHandle),
892        )
893    }
894
895    #[inline]
896    pub fn listen_device_events(&self, allowed: DeviceEvents) {
897        x11_or_wayland!(match self; Self(evlp) => evlp.listen_device_events(allowed))
898    }
899
900    #[cfg(feature = "rwh_05")]
901    #[inline]
902    pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
903        x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_05())
904    }
905
906    #[inline]
907    pub fn system_theme(&self) -> Option<Theme> {
908        None
909    }
910
911    #[cfg(feature = "rwh_06")]
912    #[inline]
913    pub fn raw_display_handle_rwh_06(
914        &self,
915    ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
916        x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_06())
917    }
918
919    pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
920        x11_or_wayland!(match self; Self(evlp) => evlp.set_control_flow(control_flow))
921    }
922
923    pub(crate) fn control_flow(&self) -> ControlFlow {
924        x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
925    }
926
927    pub(crate) fn clear_exit(&self) {
928        x11_or_wayland!(match self; Self(evlp) => evlp.clear_exit())
929    }
930
931    pub(crate) fn exit(&self) {
932        x11_or_wayland!(match self; Self(evlp) => evlp.exit())
933    }
934
935    pub(crate) fn exiting(&self) -> bool {
936        x11_or_wayland!(match self; Self(evlp) => evlp.exiting())
937    }
938
939    pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
940        match self {
941            #[cfg(x11_platform)]
942            Self::X(conn) => OwnedDisplayHandle::X(conn.x_connection().clone()),
943            #[cfg(wayland_platform)]
944            Self::Wayland(conn) => OwnedDisplayHandle::Wayland(conn.connection.clone()),
945        }
946    }
947
948    #[allow(dead_code)]
949    fn set_exit_code(&self, code: i32) {
950        x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
951    }
952
953    #[allow(dead_code)]
954    fn exit_code(&self) -> Option<i32> {
955        x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
956    }
957}
958
959#[derive(Clone)]
960#[allow(dead_code)]
961pub(crate) enum OwnedDisplayHandle {
962    #[cfg(x11_platform)]
963    X(Arc<XConnection>),
964    #[cfg(wayland_platform)]
965    Wayland(wayland_client::Connection),
966}
967
968impl OwnedDisplayHandle {
969    #[cfg(feature = "rwh_05")]
970    #[inline]
971    pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
972        match self {
973            #[cfg(x11_platform)]
974            Self::X(xconn) => {
975                let mut xlib_handle = rwh_05::XlibDisplayHandle::empty();
976                xlib_handle.display = xconn.display.cast();
977                xlib_handle.screen = xconn.default_screen_index() as _;
978                xlib_handle.into()
979            },
980
981            #[cfg(wayland_platform)]
982            Self::Wayland(conn) => {
983                use sctk::reexports::client::Proxy;
984
985                let mut wayland_handle = rwh_05::WaylandDisplayHandle::empty();
986                wayland_handle.display = conn.display().id().as_ptr() as *mut _;
987                wayland_handle.into()
988            },
989        }
990    }
991
992    #[cfg(feature = "rwh_06")]
993    #[inline]
994    pub fn raw_display_handle_rwh_06(
995        &self,
996    ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
997        use std::ptr::NonNull;
998
999        match self {
1000            #[cfg(x11_platform)]
1001            Self::X(xconn) => Ok(rwh_06::XlibDisplayHandle::new(
1002                NonNull::new(xconn.display.cast()),
1003                xconn.default_screen_index() as _,
1004            )
1005            .into()),
1006
1007            #[cfg(wayland_platform)]
1008            Self::Wayland(conn) => {
1009                use sctk::reexports::client::Proxy;
1010
1011                Ok(rwh_06::WaylandDisplayHandle::new(
1012                    NonNull::new(conn.display().id().as_ptr().cast()).unwrap(),
1013                )
1014                .into())
1015            },
1016        }
1017    }
1018}
1019
1020/// Returns the minimum `Option<Duration>`, taking into account that `None`
1021/// equates to an infinite timeout, not a zero timeout (so can't just use
1022/// `Option::min`)
1023fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
1024    a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
1025}
1026
1027#[cfg(target_os = "linux")]
1028fn is_main_thread() -> bool {
1029    rustix::thread::gettid() == rustix::process::getpid()
1030}
1031
1032#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
1033fn is_main_thread() -> bool {
1034    use libc::pthread_main_np;
1035
1036    unsafe { pthread_main_np() == 1 }
1037}
1038
1039#[cfg(target_os = "netbsd")]
1040fn is_main_thread() -> bool {
1041    std::thread::current().name() == Some("main")
1042}