Skip to content

Commit 7e56fda

Browse files
Use the user's keyboard layout from the x11 system
This change uses xkbcommon to decode the key utf8 value based on the user's current layout. The implementation is adapted from the x11rb's xkbcommon-example.
1 parent 3724a00 commit 7e56fda

File tree

3 files changed

+88
-57
lines changed

3 files changed

+88
-57
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ raw-window-handle = "0.5"
2424

2525
[target.'cfg(target_os="linux")'.dependencies]
2626
x11rb = { version = "0.13.0", features = ["cursor", "resource_manager", "allow-unsafe-code"] }
27+
xkbcommon = { version = "0.7", features = ["x11"]}
2728
x11 = { version = "2.21", features = ["xlib", "xlib_xcb"] }
2829
nix = "0.22.0"
2930

src/x11/event_loop.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::os::fd::AsRawFd;
99
use std::time::{Duration, Instant};
1010
use x11rb::connection::Connection;
1111
use x11rb::protocol::Event as XEvent;
12+
use xkbcommon::xkb as xkbc;
1213

1314
pub(super) struct EventLoop {
1415
handler: Box<dyn WindowHandler>,
@@ -18,20 +19,36 @@ pub(super) struct EventLoop {
1819
new_physical_size: Option<PhySize>,
1920
frame_interval: Duration,
2021
event_loop_running: bool,
22+
xkb_state: xkbc::State,
2123
}
2224

2325
impl EventLoop {
2426
pub fn new(
2527
window: WindowInner, handler: impl WindowHandler + 'static,
2628
parent_handle: Option<ParentHandle>,
2729
) -> Self {
30+
// Setup the xkb state
31+
let xkb_state = {
32+
let context = xkbc::Context::new(xkbc::CONTEXT_NO_FLAGS);
33+
let conn = &window.xcb_connection.conn;
34+
let device_id = xkbc::x11::get_core_keyboard_device_id(conn);
35+
assert!(device_id >= 0);
36+
let keymap = xkbc::x11::keymap_new_from_device(
37+
&context,
38+
conn,
39+
device_id,
40+
xkbc::KEYMAP_COMPILE_NO_FLAGS,
41+
);
42+
xkbc::x11::state_new_from_device(&keymap, &conn, device_id)
43+
};
2844
Self {
2945
window,
3046
handler: Box::new(handler),
3147
parent_handle,
3248
frame_interval: Duration::from_millis(15),
3349
event_loop_running: false,
3450
new_physical_size: None,
51+
xkb_state,
3552
}
3653
}
3754

@@ -261,14 +278,14 @@ impl EventLoop {
261278
XEvent::KeyPress(event) => {
262279
self.handler.on_event(
263280
&mut crate::Window::new(Window { inner: &self.window }),
264-
Event::Keyboard(convert_key_press_event(&event)),
281+
Event::Keyboard(convert_key_press_event(&event, &mut self.xkb_state)),
265282
);
266283
}
267284

268285
XEvent::KeyRelease(event) => {
269286
self.handler.on_event(
270287
&mut crate::Window::new(Window { inner: &self.window }),
271-
Event::Keyboard(convert_key_release_event(&event)),
288+
Event::Keyboard(convert_key_release_event(&event, &mut self.xkb_state)),
272289
);
273290
}
274291

src/x11/keyboard.rs

Lines changed: 68 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,14 @@
1919
//! X11 keyboard handling
2020
2121
use x11rb::protocol::xproto::{KeyButMask, KeyPressEvent, KeyReleaseEvent};
22+
use xkbcommon::xkb as xkbc;
2223

2324
use keyboard_types::*;
2425

2526
use crate::keyboard::code_to_location;
2627

2728
/// Convert a hardware scan code to a key.
28-
///
29-
/// Note: this is a hardcoded layout. We need to detect the user's
30-
/// layout from the system and apply it.
31-
fn code_to_key(code: Code, m: Modifiers) -> Key {
29+
fn code_to_key(code: Code, m: Modifiers, hw_code: xkbc::Keycode, xkb_state: &xkbc::State) -> Key {
3230
fn a(s: &str) -> Key {
3331
Key::Character(s.into())
3432
}
@@ -39,6 +37,7 @@ fn code_to_key(code: Code, m: Modifiers) -> Key {
3937
Key::Character(base.into())
4038
}
4139
}
40+
let k = || Key::Character(xkb_state.key_get_utf8(hw_code));
4241
fn n(mods: Modifiers, base: Key, num: &str) -> Key {
4342
if mods.contains(Modifiers::NUM_LOCK) != mods.contains(Modifiers::SHIFT) {
4443
Key::Character(num.into())
@@ -47,55 +46,55 @@ fn code_to_key(code: Code, m: Modifiers) -> Key {
4746
}
4847
}
4948
match code {
50-
Code::KeyA => s(m, "a", "A"),
51-
Code::KeyB => s(m, "b", "B"),
52-
Code::KeyC => s(m, "c", "C"),
53-
Code::KeyD => s(m, "d", "D"),
54-
Code::KeyE => s(m, "e", "E"),
55-
Code::KeyF => s(m, "f", "F"),
56-
Code::KeyG => s(m, "g", "G"),
57-
Code::KeyH => s(m, "h", "H"),
58-
Code::KeyI => s(m, "i", "I"),
59-
Code::KeyJ => s(m, "j", "J"),
60-
Code::KeyK => s(m, "k", "K"),
61-
Code::KeyL => s(m, "l", "L"),
62-
Code::KeyM => s(m, "m", "M"),
63-
Code::KeyN => s(m, "n", "N"),
64-
Code::KeyO => s(m, "o", "O"),
65-
Code::KeyP => s(m, "p", "P"),
66-
Code::KeyQ => s(m, "q", "Q"),
67-
Code::KeyR => s(m, "r", "R"),
68-
Code::KeyS => s(m, "s", "S"),
69-
Code::KeyT => s(m, "t", "T"),
70-
Code::KeyU => s(m, "u", "U"),
71-
Code::KeyV => s(m, "v", "V"),
72-
Code::KeyW => s(m, "w", "W"),
73-
Code::KeyX => s(m, "x", "X"),
74-
Code::KeyY => s(m, "y", "Y"),
75-
Code::KeyZ => s(m, "z", "Z"),
49+
Code::KeyA => k(),
50+
Code::KeyB => k(),
51+
Code::KeyC => k(),
52+
Code::KeyD => k(),
53+
Code::KeyE => k(),
54+
Code::KeyF => k(),
55+
Code::KeyG => k(),
56+
Code::KeyH => k(),
57+
Code::KeyI => k(),
58+
Code::KeyJ => k(),
59+
Code::KeyK => k(),
60+
Code::KeyL => k(),
61+
Code::KeyM => k(),
62+
Code::KeyN => k(),
63+
Code::KeyO => k(),
64+
Code::KeyP => k(),
65+
Code::KeyQ => k(),
66+
Code::KeyR => k(),
67+
Code::KeyS => k(),
68+
Code::KeyT => k(),
69+
Code::KeyU => k(),
70+
Code::KeyV => k(),
71+
Code::KeyW => k(),
72+
Code::KeyX => k(),
73+
Code::KeyY => k(),
74+
Code::KeyZ => k(),
7675

77-
Code::Digit0 => s(m, "0", ")"),
78-
Code::Digit1 => s(m, "1", "!"),
79-
Code::Digit2 => s(m, "2", "@"),
80-
Code::Digit3 => s(m, "3", "#"),
81-
Code::Digit4 => s(m, "4", "$"),
82-
Code::Digit5 => s(m, "5", "%"),
83-
Code::Digit6 => s(m, "6", "^"),
84-
Code::Digit7 => s(m, "7", "&"),
85-
Code::Digit8 => s(m, "8", "*"),
86-
Code::Digit9 => s(m, "9", "("),
76+
Code::Digit0 => k(),
77+
Code::Digit1 => k(),
78+
Code::Digit2 => k(),
79+
Code::Digit3 => k(),
80+
Code::Digit4 => k(),
81+
Code::Digit5 => k(),
82+
Code::Digit6 => k(),
83+
Code::Digit7 => k(),
84+
Code::Digit8 => k(),
85+
Code::Digit9 => k(),
8786

88-
Code::Backquote => s(m, "`", "~"),
89-
Code::Minus => s(m, "-", "_"),
90-
Code::Equal => s(m, "=", "+"),
91-
Code::BracketLeft => s(m, "[", "{"),
92-
Code::BracketRight => s(m, "]", "}"),
93-
Code::Backslash => s(m, "\\", "|"),
94-
Code::Semicolon => s(m, ";", ":"),
95-
Code::Quote => s(m, "'", "\""),
96-
Code::Comma => s(m, ",", "<"),
97-
Code::Period => s(m, ".", ">"),
98-
Code::Slash => s(m, "/", "?"),
87+
Code::Backquote => k(),
88+
Code::Minus => k(),
89+
Code::Equal => k(),
90+
Code::BracketLeft => k(),
91+
Code::BracketRight => k(),
92+
Code::Backslash => k(),
93+
Code::Semicolon => k(),
94+
Code::Quote => k(),
95+
Code::Comma => k(),
96+
Code::Period => k(),
97+
Code::Slash => k(),
9998

10099
Code::Space => a(" "),
101100

@@ -383,22 +382,36 @@ pub(super) fn key_mods(mods: KeyButMask) -> Modifiers {
383382
ret
384383
}
385384

386-
pub(super) fn convert_key_press_event(key_press: &KeyPressEvent) -> KeyboardEvent {
385+
pub(super) fn convert_key_press_event(
386+
key_press: &KeyPressEvent, state: &mut xkbc::State,
387+
) -> KeyboardEvent {
387388
let hw_keycode = key_press.detail;
389+
390+
// Update the xkbc state
391+
let hw_code = hw_keycode.into();
392+
state.update_key(hw_code, xkbc::KeyDirection::Down);
393+
388394
let code = hardware_keycode_to_code(hw_keycode.into());
389395
let modifiers = key_mods(key_press.state);
390-
let key = code_to_key(code, modifiers);
396+
let key = code_to_key(code, modifiers, hw_code, state);
391397
let location = code_to_location(code);
392398
let state = KeyState::Down;
393399

394400
KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false }
395401
}
396402

397-
pub(super) fn convert_key_release_event(key_release: &KeyReleaseEvent) -> KeyboardEvent {
403+
pub(super) fn convert_key_release_event(
404+
key_release: &KeyReleaseEvent, state: &mut xkbc::State,
405+
) -> KeyboardEvent {
398406
let hw_keycode = key_release.detail;
407+
408+
// Update the xkbc state
409+
let hw_code = hw_keycode.into();
410+
state.update_key(hw_code, xkbc::KeyDirection::Up);
411+
399412
let code = hardware_keycode_to_code(hw_keycode.into());
400413
let modifiers = key_mods(key_release.state);
401-
let key = code_to_key(code, modifiers);
414+
let key = code_to_key(code, modifiers, hw_code, state);
402415
let location = code_to_location(code);
403416
let state = KeyState::Up;
404417

0 commit comments

Comments
 (0)