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 2c6aba9
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 114 deletions.
23 changes: 16 additions & 7 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 @@ -27,21 +29,28 @@ fn main() -> Result<(), impl std::error::Error> {
.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.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.iter() {
println!("user event: {event:?}");
}
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
Expand Down
12 changes: 12 additions & 0 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 @@ -247,11 +248,22 @@ impl<T> EventLoop<T> {
}

/// Creates an [`EventLoopProxy`] that can be used to dispatch user events to the main event loop.
#[deprecated = "use `waker` with your own event channel instead"]
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
event_loop_proxy: self.event_loop.create_proxy(),
}
}

/// 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.
///
/// [`mpsc::channel`]: std::sync::mpsc::channel
pub fn waker(&self) -> Waker {
self.event_loop.waker()
}
}

#[cfg(feature = "rwh_06")]
Expand Down
114 changes: 67 additions & 47 deletions src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
marker::PhantomData,
ptr,
sync::mpsc::{self, Receiver, Sender},
task::{RawWaker, RawWakerVTable, Waker},
};

use core_foundation::base::{CFIndex, CFRelease};
Expand Down Expand Up @@ -208,52 +209,21 @@ impl<T: 'static> EventLoop<T> {
}

pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone())
EventLoopProxy::new(self.sender.clone(), self.waker())
}

pub fn window_target(&self) -> &RootEventLoopWindowTarget {
&self.window_target
}
}

// EventLoopExtIOS
impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom {
UIDevice::current(self.mtm).userInterfaceIdiom().into()
}
}

pub struct EventLoopProxy<T> {
sender: Sender<T>,
source: CFRunLoopSourceRef,
}

unsafe impl<T: Send> Send for EventLoopProxy<T> {}

impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone())
}
}

impl<T> Drop for EventLoopProxy<T> {
fn drop(&mut self) {
unsafe {
CFRunLoopSourceInvalidate(self.source);
CFRelease(self.source as _);
}
}
}

impl<T> EventLoopProxy<T> {
fn new(sender: Sender<T>) -> EventLoopProxy<T> {
unsafe {
pub fn waker(&self) -> Waker {
fn new_raw_waker() -> RawWaker {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}

// adding a Source to the main CFRunLoop lets us wake it up and
// process user events through the normal OS EventLoop mechanisms.
let rl = CFRunLoopGetMain();
let rl = unsafe { CFRunLoopGetMain() };
let mut context = CFRunLoopSourceContext {
version: 0,
info: ptr::null_mut(),
Expand All @@ -266,25 +236,75 @@ impl<T> EventLoopProxy<T> {
cancel: None,
perform: event_loop_proxy_handler,
};
let source =
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
let source = unsafe {
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context)
};
unsafe { CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes) };
unsafe { CFRunLoopWakeUp(rl) };
RawWaker::new(
source as *const (),
&RawWakerVTable::new(clone_waker, wake, wake_by_ref, drop_waker),
)
}

unsafe fn clone_waker(waker: *const ()) -> RawWaker {
let _source = waker as CFRunLoopSourceRef;
new_raw_waker()
}

unsafe fn wake(waker: *const ()) {
unsafe { wake_by_ref(waker) };
unsafe { drop_waker(waker) };
}

EventLoopProxy { sender, source }
unsafe fn wake_by_ref(waker: *const ()) {
let source = waker as CFRunLoopSourceRef;
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(source);
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);
}
}

unsafe fn drop_waker(waker: *const ()) {
let source = waker as CFRunLoopSourceRef;
unsafe { CFRunLoopSourceInvalidate(source) };
unsafe { CFRelease(source as _) };
}

unsafe { Waker::from_raw(new_raw_waker()) }
}
}

// EventLoopExtIOS
impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom {
UIDevice::current(self.mtm).userInterfaceIdiom().into()
}
}

pub struct EventLoopProxy<T> {
sender: Sender<T>,
waker: Waker,
}

impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
EventLoopProxy::new(self.sender.clone(), self.waker.clone())
}
}

