event.rs

  1use crate::{
  2    geometry::vector::vec2f,
  3    keymap::Keystroke,
  4    platform::{Event, NavigationDirection},
  5};
  6use cocoa::{
  7    appkit::{NSEvent, NSEventModifierFlags, NSEventType},
  8    base::{id, nil, YES},
  9    foundation::NSString as _,
 10};
 11use std::{borrow::Cow, ffi::CStr, os::raw::c_char};
 12
 13pub fn key_to_native(key: &str) -> Cow<str> {
 14    use cocoa::appkit::*;
 15    let code = match key {
 16        "backspace" => 0x7F,
 17        "up" => NSUpArrowFunctionKey,
 18        "down" => NSDownArrowFunctionKey,
 19        "left" => NSLeftArrowFunctionKey,
 20        "right" => NSRightArrowFunctionKey,
 21        "pageup" => NSPageUpFunctionKey,
 22        "pagedown" => NSPageDownFunctionKey,
 23        "delete" => NSDeleteFunctionKey,
 24        "f1" => NSF1FunctionKey,
 25        "f2" => NSF2FunctionKey,
 26        "f3" => NSF3FunctionKey,
 27        "f4" => NSF4FunctionKey,
 28        "f5" => NSF5FunctionKey,
 29        "f6" => NSF6FunctionKey,
 30        "f7" => NSF7FunctionKey,
 31        "f8" => NSF8FunctionKey,
 32        "f9" => NSF9FunctionKey,
 33        "f10" => NSF10FunctionKey,
 34        "f11" => NSF11FunctionKey,
 35        "f12" => NSF12FunctionKey,
 36        _ => return Cow::Borrowed(key),
 37    };
 38    Cow::Owned(String::from_utf16(&[code]).unwrap())
 39}
 40
 41impl Event {
 42    pub unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Self> {
 43        let event_type = native_event.eventType();
 44
 45        // Filter out event types that aren't in the NSEventType enum.
 46        // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
 47        match event_type as u64 {
 48            0 | 21 | 32 | 33 | 35 | 36 | 37 => {
 49                return None;
 50            }
 51            _ => {}
 52        }
 53
 54        match event_type {
 55            NSEventType::NSKeyDown => {
 56                let modifiers = native_event.modifierFlags();
 57                let ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
 58                let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
 59                let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
 60                let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
 61                let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
 62
 63                let unmodified_chars = CStr::from_ptr(
 64                    native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char,
 65                )
 66                .to_str()
 67                .unwrap();
 68
 69                let mut input = None;
 70                let unmodified_chars = if let Some(first_char) = unmodified_chars.chars().next() {
 71                    use cocoa::appkit::*;
 72                    const BACKSPACE_KEY: u16 = 0x7f;
 73                    const ENTER_KEY: u16 = 0x0d;
 74                    const ESCAPE_KEY: u16 = 0x1b;
 75                    const TAB_KEY: u16 = 0x09;
 76                    const SHIFT_TAB_KEY: u16 = 0x19;
 77                    const SPACE_KEY: u16 = b' ' as u16;
 78
 79                    #[allow(non_upper_case_globals)]
 80                    match first_char as u16 {
 81                        SPACE_KEY => {
 82                            input = Some(" ".to_string());
 83                            "space"
 84                        }
 85                        BACKSPACE_KEY => "backspace",
 86                        ENTER_KEY => "enter",
 87                        ESCAPE_KEY => "escape",
 88                        TAB_KEY => "tab",
 89                        SHIFT_TAB_KEY => "tab",
 90
 91                        NSUpArrowFunctionKey => "up",
 92                        NSDownArrowFunctionKey => "down",
 93                        NSLeftArrowFunctionKey => "left",
 94                        NSRightArrowFunctionKey => "right",
 95                        NSPageUpFunctionKey => "pageup",
 96                        NSPageDownFunctionKey => "pagedown",
 97                        NSDeleteFunctionKey => "delete",
 98                        NSF1FunctionKey => "f1",
 99                        NSF2FunctionKey => "f2",
100                        NSF3FunctionKey => "f3",
101                        NSF4FunctionKey => "f4",
102                        NSF5FunctionKey => "f5",
103                        NSF6FunctionKey => "f6",
104                        NSF7FunctionKey => "f7",
105                        NSF8FunctionKey => "f8",
106                        NSF9FunctionKey => "f9",
107                        NSF10FunctionKey => "f10",
108                        NSF11FunctionKey => "f11",
109                        NSF12FunctionKey => "f12",
110
111                        _ => {
112                            if !cmd && !ctrl && !function {
113                                input = Some(
114                                    CStr::from_ptr(
115                                        native_event.characters().UTF8String() as *mut c_char
116                                    )
117                                    .to_str()
118                                    .unwrap()
119                                    .into(),
120                                );
121                            }
122                            unmodified_chars
123                        }
124                    }
125                } else {
126                    return None;
127                };
128
129                Some(Self::KeyDown {
130                    keystroke: Keystroke {
131                        ctrl,
132                        alt,
133                        shift,
134                        cmd,
135                        key: unmodified_chars.into(),
136                    },
137                    input,
138                    is_held: native_event.isARepeat() == YES,
139                })
140            }
141            NSEventType::NSLeftMouseDown => {
142                let modifiers = native_event.modifierFlags();
143                window_height.map(|window_height| Self::LeftMouseDown {
144                    position: vec2f(
145                        native_event.locationInWindow().x as f32,
146                        window_height - native_event.locationInWindow().y as f32,
147                    ),
148                    ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
149                    alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
150                    shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
151                    cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
152                    click_count: native_event.clickCount() as usize,
153                })
154            }
155            NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp {
156                position: vec2f(
157                    native_event.locationInWindow().x as f32,
158                    window_height - native_event.locationInWindow().y as f32,
159                ),
160                click_count: native_event.clickCount() as usize,
161            }),
162            NSEventType::NSRightMouseDown => {
163                let modifiers = native_event.modifierFlags();
164                window_height.map(|window_height| Self::RightMouseDown {
165                    position: vec2f(
166                        native_event.locationInWindow().x as f32,
167                        window_height - native_event.locationInWindow().y as f32,
168                    ),
169                    ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
170                    alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
171                    shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
172                    cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
173                    click_count: native_event.clickCount() as usize,
174                })
175            }
176            NSEventType::NSRightMouseUp => window_height.map(|window_height| Self::RightMouseUp {
177                position: vec2f(
178                    native_event.locationInWindow().x as f32,
179                    window_height - native_event.locationInWindow().y as f32,
180                ),
181                click_count: native_event.clickCount() as usize,
182            }),
183            NSEventType::NSOtherMouseDown => {
184                let direction = match native_event.buttonNumber() {
185                    3 => NavigationDirection::Back,
186                    4 => NavigationDirection::Forward,
187                    // Other mouse buttons aren't tracked currently
188                    _ => return None,
189                };
190
191                let modifiers = native_event.modifierFlags();
192                window_height.map(|window_height| Self::NavigateMouseDown {
193                    position: vec2f(
194                        native_event.locationInWindow().x as f32,
195                        window_height - native_event.locationInWindow().y as f32,
196                    ),
197                    direction,
198                    ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
199                    alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
200                    shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
201                    cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
202                    click_count: native_event.clickCount() as usize,
203                })
204            }
205            NSEventType::NSOtherMouseUp => {
206                let direction = match native_event.buttonNumber() {
207                    3 => NavigationDirection::Back,
208                    4 => NavigationDirection::Forward,
209                    // Other mouse buttons aren't tracked currently
210                    _ => return None,
211                };
212
213                window_height.map(|window_height| Self::NavigateMouseUp {
214                    position: vec2f(
215                        native_event.locationInWindow().x as f32,
216                        window_height - native_event.locationInWindow().y as f32,
217                    ),
218                    direction,
219                })
220            }
221            NSEventType::NSLeftMouseDragged => {
222                window_height.map(|window_height| Self::LeftMouseDragged {
223                    position: vec2f(
224                        native_event.locationInWindow().x as f32,
225                        window_height - native_event.locationInWindow().y as f32,
226                    ),
227                })
228            }
229            NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
230                position: vec2f(
231                    native_event.locationInWindow().x as f32,
232                    window_height - native_event.locationInWindow().y as f32,
233                ),
234                delta: vec2f(
235                    native_event.scrollingDeltaX() as f32,
236                    native_event.scrollingDeltaY() as f32,
237                ),
238                precise: native_event.hasPreciseScrollingDeltas() == YES,
239            }),
240            NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
241                position: vec2f(
242                    native_event.locationInWindow().x as f32,
243                    window_height - native_event.locationInWindow().y as f32,
244                ),
245                left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
246            }),
247            _ => None,
248        }
249    }
250}