1use super::Event;
2use crate::{geometry::vector::vec2f, keymap::Keystroke};
3use cocoa::appkit::{
4 NSDeleteFunctionKey as DELETE_KEY, NSDownArrowFunctionKey as ARROW_DOWN_KEY,
5 NSLeftArrowFunctionKey as ARROW_LEFT_KEY, NSPageDownFunctionKey as PAGE_DOWN_KEY,
6 NSPageUpFunctionKey as PAGE_UP_KEY, NSRightArrowFunctionKey as ARROW_RIGHT_KEY,
7 NSUpArrowFunctionKey as ARROW_UP_KEY,
8};
9use cocoa::{
10 appkit::{NSEvent as _, NSEventModifierFlags, NSEventType},
11 base::{id, YES},
12 foundation::NSString as _,
13};
14use std::{ffi::CStr, os::raw::c_char};
15
16const BACKSPACE_KEY: u16 = 0x7f;
17const ENTER_KEY: u16 = 0x0d;
18const ESCAPE_KEY: u16 = 0x1b;
19
20impl Event {
21 pub unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Self> {
22 let event_type = native_event.eventType();
23
24 // Filter out event types that aren't in the NSEventType enum.
25 // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
26 match event_type as u64 {
27 0 | 21 | 32 | 33 | 35 | 36 | 37 => {
28 return None;
29 }
30 _ => {}
31 }
32
33 match event_type {
34 NSEventType::NSKeyDown => {
35 let modifiers = native_event.modifierFlags();
36 let unmodified_chars = native_event.charactersIgnoringModifiers();
37 let unmodified_chars = CStr::from_ptr(unmodified_chars.UTF8String() as *mut c_char)
38 .to_str()
39 .unwrap();
40
41 let unmodified_chars = if let Some(first_char) = unmodified_chars.chars().next() {
42 match first_char as u16 {
43 ARROW_UP_KEY => "up",
44 ARROW_DOWN_KEY => "down",
45 ARROW_LEFT_KEY => "left",
46 ARROW_RIGHT_KEY => "right",
47 PAGE_UP_KEY => "pageup",
48 PAGE_DOWN_KEY => "pagedown",
49 BACKSPACE_KEY => "backspace",
50 ENTER_KEY => "enter",
51 DELETE_KEY => "delete",
52 ESCAPE_KEY => "escape",
53 _ => unmodified_chars,
54 }
55 } else {
56 return None;
57 };
58
59 let chars = native_event.characters();
60 let chars = CStr::from_ptr(chars.UTF8String() as *mut c_char)
61 .to_str()
62 .unwrap()
63 .into();
64
65 Some(Self::KeyDown {
66 keystroke: Keystroke {
67 ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
68 alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
69 shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
70 cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
71 key: unmodified_chars.into(),
72 },
73 chars,
74 })
75 }
76 NSEventType::NSLeftMouseDown => {
77 window_height.map(|window_height| Self::LeftMouseDown {
78 position: vec2f(
79 native_event.locationInWindow().x as f32,
80 window_height - native_event.locationInWindow().y as f32,
81 ),
82 cmd: native_event
83 .modifierFlags()
84 .contains(NSEventModifierFlags::NSCommandKeyMask),
85 })
86 }
87 NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp {
88 position: vec2f(
89 native_event.locationInWindow().x as f32,
90 window_height - native_event.locationInWindow().y as f32,
91 ),
92 }),
93 NSEventType::NSLeftMouseDragged => {
94 window_height.map(|window_height| Self::LeftMouseDragged {
95 position: vec2f(
96 native_event.locationInWindow().x as f32,
97 window_height - native_event.locationInWindow().y as f32,
98 ),
99 })
100 }
101 NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
102 position: vec2f(
103 native_event.locationInWindow().x as f32,
104 window_height - native_event.locationInWindow().y as f32,
105 ),
106 delta: vec2f(
107 native_event.scrollingDeltaX() as f32,
108 native_event.scrollingDeltaY() as f32,
109 ),
110 precise: native_event.hasPreciseScrollingDeltas() == YES,
111 }),
112 _ => None,
113 }
114 }
115}