From 0f2fbe373bfc579ce286a8723cd46f87d2a13bcd Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 22 Jan 2023 23:29:38 +0100 Subject: [PATCH] Simplify event queuing on macOS (#2642) --- src/platform_impl/macos/app.rs | 75 ++--- src/platform_impl/macos/app_state.rs | 7 - src/platform_impl/macos/view.rs | 301 +++++++-------------- src/platform_impl/macos/window_delegate.rs | 30 +- 4 files changed, 143 insertions(+), 270 deletions(-) diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index 4ca5de4e..864cabd3 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -1,7 +1,5 @@ #![allow(clippy::unnecessary_cast)] -use std::collections::VecDeque; - use objc2::foundation::NSObject; use objc2::{declare_class, msg_send, ClassType}; @@ -50,70 +48,51 @@ fn maybe_dispatch_device_event(event: &NSEvent) { | NSEventType::NSLeftMouseDragged | NSEventType::NSOtherMouseDragged | NSEventType::NSRightMouseDragged => { - let mut events = VecDeque::with_capacity(3); - let delta_x = event.deltaX() as f64; let delta_y = event.deltaY() as f64; if delta_x != 0.0 { - events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::Motion { - axis: 0, - value: delta_x, - }, - })); + queue_device_event(DeviceEvent::Motion { + axis: 0, + value: delta_x, + }); } if delta_y != 0.0 { - events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::Motion { - axis: 1, - value: delta_y, - }, - })); + queue_device_event(DeviceEvent::Motion { + axis: 1, + value: delta_y, + }) } if delta_x != 0.0 || delta_y != 0.0 { - events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::MouseMotion { - delta: (delta_x, delta_y), - }, - })); + queue_device_event(DeviceEvent::MouseMotion { + delta: (delta_x, delta_y), + }); } - - AppState::queue_events(events); } NSEventType::NSLeftMouseDown | NSEventType::NSRightMouseDown | NSEventType::NSOtherMouseDown => { - let mut events = VecDeque::with_capacity(1); - - events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::Button { - button: event.buttonNumber() as u32, - state: ElementState::Pressed, - }, - })); - - AppState::queue_events(events); + queue_device_event(DeviceEvent::Button { + button: event.buttonNumber() as u32, + state: ElementState::Pressed, + }); } NSEventType::NSLeftMouseUp | NSEventType::NSRightMouseUp | NSEventType::NSOtherMouseUp => { - let mut events = VecDeque::with_capacity(1); - - events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::Button { - button: event.buttonNumber() as u32, - state: ElementState::Released, - }, - })); - - AppState::queue_events(events); + queue_device_event(DeviceEvent::Button { + button: event.buttonNumber() as u32, + state: ElementState::Released, + }); } _ => (), } } + +fn queue_device_event(event: DeviceEvent) { + let event = Event::DeviceEvent { + device_id: DEVICE_ID, + event, + }; + AppState::queue_event(EventWrapper::StaticEvent(event)); +} diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 834ca759..299ee790 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -368,13 +368,6 @@ impl AppState { HANDLER.events().push_back(wrapper); } - pub fn queue_events(mut wrappers: VecDeque) { - if !is_main_thread() { - panic!("Events queued from different thread: {:#?}", wrappers); - } - HANDLER.events().append(&mut wrappers); - } - pub fn cleared(panic_info: Weak) { let panic_info = panic_info .upgrade() diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 4621db57..c51f049f 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -1,6 +1,6 @@ #![allow(clippy::unnecessary_cast)] -use std::{boxed::Box, collections::VecDeque, os::raw::*, ptr, str, sync::Mutex}; +use std::{boxed::Box, os::raw::*, ptr, str, sync::Mutex}; use objc2::declare::{Ivar, IvarDrop}; use objc2::foundation::{ @@ -224,10 +224,7 @@ declare_class!( // 2. Even when a window resize does occur on a new tabbed window, it contains the wrong size (includes tab height). let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64); let size = logical_size.to_physical::(self.scale_factor()); - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::Resized(size), - })); + self.queue_event(WindowEvent::Resized(size)); } #[sel(drawRect:)] @@ -329,10 +326,7 @@ declare_class!( // Notify IME is active if application still doesn't know it. if self.state.ime_state == ImeState::Disabled { self.state.input_source = self.current_input_source(); - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::Ime(Ime::Enabled), - })); + self.queue_event(WindowEvent::Ime(Ime::Enabled)); } // Don't update self.state to preedit when we've just commited a string, since the following @@ -350,10 +344,7 @@ declare_class!( }; // Send WindowEvent for updating marked text - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::Ime(Ime::Preedit(preedit_string, cursor_range)), - })); + self.queue_event(WindowEvent::Ime(Ime::Preedit(preedit_string, cursor_range))); } #[sel(unmarkText)] @@ -364,10 +355,7 @@ declare_class!( let input_context = self.inputContext().expect("input context"); input_context.discardMarkedText(); - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), - })); + self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None))); if self.is_ime_enabled() { // Leave the Preedit self.state self.state.ime_state = ImeState::Enabled; @@ -436,14 +424,8 @@ declare_class!( let is_control = string.chars().next().map_or(false, |c| c.is_control()); if self.is_ime_enabled() && !is_control { - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), - })); - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::Ime(Ime::Commit(string)), - })); + self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None))); + self.queue_event(WindowEvent::Ime(Ime::Commit(string))); self.state.ime_state = ImeState::Commited; } } @@ -473,16 +455,11 @@ declare_class!( #[sel(keyDown:)] fn key_down(&mut self, event: &NSEvent) { trace_scope!("keyDown:"); - let window_id = self.window_id(); - let input_source = self.current_input_source(); if self.state.input_source != input_source && self.is_ime_enabled() { self.state.ime_state = ImeState::Disabled; self.state.input_source = input_source; - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id, - event: WindowEvent::Ime(Ime::Disabled), - })); + self.queue_event(WindowEvent::Ime(Ime::Disabled)); } let was_in_preedit = self.state.ime_state == ImeState::Preedit; @@ -518,27 +495,19 @@ declare_class!( if !ime_related || self.state.forward_key_to_app || !self.state.ime_allowed { #[allow(deprecated)] - let window_event = Event::WindowEvent { - window_id, - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: ElementState::Pressed, - scancode, - virtual_keycode, - modifiers: event_mods(event), - }, - is_synthetic: false, + self.queue_event(WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + state: ElementState::Pressed, + scancode, + virtual_keycode, + modifiers: event_mods(event), }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + is_synthetic: false, + }); for character in characters.chars().filter(|c| !is_corporate_character(*c)) { - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id, - event: WindowEvent::ReceivedCharacter(character), - })); + self.queue_event(WindowEvent::ReceivedCharacter(character)); } } } @@ -554,21 +523,16 @@ declare_class!( // We want to send keyboard input when we are not currently in preedit if self.state.ime_state != ImeState::Preedit { #[allow(deprecated)] - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: ElementState::Released, - scancode, - virtual_keycode, - modifiers: event_mods(event), - }, - is_synthetic: false, + self.queue_event(WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + state: ElementState::Released, + scancode, + virtual_keycode, + modifiers: event_mods(event), }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + is_synthetic: false, + }); } } @@ -576,15 +540,13 @@ declare_class!( fn flags_changed(&mut self, event: &NSEvent) { trace_scope!("flagsChanged:"); - let mut events = VecDeque::with_capacity(4); - if let Some(window_event) = modifier_event( event, NSEventModifierFlags::NSShiftKeyMask, self.state.modifiers.shift(), ) { self.state.modifiers.toggle(ModifiersState::SHIFT); - events.push_back(window_event); + self.queue_event(window_event); } if let Some(window_event) = modifier_event( @@ -593,7 +555,7 @@ declare_class!( self.state.modifiers.ctrl(), ) { self.state.modifiers.toggle(ModifiersState::CTRL); - events.push_back(window_event); + self.queue_event(window_event); } if let Some(window_event) = modifier_event( @@ -602,7 +564,7 @@ declare_class!( self.state.modifiers.logo(), ) { self.state.modifiers.toggle(ModifiersState::LOGO); - events.push_back(window_event); + self.queue_event(window_event); } if let Some(window_event) = modifier_event( @@ -611,22 +573,10 @@ declare_class!( self.state.modifiers.alt(), ) { self.state.modifiers.toggle(ModifiersState::ALT); - events.push_back(window_event); + self.queue_event(window_event); } - let window_id = self.window_id(); - - for event in events { - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id, - event, - })); - } - - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged(self.state.modifiers), - })); + self.queue_event(WindowEvent::ModifiersChanged(self.state.modifiers)); } #[sel(insertTab:)] @@ -667,21 +617,16 @@ declare_class!( self.update_potentially_stale_modifiers(&event); #[allow(deprecated)] - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: ElementState::Pressed, - scancode: scancode as _, - virtual_keycode, - modifiers: event_mods(&event), - }, - is_synthetic: false, + self.queue_event(WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + state: ElementState::Pressed, + scancode: scancode as _, + virtual_keycode, + modifiers: event_mods(&event), }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + is_synthetic: false, + }); } #[sel(mouseDown:)] @@ -751,27 +696,18 @@ declare_class!( #[sel(mouseEntered:)] fn mouse_entered(&self, _event: &NSEvent) { trace_scope!("mouseEntered:"); - let enter_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::CursorEntered { - device_id: DEVICE_ID, - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(enter_event)); + self.queue_event(WindowEvent::CursorEntered { + device_id: DEVICE_ID, + }); } #[sel(mouseExited:)] fn mouse_exited(&self, _event: &NSEvent) { trace_scope!("mouseExited:"); - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::CursorLeft { - device_id: DEVICE_ID, - }, - }; - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_event(WindowEvent::CursorLeft { + device_id: DEVICE_ID, + }); } #[sel(scrollWheel:)] @@ -812,32 +748,21 @@ declare_class!( }, }; - let device_event = Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::MouseWheel { delta }, - }; - self.update_potentially_stale_modifiers(event); - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::MouseWheel { - device_id: DEVICE_ID, - delta, - phase, - modifiers: event_mods(event), - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(device_event)); - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_device_event(DeviceEvent::MouseWheel { delta }); + self.queue_event(WindowEvent::MouseWheel { + device_id: DEVICE_ID, + delta, + phase, + modifiers: event_mods(event), + }); } #[sel(magnifyWithEvent:)] fn magnify_with_event(&self, event: &NSEvent) { trace_scope!("magnifyWithEvent:"); - let delta = event.magnification(); let phase = match event.phase() { NSEventPhase::NSEventPhaseBegan => TouchPhase::Started, NSEventPhase::NSEventPhaseChanged => TouchPhase::Moved, @@ -846,37 +771,26 @@ declare_class!( _ => return, }; - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::TouchpadMagnify { - device_id: DEVICE_ID, - delta, - phase, - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_event(WindowEvent::TouchpadMagnify { + device_id: DEVICE_ID, + delta: event.magnification(), + phase, + }); } #[sel(smartMagnifyWithEvent:)] fn smart_magnify_with_event(&self, _event: &NSEvent) { trace_scope!("smartMagnifyWithEvent:"); - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::SmartMagnify { - device_id: DEVICE_ID, - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_event(WindowEvent::SmartMagnify { + device_id: DEVICE_ID, + }); } #[sel(rotateWithEvent:)] fn rotate_with_event(&self, event: &NSEvent) { trace_scope!("rotateWithEvent:"); - let delta = event.rotation(); let phase = match event.phase() { NSEventPhase::NSEventPhaseBegan => TouchPhase::Started, NSEventPhase::NSEventPhaseChanged => TouchPhase::Moved, @@ -885,16 +799,11 @@ declare_class!( _ => return, }; - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::TouchpadRotate { - device_id: DEVICE_ID, - delta, - phase, - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_event(WindowEvent::TouchpadRotate { + device_id: DEVICE_ID, + delta: event.rotation(), + phase, + }); } #[sel(pressureChangeWithEvent:)] @@ -903,19 +812,11 @@ declare_class!( self.mouse_motion(event); - let pressure = event.pressure(); - let stage = event.stage(); - - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::TouchpadPressure { - device_id: DEVICE_ID, - pressure, - stage: stage as i64, - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_event(WindowEvent::TouchpadPressure { + device_id: DEVICE_ID, + pressure: event.pressure(), + stage: event.stage() as i64, + }); } // Allows us to receive Ctrl-Tab and Ctrl-Esc. @@ -959,6 +860,22 @@ impl WinitView { WindowId(self._ns_window.id()) } + fn queue_event(&self, event: WindowEvent<'static>) { + let event = Event::WindowEvent { + window_id: self.window_id(), + event, + }; + AppState::queue_event(EventWrapper::StaticEvent(event)); + } + + fn queue_device_event(&self, event: DeviceEvent) { + let event = Event::DeviceEvent { + device_id: DEVICE_ID, + event, + }; + AppState::queue_event(EventWrapper::StaticEvent(event)); + } + fn scale_factor(&self) -> f64 { self.window().backingScaleFactor() as f64 } @@ -989,10 +906,7 @@ impl WinitView { if self.state.ime_state != ImeState::Disabled { self.state.ime_state = ImeState::Disabled; - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::Ime(Ime::Disabled), - })); + self.queue_event(WindowEvent::Ime(Ime::Disabled)); } } @@ -1008,10 +922,7 @@ impl WinitView { if self.state.modifiers != event_modifiers { self.state.modifiers = event_modifiers; - AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::ModifiersChanged(self.state.modifiers), - })); + self.queue_event(WindowEvent::ModifiersChanged(self.state.modifiers)); } } @@ -1020,17 +931,12 @@ impl WinitView { self.update_potentially_stale_modifiers(event); - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::MouseInput { - device_id: DEVICE_ID, - state: button_state, - button, - modifiers: event_mods(event), - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_event(WindowEvent::MouseInput { + device_id: DEVICE_ID, + state: button_state, + button, + modifiers: event_mods(event), + }); } fn mouse_motion(&mut self, event: &NSEvent) { @@ -1056,16 +962,11 @@ impl WinitView { self.update_potentially_stale_modifiers(event); - let window_event = Event::WindowEvent { - window_id: self.window_id(), - event: WindowEvent::CursorMoved { - device_id: DEVICE_ID, - position: logical_position.to_physical(self.scale_factor()), - modifiers: event_mods(event), - }, - }; - - AppState::queue_event(EventWrapper::StaticEvent(window_event)); + self.queue_event(WindowEvent::CursorMoved { + device_id: DEVICE_ID, + position: logical_position.to_physical(self.scale_factor()), + modifiers: event_mods(event), + }); } } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 0d770531..e44e5c2b 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -65,7 +65,7 @@ declare_class!( Ivar::write(&mut this.previous_scale_factor, scale_factor); if scale_factor != 1.0 { - this.emit_static_scale_factor_changed_event(); + this.queue_static_scale_factor_changed_event(); } this.window.setDelegate(Some(this)); @@ -94,7 +94,7 @@ declare_class!( #[sel(windowShouldClose:)] fn window_should_close(&self, _: Option<&Object>) -> bool { trace_scope!("windowShouldClose:"); - self.emit_event(WindowEvent::CloseRequested); + self.queue_event(WindowEvent::CloseRequested); false } @@ -107,7 +107,7 @@ declare_class!( // be called after the window closes. self.window.setDelegate(None); }); - self.emit_event(WindowEvent::Destroyed); + self.queue_event(WindowEvent::Destroyed); } #[sel(windowDidResize:)] @@ -127,7 +127,7 @@ declare_class!( #[sel(windowDidChangeBackingProperties:)] fn window_did_change_backing_properties(&mut self, _: Option<&Object>) { trace_scope!("windowDidChangeBackingProperties:"); - self.emit_static_scale_factor_changed_event(); + self.queue_static_scale_factor_changed_event(); } #[sel(windowDidBecomeKey:)] @@ -135,7 +135,7 @@ declare_class!( trace_scope!("windowDidBecomeKey:"); // TODO: center the cursor if the window had mouse grab when it // lost focus - self.emit_event(WindowEvent::Focused(true)); + self.queue_event(WindowEvent::Focused(true)); } #[sel(windowDidResignKey:)] @@ -155,10 +155,10 @@ declare_class!( // Both update the state and emit a ModifiersChanged event. if !view.state.modifiers.is_empty() { view.state.modifiers = ModifiersState::empty(); - self.emit_event(WindowEvent::ModifiersChanged(view.state.modifiers)); + self.queue_event(WindowEvent::ModifiersChanged(view.state.modifiers)); } - self.emit_event(WindowEvent::Focused(false)); + self.queue_event(WindowEvent::Focused(false)); } /// Invoked when the dragged image enters destination bounds or frame @@ -174,7 +174,7 @@ declare_class!( filenames.into_iter().for_each(|file| { let path = PathBuf::from(file.to_string()); - self.emit_event(WindowEvent::HoveredFile(path)); + self.queue_event(WindowEvent::HoveredFile(path)); }); true @@ -200,7 +200,7 @@ declare_class!( filenames.into_iter().for_each(|file| { let path = PathBuf::from(file.to_string()); - self.emit_event(WindowEvent::DroppedFile(path)); + self.queue_event(WindowEvent::DroppedFile(path)); }); true @@ -216,7 +216,7 @@ declare_class!( #[sel(draggingExited:)] fn dragging_exited(&self, _: Option<&Object>) { trace_scope!("draggingExited:"); - self.emit_event(WindowEvent::HoveredFileCancelled); + self.queue_event(WindowEvent::HoveredFileCancelled); } /// Invoked when before enter fullscreen @@ -357,7 +357,7 @@ declare_class!( #[sel(windowDidChangeOcclusionState:)] fn window_did_change_occlusion_state(&self, _: Option<&Object>) { trace_scope!("windowDidChangeOcclusionState:"); - self.emit_event(WindowEvent::Occluded( + self.queue_event(WindowEvent::Occluded( !self .window .occlusionState() @@ -389,7 +389,7 @@ declare_class!( shared_state.current_theme = Some(theme); drop(shared_state); if current_theme != Some(theme) { - self.emit_event(WindowEvent::ThemeChanged(theme)); + self.queue_event(WindowEvent::ThemeChanged(theme)); } } } @@ -406,7 +406,7 @@ impl WinitWindowDelegate { } } - fn emit_event(&self, event: WindowEvent<'static>) { + fn queue_event(&self, event: WindowEvent<'static>) { let event = Event::WindowEvent { window_id: WindowId(self.window.id()), event, @@ -414,7 +414,7 @@ impl WinitWindowDelegate { AppState::queue_event(EventWrapper::StaticEvent(event)); } - fn emit_static_scale_factor_changed_event(&mut self) { + fn queue_static_scale_factor_changed_event(&mut self) { let scale_factor = self.window.scale_factor(); if scale_factor == *self.previous_scale_factor { return; @@ -437,7 +437,7 @@ impl WinitWindowDelegate { *self.previous_position = Some(Box::new((x, y))); let scale_factor = self.window.scale_factor(); let physical_pos = LogicalPosition::::from((x, y)).to_physical(scale_factor); - self.emit_event(WindowEvent::Moved(physical_pos)); + self.queue_event(WindowEvent::Moved(physical_pos)); } }