Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EventLoopProxy::waker(self) -> Waker #3424

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/child_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn main() -> Result<(), impl std::error::Error> {

println!("parent window: {parent_window:?})");

event_loop.run(move |event: Event<()>, elwt| {
event_loop.run(move |event: Event, elwt| {
if let Event::WindowEvent { event, window_id } = event {
match event {
WindowEvent::CloseRequested => {
Expand Down
27 changes: 17 additions & 10 deletions examples/custom_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#[cfg(not(web_platform))]
fn main() -> Result<(), impl std::error::Error> {
use std::sync::mpsc;

use simple_logger::SimpleLogger;
use winit::{
event::{Event, WindowEvent},
Expand All @@ -18,30 +20,35 @@ fn main() -> Result<(), impl std::error::Error> {
}

SimpleLogger::new().init().unwrap();
let event_loop = EventLoopBuilder::<CustomEvent>::with_user_event()
.build()
.unwrap();
let event_loop = EventLoopBuilder::new().build().unwrap();

let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();

// `EventLoopProxy` allows you to dispatch custom events to the main Winit event
// loop from any thread.
let event_loop_proxy = event_loop.create_proxy();
let waker = event_loop.create_proxy().waker();

let (sender, receiver) = mpsc::channel();

std::thread::spawn(move || {
// Wake up the `event_loop` once every second and dispatch a custom event
// from a different thread.
// Dispatch a custom event from a different thread once every second,
// and wake the `event_loop` so that it can handle it.
loop {
std::thread::sleep(std::time::Duration::from_secs(1));
event_loop_proxy.send_event(CustomEvent::Timer).ok();
if sender.send(CustomEvent::Timer).is_err() {
break;
}
waker.wake_by_ref();
}
});

event_loop.run(move |event, elwt| match event {
Event::UserEvent(event) => println!("user event: {event:?}"),
Event::NewEvents(_) => {
for event in receiver.try_iter() {
println!("user event: {event:?}");
}
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
Expand Down
2 changes: 1 addition & 1 deletion examples/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ mod wasm {
log_list
}

pub fn log_event(log_list: &web_sys::Element, event: &Event<()>) {
pub fn log_event(log_list: &web_sys::Element, event: &Event) {
log::debug!("{:?}", event);

// Getting access to browser logs requires a lot of setup on mobile devices.
Expand Down
7 changes: 6 additions & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ use crate::{
///
/// See the module-level docs for more information on the event loop manages each event.
#[derive(Debug, Clone, PartialEq)]
pub enum Event<T: 'static> {
pub enum Event<T: 'static = ()> {
/// Emitted when new events arrive from the OS to be processed.
///
/// This event type is useful as a place to put code that should be done before you start
Expand All @@ -80,6 +80,7 @@ pub enum Event<T: 'static> {
},

/// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
#[deprecated = "use `EventLoopProxy::waker`, and listen for wakeups in `NewEvents`"]
UserEvent(T),

/// Emitted when the application has been suspended.
Expand Down Expand Up @@ -257,9 +258,11 @@ pub enum Event<T: 'static> {

impl<T> Event<T> {
#[allow(clippy::result_large_err)]
#[deprecated = "Event::UserEvent is deprecated, so there is no need for this any more"]
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
use self::Event::*;
match self {
#[allow(deprecated)]
UserEvent(_) => Err(self),
WindowEvent { window_id, event } => Ok(WindowEvent { window_id, event }),
DeviceEvent { device_id, event } => Ok(DeviceEvent { device_id, event }),
Expand Down Expand Up @@ -1164,6 +1167,7 @@ mod tests {

// Mainline events.
let wid = unsafe { WindowId::dummy() };
#[allow(deprecated)]
x(UserEvent(()));
x(NewEvents(event::StartCause::Init));
x(AboutToWait);
Expand Down Expand Up @@ -1278,6 +1282,7 @@ mod tests {
}

#[test]
#[allow(deprecated)]
fn test_map_nonuser_event() {
foreach_event!(|event: event::Event<()>| {
let is_user = matches!(event, event::Event::UserEvent(()));
Expand Down
38 changes: 35 additions & 3 deletions src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::ops::Deref;
#[cfg(any(x11_platform, wayland_platform))]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::task::Waker;
use std::{error, fmt};

#[cfg(not(web_platform))]
Expand Down Expand Up @@ -67,7 +68,10 @@ impl EventLoopBuilder<()> {
/// Start building a new event loop.
#[inline]
pub fn new() -> Self {
Self::with_user_event()
Self {
platform_specific: Default::default(),
_p: PhantomData,
}
}
}

Expand All @@ -77,6 +81,7 @@ impl<T> EventLoopBuilder<T> {
/// Start building a new event loop, with the given type as the user event
/// type.
#[inline]
#[deprecated = "use `EventLoopProxy` with your own event channel instead"]
pub fn with_user_event() -> Self {
Self {
platform_specific: Default::default(),
Expand Down Expand Up @@ -205,6 +210,7 @@ impl EventLoop<()> {
impl<T> EventLoop<T> {
#[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."]
pub fn with_user_event() -> Result<EventLoop<T>, EventLoopError> {
#[allow(deprecated)]
EventLoopBuilder::<T>::with_user_event().build()
}

Expand Down Expand Up @@ -246,7 +252,8 @@ impl<T> EventLoop<T> {
self.event_loop.run(event_handler)
}

/// Creates an [`EventLoopProxy`] that can be used to dispatch user events to the main event loop.
/// Creates an [`EventLoopProxy`] that can be used to control the event
/// loop from a different thread.
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
event_loop_proxy: self.event_loop.create_proxy(),
Expand Down Expand Up @@ -442,7 +449,10 @@ unsafe impl rwh_05::HasRawDisplayHandle for OwnedDisplayHandle {
}

/// Used to send custom events to [`EventLoop`].
pub struct EventLoopProxy<T: 'static> {
///
/// This has a generic type `T` that represents a user event, but that is
/// deprecated.
pub struct EventLoopProxy<T: 'static = ()> {
event_loop_proxy: platform_impl::EventLoopProxy<T>,
}

Expand All @@ -462,9 +472,31 @@ impl<T: 'static> EventLoopProxy<T> {
/// Returns an `Err` if the associated [`EventLoop`] no longer exists.
///
/// [`UserEvent(event)`]: Event::UserEvent
#[deprecated = "get a `waker` and wake that instead"]
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.event_loop_proxy.send_event(event)
}

/// Create a [`Waker`] that can wake the event loop from any thread.
///
/// This is usually used in combination with [`mpsc::channel`] to send the
/// data that should be processed by the event loop.
///
/// If you do use a channel, the data will be accessible from inside
/// [`Event::NewEvents`].
///
/// [`mpsc::channel`]: std::sync::mpsc::channel
pub fn waker(self) -> Waker {
self.event_loop_proxy.waker()
}
}

// Note: This does not have the generic from `EventLoopProxy<T>`, since this
// is the new API that shouldn't need it.
impl From<EventLoopProxy> for Waker {
fn from(proxy: EventLoopProxy) -> Self {
proxy.waker()
}
}
Comment on lines +494 to 500
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is wanted — eventually EventLoopProxy will (presumably) just be replaced with a Waker.

In fact, maybe just deprecate EventLoopProxy and create_proxy now, replacing with EventLoop::create_waker?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to reuse proxy in the future for some internal stuff in the future around Send/Sync and also, not be limited on the Waker API, since it's rather limited. But yeah, in the current state they are mostly the same.


impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer
{
while let Ok(event) = self.user_events_receiver.try_recv() {
#[allow(deprecated)]
callback(crate::event::Event::UserEvent(event), self.window_target());
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/platform_impl/ios/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ macro_rules! bug_assert {
};
}

/// The event loop may have queued user events ready.
#[derive(Debug)]
pub(crate) struct HandlePendingUserEvents;

Expand Down Expand Up @@ -710,6 +711,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
}
drop(this);

#[allow(deprecated)]
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));

loop {
Expand Down Expand Up @@ -745,6 +747,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
}
}

#[allow(deprecated)]
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
}
}
Expand Down