2022-12-25 09:57:27 +02:00
|
|
|
#![cfg(free_unix)]
|
|
|
|
|
|
|
|
|
|
#[cfg(all(not(x11_platform), not(wayland_platform)))]
|
2020-06-15 09:15:27 +02:00
|
|
|
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
|
|
|
|
|
|
2024-09-06 17:20:11 +03:00
|
|
|
use std::env;
|
2023-10-15 20:31:29 +04:00
|
|
|
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
2023-08-13 23:20:09 +04:00
|
|
|
use std::time::Duration;
|
2020-06-15 09:15:27 +02:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
#[cfg(x11_platform)]
|
2025-05-17 04:26:09 +02:00
|
|
|
use dpi::Size;
|
|
|
|
|
use winit_core::application::ApplicationHandler;
|
|
|
|
|
use winit_core::error::{EventLoopError, NotSupportedError};
|
2025-05-20 16:56:53 +02:00
|
|
|
use winit_core::event_loop::pump_events::PumpStatus;
|
2025-05-17 04:26:09 +02:00
|
|
|
use winit_core::event_loop::ActiveEventLoop;
|
|
|
|
|
use winit_core::window::ActivationToken;
|
|
|
|
|
|
|
|
|
|
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
|
2024-01-04 12:54:35 +01:00
|
|
|
#[cfg(x11_platform)]
|
2025-03-08 10:14:08 +03:00
|
|
|
use crate::platform::x11::WindowType as XWindowType;
|
2017-09-01 11:04:57 +02:00
|
|
|
|
2024-01-04 12:54:35 +01:00
|
|
|
pub(crate) mod common;
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(wayland_platform)]
|
2024-01-04 12:54:35 +01:00
|
|
|
pub(crate) mod wayland;
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2024-01-04 12:54:35 +01:00
|
|
|
pub(crate) mod x11;
|
2017-03-03 20:56:27 +01:00
|
|
|
|
2022-06-10 13:43:33 +03:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
2022-02-16 22:09:03 +01:00
|
|
|
pub(crate) enum Backend {
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2022-02-16 22:09:03 +01:00
|
|
|
X,
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(wayland_platform)]
|
2022-02-16 22:09:03 +01:00
|
|
|
Wayland,
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-10 13:43:33 +03:00
|
|
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
|
2022-02-16 22:09:03 +01:00
|
|
|
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
|
|
|
|
pub(crate) forced_backend: Option<Backend>,
|
|
|
|
|
pub(crate) any_thread: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-10 13:43:33 +03:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2022-04-20 01:56:56 +03:00
|
|
|
pub struct ApplicationName {
|
|
|
|
|
pub general: String,
|
|
|
|
|
pub instance: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ApplicationName {
|
|
|
|
|
pub fn new(general: String, instance: String) -> Self {
|
|
|
|
|
Self { general, instance }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-08 00:46:28 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
2024-01-31 17:29:59 +04:00
|
|
|
pub struct PlatformSpecificWindowAttributes {
|
2022-04-20 01:56:56 +03:00
|
|
|
pub name: Option<ApplicationName>,
|
2023-07-20 13:16:51 +00:00
|
|
|
pub activation_token: Option<ActivationToken>,
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2024-01-31 17:29:59 +04:00
|
|
|
pub x11: X11WindowAttributes,
|
2023-08-11 02:30:55 -07:00
|
|
|
}
|
|
|
|
|
|
2024-08-08 00:46:28 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
2023-08-11 02:30:55 -07:00
|
|
|
#[cfg(x11_platform)]
|
2024-01-31 17:29:59 +04:00
|
|
|
pub struct X11WindowAttributes {
|
2023-08-05 14:58:23 -07:00
|
|
|
pub visual_id: Option<x11rb::protocol::xproto::Visualid>,
|
2017-01-28 13:03:17 +01:00
|
|
|
pub screen_id: Option<i32>,
|
2020-01-03 23:14:11 -07:00
|
|
|
pub base_size: Option<Size>,
|
2018-05-20 10:47:22 -04:00
|
|
|
pub override_redirect: bool,
|
2019-09-24 00:10:33 +10:00
|
|
|
pub x11_window_types: Vec<XWindowType>,
|
2023-08-11 02:30:55 -07:00
|
|
|
|
|
|
|
|
/// The parent window to embed this window into.
|
|
|
|
|
pub embed_window: Option<x11rb::protocol::xproto::Window>,
|
2017-01-28 13:03:17 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-27 01:20:21 -07:00
|
|
|
#[cfg_attr(not(x11_platform), allow(clippy::derivable_impls))]
|
2024-01-31 17:29:59 +04:00
|
|
|
impl Default for PlatformSpecificWindowAttributes {
|
2019-09-24 00:10:33 +10:00
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
2022-04-20 01:56:56 +03:00
|
|
|
name: None,
|
2023-07-20 13:16:51 +00:00
|
|
|
activation_token: None,
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2024-01-31 17:29:59 +04:00
|
|
|
x11: X11WindowAttributes {
|
2023-08-11 02:30:55 -07:00
|
|
|
visual_id: None,
|
|
|
|
|
screen_id: None,
|
|
|
|
|
base_size: None,
|
|
|
|
|
override_redirect: false,
|
|
|
|
|
x11_window_types: vec![XWindowType::Normal],
|
|
|
|
|
embed_window: None,
|
|
|
|
|
},
|
2019-09-24 00:10:33 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-15 09:15:27 +02:00
|
|
|
/// `x11_or_wayland!(match expr; Enum(foo) => foo.something())`
|
|
|
|
|
/// expands to the equivalent of
|
|
|
|
|
/// ```ignore
|
|
|
|
|
/// match self {
|
|
|
|
|
/// Enum::X(foo) => foo.something(),
|
|
|
|
|
/// Enum::Wayland(foo) => foo.something(),
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
/// The result can be converted to another enum by adding `; as AnotherEnum`
|
|
|
|
|
macro_rules! x11_or_wayland {
|
|
|
|
|
(match $what:expr; $enum:ident ( $($c1:tt)* ) => $x:expr; as $enum2:ident ) => {
|
|
|
|
|
match $what {
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2020-06-15 09:15:27 +02:00
|
|
|
$enum::X($($c1)*) => $enum2::X($x),
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(wayland_platform)]
|
2020-06-15 09:15:27 +02:00
|
|
|
$enum::Wayland($($c1)*) => $enum2::Wayland($x),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
(match $what:expr; $enum:ident ( $($c1:tt)* ) => $x:expr) => {
|
|
|
|
|
match $what {
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2020-06-15 09:15:27 +02:00
|
|
|
$enum::X($($c1)*) => $x,
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(wayland_platform)]
|
2020-06-15 09:15:27 +02:00
|
|
|
$enum::Wayland($($c1)*) => $x,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-03 08:40:04 +01:00
|
|
|
#[derive(Debug)]
|
2025-05-17 03:56:51 +02:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2024-06-24 13:04:55 +03:00
|
|
|
pub enum EventLoop {
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(wayland_platform)]
|
2024-06-24 13:04:55 +03:00
|
|
|
Wayland(Box<wayland::EventLoop>),
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2024-06-24 13:04:55 +03:00
|
|
|
X(x11::EventLoop),
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-24 13:04:55 +03:00
|
|
|
impl EventLoop {
|
2023-08-13 23:20:09 +04:00
|
|
|
pub(crate) fn new(
|
|
|
|
|
attributes: &PlatformSpecificEventLoopAttributes,
|
|
|
|
|
) -> Result<Self, EventLoopError> {
|
2022-02-16 22:09:03 +01:00
|
|
|
if !attributes.any_thread && !is_main_thread() {
|
|
|
|
|
panic!(
|
|
|
|
|
"Initializing the event loop outside of the main thread is a significant \
|
|
|
|
|
cross-platform compatibility hazard. If you absolutely need to create an \
|
|
|
|
|
EventLoop on a different thread, you can use the \
|
2024-10-14 03:34:11 +09:00
|
|
|
`EventLoopBuilderExtX11::with_any_thread` or \
|
|
|
|
|
`EventLoopBuilderExtWayland::with_any_thread` functions."
|
2022-02-16 22:09:03 +01:00
|
|
|
);
|
|
|
|
|
}
|
2019-10-18 11:51:06 -04:00
|
|
|
|
2023-10-19 17:03:15 +04:00
|
|
|
// NOTE: Wayland first because of X11 could be present under Wayland as well. Empty
|
|
|
|
|
// variables are also treated as not set.
|
2023-09-03 14:24:05 -07:00
|
|
|
let backend = match (
|
|
|
|
|
attributes.forced_backend,
|
2023-10-19 17:03:15 +04:00
|
|
|
env::var("WAYLAND_DISPLAY")
|
2024-02-07 06:41:23 +04:00
|
|
|
.ok()
|
|
|
|
|
.filter(|var| !var.is_empty())
|
|
|
|
|
.or_else(|| env::var("WAYLAND_SOCKET").ok())
|
|
|
|
|
.filter(|var| !var.is_empty())
|
|
|
|
|
.is_some(),
|
2023-10-19 17:03:15 +04:00
|
|
|
env::var("DISPLAY").map(|var| !var.is_empty()).unwrap_or(false),
|
2023-09-03 14:24:05 -07:00
|
|
|
) {
|
|
|
|
|
// User is forcing a backend.
|
|
|
|
|
(Some(backend), ..) => backend,
|
|
|
|
|
// Wayland is present.
|
|
|
|
|
#[cfg(wayland_platform)]
|
|
|
|
|
(None, true, _) => Backend::Wayland,
|
|
|
|
|
// X11 is present.
|
|
|
|
|
#[cfg(x11_platform)]
|
|
|
|
|
(None, _, true) => Backend::X,
|
|
|
|
|
// No backend is present.
|
2024-01-25 10:49:36 +00:00
|
|
|
(_, wayland_display, x11_display) => {
|
|
|
|
|
let msg = if wayland_display && !cfg!(wayland_platform) {
|
|
|
|
|
"DISPLAY is not set; note: enable the `winit/wayland` feature to support \
|
|
|
|
|
Wayland"
|
|
|
|
|
} else if x11_display && !cfg!(x11_platform) {
|
2024-02-07 06:41:23 +04:00
|
|
|
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET is set; note: enable the \
|
|
|
|
|
`winit/x11` feature to support X11"
|
2024-01-25 10:49:36 +00:00
|
|
|
} else {
|
2024-02-07 06:41:23 +04:00
|
|
|
"neither WAYLAND_DISPLAY nor WAYLAND_SOCKET nor DISPLAY is set."
|
2024-01-25 10:49:36 +00:00
|
|
|
};
|
2024-09-06 17:20:11 +03:00
|
|
|
return Err(NotSupportedError::new(msg).into());
|
2023-09-03 14:24:05 -07:00
|
|
|
},
|
|
|
|
|
};
|
2020-06-15 09:15:27 +02:00
|
|
|
|
2023-09-03 14:24:05 -07:00
|
|
|
// Create the display based on the backend.
|
|
|
|
|
match backend {
|
|
|
|
|
#[cfg(wayland_platform)]
|
2025-02-20 20:21:07 +03:00
|
|
|
Backend::Wayland => EventLoop::new_wayland_any_thread(),
|
2023-09-03 14:24:05 -07:00
|
|
|
#[cfg(x11_platform)]
|
2025-02-20 20:21:07 +03:00
|
|
|
Backend::X => EventLoop::new_x11_any_thread(),
|
2023-08-13 23:20:09 +04:00
|
|
|
}
|
2017-09-01 11:04:57 +02:00
|
|
|
}
|
|
|
|
|
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(wayland_platform)]
|
2024-06-24 13:04:55 +03:00
|
|
|
fn new_wayland_any_thread() -> Result<EventLoop, EventLoopError> {
|
2022-07-29 14:39:41 +03:00
|
|
|
wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
|
2017-09-01 11:04:57 +02:00
|
|
|
}
|
|
|
|
|
|
2022-12-25 09:57:27 +02:00
|
|
|
#[cfg(x11_platform)]
|
2024-06-24 13:04:55 +03:00
|
|
|
fn new_x11_any_thread() -> Result<EventLoop, EventLoopError> {
|
2025-03-08 10:14:08 +03:00
|
|
|
x11::EventLoop::new().map(EventLoop::X)
|
2017-09-01 11:04:57 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-26 14:38:10 +01:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_wayland(&self) -> bool {
|
|
|
|
|
match *self {
|
|
|
|
|
#[cfg(wayland_platform)]
|
|
|
|
|
EventLoop::Wayland(_) => true,
|
|
|
|
|
#[cfg(x11_platform)]
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-11 15:38:09 +02:00
|
|
|
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
|
2024-05-20 20:27:36 +04:00
|
|
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app))
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
2017-09-23 09:36:30 +02:00
|
|
|
|
2024-06-24 13:04:55 +03:00
|
|
|
pub fn run_app_on_demand<A: ApplicationHandler>(
|
2024-05-20 20:27:36 +04:00
|
|
|
&mut self,
|
2024-07-11 15:38:09 +02:00
|
|
|
app: A,
|
2024-05-20 20:27:36 +04:00
|
|
|
) -> Result<(), EventLoopError> {
|
|
|
|
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app))
|
2023-06-18 12:40:03 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-24 13:04:55 +03:00
|
|
|
pub fn pump_app_events<A: ApplicationHandler>(
|
2024-05-20 20:27:36 +04:00
|
|
|
&mut self,
|
|
|
|
|
timeout: Option<Duration>,
|
2024-07-11 15:38:09 +02:00
|
|
|
app: A,
|
2024-05-20 20:27:36 +04:00
|
|
|
) -> PumpStatus {
|
|
|
|
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_app_events(timeout, app))
|
2023-06-18 12:40:03 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-06 21:02:53 +03:00
|
|
|
pub fn window_target(&self) -> &dyn ActiveEventLoop {
|
2022-06-07 23:17:45 +02:00
|
|
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
|
2017-09-23 09:36:30 +02:00
|
|
|
}
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
2017-05-25 23:19:13 +10:00
|
|
|
|
2024-06-24 13:04:55 +03:00
|
|
|
impl AsFd for EventLoop {
|
2023-10-15 20:31:29 +04:00
|
|
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
|
|
|
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_fd())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-24 13:04:55 +03:00
|
|
|
impl AsRawFd for EventLoop {
|
2023-10-15 20:31:29 +04:00
|
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
|
|
|
x11_or_wayland!(match self; EventLoop(evlp) => evlp.as_raw_fd())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-18 12:40:03 +01:00
|
|
|
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
|
|
|
|
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
|
|
|
|
/// `Option::min`)
|
|
|
|
|
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
|
|
|
|
|
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 11:51:06 -04:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
fn is_main_thread() -> bool {
|
2023-07-22 02:32:27 -07:00
|
|
|
rustix::thread::gettid() == rustix::process::getpid()
|
2019-10-18 11:51:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
|
|
|
|
fn is_main_thread() -> bool {
|
|
|
|
|
use libc::pthread_main_np;
|
|
|
|
|
|
|
|
|
|
unsafe { pthread_main_np() == 1 }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "netbsd")]
|
|
|
|
|
fn is_main_thread() -> bool {
|
2020-08-20 18:12:01 +00:00
|
|
|
std::thread::current().name() == Some("main")
|
2019-10-18 11:51:06 -04:00
|
|
|
}
|