Overhaul the Keyboard API
Overhaul the keyboard API in winit to mimic the W3C specification
to achieve better crossplatform parity. The `KeyboardInput` event
is now uses `KeyEvent` which consists of:
- `physical_key` - a cross platform way to refer to scancodes;
- `logical_key` - keysym value, which shows your key respecting the
layout;
- `text` - the text produced by this keypress;
- `location` - the location of the key on the keyboard;
- `repeat` - whether the key was produced by the repeat.
And also a `platform_specific` field which encapsulates extra
information on desktop platforms, like key without modifiers
and text with all modifiers.
The `Modifiers` were also slightly reworked as in, the information
whether the left or right modifier is pressed is now also exposed
on platforms where it could be queried reliably. The support was
also added for the web and orbital platforms finishing the API
change.
This change made the `OptionAsAlt` API on macOS redundant thus it
was removed all together.
Co-authored-by: Artúr Kovács <kovacs.artur.barnabas@gmail.com>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
Co-authored-by: daxpedda <daxpedda@gmail.com>
Fixes: #2631.
Fixes: #2055.
Fixes: #2032.
Fixes: #1904.
Fixes: #1810.
Fixes: #1700.
Fixes: #1443.
Fixes: #1343.
Fixes: #1208.
Fixes: #1151.
Fixes: #812.
Fixes: #600.
Fixes: #361.
Fixes: #343.
This commit is contained in:
parent
f3f46cb3f6
commit
918430979f
81 changed files with 9577 additions and 3419 deletions
882
src/platform_impl/linux/common/keymap.rs
Normal file
882
src/platform_impl/linux/common/keymap.rs
Normal file
|
|
@ -0,0 +1,882 @@
|
|||
//! Convert XKB keys to Winit keys.
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode};
|
||||
|
||||
/// Map the raw X11-style keycode to the `KeyCode` enum.
|
||||
///
|
||||
/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
|
||||
pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode {
|
||||
let rawkey = keycode - 8;
|
||||
// The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as
|
||||
// libxkbcommon's documentation seems to suggest that the keycode values we're interested in
|
||||
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
|
||||
// I can only hope they agree on what the keycodes mean.
|
||||
//
|
||||
// Some of the keycodes are likely superfluous for our purposes, and some are ones which are
|
||||
// difficult to test the correctness of, or discover the purpose of. Because of this, they've
|
||||
// either been commented out here, or not included at all.
|
||||
match rawkey {
|
||||
0 => KeyCode::Unidentified(NativeKeyCode::Xkb(0)),
|
||||
1 => KeyCode::Escape,
|
||||
2 => KeyCode::Digit1,
|
||||
3 => KeyCode::Digit2,
|
||||
4 => KeyCode::Digit3,
|
||||
5 => KeyCode::Digit4,
|
||||
6 => KeyCode::Digit5,
|
||||
7 => KeyCode::Digit6,
|
||||
8 => KeyCode::Digit7,
|
||||
9 => KeyCode::Digit8,
|
||||
10 => KeyCode::Digit9,
|
||||
11 => KeyCode::Digit0,
|
||||
12 => KeyCode::Minus,
|
||||
13 => KeyCode::Equal,
|
||||
14 => KeyCode::Backspace,
|
||||
15 => KeyCode::Tab,
|
||||
16 => KeyCode::KeyQ,
|
||||
17 => KeyCode::KeyW,
|
||||
18 => KeyCode::KeyE,
|
||||
19 => KeyCode::KeyR,
|
||||
20 => KeyCode::KeyT,
|
||||
21 => KeyCode::KeyY,
|
||||
22 => KeyCode::KeyU,
|
||||
23 => KeyCode::KeyI,
|
||||
24 => KeyCode::KeyO,
|
||||
25 => KeyCode::KeyP,
|
||||
26 => KeyCode::BracketLeft,
|
||||
27 => KeyCode::BracketRight,
|
||||
28 => KeyCode::Enter,
|
||||
29 => KeyCode::ControlLeft,
|
||||
30 => KeyCode::KeyA,
|
||||
31 => KeyCode::KeyS,
|
||||
32 => KeyCode::KeyD,
|
||||
33 => KeyCode::KeyF,
|
||||
34 => KeyCode::KeyG,
|
||||
35 => KeyCode::KeyH,
|
||||
36 => KeyCode::KeyJ,
|
||||
37 => KeyCode::KeyK,
|
||||
38 => KeyCode::KeyL,
|
||||
39 => KeyCode::Semicolon,
|
||||
40 => KeyCode::Quote,
|
||||
41 => KeyCode::Backquote,
|
||||
42 => KeyCode::ShiftLeft,
|
||||
43 => KeyCode::Backslash,
|
||||
44 => KeyCode::KeyZ,
|
||||
45 => KeyCode::KeyX,
|
||||
46 => KeyCode::KeyC,
|
||||
47 => KeyCode::KeyV,
|
||||
48 => KeyCode::KeyB,
|
||||
49 => KeyCode::KeyN,
|
||||
50 => KeyCode::KeyM,
|
||||
51 => KeyCode::Comma,
|
||||
52 => KeyCode::Period,
|
||||
53 => KeyCode::Slash,
|
||||
54 => KeyCode::ShiftRight,
|
||||
55 => KeyCode::NumpadMultiply,
|
||||
56 => KeyCode::AltLeft,
|
||||
57 => KeyCode::Space,
|
||||
58 => KeyCode::CapsLock,
|
||||
59 => KeyCode::F1,
|
||||
60 => KeyCode::F2,
|
||||
61 => KeyCode::F3,
|
||||
62 => KeyCode::F4,
|
||||
63 => KeyCode::F5,
|
||||
64 => KeyCode::F6,
|
||||
65 => KeyCode::F7,
|
||||
66 => KeyCode::F8,
|
||||
67 => KeyCode::F9,
|
||||
68 => KeyCode::F10,
|
||||
69 => KeyCode::NumLock,
|
||||
70 => KeyCode::ScrollLock,
|
||||
71 => KeyCode::Numpad7,
|
||||
72 => KeyCode::Numpad8,
|
||||
73 => KeyCode::Numpad9,
|
||||
74 => KeyCode::NumpadSubtract,
|
||||
75 => KeyCode::Numpad4,
|
||||
76 => KeyCode::Numpad5,
|
||||
77 => KeyCode::Numpad6,
|
||||
78 => KeyCode::NumpadAdd,
|
||||
79 => KeyCode::Numpad1,
|
||||
80 => KeyCode::Numpad2,
|
||||
81 => KeyCode::Numpad3,
|
||||
82 => KeyCode::Numpad0,
|
||||
83 => KeyCode::NumpadDecimal,
|
||||
85 => KeyCode::Lang5,
|
||||
86 => KeyCode::IntlBackslash,
|
||||
87 => KeyCode::F11,
|
||||
88 => KeyCode::F12,
|
||||
89 => KeyCode::IntlRo,
|
||||
90 => KeyCode::Lang3,
|
||||
91 => KeyCode::Lang4,
|
||||
92 => KeyCode::Convert,
|
||||
93 => KeyCode::KanaMode,
|
||||
94 => KeyCode::NonConvert,
|
||||
// 95 => KeyCode::KPJPCOMMA,
|
||||
96 => KeyCode::NumpadEnter,
|
||||
97 => KeyCode::ControlRight,
|
||||
98 => KeyCode::NumpadDivide,
|
||||
99 => KeyCode::PrintScreen,
|
||||
100 => KeyCode::AltRight,
|
||||
// 101 => KeyCode::LINEFEED,
|
||||
102 => KeyCode::Home,
|
||||
103 => KeyCode::ArrowUp,
|
||||
104 => KeyCode::PageUp,
|
||||
105 => KeyCode::ArrowLeft,
|
||||
106 => KeyCode::ArrowRight,
|
||||
107 => KeyCode::End,
|
||||
108 => KeyCode::ArrowDown,
|
||||
109 => KeyCode::PageDown,
|
||||
110 => KeyCode::Insert,
|
||||
111 => KeyCode::Delete,
|
||||
// 112 => KeyCode::MACRO,
|
||||
113 => KeyCode::AudioVolumeMute,
|
||||
114 => KeyCode::AudioVolumeDown,
|
||||
115 => KeyCode::AudioVolumeUp,
|
||||
// 116 => KeyCode::POWER,
|
||||
117 => KeyCode::NumpadEqual,
|
||||
// 118 => KeyCode::KPPLUSMINUS,
|
||||
119 => KeyCode::Pause,
|
||||
// 120 => KeyCode::SCALE,
|
||||
121 => KeyCode::NumpadComma,
|
||||
122 => KeyCode::Lang1,
|
||||
123 => KeyCode::Lang2,
|
||||
124 => KeyCode::IntlYen,
|
||||
125 => KeyCode::SuperLeft,
|
||||
126 => KeyCode::SuperRight,
|
||||
127 => KeyCode::ContextMenu,
|
||||
// 128 => KeyCode::STOP,
|
||||
// 129 => KeyCode::AGAIN,
|
||||
// 130 => KeyCode::PROPS,
|
||||
// 131 => KeyCode::UNDO,
|
||||
// 132 => KeyCode::FRONT,
|
||||
// 133 => KeyCode::COPY,
|
||||
// 134 => KeyCode::OPEN,
|
||||
// 135 => KeyCode::PASTE,
|
||||
// 136 => KeyCode::FIND,
|
||||
// 137 => KeyCode::CUT,
|
||||
// 138 => KeyCode::HELP,
|
||||
// 139 => KeyCode::MENU,
|
||||
// 140 => KeyCode::CALC,
|
||||
// 141 => KeyCode::SETUP,
|
||||
// 142 => KeyCode::SLEEP,
|
||||
// 143 => KeyCode::WAKEUP,
|
||||
// 144 => KeyCode::FILE,
|
||||
// 145 => KeyCode::SENDFILE,
|
||||
// 146 => KeyCode::DELETEFILE,
|
||||
// 147 => KeyCode::XFER,
|
||||
// 148 => KeyCode::PROG1,
|
||||
// 149 => KeyCode::PROG2,
|
||||
// 150 => KeyCode::WWW,
|
||||
// 151 => KeyCode::MSDOS,
|
||||
// 152 => KeyCode::COFFEE,
|
||||
// 153 => KeyCode::ROTATE_DISPLAY,
|
||||
// 154 => KeyCode::CYCLEWINDOWS,
|
||||
// 155 => KeyCode::MAIL,
|
||||
// 156 => KeyCode::BOOKMARKS,
|
||||
// 157 => KeyCode::COMPUTER,
|
||||
// 158 => KeyCode::BACK,
|
||||
// 159 => KeyCode::FORWARD,
|
||||
// 160 => KeyCode::CLOSECD,
|
||||
// 161 => KeyCode::EJECTCD,
|
||||
// 162 => KeyCode::EJECTCLOSECD,
|
||||
163 => KeyCode::MediaTrackNext,
|
||||
164 => KeyCode::MediaPlayPause,
|
||||
165 => KeyCode::MediaTrackPrevious,
|
||||
166 => KeyCode::MediaStop,
|
||||
// 167 => KeyCode::RECORD,
|
||||
// 168 => KeyCode::REWIND,
|
||||
// 169 => KeyCode::PHONE,
|
||||
// 170 => KeyCode::ISO,
|
||||
// 171 => KeyCode::CONFIG,
|
||||
// 172 => KeyCode::HOMEPAGE,
|
||||
// 173 => KeyCode::REFRESH,
|
||||
// 174 => KeyCode::EXIT,
|
||||
// 175 => KeyCode::MOVE,
|
||||
// 176 => KeyCode::EDIT,
|
||||
// 177 => KeyCode::SCROLLUP,
|
||||
// 178 => KeyCode::SCROLLDOWN,
|
||||
// 179 => KeyCode::KPLEFTPAREN,
|
||||
// 180 => KeyCode::KPRIGHTPAREN,
|
||||
// 181 => KeyCode::NEW,
|
||||
// 182 => KeyCode::REDO,
|
||||
183 => KeyCode::F13,
|
||||
184 => KeyCode::F14,
|
||||
185 => KeyCode::F15,
|
||||
186 => KeyCode::F16,
|
||||
187 => KeyCode::F17,
|
||||
188 => KeyCode::F18,
|
||||
189 => KeyCode::F19,
|
||||
190 => KeyCode::F20,
|
||||
191 => KeyCode::F21,
|
||||
192 => KeyCode::F22,
|
||||
193 => KeyCode::F23,
|
||||
194 => KeyCode::F24,
|
||||
// 200 => KeyCode::PLAYCD,
|
||||
// 201 => KeyCode::PAUSECD,
|
||||
// 202 => KeyCode::PROG3,
|
||||
// 203 => KeyCode::PROG4,
|
||||
// 204 => KeyCode::DASHBOARD,
|
||||
// 205 => KeyCode::SUSPEND,
|
||||
// 206 => KeyCode::CLOSE,
|
||||
// 207 => KeyCode::PLAY,
|
||||
// 208 => KeyCode::FASTFORWARD,
|
||||
// 209 => KeyCode::BASSBOOST,
|
||||
// 210 => KeyCode::PRINT,
|
||||
// 211 => KeyCode::HP,
|
||||
// 212 => KeyCode::CAMERA,
|
||||
// 213 => KeyCode::SOUND,
|
||||
// 214 => KeyCode::QUESTION,
|
||||
// 215 => KeyCode::EMAIL,
|
||||
// 216 => KeyCode::CHAT,
|
||||
// 217 => KeyCode::SEARCH,
|
||||
// 218 => KeyCode::CONNECT,
|
||||
// 219 => KeyCode::FINANCE,
|
||||
// 220 => KeyCode::SPORT,
|
||||
// 221 => KeyCode::SHOP,
|
||||
// 222 => KeyCode::ALTERASE,
|
||||
// 223 => KeyCode::CANCEL,
|
||||
// 224 => KeyCode::BRIGHTNESSDOW,
|
||||
// 225 => KeyCode::BRIGHTNESSU,
|
||||
// 226 => KeyCode::MEDIA,
|
||||
// 227 => KeyCode::SWITCHVIDEOMODE,
|
||||
// 228 => KeyCode::KBDILLUMTOGGLE,
|
||||
// 229 => KeyCode::KBDILLUMDOWN,
|
||||
// 230 => KeyCode::KBDILLUMUP,
|
||||
// 231 => KeyCode::SEND,
|
||||
// 232 => KeyCode::REPLY,
|
||||
// 233 => KeyCode::FORWARDMAIL,
|
||||
// 234 => KeyCode::SAVE,
|
||||
// 235 => KeyCode::DOCUMENTS,
|
||||
// 236 => KeyCode::BATTERY,
|
||||
// 237 => KeyCode::BLUETOOTH,
|
||||
// 238 => KeyCode::WLAN,
|
||||
// 239 => KeyCode::UWB,
|
||||
240 => KeyCode::Unidentified(NativeKeyCode::Unidentified),
|
||||
// 241 => KeyCode::VIDEO_NEXT,
|
||||
// 242 => KeyCode::VIDEO_PREV,
|
||||
// 243 => KeyCode::BRIGHTNESS_CYCLE,
|
||||
// 244 => KeyCode::BRIGHTNESS_AUTO,
|
||||
// 245 => KeyCode::DISPLAY_OFF,
|
||||
// 246 => KeyCode::WWAN,
|
||||
// 247 => KeyCode::RFKILL,
|
||||
// 248 => KeyCode::KEY_MICMUTE,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::Xkb(rawkey)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keycode_to_raw(keycode: KeyCode) -> Option<u32> {
|
||||
match keycode {
|
||||
KeyCode::Unidentified(NativeKeyCode::Unidentified) => Some(240),
|
||||
KeyCode::Unidentified(NativeKeyCode::Xkb(raw)) => Some(raw),
|
||||
KeyCode::Escape => Some(1),
|
||||
KeyCode::Digit1 => Some(2),
|
||||
KeyCode::Digit2 => Some(3),
|
||||
KeyCode::Digit3 => Some(4),
|
||||
KeyCode::Digit4 => Some(5),
|
||||
KeyCode::Digit5 => Some(6),
|
||||
KeyCode::Digit6 => Some(7),
|
||||
KeyCode::Digit7 => Some(8),
|
||||
KeyCode::Digit8 => Some(9),
|
||||
KeyCode::Digit9 => Some(10),
|
||||
KeyCode::Digit0 => Some(11),
|
||||
KeyCode::Minus => Some(12),
|
||||
KeyCode::Equal => Some(13),
|
||||
KeyCode::Backspace => Some(14),
|
||||
KeyCode::Tab => Some(15),
|
||||
KeyCode::KeyQ => Some(16),
|
||||
KeyCode::KeyW => Some(17),
|
||||
KeyCode::KeyE => Some(18),
|
||||
KeyCode::KeyR => Some(19),
|
||||
KeyCode::KeyT => Some(20),
|
||||
KeyCode::KeyY => Some(21),
|
||||
KeyCode::KeyU => Some(22),
|
||||
KeyCode::KeyI => Some(23),
|
||||
KeyCode::KeyO => Some(24),
|
||||
KeyCode::KeyP => Some(25),
|
||||
KeyCode::BracketLeft => Some(26),
|
||||
KeyCode::BracketRight => Some(27),
|
||||
KeyCode::Enter => Some(28),
|
||||
KeyCode::ControlLeft => Some(29),
|
||||
KeyCode::KeyA => Some(30),
|
||||
KeyCode::KeyS => Some(31),
|
||||
KeyCode::KeyD => Some(32),
|
||||
KeyCode::KeyF => Some(33),
|
||||
KeyCode::KeyG => Some(34),
|
||||
KeyCode::KeyH => Some(35),
|
||||
KeyCode::KeyJ => Some(36),
|
||||
KeyCode::KeyK => Some(37),
|
||||
KeyCode::KeyL => Some(38),
|
||||
KeyCode::Semicolon => Some(39),
|
||||
KeyCode::Quote => Some(40),
|
||||
KeyCode::Backquote => Some(41),
|
||||
KeyCode::ShiftLeft => Some(42),
|
||||
KeyCode::Backslash => Some(43),
|
||||
KeyCode::KeyZ => Some(44),
|
||||
KeyCode::KeyX => Some(45),
|
||||
KeyCode::KeyC => Some(46),
|
||||
KeyCode::KeyV => Some(47),
|
||||
KeyCode::KeyB => Some(48),
|
||||
KeyCode::KeyN => Some(49),
|
||||
KeyCode::KeyM => Some(50),
|
||||
KeyCode::Comma => Some(51),
|
||||
KeyCode::Period => Some(52),
|
||||
KeyCode::Slash => Some(53),
|
||||
KeyCode::ShiftRight => Some(54),
|
||||
KeyCode::NumpadMultiply => Some(55),
|
||||
KeyCode::AltLeft => Some(56),
|
||||
KeyCode::Space => Some(57),
|
||||
KeyCode::CapsLock => Some(58),
|
||||
KeyCode::F1 => Some(59),
|
||||
KeyCode::F2 => Some(60),
|
||||
KeyCode::F3 => Some(61),
|
||||
KeyCode::F4 => Some(62),
|
||||
KeyCode::F5 => Some(63),
|
||||
KeyCode::F6 => Some(64),
|
||||
KeyCode::F7 => Some(65),
|
||||
KeyCode::F8 => Some(66),
|
||||
KeyCode::F9 => Some(67),
|
||||
KeyCode::F10 => Some(68),
|
||||
KeyCode::NumLock => Some(69),
|
||||
KeyCode::ScrollLock => Some(70),
|
||||
KeyCode::Numpad7 => Some(71),
|
||||
KeyCode::Numpad8 => Some(72),
|
||||
KeyCode::Numpad9 => Some(73),
|
||||
KeyCode::NumpadSubtract => Some(74),
|
||||
KeyCode::Numpad4 => Some(75),
|
||||
KeyCode::Numpad5 => Some(76),
|
||||
KeyCode::Numpad6 => Some(77),
|
||||
KeyCode::NumpadAdd => Some(78),
|
||||
KeyCode::Numpad1 => Some(79),
|
||||
KeyCode::Numpad2 => Some(80),
|
||||
KeyCode::Numpad3 => Some(81),
|
||||
KeyCode::Numpad0 => Some(82),
|
||||
KeyCode::NumpadDecimal => Some(83),
|
||||
KeyCode::Lang5 => Some(85),
|
||||
KeyCode::IntlBackslash => Some(86),
|
||||
KeyCode::F11 => Some(87),
|
||||
KeyCode::F12 => Some(88),
|
||||
KeyCode::IntlRo => Some(89),
|
||||
KeyCode::Lang3 => Some(90),
|
||||
KeyCode::Lang4 => Some(91),
|
||||
KeyCode::Convert => Some(92),
|
||||
KeyCode::KanaMode => Some(93),
|
||||
KeyCode::NonConvert => Some(94),
|
||||
KeyCode::NumpadEnter => Some(96),
|
||||
KeyCode::ControlRight => Some(97),
|
||||
KeyCode::NumpadDivide => Some(98),
|
||||
KeyCode::PrintScreen => Some(99),
|
||||
KeyCode::AltRight => Some(100),
|
||||
KeyCode::Home => Some(102),
|
||||
KeyCode::ArrowUp => Some(103),
|
||||
KeyCode::PageUp => Some(104),
|
||||
KeyCode::ArrowLeft => Some(105),
|
||||
KeyCode::ArrowRight => Some(106),
|
||||
KeyCode::End => Some(107),
|
||||
KeyCode::ArrowDown => Some(108),
|
||||
KeyCode::PageDown => Some(109),
|
||||
KeyCode::Insert => Some(110),
|
||||
KeyCode::Delete => Some(111),
|
||||
KeyCode::AudioVolumeMute => Some(113),
|
||||
KeyCode::AudioVolumeDown => Some(114),
|
||||
KeyCode::AudioVolumeUp => Some(115),
|
||||
KeyCode::NumpadEqual => Some(117),
|
||||
KeyCode::Pause => Some(119),
|
||||
KeyCode::NumpadComma => Some(121),
|
||||
KeyCode::Lang1 => Some(122),
|
||||
KeyCode::Lang2 => Some(123),
|
||||
KeyCode::IntlYen => Some(124),
|
||||
KeyCode::SuperLeft => Some(125),
|
||||
KeyCode::SuperRight => Some(126),
|
||||
KeyCode::ContextMenu => Some(127),
|
||||
KeyCode::MediaTrackNext => Some(163),
|
||||
KeyCode::MediaPlayPause => Some(164),
|
||||
KeyCode::MediaTrackPrevious => Some(165),
|
||||
KeyCode::MediaStop => Some(166),
|
||||
KeyCode::F13 => Some(183),
|
||||
KeyCode::F14 => Some(184),
|
||||
KeyCode::F15 => Some(185),
|
||||
KeyCode::F16 => Some(186),
|
||||
KeyCode::F17 => Some(187),
|
||||
KeyCode::F18 => Some(188),
|
||||
KeyCode::F19 => Some(189),
|
||||
KeyCode::F20 => Some(190),
|
||||
KeyCode::F21 => Some(191),
|
||||
KeyCode::F22 => Some(192),
|
||||
KeyCode::F23 => Some(193),
|
||||
KeyCode::F24 => Some(194),
|
||||
_ => None,
|
||||
}
|
||||
.map(|raw| raw + 8)
|
||||
}
|
||||
|
||||
pub fn keysym_to_key(keysym: u32) -> Key {
|
||||
use xkbcommon_dl::keysyms;
|
||||
match keysym {
|
||||
// TTY function keys
|
||||
keysyms::XKB_KEY_BackSpace => Key::Backspace,
|
||||
keysyms::XKB_KEY_Tab => Key::Tab,
|
||||
// keysyms::XKB_KEY_Linefeed => Key::Linefeed,
|
||||
keysyms::XKB_KEY_Clear => Key::Clear,
|
||||
keysyms::XKB_KEY_Return => Key::Enter,
|
||||
keysyms::XKB_KEY_Pause => Key::Pause,
|
||||
keysyms::XKB_KEY_Scroll_Lock => Key::ScrollLock,
|
||||
keysyms::XKB_KEY_Sys_Req => Key::PrintScreen,
|
||||
keysyms::XKB_KEY_Escape => Key::Escape,
|
||||
keysyms::XKB_KEY_Delete => Key::Delete,
|
||||
|
||||
// IME keys
|
||||
keysyms::XKB_KEY_Multi_key => Key::Compose,
|
||||
keysyms::XKB_KEY_Codeinput => Key::CodeInput,
|
||||
keysyms::XKB_KEY_SingleCandidate => Key::SingleCandidate,
|
||||
keysyms::XKB_KEY_MultipleCandidate => Key::AllCandidates,
|
||||
keysyms::XKB_KEY_PreviousCandidate => Key::PreviousCandidate,
|
||||
|
||||
// Japanese keys
|
||||
keysyms::XKB_KEY_Kanji => Key::KanjiMode,
|
||||
keysyms::XKB_KEY_Muhenkan => Key::NonConvert,
|
||||
keysyms::XKB_KEY_Henkan_Mode => Key::Convert,
|
||||
keysyms::XKB_KEY_Romaji => Key::Romaji,
|
||||
keysyms::XKB_KEY_Hiragana => Key::Hiragana,
|
||||
keysyms::XKB_KEY_Hiragana_Katakana => Key::HiraganaKatakana,
|
||||
keysyms::XKB_KEY_Zenkaku => Key::Zenkaku,
|
||||
keysyms::XKB_KEY_Hankaku => Key::Hankaku,
|
||||
keysyms::XKB_KEY_Zenkaku_Hankaku => Key::ZenkakuHankaku,
|
||||
// keysyms::XKB_KEY_Touroku => Key::Touroku,
|
||||
// keysyms::XKB_KEY_Massyo => Key::Massyo,
|
||||
keysyms::XKB_KEY_Kana_Lock => Key::KanaMode,
|
||||
keysyms::XKB_KEY_Kana_Shift => Key::KanaMode,
|
||||
keysyms::XKB_KEY_Eisu_Shift => Key::Alphanumeric,
|
||||
keysyms::XKB_KEY_Eisu_toggle => Key::Alphanumeric,
|
||||
// NOTE: The next three items are aliases for values we've already mapped.
|
||||
// keysyms::XKB_KEY_Kanji_Bangou => Key::CodeInput,
|
||||
// keysyms::XKB_KEY_Zen_Koho => Key::AllCandidates,
|
||||
// keysyms::XKB_KEY_Mae_Koho => Key::PreviousCandidate,
|
||||
|
||||
// Cursor control & motion
|
||||
keysyms::XKB_KEY_Home => Key::Home,
|
||||
keysyms::XKB_KEY_Left => Key::ArrowLeft,
|
||||
keysyms::XKB_KEY_Up => Key::ArrowUp,
|
||||
keysyms::XKB_KEY_Right => Key::ArrowRight,
|
||||
keysyms::XKB_KEY_Down => Key::ArrowDown,
|
||||
// keysyms::XKB_KEY_Prior => Key::PageUp,
|
||||
keysyms::XKB_KEY_Page_Up => Key::PageUp,
|
||||
// keysyms::XKB_KEY_Next => Key::PageDown,
|
||||
keysyms::XKB_KEY_Page_Down => Key::PageDown,
|
||||
keysyms::XKB_KEY_End => Key::End,
|
||||
// keysyms::XKB_KEY_Begin => Key::Begin,
|
||||
|
||||
// Misc. functions
|
||||
keysyms::XKB_KEY_Select => Key::Select,
|
||||
keysyms::XKB_KEY_Print => Key::PrintScreen,
|
||||
keysyms::XKB_KEY_Execute => Key::Execute,
|
||||
keysyms::XKB_KEY_Insert => Key::Insert,
|
||||
keysyms::XKB_KEY_Undo => Key::Undo,
|
||||
keysyms::XKB_KEY_Redo => Key::Redo,
|
||||
keysyms::XKB_KEY_Menu => Key::ContextMenu,
|
||||
keysyms::XKB_KEY_Find => Key::Find,
|
||||
keysyms::XKB_KEY_Cancel => Key::Cancel,
|
||||
keysyms::XKB_KEY_Help => Key::Help,
|
||||
keysyms::XKB_KEY_Break => Key::Pause,
|
||||
keysyms::XKB_KEY_Mode_switch => Key::ModeChange,
|
||||
// keysyms::XKB_KEY_script_switch => Key::ModeChange,
|
||||
keysyms::XKB_KEY_Num_Lock => Key::NumLock,
|
||||
|
||||
// Keypad keys
|
||||
// keysyms::XKB_KEY_KP_Space => Key::Character(" "),
|
||||
keysyms::XKB_KEY_KP_Tab => Key::Tab,
|
||||
keysyms::XKB_KEY_KP_Enter => Key::Enter,
|
||||
keysyms::XKB_KEY_KP_F1 => Key::F1,
|
||||
keysyms::XKB_KEY_KP_F2 => Key::F2,
|
||||
keysyms::XKB_KEY_KP_F3 => Key::F3,
|
||||
keysyms::XKB_KEY_KP_F4 => Key::F4,
|
||||
keysyms::XKB_KEY_KP_Home => Key::Home,
|
||||
keysyms::XKB_KEY_KP_Left => Key::ArrowLeft,
|
||||
keysyms::XKB_KEY_KP_Up => Key::ArrowLeft,
|
||||
keysyms::XKB_KEY_KP_Right => Key::ArrowRight,
|
||||
keysyms::XKB_KEY_KP_Down => Key::ArrowDown,
|
||||
// keysyms::XKB_KEY_KP_Prior => Key::PageUp,
|
||||
keysyms::XKB_KEY_KP_Page_Up => Key::PageUp,
|
||||
// keysyms::XKB_KEY_KP_Next => Key::PageDown,
|
||||
keysyms::XKB_KEY_KP_Page_Down => Key::PageDown,
|
||||
keysyms::XKB_KEY_KP_End => Key::End,
|
||||
// This is the key labeled "5" on the numpad when NumLock is off.
|
||||
// keysyms::XKB_KEY_KP_Begin => Key::Begin,
|
||||
keysyms::XKB_KEY_KP_Insert => Key::Insert,
|
||||
keysyms::XKB_KEY_KP_Delete => Key::Delete,
|
||||
// keysyms::XKB_KEY_KP_Equal => Key::Equal,
|
||||
// keysyms::XKB_KEY_KP_Multiply => Key::Multiply,
|
||||
// keysyms::XKB_KEY_KP_Add => Key::Add,
|
||||
// keysyms::XKB_KEY_KP_Separator => Key::Separator,
|
||||
// keysyms::XKB_KEY_KP_Subtract => Key::Subtract,
|
||||
// keysyms::XKB_KEY_KP_Decimal => Key::Decimal,
|
||||
// keysyms::XKB_KEY_KP_Divide => Key::Divide,
|
||||
|
||||
// keysyms::XKB_KEY_KP_0 => Key::Character("0"),
|
||||
// keysyms::XKB_KEY_KP_1 => Key::Character("1"),
|
||||
// keysyms::XKB_KEY_KP_2 => Key::Character("2"),
|
||||
// keysyms::XKB_KEY_KP_3 => Key::Character("3"),
|
||||
// keysyms::XKB_KEY_KP_4 => Key::Character("4"),
|
||||
// keysyms::XKB_KEY_KP_5 => Key::Character("5"),
|
||||
// keysyms::XKB_KEY_KP_6 => Key::Character("6"),
|
||||
// keysyms::XKB_KEY_KP_7 => Key::Character("7"),
|
||||
// keysyms::XKB_KEY_KP_8 => Key::Character("8"),
|
||||
// keysyms::XKB_KEY_KP_9 => Key::Character("9"),
|
||||
|
||||
// Function keys
|
||||
keysyms::XKB_KEY_F1 => Key::F1,
|
||||
keysyms::XKB_KEY_F2 => Key::F2,
|
||||
keysyms::XKB_KEY_F3 => Key::F3,
|
||||
keysyms::XKB_KEY_F4 => Key::F4,
|
||||
keysyms::XKB_KEY_F5 => Key::F5,
|
||||
keysyms::XKB_KEY_F6 => Key::F6,
|
||||
keysyms::XKB_KEY_F7 => Key::F7,
|
||||
keysyms::XKB_KEY_F8 => Key::F8,
|
||||
keysyms::XKB_KEY_F9 => Key::F9,
|
||||
keysyms::XKB_KEY_F10 => Key::F10,
|
||||
keysyms::XKB_KEY_F11 => Key::F11,
|
||||
keysyms::XKB_KEY_F12 => Key::F12,
|
||||
keysyms::XKB_KEY_F13 => Key::F13,
|
||||
keysyms::XKB_KEY_F14 => Key::F14,
|
||||
keysyms::XKB_KEY_F15 => Key::F15,
|
||||
keysyms::XKB_KEY_F16 => Key::F16,
|
||||
keysyms::XKB_KEY_F17 => Key::F17,
|
||||
keysyms::XKB_KEY_F18 => Key::F18,
|
||||
keysyms::XKB_KEY_F19 => Key::F19,
|
||||
keysyms::XKB_KEY_F20 => Key::F20,
|
||||
keysyms::XKB_KEY_F21 => Key::F21,
|
||||
keysyms::XKB_KEY_F22 => Key::F22,
|
||||
keysyms::XKB_KEY_F23 => Key::F23,
|
||||
keysyms::XKB_KEY_F24 => Key::F24,
|
||||
keysyms::XKB_KEY_F25 => Key::F25,
|
||||
keysyms::XKB_KEY_F26 => Key::F26,
|
||||
keysyms::XKB_KEY_F27 => Key::F27,
|
||||
keysyms::XKB_KEY_F28 => Key::F28,
|
||||
keysyms::XKB_KEY_F29 => Key::F29,
|
||||
keysyms::XKB_KEY_F30 => Key::F30,
|
||||
keysyms::XKB_KEY_F31 => Key::F31,
|
||||
keysyms::XKB_KEY_F32 => Key::F32,
|
||||
keysyms::XKB_KEY_F33 => Key::F33,
|
||||
keysyms::XKB_KEY_F34 => Key::F34,
|
||||
keysyms::XKB_KEY_F35 => Key::F35,
|
||||
|
||||
// Modifiers
|
||||
keysyms::XKB_KEY_Shift_L => Key::Shift,
|
||||
keysyms::XKB_KEY_Shift_R => Key::Shift,
|
||||
keysyms::XKB_KEY_Control_L => Key::Control,
|
||||
keysyms::XKB_KEY_Control_R => Key::Control,
|
||||
keysyms::XKB_KEY_Caps_Lock => Key::CapsLock,
|
||||
// keysyms::XKB_KEY_Shift_Lock => Key::ShiftLock,
|
||||
|
||||
// keysyms::XKB_KEY_Meta_L => Key::Meta,
|
||||
// keysyms::XKB_KEY_Meta_R => Key::Meta,
|
||||
keysyms::XKB_KEY_Alt_L => Key::Alt,
|
||||
keysyms::XKB_KEY_Alt_R => Key::Alt,
|
||||
keysyms::XKB_KEY_Super_L => Key::Super,
|
||||
keysyms::XKB_KEY_Super_R => Key::Super,
|
||||
keysyms::XKB_KEY_Hyper_L => Key::Hyper,
|
||||
keysyms::XKB_KEY_Hyper_R => Key::Hyper,
|
||||
|
||||
// XKB function and modifier keys
|
||||
// keysyms::XKB_KEY_ISO_Lock => Key::IsoLock,
|
||||
// keysyms::XKB_KEY_ISO_Level2_Latch => Key::IsoLevel2Latch,
|
||||
keysyms::XKB_KEY_ISO_Level3_Shift => Key::AltGraph,
|
||||
keysyms::XKB_KEY_ISO_Level3_Latch => Key::AltGraph,
|
||||
keysyms::XKB_KEY_ISO_Level3_Lock => Key::AltGraph,
|
||||
// keysyms::XKB_KEY_ISO_Level5_Shift => Key::IsoLevel5Shift,
|
||||
// keysyms::XKB_KEY_ISO_Level5_Latch => Key::IsoLevel5Latch,
|
||||
// keysyms::XKB_KEY_ISO_Level5_Lock => Key::IsoLevel5Lock,
|
||||
// keysyms::XKB_KEY_ISO_Group_Shift => Key::IsoGroupShift,
|
||||
// keysyms::XKB_KEY_ISO_Group_Latch => Key::IsoGroupLatch,
|
||||
// keysyms::XKB_KEY_ISO_Group_Lock => Key::IsoGroupLock,
|
||||
keysyms::XKB_KEY_ISO_Next_Group => Key::GroupNext,
|
||||
// keysyms::XKB_KEY_ISO_Next_Group_Lock => Key::GroupNextLock,
|
||||
keysyms::XKB_KEY_ISO_Prev_Group => Key::GroupPrevious,
|
||||
// keysyms::XKB_KEY_ISO_Prev_Group_Lock => Key::GroupPreviousLock,
|
||||
keysyms::XKB_KEY_ISO_First_Group => Key::GroupFirst,
|
||||
// keysyms::XKB_KEY_ISO_First_Group_Lock => Key::GroupFirstLock,
|
||||
keysyms::XKB_KEY_ISO_Last_Group => Key::GroupLast,
|
||||
// keysyms::XKB_KEY_ISO_Last_Group_Lock => Key::GroupLastLock,
|
||||
//
|
||||
keysyms::XKB_KEY_ISO_Left_Tab => Key::Tab,
|
||||
// keysyms::XKB_KEY_ISO_Move_Line_Up => Key::IsoMoveLineUp,
|
||||
// keysyms::XKB_KEY_ISO_Move_Line_Down => Key::IsoMoveLineDown,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Line_Up => Key::IsoPartialLineUp,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Line_Down => Key::IsoPartialLineDown,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Space_Left => Key::IsoPartialSpaceLeft,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Space_Right => Key::IsoPartialSpaceRight,
|
||||
// keysyms::XKB_KEY_ISO_Set_Margin_Left => Key::IsoSetMarginLeft,
|
||||
// keysyms::XKB_KEY_ISO_Set_Margin_Right => Key::IsoSetMarginRight,
|
||||
// keysyms::XKB_KEY_ISO_Release_Margin_Left => Key::IsoReleaseMarginLeft,
|
||||
// keysyms::XKB_KEY_ISO_Release_Margin_Right => Key::IsoReleaseMarginRight,
|
||||
// keysyms::XKB_KEY_ISO_Release_Both_Margins => Key::IsoReleaseBothMargins,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Left => Key::IsoFastCursorLeft,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Right => Key::IsoFastCursorRight,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Up => Key::IsoFastCursorUp,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Down => Key::IsoFastCursorDown,
|
||||
// keysyms::XKB_KEY_ISO_Continuous_Underline => Key::IsoContinuousUnderline,
|
||||
// keysyms::XKB_KEY_ISO_Discontinuous_Underline => Key::IsoDiscontinuousUnderline,
|
||||
// keysyms::XKB_KEY_ISO_Emphasize => Key::IsoEmphasize,
|
||||
// keysyms::XKB_KEY_ISO_Center_Object => Key::IsoCenterObject,
|
||||
keysyms::XKB_KEY_ISO_Enter => Key::Enter,
|
||||
|
||||
// XKB_KEY_dead_grave..XKB_KEY_dead_currency
|
||||
|
||||
// XKB_KEY_dead_lowline..XKB_KEY_dead_longsolidusoverlay
|
||||
|
||||
// XKB_KEY_dead_a..XKB_KEY_dead_capital_schwa
|
||||
|
||||
// XKB_KEY_dead_greek
|
||||
|
||||
// XKB_KEY_First_Virtual_Screen..XKB_KEY_Terminate_Server
|
||||
|
||||
// XKB_KEY_AccessX_Enable..XKB_KEY_AudibleBell_Enable
|
||||
|
||||
// XKB_KEY_Pointer_Left..XKB_KEY_Pointer_Drag5
|
||||
|
||||
// XKB_KEY_Pointer_EnableKeys..XKB_KEY_Pointer_DfltBtnPrev
|
||||
|
||||
// XKB_KEY_ch..XKB_KEY_C_H
|
||||
|
||||
// 3270 terminal keys
|
||||
// keysyms::XKB_KEY_3270_Duplicate => Key::Duplicate,
|
||||
// keysyms::XKB_KEY_3270_FieldMark => Key::FieldMark,
|
||||
// keysyms::XKB_KEY_3270_Right2 => Key::Right2,
|
||||
// keysyms::XKB_KEY_3270_Left2 => Key::Left2,
|
||||
// keysyms::XKB_KEY_3270_BackTab => Key::BackTab,
|
||||
keysyms::XKB_KEY_3270_EraseEOF => Key::EraseEof,
|
||||
// keysyms::XKB_KEY_3270_EraseInput => Key::EraseInput,
|
||||
// keysyms::XKB_KEY_3270_Reset => Key::Reset,
|
||||
// keysyms::XKB_KEY_3270_Quit => Key::Quit,
|
||||
// keysyms::XKB_KEY_3270_PA1 => Key::Pa1,
|
||||
// keysyms::XKB_KEY_3270_PA2 => Key::Pa2,
|
||||
// keysyms::XKB_KEY_3270_PA3 => Key::Pa3,
|
||||
// keysyms::XKB_KEY_3270_Test => Key::Test,
|
||||
keysyms::XKB_KEY_3270_Attn => Key::Attn,
|
||||
// keysyms::XKB_KEY_3270_CursorBlink => Key::CursorBlink,
|
||||
// keysyms::XKB_KEY_3270_AltCursor => Key::AltCursor,
|
||||
// keysyms::XKB_KEY_3270_KeyClick => Key::KeyClick,
|
||||
// keysyms::XKB_KEY_3270_Jump => Key::Jump,
|
||||
// keysyms::XKB_KEY_3270_Ident => Key::Ident,
|
||||
// keysyms::XKB_KEY_3270_Rule => Key::Rule,
|
||||
// keysyms::XKB_KEY_3270_Copy => Key::Copy,
|
||||
keysyms::XKB_KEY_3270_Play => Key::Play,
|
||||
// keysyms::XKB_KEY_3270_Setup => Key::Setup,
|
||||
// keysyms::XKB_KEY_3270_Record => Key::Record,
|
||||
// keysyms::XKB_KEY_3270_ChangeScreen => Key::ChangeScreen,
|
||||
// keysyms::XKB_KEY_3270_DeleteWord => Key::DeleteWord,
|
||||
keysyms::XKB_KEY_3270_ExSelect => Key::ExSel,
|
||||
keysyms::XKB_KEY_3270_CursorSelect => Key::CrSel,
|
||||
keysyms::XKB_KEY_3270_PrintScreen => Key::PrintScreen,
|
||||
keysyms::XKB_KEY_3270_Enter => Key::Enter,
|
||||
|
||||
keysyms::XKB_KEY_space => Key::Space,
|
||||
// XKB_KEY_exclam..XKB_KEY_Sinh_kunddaliya
|
||||
|
||||
// XFree86
|
||||
// keysyms::XKB_KEY_XF86ModeLock => Key::ModeLock,
|
||||
|
||||
// XFree86 - Backlight controls
|
||||
keysyms::XKB_KEY_XF86MonBrightnessUp => Key::BrightnessUp,
|
||||
keysyms::XKB_KEY_XF86MonBrightnessDown => Key::BrightnessDown,
|
||||
// keysyms::XKB_KEY_XF86KbdLightOnOff => Key::LightOnOff,
|
||||
// keysyms::XKB_KEY_XF86KbdBrightnessUp => Key::KeyboardBrightnessUp,
|
||||
// keysyms::XKB_KEY_XF86KbdBrightnessDown => Key::KeyboardBrightnessDown,
|
||||
|
||||
// XFree86 - "Internet"
|
||||
keysyms::XKB_KEY_XF86Standby => Key::Standby,
|
||||
keysyms::XKB_KEY_XF86AudioLowerVolume => Key::AudioVolumeDown,
|
||||
keysyms::XKB_KEY_XF86AudioRaiseVolume => Key::AudioVolumeUp,
|
||||
keysyms::XKB_KEY_XF86AudioPlay => Key::MediaPlay,
|
||||
keysyms::XKB_KEY_XF86AudioStop => Key::MediaStop,
|
||||
keysyms::XKB_KEY_XF86AudioPrev => Key::MediaTrackPrevious,
|
||||
keysyms::XKB_KEY_XF86AudioNext => Key::MediaTrackNext,
|
||||
keysyms::XKB_KEY_XF86HomePage => Key::BrowserHome,
|
||||
keysyms::XKB_KEY_XF86Mail => Key::LaunchMail,
|
||||
// keysyms::XKB_KEY_XF86Start => Key::Start,
|
||||
keysyms::XKB_KEY_XF86Search => Key::BrowserSearch,
|
||||
keysyms::XKB_KEY_XF86AudioRecord => Key::MediaRecord,
|
||||
|
||||
// XFree86 - PDA
|
||||
keysyms::XKB_KEY_XF86Calculator => Key::LaunchApplication2,
|
||||
// keysyms::XKB_KEY_XF86Memo => Key::Memo,
|
||||
// keysyms::XKB_KEY_XF86ToDoList => Key::ToDoList,
|
||||
keysyms::XKB_KEY_XF86Calendar => Key::LaunchCalendar,
|
||||
keysyms::XKB_KEY_XF86PowerDown => Key::Power,
|
||||
// keysyms::XKB_KEY_XF86ContrastAdjust => Key::AdjustContrast,
|
||||
// keysyms::XKB_KEY_XF86RockerUp => Key::RockerUp,
|
||||
// keysyms::XKB_KEY_XF86RockerDown => Key::RockerDown,
|
||||
// keysyms::XKB_KEY_XF86RockerEnter => Key::RockerEnter,
|
||||
|
||||
// XFree86 - More "Internet"
|
||||
keysyms::XKB_KEY_XF86Back => Key::BrowserBack,
|
||||
keysyms::XKB_KEY_XF86Forward => Key::BrowserForward,
|
||||
// keysyms::XKB_KEY_XF86Stop => Key::Stop,
|
||||
keysyms::XKB_KEY_XF86Refresh => Key::BrowserRefresh,
|
||||
keysyms::XKB_KEY_XF86PowerOff => Key::Power,
|
||||
keysyms::XKB_KEY_XF86WakeUp => Key::WakeUp,
|
||||
keysyms::XKB_KEY_XF86Eject => Key::Eject,
|
||||
keysyms::XKB_KEY_XF86ScreenSaver => Key::LaunchScreenSaver,
|
||||
keysyms::XKB_KEY_XF86WWW => Key::LaunchWebBrowser,
|
||||
keysyms::XKB_KEY_XF86Sleep => Key::Standby,
|
||||
keysyms::XKB_KEY_XF86Favorites => Key::BrowserFavorites,
|
||||
keysyms::XKB_KEY_XF86AudioPause => Key::MediaPause,
|
||||
// keysyms::XKB_KEY_XF86AudioMedia => Key::AudioMedia,
|
||||
keysyms::XKB_KEY_XF86MyComputer => Key::LaunchApplication1,
|
||||
// keysyms::XKB_KEY_XF86VendorHome => Key::VendorHome,
|
||||
// keysyms::XKB_KEY_XF86LightBulb => Key::LightBulb,
|
||||
// keysyms::XKB_KEY_XF86Shop => Key::BrowserShop,
|
||||
// keysyms::XKB_KEY_XF86History => Key::BrowserHistory,
|
||||
// keysyms::XKB_KEY_XF86OpenURL => Key::OpenUrl,
|
||||
// keysyms::XKB_KEY_XF86AddFavorite => Key::AddFavorite,
|
||||
// keysyms::XKB_KEY_XF86HotLinks => Key::HotLinks,
|
||||
// keysyms::XKB_KEY_XF86BrightnessAdjust => Key::BrightnessAdjust,
|
||||
// keysyms::XKB_KEY_XF86Finance => Key::BrowserFinance,
|
||||
// keysyms::XKB_KEY_XF86Community => Key::BrowserCommunity,
|
||||
keysyms::XKB_KEY_XF86AudioRewind => Key::MediaRewind,
|
||||
// keysyms::XKB_KEY_XF86BackForward => Key::???,
|
||||
// XKB_KEY_XF86Launch0..XKB_KEY_XF86LaunchF
|
||||
|
||||
// XKB_KEY_XF86ApplicationLeft..XKB_KEY_XF86CD
|
||||
keysyms::XKB_KEY_XF86Calculater => Key::LaunchApplication2, // Nice typo, libxkbcommon :)
|
||||
// XKB_KEY_XF86Clear
|
||||
keysyms::XKB_KEY_XF86Close => Key::Close,
|
||||
keysyms::XKB_KEY_XF86Copy => Key::Copy,
|
||||
keysyms::XKB_KEY_XF86Cut => Key::Cut,
|
||||
// XKB_KEY_XF86Display..XKB_KEY_XF86Documents
|
||||
keysyms::XKB_KEY_XF86Excel => Key::LaunchSpreadsheet,
|
||||
// XKB_KEY_XF86Explorer..XKB_KEY_XF86iTouch
|
||||
keysyms::XKB_KEY_XF86LogOff => Key::LogOff,
|
||||
// XKB_KEY_XF86Market..XKB_KEY_XF86MenuPB
|
||||
keysyms::XKB_KEY_XF86MySites => Key::BrowserFavorites,
|
||||
keysyms::XKB_KEY_XF86New => Key::New,
|
||||
// XKB_KEY_XF86News..XKB_KEY_XF86OfficeHome
|
||||
keysyms::XKB_KEY_XF86Open => Key::Open,
|
||||
// XKB_KEY_XF86Option
|
||||
keysyms::XKB_KEY_XF86Paste => Key::Paste,
|
||||
keysyms::XKB_KEY_XF86Phone => Key::LaunchPhone,
|
||||
// XKB_KEY_XF86Q
|
||||
keysyms::XKB_KEY_XF86Reply => Key::MailReply,
|
||||
keysyms::XKB_KEY_XF86Reload => Key::BrowserRefresh,
|
||||
// XKB_KEY_XF86RotateWindows..XKB_KEY_XF86RotationKB
|
||||
keysyms::XKB_KEY_XF86Save => Key::Save,
|
||||
// XKB_KEY_XF86ScrollUp..XKB_KEY_XF86ScrollClick
|
||||
keysyms::XKB_KEY_XF86Send => Key::MailSend,
|
||||
keysyms::XKB_KEY_XF86Spell => Key::SpellCheck,
|
||||
keysyms::XKB_KEY_XF86SplitScreen => Key::SplitScreenToggle,
|
||||
// XKB_KEY_XF86Support..XKB_KEY_XF86User2KB
|
||||
keysyms::XKB_KEY_XF86Video => Key::LaunchMediaPlayer,
|
||||
// XKB_KEY_XF86WheelButton
|
||||
keysyms::XKB_KEY_XF86Word => Key::LaunchWordProcessor,
|
||||
// XKB_KEY_XF86Xfer
|
||||
keysyms::XKB_KEY_XF86ZoomIn => Key::ZoomIn,
|
||||
keysyms::XKB_KEY_XF86ZoomOut => Key::ZoomOut,
|
||||
|
||||
// XKB_KEY_XF86Away..XKB_KEY_XF86Messenger
|
||||
keysyms::XKB_KEY_XF86WebCam => Key::LaunchWebCam,
|
||||
keysyms::XKB_KEY_XF86MailForward => Key::MailForward,
|
||||
// XKB_KEY_XF86Pictures
|
||||
keysyms::XKB_KEY_XF86Music => Key::LaunchMusicPlayer,
|
||||
|
||||
// XKB_KEY_XF86Battery..XKB_KEY_XF86UWB
|
||||
//
|
||||
keysyms::XKB_KEY_XF86AudioForward => Key::MediaFastForward,
|
||||
// XKB_KEY_XF86AudioRepeat
|
||||
keysyms::XKB_KEY_XF86AudioRandomPlay => Key::RandomToggle,
|
||||
keysyms::XKB_KEY_XF86Subtitle => Key::Subtitle,
|
||||
keysyms::XKB_KEY_XF86AudioCycleTrack => Key::MediaAudioTrack,
|
||||
// XKB_KEY_XF86CycleAngle..XKB_KEY_XF86Blue
|
||||
//
|
||||
keysyms::XKB_KEY_XF86Suspend => Key::Standby,
|
||||
keysyms::XKB_KEY_XF86Hibernate => Key::Hibernate,
|
||||
// XKB_KEY_XF86TouchpadToggle..XKB_KEY_XF86TouchpadOff
|
||||
//
|
||||
keysyms::XKB_KEY_XF86AudioMute => Key::AudioVolumeMute,
|
||||
|
||||
// XKB_KEY_XF86Switch_VT_1..XKB_KEY_XF86Switch_VT_12
|
||||
|
||||
// XKB_KEY_XF86Ungrab..XKB_KEY_XF86ClearGrab
|
||||
keysyms::XKB_KEY_XF86Next_VMode => Key::VideoModeNext,
|
||||
// keysyms::XKB_KEY_XF86Prev_VMode => Key::VideoModePrevious,
|
||||
// XKB_KEY_XF86LogWindowTree..XKB_KEY_XF86LogGrabInfo
|
||||
|
||||
// XKB_KEY_SunFA_Grave..XKB_KEY_SunFA_Cedilla
|
||||
|
||||
// keysyms::XKB_KEY_SunF36 => Key::F36 | Key::F11,
|
||||
// keysyms::XKB_KEY_SunF37 => Key::F37 | Key::F12,
|
||||
|
||||
// keysyms::XKB_KEY_SunSys_Req => Key::PrintScreen,
|
||||
// The next couple of xkb (until XKB_KEY_SunStop) are already handled.
|
||||
// XKB_KEY_SunPrint_Screen..XKB_KEY_SunPageDown
|
||||
|
||||
// XKB_KEY_SunUndo..XKB_KEY_SunFront
|
||||
keysyms::XKB_KEY_SunCopy => Key::Copy,
|
||||
keysyms::XKB_KEY_SunOpen => Key::Open,
|
||||
keysyms::XKB_KEY_SunPaste => Key::Paste,
|
||||
keysyms::XKB_KEY_SunCut => Key::Cut,
|
||||
|
||||
// XKB_KEY_SunPowerSwitch
|
||||
keysyms::XKB_KEY_SunAudioLowerVolume => Key::AudioVolumeDown,
|
||||
keysyms::XKB_KEY_SunAudioMute => Key::AudioVolumeMute,
|
||||
keysyms::XKB_KEY_SunAudioRaiseVolume => Key::AudioVolumeUp,
|
||||
// XKB_KEY_SunVideoDegauss
|
||||
keysyms::XKB_KEY_SunVideoLowerBrightness => Key::BrightnessDown,
|
||||
keysyms::XKB_KEY_SunVideoRaiseBrightness => Key::BrightnessUp,
|
||||
// XKB_KEY_SunPowerSwitchShift
|
||||
//
|
||||
0 => Key::Unidentified(NativeKey::Unidentified),
|
||||
_ => Key::Unidentified(NativeKey::Xkb(keysym)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keysym_location(keysym: u32) -> KeyLocation {
|
||||
use xkbcommon_dl::keysyms;
|
||||
match keysym {
|
||||
keysyms::XKB_KEY_Shift_L
|
||||
| keysyms::XKB_KEY_Control_L
|
||||
| keysyms::XKB_KEY_Meta_L
|
||||
| keysyms::XKB_KEY_Alt_L
|
||||
| keysyms::XKB_KEY_Super_L
|
||||
| keysyms::XKB_KEY_Hyper_L => KeyLocation::Left,
|
||||
keysyms::XKB_KEY_Shift_R
|
||||
| keysyms::XKB_KEY_Control_R
|
||||
| keysyms::XKB_KEY_Meta_R
|
||||
| keysyms::XKB_KEY_Alt_R
|
||||
| keysyms::XKB_KEY_Super_R
|
||||
| keysyms::XKB_KEY_Hyper_R => KeyLocation::Right,
|
||||
keysyms::XKB_KEY_KP_0
|
||||
| keysyms::XKB_KEY_KP_1
|
||||
| keysyms::XKB_KEY_KP_2
|
||||
| keysyms::XKB_KEY_KP_3
|
||||
| keysyms::XKB_KEY_KP_4
|
||||
| keysyms::XKB_KEY_KP_5
|
||||
| keysyms::XKB_KEY_KP_6
|
||||
| keysyms::XKB_KEY_KP_7
|
||||
| keysyms::XKB_KEY_KP_8
|
||||
| keysyms::XKB_KEY_KP_9
|
||||
| keysyms::XKB_KEY_KP_Space
|
||||
| keysyms::XKB_KEY_KP_Tab
|
||||
| keysyms::XKB_KEY_KP_Enter
|
||||
| keysyms::XKB_KEY_KP_F1
|
||||
| keysyms::XKB_KEY_KP_F2
|
||||
| keysyms::XKB_KEY_KP_F3
|
||||
| keysyms::XKB_KEY_KP_F4
|
||||
| keysyms::XKB_KEY_KP_Home
|
||||
| keysyms::XKB_KEY_KP_Left
|
||||
| keysyms::XKB_KEY_KP_Up
|
||||
| keysyms::XKB_KEY_KP_Right
|
||||
| keysyms::XKB_KEY_KP_Down
|
||||
| keysyms::XKB_KEY_KP_Page_Up
|
||||
| keysyms::XKB_KEY_KP_Page_Down
|
||||
| keysyms::XKB_KEY_KP_End
|
||||
| keysyms::XKB_KEY_KP_Begin
|
||||
| keysyms::XKB_KEY_KP_Insert
|
||||
| keysyms::XKB_KEY_KP_Delete
|
||||
| keysyms::XKB_KEY_KP_Equal
|
||||
| keysyms::XKB_KEY_KP_Multiply
|
||||
| keysyms::XKB_KEY_KP_Add
|
||||
| keysyms::XKB_KEY_KP_Separator
|
||||
| keysyms::XKB_KEY_KP_Subtract
|
||||
| keysyms::XKB_KEY_KP_Decimal
|
||||
| keysyms::XKB_KEY_KP_Divide => KeyLocation::Numpad,
|
||||
_ => KeyLocation::Standard,
|
||||
}
|
||||
}
|
||||
2
src/platform_impl/linux/common/mod.rs
Normal file
2
src/platform_impl/linux/common/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod keymap;
|
||||
pub mod xkb_state;
|
||||
668
src/platform_impl/linux/common/xkb_state.rs
Normal file
668
src/platform_impl/linux/common/xkb_state.rs
Normal file
|
|
@ -0,0 +1,668 @@
|
|||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
use xkbcommon_dl::{
|
||||
self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH,
|
||||
};
|
||||
#[cfg(feature = "wayland")]
|
||||
use {memmap2::MmapOptions, wayland_backend::io_lifetimes::OwnedFd};
|
||||
#[cfg(feature = "x11")]
|
||||
use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::XKBCOMMON_X11_HANDLE as XKBXH};
|
||||
|
||||
use crate::event::KeyEvent;
|
||||
use crate::platform_impl::common::keymap;
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
use crate::{
|
||||
event::ElementState,
|
||||
keyboard::{Key, KeyCode, KeyLocation},
|
||||
};
|
||||
|
||||
// TODO: Wire this up without using a static `AtomicBool`.
|
||||
static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reset_dead_keys() {
|
||||
RESET_DEAD_KEYS.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KbdState {
|
||||
#[cfg(feature = "x11")]
|
||||
xcb_connection: *mut xcb_connection_t,
|
||||
xkb_context: *mut ffi::xkb_context,
|
||||
xkb_keymap: *mut ffi::xkb_keymap,
|
||||
xkb_state: *mut ffi::xkb_state,
|
||||
xkb_compose_table: *mut ffi::xkb_compose_table,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
xkb_compose_state_2: *mut ffi::xkb_compose_state,
|
||||
mods_state: ModifiersState,
|
||||
#[cfg(feature = "x11")]
|
||||
pub core_keyboard_id: i32,
|
||||
scratch_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl KbdState {
|
||||
pub fn update_modifiers(
|
||||
&mut self,
|
||||
mods_depressed: u32,
|
||||
mods_latched: u32,
|
||||
mods_locked: u32,
|
||||
depressed_group: u32,
|
||||
latched_group: u32,
|
||||
locked_group: u32,
|
||||
) {
|
||||
if !self.ready() {
|
||||
return;
|
||||
}
|
||||
let mask = unsafe {
|
||||
(XKBH.xkb_state_update_mask)(
|
||||
self.xkb_state,
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
depressed_group,
|
||||
latched_group,
|
||||
locked_group,
|
||||
)
|
||||
};
|
||||
if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) {
|
||||
// effective value of mods have changed, we need to update our state
|
||||
self.mods_state.update_with(self.xkb_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_one_sym_raw(&mut self, keycode: u32) -> u32 {
|
||||
if !self.ready() {
|
||||
return 0;
|
||||
}
|
||||
unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode) }
|
||||
}
|
||||
|
||||
pub fn get_utf8_raw(&mut self, keycode: u32) -> Option<SmolStr> {
|
||||
if !self.ready() {
|
||||
return None;
|
||||
}
|
||||
let xkb_state = self.xkb_state;
|
||||
self.make_string_with({
|
||||
|ptr, len| unsafe { (XKBH.xkb_state_key_get_utf8)(xkb_state, keycode, ptr, len) }
|
||||
})
|
||||
}
|
||||
|
||||
fn compose_feed_normal(&mut self, keysym: u32) -> Option<ffi::xkb_compose_feed_result> {
|
||||
self.compose_feed(self.xkb_compose_state, keysym)
|
||||
}
|
||||
|
||||
fn compose_feed_2(&mut self, keysym: u32) -> Option<ffi::xkb_compose_feed_result> {
|
||||
self.compose_feed(self.xkb_compose_state_2, keysym)
|
||||
}
|
||||
|
||||
fn compose_feed(
|
||||
&mut self,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
keysym: u32,
|
||||
) -> Option<ffi::xkb_compose_feed_result> {
|
||||
if !self.ready() || self.xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) {
|
||||
unsafe { self.init_compose() };
|
||||
}
|
||||
Some(unsafe { (XKBCH.xkb_compose_state_feed)(xkb_compose_state, keysym) })
|
||||
}
|
||||
|
||||
fn compose_status_normal(&mut self) -> Option<ffi::xkb_compose_status> {
|
||||
self.compose_status(self.xkb_compose_state)
|
||||
}
|
||||
|
||||
fn compose_status(
|
||||
&mut self,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
) -> Option<ffi::xkb_compose_status> {
|
||||
if !self.ready() || xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { (XKBCH.xkb_compose_state_get_status)(xkb_compose_state) })
|
||||
}
|
||||
|
||||
fn compose_get_utf8_normal(&mut self) -> Option<SmolStr> {
|
||||
self.compose_get_utf8(self.xkb_compose_state)
|
||||
}
|
||||
|
||||
fn compose_get_utf8_2(&mut self) -> Option<SmolStr> {
|
||||
self.compose_get_utf8(self.xkb_compose_state_2)
|
||||
}
|
||||
|
||||
fn compose_get_utf8(
|
||||
&mut self,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
) -> Option<SmolStr> {
|
||||
if !self.ready() || xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
self.make_string_with(|ptr, len| unsafe {
|
||||
(XKBCH.xkb_compose_state_get_utf8)(xkb_compose_state, ptr, len)
|
||||
})
|
||||
}
|
||||
|
||||
/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and
|
||||
/// `xkb_state_key_get_utf8`.
|
||||
fn make_string_with<F>(&mut self, mut f: F) -> Option<SmolStr>
|
||||
where
|
||||
F: FnMut(*mut i8, usize) -> i32,
|
||||
{
|
||||
let size = f(ptr::null_mut(), 0);
|
||||
if size == 0 {
|
||||
return None;
|
||||
}
|
||||
let size = usize::try_from(size).unwrap();
|
||||
self.scratch_buffer.clear();
|
||||
// The allocated buffer must include space for the null-terminator
|
||||
self.scratch_buffer.reserve(size + 1);
|
||||
unsafe {
|
||||
let written = f(
|
||||
self.scratch_buffer.as_mut_ptr().cast(),
|
||||
self.scratch_buffer.capacity(),
|
||||
);
|
||||
if usize::try_from(written).unwrap() != size {
|
||||
// This will likely never happen
|
||||
return None;
|
||||
}
|
||||
self.scratch_buffer.set_len(size);
|
||||
};
|
||||
byte_slice_to_smol_str(&self.scratch_buffer)
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
if ffi::XKBCOMMON_OPTION.as_ref().is_none() {
|
||||
return Err(Error::XKBNotFound);
|
||||
}
|
||||
|
||||
let context =
|
||||
unsafe { (XKBH.xkb_context_new)(ffi::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) };
|
||||
if context.is_null() {
|
||||
return Err(Error::XKBNotFound);
|
||||
}
|
||||
|
||||
let mut me = Self {
|
||||
#[cfg(feature = "x11")]
|
||||
xcb_connection: ptr::null_mut(),
|
||||
xkb_context: context,
|
||||
xkb_keymap: ptr::null_mut(),
|
||||
xkb_state: ptr::null_mut(),
|
||||
xkb_compose_table: ptr::null_mut(),
|
||||
xkb_compose_state: ptr::null_mut(),
|
||||
xkb_compose_state_2: ptr::null_mut(),
|
||||
mods_state: ModifiersState::new(),
|
||||
#[cfg(feature = "x11")]
|
||||
core_keyboard_id: 0,
|
||||
scratch_buffer: Vec::new(),
|
||||
};
|
||||
|
||||
unsafe { me.init_compose() };
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
pub fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result<Self, Error> {
|
||||
let mut me = Self::new()?;
|
||||
me.xcb_connection = connection;
|
||||
|
||||
let result = unsafe {
|
||||
(XKBXH.xkb_x11_setup_xkb_extension)(
|
||||
connection,
|
||||
1,
|
||||
2,
|
||||
xkbcommon_dl::x11::xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
assert_eq!(result, 1, "Failed to initialize libxkbcommon");
|
||||
|
||||
unsafe { me.init_with_x11_keymap() };
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
unsafe fn init_compose(&mut self) {
|
||||
let locale = env::var_os("LC_ALL")
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.or_else(|| env::var_os("LC_CTYPE"))
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.or_else(|| env::var_os("LANG"))
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.unwrap_or_else(|| "C".into());
|
||||
let locale = CString::new(locale.into_vec()).unwrap();
|
||||
|
||||
let compose_table = (XKBCH.xkb_compose_table_new_from_locale)(
|
||||
self.xkb_context,
|
||||
locale.as_ptr(),
|
||||
ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if compose_table.is_null() {
|
||||
// init of compose table failed, continue without compose
|
||||
return;
|
||||
}
|
||||
|
||||
let compose_state = (XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if compose_state.is_null() {
|
||||
// init of compose state failed, continue without compose
|
||||
(XKBCH.xkb_compose_table_unref)(compose_table);
|
||||
return;
|
||||
}
|
||||
|
||||
let compose_state_2 = (XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if compose_state_2.is_null() {
|
||||
// init of compose state failed, continue without compose
|
||||
(XKBCH.xkb_compose_table_unref)(compose_table);
|
||||
(XKBCH.xkb_compose_state_unref)(compose_state);
|
||||
return;
|
||||
}
|
||||
|
||||
self.xkb_compose_table = compose_table;
|
||||
self.xkb_compose_state = compose_state;
|
||||
self.xkb_compose_state_2 = compose_state_2;
|
||||
}
|
||||
|
||||
unsafe fn post_init(&mut self, state: *mut ffi::xkb_state, keymap: *mut ffi::xkb_keymap) {
|
||||
self.xkb_keymap = keymap;
|
||||
self.xkb_state = state;
|
||||
self.mods_state.update_with(state);
|
||||
}
|
||||
|
||||
unsafe fn de_init(&mut self) {
|
||||
(XKBH.xkb_state_unref)(self.xkb_state);
|
||||
self.xkb_state = ptr::null_mut();
|
||||
(XKBH.xkb_keymap_unref)(self.xkb_keymap);
|
||||
self.xkb_keymap = ptr::null_mut();
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
pub unsafe fn init_with_x11_keymap(&mut self) {
|
||||
if !self.xkb_keymap.is_null() {
|
||||
self.de_init();
|
||||
}
|
||||
|
||||
// TODO: Support keyboards other than the "virtual core keyboard device".
|
||||
self.core_keyboard_id = (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection);
|
||||
let keymap = (XKBXH.xkb_x11_keymap_new_from_device)(
|
||||
self.xkb_context,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
);
|
||||
if keymap.is_null() {
|
||||
panic!("Failed to get keymap from X11 server.");
|
||||
}
|
||||
|
||||
let state = (XKBXH.xkb_x11_state_new_from_device)(
|
||||
keymap,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
);
|
||||
self.post_init(state, keymap);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub unsafe fn init_with_fd(&mut self, fd: OwnedFd, size: usize) {
|
||||
if !self.xkb_keymap.is_null() {
|
||||
self.de_init();
|
||||
}
|
||||
|
||||
let map = MmapOptions::new()
|
||||
.len(size)
|
||||
.map_copy_read_only(&fd)
|
||||
.unwrap();
|
||||
|
||||
let keymap = (XKBH.xkb_keymap_new_from_string)(
|
||||
self.xkb_context,
|
||||
map.as_ptr() as *const _,
|
||||
ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if keymap.is_null() {
|
||||
panic!("Received invalid keymap from compositor.");
|
||||
}
|
||||
|
||||
let state = (XKBH.xkb_state_new)(keymap);
|
||||
self.post_init(state, keymap);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool {
|
||||
unsafe { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ready(&self) -> bool {
|
||||
!self.xkb_state.is_null()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mods_state(&self) -> ModifiersState {
|
||||
self.mods_state
|
||||
}
|
||||
|
||||
pub fn process_key_event(
|
||||
&mut self,
|
||||
keycode: u32,
|
||||
state: ElementState,
|
||||
repeat: bool,
|
||||
) -> KeyEvent {
|
||||
let mut event =
|
||||
KeyEventResults::new(self, keycode, !repeat && state == ElementState::Pressed);
|
||||
let physical_key = event.keycode();
|
||||
let (logical_key, location) = event.key();
|
||||
let text = event.text();
|
||||
let (key_without_modifiers, _) = event.key_without_modifiers();
|
||||
let text_with_all_modifiers = event.text_with_all_modifiers();
|
||||
|
||||
let platform_specific = KeyEventExtra {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
};
|
||||
|
||||
KeyEvent {
|
||||
physical_key,
|
||||
logical_key,
|
||||
text,
|
||||
location,
|
||||
state,
|
||||
repeat,
|
||||
platform_specific,
|
||||
}
|
||||
}
|
||||
|
||||
fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option<SmolStr> {
|
||||
self.scratch_buffer.clear();
|
||||
self.scratch_buffer.reserve(8);
|
||||
loop {
|
||||
let bytes_written = unsafe {
|
||||
(XKBH.xkb_keysym_to_utf8)(
|
||||
keysym,
|
||||
self.scratch_buffer.as_mut_ptr().cast(),
|
||||
self.scratch_buffer.capacity(),
|
||||
)
|
||||
};
|
||||
if bytes_written == 0 {
|
||||
return None;
|
||||
} else if bytes_written == -1 {
|
||||
self.scratch_buffer.reserve(8);
|
||||
} else {
|
||||
unsafe {
|
||||
self.scratch_buffer
|
||||
.set_len(bytes_written.try_into().unwrap())
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the null-terminator
|
||||
self.scratch_buffer.pop();
|
||||
byte_slice_to_smol_str(&self.scratch_buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KbdState {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.xkb_compose_state.is_null() {
|
||||
(XKBCH.xkb_compose_state_unref)(self.xkb_compose_state);
|
||||
}
|
||||
if !self.xkb_compose_state_2.is_null() {
|
||||
(XKBCH.xkb_compose_state_unref)(self.xkb_compose_state_2);
|
||||
}
|
||||
if !self.xkb_compose_table.is_null() {
|
||||
(XKBCH.xkb_compose_table_unref)(self.xkb_compose_table);
|
||||
}
|
||||
if !self.xkb_state.is_null() {
|
||||
(XKBH.xkb_state_unref)(self.xkb_state);
|
||||
}
|
||||
if !self.xkb_keymap.is_null() {
|
||||
(XKBH.xkb_keymap_unref)(self.xkb_keymap);
|
||||
}
|
||||
(XKBH.xkb_context_unref)(self.xkb_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyEventResults<'a> {
|
||||
state: &'a mut KbdState,
|
||||
keycode: u32,
|
||||
keysym: u32,
|
||||
compose: Option<XkbCompose>,
|
||||
}
|
||||
|
||||
impl<'a> KeyEventResults<'a> {
|
||||
fn new(state: &'a mut KbdState, keycode: u32, compose: bool) -> Self {
|
||||
let keysym = state.get_one_sym_raw(keycode);
|
||||
|
||||
let compose = if compose {
|
||||
Some(match state.compose_feed_normal(keysym) {
|
||||
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => {
|
||||
// Unwrapping is safe here, as `compose_feed` returns `None` when composition is uninitialized.
|
||||
XkbCompose::Accepted(state.compose_status_normal().unwrap())
|
||||
}
|
||||
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => XkbCompose::Ignored,
|
||||
None => XkbCompose::Uninitialized,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
KeyEventResults {
|
||||
state,
|
||||
keycode,
|
||||
keysym,
|
||||
compose,
|
||||
}
|
||||
}
|
||||
|
||||
fn keycode(&mut self) -> KeyCode {
|
||||
keymap::raw_keycode_to_keycode(self.keycode)
|
||||
}
|
||||
|
||||
pub fn key(&mut self) -> (Key, KeyLocation) {
|
||||
self.keysym_to_key(self.keysym)
|
||||
.unwrap_or_else(|(key, location)| match self.compose {
|
||||
Some(XkbCompose::Accepted(ffi::xkb_compose_status::XKB_COMPOSE_COMPOSING)) => {
|
||||
// When pressing a dead key twice, the non-combining variant of that character will be
|
||||
// produced. Since this function only concerns itself with a single keypress, we simulate
|
||||
// this double press here by feeding the keysym to the compose state twice.
|
||||
self.state.compose_feed_2(self.keysym);
|
||||
match self.state.compose_feed_2(self.keysym) {
|
||||
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => (
|
||||
// Extracting only a single `char` here *should* be fine, assuming that no dead
|
||||
// key's non-combining variant ever occupies more than one `char`.
|
||||
Key::Dead(
|
||||
self.state
|
||||
.compose_get_utf8_2()
|
||||
.map(|s| s.chars().next().unwrap()),
|
||||
),
|
||||
location,
|
||||
),
|
||||
_ => (key, location),
|
||||
}
|
||||
}
|
||||
_ => (
|
||||
self.composed_text()
|
||||
.unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym))
|
||||
.map(Key::Character)
|
||||
.unwrap_or(key),
|
||||
location,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn key_without_modifiers(&mut self) -> (Key, KeyLocation) {
|
||||
// This will become a pointer to an array which libxkbcommon owns, so we don't need to deallocate it.
|
||||
let mut keysyms = ptr::null();
|
||||
let keysym_count = unsafe {
|
||||
(XKBH.xkb_keymap_key_get_syms_by_level)(
|
||||
self.state.xkb_keymap,
|
||||
self.keycode,
|
||||
0,
|
||||
0,
|
||||
&mut keysyms,
|
||||
)
|
||||
};
|
||||
let keysym = if keysym_count == 1 {
|
||||
unsafe { *keysyms }
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.keysym_to_key(keysym)
|
||||
.unwrap_or_else(|(key, location)| {
|
||||
(
|
||||
self.state
|
||||
.keysym_to_utf8_raw(keysym)
|
||||
.map(Key::Character)
|
||||
.unwrap_or(key),
|
||||
location,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn keysym_to_key(&mut self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> {
|
||||
let location = super::keymap::keysym_location(keysym);
|
||||
let key = super::keymap::keysym_to_key(keysym);
|
||||
if matches!(key, Key::Unidentified(_)) {
|
||||
Err((key, location))
|
||||
} else {
|
||||
Ok((key, location))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text(&mut self) -> Option<SmolStr> {
|
||||
self.composed_text()
|
||||
.unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym))
|
||||
}
|
||||
|
||||
pub fn text_with_all_modifiers(&mut self) -> Option<SmolStr> {
|
||||
// The current behaviour makes it so composing a character overrides attempts to input a
|
||||
// control character with the `Ctrl` key. We can potentially add a configuration option
|
||||
// if someone specifically wants the oppsite behaviour.
|
||||
self.composed_text()
|
||||
.unwrap_or_else(|_| self.state.get_utf8_raw(self.keycode))
|
||||
}
|
||||
|
||||
fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> {
|
||||
if let Some(compose) = &self.compose {
|
||||
match compose {
|
||||
XkbCompose::Accepted(status) => match status {
|
||||
ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => {
|
||||
Ok(self.state.compose_get_utf8_normal())
|
||||
}
|
||||
ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()),
|
||||
_ => Ok(None),
|
||||
},
|
||||
XkbCompose::Ignored | XkbCompose::Uninitialized => Err(()),
|
||||
}
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||
///
|
||||
/// For some modifiers, this means that the key is currently pressed, others are toggled
|
||||
/// (like caps lock).
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ModifiersState {
|
||||
/// The "control" key
|
||||
pub ctrl: bool,
|
||||
/// The "alt" key
|
||||
pub alt: bool,
|
||||
/// The "shift" key
|
||||
pub shift: bool,
|
||||
/// The "Caps lock" key
|
||||
pub caps_lock: bool,
|
||||
/// The "logo" key
|
||||
///
|
||||
/// Also known as the "windows" key on most keyboards
|
||||
pub logo: bool,
|
||||
/// The "Num lock" key
|
||||
pub num_lock: bool,
|
||||
}
|
||||
|
||||
impl ModifiersState {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn update_with(&mut self, state: *mut ffi::xkb_state) {
|
||||
let mod_name_is_active = |mod_name: &[u8]| unsafe {
|
||||
(XKBH.xkb_state_mod_name_is_active)(
|
||||
state,
|
||||
mod_name.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
|
||||
) > 0
|
||||
};
|
||||
self.ctrl = mod_name_is_active(ffi::XKB_MOD_NAME_CTRL);
|
||||
self.alt = mod_name_is_active(ffi::XKB_MOD_NAME_ALT);
|
||||
self.shift = mod_name_is_active(ffi::XKB_MOD_NAME_SHIFT);
|
||||
self.caps_lock = mod_name_is_active(ffi::XKB_MOD_NAME_CAPS);
|
||||
self.logo = mod_name_is_active(ffi::XKB_MOD_NAME_LOGO);
|
||||
self.num_lock = mod_name_is_active(ffi::XKB_MOD_NAME_NUM);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModifiersState> for crate::keyboard::ModifiersState {
|
||||
fn from(mods: ModifiersState) -> crate::keyboard::ModifiersState {
|
||||
let mut to_mods = crate::keyboard::ModifiersState::empty();
|
||||
to_mods.set(crate::keyboard::ModifiersState::SHIFT, mods.shift);
|
||||
to_mods.set(crate::keyboard::ModifiersState::CONTROL, mods.ctrl);
|
||||
to_mods.set(crate::keyboard::ModifiersState::ALT, mods.alt);
|
||||
to_mods.set(crate::keyboard::ModifiersState::SUPER, mods.logo);
|
||||
to_mods
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// libxkbcommon is not available
|
||||
XKBNotFound,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum XkbCompose {
|
||||
Accepted(ffi::xkb_compose_status),
|
||||
Ignored,
|
||||
Uninitialized,
|
||||
}
|
||||
|
||||
// Note: This is track_caller so we can have more informative line numbers when logging
|
||||
#[track_caller]
|
||||
fn byte_slice_to_smol_str(bytes: &[u8]) -> Option<SmolStr> {
|
||||
std::str::from_utf8(bytes)
|
||||
.map(SmolStr::new)
|
||||
.map_err(|e| {
|
||||
warn!(
|
||||
"UTF-8 received from libxkbcommon ({:?}) was invalid: {e}",
|
||||
bytes
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue