// Copyright 2020 The Druid Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Baseview modifications to druid code: // - collect functions from various files // - update imports, paths etc //! X11 keyboard handling use x11rb::protocol::xproto::{KeyButMask, KeyPressEvent, KeyReleaseEvent}; use keyboard_types::*; use crate::keyboard::code_to_location; /// Convert a hardware scan code to a key. /// /// Note: this is a hardcoded layout. We need to detect the user's /// layout from the system and apply it. fn code_to_key(code: Code, m: Modifiers) -> Key { fn a(s: &str) -> Key { Key::Character(s.into()) } fn s(mods: Modifiers, base: &str, shifted: &str) -> Key { if mods.contains(Modifiers::SHIFT) { Key::Character(shifted.into()) } else { Key::Character(base.into()) } } fn n(mods: Modifiers, base: Key, num: &str) -> Key { if mods.contains(Modifiers::NUM_LOCK) != mods.contains(Modifiers::SHIFT) { Key::Character(num.into()) } else { base } } match code { Code::KeyA => s(m, "a", "A"), Code::KeyB => s(m, "b", "B"), Code::KeyC => s(m, "c", "C"), Code::KeyD => s(m, "d", "D"), Code::KeyE => s(m, "e", "E"), Code::KeyF => s(m, "f", "F"), Code::KeyG => s(m, "g", "G"), Code::KeyH => s(m, "h", "H"), Code::KeyI => s(m, "i", "I"), Code::KeyJ => s(m, "j", "J"), Code::KeyK => s(m, "k", "K"), Code::KeyL => s(m, "l", "L"), Code::KeyM => s(m, "m", "M"), Code::KeyN => s(m, "n", "N"), Code::KeyO => s(m, "o", "O"), Code::KeyP => s(m, "p", "P"), Code::KeyQ => s(m, "q", "Q"), Code::KeyR => s(m, "r", "R"), Code::KeyS => s(m, "s", "S"), Code::KeyT => s(m, "t", "T"), Code::KeyU => s(m, "u", "U"), Code::KeyV => s(m, "v", "V"), Code::KeyW => s(m, "w", "W"), Code::KeyX => s(m, "x", "X"), Code::KeyY => s(m, "y", "Y"), Code::KeyZ => s(m, "z", "Z"), Code::Digit0 => s(m, "0", ")"), Code::Digit1 => s(m, "1", "!"), Code::Digit2 => s(m, "2", "@"), Code::Digit3 => s(m, "3", "#"), Code::Digit4 => s(m, "4", "$"), Code::Digit5 => s(m, "5", "%"), Code::Digit6 => s(m, "6", "^"), Code::Digit7 => s(m, "7", "&"), Code::Digit8 => s(m, "8", "*"), Code::Digit9 => s(m, "9", "("), Code::Backquote => s(m, "`", "~"), Code::Minus => s(m, "-", "_"), Code::Equal => s(m, "=", "+"), Code::BracketLeft => s(m, "[", "{"), Code::BracketRight => s(m, "]", "}"), Code::Backslash => s(m, "\\", "|"), Code::Semicolon => s(m, ";", ":"), Code::Quote => s(m, "'", "\""), Code::Comma => s(m, ",", "<"), Code::Period => s(m, ".", ">"), Code::Slash => s(m, "/", "?"), Code::Space => a(" "), Code::Escape => Key::Escape, Code::Backspace => Key::Backspace, Code::Tab => Key::Tab, Code::Enter => Key::Enter, Code::ControlLeft => Key::Control, Code::ShiftLeft => Key::Shift, Code::ShiftRight => Key::Shift, Code::NumpadMultiply => a("*"), Code::AltLeft => Key::Alt, Code::CapsLock => Key::CapsLock, Code::F1 => Key::F1, Code::F2 => Key::F2, Code::F3 => Key::F3, Code::F4 => Key::F4, Code::F5 => Key::F5, Code::F6 => Key::F6, Code::F7 => Key::F7, Code::F8 => Key::F8, Code::F9 => Key::F9, Code::F10 => Key::F10, Code::NumLock => Key::NumLock, Code::ScrollLock => Key::ScrollLock, Code::Numpad0 => n(m, Key::Insert, "0"), Code::Numpad1 => n(m, Key::End, "1"), Code::Numpad2 => n(m, Key::ArrowDown, "2"), Code::Numpad3 => n(m, Key::PageDown, "3"), Code::Numpad4 => n(m, Key::ArrowLeft, "4"), Code::Numpad5 => n(m, Key::Clear, "5"), Code::Numpad6 => n(m, Key::ArrowRight, "6"), Code::Numpad7 => n(m, Key::Home, "7"), Code::Numpad8 => n(m, Key::ArrowUp, "8"), Code::Numpad9 => n(m, Key::PageUp, "9"), Code::NumpadSubtract => a("-"), Code::NumpadAdd => a("+"), Code::NumpadDecimal => n(m, Key::Delete, "."), Code::IntlBackslash => s(m, "\\", "|"), Code::F11 => Key::F11, Code::F12 => Key::F12, // This mapping is based on the picture in the w3c spec. Code::IntlRo => a("\\"), Code::Convert => Key::Convert, Code::KanaMode => Key::KanaMode, Code::NonConvert => Key::NonConvert, Code::NumpadEnter => Key::Enter, Code::ControlRight => Key::Control, Code::NumpadDivide => a("/"), Code::PrintScreen => Key::PrintScreen, Code::AltRight => Key::Alt, Code::Home => Key::Home, Code::ArrowUp => Key::ArrowUp, Code::PageUp => Key::PageUp, Code::ArrowLeft => Key::ArrowLeft, Code::ArrowRight => Key::ArrowRight, Code::End => Key::End, Code::ArrowDown => Key::ArrowDown, Code::PageDown => Key::PageDown, Code::Insert => Key::Insert, Code::Delete => Key::Delete, Code::AudioVolumeMute => Key::AudioVolumeMute, Code::AudioVolumeDown => Key::AudioVolumeDown, Code::AudioVolumeUp => Key::AudioVolumeUp, Code::NumpadEqual => a("="), Code::Pause => Key::Pause, Code::NumpadComma => a(","), Code::Lang1 => Key::HangulMode, Code::Lang2 => Key::HanjaMode, Code::IntlYen => a("¥"), Code::MetaLeft => Key::Meta, Code::MetaRight => Key::Meta, Code::ContextMenu => Key::ContextMenu, Code::BrowserStop => Key::BrowserStop, Code::Again => Key::Again, Code::Props => Key::Props, Code::Undo => Key::Undo, Code::Select => Key::Select, Code::Copy => Key::Copy, Code::Open => Key::Open, Code::Paste => Key::Paste, Code::Find => Key::Find, Code::Cut => Key::Cut, Code::Help => Key::Help, Code::LaunchApp2 => Key::LaunchApplication2, Code::WakeUp => Key::WakeUp, Code::LaunchApp1 => Key::LaunchApplication1, Code::LaunchMail => Key::LaunchMail, Code::BrowserFavorites => Key::BrowserFavorites, Code::BrowserBack => Key::BrowserBack, Code::BrowserForward => Key::BrowserForward, Code::Eject => Key::Eject, Code::MediaTrackNext => Key::MediaTrackNext, Code::MediaPlayPause => Key::MediaPlayPause, Code::MediaTrackPrevious => Key::MediaTrackPrevious, Code::MediaStop => Key::MediaStop, Code::MediaSelect => Key::LaunchMediaPlayer, Code::BrowserHome => Key::BrowserHome, Code::BrowserRefresh => Key::BrowserRefresh, Code::BrowserSearch => Key::BrowserSearch, _ => Key::Unidentified, } } #[cfg(target_os = "linux")] /// Map hardware keycode to code. /// /// In theory, the hardware keycode is device dependent, but in /// practice it's probably pretty reliable. /// /// The logic is based on NativeKeyToDOMCodeName.h in Mozilla. fn hardware_keycode_to_code(hw_keycode: u16) -> Code { match hw_keycode { 0x0009 => Code::Escape, 0x000A => Code::Digit1, 0x000B => Code::Digit2, 0x000C => Code::Digit3, 0x000D => Code::Digit4, 0x000E => Code::Digit5, 0x000F => Code::Digit6, 0x0010 => Code::Digit7, 0x0011 => Code::Digit8, 0x0012 => Code::Digit9, 0x0013 => Code::Digit0, 0x0014 => Code::Minus, 0x0015 => Code::Equal, 0x0016 => Code::Backspace, 0x0017 => Code::Tab, 0x0018 => Code::KeyQ, 0x0019 => Code::KeyW, 0x001A => Code::KeyE, 0x001B => Code::KeyR, 0x001C => Code::KeyT, 0x001D => Code::KeyY, 0x001E => Code::KeyU, 0x001F => Code::KeyI, 0x0020 => Code::KeyO, 0x0021 => Code::KeyP, 0x0022 => Code::BracketLeft, 0x0023 => Code::BracketRight, 0x0024 => Code::Enter, 0x0025 => Code::ControlLeft, 0x0026 => Code::KeyA, 0x0027 => Code::KeyS, 0x0028 => Code::KeyD, 0x0029 => Code::KeyF, 0x002A => Code::KeyG, 0x002B => Code::KeyH, 0x002C => Code::KeyJ, 0x002D => Code::KeyK, 0x002E => Code::KeyL, 0x002F => Code::Semicolon, 0x0030 => Code::Quote, 0x0031 => Code::Backquote, 0x0032 => Code::ShiftLeft, 0x0033 => Code::Backslash, 0x0034 => Code::KeyZ, 0x0035 => Code::KeyX, 0x0036 => Code::KeyC, 0x0037 => Code::KeyV, 0x0038 => Code::KeyB, 0x0039 => Code::KeyN, 0x003A => Code::KeyM, 0x003B => Code::Comma, 0x003C => Code::Period, 0x003D => Code::Slash, 0x003E => Code::ShiftRight, 0x003F => Code::NumpadMultiply, 0x0040 => Code::AltLeft, 0x0041 => Code::Space, 0x0042 => Code::CapsLock, 0x0043 => Code::F1, 0x0044 => Code::F2, 0x0045 => Code::F3, 0x0046 => Code::F4, 0x0047 => Code::F5, 0x0048 => Code::F6, 0x0049 => Code::F7, 0x004A => Code::F8, 0x004B => Code::F9, 0x004C => Code::F10, 0x004D => Code::NumLock, 0x004E => Code::ScrollLock, 0x004F => Code::Numpad7, 0x0050 => Code::Numpad8, 0x0051 => Code::Numpad9, 0x0052 => Code::NumpadSubtract, 0x0053 => Code::Numpad4, 0x0054 => Code::Numpad5, 0x0055 => Code::Numpad6, 0x0056 => Code::NumpadAdd, 0x0057 => Code::Numpad1, 0x0058 => Code::Numpad2, 0x0059 => Code::Numpad3, 0x005A => Code::Numpad0, 0x005B => Code::NumpadDecimal, 0x005E => Code::IntlBackslash, 0x005F => Code::F11, 0x0060 => Code::F12, 0x0061 => Code::IntlRo, 0x0064 => Code::Convert, 0x0065 => Code::KanaMode, 0x0066 => Code::NonConvert, 0x0068 => Code::NumpadEnter, 0x0069 => Code::ControlRight, 0x006A => Code::NumpadDivide, 0x006B => Code::PrintScreen, 0x006C => Code::AltRight, 0x006E => Code::Home, 0x006F => Code::ArrowUp, 0x0070 => Code::PageUp, 0x0071 => Code::ArrowLeft, 0x0072 => Code::ArrowRight, 0x0073 => Code::End, 0x0074 => Code::ArrowDown, 0x0075 => Code::PageDown, 0x0076 => Code::Insert, 0x0077 => Code::Delete, 0x0079 => Code::AudioVolumeMute, 0x007A => Code::AudioVolumeDown, 0x007B => Code::AudioVolumeUp, 0x007D => Code::NumpadEqual, 0x007F => Code::Pause, 0x0081 => Code::NumpadComma, 0x0082 => Code::Lang1, 0x0083 => Code::Lang2, 0x0084 => Code::IntlYen, 0x0085 => Code::MetaLeft, 0x0086 => Code::MetaRight, 0x0087 => Code::ContextMenu, 0x0088 => Code::BrowserStop, 0x0089 => Code::Again, 0x008A => Code::Props, 0x008B => Code::Undo, 0x008C => Code::Select, 0x008D => Code::Copy, 0x008E => Code::Open, 0x008F => Code::Paste, 0x0090 => Code::Find, 0x0091 => Code::Cut, 0x0092 => Code::Help, 0x0094 => Code::LaunchApp2, 0x0097 => Code::WakeUp, 0x0098 => Code::LaunchApp1, // key to right of volume controls on T430s produces 0x9C // but no documentation of what it should map to :/ 0x00A3 => Code::LaunchMail, 0x00A4 => Code::BrowserFavorites, 0x00A6 => Code::BrowserBack, 0x00A7 => Code::BrowserForward, 0x00A9 => Code::Eject, 0x00AB => Code::MediaTrackNext, 0x00AC => Code::MediaPlayPause, 0x00AD => Code::MediaTrackPrevious, 0x00AE => Code::MediaStop, 0x00B3 => Code::MediaSelect, 0x00B4 => Code::BrowserHome, 0x00B5 => Code::BrowserRefresh, 0x00E1 => Code::BrowserSearch, _ => Code::Unidentified, } } // Extracts the keyboard modifiers from, e.g., the `state` field of // `x11rb::protocol::xproto::ButtonPressEvent` pub(super) fn key_mods(mods: KeyButMask) -> Modifiers { let mut ret = Modifiers::default(); let key_masks = [ (KeyButMask::SHIFT, Modifiers::SHIFT), (KeyButMask::CONTROL, Modifiers::CONTROL), // X11's mod keys are configurable, but this seems // like a reasonable default for US keyboards, at least, // where the "windows" key seems to be MOD_MASK_4. (KeyButMask::MOD1, Modifiers::ALT), (KeyButMask::MOD2, Modifiers::NUM_LOCK), (KeyButMask::MOD4, Modifiers::META), (KeyButMask::LOCK, Modifiers::CAPS_LOCK), ]; for (mask, modifiers) in &key_masks { if mods.contains(*mask) { ret |= *modifiers; } } ret } pub(super) fn convert_key_press_event(key_press: &KeyPressEvent) -> KeyboardEvent { let hw_keycode = key_press.detail; let code = hardware_keycode_to_code(hw_keycode.into()); let modifiers = key_mods(key_press.state); let key = code_to_key(code, modifiers); let location = code_to_location(code); let state = KeyState::Down; KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false } } pub(super) fn convert_key_release_event(key_release: &KeyReleaseEvent) -> KeyboardEvent { let hw_keycode = key_release.detail; let code = hardware_keycode_to_code(hw_keycode.into()); let modifiers = key_mods(key_release.state); let key = code_to_key(code, modifiers); let location = code_to_location(code); let state = KeyState::Up; KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false } }