diff --git a/examples/spam-events.rs b/examples/spam-events.rs new file mode 100644 index 0000000000..ae903a61ec --- /dev/null +++ b/examples/spam-events.rs @@ -0,0 +1,83 @@ +use std::error::Error; +use std::fs::File; + +use winit::application::ApplicationHandler; +use winit::event_loop::{ActiveEventLoop, EventLoop}; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +enum UserEvent { + WakeUp, + Counter(u64), +} + +struct Application { + file: std::fs::File, +} + +impl Application { + fn new(_event_loop: &EventLoop) -> Self { + Self { + file: File::options() + .write(true) + .truncate(true) + .append(false) + .create(true) + .open("log.txt") + .unwrap(), + } + } +} + +impl ApplicationHandler for Application { + fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) { + // write events to file, leave stdout for other info + use std::io::Write; + writeln!(&mut self.file, "User event: {event:?}").unwrap(); + + if let UserEvent::Counter(c) = event { + if c == 15000 { + std::process::exit(0); + } + } + } + + fn resumed(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {} + + fn window_event( + &mut self, + _event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + _event: winit::event::WindowEvent, + ) { + } +} + +fn main() -> Result<(), Box> { + let event_loop = EventLoop::::with_user_event().build()?; + let proxy = event_loop.create_proxy(); + + std::thread::spawn(move || { + let mut counter = 0; + loop { + if proxy.send_event(UserEvent::Counter(counter)).is_err() { + println!("Failed: {}", counter); + } + + counter += 1; + + if counter > 15000 { + let mut wakeup_counter = 1; + loop { + let _ = proxy.send_event(UserEvent::WakeUp); + println!("Sent {wakeup_counter} WakeUp events"); + wakeup_counter += 1; + } + } + } + }); + + let mut state = Application::new(&event_loop); + + event_loop.run_app(&mut state).map_err(Into::into) +} diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 2385ab8a15..c7a9f2d9e4 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -32,7 +32,6 @@ with it, the migration guide should be added below the entry, like: To migrate it we should do X, Y, and then Z, for example: // Code snippet. - ``` The migration guide could reference other migration examples in the current @@ -43,7 +42,13 @@ changelog entry. ### Added - Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`. +- Add `event_loop::EventLoopProxyError` +- Return an error in `EventLoopProxy::send_event` when the event loop is busy on Windows and can't accept new events. ### Fixed - On macOS, fix panic on exit when dropping windows outside the event loop. + +### Fixed + +- Removed `event_loop::EventLoopClosed` diff --git a/src/event_loop.rs b/src/event_loop.rs index 99e72040f8..31b88e4163 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -553,7 +553,7 @@ impl EventLoopProxy { /// Returns an `Err` if the associated [`EventLoop`] no longer exists. /// /// [`UserEvent(event)`]: Event::UserEvent - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered(); self.event_loop_proxy.send_event(event) @@ -566,20 +566,32 @@ impl fmt::Debug for EventLoopProxy { } } -/// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that -/// no longer exists. -/// -/// Contains the original event given to [`EventLoopProxy::send_event`]. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventLoopClosed(pub T); +pub enum EventLoopProxyError { + /// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] + /// that no longer exists. + /// + /// Contains the original event given to [`EventLoopProxy::send_event`]. + Closed(T), + /// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] + /// that is busy handling events and can't accept new events because it reached its limit. + /// This will happen on Windows for example, if more than 10,000 events are sent in a short + /// time. + Busy, +} -impl fmt::Display for EventLoopClosed { +impl fmt::Display for EventLoopProxyError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Tried to wake up a closed `EventLoop`") + match self { + EventLoopProxyError::Closed(_) => f.write_str("Tried to wake up a closed `EventLoop`"), + EventLoopProxyError::Busy => { + f.write_str("Tried to wake up `EventLoop` while it is busy") + }, + } } } -impl error::Error for EventLoopClosed {} +impl error::Error for EventLoopProxyError {} /// Control when device events are captured. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 31285b63f9..2c66c6fa0d 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -640,8 +640,10 @@ impl Clone for EventLoopProxy { } impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed> { - self.user_events_sender.send(event).map_err(|err| event_loop::EventLoopClosed(err.0))?; + pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopProxyError> { + self.user_events_sender + .send(event) + .map_err(|err| event_loop::EventLoopProxyError::Closed(err.0))?; self.waker.wake(); Ok(()) } diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index 40293d9f79..8c57f60e67 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -17,7 +17,7 @@ use objc2_foundation::{MainThreadMarker, NSString}; use crate::error::EventLoopError; use crate::event::Event; use crate::event_loop::{ - ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed, + ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopProxyError, }; use crate::platform::ios::Idiom; use crate::platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents}; @@ -269,8 +269,10 @@ impl EventLoopProxy { } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.sender.send(event).map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?; + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { + self.sender + .send(event) + .map_err(|::std::sync::mpsc::SendError(x)| EventLoopProxyError::Closed(x))?; unsafe { // let the main thread know there's a new event CFRunLoopSourceSignal(self.source); diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 7f0877f6a3..b0f234d5c3 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -20,7 +20,7 @@ use self::x11::{X11Error, XConnection, XError, XNotSupported}; use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError}; use crate::event_loop::{ - ActiveEventLoop as RootELW, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed, + ActiveEventLoop as RootELW, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopProxyError, }; use crate::icon::Icon; use crate::keyboard::Key; @@ -828,7 +828,7 @@ impl AsRawFd for EventLoop { } impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event)) } } diff --git a/src/platform_impl/linux/wayland/event_loop/proxy.rs b/src/platform_impl/linux/wayland/event_loop/proxy.rs index 9dc7d99280..c46f94f890 100644 --- a/src/platform_impl/linux/wayland/event_loop/proxy.rs +++ b/src/platform_impl/linux/wayland/event_loop/proxy.rs @@ -4,7 +4,7 @@ use std::sync::mpsc::SendError; use sctk::reexports::calloop::channel::Sender; -use crate::event_loop::EventLoopClosed; +use crate::event_loop::EventLoopProxyError; /// A handle that can be sent across the threads and used to wake up the `EventLoop`. pub struct EventLoopProxy { @@ -22,7 +22,9 @@ impl EventLoopProxy { Self { user_events_sender } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.user_events_sender.send(event).map_err(|SendError(error)| EventLoopClosed(error)) + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { + self.user_events_sender + .send(event) + .map_err(|SendError(error)| EventLoopProxyError::Closed(error)) } } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 6a4708e7b5..278f54e97d 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -29,7 +29,9 @@ use x11rb::xcb_ffi::ReplyOrIdError; use crate::error::{EventLoopError, OsError as RootOsError}; use crate::event::{Event, StartCause, WindowEvent}; -use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed}; +use crate::event_loop::{ + ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopProxyError, +}; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::common::xkb::Context; use crate::platform_impl::platform::{min_timeout, WindowId}; @@ -82,8 +84,8 @@ impl Clone for WakeSender { } impl WakeSender { - pub fn send(&self, t: T) -> Result<(), EventLoopClosed> { - let res = self.sender.send(t).map_err(|e| EventLoopClosed(e.0)); + pub fn send(&self, t: T) -> Result<(), EventLoopProxyError> { + let res = self.sender.send(t).map_err(|e| EventLoopProxyError::Closed(e.0)); if res.is_ok() { self.waker.ping(); } @@ -726,8 +728,8 @@ impl ActiveEventLoop { } impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.user_sender.send(event).map_err(|e| EventLoopClosed(e.0)) + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { + self.user_sender.send(event) } } diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index b5a3580970..5346138c33 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -28,7 +28,7 @@ use super::observer::setup_control_flow_observers; use crate::error::EventLoopError; use crate::event::Event; use crate::event_loop::{ - ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents, EventLoopClosed, + ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents, EventLoopProxyError, }; use crate::platform::macos::ActivationPolicy; use crate::platform::pump_events::PumpStatus; @@ -491,8 +491,8 @@ impl EventLoopProxy { } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.sender.send(event).map_err(|mpsc::SendError(x)| EventLoopClosed(x))?; + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { + self.sender.send(event).map_err(|mpsc::SendError(x)| EventLoopProxyError::Closed(x))?; unsafe { // let the main thread know there's a new event CFRunLoopSourceSignal(self.source); diff --git a/src/platform_impl/orbital/event_loop.rs b/src/platform_impl/orbital/event_loop.rs index 59ba9e522d..6c6a0f2383 100644 --- a/src/platform_impl/orbital/event_loop.rs +++ b/src/platform_impl/orbital/event_loop.rs @@ -718,10 +718,10 @@ pub struct EventLoopProxy { } impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed> { + pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopProxyError> { self.user_events_sender .send(event) - .map_err(|mpsc::SendError(x)| event_loop::EventLoopClosed(x))?; + .map_err(|mpsc::SendError(x)| event_loop::EventLoopProxyError::Closed(x))?; self.wake_socket.wake().unwrap(); diff --git a/src/platform_impl/web/event_loop/proxy.rs b/src/platform_impl/web/event_loop/proxy.rs index bd8e714635..26efb5401f 100644 --- a/src/platform_impl/web/event_loop/proxy.rs +++ b/src/platform_impl/web/event_loop/proxy.rs @@ -2,7 +2,7 @@ use std::rc::Weak; use std::sync::mpsc::{SendError, Sender}; use super::runner::Execution; -use crate::event_loop::EventLoopClosed; +use crate::event_loop::EventLoopProxyError; use crate::platform_impl::platform::r#async::Waker; pub struct EventLoopProxy { @@ -15,8 +15,8 @@ impl EventLoopProxy { Self { runner, sender } } - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.sender.send(event).map_err(|SendError(event)| EventLoopClosed(event))?; + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { + self.sender.send(event).map_err(|SendError(event)| EventLoopProxyError::Closed(event))?; self.runner.wake(); Ok(()) } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4fc31553ff..ca2ae26fd7 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex, MutexGuard}; use std::time::{Duration, Instant}; use std::{mem, panic, ptr}; +use crate::event_loop::EventLoopProxyError; use crate::utils::Lazy; use windows_sys::Win32::Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE; @@ -62,7 +63,7 @@ use crate::error::EventLoopError; use crate::event::{ DeviceEvent, Event, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, WindowEvent, }; -use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed}; +use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents}; use crate::keyboard::ModifiersState; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::platform::dark_mode::try_theme; @@ -741,14 +742,16 @@ impl Clone for EventLoopProxy { } impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.event_send - .send(event) - .map(|result| { - unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) }; - result - }) - .map_err(|e| EventLoopClosed(e.0)) + pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError> { + self.event_send.send(event).map_err(|e| EventLoopProxyError::Closed(e.0)).and_then( + |result| { + if unsafe { PostMessageW(self.target_window, USER_EVENT_MSG_ID.get(), 0, 0) } == 0 { + Err(EventLoopProxyError::Busy) + } else { + Ok(result) + } + }, + ) } }