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 })
74 }
75 NSEventType::NSLeftMouseDown => {
76 window_height.map(|window_height| Self::LeftMouseDown {
77 position: vec2f(
78 native_event.locationInWindow().x as f32,
79 window_height - native_event.locationInWindow().y as f32,
80 ),
81 cmd: native_event
82 .modifierFlags()
83 .contains(NSEventModifierFlags::NSCommandKeyMask),
84 })
85 }
86 NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp {
87 position: vec2f(
88 native_event.locationInWindow().x as f32,
89 window_height - native_event.locationInWindow().y as f32,
90 ),
91 }),
92 NSEventType::NSLeftMouseDragged => {
93 window_height.map(|window_height| Self::LeftMouseDragged {
94 position: vec2f(
95 native_event.locationInWindow().x as f32,
96 window_height - native_event.locationInWindow().y as f32,
97 ),
98 })
99 }
100 NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
101 position: vec2f(
102 native_event.locationInWindow().x as f32,
103 window_height - native_event.locationInWindow().y as f32,
104 ),
105 delta: vec2f(
106 native_event.scrollingDeltaX() as f32,
107 native_event.scrollingDeltaY() as f32,
108 ),
109 precise: native_event.hasPreciseScrollingDeltas() == YES,
110 }),
111 NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
112 position: vec2f(
113 native_event.locationInWindow().x as f32,
114 window_height - native_event.locationInWindow().y as f32,
115 ),
116 }),
117 _ => None,
118 }
119 }
120}