Simplify event queuing on macOS (#2642)

This commit is contained in:
Mads Marquart 2023-01-22 23:29:38 +01:00 committed by GitHub
parent 7341ee80ea
commit 0f2fbe373b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 270 deletions

View file

@ -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));
}

View file

@ -368,13 +368,6 @@ impl AppState {
HANDLER.events().push_back(wrapper);
}
pub fn queue_events(mut wrappers: VecDeque<EventWrapper>) {
if !is_main_thread() {
panic!("Events queued from different thread: {:#?}", wrappers);
}
HANDLER.events().append(&mut wrappers);
}
pub fn cleared(panic_info: Weak<PanicInfo>) {
let panic_info = panic_info
.upgrade()

View file

@ -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::<u32>(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),
});
}
}

View file

@ -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::<f64>::from((x, y)).to_physical(scale_factor);
self.emit_event(WindowEvent::Moved(physical_pos));
self.queue_event(WindowEvent::Moved(physical_pos));
}
}