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            }),
133            NSEventType::NSRightMouseDown => {
134                let modifiers = native_event.modifierFlags();
135                window_height.map(|window_height| Self::RightMouseDown {
136                    position: vec2f(
137                        native_event.locationInWindow().x as f32,
138                        window_height - native_event.locationInWindow().y as f32,
139                    ),
140                    ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
141                    alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
142                    shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
143                    cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
144                    click_count: native_event.clickCount() as usize,
145                })
146            }
147            NSEventType::NSRightMouseUp => window_height.map(|window_height| Self::RightMouseUp {
148                position: vec2f(
149                    native_event.locationInWindow().x as f32,
150                    window_height - native_event.locationInWindow().y as f32,
151                ),
152            }),
153            NSEventType::NSOtherMouseDown => {
154                let direction = match native_event.buttonNumber() {
155                    3 => NavigationDirection::Back,
156                    4 => NavigationDirection::Forward,
157                    // Other mouse buttons aren't tracked currently
158                    _ => return None,
159                };
160
161                let modifiers = native_event.modifierFlags();
162                window_height.map(|window_height| Self::NavigateMouseDown {
163                    position: vec2f(
164                        native_event.locationInWindow().x as f32,
165                        window_height - native_event.locationInWindow().y as f32,
166                    ),
167                    direction,
168                    ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
169                    alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
170                    shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
171                    cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
172                    click_count: native_event.clickCount() as usize,
173                })
174            }
175            NSEventType::NSOtherMouseUp => {
176                let direction = match native_event.buttonNumber() {
177                    3 => NavigationDirection::Back,
178                    4 => NavigationDirection::Forward,
179                    // Other mouse buttons aren't tracked currently
180                    _ => return None,
181                };
182
183                window_height.map(|window_height| Self::NavigateMouseUp {
184                    position: vec2f(
185                        native_event.locationInWindow().x as f32,
186                        window_height - native_event.locationInWindow().y as f32,
187                    ),
188                    direction,
189                })
190            }
191            NSEventType::NSLeftMouseDragged => {
192                window_height.map(|window_height| Self::LeftMouseDragged {
193                    position: vec2f(
194                        native_event.locationInWindow().x as f32,
195                        window_height - native_event.locationInWindow().y as f32,
196                    ),
197                })
198            }
199            NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
200                position: vec2f(
201                    native_event.locationInWindow().x as f32,
202                    window_height - native_event.locationInWindow().y as f32,
203                ),
204                delta: vec2f(
205                    native_event.scrollingDeltaX() as f32,
206                    native_event.scrollingDeltaY() as f32,
207                ),
208                precise: native_event.hasPreciseScrollingDeltas() == YES,
209            }),
210            NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
211                position: vec2f(
212                    native_event.locationInWindow().x as f32,
213                    window_height - native_event.locationInWindow().y as f32,
214                ),
215                left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
216            }),
217            _ => None,
218        }
219    }
220}