From 8ad016362a4706a7b274d2d7a7a5fc89075de5b2 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 26 May 2025 06:48:52 +0200 Subject: [PATCH] chore: move event loop recreation check into backends themselves --- src/event_loop.rs | 12 ------------ src/platform_impl/web/event_loop/mod.rs | 13 +++++++++++++ src/platform_impl/web/event_loop/runner.rs | 4 ++-- winit-android/src/event_loop.rs | 6 ++++++ winit-appkit/src/app_state.rs | 5 ++--- winit-appkit/src/event_loop.rs | 3 ++- winit-core/src/error.rs | 8 +++++++- winit-orbital/src/event_loop.rs | 7 +++++++ winit-uikit/src/event_loop.rs | 8 ++++---- winit-wayland/src/event_loop/mod.rs | 8 +++++++- winit-win32/src/event_loop.rs | 8 +++++++- winit-x11/src/event_loop.rs | 7 +++++++ 12 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/event_loop.rs b/src/event_loop.rs index de1d22c7..dbd167cb 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -11,7 +11,6 @@ use std::marker::PhantomData; #[cfg(any(x11_platform, wayland_platform))] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; -use std::sync::atomic::{AtomicBool, Ordering}; use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; pub use winit_core::event_loop::*; @@ -53,8 +52,6 @@ pub struct EventLoopBuilder { pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes, } -static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); - impl EventLoopBuilder { /// Builds a new event loop. /// @@ -93,10 +90,6 @@ impl EventLoopBuilder { pub fn build(&mut self) -> Result { let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered(); - if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { - return Err(EventLoopError::RecreationAttempt); - } - // Certain platforms accept a mutable reference in their API. #[allow(clippy::unnecessary_mut_passed)] Ok(EventLoop { @@ -104,11 +97,6 @@ impl EventLoopBuilder { _marker: PhantomData, }) } - - #[cfg(web_platform)] - pub(crate) fn allow_event_loop_recreation() { - EVENT_LOOP_CREATED.store(false, Ordering::Relaxed); - } } impl EventLoop { diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index cc002ff9..db6d87e9 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -1,3 +1,5 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + use winit_core::application::ApplicationHandler; use winit_core::error::{EventLoopError, NotSupportedError}; use winit_core::event_loop::ActiveEventLoop as RootActiveEventLoop; @@ -20,11 +22,22 @@ pub struct EventLoop { #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes {} +static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); + impl EventLoop { pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result { + if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { + // For better cross-platformness. + return Err(EventLoopError::RecreationAttempt); + } + Ok(EventLoop { elw: ActiveEventLoop::new() }) } + fn allow_event_loop_recreation() { + EVENT_LOOP_CREATED.store(false, Ordering::Relaxed); + } + pub fn run_app(self, app: A) -> ! { let app = Box::new(app); diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index 292c08cc..f6a6e426 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -22,7 +22,7 @@ use super::super::main_thread::MainThreadMarker; use super::super::monitor::MonitorHandler; use super::proxy::EventLoopProxy; use super::state::State; -use super::{backend, ActiveEventLoop}; +use super::{backend, ActiveEventLoop, EventLoop}; use crate::platform::web::{PollStrategy, WaitUntilStrategy}; use crate::platform_impl::platform::backend::{EventListenerHandle, SafeAreaHandle}; use crate::platform_impl::platform::r#async::DispatchRunner; @@ -773,7 +773,7 @@ impl Shared { // * The `register_redraw_request` closure. // * The `destroy_fn` closure. if self.0.event_loop_recreation.get() { - crate::event_loop::EventLoopBuilder::allow_event_loop_recreation(); + EventLoop::allow_event_loop_recreation(); } } diff --git a/winit-android/src/event_loop.rs b/winit-android/src/event_loop.rs index 45c1654d..56d18283 100644 --- a/winit-android/src/event_loop.rs +++ b/winit-android/src/event_loop.rs @@ -128,6 +128,12 @@ const GLOBAL_WINDOW: WindowId = WindowId::from_raw(0); impl EventLoop { pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result { + static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); + if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { + // For better cross-platformness. + return Err(EventLoopError::RecreationAttempt); + } + let android_app = attributes.android_app.as_ref().expect( "An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \ Android", diff --git a/winit-appkit/src/app_state.rs b/winit-appkit/src/app_state.rs index 53261246..4fab8d53 100644 --- a/winit-appkit/src/app_state.rs +++ b/winit-appkit/src/app_state.rs @@ -58,7 +58,7 @@ impl AppState { activation_policy: Option, default_menu: bool, activate_ignoring_other_apps: bool, - ) -> Rc { + ) -> Option> { let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || { Self::get(mtm).with_handler(|app, event_loop| app.proxy_wake_up(event_loop)); })); @@ -85,8 +85,7 @@ impl AppState { pending_redraw: RefCell::new(vec![]), }); - GLOBAL.get(mtm).set(this.clone()).expect("application state can only be set once"); - this + GLOBAL.get(mtm).set(this.clone()).ok().and(Some(this)) } pub fn get(mtm: MainThreadMarker) -> Rc { diff --git a/winit-appkit/src/event_loop.rs b/winit-appkit/src/event_loop.rs index f97de277..a12e8446 100644 --- a/winit-appkit/src/event_loop.rs +++ b/winit-appkit/src/event_loop.rs @@ -182,7 +182,8 @@ impl EventLoop { activation_policy, attributes.default_menu, attributes.activate_ignoring_other_apps, - ); + ) + .ok_or_else(|| EventLoopError::RecreationAttempt)?; // Initialize the application (if it has not already been). let app = NSApplication::sharedApplication(mtm); diff --git a/winit-core/src/error.rs b/winit-core/src/error.rs index 39635639..3a6e1b83 100644 --- a/winit-core/src/error.rs +++ b/winit-core/src/error.rs @@ -19,7 +19,13 @@ pub enum EventLoopError { impl fmt::Display for EventLoopError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::RecreationAttempt => write!(f, "EventLoop can't be recreated"), + Self::RecreationAttempt => { + write!( + f, + "EventLoop can't be recreated, only a single instance of it is supported (for \ + cross-platform compatibility)" + ) + }, Self::Os(err) => err.fmt(f), Self::ExitFailure(status) => write!(f, "Exit Failure: {status}"), Self::NotSupported(err) => err.fmt(f), diff --git a/winit-orbital/src/event_loop.rs b/winit-orbital/src/event_loop.rs index 595f4cdf..77f1aaea 100644 --- a/winit-orbital/src/event_loop.rs +++ b/winit-orbital/src/event_loop.rs @@ -1,5 +1,6 @@ use std::cell::Cell; use std::collections::VecDeque; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Arc, Mutex}; use std::time::Instant; use std::{iter, mem, slice}; @@ -280,6 +281,12 @@ pub struct EventLoop { impl EventLoop { pub fn new(_: &PlatformSpecificEventLoopAttributes) -> Result { + static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); + if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { + // For better cross-platformness. + return Err(EventLoopError::RecreationAttempt); + } + // NOTE: Create a channel which can hold only one event to automatically _squash_ user // events. let (user_events_sender, user_events_receiver) = mpsc::sync_channel(1); diff --git a/winit-uikit/src/event_loop.rs b/winit-uikit/src/event_loop.rs index 990c0556..3b5ed3e4 100644 --- a/winit-uikit/src/event_loop.rs +++ b/winit-uikit/src/event_loop.rs @@ -148,10 +148,10 @@ impl EventLoop { static mut SINGLETON_INIT: bool = false; unsafe { - assert!( - !SINGLETON_INIT, - "Only one `EventLoop` is supported on iOS. `EventLoopProxy` might be helpful" - ); + if SINGLETON_INIT { + // Required, AppState is global state, and event loop can only be run once. + return Err(EventLoopError::RecreationAttempt); + } SINGLETON_INIT = true; } diff --git a/winit-wayland/src/event_loop/mod.rs b/winit-wayland/src/event_loop/mod.rs index c5810bfb..525b0e8e 100644 --- a/winit-wayland/src/event_loop/mod.rs +++ b/winit-wayland/src/event_loop/mod.rs @@ -5,7 +5,7 @@ use std::io::Result as IOResult; use std::mem; use std::os::fd::OwnedFd; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Condvar, Mutex}; use std::thread::JoinHandle; use std::time::{Duration, Instant}; @@ -80,6 +80,12 @@ pub struct EventLoop { impl EventLoop { pub fn new() -> Result { + static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); + if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { + // For better cross-platformness. + return Err(EventLoopError::RecreationAttempt); + } + let connection = Connection::connect_to_env().map_err(|err| os_error!(err))?; let (globals, mut event_queue) = diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index ecbb6638..95480e53 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -6,7 +6,7 @@ use std::cell::Cell; use std::ffi::c_void; use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _, OwnedHandle, RawHandle}; use std::rc::Rc; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, LazyLock, Mutex, MutexGuard}; use std::time::{Duration, Instant}; use std::{fmt, mem, panic, ptr}; @@ -195,6 +195,12 @@ impl EventLoop { pub fn new( attributes: &mut PlatformSpecificEventLoopAttributes, ) -> Result { + static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); + if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { + // For better cross-platformness. + return Err(EventLoopError::RecreationAttempt); + } + let thread_id = unsafe { GetCurrentThreadId() }; if !attributes.any_thread && thread_id != main_thread_id() { diff --git a/winit-x11/src/event_loop.rs b/winit-x11/src/event_loop.rs index 94a71396..b1b309ec 100644 --- a/winit-x11/src/event_loop.rs +++ b/winit-x11/src/event_loop.rs @@ -5,6 +5,7 @@ use std::mem::MaybeUninit; use std::ops::Deref; use std::os::raw::*; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; use std::sync::{Arc, LazyLock, Mutex, Weak}; use std::time::{Duration, Instant}; @@ -207,6 +208,12 @@ struct EventLoopState { impl EventLoop { pub fn new() -> Result { + static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); + if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { + // Required? + return Err(EventLoopError::RecreationAttempt); + } + let xconn = match X11_BACKEND.lock().unwrap_or_else(|e| e.into_inner()).as_ref() { Ok(xconn) => xconn.clone(), Err(err) => return Err(os_error!(err.clone()).into()),