macOS: Fix crash when pressing Caps Lock (#4024)

Events emitted by `flagsChanged:` cannot access
`charactersIgnoringModifiers`. We were previously doing this because we
were trying to re-use the `create_key_event` function, but that is unsuited
for this purpose, so I have separated the `flagsChanged:` logic out from it.
This commit is contained in:
Mads Marquart 2024-12-03 18:48:23 +01:00 committed by GitHub
parent 3657506f6e
commit f314cd2b9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 38 additions and 33 deletions

View file

@ -212,3 +212,4 @@ changelog entry.
- On macOS, fixed the scancode conversion for audio volume keys. - On macOS, fixed the scancode conversion for audio volume keys.
- On macOS, fixed the scancode conversion for `IntlBackslash`. - On macOS, fixed the scancode conversion for `IntlBackslash`.
- On macOS, fixed redundant `SurfaceResized` event at window creation. - On macOS, fixed redundant `SurfaceResized` event at window creation.
- On macOS, fix crash when pressing Caps Lock in certain configurations.

View file

@ -92,17 +92,12 @@ fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
/// Create `KeyEvent` for the given `NSEvent`. /// Create `KeyEvent` for the given `NSEvent`.
/// ///
/// This function shouldn't be called when the IME input is in process. /// This function shouldn't be called when the IME input is in process.
pub(crate) fn create_key_event( pub(crate) fn create_key_event(ns_event: &NSEvent, is_press: bool, is_repeat: bool) -> KeyEvent {
ns_event: &NSEvent,
is_press: bool,
is_repeat: bool,
key_override: Option<PhysicalKey>,
) -> KeyEvent {
use ElementState::{Pressed, Released}; use ElementState::{Pressed, Released};
let state = if is_press { Pressed } else { Released }; let state = if is_press { Pressed } else { Released };
let scancode = unsafe { ns_event.keyCode() }; let scancode = unsafe { ns_event.keyCode() };
let mut physical_key = key_override.unwrap_or_else(|| scancode_to_physicalkey(scancode as u32)); let mut physical_key = scancode_to_physicalkey(scancode as u32);
// NOTE: The logical key should heed both SHIFT and ALT if possible. // NOTE: The logical key should heed both SHIFT and ALT if possible.
// For instance: // For instance:
@ -111,20 +106,15 @@ pub(crate) fn create_key_event(
// * Pressing CTRL SHIFT A: logical key should also be "A" // * Pressing CTRL SHIFT A: logical key should also be "A"
// This is not easy to tease out of `NSEvent`, but we do our best. // This is not easy to tease out of `NSEvent`, but we do our best.
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() { let characters = unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default();
let text_with_all_modifiers = if characters.is_empty() {
None None
} else { } else {
let characters = if matches!(physical_key, PhysicalKey::Unidentified(_)) {
unsafe { ns_event.characters() }.map(|s| s.to_string()).unwrap_or_default(); // The key may be one of the funky function keys
if characters.is_empty() { physical_key = extra_function_key_to_code(scancode, &characters);
None
} else {
if matches!(physical_key, PhysicalKey::Unidentified(_)) {
// The key may be one of the funky function keys
physical_key = extra_function_key_to_code(scancode, &characters);
}
Some(SmolStr::new(characters))
} }
Some(SmolStr::new(characters))
}; };
let key_from_code = code_to_key(physical_key, scancode); let key_from_code = code_to_key(physical_key, scancode);

View file

@ -21,13 +21,13 @@ use super::app_state::AppState;
use super::cursor::{default_cursor, invisible_cursor}; use super::cursor::{default_cursor, invisible_cursor};
use super::event::{ use super::event::{
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed, code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
scancode_to_physicalkey, scancode_to_physicalkey, KeyEventExtra,
}; };
use super::window::WinitWindow; use super::window::WinitWindow;
use crate::dpi::{LogicalPosition, LogicalSize}; use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::{ use crate::event::{
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, PointerKind, DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
PointerSource, TouchPhase, WindowEvent, PointerKind, PointerSource, TouchPhase, WindowEvent,
}; };
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey}; use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey};
use crate::platform::macos::OptionAsAlt; use crate::platform::macos::OptionAsAlt;
@ -490,7 +490,7 @@ declare_class!(
}; };
if !had_ime_input || self.ivars().forward_key_to_app.get() { if !had_ime_input || self.ivars().forward_key_to_app.get() {
let key_event = create_key_event(&event, true, unsafe { event.isARepeat() }, None); let key_event = create_key_event(&event, true, unsafe { event.isARepeat() });
self.queue_event(WindowEvent::KeyboardInput { self.queue_event(WindowEvent::KeyboardInput {
device_id: None, device_id: None,
event: key_event, event: key_event,
@ -513,7 +513,7 @@ declare_class!(
) { ) {
self.queue_event(WindowEvent::KeyboardInput { self.queue_event(WindowEvent::KeyboardInput {
device_id: None, device_id: None,
event: create_key_event(&event, false, false, None), event: create_key_event(&event, false, false),
is_synthetic: false, is_synthetic: false,
}); });
} }
@ -560,7 +560,7 @@ declare_class!(
.expect("could not find current event"); .expect("could not find current event");
self.update_modifiers(&event, false); self.update_modifiers(&event, false);
let event = create_key_event(&event, true, unsafe { event.isARepeat() }, None); let event = create_key_event(&event, true, unsafe { event.isARepeat() });
self.queue_event(WindowEvent::KeyboardInput { self.queue_event(WindowEvent::KeyboardInput {
device_id: None, device_id: None,
@ -946,22 +946,36 @@ impl WinitView {
let scancode = unsafe { ns_event.keyCode() }; let scancode = unsafe { ns_event.keyCode() };
let physical_key = scancode_to_physicalkey(scancode as u32); let physical_key = scancode_to_physicalkey(scancode as u32);
// We'll correct the `is_press` later. let logical_key = code_to_key(physical_key, scancode);
let mut event = create_key_event(ns_event, false, false, Some(physical_key));
let key = code_to_key(physical_key, scancode);
// Ignore processing of unknown modifiers because we can't determine whether // Ignore processing of unknown modifiers because we can't determine whether
// it was pressed or release reliably. // it was pressed or release reliably.
let Some(event_modifier) = key_to_modifier(&key) else { //
// Furthermore, sometimes normal keys are reported inside flagsChanged:, such as
// when holding Caps Lock while pressing another key, see:
// https://github.com/alacritty/alacritty/issues/8268
let Some(event_modifier) = key_to_modifier(&logical_key) else {
break 'send_event; break 'send_event;
}; };
event.physical_key = physical_key;
event.logical_key = key.clone(); let mut event = KeyEvent {
event.location = code_to_location(physical_key); location: code_to_location(physical_key),
logical_key: logical_key.clone(),
physical_key,
repeat: false,
// We'll correct this later.
state: Pressed,
text: None,
platform_specific: KeyEventExtra {
text_with_all_modifiers: None,
key_without_modifiers: logical_key.clone(),
},
};
let location_mask = ModLocationMask::from_location(event.location); let location_mask = ModLocationMask::from_location(event.location);
let mut phys_mod_state = self.ivars().phys_modifiers.borrow_mut(); let mut phys_mod_state = self.ivars().phys_modifiers.borrow_mut();
let phys_mod = phys_mod_state.entry(key).or_insert(ModLocationMask::empty()); let phys_mod =
phys_mod_state.entry(logical_key).or_insert(ModLocationMask::empty());
let is_active = current_modifiers.state().contains(event_modifier); let is_active = current_modifiers.state().contains(event_modifier);
let mut events = VecDeque::with_capacity(2); let mut events = VecDeque::with_capacity(2);