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()
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ use std::{
|
|||
#[cfg(x11_platform)]
|
||||
use once_cell::sync::Lazy;
|
||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
pub use self::x11::XNotSupported;
|
||||
|
|
@ -28,11 +29,13 @@ use crate::platform::x11::XlibErrorHook;
|
|||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::Event,
|
||||
event::{Event, KeyEvent},
|
||||
event_loop::{
|
||||
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
|
||||
},
|
||||
icon::Icon,
|
||||
keyboard::{Key, KeyCode},
|
||||
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
|
||||
window::{
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
|
|
@ -42,6 +45,7 @@ use crate::{
|
|||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
pub(self) use crate::platform_impl::Fullscreen;
|
||||
|
||||
pub mod common;
|
||||
#[cfg(wayland_platform)]
|
||||
pub mod wayland;
|
||||
#[cfg(x11_platform)]
|
||||
|
|
@ -514,6 +518,11 @@ impl Window {
|
|||
x11_or_wayland!(match self; Window(w) => w.set_ime_position(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset_dead_keys(&self) {
|
||||
common::xkb_state::reset_dead_keys()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_allowed(allowed))
|
||||
|
|
@ -624,6 +633,37 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub key_without_modifiers: Key,
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
}
|
||||
|
||||
impl KeyEventExtModifierSupplement for KeyEvent {
|
||||
#[inline]
|
||||
fn text_with_all_modifiers(&self) -> Option<&str> {
|
||||
self.platform_specific
|
||||
.text_with_all_modifiers
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn key_without_modifiers(&self) -> Key {
|
||||
self.platform_specific.key_without_modifiers.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyCodeExtScancode for KeyCode {
|
||||
fn from_scancode(scancode: u32) -> KeyCode {
|
||||
common::keymap::raw_keycode_to_keycode(scancode)
|
||||
}
|
||||
|
||||
fn to_scancode(self) -> Option<u32> {
|
||||
common::keymap::keycode_to_raw(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hooks for X11 errors.
|
||||
#[cfg(x11_platform)]
|
||||
pub(crate) static mut XLIB_ERROR_HOOKS: Lazy<Mutex<Vec<XlibErrorHook>>> =
|
||||
|
|
|
|||
|
|
@ -1,192 +0,0 @@
|
|||
use sctk::seat::keyboard::keysyms;
|
||||
|
||||
use crate::event::VirtualKeyCode;
|
||||
|
||||
/// Convert xkb keysym into winit's VirtualKeyCode.
|
||||
pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
match keysym {
|
||||
// Numbers.
|
||||
keysyms::XKB_KEY_1 => Some(VirtualKeyCode::Key1),
|
||||
keysyms::XKB_KEY_2 => Some(VirtualKeyCode::Key2),
|
||||
keysyms::XKB_KEY_3 => Some(VirtualKeyCode::Key3),
|
||||
keysyms::XKB_KEY_4 => Some(VirtualKeyCode::Key4),
|
||||
keysyms::XKB_KEY_5 => Some(VirtualKeyCode::Key5),
|
||||
keysyms::XKB_KEY_6 => Some(VirtualKeyCode::Key6),
|
||||
keysyms::XKB_KEY_7 => Some(VirtualKeyCode::Key7),
|
||||
keysyms::XKB_KEY_8 => Some(VirtualKeyCode::Key8),
|
||||
keysyms::XKB_KEY_9 => Some(VirtualKeyCode::Key9),
|
||||
keysyms::XKB_KEY_0 => Some(VirtualKeyCode::Key0),
|
||||
// Letters.
|
||||
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),
|
||||
keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B),
|
||||
keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C),
|
||||
keysyms::XKB_KEY_D | keysyms::XKB_KEY_d => Some(VirtualKeyCode::D),
|
||||
keysyms::XKB_KEY_E | keysyms::XKB_KEY_e => Some(VirtualKeyCode::E),
|
||||
keysyms::XKB_KEY_F | keysyms::XKB_KEY_f => Some(VirtualKeyCode::F),
|
||||
keysyms::XKB_KEY_G | keysyms::XKB_KEY_g => Some(VirtualKeyCode::G),
|
||||
keysyms::XKB_KEY_H | keysyms::XKB_KEY_h => Some(VirtualKeyCode::H),
|
||||
keysyms::XKB_KEY_I | keysyms::XKB_KEY_i => Some(VirtualKeyCode::I),
|
||||
keysyms::XKB_KEY_J | keysyms::XKB_KEY_j => Some(VirtualKeyCode::J),
|
||||
keysyms::XKB_KEY_K | keysyms::XKB_KEY_k => Some(VirtualKeyCode::K),
|
||||
keysyms::XKB_KEY_L | keysyms::XKB_KEY_l => Some(VirtualKeyCode::L),
|
||||
keysyms::XKB_KEY_M | keysyms::XKB_KEY_m => Some(VirtualKeyCode::M),
|
||||
keysyms::XKB_KEY_N | keysyms::XKB_KEY_n => Some(VirtualKeyCode::N),
|
||||
keysyms::XKB_KEY_O | keysyms::XKB_KEY_o => Some(VirtualKeyCode::O),
|
||||
keysyms::XKB_KEY_P | keysyms::XKB_KEY_p => Some(VirtualKeyCode::P),
|
||||
keysyms::XKB_KEY_Q | keysyms::XKB_KEY_q => Some(VirtualKeyCode::Q),
|
||||
keysyms::XKB_KEY_R | keysyms::XKB_KEY_r => Some(VirtualKeyCode::R),
|
||||
keysyms::XKB_KEY_S | keysyms::XKB_KEY_s => Some(VirtualKeyCode::S),
|
||||
keysyms::XKB_KEY_T | keysyms::XKB_KEY_t => Some(VirtualKeyCode::T),
|
||||
keysyms::XKB_KEY_U | keysyms::XKB_KEY_u => Some(VirtualKeyCode::U),
|
||||
keysyms::XKB_KEY_V | keysyms::XKB_KEY_v => Some(VirtualKeyCode::V),
|
||||
keysyms::XKB_KEY_W | keysyms::XKB_KEY_w => Some(VirtualKeyCode::W),
|
||||
keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X),
|
||||
keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y),
|
||||
keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z),
|
||||
// Escape.
|
||||
keysyms::XKB_KEY_Escape => Some(VirtualKeyCode::Escape),
|
||||
// Function keys.
|
||||
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
|
||||
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
|
||||
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
|
||||
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
|
||||
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
|
||||
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
|
||||
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
|
||||
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
|
||||
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
|
||||
keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10),
|
||||
keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11),
|
||||
keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12),
|
||||
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
|
||||
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
|
||||
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
|
||||
keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16),
|
||||
keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17),
|
||||
keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18),
|
||||
keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19),
|
||||
keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20),
|
||||
keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21),
|
||||
keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22),
|
||||
keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23),
|
||||
keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24),
|
||||
// Flow control.
|
||||
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
|
||||
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),
|
||||
keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause),
|
||||
keysyms::XKB_KEY_Insert => Some(VirtualKeyCode::Insert),
|
||||
keysyms::XKB_KEY_Home => Some(VirtualKeyCode::Home),
|
||||
keysyms::XKB_KEY_Delete => Some(VirtualKeyCode::Delete),
|
||||
keysyms::XKB_KEY_End => Some(VirtualKeyCode::End),
|
||||
keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||
keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp),
|
||||
// Arrows.
|
||||
keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left),
|
||||
keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up),
|
||||
keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right),
|
||||
keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down),
|
||||
|
||||
keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back),
|
||||
keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return),
|
||||
keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space),
|
||||
|
||||
keysyms::XKB_KEY_Multi_key => Some(VirtualKeyCode::Compose),
|
||||
keysyms::XKB_KEY_caret => Some(VirtualKeyCode::Caret),
|
||||
|
||||
// Keypad.
|
||||
keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock),
|
||||
keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0),
|
||||
keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1),
|
||||
keysyms::XKB_KEY_KP_2 => Some(VirtualKeyCode::Numpad2),
|
||||
keysyms::XKB_KEY_KP_3 => Some(VirtualKeyCode::Numpad3),
|
||||
keysyms::XKB_KEY_KP_4 => Some(VirtualKeyCode::Numpad4),
|
||||
keysyms::XKB_KEY_KP_5 => Some(VirtualKeyCode::Numpad5),
|
||||
keysyms::XKB_KEY_KP_6 => Some(VirtualKeyCode::Numpad6),
|
||||
keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7),
|
||||
keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8),
|
||||
keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9),
|
||||
// Misc.
|
||||
// => Some(VirtualKeyCode::AbntC1),
|
||||
// => Some(VirtualKeyCode::AbntC2),
|
||||
keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Plus),
|
||||
keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe),
|
||||
// => Some(VirtualKeyCode::Apps),
|
||||
keysyms::XKB_KEY_at => Some(VirtualKeyCode::At),
|
||||
// => Some(VirtualKeyCode::Ax),
|
||||
keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash),
|
||||
keysyms::XKB_KEY_XF86Calculator => Some(VirtualKeyCode::Calculator),
|
||||
// => Some(VirtualKeyCode::Capital),
|
||||
keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon),
|
||||
keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma),
|
||||
// => Some(VirtualKeyCode::Convert),
|
||||
keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals),
|
||||
keysyms::XKB_KEY_grave => Some(VirtualKeyCode::Grave),
|
||||
// => Some(VirtualKeyCode::Kana),
|
||||
keysyms::XKB_KEY_Kanji => Some(VirtualKeyCode::Kanji),
|
||||
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
|
||||
keysyms::XKB_KEY_bracketleft => Some(VirtualKeyCode::LBracket),
|
||||
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
|
||||
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
|
||||
keysyms::XKB_KEY_Super_L => Some(VirtualKeyCode::LWin),
|
||||
keysyms::XKB_KEY_XF86Mail => Some(VirtualKeyCode::Mail),
|
||||
// => Some(VirtualKeyCode::MediaSelect),
|
||||
// => Some(VirtualKeyCode::MediaStop),
|
||||
keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus),
|
||||
keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Asterisk),
|
||||
keysyms::XKB_KEY_XF86AudioMute => Some(VirtualKeyCode::Mute),
|
||||
// => Some(VirtualKeyCode::MyComputer),
|
||||
keysyms::XKB_KEY_XF86AudioNext => Some(VirtualKeyCode::NextTrack),
|
||||
// => Some(VirtualKeyCode::NoConvert),
|
||||
keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma),
|
||||
keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter),
|
||||
keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals),
|
||||
keysyms::XKB_KEY_KP_Add => Some(VirtualKeyCode::NumpadAdd),
|
||||
keysyms::XKB_KEY_KP_Subtract => Some(VirtualKeyCode::NumpadSubtract),
|
||||
keysyms::XKB_KEY_KP_Multiply => Some(VirtualKeyCode::NumpadMultiply),
|
||||
keysyms::XKB_KEY_KP_Divide => Some(VirtualKeyCode::NumpadDivide),
|
||||
keysyms::XKB_KEY_KP_Decimal => Some(VirtualKeyCode::NumpadDecimal),
|
||||
keysyms::XKB_KEY_KP_Page_Up => Some(VirtualKeyCode::PageUp),
|
||||
keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||
keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home),
|
||||
keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End),
|
||||
keysyms::XKB_KEY_KP_Left => Some(VirtualKeyCode::Left),
|
||||
keysyms::XKB_KEY_KP_Up => Some(VirtualKeyCode::Up),
|
||||
keysyms::XKB_KEY_KP_Right => Some(VirtualKeyCode::Right),
|
||||
keysyms::XKB_KEY_KP_Down => Some(VirtualKeyCode::Down),
|
||||
// => Some(VirtualKeyCode::OEM102),
|
||||
keysyms::XKB_KEY_period => Some(VirtualKeyCode::Period),
|
||||
// => Some(VirtualKeyCode::Playpause),
|
||||
keysyms::XKB_KEY_XF86PowerOff => Some(VirtualKeyCode::Power),
|
||||
keysyms::XKB_KEY_XF86AudioPrev => Some(VirtualKeyCode::PrevTrack),
|
||||
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
|
||||
keysyms::XKB_KEY_bracketright => Some(VirtualKeyCode::RBracket),
|
||||
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
|
||||
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
|
||||
keysyms::XKB_KEY_Super_R => Some(VirtualKeyCode::RWin),
|
||||
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),
|
||||
keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash),
|
||||
keysyms::XKB_KEY_XF86Sleep => Some(VirtualKeyCode::Sleep),
|
||||
// => Some(VirtualKeyCode::Stop),
|
||||
// => Some(VirtualKeyCode::Sysrq),
|
||||
keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab),
|
||||
keysyms::XKB_KEY_ISO_Left_Tab => Some(VirtualKeyCode::Tab),
|
||||
keysyms::XKB_KEY_underscore => Some(VirtualKeyCode::Underline),
|
||||
// => Some(VirtualKeyCode::Unlabeled),
|
||||
keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown),
|
||||
keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp),
|
||||
// => Some(VirtualKeyCode::Wake),
|
||||
// => Some(VirtualKeyCode::Webback),
|
||||
// => Some(VirtualKeyCode::WebFavorites),
|
||||
// => Some(VirtualKeyCode::WebForward),
|
||||
// => Some(VirtualKeyCode::WebHome),
|
||||
// => Some(VirtualKeyCode::WebRefresh),
|
||||
// => Some(VirtualKeyCode::WebSearch),
|
||||
// => Some(VirtualKeyCode::WebStop),
|
||||
keysyms::XKB_KEY_yen => Some(VirtualKeyCode::Yen),
|
||||
keysyms::XKB_KEY_XF86Copy => Some(VirtualKeyCode::Copy),
|
||||
keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste),
|
||||
keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut),
|
||||
// Fallback.
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,242 +1,373 @@
|
|||
//! The keyboard input handling.
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use calloop::timer::{TimeoutAction, Timer};
|
||||
use calloop::{LoopHandle, RegistrationToken};
|
||||
use log::warn;
|
||||
|
||||
use sctk::reexports::client::delegate_dispatch;
|
||||
use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
|
||||
use sctk::reexports::client::protocol::wl_keyboard::{
|
||||
Event as WlKeyboardEvent, KeyState as WlKeyState, KeymapFormat as WlKeymapFormat,
|
||||
};
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
|
||||
|
||||
use sctk::seat::keyboard::{KeyEvent, KeyboardData, KeyboardDataExt, KeyboardHandler, Modifiers};
|
||||
use sctk::seat::SeatState;
|
||||
|
||||
use crate::event::{ElementState, ModifiersState, WindowEvent};
|
||||
use crate::event::{ElementState, WindowEvent};
|
||||
use crate::keyboard::ModifiersState;
|
||||
|
||||
use crate::platform_impl::common::xkb_state::KbdState;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::seat::WinitSeatState;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
use crate::platform_impl::wayland::{self, DeviceId, WindowId};
|
||||
|
||||
mod keymap;
|
||||
|
||||
impl WinitState {
|
||||
pub fn handle_key_input(
|
||||
&mut self,
|
||||
keyboard: &WlKeyboard,
|
||||
event: KeyEvent,
|
||||
state: ElementState,
|
||||
impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
fn event(
|
||||
state: &mut WinitState,
|
||||
wl_keyboard: &WlKeyboard,
|
||||
event: <WlKeyboard as Proxy>::Event,
|
||||
data: &KeyboardData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<WinitState>,
|
||||
) {
|
||||
let window_id = match *keyboard.winit_data().window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
let seat_state = match state.seats.get_mut(&data.seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let virtual_keycode = keymap::keysym_to_vkey(event.keysym);
|
||||
|
||||
let seat_state = self.seats.get(&keyboard.seat().id()).unwrap();
|
||||
let modifiers = seat_state.modifiers;
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
#[allow(deprecated)]
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||
DeviceId,
|
||||
)),
|
||||
input: crate::event::KeyboardInput {
|
||||
state,
|
||||
scancode: event.raw_code,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
match event {
|
||||
WlKeyboardEvent::Keymap { format, fd, size } => match format {
|
||||
WEnum::Value(format) => match format {
|
||||
WlKeymapFormat::NoKeymap => {
|
||||
warn!("non-xkb compatible keymap")
|
||||
}
|
||||
WlKeymapFormat::XkbV1 => unsafe {
|
||||
seat_state
|
||||
.keyboard_state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.xkb_state
|
||||
.init_with_fd(fd, size as usize);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
WEnum::Unknown(value) => {
|
||||
warn!("unknown keymap format 0x{:x}", value)
|
||||
}
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
WlKeyboardEvent::Enter { surface, .. } => {
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
|
||||
// Don't send utf8 chars on release.
|
||||
if state == ElementState::Released {
|
||||
return;
|
||||
}
|
||||
// Mark the window as focused.
|
||||
match state.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(true),
|
||||
None => return,
|
||||
};
|
||||
|
||||
if let Some(txt) = event.utf8 {
|
||||
for ch in txt.chars() {
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id);
|
||||
// Drop the repeat, if there were any.
|
||||
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
|
||||
|
||||
// The keyboard focus is considered as general focus.
|
||||
state
|
||||
.events_sink
|
||||
.push_window_event(WindowEvent::Focused(true), window_id);
|
||||
|
||||
*data.window_id.lock().unwrap() = Some(window_id);
|
||||
|
||||
// HACK: this is just for GNOME not fixing their ordering issue of modifiers.
|
||||
if std::mem::take(&mut seat_state.modifiers_pending) {
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
WlKeyboardEvent::Leave { surface, .. } => {
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
|
||||
// NOTE: we should drop the repeat regardless whethere it was for the present
|
||||
// window of for the window which just went gone.
|
||||
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
|
||||
|
||||
// NOTE: The check whether the window exists is essential as we might get a
|
||||
// nil surface, regardless of what protocol says.
|
||||
match state.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(false),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Notify that no modifiers are being pressed.
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
|
||||
window_id,
|
||||
);
|
||||
|
||||
// We don't need to update it above, because the next `Enter` will overwrite
|
||||
// anyway.
|
||||
*data.window_id.lock().unwrap() = None;
|
||||
|
||||
state
|
||||
.events_sink
|
||||
.push_window_event(WindowEvent::Focused(false), window_id);
|
||||
}
|
||||
WlKeyboardEvent::Key {
|
||||
key,
|
||||
state: key_state,
|
||||
..
|
||||
} if key_state == WEnum::Value(WlKeyState::Pressed) => {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
ElementState::Pressed,
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
let delay = match keyboard_state.repeat_info {
|
||||
RepeatInfo::Repeat { delay, .. } => delay,
|
||||
RepeatInfo::Disable => return,
|
||||
};
|
||||
|
||||
if !keyboard_state.xkb_state.key_repeats(key) {
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard_state.current_repeat = Some(key);
|
||||
|
||||
// NOTE terminate ongoing timer and start a new timer.
|
||||
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
}
|
||||
|
||||
let timer = Timer::from_duration(delay);
|
||||
let wl_keyboard = wl_keyboard.clone();
|
||||
keyboard_state.repeat_token = keyboard_state
|
||||
.loop_handle
|
||||
.insert_source(timer, move |_, _, state| {
|
||||
let data = wl_keyboard.data::<KeyboardData>().unwrap();
|
||||
let seat_state = state.seats.get_mut(&data.seat.id()).unwrap();
|
||||
|
||||
// NOTE: The removed on event source is batched, but key change to
|
||||
// `None` is instant.
|
||||
let repeat_keycode =
|
||||
match seat_state.keyboard_state.as_ref().unwrap().current_repeat {
|
||||
Some(repeat_keycode) => repeat_keycode,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
repeat_keycode,
|
||||
ElementState::Pressed,
|
||||
true,
|
||||
);
|
||||
|
||||
// NOTE: the gap could change dynamically while repeat is going.
|
||||
match seat_state.keyboard_state.as_ref().unwrap().repeat_info {
|
||||
RepeatInfo::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
|
||||
RepeatInfo::Disable => TimeoutAction::Drop,
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
WlKeyboardEvent::Key {
|
||||
key,
|
||||
state: key_state,
|
||||
..
|
||||
} if key_state == WEnum::Value(WlKeyState::Released) => {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
ElementState::Released,
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
if keyboard_state.repeat_info != RepeatInfo::Disable
|
||||
&& keyboard_state.xkb_state.key_repeats(key)
|
||||
&& Some(key) == keyboard_state.current_repeat
|
||||
{
|
||||
keyboard_state.current_repeat = None;
|
||||
}
|
||||
}
|
||||
WlKeyboardEvent::Modifiers {
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
group,
|
||||
..
|
||||
} => {
|
||||
let xkb_state = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_state;
|
||||
xkb_state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||
seat_state.modifiers = xkb_state.mods_state().into();
|
||||
|
||||
// HACK: part of the workaround from `WlKeyboardEvent::Enter`.
|
||||
let window_id = match *data.window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
None => {
|
||||
seat_state.modifiers_pending = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
WlKeyboardEvent::RepeatInfo { rate, delay } => {
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.repeat_info = if rate == 0 {
|
||||
// Stop the repeat once we get a disable event.
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(repeat_token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(repeat_token);
|
||||
}
|
||||
RepeatInfo::Disable
|
||||
} else {
|
||||
let gap = Duration::from_micros(1_000_000 / rate as u64);
|
||||
let delay = Duration::from_millis(delay as u64);
|
||||
RepeatInfo::Repeat { gap, delay }
|
||||
};
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardHandler for WinitState {
|
||||
fn enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
surface: &WlSurface,
|
||||
_serial: u32,
|
||||
_raw: &[u32],
|
||||
_keysyms: &[u32],
|
||||
) {
|
||||
let window_id = wayland::make_wid(surface);
|
||||
/// The state of the keyboard on the current seat.
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardState {
|
||||
/// The underlying WlKeyboard.
|
||||
pub keyboard: WlKeyboard,
|
||||
|
||||
// Mark the window as focused.
|
||||
match self.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(true),
|
||||
None => return,
|
||||
};
|
||||
/// Loop handle to handle key repeat.
|
||||
pub loop_handle: LoopHandle<'static, WinitState>,
|
||||
|
||||
// Window gained focus.
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::Focused(true), window_id);
|
||||
/// The state of the keyboard.
|
||||
pub xkb_state: KbdState,
|
||||
|
||||
*keyboard.winit_data().window_id.lock().unwrap() = Some(window_id);
|
||||
/// The information about the repeat rate obtained from the compositor.
|
||||
pub repeat_info: RepeatInfo,
|
||||
|
||||
// NOTE: GNOME still hasn't violates the specification wrt ordering of such
|
||||
// events. See https://gitlab.gnome.org/GNOME/mutter/-/issues/2231.
|
||||
let seat_state = self.seats.get_mut(&keyboard.seat().id()).unwrap();
|
||||
if std::mem::take(&mut seat_state.modifiers_pending) {
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(seat_state.modifiers),
|
||||
window_id,
|
||||
);
|
||||
/// The token of the current handle inside the calloop's event loop.
|
||||
pub repeat_token: Option<RegistrationToken>,
|
||||
|
||||
/// The current repeat raw key.
|
||||
pub current_repeat: Option<u32>,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
pub fn new(keyboard: WlKeyboard, loop_handle: LoopHandle<'static, WinitState>) -> Self {
|
||||
Self {
|
||||
keyboard,
|
||||
loop_handle,
|
||||
xkb_state: KbdState::new().unwrap(),
|
||||
repeat_info: RepeatInfo::default(),
|
||||
repeat_token: None,
|
||||
current_repeat: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
surface: &WlSurface,
|
||||
_serial: u32,
|
||||
) {
|
||||
let window_id = wayland::make_wid(surface);
|
||||
|
||||
// XXX The check whether the window exists is essential as we might get a nil surface...
|
||||
match self.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(false),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Notify that no modifiers are being pressed.
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
||||
window_id,
|
||||
);
|
||||
|
||||
*keyboard.winit_data().window_id.lock().unwrap() = None;
|
||||
|
||||
// Window lost focus.
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::Focused(false), window_id);
|
||||
}
|
||||
|
||||
fn press_key(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
self.handle_key_input(keyboard, event, ElementState::Pressed);
|
||||
}
|
||||
|
||||
fn release_key(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
self.handle_key_input(keyboard, event, ElementState::Released);
|
||||
}
|
||||
|
||||
// FIXME(kchibisov): recent spec suggested that this event could be sent
|
||||
// without any focus to indicate modifiers for the pointer, so update
|
||||
// for will be required once https://github.com/rust-windowing/winit/issues/2768
|
||||
// wrt winit design of the event is resolved.
|
||||
fn update_modifiers(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
_serial: u32,
|
||||
modifiers: Modifiers,
|
||||
) {
|
||||
let modifiers = ModifiersState::from(modifiers);
|
||||
let seat_state = self.seats.get_mut(&keyboard.seat().id()).unwrap();
|
||||
seat_state.modifiers = modifiers;
|
||||
|
||||
// NOTE: part of the workaround from `fn enter`, see it above.
|
||||
let window_id = match *keyboard.winit_data().window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
None => {
|
||||
seat_state.modifiers_pending = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::ModifiersChanged(modifiers), window_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// The extension to KeyboardData used to store the `window_id`.
|
||||
pub struct WinitKeyboardData {
|
||||
impl Drop for KeyboardState {
|
||||
fn drop(&mut self) {
|
||||
if self.keyboard.version() >= 3 {
|
||||
self.keyboard.release();
|
||||
}
|
||||
|
||||
if let Some(token) = self.repeat_token.take() {
|
||||
self.loop_handle.remove(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The rate at which a pressed key is repeated.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RepeatInfo {
|
||||
/// Keys will be repeated at the specified rate and delay.
|
||||
Repeat {
|
||||
/// The time between the key repeats.
|
||||
gap: Duration,
|
||||
|
||||
/// Delay (in milliseconds) between a key press and the start of repetition.
|
||||
delay: Duration,
|
||||
},
|
||||
|
||||
/// Keys should not be repeated.
|
||||
Disable,
|
||||
}
|
||||
|
||||
impl Default for RepeatInfo {
|
||||
/// The default repeat rate is 25 keys per second with the delay of 200ms.
|
||||
///
|
||||
/// The values are picked based on the default in various compositors and Xorg.
|
||||
fn default() -> Self {
|
||||
Self::Repeat {
|
||||
gap: Duration::from_millis(40),
|
||||
delay: Duration::from_millis(200),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Keyboard user data.
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardData {
|
||||
/// The currently focused window surface. Could be `None` on bugged compositors, like mutter.
|
||||
window_id: Mutex<Option<WindowId>>,
|
||||
|
||||
/// The original keyboard date.
|
||||
keyboard_data: KeyboardData<WinitState>,
|
||||
/// The seat used to create this keyboard.
|
||||
seat: WlSeat,
|
||||
}
|
||||
|
||||
impl WinitKeyboardData {
|
||||
impl KeyboardData {
|
||||
pub fn new(seat: WlSeat) -> Self {
|
||||
Self {
|
||||
window_id: Default::default(),
|
||||
keyboard_data: KeyboardData::new(seat),
|
||||
seat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardDataExt for WinitKeyboardData {
|
||||
type State = WinitState;
|
||||
fn key_input(
|
||||
seat_state: &mut WinitSeatState,
|
||||
event_sink: &mut EventSink,
|
||||
data: &KeyboardData,
|
||||
keycode: u32,
|
||||
state: ElementState,
|
||||
repeat: bool,
|
||||
) {
|
||||
let window_id = match *data.window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
None => return,
|
||||
};
|
||||
|
||||
fn keyboard_data(&self) -> &KeyboardData<Self::State> {
|
||||
&self.keyboard_data
|
||||
}
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
|
||||
fn keyboard_data_mut(&mut self) -> &mut KeyboardData<Self::State> {
|
||||
&mut self.keyboard_data
|
||||
}
|
||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||
let event = keyboard_state
|
||||
.xkb_state
|
||||
.process_key_event(keycode, state, repeat);
|
||||
|
||||
event_sink.push_window_event(
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
|
||||
pub trait WinitKeyboardDataExt {
|
||||
fn winit_data(&self) -> &WinitKeyboardData;
|
||||
|
||||
fn seat(&self) -> &WlSeat {
|
||||
self.winit_data().keyboard_data().seat()
|
||||
}
|
||||
}
|
||||
|
||||
impl WinitKeyboardDataExt for WlKeyboard {
|
||||
fn winit_data(&self) -> &WinitKeyboardData {
|
||||
self.data::<WinitKeyboardData>()
|
||||
.expect("failed to get keyboard data.")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Modifiers> for ModifiersState {
|
||||
fn from(mods: Modifiers) -> ModifiersState {
|
||||
let mut wl_mods = ModifiersState::empty();
|
||||
wl_mods.set(ModifiersState::SHIFT, mods.shift);
|
||||
wl_mods.set(ModifiersState::CTRL, mods.ctrl);
|
||||
wl_mods.set(ModifiersState::ALT, mods.alt);
|
||||
wl_mods.set(ModifiersState::LOGO, mods.logo);
|
||||
wl_mods
|
||||
}
|
||||
}
|
||||
|
||||
delegate_dispatch!(WinitState: [ WlKeyboard: WinitKeyboardData] => SeatState);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use std::sync::Arc;
|
|||
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
|
|
@ -14,7 +13,7 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
|||
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
||||
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
||||
|
||||
use crate::event::{ElementState, ModifiersState};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
|
||||
mod keyboard;
|
||||
|
|
@ -26,7 +25,7 @@ pub use pointer::relative_pointer::RelativePointerState;
|
|||
pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
|
||||
pub use text_input::{TextInputState, ZwpTextInputV3Ext};
|
||||
|
||||
use keyboard::WinitKeyboardData;
|
||||
use keyboard::{KeyboardData, KeyboardState};
|
||||
use text_input::TextInputData;
|
||||
use touch::TouchPoint;
|
||||
|
||||
|
|
@ -91,20 +90,9 @@ impl SeatHandler for WinitState {
|
|||
seat_state.touch = self.seat_state.get_touch(queue_handle, &seat).ok();
|
||||
}
|
||||
SeatCapability::Keyboard if seat_state.keyboard_state.is_none() => {
|
||||
let keyboard = self
|
||||
.seat_state
|
||||
.get_keyboard_with_repeat_with_data(
|
||||
queue_handle,
|
||||
&seat,
|
||||
WinitKeyboardData::new(seat.clone()),
|
||||
self.loop_handle.clone(),
|
||||
Box::new(|state, keyboard, event| {
|
||||
state.handle_key_input(keyboard, event, ElementState::Pressed);
|
||||
}),
|
||||
)
|
||||
.expect("failed to create keyboard with present capability.");
|
||||
|
||||
seat_state.keyboard_state = Some(KeyboardState { keyboard });
|
||||
let keyboard = seat.get_keyboard(queue_handle, KeyboardData::new(seat.clone()));
|
||||
seat_state.keyboard_state =
|
||||
Some(KeyboardState::new(keyboard, self.loop_handle.clone()));
|
||||
}
|
||||
SeatCapability::Pointer if seat_state.pointer.is_none() => {
|
||||
let surface = self.compositor_state.create_surface(queue_handle);
|
||||
|
|
@ -192,11 +180,7 @@ impl SeatHandler for WinitState {
|
|||
}
|
||||
}
|
||||
SeatCapability::Keyboard => {
|
||||
if let Some(keyboard_state) = seat_state.keyboard_state.take() {
|
||||
if keyboard_state.keyboard.version() >= 3 {
|
||||
keyboard_state.keyboard.release();
|
||||
}
|
||||
}
|
||||
seat_state.keyboard_state = None;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -225,10 +209,4 @@ impl SeatHandler for WinitState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardState {
|
||||
/// The underlying WlKeyboard.
|
||||
pub keyboard: WlKeyboard,
|
||||
}
|
||||
|
||||
sctk::delegate_seat!(WinitState);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ impl PointerHandler for WinitState {
|
|||
) {
|
||||
let seat = pointer.winit_data().seat();
|
||||
let seat_state = self.seats.get(&seat.id()).unwrap();
|
||||
let modifiers = seat_state.modifiers;
|
||||
|
||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||
|
||||
|
|
@ -130,7 +129,6 @@ impl PointerHandler for WinitState {
|
|||
WindowEvent::CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
|
|
@ -151,7 +149,6 @@ impl PointerHandler for WinitState {
|
|||
WindowEvent::CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
|
|
@ -177,7 +174,6 @@ impl PointerHandler for WinitState {
|
|||
device_id,
|
||||
state,
|
||||
button,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
|
|
@ -231,7 +227,6 @@ impl PointerHandler for WinitState {
|
|||
device_id,
|
||||
delta,
|
||||
phase,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,19 @@
|
|||
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc};
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||
use libc::{c_char, c_int, c_long, c_ulong};
|
||||
|
||||
use super::{
|
||||
events, ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd,
|
||||
DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
|
||||
XExtension,
|
||||
ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState,
|
||||
GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension,
|
||||
};
|
||||
|
||||
use util::modifiers::{ModifierKeyState, ModifierKeymap};
|
||||
|
||||
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{
|
||||
DeviceEvent, ElementState, Event, Ime, KeyboardInput, ModifiersState, TouchPhase,
|
||||
WindowEvent,
|
||||
},
|
||||
event::{DeviceEvent, ElementState, Event, Ime, RawKeyEvent, TouchPhase, WindowEvent},
|
||||
event_loop::EventLoopWindowTarget as RootELW,
|
||||
keyboard::ModifiersState,
|
||||
platform_impl::platform::common::{keymap, xkb_state::KbdState},
|
||||
};
|
||||
|
||||
/// The X11 documentation states: "Keycodes lie in the inclusive range `[8, 255]`".
|
||||
|
|
@ -30,9 +26,9 @@ pub(super) struct EventProcessor<T: 'static> {
|
|||
pub(super) randr_event_offset: c_int,
|
||||
pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
|
||||
pub(super) xi2ext: XExtension,
|
||||
pub(super) xkbext: XExtension,
|
||||
pub(super) target: Rc<RootELW<T>>,
|
||||
pub(super) mod_keymap: ModifierKeymap,
|
||||
pub(super) device_mod_state: ModifierKeyState,
|
||||
pub(super) kb_state: KbdState,
|
||||
// Number of touch events currently in progress
|
||||
pub(super) num_touch: u32,
|
||||
pub(super) first_touch: Option<u64>,
|
||||
|
|
@ -134,47 +130,8 @@ impl<T: 'static> EventProcessor<T> {
|
|||
return;
|
||||
}
|
||||
|
||||
// We can't call a `&mut self` method because of the above borrow,
|
||||
// so we use this macro for repeated modifier state updates.
|
||||
macro_rules! update_modifiers {
|
||||
( $state:expr , $modifier:expr ) => {{
|
||||
match ($state, $modifier) {
|
||||
(state, modifier) => {
|
||||
if let Some(modifiers) =
|
||||
self.device_mod_state.update_state(&state, modifier)
|
||||
{
|
||||
if let Some(window_id) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window_id),
|
||||
event: WindowEvent::ModifiersChanged(modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
let event_type = xev.get_type();
|
||||
match event_type {
|
||||
ffi::MappingNotify => {
|
||||
let mapping: &ffi::XMappingEvent = xev.as_ref();
|
||||
|
||||
if mapping.request == ffi::MappingModifier
|
||||
|| mapping.request == ffi::MappingKeyboard
|
||||
{
|
||||
unsafe {
|
||||
(wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut());
|
||||
}
|
||||
wt.xconn
|
||||
.check_errors()
|
||||
.expect("Failed to call XRefreshKeyboardMapping");
|
||||
|
||||
self.mod_keymap.reset_from_x_connection(&wt.xconn);
|
||||
self.device_mod_state.update_keymap(&self.mod_keymap);
|
||||
}
|
||||
}
|
||||
|
||||
ffi::ClientMessage => {
|
||||
let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
|
||||
|
||||
|
|
@ -577,90 +534,35 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
}
|
||||
|
||||
ffi::KeyPress | ffi::KeyRelease => {
|
||||
use crate::event::ElementState::{Pressed, Released};
|
||||
|
||||
// Note that in compose/pre-edit sequences, this will always be Released.
|
||||
let state = if xev.get_type() == ffi::KeyPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
|
||||
// Note that in compose/pre-edit sequences, we'll always receive KeyRelease events
|
||||
ffi::KeyPress => {
|
||||
let xkev: &mut ffi::XKeyEvent = xev.as_mut();
|
||||
|
||||
let window = xkev.window;
|
||||
let window_id = mkwid(window);
|
||||
|
||||
// Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable
|
||||
// value, though this should only be an issue under multiseat configurations.
|
||||
let device = util::VIRTUAL_CORE_KEYBOARD;
|
||||
let device_id = mkdid(device);
|
||||
let keycode = xkev.keycode;
|
||||
let written = if let Some(ic) = wt.ime.borrow().get_context(window) {
|
||||
wt.xconn.lookup_utf8(ic, xkev)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||
// a keycode of 0.
|
||||
if keycode != 0 && !self.is_composing {
|
||||
let scancode = keycode - KEYCODE_OFFSET as u32;
|
||||
let keysym = wt.xconn.lookup_keysym(xkev);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
update_modifiers!(
|
||||
ModifiersState::from_x11_mask(xkev.state),
|
||||
self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode)
|
||||
);
|
||||
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
#[allow(deprecated)]
|
||||
callback(Event::WindowEvent {
|
||||
// If we're composing right now, send the string we've got from X11 via
|
||||
// Ime::Commit.
|
||||
if self.is_composing && xkev.keycode == 0 && !written.is_empty() {
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
};
|
||||
callback(event);
|
||||
|
||||
if state == Pressed {
|
||||
let written = if let Some(ic) = wt.ime.borrow().get_context(window) {
|
||||
wt.xconn.lookup_utf8(ic, xkev)
|
||||
} else {
|
||||
return;
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Ime(Ime::Commit(written)),
|
||||
};
|
||||
|
||||
// If we're composing right now, send the string we've got from X11 via
|
||||
// Ime::Commit.
|
||||
if self.is_composing && keycode == 0 && !written.is_empty() {
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
};
|
||||
callback(event);
|
||||
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Ime(Ime::Commit(written)),
|
||||
};
|
||||
|
||||
self.is_composing = false;
|
||||
callback(event);
|
||||
} else {
|
||||
for chr in written.chars() {
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ReceivedCharacter(chr),
|
||||
};
|
||||
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
self.is_composing = false;
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -696,9 +598,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
return;
|
||||
}
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
let state = if xev.evtype == ffi::XI_ButtonPress {
|
||||
Pressed
|
||||
} else {
|
||||
|
|
@ -711,7 +610,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Left,
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
ffi::Button2 => callback(Event::WindowEvent {
|
||||
|
|
@ -720,7 +618,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Middle,
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
ffi::Button3 => callback(Event::WindowEvent {
|
||||
|
|
@ -729,7 +626,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Right,
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
|
||||
|
|
@ -750,7 +646,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
phase: TouchPhase::Moved,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -762,7 +657,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Other(x as u16),
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
|
@ -773,9 +667,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
let window_id = mkwid(xev.event);
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
let cursor_moved = self.with_window(xev.event, |window| {
|
||||
let mut shared_state_lock = window.shared_state_lock();
|
||||
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
||||
|
|
@ -788,7 +679,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
} else if cursor_moved.is_none() {
|
||||
|
|
@ -835,7 +725,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
},
|
||||
phase: TouchPhase::Moved,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
|
@ -889,25 +778,11 @@ impl<T: 'static> EventProcessor<T> {
|
|||
|
||||
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||
|
||||
// The mods field on this event isn't actually populated, so query the
|
||||
// pointer device. In the future, we can likely remove this round-trip by
|
||||
// relying on `Xkb` for modifier values.
|
||||
//
|
||||
// This needs to only be done after confirming the window still exists,
|
||||
// since otherwise we risk getting a `BadWindow` error if the window was
|
||||
// dropped with queued events.
|
||||
let modifiers = wt
|
||||
.xconn
|
||||
.query_pointer(xev.event, xev.deviceid)
|
||||
.expect("Failed to query pointer device")
|
||||
.get_modifier_state();
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -935,10 +810,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
.focus(xev.event)
|
||||
.expect("Failed to focus input context");
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
|
||||
self.device_mod_state.update_state(&modifiers, None);
|
||||
|
||||
if self.active_window != Some(xev.event) {
|
||||
self.active_window = Some(xev.event);
|
||||
|
||||
|
|
@ -956,10 +827,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: Focused(true),
|
||||
});
|
||||
|
||||
let modifiers: crate::keyboard::ModifiersState =
|
||||
self.kb_state.mods_state().into();
|
||||
if !modifiers.is_empty() {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(modifiers),
|
||||
event: WindowEvent::ModifiersChanged(modifiers.into()),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -977,7 +850,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -986,8 +858,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
wt,
|
||||
window_id,
|
||||
ElementState::Pressed,
|
||||
&self.mod_keymap,
|
||||
&mut self.device_mod_state,
|
||||
&mut self.kb_state,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
|
|
@ -1013,14 +884,15 @@ impl<T: 'static> EventProcessor<T> {
|
|||
wt,
|
||||
window_id,
|
||||
ElementState::Released,
|
||||
&self.mod_keymap,
|
||||
&mut self.device_mod_state,
|
||||
&mut self.kb_state,
|
||||
&mut callback,
|
||||
);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
||||
event: WindowEvent::ModifiersChanged(
|
||||
ModifiersState::empty().into(),
|
||||
),
|
||||
});
|
||||
|
||||
if let Some(window) = self.with_window(xev.event, Arc::clone) {
|
||||
|
|
@ -1045,7 +917,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
};
|
||||
if self.window_exists(xev.event) {
|
||||
let id = xev.detail as u64;
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
let location = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||
|
||||
// Mouse cursor position changes when touch events are received.
|
||||
|
|
@ -1057,7 +928,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: WindowEvent::CursorMoved {
|
||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
|
||||
position: location.cast(),
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1143,6 +1013,39 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// The regular KeyPress event has a problem where if you press a dead key, a KeyPress
|
||||
// event won't be emitted. XInput 2 does not have this problem.
|
||||
ffi::XI_KeyPress | ffi::XI_KeyRelease if !self.is_composing => {
|
||||
if let Some(active_window) = self.active_window {
|
||||
let state = if xev.evtype == ffi::XI_KeyPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
|
||||
let xkev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
// We use `self.active_window` here as `xkev.event` has a completely different
|
||||
// value for some reason.
|
||||
let window_id = mkwid(active_window);
|
||||
|
||||
let device_id = mkdid(xkev.deviceid);
|
||||
let keycode = xkev.detail as u32;
|
||||
|
||||
let repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat;
|
||||
let event = self.kb_state.process_key_event(keycode, state, repeat);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
|
||||
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
|
|
@ -1153,46 +1056,19 @@ impl<T: 'static> EventProcessor<T> {
|
|||
};
|
||||
|
||||
let device_id = mkdid(xev.sourceid);
|
||||
let keycode = xev.detail;
|
||||
let scancode = keycode - KEYCODE_OFFSET as i32;
|
||||
if scancode < 0 {
|
||||
let keycode = xev.detail as u32;
|
||||
if keycode < KEYCODE_OFFSET as u32 {
|
||||
return;
|
||||
}
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
let physical_key = keymap::raw_keycode_to_keycode(keycode);
|
||||
|
||||
#[allow(deprecated)]
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode: scancode as u32,
|
||||
virtual_keycode,
|
||||
event: DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key,
|
||||
state,
|
||||
modifiers,
|
||||
}),
|
||||
});
|
||||
|
||||
if let Some(modifier) =
|
||||
self.mod_keymap.get_modifier(keycode as ffi::KeyCode)
|
||||
{
|
||||
self.device_mod_state.key_event(
|
||||
state,
|
||||
keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
|
||||
let new_modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
if modifiers != new_modifiers {
|
||||
if let Some(window_id) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window_id),
|
||||
event: WindowEvent::ModifiersChanged(new_modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_HierarchyChanged => {
|
||||
|
|
@ -1222,6 +1098,55 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
if event_type == self.xkbext.first_event_id {
|
||||
let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) };
|
||||
match xev.xkb_type {
|
||||
ffi::XkbNewKeyboardNotify => {
|
||||
let xev = unsafe {
|
||||
&*(xev as *const _ as *const ffi::XkbNewKeyboardNotifyEvent)
|
||||
};
|
||||
let keycodes_changed_flag = 0x1;
|
||||
let geometry_changed_flag = 0x1 << 1;
|
||||
|
||||
let keycodes_changed =
|
||||
util::has_flag(xev.changed, keycodes_changed_flag);
|
||||
let geometry_changed =
|
||||
util::has_flag(xev.changed, geometry_changed_flag);
|
||||
|
||||
if xev.device == self.kb_state.core_keyboard_id
|
||||
&& (keycodes_changed || geometry_changed)
|
||||
{
|
||||
unsafe { self.kb_state.init_with_x11_keymap() };
|
||||
}
|
||||
}
|
||||
ffi::XkbStateNotify => {
|
||||
let xev =
|
||||
unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) };
|
||||
|
||||
let prev_mods = self.kb_state.mods_state();
|
||||
self.kb_state.update_modifiers(
|
||||
xev.base_mods,
|
||||
xev.latched_mods,
|
||||
xev.locked_mods,
|
||||
xev.base_group as u32,
|
||||
xev.latched_group as u32,
|
||||
xev.locked_group as u32,
|
||||
);
|
||||
let new_mods = self.kb_state.mods_state();
|
||||
if prev_mods != new_mods {
|
||||
if let Some(window) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window),
|
||||
event: WindowEvent::ModifiersChanged(
|
||||
Into::<ModifiersState>::into(new_mods).into(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if event_type == self.randr_event_offset {
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = monitor::invalidate_cached_monitor_list();
|
||||
|
|
@ -1345,14 +1270,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||
wt: &super::EventLoopWindowTarget<T>,
|
||||
window_id: crate::window::WindowId,
|
||||
state: ElementState,
|
||||
mod_keymap: &ModifierKeymap,
|
||||
device_mod_state: &mut ModifierKeyState,
|
||||
kb_state: &mut KbdState,
|
||||
callback: &mut F,
|
||||
) where
|
||||
F: FnMut(Event<'_, T>),
|
||||
{
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
let modifiers = device_mod_state.modifiers();
|
||||
|
||||
// Update modifiers state and emit key events based on which keys are currently pressed.
|
||||
for keycode in wt
|
||||
|
|
@ -1361,29 +1284,13 @@ impl<T: 'static> EventProcessor<T> {
|
|||
.into_iter()
|
||||
.filter(|k| *k >= KEYCODE_OFFSET)
|
||||
{
|
||||
let scancode = (keycode - KEYCODE_OFFSET) as u32;
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) {
|
||||
device_mod_state.key_event(
|
||||
ElementState::Pressed,
|
||||
keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
let keycode = keycode as u32;
|
||||
let event = kb_state.process_key_event(keycode, state, false);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
event,
|
||||
is_synthetic: true,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
mod dnd;
|
||||
mod event_processor;
|
||||
mod events;
|
||||
pub mod ffi;
|
||||
mod ime;
|
||||
mod monitor;
|
||||
|
|
@ -42,8 +41,8 @@ use self::{
|
|||
dnd::{Dnd, DndState},
|
||||
event_processor::EventProcessor,
|
||||
ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender},
|
||||
util::modifiers::ModifierKeymap,
|
||||
};
|
||||
use super::common::xkb_state::KbdState;
|
||||
use crate::{
|
||||
error::OsError as RootOsError,
|
||||
event::{Event, StartCause},
|
||||
|
|
@ -202,6 +201,27 @@ impl<T: 'static> EventLoop<T> {
|
|||
ext
|
||||
};
|
||||
|
||||
let xkbext = {
|
||||
let mut ext = XExtension::default();
|
||||
|
||||
let res = unsafe {
|
||||
(xconn.xlib.XkbQueryExtension)(
|
||||
xconn.display,
|
||||
&mut ext.opcode,
|
||||
&mut ext.first_event_id,
|
||||
&mut ext.first_error_id,
|
||||
&mut 1,
|
||||
&mut 0,
|
||||
)
|
||||
};
|
||||
|
||||
if res == ffi::False {
|
||||
panic!("X server missing XKB extension");
|
||||
}
|
||||
|
||||
ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
|
|
@ -219,9 +239,6 @@ impl<T: 'static> EventLoop<T> {
|
|||
|
||||
xconn.update_cached_wm_info(root);
|
||||
|
||||
let mut mod_keymap = ModifierKeymap::new();
|
||||
mod_keymap.reset_from_x_connection(&xconn);
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let waker = Arc::new(Waker::new(poll.registry(), USER_REDRAW_TOKEN).unwrap());
|
||||
|
||||
|
|
@ -232,6 +249,10 @@ impl<T: 'static> EventLoop<T> {
|
|||
let (user_sender, user_channel) = std::sync::mpsc::channel();
|
||||
let (redraw_sender, redraw_channel) = std::sync::mpsc::channel();
|
||||
|
||||
let kb_state =
|
||||
KbdState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) })
|
||||
.unwrap();
|
||||
|
||||
let window_target = EventLoopWindowTarget {
|
||||
ime,
|
||||
root,
|
||||
|
|
@ -264,8 +285,8 @@ impl<T: 'static> EventLoop<T> {
|
|||
ime_receiver,
|
||||
ime_event_receiver,
|
||||
xi2ext,
|
||||
mod_keymap,
|
||||
device_mod_state: Default::default(),
|
||||
xkbext,
|
||||
kb_state,
|
||||
num_touch: 0,
|
||||
first_touch: None,
|
||||
active_window: None,
|
||||
|
|
@ -279,6 +300,15 @@ impl<T: 'static> EventLoop<T> {
|
|||
.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
|
||||
.queue();
|
||||
|
||||
get_xtarget(&target)
|
||||
.xconn
|
||||
.select_xkb_events(
|
||||
0x100, // Use the "core keyboard device"
|
||||
ffi::XkbNewKeyboardNotifyMask | ffi::XkbStateNotifyMask,
|
||||
)
|
||||
.unwrap()
|
||||
.queue();
|
||||
|
||||
event_processor.init_device(ffi::XIAllDevices);
|
||||
|
||||
EventLoop {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::{slice, str};
|
||||
|
||||
use super::*;
|
||||
use crate::event::ModifiersState;
|
||||
|
||||
pub const VIRTUAL_CORE_POINTER: c_int = 2;
|
||||
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
||||
|
|
@ -11,21 +10,6 @@ pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
|||
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
impl ModifiersState {
|
||||
pub(crate) fn from_x11(state: &ffi::XIModifierState) -> Self {
|
||||
ModifiersState::from_x11_mask(state.effective as c_uint)
|
||||
}
|
||||
|
||||
pub(crate) fn from_x11_mask(mask: c_uint) -> Self {
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::ALT, mask & ffi::Mod1Mask != 0);
|
||||
m.set(ModifiersState::SHIFT, mask & ffi::ShiftMask != 0);
|
||||
m.set(ModifiersState::CTRL, mask & ffi::ControlMask != 0);
|
||||
m.set(ModifiersState::LOGO, mask & ffi::Mod4Mask != 0);
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Some of these fields are not used, but may be of use in the future.
|
||||
pub struct PointerState<'a> {
|
||||
xconn: &'a XConnection,
|
||||
|
|
@ -36,17 +20,10 @@ pub struct PointerState<'a> {
|
|||
pub win_x: c_double,
|
||||
pub win_y: c_double,
|
||||
buttons: ffi::XIButtonState,
|
||||
modifiers: ffi::XIModifierState,
|
||||
pub group: ffi::XIGroupState,
|
||||
pub relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> PointerState<'a> {
|
||||
pub fn get_modifier_state(&self) -> ModifiersState {
|
||||
ModifiersState::from_x11(&self.modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for PointerState<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !self.buttons.mask.is_null() {
|
||||
|
|
@ -81,12 +58,12 @@ impl XConnection {
|
|||
Flusher::new(self)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher<'_>> {
|
||||
let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) };
|
||||
if status == ffi::True {
|
||||
Some(Flusher::new(self))
|
||||
} else {
|
||||
error!("Could not select XKB events: The XKB extension is not initialized!");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +110,6 @@ impl XConnection {
|
|||
win_x,
|
||||
win_y,
|
||||
buttons,
|
||||
modifiers,
|
||||
group,
|
||||
relative_to_window,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{iter::Enumerate, ptr, slice::Iter};
|
||||
use std::{iter::Enumerate, slice::Iter};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -62,20 +62,6 @@ impl Iterator for KeymapIter<'_> {
|
|||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym {
|
||||
unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) }
|
||||
}
|
||||
|
||||
pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym {
|
||||
let mut keysym = 0;
|
||||
|
||||
unsafe {
|
||||
(self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut());
|
||||
}
|
||||
|
||||
keysym
|
||||
}
|
||||
|
||||
pub fn query_keymap(&self) -> Keymap {
|
||||
let mut keys = [0; 32];
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ mod icon;
|
|||
mod input;
|
||||
pub mod keys;
|
||||
mod memory;
|
||||
pub mod modifiers;
|
||||
mod randr;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
|
|
|
|||
|
|
@ -245,7 +245,6 @@ impl UnownedWindow {
|
|||
| ffi::StructureNotifyMask
|
||||
| ffi::VisibilityChangeMask
|
||||
| ffi::KeyPressMask
|
||||
| ffi::KeyReleaseMask
|
||||
| ffi::KeymapStateMask
|
||||
| ffi::ButtonPressMask
|
||||
| ffi::ButtonReleaseMask
|
||||
|
|
@ -448,17 +447,17 @@ impl UnownedWindow {
|
|||
|
||||
// Select XInput2 events
|
||||
let mask = ffi::XI_MotionMask
|
||||
| ffi::XI_ButtonPressMask
|
||||
| ffi::XI_ButtonReleaseMask
|
||||
//| ffi::XI_KeyPressMask
|
||||
//| ffi::XI_KeyReleaseMask
|
||||
| ffi::XI_EnterMask
|
||||
| ffi::XI_LeaveMask
|
||||
| ffi::XI_FocusInMask
|
||||
| ffi::XI_FocusOutMask
|
||||
| ffi::XI_TouchBeginMask
|
||||
| ffi::XI_TouchUpdateMask
|
||||
| ffi::XI_TouchEndMask;
|
||||
| ffi::XI_ButtonPressMask
|
||||
| ffi::XI_ButtonReleaseMask
|
||||
| ffi::XI_KeyPressMask
|
||||
| ffi::XI_KeyReleaseMask
|
||||
| ffi::XI_EnterMask
|
||||
| ffi::XI_LeaveMask
|
||||
| ffi::XI_FocusInMask
|
||||
| ffi::XI_FocusOutMask
|
||||
| ffi::XI_TouchBeginMask
|
||||
| ffi::XI_TouchUpdateMask
|
||||
| ffi::XI_TouchEndMask;
|
||||
xconn
|
||||
.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask)
|
||||
.queue();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue