Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/src/platform/mac/keycode.rs
1use std::slice;
2 
3use cocoa::{
4 base::{id, nil, BOOL},
5 foundation::{NSArray, NSString, NSUInteger},
6};
7use objc::{msg_send, sel, sel_impl};
8use strato_ui_core::keymap::Keystroke;
9use strato_ui_core::platform::keyboard::{KeyCode, NativeKeyCode, PhysicalKey};
10 
11use super::make_nsstring;
12 
13// Modifier key mask values for the Carbon API.
14pub const CMD_KEY: u16 = 256;
15pub const SHIFT_KEY: u16 = 512;
16pub const OPTION_KEY: u16 = 2048;
17pub const CONTROL_KEY: u16 = 4096;
18 
19extern "C" {
20 fn charToKeyCodes(keyChar: id) -> id;
21 fn keyCodeToChar(keyCode: NSUInteger, shifted: BOOL) -> id;
22}
23 
24pub struct Keycode(pub u16);
25 
26impl Keycode {
27 pub fn try_to_key_name(self, shift_key_pressed: bool) -> Option<String> {
28 unsafe {
29 // The underlying core-foundation library interprets objc BOOL type as bool
30 // in aarch machines but as i8 in intel machines so we need to call .into here.
31 // But clippy isn't smart enough to know that so we silence it here for now.
32 #[allow(clippy::useless_conversion)]
33 let key = keyCodeToChar(self.0 as u64, shift_key_pressed.into());
34 
35 if key == nil {
36 return None;
37 }
38 
39 let cstr = key.UTF8String() as *const u8;
40 std::str::from_utf8(slice::from_raw_parts(cstr, key.len()))
41 .ok()
42 .map(|s| s.to_string())
43 }
44 }
45 
46 // There could have multiple keycodes mapping to one virtual key. Return an iterator
47 // to all possible values of keycode here.
48 pub fn keycodes_from_key_name(key_name: &str) -> impl Iterator<Item = Keycode> {
49 unsafe {
50 let keycodes: id = charToKeyCodes(make_nsstring(key_name));
51 let keycodes_length = keycodes.count();
52 
53 (0..keycodes_length).map(move |i| {
54 let keycode: NSUInteger =
55 msg_send![keycodes.objectAtIndex(i), unsignedIntegerValue];
56 Self(keycode as u16)
57 })
58 }
59 }
60}
61 
62// Convert modifier flags to Carbon style modifier key mask.
63pub fn modifier_code(keystroke: &Keystroke) -> u16 {
64 let mut code = 0;
65 if keystroke.alt {
66 code |= OPTION_KEY;
67 }
68 
69 if keystroke.cmd {
70 code |= CMD_KEY;
71 }
72 
73 if keystroke.shift {
74 code |= SHIFT_KEY;
75 }
76 
77 if keystroke.ctrl {
78 code |= CONTROL_KEY;
79 }
80 
81 code
82}
83 
84// The following types and functions are taken from winit's appkit implementation.
85// We redefine them here to avoid needing to include the entirety of winit as a dependency for MacOS.
86// --------------------------------------------------------------------------------------------------------
87 
88/// Converts a scancode to a physical key. Logic is taken from winit appkit code.
89pub(crate) fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
90 // Follows what Chromium and Firefox do:
91 // https://chromium.googlesource.com/chromium/src.git/+/3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c/ui/events/keycodes/dom/dom_code_data.inc
92 // https://searchfox.org/mozilla-central/rev/c597e9c789ad36af84a0370d395be066b7dc94f4/widget/NativeKeyToDOMCodeName.h
93 //
94 // See also:
95 // Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
96 //
97 // Also see https://developer.apple.com/documentation/appkit/function-key-unicode-values:
98 //
99 // > the system handles some function keys at a lower level and your app never sees them.
100 // > Examples include the Volume Up key, Volume Down key, Volume Mute key, Eject key, and
101 // > Function key found on many Macs.
102 //
103 // So the handling of some of these is mostly for show.
104 PhysicalKey::Code(match scancode {
105 0x00 => KeyCode::KeyA,
106 0x01 => KeyCode::KeyS,
107 0x02 => KeyCode::KeyD,
108 0x03 => KeyCode::KeyF,
109 0x04 => KeyCode::KeyH,
110 0x05 => KeyCode::KeyG,
111 0x06 => KeyCode::KeyZ,
112 0x07 => KeyCode::KeyX,
113 0x08 => KeyCode::KeyC,
114 0x09 => KeyCode::KeyV,
115 // This key is typically located near LeftShift key, roughly the same location as backquote
116 // (`) on Windows' US layout.
117 //
118 // The keycap varies on international keyboards.
119 0x0a => KeyCode::IntlBackslash,
120 0x0b => KeyCode::KeyB,
121 0x0c => KeyCode::KeyQ,
122 0x0d => KeyCode::KeyW,
123 0x0e => KeyCode::KeyE,
124 0x0f => KeyCode::KeyR,
125 0x10 => KeyCode::KeyY,
126 0x11 => KeyCode::KeyT,
127 0x12 => KeyCode::Digit1,
128 0x13 => KeyCode::Digit2,
129 0x14 => KeyCode::Digit3,
130 0x15 => KeyCode::Digit4,
131 0x16 => KeyCode::Digit6,
132 0x17 => KeyCode::Digit5,
133 0x18 => KeyCode::Equal,
134 0x19 => KeyCode::Digit9,
135 0x1a => KeyCode::Digit7,
136 0x1b => KeyCode::Minus,
137 0x1c => KeyCode::Digit8,
138 0x1d => KeyCode::Digit0,
139 0x1e => KeyCode::BracketRight,
140 0x1f => KeyCode::KeyO,
141 0x20 => KeyCode::KeyU,
142 0x21 => KeyCode::BracketLeft,
143 0x22 => KeyCode::KeyI,
144 0x23 => KeyCode::KeyP,
145 0x24 => KeyCode::Enter,
146 0x25 => KeyCode::KeyL,
147 0x26 => KeyCode::KeyJ,
148 0x27 => KeyCode::Quote,
149 0x28 => KeyCode::KeyK,
150 0x29 => KeyCode::Semicolon,
151 0x2a => KeyCode::Backslash,
152 0x2b => KeyCode::Comma,
153 0x2c => KeyCode::Slash,
154 0x2d => KeyCode::KeyN,
155 0x2e => KeyCode::KeyM,
156 0x2f => KeyCode::Period,
157 0x30 => KeyCode::Tab,
158 0x31 => KeyCode::Space,
159 0x32 => KeyCode::Backquote,
160 0x33 => KeyCode::Backspace,
161 // 0x34 => unknown, // kVK_Powerbook_KeypadEnter
162 0x35 => KeyCode::Escape,
163 0x36 => KeyCode::SuperRight,
164 0x37 => KeyCode::SuperLeft,
165 0x38 => KeyCode::ShiftLeft,
166 0x39 => KeyCode::CapsLock,
167 0x3a => KeyCode::AltLeft,
168 0x3b => KeyCode::ControlLeft,
169 0x3c => KeyCode::ShiftRight,
170 0x3d => KeyCode::AltRight,
171 0x3e => KeyCode::ControlRight,
172 0x3f => KeyCode::Fn,
173 0x40 => KeyCode::F17,
174 0x41 => KeyCode::NumpadDecimal,
175 // 0x42 -> unknown,
176 0x43 => KeyCode::NumpadMultiply,
177 // 0x44 => unknown,
178 0x45 => KeyCode::NumpadAdd,
179 // 0x46 => unknown,
180 0x47 => KeyCode::NumLock, // kVK_ANSI_KeypadClear
181 0x48 => KeyCode::AudioVolumeUp,
182 0x49 => KeyCode::AudioVolumeDown,
183 0x4a => KeyCode::AudioVolumeMute,
184 0x4b => KeyCode::NumpadDivide,
185 0x4c => KeyCode::NumpadEnter,
186 // 0x4d => unknown,
187 0x4e => KeyCode::NumpadSubtract,
188 0x4f => KeyCode::F18,
189 0x50 => KeyCode::F19,
190 0x51 => KeyCode::NumpadEqual,
191 0x52 => KeyCode::Numpad0,
192 0x53 => KeyCode::Numpad1,
193 0x54 => KeyCode::Numpad2,
194 0x55 => KeyCode::Numpad3,
195 0x56 => KeyCode::Numpad4,
196 0x57 => KeyCode::Numpad5,
197 0x58 => KeyCode::Numpad6,
198 0x59 => KeyCode::Numpad7,
199 0x5a => KeyCode::F20,
200 0x5b => KeyCode::Numpad8,
201 0x5c => KeyCode::Numpad9,
202 0x5d => KeyCode::IntlYen,
203 0x5e => KeyCode::IntlRo,
204 0x5f => KeyCode::NumpadComma,
205 0x60 => KeyCode::F5,
206 0x61 => KeyCode::F6,
207 0x62 => KeyCode::F7,
208 0x63 => KeyCode::F3,
209 0x64 => KeyCode::F8,
210 0x65 => KeyCode::F9,
211 0x66 => KeyCode::Lang2,
212 0x67 => KeyCode::F11,
213 0x68 => KeyCode::Lang1,
214 0x69 => KeyCode::F13,
215 0x6a => KeyCode::F16,
216 0x6b => KeyCode::F14,
217 // 0x6c => unknown,
218 0x6d => KeyCode::F10,
219 0x6e => KeyCode::ContextMenu,
220 0x6f => KeyCode::F12,
221 // 0x70 => unknown,
222 0x71 => KeyCode::F15,
223 0x72 => KeyCode::Insert,
224 0x73 => KeyCode::Home,
225 0x74 => KeyCode::PageUp,
226 0x75 => KeyCode::Delete,
227 0x76 => KeyCode::F4,
228 0x77 => KeyCode::End,
229 0x78 => KeyCode::F2,
230 0x79 => KeyCode::PageDown,
231 0x7a => KeyCode::F1,
232 0x7b => KeyCode::ArrowLeft,
233 0x7c => KeyCode::ArrowRight,
234 0x7d => KeyCode::ArrowDown,
235 0x7e => KeyCode::ArrowUp,
236 0x7f => KeyCode::Power, // On 10.7 and 10.8 only
237 _ => return PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
238 })
239}
240