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