impl<T> EventLoopProxy<T> {
fn new(sender: Sender<T>, waker: Waker) -> Self {
EventLoopProxy { sender, waker }
}

pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender
.send(event)
.map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);
}
.map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
self.waker.wake_by_ref();
Ok(())
}
}
Expand Down
116 changes: 69 additions & 47 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ use std::{
ptr,
rc::{Rc, Weak},
sync::mpsc,
task::{RawWaker, RawWakerVTable, Waker},
time::{Duration, Instant},
};

use core_foundation::base::{CFIndex, CFRelease};
use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal,
CFRunLoopWakeUp,
};
use icrate::AppKit::{
NSApplication, NSApplicationActivationPolicyAccessory, NSApplicationActivationPolicyProhibited,
Expand Down Expand Up @@ -472,7 +474,67 @@ impl<T> EventLoop<T> {
}

pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.sender.clone())
EventLoopProxy::new(self.sender.clone(), self.waker())
}

pub fn waker(&self) -> Waker {
fn new_raw_waker() -> RawWaker {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}

// adding a Source to the main CFRunLoop lets us wake it up and
// process user events through the normal OS EventLoop mechanisms.
let rl = unsafe { CFRunLoopGetMain() };
let mut context = CFRunLoopSourceContext {
version: 0,
info: ptr::null_mut(),
retain: None,
release: None,
copyDescription: None,
equal: None,
hash: None,
schedule: None,
cancel: None,
perform: event_loop_proxy_handler,
};
let source = unsafe {
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context)
};
unsafe { CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes) };
unsafe { CFRunLoopWakeUp(rl) };
RawWaker::new(
source as *const (),
&RawWakerVTable::new(clone_waker, wake, wake_by_ref, drop_waker),
)
}

unsafe fn clone_waker(waker: *const ()) -> RawWaker {
let _source = waker as CFRunLoopSourceRef;
new_raw_waker()
}

unsafe fn wake(waker: *const ()) {
unsafe { wake_by_ref(waker) };
unsafe { drop_waker(waker) };
}

unsafe fn wake_by_ref(waker: *const ()) {
let source = waker as CFRunLoopSourceRef;
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(source);
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);
}
}

unsafe fn drop_waker(waker: *const ()) {
let source = waker as CFRunLoopSourceRef;
unsafe { CFRunLoopSourceInvalidate(source) };
unsafe { CFRelease(source as _) };
}

unsafe { Waker::from_raw(new_raw_waker()) }
}
}

Expand Down Expand Up @@ -532,65 +594,25 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(

pub struct EventLoopProxy<T> {
sender: mpsc::Sender<T>,
source: CFRunLoopSourceRef,
}

unsafe impl<T: Send> Send for EventLoopProxy<T> {}

impl<T> Drop for EventLoopProxy<T> {
fn drop(&mut self) {
unsafe {
CFRelease(self.source as _);
}
}
waker: Waker,
}

impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
EventLoopProxy::new(self.sender.clone())
EventLoopProxy::new(self.sender.clone(), self.waker.clone())
}
}

impl<T> EventLoopProxy<T> {
fn new(sender: mpsc::Sender<T>) -> Self {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}

// adding a Source to the main CFRunLoop lets us wake it up and
// process user events through the normal OS EventLoop mechanisms.
let rl = CFRunLoopGetMain();
let mut context = CFRunLoopSourceContext {
version: 0,
info: ptr::null_mut(),
retain: None,
release: None,
copyDescription: None,
equal: None,
hash: None,
schedule: None,
cancel: None,
perform: event_loop_proxy_handler,
};
let source =
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);

EventLoopProxy { sender, source }
}
fn new(sender: mpsc::Sender<T>, waker: Waker) -> Self {
EventLoopProxy { sender, waker }
}

pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender
.send(event)
.map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);
let rl = CFRunLoopGetMain();
CFRunLoopWakeUp(rl);
}
self.waker.wake_by_ref();
Ok(())
}
}

0 comments on commit 2c6aba9

Please sign in to comment.