Skip to content

Commit

Permalink
Replace EventLoopExtUnix with EventLoopBuilderExtUnix for ease of use
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 5, 2022
1 parent f4a4ece commit 009619a
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 134 deletions.
2 changes: 1 addition & 1 deletion src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl<T> EventLoopBuilder<T> {
/// 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.
///
Expand Down
108 changes: 22 additions & 86 deletions src/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand All @@ -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
Expand Down Expand Up @@ -93,105 +92,42 @@ impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
}
}

/// 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<Self, XNotSupported>
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<Self, XNotSupported>
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<T>(event_loop: LinuxEventLoop<T>) -> EventLoop<T> {
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<T> EventLoopExtUnix for EventLoop<T> {
#[inline]
fn new_any_thread() -> Self {
// Temporary
wrap_ev(LinuxEventLoop::new_any_thread(&Default::default()))
}

impl<T> EventLoopBuilderExtUnix for EventLoopBuilder<T> {
#[inline]
#[cfg(feature = "x11")]
fn new_x11_any_thread() -> Result<Self, XNotSupported> {
// 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<Self, XNotSupported> {
// 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
}
}

Expand Down
93 changes: 46 additions & 47 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,26 @@ 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 {
X,
Wayland,
}

#[derive(Debug, Copy, Clone, PartialEq, Hash)]
pub(crate) struct PlatformSpecificEventLoopAttributes {
pub(crate) forced_backend: Option<Backend>,
pub(crate) any_thread: bool,
}

impl Default for PlatformSpecificEventLoopAttributes {
fn default() -> Self {
Self {
forced_backend: None,
any_thread: false,
}
}
}

#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
Expand Down Expand Up @@ -571,27 +589,41 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
}

impl<T: 'static> EventLoop<T> {
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> EventLoop<T> {
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<T> {
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");
Expand All @@ -604,13 +636,13 @@ impl<T: 'static> EventLoop<T> {
}

#[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,
};
Expand All @@ -627,34 +659,12 @@ impl<T: 'static> EventLoop<T> {
}

#[cfg(feature = "wayland")]
pub(crate) fn new_wayland(
attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<EventLoop<T>, Box<dyn Error>> {
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<EventLoop<T>, Box<dyn Error>> {
fn new_wayland_any_thread() -> Result<EventLoop<T>, Box<dyn Error>> {
wayland::EventLoop::new().map(EventLoop::Wayland)
}

#[cfg(feature = "x11")]
pub(crate) fn new_x11(
attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<EventLoop<T>, 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<EventLoop<T>, XNotSupported> {
fn new_x11_any_thread() -> Result<EventLoop<T>, XNotSupported> {
let xconn = match X11_BACKEND.lock().as_ref() {
Ok(xconn) => xconn.clone(),
Err(err) => return Err(err.clone()),
Expand Down Expand Up @@ -765,17 +775,6 @@ fn sticky_exit_callback<T, F>(
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};
Expand Down

0 comments on commit 009619a

Please sign in to comment.