diff --git a/winit-common/src/core_foundation/event_loop_proxy.rs b/winit-common/src/core_foundation/event_loop_proxy.rs index 290a557d1..5556ff741 100644 --- a/winit-common/src/core_foundation/event_loop_proxy.rs +++ b/winit-common/src/core_foundation/event_loop_proxy.rs @@ -1,9 +1,12 @@ use std::os::raw::c_void; +use std::ptr::NonNull; use std::sync::Arc; +use std::task::{RawWaker, RawWakerVTable, Waker}; use objc2::MainThreadMarker; use objc2_core_foundation::{ kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoop, CFRunLoopSource, CFRunLoopSourceContext, + Type, }; use winit_core::event_loop::EventLoopProxyProvider; @@ -119,4 +122,54 @@ impl EventLoopProxyProvider for EventLoopProxy { // main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it). self.main_loop.wake_up(); } + + fn waker(&self) -> Waker { + const VTABLE: RawWakerVTable = + RawWakerVTable::new(clone_waker, wake, wake_by_ref, drop_waker); + + unsafe fn clone_waker(data: *const ()) -> RawWaker { + // SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null. + let source = unsafe { &*data.cast::() }; + + // Increment reference count. + let source = source.retain(); + + // Pass ownership to the raw waker. + let data: *const CFRunLoopSource = CFRetained::into_raw(source).as_ptr(); + RawWaker::new(data.cast(), &VTABLE) + } + + unsafe fn wake(data: *const ()) { + unsafe { wake_by_ref(data) }; + unsafe { drop_waker(data) }; + } + + unsafe fn wake_by_ref(data: *const ()) { + // SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null. + let source = unsafe { &*data.cast::() }; + + // Signal the source, which ends up later invoking `perform` on the main thread. + // + // Multiple signals in quick succession are automatically coalesced into a single + // signal. + source.signal(); + + let main_loop = CFRunLoop::main().unwrap(); + // Let the main thread know there's a new event. + // + // This is required since we may be (probably are) running on a different thread, and + // the main loop may be sleeping (and `CFRunLoopSourceSignal` won't wake it). + main_loop.wake_up(); + } + + unsafe fn drop_waker(data: *const ()) { + let source = data.cast::().cast_mut(); + // SAFETY: The poiner came from `CFRunLoopSource` and is valid and non-null. + // We take ownership of a retain count here. + let _source = unsafe { CFRetained::from_raw(NonNull::new_unchecked(source)) }; + } + + let data: *const CFRunLoopSource = CFRetained::into_raw(self.source.clone()).as_ptr(); + unsafe { Waker::from_raw(RawWaker::new(data.cast(), &VTABLE)) } + } } diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index 71307ee7c..60dc3e93f 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -4,6 +4,7 @@ pub mod run_on_demand; use std::fmt::{self, Debug}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; +use std::task::Waker; use std::time::Duration; use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; @@ -150,6 +151,13 @@ impl EventLoopProxy { self.proxy.wake_up(); } + /// Get a [`Waker`] that calls [`wake_up`] on the proxy when awoken. + /// + /// This may be useful to `async` code or otherwise interoperating with `std`. + pub fn waker(&self) -> Waker { + self.proxy.waker() + } + pub fn new(proxy: Arc) -> Self { Self { proxy } } @@ -158,6 +166,9 @@ impl EventLoopProxy { pub trait EventLoopProxyProvider: Send + Sync + Debug { /// See [`EventLoopProxy::wake_up`] for details. fn wake_up(&self); + + /// See [`EventLoopProxy::waker`] for details. + fn waker(&self) -> Waker; } /// A proxy for the underlying display handle. diff --git a/winit-orbital/src/event_loop.rs b/winit-orbital/src/event_loop.rs index 77f1aaeaa..4e5ac7580 100644 --- a/winit-orbital/src/event_loop.rs +++ b/winit-orbital/src/event_loop.rs @@ -2,6 +2,7 @@ use std::cell::Cell; use std::collections::VecDeque; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Arc, Mutex}; +use std::task::Waker; use std::time::Instant; use std::{iter, mem, slice}; @@ -276,7 +277,6 @@ impl EventState { pub struct EventLoop { windows: Vec<(Arc, EventState)>, window_target: ActiveEventLoop, - user_events_receiver: mpsc::Receiver<()>, } impl EventLoop { @@ -577,7 +577,7 @@ impl EventLoop { i += 1; } - while self.user_events_receiver.try_recv().is_ok() { + if self.wake_up.swap(false, Ordering::Relaxed) { app.proxy_wake_up(&self.window_target); } @@ -666,17 +666,26 @@ impl EventLoop { #[derive(Debug)] pub struct EventLoopProxy { - user_events_sender: mpsc::SyncSender<()>, - pub(super) wake_socket: TimeSocket, + waker: Waker, } impl EventLoopProxyProvider for EventLoopProxy { fn wake_up(&self) { - // When we fail to send the event it means that we haven't woken up to read the previous - // event. - if self.user_events_sender.try_send(()).is_ok() { - self.wake_socket.wake().unwrap(); - } + self.waker.wake_by_ref(); + } + + fn into_waker(self) -> Waker { + self.waker + } +} + +impl std::task::Wake for TimeSocket { + fn wake(self: Arc) { + TimeSocket::wake(&*self).unwrap(); + } + + fn wake_by_ref(self: &Arc) { + TimeSocket::wake(&**self).unwrap(); } }