Skip to content

Commit

Permalink
Add EventLoop::waker
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 25, 2024
1 parent b36d8d1 commit c3d673d
Show file tree
Hide file tree
Showing 19 changed files with 349 additions and 210 deletions.
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()
}
}

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

0 comments on commit c3d673d

Please sign in to comment.