macOS: Call ApplicationHandler directly instead of using Event (#3753)
Additionally, always queue events in `handle_scale_factor_changed`. These events were intentionally not queued before, since they are executed inside `handle_scale_factor_changed`, which is itself queued. Though that's a bit too subtle, so let's just take the minuscule perf hit of redundantly checking whether we need to queue again. Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
10dc0674bb
commit
c0c14aaf00
6 changed files with 184 additions and 103 deletions
|
|
@ -8,13 +8,15 @@ use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
|||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::event::{StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
use super::event_handler::EventHandler;
|
||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||
use super::observer::{EventLoopWaker, RunLoop};
|
||||
use super::{menu, WindowId, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
use super::{menu, WindowId};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct AppState {
|
||||
|
|
@ -166,7 +168,7 @@ impl ApplicationDelegate {
|
|||
/// of the given closure.
|
||||
pub fn set_event_handler<R>(
|
||||
&self,
|
||||
handler: impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop),
|
||||
handler: &mut dyn ApplicationHandler<HandlePendingUserEvents>,
|
||||
closure: impl FnOnce() -> R,
|
||||
) -> R {
|
||||
self.ivars().event_handler.set(handler, closure)
|
||||
|
|
@ -200,7 +202,9 @@ impl ApplicationDelegate {
|
|||
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
|
||||
/// and we won't need to re-launch the app if subsequent EventLoops are run.
|
||||
pub fn internal_exit(&self) {
|
||||
self.handle_event(Event::LoopExiting);
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.exiting(event_loop);
|
||||
});
|
||||
|
||||
self.set_is_running(false);
|
||||
self.set_stop_on_redraw(false);
|
||||
|
|
@ -241,26 +245,13 @@ impl ApplicationDelegate {
|
|||
self.ivars().control_flow.get()
|
||||
}
|
||||
|
||||
pub fn maybe_queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.maybe_queue_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
}
|
||||
|
||||
pub fn handle_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.handle_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
}
|
||||
|
||||
pub fn maybe_queue_device_event(&self, event: DeviceEvent) {
|
||||
self.maybe_queue_event(Event::DeviceEvent { device_id: DEVICE_ID, event });
|
||||
}
|
||||
|
||||
pub fn handle_redraw(&self, window_id: WindowId) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
// Redraw request might come out of order from the OS.
|
||||
// -> Don't go back into the event handler when our callstack originates from there
|
||||
if !self.ivars().event_handler.in_use() {
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
});
|
||||
|
||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
|
||||
|
|
@ -282,7 +273,11 @@ impl ApplicationDelegate {
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
fn maybe_queue_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
pub fn maybe_queue_with_handler(
|
||||
&self,
|
||||
callback: impl FnOnce(&mut dyn ApplicationHandler<HandlePendingUserEvents>, &RootActiveEventLoop)
|
||||
+ 'static,
|
||||
) {
|
||||
// Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.)
|
||||
// result in an event being queued, and applied at a later point.
|
||||
//
|
||||
|
|
@ -290,25 +285,33 @@ impl ApplicationDelegate {
|
|||
// so to make sure that we don't encounter re-entrancy issues, we first check if we're
|
||||
// currently handling another event, and if we are, we queue the event instead.
|
||||
if !self.ivars().event_handler.in_use() {
|
||||
self.handle_event(event);
|
||||
self.with_handler(callback);
|
||||
} else {
|
||||
tracing::debug!(?event, "had to queue event since another is currently being handled");
|
||||
tracing::debug!("had to queue event since another is currently being handled");
|
||||
let this = self.retain();
|
||||
self.ivars().run_loop.queue_closure(move || this.handle_event(event));
|
||||
self.ivars().run_loop.queue_closure(move || {
|
||||
this.with_handler(callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn handle_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
self.ivars().event_handler.handle_event(event, &ActiveEventLoop::new_root(self.retain()))
|
||||
fn with_handler<
|
||||
F: FnOnce(&mut dyn ApplicationHandler<HandlePendingUserEvents>, &RootActiveEventLoop),
|
||||
>(
|
||||
&self,
|
||||
callback: F,
|
||||
) {
|
||||
let event_loop = ActiveEventLoop::new_root(self.retain());
|
||||
self.ivars().event_handler.handle(callback, &event_loop);
|
||||
}
|
||||
|
||||
/// dispatch `NewEvents(Init)` + `Resumed`
|
||||
pub fn dispatch_init_events(&self) {
|
||||
self.handle_event(Event::NewEvents(StartCause::Init));
|
||||
self.with_handler(|app, event_loop| app.new_events(event_loop, StartCause::Init));
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though macOS
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
self.handle_event(Event::Resumed);
|
||||
self.with_handler(|app, event_loop| app.resumed(event_loop));
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver after finishing waiting for new events
|
||||
|
|
@ -341,7 +344,7 @@ impl ApplicationDelegate {
|
|||
},
|
||||
};
|
||||
|
||||
self.handle_event(Event::NewEvents(cause));
|
||||
self.with_handler(|app, event_loop| app.new_events(event_loop, cause));
|
||||
}
|
||||
|
||||
// Called by RunLoopObserver before waiting for new events
|
||||
|
|
@ -358,17 +361,17 @@ impl ApplicationDelegate {
|
|||
return;
|
||||
}
|
||||
|
||||
self.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
self.with_handler(|app, event_loop| app.user_event(event_loop, HandlePendingUserEvents));
|
||||
|
||||
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
|
||||
for window_id in redraw {
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, RootWindowId(window_id), WindowEvent::RedrawRequested);
|
||||
});
|
||||
}
|
||||
|
||||
self.handle_event(Event::AboutToWait);
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.about_to_wait(event_loop);
|
||||
});
|
||||
|
||||
if self.exiting() {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue