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