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