use std::os::raw::c_ushort; use cocoa::{ appkit::{NSEvent, NSEventModifierFlags}, base::id, }; use crate::{ dpi::LogicalSize, event::{ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}, platform_impl::platform::{ util::{IdRef, Never}, DEVICE_ID, }, }; #[derive(Debug)] pub enum EventWrapper { StaticEvent(Event<'static, Never>), EventProxy(EventProxy), } #[derive(Debug, PartialEq)] pub enum EventProxy { HiDpiFactorChangedProxy { ns_window: IdRef, suggested_size: LogicalSize, hidpi_factor: f64, }, } pub fn char_to_keycode(c: char) -> Option { // We only translate keys that are affected by keyboard layout. // // Note that since keys are translated in a somewhat "dumb" way (reading character) // there is a concern that some combination, i.e. Cmd+char, causes the wrong // letter to be received, and so we receive the wrong key. // // Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626 Some(match c { 'a' | 'A' => VirtualKeyCode::A, 'b' | 'B' => VirtualKeyCode::B, 'c' | 'C' => VirtualKeyCode::C, 'd' | 'D' => VirtualKeyCode::D, 'e' | 'E' => VirtualKeyCode::E, 'f' | 'F' => VirtualKeyCode::F, 'g' | 'G' => VirtualKeyCode::G, 'h' | 'H' => VirtualKeyCode::H, 'i' | 'I' => VirtualKeyCode::I, 'j' | 'J' => VirtualKeyCode::J, 'k' | 'K' => VirtualKeyCode::K, 'l' | 'L' => VirtualKeyCode::L, 'm' | 'M' => VirtualKeyCode::M, 'n' | 'N' => VirtualKeyCode::N, 'o' | 'O' => VirtualKeyCode::O, 'p' | 'P' => VirtualKeyCode::P, 'q' | 'Q' => VirtualKeyCode::Q, 'r' | 'R' => VirtualKeyCode::R, 's' | 'S' => VirtualKeyCode::S, 't' | 'T' => VirtualKeyCode::T, 'u' | 'U' => VirtualKeyCode::U, 'v' | 'V' => VirtualKeyCode::V, 'w' | 'W' => VirtualKeyCode::W, 'x' | 'X' => VirtualKeyCode::X, 'y' | 'Y' => VirtualKeyCode::Y, 'z' | 'Z' => VirtualKeyCode::Z, '1' | '!' => VirtualKeyCode::Key1, '2' | '@' => VirtualKeyCode::Key2, '3' | '#' => VirtualKeyCode::Key3, '4' | '$' => VirtualKeyCode::Key4, '5' | '%' => VirtualKeyCode::Key5, '6' | '^' => VirtualKeyCode::Key6, '7' | '&' => VirtualKeyCode::Key7, '8' | '*' => VirtualKeyCode::Key8, '9' | '(' => VirtualKeyCode::Key9, '0' | ')' => VirtualKeyCode::Key0, '=' | '+' => VirtualKeyCode::Equals, '-' | '_' => VirtualKeyCode::Minus, ']' | '}' => VirtualKeyCode::RBracket, '[' | '{' => VirtualKeyCode::LBracket, '\'' | '"' => VirtualKeyCode::Apostrophe, ';' | ':' => VirtualKeyCode::Semicolon, '\\' | '|' => VirtualKeyCode::Backslash, ',' | '<' => VirtualKeyCode::Comma, '/' | '?' => VirtualKeyCode::Slash, '.' | '>' => VirtualKeyCode::Period, '`' | '~' => VirtualKeyCode::Grave, _ => return None, }) } pub fn scancode_to_keycode(scancode: c_ushort) -> Option { Some(match scancode { 0x00 => VirtualKeyCode::A, 0x01 => VirtualKeyCode::S, 0x02 => VirtualKeyCode::D, 0x03 => VirtualKeyCode::F, 0x04 => VirtualKeyCode::H, 0x05 => VirtualKeyCode::G, 0x06 => VirtualKeyCode::Z, 0x07 => VirtualKeyCode::X, 0x08 => VirtualKeyCode::C, 0x09 => VirtualKeyCode::V, //0x0a => World 1, 0x0b => VirtualKeyCode::B, 0x0c => VirtualKeyCode::Q, 0x0d => VirtualKeyCode::W, 0x0e => VirtualKeyCode::E, 0x0f => VirtualKeyCode::R, 0x10 => VirtualKeyCode::Y, 0x11 => VirtualKeyCode::T, 0x12 => VirtualKeyCode::Key1, 0x13 => VirtualKeyCode::Key2, 0x14 => VirtualKeyCode::Key3, 0x15 => VirtualKeyCode::Key4, 0x16 => VirtualKeyCode::Key6, 0x17 => VirtualKeyCode::Key5, 0x18 => VirtualKeyCode::Equals, 0x19 => VirtualKeyCode::Key9, 0x1a => VirtualKeyCode::Key7, 0x1b => VirtualKeyCode::Minus, 0x1c => VirtualKeyCode::Key8, 0x1d => VirtualKeyCode::Key0, 0x1e => VirtualKeyCode::RBracket, 0x1f => VirtualKeyCode::O, 0x20 => VirtualKeyCode::U, 0x21 => VirtualKeyCode::LBracket, 0x22 => VirtualKeyCode::I, 0x23 => VirtualKeyCode::P, 0x24 => VirtualKeyCode::Return, 0x25 => VirtualKeyCode::L, 0x26 => VirtualKeyCode::J, 0x27 => VirtualKeyCode::Apostrophe, 0x28 => VirtualKeyCode::K, 0x29 => VirtualKeyCode::Semicolon, 0x2a => VirtualKeyCode::Backslash, 0x2b => VirtualKeyCode::Comma, 0x2c => VirtualKeyCode::Slash, 0x2d => VirtualKeyCode::N, 0x2e => VirtualKeyCode::M, 0x2f => VirtualKeyCode::Period, 0x30 => VirtualKeyCode::Tab, 0x31 => VirtualKeyCode::Space, 0x32 => VirtualKeyCode::Grave, 0x33 => VirtualKeyCode::Back, //0x34 => unkown, 0x35 => VirtualKeyCode::Escape, 0x36 => VirtualKeyCode::RWin, 0x37 => VirtualKeyCode::LWin, 0x38 => VirtualKeyCode::LShift, //0x39 => Caps lock, 0x3a => VirtualKeyCode::LAlt, 0x3b => VirtualKeyCode::LControl, 0x3c => VirtualKeyCode::RShift, 0x3d => VirtualKeyCode::RAlt, 0x3e => VirtualKeyCode::RControl, //0x3f => Fn key, 0x40 => VirtualKeyCode::F17, 0x41 => VirtualKeyCode::Decimal, //0x42 -> unkown, 0x43 => VirtualKeyCode::Multiply, //0x44 => unkown, 0x45 => VirtualKeyCode::Add, //0x46 => unkown, 0x47 => VirtualKeyCode::Numlock, //0x48 => KeypadClear, 0x49 => VirtualKeyCode::VolumeUp, 0x4a => VirtualKeyCode::VolumeDown, 0x4b => VirtualKeyCode::Divide, 0x4c => VirtualKeyCode::NumpadEnter, //0x4d => unkown, 0x4e => VirtualKeyCode::Subtract, 0x4f => VirtualKeyCode::F18, 0x50 => VirtualKeyCode::F19, 0x51 => VirtualKeyCode::NumpadEquals, 0x52 => VirtualKeyCode::Numpad0, 0x53 => VirtualKeyCode::Numpad1, 0x54 => VirtualKeyCode::Numpad2, 0x55 => VirtualKeyCode::Numpad3, 0x56 => VirtualKeyCode::Numpad4, 0x57 => VirtualKeyCode::Numpad5, 0x58 => VirtualKeyCode::Numpad6, 0x59 => VirtualKeyCode::Numpad7, 0x5a => VirtualKeyCode::F20, 0x5b => VirtualKeyCode::Numpad8, 0x5c => VirtualKeyCode::Numpad9, 0x5d => VirtualKeyCode::Yen, //0x5e => JIS Ro, //0x5f => unkown, 0x60 => VirtualKeyCode::F5, 0x61 => VirtualKeyCode::F6, 0x62 => VirtualKeyCode::F7, 0x63 => VirtualKeyCode::F3, 0x64 => VirtualKeyCode::F8, 0x65 => VirtualKeyCode::F9, //0x66 => JIS Eisuu (macOS), 0x67 => VirtualKeyCode::F11, //0x68 => JIS Kanna (macOS), 0x69 => VirtualKeyCode::F13, 0x6a => VirtualKeyCode::F16, 0x6b => VirtualKeyCode::F14, //0x6c => unkown, 0x6d => VirtualKeyCode::F10, //0x6e => unkown, 0x6f => VirtualKeyCode::F12, //0x70 => unkown, 0x71 => VirtualKeyCode::F15, 0x72 => VirtualKeyCode::Insert, 0x73 => VirtualKeyCode::Home, 0x74 => VirtualKeyCode::PageUp, 0x75 => VirtualKeyCode::Delete, 0x76 => VirtualKeyCode::F4, 0x77 => VirtualKeyCode::End, 0x78 => VirtualKeyCode::F2, 0x79 => VirtualKeyCode::PageDown, 0x7a => VirtualKeyCode::F1, 0x7b => VirtualKeyCode::Left, 0x7c => VirtualKeyCode::Right, 0x7d => VirtualKeyCode::Down, 0x7e => VirtualKeyCode::Up, //0x7f => unkown, 0xa => VirtualKeyCode::Caret, _ => return None, }) } // While F1-F20 have scancodes we can match on, we have to check against UTF-16 // constants for the rest. // https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ pub fn check_function_keys(string: &str) -> Option { if let Some(ch) = string.encode_utf16().next() { return Some(match ch { 0xf718 => VirtualKeyCode::F21, 0xf719 => VirtualKeyCode::F22, 0xf71a => VirtualKeyCode::F23, 0xf71b => VirtualKeyCode::F24, _ => return None, }); } None } pub fn event_mods(event: id) -> ModifiersState { let flags = unsafe { NSEvent::modifierFlags(event) }; let mut m = ModifiersState::empty(); m.set( ModifiersState::SHIFT, flags.contains(NSEventModifierFlags::NSShiftKeyMask), ); m.set( ModifiersState::CTRL, flags.contains(NSEventModifierFlags::NSControlKeyMask), ); m.set( ModifiersState::ALT, flags.contains(NSEventModifierFlags::NSAlternateKeyMask), ); m.set( ModifiersState::LOGO, flags.contains(NSEventModifierFlags::NSCommandKeyMask), ); m } pub fn get_scancode(event: cocoa::base::id) -> c_ushort { // In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character, // and there is no easy way to navtively retrieve the layout-dependent character. // In winit, we use keycode to refer to the key's character, and so this function aligns // AppKit's terminology with ours. unsafe { msg_send![event, keyCode] } } pub unsafe fn modifier_event( ns_event: id, keymask: NSEventModifierFlags, was_key_pressed: bool, ) -> Option> { if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask) || was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) { let state = if was_key_pressed { ElementState::Released } else { ElementState::Pressed }; let scancode = get_scancode(ns_event); let virtual_keycode = scancode_to_keycode(scancode); Some(WindowEvent::KeyboardInput { device_id: DEVICE_ID, input: KeyboardInput { state, scancode: scancode as _, virtual_keycode, modifiers: event_mods(ns_event), }, is_synthetic: false, }) } else { None } }