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