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 ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
26 let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
27 let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
28 let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
29
30 let unmodified_chars = CStr::from_ptr(
31 native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char,
32 )
33 .to_str()
34 .unwrap();
35
36 let mut input = None;
37 let unmodified_chars = if let Some(first_char) = unmodified_chars.chars().next() {
38 use cocoa::appkit::*;
39 const BACKSPACE_KEY: u16 = 0x7f;
40 const ENTER_KEY: u16 = 0x0d;
41 const ESCAPE_KEY: u16 = 0x1b;
42 const TAB_KEY: u16 = 0x09;
43 const SHIFT_TAB_KEY: u16 = 0x19;
44 const SPACE_KEY: u16 = b' ' as u16;
45
46 #[allow(non_upper_case_globals)]
47 match first_char as u16 {
48 SPACE_KEY => {
49 input = Some(" ".to_string());
50 "space"
51 }
52 BACKSPACE_KEY => "backspace",
53 ENTER_KEY => "enter",
54 ESCAPE_KEY => "escape",
55 TAB_KEY => "tab",
56 SHIFT_TAB_KEY => "tab",
57
58 NSUpArrowFunctionKey => "up",
59 NSDownArrowFunctionKey => "down",
60 NSLeftArrowFunctionKey => "left",
61 NSRightArrowFunctionKey => "right",
62 NSPageUpFunctionKey => "pageup",
63 NSPageDownFunctionKey => "pagedown",
64 NSDeleteFunctionKey => "delete",
65 NSF1FunctionKey => "f1",
66 NSF2FunctionKey => "f2",
67 NSF3FunctionKey => "f3",
68 NSF4FunctionKey => "f4",
69 NSF5FunctionKey => "f5",
70 NSF6FunctionKey => "f6",
71 NSF7FunctionKey => "f7",
72 NSF8FunctionKey => "f8",
73 NSF9FunctionKey => "f9",
74 NSF10FunctionKey => "f10",
75 NSF11FunctionKey => "f11",
76 NSF12FunctionKey => "f12",
77
78 _ => {
79 if !cmd && !ctrl {
80 input = Some(
81 CStr::from_ptr(
82 native_event.characters().UTF8String() as *mut c_char
83 )
84 .to_str()
85 .unwrap()
86 .into(),
87 );
88 }
89 unmodified_chars
90 }
91 }
92 } else {
93 return None;
94 };
95
96 Some(Self::KeyDown {
97 keystroke: Keystroke {
98 ctrl,
99 alt,
100 shift,
101 cmd,
102 key: unmodified_chars.into(),
103 },
104 input,
105 is_held: native_event.isARepeat() == YES,
106 })
107 }
108 NSEventType::NSLeftMouseDown => {
109 let modifiers = native_event.modifierFlags();
110 window_height.map(|window_height| Self::LeftMouseDown {
111 position: vec2f(
112 native_event.locationInWindow().x as f32,
113 window_height - native_event.locationInWindow().y as f32,
114 ),
115 ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
116 alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
117 shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
118 cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
119 click_count: native_event.clickCount() as usize,
120 })
121 }
122 NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp {
123 position: vec2f(
124 native_event.locationInWindow().x as f32,
125 window_height - native_event.locationInWindow().y as f32,
126 ),
127 }),
128 NSEventType::NSLeftMouseDragged => {
129 window_height.map(|window_height| Self::LeftMouseDragged {
130 position: vec2f(
131 native_event.locationInWindow().x as f32,
132 window_height - native_event.locationInWindow().y as f32,
133 ),
134 })
135 }
136 NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
137 position: vec2f(
138 native_event.locationInWindow().x as f32,
139 window_height - native_event.locationInWindow().y as f32,
140 ),
141 delta: vec2f(
142 native_event.scrollingDeltaX() as f32,
143 native_event.scrollingDeltaY() as f32,
144 ),
145 precise: native_event.hasPreciseScrollingDeltas() == YES,
146 }),
147 NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
148 position: vec2f(
149 native_event.locationInWindow().x as f32,
150 window_height - native_event.locationInWindow().y as f32,
151 ),
152 left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
153 }),
154 _ => None,
155 }
156 }
157}