1use crate::{geometry::vector::vec2f, keymap::Keystroke, platform::Event};
2use cocoa::appkit::{
3 NSDeleteFunctionKey as DELETE_KEY, NSDownArrowFunctionKey as ARROW_DOWN_KEY,
4 NSLeftArrowFunctionKey as ARROW_LEFT_KEY, NSPageDownFunctionKey as PAGE_DOWN_KEY,
5 NSPageUpFunctionKey as PAGE_UP_KEY, NSRightArrowFunctionKey as ARROW_RIGHT_KEY,
6 NSUpArrowFunctionKey as ARROW_UP_KEY,
7};
8use cocoa::{
9 appkit::{NSEvent, NSEventModifierFlags, NSEventType},
10 base::{id, nil, YES},
11 foundation::NSString as _,
12};
13use std::{ffi::CStr, os::raw::c_char};
14
15const BACKSPACE_KEY: u16 = 0x7f;
16const ENTER_KEY: u16 = 0x0d;
17const ESCAPE_KEY: u16 = 0x1b;
18const TAB_KEY: u16 = 0x09;
19
20impl Event {
21 pub unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Self> {
22 let event_type = native_event.eventType();
23
24 // Filter out event types that aren't in the NSEventType enum.
25 // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
26 match event_type as u64 {
27 0 | 21 | 32 | 33 | 35 | 36 | 37 => {
28 return None;
29 }
30 _ => {}
31 }
32
33 match event_type {
34 NSEventType::NSKeyDown => {
35 let modifiers = native_event.modifierFlags();
36 let unmodified_chars = native_event.charactersIgnoringModifiers();
37 let unmodified_chars = CStr::from_ptr(unmodified_chars.UTF8String() as *mut c_char)
38 .to_str()
39 .unwrap();
40
41 let unmodified_chars = if let Some(first_char) = unmodified_chars.chars().next() {
42 match first_char as u16 {
43 ARROW_UP_KEY => "up",
44 ARROW_DOWN_KEY => "down",
45 ARROW_LEFT_KEY => "left",
46 ARROW_RIGHT_KEY => "right",
47 PAGE_UP_KEY => "pageup",
48 PAGE_DOWN_KEY => "pagedown",
49 BACKSPACE_KEY => "backspace",
50 ENTER_KEY => "enter",
51 DELETE_KEY => "delete",
52 ESCAPE_KEY => "escape",
53 TAB_KEY => "tab",
54 _ => unmodified_chars,
55 }
56 } else {
57 return None;
58 };
59
60 let chars = native_event.characters();
61 let chars = CStr::from_ptr(chars.UTF8String() as *mut c_char)
62 .to_str()
63 .unwrap()
64 .into();
65
66 Some(Self::KeyDown {
67 keystroke: Keystroke {
68 ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
69 alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
70 shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
71 cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
72 key: unmodified_chars.into(),
73 },
74 chars,
75 is_held: native_event.isARepeat() == YES,
76 })
77 }
78 NSEventType::NSLeftMouseDown => {
79 window_height.map(|window_height| Self::LeftMouseDown {
80 position: vec2f(
81 native_event.locationInWindow().x as f32,
82 window_height - native_event.locationInWindow().y as f32,
83 ),
84 cmd: native_event
85 .modifierFlags()
86 .contains(NSEventModifierFlags::NSCommandKeyMask),
87 })
88 }
89 NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp {
90 position: vec2f(
91 native_event.locationInWindow().x as f32,
92 window_height - native_event.locationInWindow().y as f32,
93 ),
94 }),
95 NSEventType::NSLeftMouseDragged => {
96 window_height.map(|window_height| Self::LeftMouseDragged {
97 position: vec2f(
98 native_event.locationInWindow().x as f32,
99 window_height - native_event.locationInWindow().y as f32,
100 ),
101 })
102 }
103 NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
104 position: vec2f(
105 native_event.locationInWindow().x as f32,
106 window_height - native_event.locationInWindow().y as f32,
107 ),
108 delta: vec2f(
109 native_event.scrollingDeltaX() as f32,
110 native_event.scrollingDeltaY() as f32,
111 ),
112 precise: native_event.hasPreciseScrollingDeltas() == YES,
113 }),
114 NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
115 position: vec2f(
116 native_event.locationInWindow().x as f32,
117 window_height - native_event.locationInWindow().y as f32,
118 ),
119 left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
120 }),
121 _ => None,
122 }
123 }
124}