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