diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index 38372f42..539bf6f6 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -5,6 +5,7 @@ use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, N use objc2_foundation::{MainThreadMarker, NSObject}; use super::app_state::ApplicationDelegate; +use super::DEVICE_ID; use crate::event::{DeviceEvent, ElementState}; declare_class!( @@ -57,29 +58,47 @@ fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent) let delta_y = unsafe { event.deltaY() } as f64; if delta_x != 0.0 { - delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 0, value: delta_x }); + delegate.maybe_queue_with_handler(move |app, event_loop| { + app.device_event(event_loop, DEVICE_ID, DeviceEvent::Motion { + axis: 0, + value: delta_x, + }); + }); } if delta_y != 0.0 { - delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 1, value: delta_y }) + delegate.maybe_queue_with_handler(move |app, event_loop| { + app.device_event(event_loop, DEVICE_ID, DeviceEvent::Motion { + axis: 1, + value: delta_y, + }); + }) } if delta_x != 0.0 || delta_y != 0.0 { - delegate.maybe_queue_device_event(DeviceEvent::MouseMotion { - delta: (delta_x, delta_y), + delegate.maybe_queue_with_handler(move |app, event_loop| { + app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseMotion { + delta: (delta_x, delta_y), + }); }); } }, NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => { - delegate.maybe_queue_device_event(DeviceEvent::Button { - button: unsafe { event.buttonNumber() } as u32, - state: ElementState::Pressed, + let button = unsafe { event.buttonNumber() } as u32; + delegate.maybe_queue_with_handler(move |app, event_loop| { + app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button { + button, + state: ElementState::Pressed, + }); }); }, NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => { - delegate.maybe_queue_device_event(DeviceEvent::Button { - button: unsafe { event.buttonNumber() } as u32, - state: ElementState::Released, + let button = unsafe { event.buttonNumber() } as u32; + delegate.maybe_queue_with_handler(move |app, event_loop| { + app.device_event(event_loop, DEVICE_ID, DeviceEvent::Button { + button, + state: ElementState::Released, + }); }); }, _ => (), diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index ee149384..1f0eb2e8 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -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( &self, - handler: impl FnMut(Event, &RootActiveEventLoop), + handler: &mut dyn ApplicationHandler, 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) { + pub fn maybe_queue_with_handler( + &self, + callback: impl FnOnce(&mut dyn ApplicationHandler, &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) { - self.ivars().event_handler.handle_event(event, &ActiveEventLoop::new_root(self.retain())) + fn with_handler< + F: FnOnce(&mut dyn ApplicationHandler, &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); diff --git a/src/platform_impl/macos/event_handler.rs b/src/platform_impl/macos/event_handler.rs index 5c353c14..b38d0589 100644 --- a/src/platform_impl/macos/event_handler.rs +++ b/src/platform_impl/macos/event_handler.rs @@ -2,31 +2,31 @@ use std::cell::RefCell; use std::{fmt, mem}; use super::app_state::HandlePendingUserEvents; -use crate::event::Event; +use crate::application::ApplicationHandler; use crate::event_loop::ActiveEventLoop as RootActiveEventLoop; -struct EventHandlerData { - #[allow(clippy::type_complexity)] - handler: Box, &RootActiveEventLoop) + 'static>, -} - -impl fmt::Debug for EventHandlerData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EventHandlerData").finish_non_exhaustive() - } -} - -#[derive(Debug)] +#[derive(Default)] pub(crate) struct EventHandler { /// This can be in the following states: /// - Not registered by the event loop (None). /// - Present (Some(handler)). /// - Currently executing the handler / in use (RefCell borrowed). - inner: RefCell>, + inner: RefCell>>, +} + +impl fmt::Debug for EventHandler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let state = match self.inner.try_borrow().as_deref() { + Ok(Some(_)) => "", + Ok(None) => "", + Err(_) => "", + }; + f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive() + } } impl EventHandler { - pub(crate) const fn new() -> Self { + pub(crate) fn new() -> Self { Self { inner: RefCell::new(None) } } @@ -37,7 +37,7 @@ impl EventHandler { /// from within the closure. pub(crate) fn set<'handler, R>( &self, - handler: impl FnMut(Event, &RootActiveEventLoop) + 'handler, + app: &'handler mut dyn ApplicationHandler, closure: impl FnOnce() -> R, ) -> R { // SAFETY: We extend the lifetime of the handler here so that we can @@ -48,9 +48,9 @@ impl EventHandler { // extended beyond `'handler`. let handler = unsafe { mem::transmute::< - Box, &RootActiveEventLoop) + 'handler>, - Box, &RootActiveEventLoop) + 'static>, - >(Box::new(handler)) + &'handler mut dyn ApplicationHandler, + &'static mut dyn ApplicationHandler, + >(app) }; match self.inner.try_borrow_mut().as_deref_mut() { @@ -58,7 +58,7 @@ impl EventHandler { unreachable!("tried to set handler while another was already set"); }, Ok(data @ None) => { - *data = Some(EventHandlerData { handler }); + *data = Some(handler); }, Err(_) => { unreachable!("tried to set handler that is currently in use"); @@ -109,20 +109,22 @@ impl EventHandler { matches!(self.inner.try_borrow().as_deref(), Ok(Some(_))) } - pub(crate) fn handle_event( + pub(crate) fn handle< + F: FnOnce(&mut dyn ApplicationHandler, &RootActiveEventLoop), + >( &self, - event: Event, + callback: F, event_loop: &RootActiveEventLoop, ) { match self.inner.try_borrow_mut().as_deref_mut() { - Ok(Some(EventHandlerData { handler })) => { + Ok(Some(user_app)) => { // It is important that we keep the reference borrowed here, // so that `in_use` can properly detect that the handler is // still in use. // // If the handler unwinds, the `RefMut` will ensure that the // handler is no longer borrowed. - (handler)(event, event_loop); + callback(*user_app, event_loop); }, Ok(None) => { // `NSApplication`, our app delegate and this handler are all diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index fe405650..69caaafc 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -27,7 +27,6 @@ use super::monitor::{self, MonitorHandle}; use super::observer::setup_control_flow_observers; use crate::application::ApplicationHandler; use crate::error::EventLoopError; -use crate::event::Event; use crate::event_loop::{ ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents, EventLoopClosed, }; @@ -156,28 +155,78 @@ impl ActiveEventLoop { } } -fn map_user_event>( - app: &mut A, +// Temporary helper, will be removed in https://github.com/rust-windowing/winit/pull/3694 +struct MapUserEvent { + app: A, receiver: Rc>, -) -> impl FnMut(Event, &RootWindowTarget) + '_ { - move |event, window_target| match event { - Event::NewEvents(cause) => app.new_events(window_target, cause), - Event::WindowEvent { window_id, event } => { - app.window_event(window_target, window_id, event) - }, - Event::DeviceEvent { device_id, event } => { - app.device_event(window_target, device_id, event) - }, - Event::UserEvent(_) => { - for event in receiver.try_iter() { - app.user_event(window_target, event); - } - }, - Event::Suspended => app.suspended(window_target), - Event::Resumed => app.resumed(window_target), - Event::AboutToWait => app.about_to_wait(window_target), - Event::LoopExiting => app.exiting(window_target), - Event::MemoryWarning => app.memory_warning(window_target), +} + +impl, T: 'static> ApplicationHandler + for MapUserEvent +{ + #[inline] + fn new_events( + &mut self, + event_loop: &crate::event_loop::ActiveEventLoop, + cause: crate::event::StartCause, + ) { + self.app.new_events(event_loop, cause); + } + + #[inline] + fn resumed(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { + self.app.resumed(event_loop); + } + + #[inline] + fn user_event( + &mut self, + event_loop: &crate::event_loop::ActiveEventLoop, + _event: HandlePendingUserEvents, + ) { + for event in self.receiver.try_iter() { + self.app.user_event(event_loop, event); + } + } + + #[inline] + fn window_event( + &mut self, + event_loop: &crate::event_loop::ActiveEventLoop, + window_id: crate::window::WindowId, + event: crate::event::WindowEvent, + ) { + self.app.window_event(event_loop, window_id, event); + } + + #[inline] + fn device_event( + &mut self, + event_loop: &crate::event_loop::ActiveEventLoop, + device_id: crate::event::DeviceId, + event: crate::event::DeviceEvent, + ) { + self.app.device_event(event_loop, device_id, event); + } + + #[inline] + fn about_to_wait(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { + self.app.about_to_wait(event_loop); + } + + #[inline] + fn suspended(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { + self.app.suspended(event_loop); + } + + #[inline] + fn exiting(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { + self.app.exiting(event_loop); + } + + #[inline] + fn memory_warning(&mut self, event_loop: &crate::event_loop::ActiveEventLoop) { + self.app.memory_warning(event_loop); } } @@ -284,9 +333,9 @@ impl EventLoop { &mut self, app: &mut A, ) -> Result<(), EventLoopError> { - let handler = map_user_event(app, self.receiver.clone()); + let mut handler = MapUserEvent { app, receiver: self.receiver.clone() }; - self.delegate.set_event_handler(handler, || { + self.delegate.set_event_handler(&mut handler, || { autoreleasepool(|_| { // clear / normalize pump_events state self.delegate.set_wait_timeout(None); @@ -324,9 +373,9 @@ impl EventLoop { timeout: Option, app: &mut A, ) -> PumpStatus { - let handler = map_user_event(app, self.receiver.clone()); + let mut handler = MapUserEvent { app, receiver: self.receiver.clone() }; - self.delegate.set_event_handler(handler, || { + self.delegate.set_event_handler(&mut handler, || { autoreleasepool(|_| { // As a special case, if the application hasn't been launched yet then we at least // run the loop until it has fully launched. diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 50cffcee..a5b048ea 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -31,6 +31,7 @@ use crate::event::{ }; use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey}; use crate::platform::macos::OptionAsAlt; +use crate::window::WindowId as RootWindowId; #[derive(Debug)] struct CursorState { @@ -686,7 +687,9 @@ declare_class!( self.update_modifiers(event, false); - self.ivars().app_delegate.maybe_queue_device_event(DeviceEvent::MouseWheel { delta }); + self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop| + app.device_event(event_loop, DEVICE_ID, DeviceEvent::MouseWheel { delta }) + ); self.queue_event(WindowEvent::MouseWheel { device_id: DEVICE_ID, delta, @@ -830,7 +833,10 @@ impl WinitView { } fn queue_event(&self, event: WindowEvent) { - self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event); + let window_id = RootWindowId(self.window().id()); + self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop| { + app.window_event(event_loop, window_id, event); + }); } fn scale_factor(&self) -> f64 { diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 674088cf..c50af9b6 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -39,7 +39,7 @@ use crate::event::{InnerSizeWriter, WindowEvent}; use crate::platform::macos::{OptionAsAlt, WindowExtMacOS}; use crate::window::{ Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType, - WindowAttributes, WindowButtons, WindowLevel, + WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel, }; #[derive(Clone, Debug)] @@ -807,11 +807,13 @@ impl WindowDelegate { } pub(crate) fn queue_event(&self, event: WindowEvent) { - self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event); + let window_id = RootWindowId(self.window().id()); + self.ivars().app_delegate.maybe_queue_with_handler(move |app, event_loop| { + app.window_event(event_loop, window_id, event); + }); } fn handle_scale_factor_changed(&self, scale_factor: CGFloat) { - let app_delegate = &self.ivars().app_delegate; let window = self.window(); let content_size = window.contentRectForFrameRect(window.frame()).size; @@ -819,7 +821,7 @@ impl WindowDelegate { let suggested_size = content_size.to_physical(scale_factor); let new_inner_size = Arc::new(Mutex::new(suggested_size)); - app_delegate.handle_window_event(window.id(), WindowEvent::ScaleFactorChanged { + self.queue_event(WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)), }); @@ -831,7 +833,7 @@ impl WindowDelegate { let size = NSSize::new(logical_size.width, logical_size.height); window.setContentSize(size); } - app_delegate.handle_window_event(window.id(), WindowEvent::Resized(physical_size)); + self.queue_event(WindowEvent::Resized(physical_size)); } fn emit_move_event(&self) {