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