From 6afee17f5209cff25a7688d4b5a85a46c3ae1d31 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 5 Jan 2022 18:09:07 +0100 Subject: [PATCH] Replace EventLoopExtUnix with EventLoopBuilderExtUnix for ease of use --- CHANGELOG.md | 2 + src/event_loop.rs | 2 +- src/platform/unix.rs | 108 +++++++-------------------------- src/platform_impl/linux/mod.rs | 95 +++++++++++++++-------------- 4 files changed, 73 insertions(+), 134 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35b2e1326d..fba19d58d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ renamed methods). - Replaced `EventLoopExtWindows` with `EventLoopBuilderExtWindows` (which also has renamed methods). +- Replaced `EventLoopExtUnix` with `EventLoopBuilderExtUnix` (which also has + renamed methods). # 0.26.1 (2022-01-05) diff --git a/src/event_loop.rs b/src/event_loop.rs index aeaeb7cef7..5a6eaa6b13 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -81,7 +81,7 @@ impl EventLoopBuilder { /// on the main thread.*** Attempting to create the event loop on a /// different thread will panic. This restriction isn't strictly necessary /// on all platforms, but is imposed to eliminate any nasty surprises when - /// porting to platforms that require it. `EventLoopExt::new_any_thread` + /// porting to platforms that require it. `EventLoopBuilderExt::any_thread` /// functions are exposed in the relevant `platform` module if the target /// platform supports creating an event loop on any thread. /// diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 83e061768b..6c3dfb8dbf 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -11,7 +11,7 @@ use std::os::raw; use std::{ptr, sync::Arc}; use crate::{ - event_loop::{EventLoop, EventLoopWindowTarget}, + event_loop::{EventLoopBuilder, EventLoopWindowTarget}, monitor::MonitorHandle, window::{Window, WindowBuilder}, }; @@ -21,8 +21,7 @@ use crate::dpi::Size; #[cfg(feature = "x11")] use crate::platform_impl::x11::{ffi::XVisualInfo, XConnection}; use crate::platform_impl::{ - EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget, - Window as LinuxWindow, + Backend, EventLoopWindowTarget as LinuxEventLoopWindowTarget, Window as LinuxWindow, }; // TODO: stupid hack so that glutin can do its work @@ -93,105 +92,42 @@ impl EventLoopWindowTargetExtUnix for EventLoopWindowTarget { } } -/// Additional methods on `EventLoop` that are specific to Unix. -pub trait EventLoopExtUnix { - /// Builds a new `EventLoop` that is forced to use X11. - /// - /// # Panics - /// - /// If called outside the main thread. To initialize an X11 event loop outside - /// the main thread, use [`new_x11_any_thread`](#tymethod.new_x11_any_thread). +/// Additional methods on [`EventLoopBuilder`] that are specific to Unix. +pub trait EventLoopBuilderExtUnix { + /// Force using X11. #[cfg(feature = "x11")] - fn new_x11() -> Result - where - Self: Sized; + fn x11(&mut self) -> &mut Self; - /// Builds a new `EventLoop` that is forced to use Wayland. - /// - /// # Panics - /// - /// If called outside the main thread. To initialize a Wayland event loop outside - /// the main thread, use [`new_wayland_any_thread`](#tymethod.new_wayland_any_thread). + /// Force using Wayland. #[cfg(feature = "wayland")] - fn new_wayland() -> Self - where - Self: Sized; - - /// Builds a new `EventLoop` on any thread. - /// - /// This method bypasses the cross-platform compatibility requirement - /// that `EventLoop` be created on the main thread. - fn new_any_thread() -> Self - where - Self: Sized; + fn wayland(&mut self) -> &mut Self; - /// Builds a new X11 `EventLoop` on any thread. + /// Allows building the `EventLoop` on any thread. /// - /// This method bypasses the cross-platform compatibility requirement - /// that `EventLoop` be created on the main thread. - #[cfg(feature = "x11")] - fn new_x11_any_thread() -> Result - where - Self: Sized; - - /// Builds a new Wayland `EventLoop` on any thread. - /// - /// This method bypasses the cross-platform compatibility requirement - /// that `EventLoop` be created on the main thread. - #[cfg(feature = "wayland")] - fn new_wayland_any_thread() -> Self - where - Self: Sized; -} - -fn wrap_ev(event_loop: LinuxEventLoop) -> EventLoop { - EventLoop { - event_loop, - _marker: std::marker::PhantomData, - } + /// This method bypasses the cross-platform compatibility requirement that + /// `EventLoop` must be created on the main thread. + fn any_thread(&mut self, any_thread: bool) -> &mut Self; } -impl EventLoopExtUnix for EventLoop { - #[inline] - fn new_any_thread() -> Self { - // Temporary - wrap_ev(LinuxEventLoop::new_any_thread(&Default::default())) - } - +impl EventLoopBuilderExtUnix for EventLoopBuilder { #[inline] #[cfg(feature = "x11")] - fn new_x11_any_thread() -> Result { - // Temporary - LinuxEventLoop::new_x11_any_thread(&Default::default()).map(wrap_ev) + fn x11(&mut self) -> &mut Self { + self.platform_specific.forced_backend = Some(Backend::X); + self } #[inline] #[cfg(feature = "wayland")] - fn new_wayland_any_thread() -> Self { - wrap_ev( - // Temporary - LinuxEventLoop::new_wayland_any_thread(&Default::default()) - // TODO: propagate - .expect("failed to open Wayland connection"), - ) - } - - #[inline] - #[cfg(feature = "x11")] - fn new_x11() -> Result { - // Temporary - LinuxEventLoop::new_x11(&Default::default()).map(wrap_ev) + fn wayland(&mut self) -> &mut Self { + self.platform_specific.forced_backend = Some(Backend::Wayland); + self } #[inline] - #[cfg(feature = "wayland")] - fn new_wayland() -> Self { - wrap_ev( - // Temporary - LinuxEventLoop::new_wayland(&Default::default()) - // TODO: propagate - .expect("failed to open Wayland connection"), - ) + fn any_thread(&mut self, any_thread: bool) -> &mut Self { + self.platform_specific.any_thread = any_thread; + self } } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 882b51fa6c..75136950b4 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -49,8 +49,28 @@ pub mod x11; /// If this variable is set with any other value, winit will panic. const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; -#[derive(Default, Debug, Copy, Clone, PartialEq, Hash)] -pub(crate) struct PlatformSpecificEventLoopAttributes {} +#[derive(Debug, Copy, Clone, PartialEq, Hash)] +pub(crate) enum Backend { + #[cfg(feature = "x11")] + X, + #[cfg(feature = "wayland")] + Wayland, +} + +#[derive(Debug, Copy, Clone, PartialEq, Hash)] +pub(crate) struct PlatformSpecificEventLoopAttributes { + pub(crate) forced_backend: Option, + pub(crate) any_thread: bool, +} + +impl Default for PlatformSpecificEventLoopAttributes { + fn default() -> Self { + Self { + forced_backend: None, + any_thread: false, + } + } +} #[derive(Clone)] pub struct PlatformSpecificWindowBuilderAttributes { @@ -571,27 +591,41 @@ impl Clone for EventLoopProxy { } impl EventLoop { - pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> EventLoop { - assert_is_main_thread("new_any_thread"); + pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self { + 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 really, absolutely need to create an \ + EventLoop on a different thread, you can use the \ + `EventLoopBuilderExtWindows::any_thread` function." + ); + } - EventLoop::new_any_thread(attributes) - } + #[cfg(feature = "x11")] + if attributes.forced_backend == Some(Backend::X) { + // TODO: Propagate + return EventLoop::new_x11_any_thread().unwrap(); + } + + #[cfg(feature = "wayland")] + if attributes.forced_backend == Some(Backend::Wayland) { + // TODO: Propagate + return EventLoop::new_wayland_any_thread().expect("failed to open Wayland connection"); + } - #[allow(unused_variables)] - pub(crate) fn new_any_thread(attributes: &PlatformSpecificEventLoopAttributes) -> EventLoop { if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) { match env_var.as_str() { "x11" => { // TODO: propagate #[cfg(feature = "x11")] - return EventLoop::new_x11_any_thread(attributes) + return EventLoop::new_x11_any_thread() .expect("Failed to initialize X11 backend"); #[cfg(not(feature = "x11"))] panic!("x11 feature is not enabled") } "wayland" => { #[cfg(feature = "wayland")] - return EventLoop::new_wayland_any_thread(attributes) + return EventLoop::new_wayland_any_thread() .expect("Failed to initialize Wayland backend"); #[cfg(not(feature = "wayland"))] panic!("wayland feature is not enabled"); @@ -604,13 +638,13 @@ impl EventLoop { } #[cfg(feature = "wayland")] - let wayland_err = match EventLoop::new_wayland_any_thread(attributes) { + let wayland_err = match EventLoop::new_wayland_any_thread() { Ok(event_loop) => return event_loop, Err(err) => err, }; #[cfg(feature = "x11")] - let x11_err = match EventLoop::new_x11_any_thread(attributes) { + let x11_err = match EventLoop::new_x11_any_thread() { Ok(event_loop) => return event_loop, Err(err) => err, }; @@ -627,34 +661,12 @@ impl EventLoop { } #[cfg(feature = "wayland")] - pub(crate) fn new_wayland( - attributes: &PlatformSpecificEventLoopAttributes, - ) -> Result, Box> { - assert_is_main_thread("new_wayland_any_thread"); - - EventLoop::new_wayland_any_thread(attributes) - } - - #[cfg(feature = "wayland")] - pub(crate) fn new_wayland_any_thread( - _: &PlatformSpecificEventLoopAttributes, - ) -> Result, Box> { + fn new_wayland_any_thread() -> Result, Box> { wayland::EventLoop::new().map(EventLoop::Wayland) } #[cfg(feature = "x11")] - pub(crate) fn new_x11( - attributes: &PlatformSpecificEventLoopAttributes, - ) -> Result, XNotSupported> { - assert_is_main_thread("new_x11_any_thread"); - - EventLoop::new_x11_any_thread(attributes) - } - - #[cfg(feature = "x11")] - pub(crate) fn new_x11_any_thread( - _: &PlatformSpecificEventLoopAttributes, - ) -> Result, XNotSupported> { + fn new_x11_any_thread() -> Result, XNotSupported> { let xconn = match X11_BACKEND.lock().as_ref() { Ok(xconn) => xconn.clone(), Err(err) => return Err(err.clone()), @@ -765,17 +777,6 @@ fn sticky_exit_callback( callback(evt, target, cf) } -fn assert_is_main_thread(suggested_method: &str) { - if !is_main_thread() { - panic!( - "Initializing the event loop outside of the main thread is a significant \ - cross-platform compatibility hazard. If you really, absolutely need to create an \ - EventLoop on a different thread, please use the `EventLoopExtUnix::{}` function.", - suggested_method - ); - } -} - #[cfg(target_os = "linux")] fn is_main_thread() -> bool { use libc::{c_long, getpid, syscall, SYS_gettid};