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
|
|
@ -1,7 +1,6 @@
|
||||||
use std::cell::{Cell, OnceCell, RefCell};
|
use std::cell::{Cell, OnceCell, RefCell};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::sync::atomic::Ordering as AtomicOrdering;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
|
@ -11,7 +10,8 @@ use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningAppli
|
||||||
use objc2_foundation::NSNotification;
|
use objc2_foundation::NSNotification;
|
||||||
|
|
||||||
use super::super::event_handler::EventHandler;
|
use super::super::event_handler::EventHandler;
|
||||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, EventLoopProxy, PanicInfo};
|
use super::super::event_loop_proxy::EventLoopProxy;
|
||||||
|
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||||
use super::menu;
|
use super::menu;
|
||||||
use super::observer::{EventLoopWaker, RunLoop};
|
use super::observer::{EventLoopWaker, RunLoop};
|
||||||
use crate::application::ApplicationHandler;
|
use crate::application::ApplicationHandler;
|
||||||
|
|
@ -59,13 +59,17 @@ impl AppState {
|
||||||
default_menu: bool,
|
default_menu: bool,
|
||||||
activate_ignoring_other_apps: bool,
|
activate_ignoring_other_apps: bool,
|
||||||
) -> Rc<Self> {
|
) -> Rc<Self> {
|
||||||
let this = Rc::new(AppState {
|
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
|
||||||
|
Self::get(mtm).with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
||||||
|
}));
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
mtm,
|
mtm,
|
||||||
activation_policy,
|
activation_policy,
|
||||||
event_loop_proxy: Arc::new(EventLoopProxy::new()),
|
|
||||||
default_menu,
|
default_menu,
|
||||||
activate_ignoring_other_apps,
|
activate_ignoring_other_apps,
|
||||||
run_loop: RunLoop::main(mtm),
|
run_loop: RunLoop::main(mtm),
|
||||||
|
event_loop_proxy,
|
||||||
event_handler: EventHandler::new(),
|
event_handler: EventHandler::new(),
|
||||||
stop_on_launch: Cell::new(false),
|
stop_on_launch: Cell::new(false),
|
||||||
stop_before_wait: Cell::new(false),
|
stop_before_wait: Cell::new(false),
|
||||||
|
|
@ -351,10 +355,6 @@ impl AppState {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.event_loop_proxy.wake_up.swap(false, AtomicOrdering::Relaxed) {
|
|
||||||
self.with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
|
||||||
}
|
|
||||||
|
|
||||||
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
|
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
|
||||||
for window_id in redraw {
|
for window_id in redraw {
|
||||||
self.with_handler(|app, event_loop| {
|
self.with_handler(|app, event_loop| {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::os::raw::c_void;
|
|
||||||
use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
|
use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
|
||||||
use std::ptr;
|
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
|
@ -15,11 +12,6 @@ use objc2_app_kit::{
|
||||||
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
|
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
|
||||||
NSApplicationWillTerminateNotification, NSWindow,
|
NSApplicationWillTerminateNotification, NSWindow,
|
||||||
};
|
};
|
||||||
use objc2_core_foundation::{
|
|
||||||
kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoopAddSource, CFRunLoopGetMain,
|
|
||||||
CFRunLoopSource, CFRunLoopSourceContext, CFRunLoopSourceCreate, CFRunLoopSourceSignal,
|
|
||||||
CFRunLoopWakeUp,
|
|
||||||
};
|
|
||||||
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
||||||
use rwh_06::HasDisplayHandle;
|
use rwh_06::HasDisplayHandle;
|
||||||
|
|
||||||
|
|
@ -34,8 +26,7 @@ use crate::application::ApplicationHandler;
|
||||||
use crate::error::{EventLoopError, RequestError};
|
use crate::error::{EventLoopError, RequestError};
|
||||||
use crate::event_loop::{
|
use crate::event_loop::{
|
||||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
EventLoopProxy as CoreEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
|
||||||
};
|
};
|
||||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||||
use crate::platform::macos::ActivationPolicy;
|
use crate::platform::macos::ActivationPolicy;
|
||||||
|
|
@ -429,54 +420,3 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EventLoopProxy {
|
|
||||||
pub(crate) wake_up: AtomicBool,
|
|
||||||
source: CFRetained<CFRunLoopSource>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for EventLoopProxy {}
|
|
||||||
unsafe impl Sync for EventLoopProxy {}
|
|
||||||
|
|
||||||
impl EventLoopProxy {
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
unsafe {
|
|
||||||
// just wake up the eventloop
|
|
||||||
extern "C-unwind" fn event_loop_proxy_handler(_context: *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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
126
src/platform_impl/apple/event_loop_proxy.rs
Normal file
126
src/platform_impl/apple/event_loop_proxy.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use objc2::MainThreadMarker;
|
||||||
|
use objc2_core_foundation::{
|
||||||
|
kCFRunLoopCommonModes, CFIndex, CFRetained, CFRunLoop, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||||
|
CFRunLoopSource, CFRunLoopSourceContext, CFRunLoopSourceCreate, CFRunLoopSourceInvalidate,
|
||||||
|
CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::event_loop::EventLoopProxyProvider;
|
||||||
|
|
||||||
|
/// A waker that signals a `CFRunLoopSource` on the main thread.
|
||||||
|
///
|
||||||
|
/// We use this to integrate with the system as cleanly as possible (instead of e.g. keeping an
|
||||||
|
/// atomic around that we check on each iteration of the event loop).
|
||||||
|
///
|
||||||
|
/// See <https://developer.apple.com/documentation/corefoundation/cfrunloopsource?language=objc>.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) struct EventLoopProxy {
|
||||||
|
source: CFRetained<CFRunLoopSource>,
|
||||||
|
/// Cached value of `CFRunLoopGetMain`.
|
||||||
|
main_loop: CFRetained<CFRunLoop>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(madsmtm): Mark `CFRunLoopSource` + `CFRunLoop` as `Send` + `Sync`.
|
||||||
|
unsafe impl Send for EventLoopProxy {}
|
||||||
|
unsafe impl Sync for EventLoopProxy {}
|
||||||
|
|
||||||
|
impl EventLoopProxy {
|
||||||
|
/// Create a new proxy, registering it to be performed on the main thread.
|
||||||
|
///
|
||||||
|
/// The provided closure should call `proxy_wake_up` on the application.
|
||||||
|
pub(crate) fn new<F: Fn() + 'static>(mtm: MainThreadMarker, signaller: F) -> Self {
|
||||||
|
// We use an `Arc` here to make sure that the reference-counting of the signal container is
|
||||||
|
// atomic (`Retained`/`CFRetained` would be valid alternatives too).
|
||||||
|
let signaller = Arc::new(signaller);
|
||||||
|
|
||||||
|
unsafe extern "C-unwind" fn retain<F>(info: *const c_void) -> *const c_void {
|
||||||
|
// SAFETY: The pointer was passed to `CFRunLoopSourceContext.info` below.
|
||||||
|
unsafe { Arc::increment_strong_count(info.cast::<F>()) };
|
||||||
|
info
|
||||||
|
}
|
||||||
|
unsafe extern "C-unwind" fn release<F>(info: *const c_void) {
|
||||||
|
// SAFETY: The pointer was passed to `CFRunLoopSourceContext.info` below.
|
||||||
|
unsafe { Arc::decrement_strong_count(info.cast::<F>()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointer equality / hashing.
|
||||||
|
extern "C-unwind" fn equal(info1: *const c_void, info2: *const c_void) -> u8 {
|
||||||
|
(info1 == info2) as u8
|
||||||
|
}
|
||||||
|
extern "C-unwind" fn hash(info: *const c_void) -> usize {
|
||||||
|
info as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the provided closure.
|
||||||
|
unsafe extern "C-unwind" fn perform<F: Fn()>(info: *mut c_void) {
|
||||||
|
// SAFETY: The pointer was passed to `CFRunLoopSourceContext.info` below.
|
||||||
|
let signaller = unsafe { &*info.cast::<F>() };
|
||||||
|
(signaller)();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire last.
|
||||||
|
let order = CFIndex::MAX - 1;
|
||||||
|
|
||||||
|
// This is marked `mut` to match the signature of `CFRunLoopSourceCreate`, but the
|
||||||
|
// information is copied, and not actually mutated.
|
||||||
|
let mut context = CFRunLoopSourceContext {
|
||||||
|
version: 0,
|
||||||
|
// This is retained on creation.
|
||||||
|
info: Arc::as_ptr(&signaller) as *mut c_void,
|
||||||
|
retain: Some(retain::<F>),
|
||||||
|
release: Some(release::<F>),
|
||||||
|
copyDescription: None,
|
||||||
|
equal: Some(equal),
|
||||||
|
hash: Some(hash),
|
||||||
|
schedule: None,
|
||||||
|
cancel: None,
|
||||||
|
perform: Some(perform::<F>),
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: The normal callbacks are thread-safe (`retain`/`release` use atomics, and
|
||||||
|
// `equal`/`hash` only access a pointer).
|
||||||
|
//
|
||||||
|
// Note that the `perform` callback isn't thread-safe (we don't have `F: Send + Sync`), but
|
||||||
|
// that's okay, since we are on the main thread, and the source is only added to the main
|
||||||
|
// run loop (below), and hence only performed there.
|
||||||
|
//
|
||||||
|
// Keeping the closure alive beyond this scope is fine, because `F: 'static`.
|
||||||
|
let source = unsafe {
|
||||||
|
let _ = mtm;
|
||||||
|
CFRunLoopSourceCreate(None, order, &mut context).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the source to be performed on the main thread.
|
||||||
|
let main_loop = unsafe { CFRunLoopGetMain() }.unwrap();
|
||||||
|
unsafe { CFRunLoopAddSource(&main_loop, Some(&source), kCFRunLoopCommonModes) };
|
||||||
|
|
||||||
|
Self { source, main_loop }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(madsmtm): Use this on macOS too.
|
||||||
|
// More difficult there, since the user can re-start the event loop.
|
||||||
|
#[cfg_attr(target_os = "macos", allow(dead_code))]
|
||||||
|
pub(crate) fn invalidate(&self) {
|
||||||
|
// NOTE: We do NOT fire this on `Drop`, since we want the proxy to be cloneable, such that
|
||||||
|
// we only need to register a single source even if there's multiple proxies in use.
|
||||||
|
unsafe { CFRunLoopSourceInvalidate(&self.source) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventLoopProxyProvider for EventLoopProxy {
|
||||||
|
fn wake_up(&self) {
|
||||||
|
// 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.
|
||||||
|
unsafe { CFRunLoopSourceSignal(&self.source) };
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
unsafe { CFRunLoopWakeUp(&self.main_loop) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod appkit;
|
mod appkit;
|
||||||
mod event_handler;
|
mod event_handler;
|
||||||
|
mod event_loop_proxy;
|
||||||
mod notification_center;
|
mod notification_center;
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
mod uikit;
|
mod uikit;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
use std::cell::{OnceCell, RefCell, RefMut};
|
use std::cell::{OnceCell, RefCell, RefMut};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
@ -19,8 +18,9 @@ use objc2_core_foundation::{
|
||||||
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView};
|
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView};
|
||||||
|
|
||||||
use super::super::event_handler::EventHandler;
|
use super::super::event_handler::EventHandler;
|
||||||
|
use super::super::event_loop_proxy::EventLoopProxy;
|
||||||
use super::window::WinitUIWindow;
|
use super::window::WinitUIWindow;
|
||||||
use super::{ActiveEventLoop, EventLoopProxy};
|
use super::ActiveEventLoop;
|
||||||
use crate::application::ApplicationHandler;
|
use crate::application::ApplicationHandler;
|
||||||
use crate::dpi::PhysicalSize;
|
use crate::dpi::PhysicalSize;
|
||||||
use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent};
|
use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent};
|
||||||
|
|
@ -102,7 +102,7 @@ pub(crate) struct AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
// basically everything in UIKit requires the main thread, so it's pointless to use the
|
||||||
// std::sync APIs.
|
// std::sync APIs.
|
||||||
// must be mut because plain `static` requires `Sync`
|
// must be mut because plain `static` requires `Sync`
|
||||||
|
|
@ -114,17 +114,21 @@ impl AppState {
|
||||||
if guard.is_none() {
|
if guard.is_none() {
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[cold]
|
#[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 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 {
|
**guard = Some(AppState {
|
||||||
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
|
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
|
||||||
control_flow: ControlFlow::default(),
|
control_flow: ControlFlow::default(),
|
||||||
waker,
|
waker,
|
||||||
event_loop_proxy: Arc::new(EventLoopProxy::new()),
|
event_loop_proxy,
|
||||||
queued_events: Vec::new(),
|
queued_events: Vec::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
init_guard(&mut guard);
|
init_guard(&mut guard, mtm);
|
||||||
}
|
}
|
||||||
RefMut::map(guard, |state| state.as_mut().unwrap())
|
RefMut::map(guard, |state| state.as_mut().unwrap())
|
||||||
}
|
}
|
||||||
|
|
@ -394,13 +398,8 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||||
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
|
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
|
||||||
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
||||||
}
|
}
|
||||||
let event_loop_proxy = this.event_loop_proxy().clone();
|
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
|
|
||||||
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
let queued_events = mem::take(&mut this.queued_events);
|
let queued_events = mem::take(&mut this.queued_events);
|
||||||
|
|
@ -412,10 +411,6 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||||
for event in queued_events {
|
for event in queued_events {
|
||||||
handle_wrapped_event(mtm, event);
|
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);
|
drop(this);
|
||||||
|
|
||||||
get_handler(mtm).handle(|app| app.exiting(&ActiveEventLoop { mtm }));
|
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) {
|
fn handle_wrapped_event(mtm: MainThreadMarker, event: EventWrapper) {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
use std::ffi::{c_char, c_int, c_void};
|
use std::ffi::{c_char, c_int, c_void};
|
||||||
use std::ptr::{self, NonNull};
|
use std::ptr::{self, NonNull};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use objc2::rc::Retained;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::ProtocolObject;
|
use objc2::runtime::ProtocolObject;
|
||||||
use objc2::{msg_send, ClassType, MainThreadMarker};
|
use objc2::{msg_send, ClassType, MainThreadMarker};
|
||||||
use objc2_core_foundation::{
|
use objc2_core_foundation::{
|
||||||
kCFRunLoopCommonModes, kCFRunLoopDefaultMode, CFIndex, CFRetained, CFRunLoopActivity,
|
kCFRunLoopDefaultMode, CFIndex, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopGetMain,
|
||||||
CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopObserver,
|
CFRunLoopObserver, CFRunLoopObserverCreate,
|
||||||
CFRunLoopObserverCreate, CFRunLoopSource, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
|
||||||
CFRunLoopSourceInvalidate, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
|
||||||
};
|
};
|
||||||
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
||||||
use objc2_ui_kit::{
|
use objc2_ui_kit::{
|
||||||
|
|
@ -29,8 +26,7 @@ use crate::application::ApplicationHandler;
|
||||||
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
use crate::error::{EventLoopError, NotSupportedError, RequestError};
|
||||||
use crate::event_loop::{
|
use crate::event_loop::{
|
||||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
EventLoopProxy as CoreEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
|
||||||
};
|
};
|
||||||
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
use crate::monitor::MonitorHandle as RootMonitorHandle;
|
||||||
use crate::platform_impl::Window;
|
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() {
|
fn setup_control_flow_observers() {
|
||||||
unsafe {
|
unsafe {
|
||||||
// begin is queued with the highest priority to ensure it is processed before other
|
// begin is queued with the highest priority to ensure it is processed before other
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ mod window;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub(crate) use self::event_loop::{
|
pub(crate) use self::event_loop::{
|
||||||
ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes,
|
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
|
||||||
};
|
};
|
||||||
pub(crate) use self::monitor::MonitorHandle;
|
pub(crate) use self::monitor::MonitorHandle;
|
||||||
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
|
pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue