Skip to content

Commit aeed9b9

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 aeed9b9

File tree

3 files changed

+100
-58
lines changed

3 files changed

+100
-58
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: 80 additions & 56 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,15 @@ fn code_to_key(code: Code, m: Modifiers) -> Key {
3937
Key::Character(base.into())
4038
}
4139
}
40+
fn k(mods: Modifiers, base: &str, code: xkbc::Keycode, state: &xkbc::State) -> Key {
41+
if mods.contains(Modifiers::CONTROL) {
42+
// When ctrl is set, then state.key_get_utf8 return control sequence like \x1e.
43+
// TODO: handle this better?
44+
Key::Character(base.into())
45+
} else {
46+
Key::Character(state.key_get_utf8(code))
47+
}
48+
}
4249
fn n(mods: Modifiers, base: Key, num: &str) -> Key {
4350
if mods.contains(Modifiers::NUM_LOCK) != mods.contains(Modifiers::SHIFT) {
4451
Key::Character(num.into())
@@ -47,55 +54,55 @@ fn code_to_key(code: Code, m: Modifiers) -> Key {
4754
}
4855
}
4956
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"),
57+
Code::KeyA => k(m, "a", hw_code, xkb_state),
58+
Code::KeyB => k(m, "b", hw_code, xkb_state),
59+
Code::KeyC => k(m, "c", hw_code, xkb_state),
60+
Code::KeyD => k(m, "d", hw_code, xkb_state),
61+
Code::KeyE => k(m, "e", hw_code, xkb_state),
62+
Code::KeyF => k(m, "f", hw_code, xkb_state),
63+
Code::KeyG => k(m, "g", hw_code, xkb_state),
64+
Code::KeyH => k(m, "h", hw_code, xkb_state),
65+
Code::KeyI => k(m, "i", hw_code, xkb_state),
66+
Code::KeyJ => k(m, "j", hw_code, xkb_state),
67+
Code::KeyK => k(m, "k", hw_code, xkb_state),
68+
Code::KeyL => k(m, "l", hw_code, xkb_state),
69+
Code::KeyM => k(m, "m", hw_code, xkb_state),
70+
Code::KeyN => k(m, "n", hw_code, xkb_state),
71+
Code::KeyO => k(m, "o", hw_code, xkb_state),
72+
Code::KeyP => k(m, "p", hw_code, xkb_state),
73+
Code::KeyQ => k(m, "q", hw_code, xkb_state),
74+
Code::KeyR => k(m, "r", hw_code, xkb_state),
75+
Code::KeyS => k(m, "s", hw_code, xkb_state),
76+
Code::KeyT => k(m, "t", hw_code, xkb_state),
77+
Code::KeyU => k(m, "u", hw_code, xkb_state),
78+
Code::KeyV => k(m, "v", hw_code, xkb_state),
79+
Code::KeyW => k(m, "w", hw_code, xkb_state),
80+
Code::KeyX => k(m, "x", hw_code, xkb_state),
81+
Code::KeyY => k(m, "y", hw_code, xkb_state),
82+
Code::KeyZ => k(m, "z", hw_code, xkb_state),
7683

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", "("),
84+
Code::Digit0 => k(m, "0", hw_code, xkb_state),
85+
Code::Digit1 => k(m, "1", hw_code, xkb_state),
86+
Code::Digit2 => k(m, "2", hw_code, xkb_state),
87+
Code::Digit3 => k(m, "3", hw_code, xkb_state),
88+
Code::Digit4 => k(m, "4", hw_code, xkb_state),
89+
Code::Digit5 => k(m, "5", hw_code, xkb_state),
90+
Code::Digit6 => k(m, "6", hw_code, xkb_state),
91+
Code::Digit7 => k(m, "7", hw_code, xkb_state),
92+
Code::Digit8 => k(m, "8", hw_code, xkb_state),
93+
Code::Digit9 => k(m, "9", hw_code, xkb_state),
8794

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, "/", "?"),
95+
Code::Backquote => k(m, "`", hw_code, xkb_state),
96+
Code::Minus => k(m, "-", hw_code, xkb_state),
97+
Code::Equal => k(m, "=", hw_code, xkb_state),
98+
Code::BracketLeft => k(m, "[", hw_code, xkb_state),
99+
Code::BracketRight => k(m, "]", hw_code, xkb_state),
100+
Code::Backslash => k(m, "\\", hw_code, xkb_state),
101+
Code::Semicolon => k(m, ";", hw_code, xkb_state),
102+
Code::Quote => k(m, "'", hw_code, xkb_state),
103+
Code::Comma => k(m, ",", hw_code, xkb_state),
104+
Code::Period => k(m, ".", hw_code, xkb_state),
105+
Code::Slash => k(m, "/", hw_code, xkb_state),
99106

100107
Code::Space => a(" "),
101108

@@ -383,22 +390,39 @@ pub(super) fn key_mods(mods: KeyButMask) -> Modifiers {
383390
ret
384391
}
385392

386-
pub(super) fn convert_key_press_event(key_press: &KeyPressEvent) -> KeyboardEvent {
393+
pub(super) fn convert_key_press_event(
394+
key_press: &KeyPressEvent, state: &mut xkbc::State,
395+
) -> KeyboardEvent {
387396
let hw_keycode = key_press.detail;
397+
398+
// Update the xkbc state
399+
let hw_code = hw_keycode.into();
400+
state.update_key(hw_code, xkbc::KeyDirection::Down);
401+
388402
let code = hardware_keycode_to_code(hw_keycode.into());
389403
let modifiers = key_mods(key_press.state);
390-
let key = code_to_key(code, modifiers);
404+
let key = code_to_key(code, modifiers, hw_code, state);
391405
let location = code_to_location(code);
392406
let state = KeyState::Down;
393407

394-
KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false }
408+
let ev =
409+
KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false };
410+
println!("GOT: {:?}", ev);
411+
ev
395412
}
396413

397-
pub(super) fn convert_key_release_event(key_release: &KeyReleaseEvent) -> KeyboardEvent {
414+
pub(super) fn convert_key_release_event(
415+
key_release: &KeyReleaseEvent, state: &mut xkbc::State,
416+
) -> KeyboardEvent {
398417
let hw_keycode = key_release.detail;
418+
419+
// Update the xkbc state
420+
let hw_code = hw_keycode.into();
421+
state.update_key(hw_code, xkbc::KeyDirection::Up);
422+
399423
let code = hardware_keycode_to_code(hw_keycode.into());
400424
let modifiers = key_mods(key_release.state);
401-
let key = code_to_key(code, modifiers);
425+
let key = code_to_key(code, modifiers, hw_code, state);
402426
let location = code_to_location(code);
403427
let state = KeyState::Up;
404428

0 commit comments

Comments
 (0)