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            }),
182            NSEventType::NSOtherMouseDown => {
183                let direction = match native_event.buttonNumber() {
184                    3 => NavigationDirection::Back,
185                    4 => NavigationDirection::Forward,
186                    // Other mouse buttons aren't tracked currently
187                    _ => return None,
188                };
189
190                let modifiers = native_event.modifierFlags();
191                window_height.map(|window_height| Self::NavigateMouseDown {
192                    position: vec2f(
193                        native_event.locationInWindow().x as f32,
194                        window_height - native_event.locationInWindow().y as f32,
195                    ),
196                    direction,
197                    ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
198                    alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
199                    shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
200                    cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
201                    click_count: native_event.clickCount() as usize,
202                })
203            }
204            NSEventType::NSOtherMouseUp => {
205                let direction = match native_event.buttonNumber() {
206                    3 => NavigationDirection::Back,
207                    4 => NavigationDirection::Forward,
208                    // Other mouse buttons aren't tracked currently
209                    _ => return None,
210                };
211
212                window_height.map(|window_height| Self::NavigateMouseUp {
213                    position: vec2f(
214                        native_event.locationInWindow().x as f32,
215                        window_height - native_event.locationInWindow().y as f32,
216                    ),
217                    direction,
218                })
219            }
220            NSEventType::NSLeftMouseDragged => {
221                window_height.map(|window_height| Self::LeftMouseDragged {
222                    position: vec2f(
223                        native_event.locationInWindow().x as f32,
224                        window_height - native_event.locationInWindow().y as f32,
225                    ),
226                })
227            }
228            NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
229                position: vec2f(
230                    native_event.locationInWindow().x as f32,
231                    window_height - native_event.locationInWindow().y as f32,
232                ),
233                delta: vec2f(
234                    native_event.scrollingDeltaX() as f32,
235                    native_event.scrollingDeltaY() as f32,
236                ),
237                precise: native_event.hasPreciseScrollingDeltas() == YES,
238            }),
239            NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
240                position: vec2f(
241                    native_event.locationInWindow().x as f32,
242                    window_height - native_event.locationInWindow().y as f32,
243                ),
244                left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
245            }),
246            _ => None,
247        }
248    }
249}