apple: Implement wake-ups using system mechanisms (#4146)
Do the wake-up inside the perform callback on `CFRunLoopSource`.
This commit is contained in:
parent
aa8ebdc795
commit
39c0862198
7 changed files with 154 additions and 148 deletions
|
|
@ -3,7 +3,6 @@
|
|||
use std::cell::{OnceCell, RefCell, RefMut};
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
use std::{mem, ptr};
|
||||
|
|
@ -19,8 +18,9 @@ use objc2_core_foundation::{
|
|||
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView};
|
||||
|
||||
use super::super::event_handler::EventHandler;
|
||||
use super::super::event_loop_proxy::EventLoopProxy;
|
||||
use super::window::WinitUIWindow;
|
||||
use super::{ActiveEventLoop, EventLoopProxy};
|
||||
use super::ActiveEventLoop;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent};
|
||||
|
|
@ -102,7 +102,7 @@ pub(crate) struct AppState {
|
|||
}
|
||||
|
||||
impl AppState {
|
||||
pub(crate) fn get_mut(_mtm: MainThreadMarker) -> RefMut<'static, AppState> {
|
||||
pub(crate) fn get_mut(mtm: MainThreadMarker) -> RefMut<'static, AppState> {
|
||||
// basically everything in UIKit requires the main thread, so it's pointless to use the
|
||||
// std::sync APIs.
|
||||
// must be mut because plain `static` requires `Sync`
|
||||
|
|
@ -114,17 +114,21 @@ impl AppState {
|
|||
if guard.is_none() {
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
||||
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>, mtm: MainThreadMarker) {
|
||||
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain().unwrap() });
|
||||
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
|
||||
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
||||
}));
|
||||
|
||||
**guard = Some(AppState {
|
||||
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
|
||||
control_flow: ControlFlow::default(),
|
||||
waker,
|
||||
event_loop_proxy: Arc::new(EventLoopProxy::new()),
|
||||
event_loop_proxy,
|
||||
queued_events: Vec::new(),
|
||||
});
|
||||
}
|
||||
init_guard(&mut guard);
|
||||
init_guard(&mut guard, mtm);
|
||||
}
|
||||
RefMut::map(guard, |state| state.as_mut().unwrap())
|
||||
}
|
||||
|
|
@ -394,13 +398,8 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
|||
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
|
||||
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
||||
}
|
||||
let event_loop_proxy = this.event_loop_proxy().clone();
|
||||
drop(this);
|
||||
|
||||
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
|
||||
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let queued_events = mem::take(&mut this.queued_events);
|
||||
|
|
@ -412,10 +411,6 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
|||
for event in queued_events {
|
||||
handle_wrapped_event(mtm, event);
|
||||
}
|
||||
|
||||
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
|
||||
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -503,6 +498,10 @@ pub(crate) fn terminated(application: &UIApplication) {
|
|||
drop(this);
|
||||
|
||||
get_handler(mtm).handle(|app| app.exiting(&ActiveEventLoop { mtm }));
|
||||
|
||||
let this = AppState::get_mut(mtm);
|
||||
// Prevent EventLoopProxy from firing again.
|
||||
this.event_loop_proxy.invalidate();
|
||||
}
|
||||
|
||||
fn handle_wrapped_event(mtm: MainThreadMarker, event: EventWrapper) {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
use std::ffi::{c_char, c_int, c_void};
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::ProtocolObject;
|
||||
use objc2::{msg_send, ClassType, MainThreadMarker};
|
||||
use objc2_core_foundation::{
|
||||
kCFRunLoopCommonModes, kCFRunLoopDefaultMode, CFIndex, CFRetained, CFRunLoopActivity,
|
||||
CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopObserver,
|
||||
CFRunLoopObserverCreate, CFRunLoopSource, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
||||
CFRunLoopSourceInvalidate, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
kCFRunLoopDefaultMode, CFIndex, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopGetMain,
|
||||
CFRunLoopObserver, CFRunLoopObserverCreate,
|
||||
};
|
||||
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
||||
use objc2_ui_kit::{
|
||||
|
|
@ -29,8 +26,7 @@ use crate::application::ApplicationHandler;
|
|||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
EventLoopProxy as CoreEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||
use crate::platform_impl::Window;
|
||||
|
|
@ -277,62 +273,6 @@ impl EventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopProxy {
|
||||
pub(crate) wake_up: AtomicBool,
|
||||
source: CFRetained<CFRunLoopSource>,
|
||||
}
|
||||
|
||||
unsafe impl Send for EventLoopProxy {}
|
||||
unsafe impl Sync for EventLoopProxy {}
|
||||
|
||||
impl Drop for EventLoopProxy {
|
||||
fn drop(&mut self) {
|
||||
unsafe { CFRunLoopSourceInvalidate(&self.source) };
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub(crate) fn new() -> EventLoopProxy {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C-unwind" fn event_loop_proxy_handler(_: *mut 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().unwrap();
|
||||
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: Some(event_loop_proxy_handler),
|
||||
};
|
||||
let source = CFRunLoopSourceCreate(None, CFIndex::MAX - 1, &mut context).unwrap();
|
||||
CFRunLoopAddSource(&rl, Some(&source), kCFRunLoopCommonModes);
|
||||
CFRunLoopWakeUp(&rl);
|
||||
|
||||
EventLoopProxy { wake_up: AtomicBool::new(false), source }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
self.wake_up.store(true, AtomicOrdering::Relaxed);
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(&self.source);
|
||||
let rl = CFRunLoopGetMain().unwrap();
|
||||
CFRunLoopWakeUp(&rl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_control_flow_observers() {
|
||||
unsafe {
|
||||
// begin is queued with the highest priority to ensure it is processed before other
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ mod window;
|
|||
use std::fmt;
|
||||
|
||||
pub(crate) use self::event_loop::{
|
||||
ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes,
|
||||
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
|
||||
};
|
||||
pub(crate) use self::monitor::MonitorHandle;
|
||||
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue