On macOS, fix assertion when pressing Fn key

This commit is contained in:
Kirill Chibisov 2023-11-17 15:56:03 +04:00 committed by GitHub
parent 14140607d1
commit 7bed5eecfd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 91 deletions

View file

@ -16,6 +16,7 @@ Unreleased` header.
- On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account. - On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account.
- On macOS, send a `Resized` event after each `ScaleFactorChanged` event. - On macOS, send a `Resized` event after each `ScaleFactorChanged` event.
- On Wayland, fix `wl_surface` being destroyed before associated objects. - On Wayland, fix `wl_surface` being destroyed before associated objects.
- On macOS, fix assertion when pressing `Fn` key.
# 0.29.3 # 0.29.3

View file

@ -156,13 +156,11 @@ pub(crate) fn create_key_event(
// Also not checking if this is a release event because then this issue would // Also not checking if this is a release event because then this issue would
// still affect the key release. // still affect the key release.
Some(text) if !has_ctrl => Key::Character(text.clone()), Some(text) if !has_ctrl => Key::Character(text.clone()),
_ => { _ => match key_without_modifiers.as_ref() {
let modifierless_chars = match key_without_modifiers.as_ref() { Key::Character(ch) => get_logical_key_char(ns_event, ch),
Key::Character(ch) => ch, // Don't try to get text for events which likely don't have it.
_ => "", _ => key_without_modifiers.clone(),
}; },
get_logical_key_char(ns_event, modifierless_chars)
}
}; };
(logical_key, key_without_modifiers) (logical_key, key_without_modifiers)

View file

@ -74,8 +74,8 @@ enum ImeState {
bitflags! { bitflags! {
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
struct ModLocationMask: u8 { struct ModLocationMask: u8 {
const LEFT = 1; const LEFT = 0b0001;
const RIGHT = 2; const RIGHT = 0b0010;
} }
} }
impl ModLocationMask { impl ModLocationMask {
@ -88,13 +88,13 @@ impl ModLocationMask {
} }
} }
fn key_to_modifier(key: &Key) -> ModifiersState { fn key_to_modifier(key: &Key) -> Option<ModifiersState> {
match key { match key {
Key::Named(NamedKey::Alt) => ModifiersState::ALT, Key::Named(NamedKey::Alt) => Some(ModifiersState::ALT),
Key::Named(NamedKey::Control) => ModifiersState::CONTROL, Key::Named(NamedKey::Control) => Some(ModifiersState::CONTROL),
Key::Named(NamedKey::Super) => ModifiersState::SUPER, Key::Named(NamedKey::Super) => Some(ModifiersState::SUPER),
Key::Named(NamedKey::Shift) => ModifiersState::SHIFT, Key::Named(NamedKey::Shift) => Some(ModifiersState::SHIFT),
_ => unreachable!(), _ => None,
} }
} }
@ -924,91 +924,96 @@ impl WinitView {
// event, thus we can't generate regular presses based on that. The `ModifiersChanged` // event, thus we can't generate regular presses based on that. The `ModifiersChanged`
// later will work though, since the flags are attached to the event and contain valid // later will work though, since the flags are attached to the event and contain valid
// information. // information.
if is_flags_changed_event && ns_event.key_code() != 0 { 'send_event: {
let scancode = ns_event.key_code(); if is_flags_changed_event && ns_event.key_code() != 0 {
let physical_key = PhysicalKey::from_scancode(scancode as u32); let scancode = ns_event.key_code();
let physical_key = PhysicalKey::from_scancode(scancode as u32);
// We'll correct the `is_press` later. // We'll correct the `is_press` later.
let mut event = create_key_event(ns_event, false, false, Some(physical_key)); let mut event = create_key_event(ns_event, false, false, Some(physical_key));
let key = code_to_key(physical_key, scancode); let key = code_to_key(physical_key, scancode);
let event_modifier = key_to_modifier(&key); // Ignore processing of unkown modifiers because we can't determine whether
event.physical_key = physical_key; // it was pressed or release reliably.
event.logical_key = key.clone(); let Some(event_modifier) = key_to_modifier(&key) else {
event.location = code_to_location(physical_key); break 'send_event;
let location_mask = ModLocationMask::from_location(event.location); };
event.physical_key = physical_key;
event.logical_key = key.clone();
event.location = code_to_location(physical_key);
let location_mask = ModLocationMask::from_location(event.location);
let mut phys_mod_state = self.state.phys_modifiers.borrow_mut(); let mut phys_mod_state = self.state.phys_modifiers.borrow_mut();
let phys_mod = phys_mod_state let phys_mod = phys_mod_state
.entry(key) .entry(key)
.or_insert(ModLocationMask::empty()); .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);
// There is no API for getting whether the button was pressed or released // There is no API for getting whether the button was pressed or released
// during this event. For this reason we have to do a bit of magic below // during this event. For this reason we have to do a bit of magic below
// to come up with a good guess whether this key was pressed or released. // to come up with a good guess whether this key was pressed or released.
// (This is not trivial because there are multiple buttons that may affect // (This is not trivial because there are multiple buttons that may affect
// the same modifier) // the same modifier)
if !is_active { if !is_active {
event.state = Released; event.state = Released;
if phys_mod.contains(ModLocationMask::LEFT) { if phys_mod.contains(ModLocationMask::LEFT) {
let mut event = event.clone(); let mut event = event.clone();
event.location = KeyLocation::Left; event.location = KeyLocation::Left;
event.physical_key = get_left_modifier_code(&event.logical_key).into(); event.physical_key = get_left_modifier_code(&event.logical_key).into();
events.push_back(WindowEvent::KeyboardInput { events.push_back(WindowEvent::KeyboardInput {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event, event,
is_synthetic: false, is_synthetic: false,
}); });
} }
if phys_mod.contains(ModLocationMask::RIGHT) { if phys_mod.contains(ModLocationMask::RIGHT) {
event.location = KeyLocation::Right; event.location = KeyLocation::Right;
event.physical_key = get_right_modifier_code(&event.logical_key).into(); event.physical_key = get_right_modifier_code(&event.logical_key).into();
events.push_back(WindowEvent::KeyboardInput { events.push_back(WindowEvent::KeyboardInput {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event, event,
is_synthetic: false, is_synthetic: false,
}); });
} }
*phys_mod = ModLocationMask::empty(); *phys_mod = ModLocationMask::empty();
} else {
// is_active
if *phys_mod == location_mask {
// Here we hit a contradiction:
// The modifier state was "changed" to active,
// yet the only pressed modifier key was the one that we
// just got a change event for.
// This seemingly means that the only pressed modifier is now released,
// but at the same time the modifier became active.
//
// But this scenario is possible if we released modifiers
// while the application was not in focus. (Because we don't
// get informed of modifier key events while the application
// is not focused)
// In this case we prioritize the information
// about the current modifier state which means
// that the button was pressed.
event.state = Pressed;
} else { } else {
phys_mod.toggle(location_mask); if *phys_mod == location_mask {
let is_pressed = phys_mod.contains(location_mask); // Here we hit a contradiction:
event.state = if is_pressed { Pressed } else { Released }; // The modifier state was "changed" to active,
// yet the only pressed modifier key was the one that we
// just got a change event for.
// This seemingly means that the only pressed modifier is now released,
// but at the same time the modifier became active.
//
// But this scenario is possible if we released modifiers
// while the application was not in focus. (Because we don't
// get informed of modifier key events while the application
// is not focused)
// In this case we prioritize the information
// about the current modifier state which means
// that the button was pressed.
event.state = Pressed;
} else {
phys_mod.toggle(location_mask);
let is_pressed = phys_mod.contains(location_mask);
event.state = if is_pressed { Pressed } else { Released };
}
events.push_back(WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
event,
is_synthetic: false,
});
} }
events.push_back(WindowEvent::KeyboardInput { drop(phys_mod_state);
device_id: DEVICE_ID,
event,
is_synthetic: false,
});
}
drop(phys_mod_state); for event in events {
self.queue_event(event);
for event in events { }
self.queue_event(event);
} }
} }