1 | #![cfg (free_unix)] |
2 | |
3 | #[cfg (all(not(x11_platform), not(wayland_platform)))] |
4 | compile_error!("Please select a feature to build for unix: `x11`, `wayland`" ); |
5 | |
6 | use std::collections::VecDeque; |
7 | use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; |
8 | use std::sync::Arc; |
9 | use std::time::Duration; |
10 | use std::{env, fmt}; |
11 | #[cfg (x11_platform)] |
12 | use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex}; |
13 | |
14 | #[cfg (x11_platform)] |
15 | use crate::utils::Lazy; |
16 | use smol_str::SmolStr; |
17 | |
18 | #[cfg (x11_platform)] |
19 | use self::x11::{X11Error, XConnection, XError, XNotSupported}; |
20 | use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; |
21 | use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError}; |
22 | use crate::event_loop::{ |
23 | ActiveEventLoop as RootELW, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed, |
24 | }; |
25 | use crate::icon::Icon; |
26 | use crate::keyboard::Key; |
27 | use crate::platform::pump_events::PumpStatus; |
28 | #[cfg (x11_platform)] |
29 | use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook}; |
30 | use crate::window::{ |
31 | ActivationToken, Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, |
32 | ResizeDirection, Theme, UserAttentionType, WindowAttributes, WindowButtons, WindowLevel, |
33 | }; |
34 | |
35 | pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey}; |
36 | pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; |
37 | pub(crate) use crate::icon::RgbaIcon as PlatformIcon; |
38 | pub(crate) use crate::platform_impl::Fullscreen; |
39 | |
40 | pub(crate) mod common; |
41 | #[cfg (wayland_platform)] |
42 | pub(crate) mod wayland; |
43 | #[cfg (x11_platform)] |
44 | pub(crate) mod x11; |
45 | |
46 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Hash)] |
47 | pub(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)] |
55 | pub(crate) struct PlatformSpecificEventLoopAttributes { |
56 | pub(crate) forced_backend: Option<Backend>, |
57 | pub(crate) any_thread: bool, |
58 | } |
59 | |
60 | #[derive (Debug, Clone, PartialEq, Eq)] |
61 | pub struct ApplicationName { |
62 | pub general: String, |
63 | pub instance: String, |
64 | } |
65 | |
66 | impl ApplicationName { |
67 | pub fn new(general: String, instance: String) -> Self { |
68 | Self { general, instance } |
69 | } |
70 | } |
71 | |
72 | #[derive (Clone, Debug)] |
73 | pub 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)] |
82 | pub 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))] |
94 | impl 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)] |
113 | pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported>>> = |
114 | Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(op:Arc::new))); |
115 | |
116 | #[derive (Debug, Clone)] |
117 | pub 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 | |
127 | impl fmt::Display for OsError { |
128 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
129 | match *self { |
130 | OsError::Misc(e: &'static str) => _f.pad(e), |
131 | #[cfg (x11_platform)] |
132 | OsError::XNotSupported(ref e: &XNotSupported) => fmt::Display::fmt(self:e, _f), |
133 | #[cfg (x11_platform)] |
134 | OsError::XError(ref e: &Arc) => fmt::Display::fmt(self:e, _f), |
135 | #[cfg (wayland_platform)] |
136 | OsError::WaylandError(ref e: &Arc) => fmt::Display::fmt(self:e, _f), |
137 | } |
138 | } |
139 | } |
140 | |
141 | pub(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)] |
149 | pub struct WindowId(u64); |
150 | |
151 | impl From<WindowId> for u64 { |
152 | fn from(window_id: WindowId) -> Self { |
153 | window_id.0 |
154 | } |
155 | } |
156 | |
157 | impl From<u64> for WindowId { |
158 | fn from(raw_id: u64) -> Self { |
159 | Self(raw_id) |
160 | } |
161 | } |
162 | |
163 | impl WindowId { |
164 | pub const fn dummy() -> Self { |
165 | Self(0) |
166 | } |
167 | } |
168 | |
169 | #[derive (Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
170 | pub enum DeviceId { |
171 | #[cfg (x11_platform)] |
172 | X(x11::DeviceId), |
173 | #[cfg (wayland_platform)] |
174 | Wayland(wayland::DeviceId), |
175 | } |
176 | |
177 | impl 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)] |
187 | pub 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` |
203 | macro_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 | |
222 | impl 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)] |
260 | pub enum VideoModeHandle { |
261 | #[cfg (x11_platform)] |
262 | X(x11::VideoModeHandle), |
263 | #[cfg (wayland_platform)] |
264 | Wayland(wayland::VideoModeHandle), |
265 | } |
266 | |
267 | impl 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 | |
289 | impl 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)] |
635 | pub 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)] |
641 | pub(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)] |
650 | pub(crate) static XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new()); |
651 | |
652 | #[cfg (x11_platform)] |
653 | unsafe 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 | |
699 | pub 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 | |
706 | pub enum EventLoopProxy<T: 'static> { |
707 | #[cfg (x11_platform)] |
708 | X(x11::EventLoopProxy<T>), |
709 | #[cfg (wayland_platform)] |
710 | Wayland(wayland::EventLoopProxy<T>), |
711 | } |
712 | |
713 | impl<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 | |
719 | impl<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 | |
834 | impl<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 | |
840 | impl<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 | |
846 | impl<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 | |
852 | pub enum ActiveEventLoop { |
853 | #[cfg (wayland_platform)] |
854 | Wayland(wayland::ActiveEventLoop), |
855 | #[cfg (x11_platform)] |
856 | X(x11::ActiveEventLoop), |
857 | } |
858 | |
859 | impl 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)] |
961 | pub(crate) enum OwnedDisplayHandle { |
962 | #[cfg (x11_platform)] |
963 | X(Arc<XConnection>), |
964 | #[cfg (wayland_platform)] |
965 | Wayland(wayland_client::Connection), |
966 | } |
967 | |
968 | impl 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`) |
1023 | fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> { |
1024 | a.map_or(default:b, |a_timeout: Duration| b.map_or(default:Some(a_timeout), |b_timeout: Duration| Some(a_timeout.min(b_timeout)))) |
1025 | } |
1026 | |
1027 | #[cfg (target_os = "linux" )] |
1028 | fn 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" ))] |
1033 | fn is_main_thread() -> bool { |
1034 | use libc::pthread_main_np; |
1035 | |
1036 | unsafe { pthread_main_np() == 1 } |
1037 | } |
1038 | |
1039 | #[cfg (target_os = "netbsd" )] |
1040 | fn is_main_thread() -> bool { |
1041 | std::thread::current().name() == Some("main" ) |
1042 | } |
1043 | |