From 3f70aa690945be64b0d64d131bc0184adb54cf5c Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 15 Feb 2024 00:23:48 -0800 Subject: [PATCH] x11: basic keyboard input --- Cargo.lock | 19 +++++++ crates/gpui/Cargo.toml | 4 +- crates/gpui/src/platform/linux/platform.rs | 18 +++++-- crates/gpui/src/platform/linux/x11/client.rs | 55 +++++++++++++++++++- crates/gpui/src/platform/linux/x11/window.rs | 16 ++++-- script/linux | 3 ++ 6 files changed, 103 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7297c76088b19..cdab9f60a1289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3528,6 +3528,7 @@ dependencies = [ "wayland-client", "wayland-protocols", "xcb", + "xkbcommon", ] [[package]] @@ -10840,6 +10841,24 @@ dependencies = [ "quick-xml 0.30.0", ] +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "as-raw-xcb-connection", + "libc", + "memmap2 0.8.0", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + [[package]] name = "xmlparser" version = "0.13.5" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 8e33878a9e11c..7679761291b5f 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -96,9 +96,8 @@ objc = "0.2" [target.'cfg(target_os = "linux")'.dependencies] flume = "0.11" - # todo!(linux) - Technically do not use `randr`, but it doesn't compile otherwise -xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr"] } +xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr", "xkb"] } wayland-client= { version = "0.31.2" } wayland-protocols = { version = "0.31.2", features = ["client"] } wayland-backend = { version = "0.3.3", features = ["client_system"] } @@ -108,3 +107,4 @@ blade-graphics = { git = "https://github.com/kvark/blade", rev = "c4f951a88b3457 blade-macros = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" } bytemuck = "1" cosmic-text = "0.10.0" +xkbcommon = { version = "0.7", features = ["x11"] } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 3da95555d749b..adcbac5487cb3 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -115,9 +115,21 @@ impl LinuxPlatform { callbacks: Mutex, state: Mutex, ) -> Self { - let (xcb_connection, x_root_index) = - xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Present], &[]) - .unwrap(); + let (xcb_connection, x_root_index) = xcb::Connection::connect_with_extensions( + None, + &[xcb::Extension::Present, xcb::Extension::Xkb], + &[], + ) + .unwrap(); + + let xkb_ver = xcb_connection + .wait_for_reply(xcb_connection.send_request(&xcb::xkb::UseExtension { + wanted_major: xcb::xkb::MAJOR_VERSION as u16, + wanted_minor: xcb::xkb::MINOR_VERSION as u16, + })) + .unwrap(); + assert!(xkb_ver.supported()); + let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap(); let xcb_connection = Arc::new(xcb_connection); let client_dispatcher: Arc = diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 048a24fe40475..09f045a619354 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -2,6 +2,7 @@ use std::{rc::Rc, sync::Arc}; use parking_lot::Mutex; use xcb::{x, Xid as _}; +use xkbcommon::xkb; use collections::HashMap; @@ -15,6 +16,7 @@ use crate::{ pub(crate) struct X11ClientState { pub(crate) windows: HashMap>, + xkb: xkbcommon::xkb::State, } pub(crate) struct X11Client { @@ -32,6 +34,17 @@ impl X11Client { x_root_index: i32, atoms: XcbAtoms, ) -> Self { + let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + let xkb_device_id = xkb::x11::get_core_keyboard_device_id(&xcb_connection); + let xkb_keymap = xkb::x11::keymap_new_from_device( + &xkb_context, + &xcb_connection, + xkb_device_id, + xkb::KEYMAP_COMPILE_NO_FLAGS, + ); + let xkb_state = + xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id); + Self { platform_inner: inner, xcb_connection, @@ -39,6 +52,7 @@ impl X11Client { atoms, state: Mutex::new(X11ClientState { windows: HashMap::default(), + xkb: xkb_state, }), } } @@ -92,6 +106,43 @@ impl Client for X11Client { window.request_refresh(); } xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {} + xcb::Event::X(x::Event::KeyPress(ev)) => { + let window = self.get_window(ev.event()); + let modifiers = super::modifiers_from_state(ev.state()); + let key = { + let code = ev.detail().into(); + let mut state = self.state.lock(); + let key = state.xkb.key_get_utf8(code); + state.xkb.update_key(code, xkb::KeyDirection::Down); + key + }; + window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent { + keystroke: crate::Keystroke { + modifiers, + key, + ime_key: None, + }, + is_held: false, + })); + } + xcb::Event::X(x::Event::KeyRelease(ev)) => { + let window = self.get_window(ev.event()); + let modifiers = super::modifiers_from_state(ev.state()); + let key = { + let code = ev.detail().into(); + let mut state = self.state.lock(); + let key = state.xkb.key_get_utf8(code); + state.xkb.update_key(code, xkb::KeyDirection::Up); + key + }; + window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { + keystroke: crate::Keystroke { + modifiers, + key, + ime_key: None, + }, + })); + } xcb::Event::X(x::Event::ButtonPress(ev)) => { let window = self.get_window(ev.event()); let modifiers = super::modifiers_from_state(ev.state()); @@ -103,7 +154,7 @@ impl Client for X11Client { position, modifiers, click_count: 1, - })) + })); } else { log::warn!("Unknown button press: {ev:?}"); } @@ -119,7 +170,7 @@ impl Client for X11Client { position, modifiers, click_count: 1, - })) + })); } } _ => {} diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 86d90b9c6cded..bfd13fb363799 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -162,8 +162,7 @@ impl X11WindowState { | x::EventMask::BUTTON3_MOTION | x::EventMask::BUTTON4_MOTION | x::EventMask::BUTTON5_MOTION - | x::EventMask::BUTTON_MOTION - | x::EventMask::KEYMAP_STATE, + | x::EventMask::BUTTON_MOTION, ), ]; @@ -327,9 +326,16 @@ impl X11WindowState { } pub fn handle_input(&self, input: PlatformInput) { - let mut callbacks = self.callbacks.lock(); - if let Some(ref mut fun) = callbacks.input { - fun(input); + if let Some(ref mut fun) = self.callbacks.lock().input { + if fun(input.clone()) { + return; + } + } + if let PlatformInput::KeyDown(event) = input { + let mut inner = self.inner.lock(); + if let Some(ref mut input_handler) = inner.input_handler { + input_handler.replace_text_in_range(None, &event.keystroke.key); + } } } } diff --git a/script/linux b/script/linux index b98f4b7d3ee0d..b55d60217a825 100755 --- a/script/linux +++ b/script/linux @@ -12,6 +12,7 @@ if [[ -n $apt ]]; then libfontconfig-dev vulkan-validationlayers* libwayland-dev + libxkbcommon-x11-dev ) $maysudo "$apt" install -y "${deps[@]}" exit 0 @@ -26,6 +27,7 @@ if [[ -n $dnf ]]; then fontconfig-devel vulkan-validation-layers wayland-devel + libxkbcommon-x11-devel ) $maysudo "$dnf" install -y "${deps[@]}" exit 0 @@ -40,6 +42,7 @@ if [[ -n $pacman ]]; then fontconfig vulkan-validation-layers wayland + libxkbcommon-x11 ) $maysudo "$pacman" -S --needed --noconfirm "${deps[@]}" exit 0