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