Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions examples/spam-events.rs
Original file line number Diff line number Diff line change
@@ -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<T>(_event_loop: &EventLoop<T>) -> Self {
Self {
file: File::options()
.write(true)
.truncate(true)
.append(false)
.create(true)
.open("log.txt")
.unwrap(),
}
}
}

impl ApplicationHandler<UserEvent> 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<dyn Error>> {
let event_loop = EventLoop::<UserEvent>::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)
}
7 changes: 6 additions & 1 deletion src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`
30 changes: 21 additions & 9 deletions src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ impl<T: 'static> EventLoopProxy<T> {
/// Returns an `Err` if the associated [`EventLoop`] no longer exists.
///
/// [`UserEvent(event)`]: Event::UserEvent
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError<T>> {
let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered();

self.event_loop_proxy.send_event(event)
Expand All @@ -566,20 +566,32 @@ impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
}
}

/// 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<T>(pub T);
pub enum EventLoopProxyError<T> {
/// 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<T> fmt::Display for EventLoopClosed<T> {
impl<T> fmt::Display for EventLoopProxyError<T> {
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<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
impl<T: fmt::Debug> error::Error for EventLoopProxyError<T> {}

/// Control when device events are captured.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
Expand Down
6 changes: 4 additions & 2 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,10 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
}

impl<T> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
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<T>> {
self.user_events_sender
.send(event)
.map_err(|err| event_loop::EventLoopProxyError::Closed(err.0))?;
self.waker.wake();
Ok(())
}
Expand Down
8 changes: 5 additions & 3 deletions src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -269,8 +269,10 @@ impl<T> EventLoopProxy<T> {
}
}

pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender.send(event).map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError<T>> {
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);
Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -828,7 +828,7 @@ impl<T> AsRawFd for EventLoop<T> {
}

impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError<T>> {
x11_or_wayland!(match self; EventLoopProxy(proxy) => proxy.send_event(event))
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/platform_impl/linux/wayland/event_loop/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: 'static> {
Expand All @@ -22,7 +22,9 @@ impl<T: 'static> EventLoopProxy<T> {
Self { user_events_sender }
}

pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_events_sender.send(event).map_err(|SendError(error)| EventLoopClosed(error))
pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError<T>> {
self.user_events_sender
.send(event)
.map_err(|SendError(error)| EventLoopProxyError::Closed(error))
}
}
12 changes: 7 additions & 5 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -82,8 +84,8 @@ impl<T> Clone for WakeSender<T> {
}

impl<T> WakeSender<T> {
pub fn send(&self, t: T) -> Result<(), EventLoopClosed<T>> {
let res = self.sender.send(t).map_err(|e| EventLoopClosed(e.0));
pub fn send(&self, t: T) -> Result<(), EventLoopProxyError<T>> {
let res = self.sender.send(t).map_err(|e| EventLoopProxyError::Closed(e.0));
if res.is_ok() {
self.waker.ping();
}
Expand Down Expand Up @@ -726,8 +728,8 @@ impl ActiveEventLoop {
}

impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_sender.send(event).map_err(|e| EventLoopClosed(e.0))
pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError<T>> {
self.user_sender.send(event)
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -491,8 +491,8 @@ impl<T> EventLoopProxy<T> {
}
}

pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender.send(event).map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError<T>> {
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);
Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/orbital/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,10 +718,10 @@ pub struct EventLoopProxy<T: 'static> {
}

impl<T> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopProxyError<T>> {
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();

Expand Down
6 changes: 3 additions & 3 deletions src/platform_impl/web/event_loop/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: 'static> {
Expand All @@ -15,8 +15,8 @@ impl<T: 'static> EventLoopProxy<T> {
Self { runner, sender }
}

pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender.send(event).map_err(|SendError(event)| EventLoopClosed(event))?;
pub fn send_event(&self, event: T) -> Result<(), EventLoopProxyError<T>> {
self.sender.send(event).map_err(|SendError(event)| EventLoopProxyError::Closed(event))?;
self.runner.wake();
Ok(())
}
Expand Down
21 changes: 12 additions & 9 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -741,14 +742,16 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
}

impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
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<T>> {
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)
}
},
)
}
}

Expand Down