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::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; |
7 | use std::sync::Arc; |
8 | use std::time::Duration; |
9 | use std::{collections::VecDeque, env, fmt}; |
10 | #[cfg (x11_platform)] |
11 | use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex}; |
12 | |
13 | #[cfg (x11_platform)] |
14 | use once_cell::sync::Lazy; |
15 | use smol_str::SmolStr; |
16 | |
17 | #[cfg (x11_platform)] |
18 | use crate::platform::x11::XlibErrorHook; |
19 | use 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)] |
39 | pub use x11::XNotSupported; |
40 | #[cfg (x11_platform)] |
41 | use x11::{util::WindowType as XWindowType, X11Error, XConnection, XError}; |
42 | |
43 | pub(crate) use crate::icon::RgbaIcon as PlatformIcon; |
44 | pub(crate) use crate::platform_impl::Fullscreen; |
45 | |
46 | pub mod common; |
47 | #[cfg (wayland_platform)] |
48 | pub mod wayland; |
49 | #[cfg (x11_platform)] |
50 | pub mod x11; |
51 | |
52 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Hash)] |
53 | pub(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)] |
61 | pub(crate) struct PlatformSpecificEventLoopAttributes { |
62 | pub(crate) forced_backend: Option<Backend>, |
63 | pub(crate) any_thread: bool, |
64 | } |
65 | |
66 | #[derive (Debug, Clone, PartialEq, Eq)] |
67 | pub struct ApplicationName { |
68 | pub general: String, |
69 | pub instance: String, |
70 | } |
71 | |
72 | impl ApplicationName { |
73 | pub fn new(general: String, instance: String) -> Self { |
74 | Self { general, instance } |
75 | } |
76 | } |
77 | |
78 | #[derive (Clone)] |
79 | pub 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)] |
88 | pub 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 | |
99 | impl 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)] |
118 | pub(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)] |
122 | pub 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 | |
130 | impl 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 | |
142 | pub(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)] |
150 | pub struct WindowId(u64); |
151 | |
152 | impl From<WindowId> for u64 { |
153 | fn from(window_id: WindowId) -> Self { |
154 | window_id.0 |
155 | } |
156 | } |
157 | |
158 | impl From<u64> for WindowId { |
159 | fn from(raw_id: u64) -> Self { |
160 | Self(raw_id) |
161 | } |
162 | } |
163 | |
164 | impl WindowId { |
165 | pub const unsafe fn dummy() -> Self { |
166 | Self(0) |
167 | } |
168 | } |
169 | |
170 | #[derive (Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
171 | pub enum DeviceId { |
172 | #[cfg (x11_platform)] |
173 | X(x11::DeviceId), |
174 | #[cfg (wayland_platform)] |
175 | Wayland(wayland::DeviceId), |
176 | } |
177 | |
178 | impl 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)] |
188 | pub 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` |
204 | macro_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 | |
223 | impl 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)] |
261 | pub enum VideoMode { |
262 | #[cfg (x11_platform)] |
263 | X(x11::VideoMode), |
264 | #[cfg (wayland_platform)] |
265 | Wayland(wayland::VideoMode), |
266 | } |
267 | |
268 | impl 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 | |
290 | impl 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)] |
640 | pub struct KeyEventExtra { |
641 | pub key_without_modifiers: Key, |
642 | pub text_with_all_modifiers: Option<SmolStr>, |
643 | } |
644 | |
645 | impl 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 | |
660 | impl 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)] |
672 | pub(crate) static mut XLIB_ERROR_HOOKS: Lazy<Mutex<Vec<XlibErrorHook>>> = |
673 | Lazy::new(|| Mutex::new(Vec::new())); |
674 | |
675 | #[cfg (x11_platform)] |
676 | unsafe 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 | |
722 | pub 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 | |
729 | pub enum EventLoopProxy<T: 'static> { |
730 | #[cfg (x11_platform)] |
731 | X(x11::EventLoopProxy<T>), |
732 | #[cfg (wayland_platform)] |
733 | Wayland(wayland::EventLoopProxy<T>), |
734 | } |
735 | |
736 | impl<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 | |
742 | impl<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 | |
844 | impl<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 | |
850 | impl<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 | |
856 | impl<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 | |
862 | pub enum EventLoopWindowTarget<T> { |
863 | #[cfg (wayland_platform)] |
864 | Wayland(wayland::EventLoopWindowTarget<T>), |
865 | #[cfg (x11_platform)] |
866 | X(x11::EventLoopWindowTarget<T>), |
867 | } |
868 | |
869 | impl<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`) |
955 | fn 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" )] |
962 | fn 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" ))] |
967 | fn is_main_thread() -> bool { |
968 | use libc::pthread_main_np; |
969 | |
970 | unsafe { pthread_main_np() == 1 } |
971 | } |
972 | |
973 | #[cfg (target_os = "netbsd" )] |
974 | fn is_main_thread() -> bool { |
975 | std::thread::current().name() == Some("main" ) |
976 | } |
977 | |