Skip to content

Commit 86bc86f

Browse files
goddessfreyaOsspial
authored andcommitted
Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
1 parent 742a688 commit 86bc86f

File tree

2 files changed

+136
-50
lines changed

2 files changed

+136
-50
lines changed

src/platform_impl/macos/event.rs

+86-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,67 @@ use event::{
88
};
99
use platform_impl::platform::DEVICE_ID;
1010

11-
pub fn to_virtual_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
11+
pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
12+
// We only translate keys that are affected by keyboard layout.
13+
//
14+
// Note that since keys are translated in a somewhat "dumb" way (reading character)
15+
// there is a concern that some combination, i.e. Cmd+char, causes the wrong
16+
// letter to be received, and so we receive the wrong key.
17+
//
18+
// Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626
19+
Some(match c {
20+
'a' | 'A' => VirtualKeyCode::A,
21+
'b' | 'B' => VirtualKeyCode::B,
22+
'c' | 'C' => VirtualKeyCode::C,
23+
'd' | 'D' => VirtualKeyCode::D,
24+
'e' | 'E' => VirtualKeyCode::E,
25+
'f' | 'F' => VirtualKeyCode::F,
26+
'g' | 'G' => VirtualKeyCode::G,
27+
'h' | 'H' => VirtualKeyCode::H,
28+
'i' | 'I' => VirtualKeyCode::I,
29+
'j' | 'J' => VirtualKeyCode::J,
30+
'k' | 'K' => VirtualKeyCode::K,
31+
'l' | 'L' => VirtualKeyCode::L,
32+
'm' | 'M' => VirtualKeyCode::M,
33+
'n' | 'N' => VirtualKeyCode::N,
34+
'o' | 'O' => VirtualKeyCode::O,
35+
'p' | 'P' => VirtualKeyCode::P,
36+
'q' | 'Q' => VirtualKeyCode::Q,
37+
'r' | 'R' => VirtualKeyCode::R,
38+
's' | 'S' => VirtualKeyCode::S,
39+
't' | 'T' => VirtualKeyCode::T,
40+
'u' | 'U' => VirtualKeyCode::U,
41+
'v' | 'V' => VirtualKeyCode::V,
42+
'w' | 'W' => VirtualKeyCode::W,
43+
'x' | 'X' => VirtualKeyCode::X,
44+
'y' | 'Y' => VirtualKeyCode::Y,
45+
'z' | 'Z' => VirtualKeyCode::Z,
46+
'1' | '!' => VirtualKeyCode::Key1,
47+
'2' | '@' => VirtualKeyCode::Key2,
48+
'3' | '#' => VirtualKeyCode::Key3,
49+
'4' | '$' => VirtualKeyCode::Key4,
50+
'5' | '%' => VirtualKeyCode::Key5,
51+
'6' | '^' => VirtualKeyCode::Key6,
52+
'7' | '&' => VirtualKeyCode::Key7,
53+
'8' | '*' => VirtualKeyCode::Key8,
54+
'9' | '(' => VirtualKeyCode::Key9,
55+
'0' | ')' => VirtualKeyCode::Key0,
56+
'=' | '+' => VirtualKeyCode::Equals,
57+
'-' | '_' => VirtualKeyCode::Minus,
58+
']' | '}' => VirtualKeyCode::RBracket,
59+
'[' | '{' => VirtualKeyCode::LBracket,
60+
'\''| '"' => VirtualKeyCode::Apostrophe,
61+
';' | ':' => VirtualKeyCode::Semicolon,
62+
'\\'| '|' => VirtualKeyCode::Backslash,
63+
',' | '<' => VirtualKeyCode::Comma,
64+
'/' | '?' => VirtualKeyCode::Slash,
65+
'.' | '>' => VirtualKeyCode::Period,
66+
'`' | '~' => VirtualKeyCode::Grave,
67+
_ => return None,
68+
})
69+
}
70+
71+
pub fn scancode_to_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
1272
Some(match scancode {
1373
0x00 => VirtualKeyCode::A,
1474
0x01 => VirtualKeyCode::S,
@@ -147,17 +207,18 @@ pub fn to_virtual_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
147207
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
148208
// constants for the rest.
149209
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
150-
pub fn check_function_keys(string: &Option<String>) -> Option<VirtualKeyCode> {
151-
string
152-
.as_ref()
153-
.and_then(|string| string.encode_utf16().next())
154-
.and_then(|character| match character {
155-
0xf718 => Some(VirtualKeyCode::F21),
156-
0xf719 => Some(VirtualKeyCode::F22),
157-
0xf71a => Some(VirtualKeyCode::F23),
158-
0xf71b => Some(VirtualKeyCode::F24),
159-
_ => None,
210+
pub fn check_function_keys(string: &String) -> Option<VirtualKeyCode> {
211+
if let Some(ch) = string.encode_utf16().next() {
212+
return Some(match ch {
213+
0xf718 => VirtualKeyCode::F21,
214+
0xf719 => VirtualKeyCode::F22,
215+
0xf71a => VirtualKeyCode::F23,
216+
0xf71b => VirtualKeyCode::F24,
217+
_ => return None,
160218
})
219+
}
220+
221+
None
161222
}
162223

163224
pub fn event_mods(event: id) -> ModifiersState {
@@ -172,6 +233,16 @@ pub fn event_mods(event: id) -> ModifiersState {
172233
}
173234
}
174235

236+
pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
237+
// In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
238+
// and there is no easy way to navtively retrieve the layout-dependent character.
239+
// In winit, we use keycode to refer to the key's character, and so this function aligns
240+
// AppKit's terminology with ours.
241+
unsafe {
242+
msg_send![event, keyCode]
243+
}
244+
}
245+
175246
pub unsafe fn modifier_event(
176247
ns_event: id,
177248
keymask: NSEventModifierFlags,
@@ -184,14 +255,14 @@ pub unsafe fn modifier_event(
184255
} else {
185256
ElementState::Pressed
186257
};
187-
let keycode = NSEvent::keyCode(ns_event);
188-
let scancode = keycode as u32;
189-
let virtual_keycode = to_virtual_keycode(keycode);
258+
259+
let scancode = get_scancode(ns_event);
260+
let virtual_keycode = scancode_to_keycode(scancode);
190261
Some(WindowEvent::KeyboardInput {
191262
device_id: DEVICE_ID,
192263
input: KeyboardInput {
193264
state,
194-
scancode,
265+
scancode: scancode as _,
195266
virtual_keycode,
196267
modifiers: event_mods(ns_event),
197268
},

src/platform_impl/macos/view.rs

+50-35
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use {
1818
};
1919
use platform_impl::platform::{
2020
app_state::AppState, DEVICE_ID,
21-
event::{check_function_keys, event_mods, modifier_event, to_virtual_keycode},
21+
event::{check_function_keys, event_mods, modifier_event, char_to_keycode, get_scancode, scancode_to_keycode},
2222
util::{self, IdRef}, ffi::*, window::get_window_id,
2323
};
2424

@@ -504,16 +504,53 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
504504
trace!("Completed `doCommandBySelector`");
505505
}
506506

507-
fn get_characters(event: id) -> Option<String> {
507+
fn get_characters(event: id, ignore_modifiers: bool) -> String {
508508
unsafe {
509-
let characters: id = msg_send![event, characters];
509+
let characters: id = if ignore_modifiers {
510+
msg_send![event, charactersIgnoringModifiers]
511+
} else {
512+
msg_send![event, characters]
513+
};
514+
515+
assert_ne!(characters, nil);
510516
let slice = slice::from_raw_parts(
511517
characters.UTF8String() as *const c_uchar,
512518
characters.len(),
513519
);
520+
514521
let string = str::from_utf8_unchecked(slice);
515-
Some(string.to_owned())
522+
string.to_owned()
523+
}
524+
}
525+
526+
// Retrieves a layout-independent keycode given an event.
527+
fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
528+
#[inline]
529+
fn get_code(ev: id, raw: bool) -> Option<VirtualKeyCode> {
530+
let characters = get_characters(ev, raw);
531+
characters.chars().next().map_or(None, |c| char_to_keycode(c))
516532
}
533+
534+
// Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first.
535+
// If we don't get a match, then we fall back to unmodified characters.
536+
let code = get_code(event, false)
537+
.or_else(|| {
538+
get_code(event, true)
539+
});
540+
541+
// We've checked all layout related keys, so fall through to scancode.
542+
// Reaching this code means that the key is layout-independent (e.g. Backspace, Return).
543+
//
544+
// We're additionally checking here for F21-F24 keys, since their keycode
545+
// can vary, but we know that they are encoded
546+
// in characters property.
547+
code.or_else(|| {
548+
let scancode = get_scancode(event);
549+
scancode_to_keycode(scancode)
550+
.or_else(|| {
551+
check_function_keys(&get_characters(event, true))
552+
})
553+
})
517554
}
518555

519556
extern fn key_down(this: &Object, _sel: Sel, event: id) {
@@ -522,17 +559,13 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
522559
let state_ptr: *mut c_void = *this.get_ivar("winitState");
523560
let state = &mut *(state_ptr as *mut ViewState);
524561
let window_id = WindowId(get_window_id(state.nswindow));
562+
let characters = get_characters(event, false);
525563

526-
state.raw_characters = get_characters(event);
564+
state.raw_characters = Some(characters.clone());
565+
566+
let scancode = get_scancode(event) as u32;
567+
let virtual_keycode = retrieve_keycode(event);
527568

528-
let keycode: c_ushort = msg_send![event, keyCode];
529-
// We are checking here for F21-F24 keys, since their keycode
530-
// can vary, but we know that they are encoded
531-
// in characters property.
532-
let virtual_keycode = to_virtual_keycode(keycode).or_else(|| {
533-
check_function_keys(&state.raw_characters)
534-
});
535-
let scancode = keycode as u32;
536569
let is_repeat = msg_send![event, isARepeat];
537570

538571
let window_event = Event::WindowEvent {
@@ -548,22 +581,11 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
548581
},
549582
};
550583

551-
let characters: id = msg_send![event, characters];
552-
let slice = slice::from_raw_parts(
553-
characters.UTF8String() as *const c_uchar,
554-
characters.len(),
555-
);
556-
let string = str::from_utf8_unchecked(slice);
557-
558-
state.raw_characters = {
559-
Some(string.to_owned())
560-
};
561-
562584
let pass_along = {
563585
AppState::queue_event(window_event);
564586
// Emit `ReceivedCharacter` for key repeats
565587
if is_repeat && state.is_key_down {
566-
for character in string.chars() {
588+
for character in characters.chars() {
567589
AppState::queue_event(Event::WindowEvent {
568590
window_id,
569591
event: WindowEvent::ReceivedCharacter(character),
@@ -594,16 +616,9 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
594616

595617
state.is_key_down = false;
596618

597-
// We need characters here to check for additional keys such as
598-
// F21-F24.
599-
let characters = get_characters(event);
619+
let scancode = get_scancode(event) as u32;
620+
let virtual_keycode = retrieve_keycode(event);
600621

601-
let keycode: c_ushort = msg_send![event, keyCode];
602-
let virtual_keycode = to_virtual_keycode(keycode)
603-
.or_else(|| {
604-
check_function_keys(&characters)
605-
});
606-
let scancode = keycode as u32;
607622
let window_event = Event::WindowEvent {
608623
window_id: WindowId(get_window_id(state.nswindow)),
609624
event: WindowEvent::KeyboardInput {
@@ -707,7 +722,7 @@ extern fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
707722
let state = &mut *(state_ptr as *mut ViewState);
708723

709724
let scancode = 0x2f;
710-
let virtual_keycode = to_virtual_keycode(scancode);
725+
let virtual_keycode = scancode_to_keycode(scancode);
711726
debug_assert_eq!(virtual_keycode, Some(VirtualKeyCode::Period));
712727

713728
let event: id = msg_send![NSApp(), currentEvent];

0 commit comments

Comments
 (0)