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