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