1use crate::{
2 geometry::vector::vec2f,
3 keymap::Keystroke,
4 platform::{Event, NavigationDirection},
5};
6use cocoa::{
7 appkit::{NSEvent, NSEventModifierFlags, NSEventType},
8 base::{id, nil, YES},
9 foundation::NSString as _,
10};
11use std::{borrow::Cow, ffi::CStr, os::raw::c_char};
12
13pub fn key_to_native(key: &str) -> Cow<str> {
14 use cocoa::appkit::*;
15 let code = match key {
16 "backspace" => 0x7F,
17 "up" => NSUpArrowFunctionKey,
18 "down" => NSDownArrowFunctionKey,
19 "left" => NSLeftArrowFunctionKey,
20 "right" => NSRightArrowFunctionKey,
21 "pageup" => NSPageUpFunctionKey,
22 "pagedown" => NSPageDownFunctionKey,
23 "delete" => NSDeleteFunctionKey,
24 "f1" => NSF1FunctionKey,
25 "f2" => NSF2FunctionKey,
26 "f3" => NSF3FunctionKey,
27 "f4" => NSF4FunctionKey,
28 "f5" => NSF5FunctionKey,
29 "f6" => NSF6FunctionKey,
30 "f7" => NSF7FunctionKey,
31 "f8" => NSF8FunctionKey,
32 "f9" => NSF9FunctionKey,
33 "f10" => NSF10FunctionKey,
34 "f11" => NSF11FunctionKey,
35 "f12" => NSF12FunctionKey,
36 _ => return Cow::Borrowed(key),
37 };
38 Cow::Owned(String::from_utf16(&[code]).unwrap())
39}
40
41impl Event {
42 pub unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Self> {
43 let event_type = native_event.eventType();
44
45 // Filter out event types that aren't in the NSEventType enum.
46 // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
47 match event_type as u64 {
48 0 | 21 | 32 | 33 | 35 | 36 | 37 => {
49 return None;
50 }
51 _ => {}
52 }
53
54 match event_type {
55 NSEventType::NSKeyDown => {
56 let modifiers = native_event.modifierFlags();
57 let ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
58 let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
59 let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
60 let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
61 let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
62
63 let unmodified_chars = CStr::from_ptr(
64 native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char,
65 )
66 .to_str()
67 .unwrap();
68
69 let mut input = None;
70 let unmodified_chars = if let Some(first_char) = unmodified_chars.chars().next() {
71 use cocoa::appkit::*;
72 const BACKSPACE_KEY: u16 = 0x7f;
73 const ENTER_KEY: u16 = 0x0d;
74 const ESCAPE_KEY: u16 = 0x1b;
75 const TAB_KEY: u16 = 0x09;
76 const SHIFT_TAB_KEY: u16 = 0x19;
77 const SPACE_KEY: u16 = b' ' as u16;
78
79 #[allow(non_upper_case_globals)]
80 match first_char as u16 {
81 SPACE_KEY => {
82 input = Some(" ".to_string());
83 "space"
84 }
85 BACKSPACE_KEY => "backspace",
86 ENTER_KEY => "enter",
87 ESCAPE_KEY => "escape",
88 TAB_KEY => "tab",
89 SHIFT_TAB_KEY => "tab",
90
91 NSUpArrowFunctionKey => "up",
92 NSDownArrowFunctionKey => "down",
93 NSLeftArrowFunctionKey => "left",
94 NSRightArrowFunctionKey => "right",
95 NSPageUpFunctionKey => "pageup",
96 NSPageDownFunctionKey => "pagedown",
97 NSDeleteFunctionKey => "delete",
98 NSF1FunctionKey => "f1",
99 NSF2FunctionKey => "f2",
100 NSF3FunctionKey => "f3",
101 NSF4FunctionKey => "f4",
102 NSF5FunctionKey => "f5",
103 NSF6FunctionKey => "f6",
104 NSF7FunctionKey => "f7",
105 NSF8FunctionKey => "f8",
106 NSF9FunctionKey => "f9",
107 NSF10FunctionKey => "f10",
108 NSF11FunctionKey => "f11",
109 NSF12FunctionKey => "f12",
110
111 _ => {
112 if !cmd && !ctrl && !function {
113 input = Some(
114 CStr::from_ptr(
115 native_event.characters().UTF8String() as *mut c_char
116 )
117 .to_str()
118 .unwrap()
119 .into(),
120 );
121 }
122 unmodified_chars
123 }
124 }
125 } else {
126 return None;
127 };
128
129 Some(Self::KeyDown {
130 keystroke: Keystroke {
131 ctrl,
132 alt,
133 shift,
134 cmd,
135 key: unmodified_chars.into(),
136 },
137 input,
138 is_held: native_event.isARepeat() == YES,
139 })
140 }
141 NSEventType::NSLeftMouseDown => {
142 let modifiers = native_event.modifierFlags();
143 window_height.map(|window_height| Self::LeftMouseDown {
144 position: vec2f(
145 native_event.locationInWindow().x as f32,
146 window_height - native_event.locationInWindow().y as f32,
147 ),
148 ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
149 alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
150 shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
151 cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
152 click_count: native_event.clickCount() as usize,
153 })
154 }
155 NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp {
156 position: vec2f(
157 native_event.locationInWindow().x as f32,
158 window_height - native_event.locationInWindow().y as f32,
159 ),
160 click_count: native_event.clickCount() as usize,
161 }),
162 NSEventType::NSRightMouseDown => {
163 let modifiers = native_event.modifierFlags();
164 window_height.map(|window_height| Self::RightMouseDown {
165 position: vec2f(
166 native_event.locationInWindow().x as f32,
167 window_height - native_event.locationInWindow().y as f32,
168 ),
169 ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
170 alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
171 shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
172 cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
173 click_count: native_event.clickCount() as usize,
174 })
175 }
176 NSEventType::NSRightMouseUp => window_height.map(|window_height| Self::RightMouseUp {
177 position: vec2f(
178 native_event.locationInWindow().x as f32,
179 window_height - native_event.locationInWindow().y as f32,
180 ),
181 }),
182 NSEventType::NSOtherMouseDown => {
183 let direction = match native_event.buttonNumber() {
184 3 => NavigationDirection::Back,
185 4 => NavigationDirection::Forward,
186 // Other mouse buttons aren't tracked currently
187 _ => return None,
188 };
189
190 let modifiers = native_event.modifierFlags();
191 window_height.map(|window_height| Self::NavigateMouseDown {
192 position: vec2f(
193 native_event.locationInWindow().x as f32,
194 window_height - native_event.locationInWindow().y as f32,
195 ),
196 direction,
197 ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
198 alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
199 shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
200 cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
201 click_count: native_event.clickCount() as usize,
202 })
203 }
204 NSEventType::NSOtherMouseUp => {
205 let direction = match native_event.buttonNumber() {
206 3 => NavigationDirection::Back,
207 4 => NavigationDirection::Forward,
208 // Other mouse buttons aren't tracked currently
209 _ => return None,
210 };
211
212 window_height.map(|window_height| Self::NavigateMouseUp {
213 position: vec2f(
214 native_event.locationInWindow().x as f32,
215 window_height - native_event.locationInWindow().y as f32,
216 ),
217 direction,
218 })
219 }
220 NSEventType::NSLeftMouseDragged => {
221 window_height.map(|window_height| Self::LeftMouseDragged {
222 position: vec2f(
223 native_event.locationInWindow().x as f32,
224 window_height - native_event.locationInWindow().y as f32,
225 ),
226 })
227 }
228 NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
229 position: vec2f(
230 native_event.locationInWindow().x as f32,
231 window_height - native_event.locationInWindow().y as f32,
232 ),
233 delta: vec2f(
234 native_event.scrollingDeltaX() as f32,
235 native_event.scrollingDeltaY() as f32,
236 ),
237 precise: native_event.hasPreciseScrollingDeltas() == YES,
238 }),
239 NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
240 position: vec2f(
241 native_event.locationInWindow().x as f32,
242 window_height - native_event.locationInWindow().y as f32,
243 ),
244 left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
245 }),
246 _ => None,
247 }
248 }
249}