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 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
194macro_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#[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 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 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 if !error_handled {
690 tracing::error!("X11 error: {:#?}", error);
691 *xconn.latest_error.lock().unwrap() = Some(error);
693 }
694 }
695 0
697}
698
699#[allow(clippy::large_enum_variant)]
700pub enum EventLoop<T: 'static> {
701 #[cfg(wayland_platform)]
702 Wayland(Box<wayland::EventLoop<T>>),
703 #[cfg(x11_platform)]
704 X(x11::EventLoop<T>),
705}
706
707pub enum EventLoopProxy<T: 'static> {
708 #[cfg(x11_platform)]
709 X(x11::EventLoopProxy<T>),
710 #[cfg(wayland_platform)]
711 Wayland(wayland::EventLoopProxy<T>),
712}
713
714impl<T: 'static> Clone for EventLoopProxy<T> {
715 fn clone(&self) -> Self {
716 x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.clone(); as EventLoopProxy)
717 }
718}
719
720impl<T: 'static> EventLoop<T> {
721 pub(crate) fn new(
722 attributes: &PlatformSpecificEventLoopAttributes,
723 ) -> Result<Self, EventLoopError> {
724 if !attributes.any_thread && !is_main_thread() {
725 panic!(
726 "Initializing the event loop outside of the main thread is a significant \
727 cross-platform compatibility hazard. If you absolutely need to create an \
728 EventLoop on a different thread, you can use the \
729 `EventLoopBuilderExtX11::any_thread` or `EventLoopBuilderExtWayland::any_thread` \
730 functions."
731 );
732 }
733
734 let backend = match (
737 attributes.forced_backend,
738 env::var("WAYLAND_DISPLAY")
739 .ok()
740 .filter(|var| !var.is_empty())
741 .or_else(|| env::var("WAYLAND_SOCKET").ok())
742 .filter(|var| !var.is_empty())
743 .is_some(),
744 env::var("DISPLAY").map(|var| !var.is_empty()).unwrap_or(false),
745 ) {
746 (Some(backend), ..) => backend,
748 #[cfg(wayland_platform)]
750 (None, true, _) => Backend::Wayland,
751 #[cfg(x11_platform)]
753 (None, _, true) => Backend::X,
754 (_, wayland_display, x11_display) => {
756 let msg = if wayland_display && !cfg!(wayland_platform) {
757 "DISPLAY is not set; note: enable the `winit/wayland` feature to support \
758 Wayland"
759 } else if x11_display && !cfg!(x11_platform) {
760 "neither WAYLAND_DISPLAY nor WAYLAND_SOCKET is set; note: enable the \
761 `winit/x11` feature to support X11"
762 } else {
763 "neither WAYLAND_DISPLAY nor WAYLAND_SOCKET nor DISPLAY is set."
764 };
765 return Err(EventLoopError::Os(os_error!(OsError::Misc(msg))));
766 },
767 };
768
769 match backend {
771 #[cfg(wayland_platform)]
772 Backend::Wayland => EventLoop::new_wayland_any_thread(),
773 #[cfg(x11_platform)]
774 Backend::X => EventLoop::new_x11_any_thread(),
775 }
776 }
777
778 #[cfg(wayland_platform)]
779 fn new_wayland_any_thread() -> Result<EventLoop<T>, EventLoopError> {
780 wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
781 }
782
783 #[cfg(x11_platform)]
784 fn new_x11_any_thread() -> Result<EventLoop<T>, EventLoopError> {
785 let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
786 Ok(xconn) => xconn.clone(),
787 Err(err) => {
788 return Err(EventLoopError::Os(os_error!(OsError::XNotSupported(err.clone()))))
789 },
790 };
791
792 Ok(EventLoop::X(x11::EventLoop::new(xconn)))
793 }
794
795 #[inline]
796 pub fn is_wayland(&self) -> bool {
797 match *self {
798 #[cfg(wayland_platform)]
799 EventLoop::Wayland(_) => true,
800 #[cfg(x11_platform)]
801 _ => false,
802 }
803 }
804
805 pub fn create_proxy(&self) -> EventLoopProxy<T> {
806 x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
807 }
808
809 pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
810 where
811 F: FnMut(crate::event::Event<T>, &RootELW),
812 {
813 self.run_on_demand(callback)
814 }
815
816 pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
817 where
818 F: FnMut(crate::event::Event<T>, &RootELW),
819 {
820 x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback))
821 }
822
823 pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
824 where
825 F: FnMut(crate::event::Event<T>, &RootELW),
826 {
827 x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
828 }
829
830 pub fn window_target(&self) -> &crate::event_loop::ActiveEventLoop {
831 x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
832 }
833}
834
835impl<T> AsFd for EventLoop<T> {
836 fn as_fd(&self) -> BorrowedFd<'_> {
837 x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_fd())
838 }
839}
840
841impl<T> AsRawFd for EventLoop<T> {
842 fn as_raw_fd(&self) -> RawFd {
843 x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_raw_fd())
844 }
845}
846
847impl<T: 'static> EventLoopProxy<T> {
848 pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
849 x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event))
850 }
851}
852
853#[allow(clippy::large_enum_variant)]
854pub enum ActiveEventLoop {
855 #[cfg(wayland_platform)]
856 Wayland(wayland::ActiveEventLoop),
857 #[cfg(x11_platform)]
858 X(x11::ActiveEventLoop),
859}
860
861impl ActiveEventLoop {
862 #[inline]
863 pub fn is_wayland(&self) -> bool {
864 match *self {
865 #[cfg(wayland_platform)]
866 ActiveEventLoop::Wayland(_) => true,
867 #[cfg(x11_platform)]
868 _ => false,
869 }
870 }
871
872 pub fn create_custom_cursor(&self, cursor: CustomCursorSource) -> CustomCursor {
873 x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.create_custom_cursor(cursor))
874 }
875
876 #[inline]
877 pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
878 match *self {
879 #[cfg(wayland_platform)]
880 ActiveEventLoop::Wayland(ref evlp) => {
881 evlp.available_monitors().map(MonitorHandle::Wayland).collect()
882 },
883 #[cfg(x11_platform)]
884 ActiveEventLoop::X(ref evlp) => {
885 evlp.available_monitors().map(MonitorHandle::X).collect()
886 },
887 }
888 }
889
890 #[inline]
891 pub fn primary_monitor(&self) -> Option<MonitorHandle> {
892 Some(
893 x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.primary_monitor()?; as MonitorHandle),
894 )
895 }
896
897 #[inline]
898 pub fn listen_device_events(&self, allowed: DeviceEvents) {
899 x11_or_wayland!(match self; Self(evlp) => evlp.listen_device_events(allowed))
900 }
901
902 #[cfg(feature = "rwh_05")]
903 #[inline]
904 pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
905 x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_05())
906 }
907
908 #[inline]
909 pub fn system_theme(&self) -> Option<Theme> {
910 None
911 }
912
913 #[cfg(feature = "rwh_06")]
914 #[inline]
915 pub fn raw_display_handle_rwh_06(
916 &self,
917 ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
918 x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle_rwh_06())
919 }
920
921 pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
922 x11_or_wayland!(match self; Self(evlp) => evlp.set_control_flow(control_flow))
923 }
924
925 pub(crate) fn control_flow(&self) -> ControlFlow {
926 x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
927 }
928
929 pub(crate) fn clear_exit(&self) {
930 x11_or_wayland!(match self; Self(evlp) => evlp.clear_exit())
931 }
932
933 pub(crate) fn exit(&self) {
934 x11_or_wayland!(match self; Self(evlp) => evlp.exit())
935 }
936
937 pub(crate) fn exiting(&self) -> bool {
938 x11_or_wayland!(match self; Self(evlp) => evlp.exiting())
939 }
940
941 pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
942 match self {
943 #[cfg(x11_platform)]
944 Self::X(conn) => OwnedDisplayHandle::X(conn.x_connection().clone()),
945 #[cfg(wayland_platform)]
946 Self::Wayland(conn) => OwnedDisplayHandle::Wayland(conn.connection.clone()),
947 }
948 }
949
950 #[allow(dead_code)]
951 fn set_exit_code(&self, code: i32) {
952 x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
953 }
954
955 #[allow(dead_code)]
956 fn exit_code(&self) -> Option<i32> {
957 x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
958 }
959}
960
961#[derive(Clone)]
962#[allow(dead_code)]
963pub(crate) enum OwnedDisplayHandle {
964 #[cfg(x11_platform)]
965 X(Arc<XConnection>),
966 #[cfg(wayland_platform)]
967 Wayland(wayland_client::Connection),
968}
969
970impl OwnedDisplayHandle {
971 #[cfg(feature = "rwh_05")]
972 #[inline]
973 pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
974 match self {
975 #[cfg(x11_platform)]
976 Self::X(xconn) => {
977 let mut xlib_handle = rwh_05::XlibDisplayHandle::empty();
978 xlib_handle.display = xconn.display.cast();
979 xlib_handle.screen = xconn.default_screen_index() as _;
980 xlib_handle.into()
981 },
982
983 #[cfg(wayland_platform)]
984 Self::Wayland(conn) => {
985 use sctk::reexports::client::Proxy;
986
987 let mut wayland_handle = rwh_05::WaylandDisplayHandle::empty();
988 wayland_handle.display = conn.display().id().as_ptr() as *mut _;
989 wayland_handle.into()
990 },
991 }
992 }
993
994 #[cfg(feature = "rwh_06")]
995 #[inline]
996 pub fn raw_display_handle_rwh_06(
997 &self,
998 ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
999 use std::ptr::NonNull;
1000
1001 match self {
1002 #[cfg(x11_platform)]
1003 Self::X(xconn) => Ok(rwh_06::XlibDisplayHandle::new(
1004 NonNull::new(xconn.display.cast()),
1005 xconn.default_screen_index() as _,
1006 )
1007 .into()),
1008
1009 #[cfg(wayland_platform)]
1010 Self::Wayland(conn) => {
1011 use sctk::reexports::client::Proxy;
1012
1013 Ok(rwh_06::WaylandDisplayHandle::new(
1014 NonNull::new(conn.display().id().as_ptr().cast()).unwrap(),
1015 )
1016 .into())
1017 },
1018 }
1019 }
1020}
1021
1022fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
1026 a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
1027}
1028
1029#[cfg(target_os = "linux")]
1030fn is_main_thread() -> bool {
1031 rustix::thread::gettid() == rustix::process::getpid()
1032}
1033
1034#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
1035fn is_main_thread() -> bool {
1036 use libc::pthread_main_np;
1037
1038 unsafe { pthread_main_np() == 1 }
1039}
1040
1041#[cfg(target_os = "netbsd")]
1042fn is_main_thread() -> bool {
1043 std::thread::current().name() == Some("main")
1044}