diff --git a/Cargo.toml b/Cargo.toml index d2f3c9026bc..cc1654f6bca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,11 +93,11 @@ wayland-client = { version = "0.28", features = [ "dlopen"] , optional = true } sctk = { package = "smithay-client-toolkit", version = "0.12.3", optional = true } mio = { version = "0.7", features = ["os-ext"], optional = true } mio-misc = { version = "1.0", optional = true } -x11-dl = { version = "2.18.5", optional = true } +x11-dl = { version = "2.19.1", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } memmap2 = { version = "0.2.1", optional = true } -xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "900832888ad6f11011d1369befb344a9aa8a9610" } +xkbcommon-dl = { git = "https://github.com/mahkoh/xkbcommon-dl", rev = "3f92ba445a590a2928942fd1ce24fa6965f76bca" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index 8e2e6c2458f..51083d6d386 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -826,6 +826,57 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { keysyms::XKB_KEY_SunVideoRaiseBrightness => Key::BrightnessUp, // XKB_KEY_SunPowerSwitchShift // + // Dead keys + keysyms::XKB_KEY_dead_greek => Key::Dead(None), + keysyms::XKB_KEY_dead_currency => Key::Dead(None), + keysyms::XKB_KEY_dead_stroke => Key::Dead(None), + keysyms::XKB_KEY_dead_voiced_sound => Key::Dead(None), + keysyms::XKB_KEY_dead_semivoiced_sound => Key::Dead(None), + keysyms::XKB_KEY_dead_lowline => Key::Dead(None), + keysyms::XKB_KEY_dead_aboveverticalline => Key::Dead(None), + keysyms::XKB_KEY_dead_belowverticalline => Key::Dead(None), + keysyms::XKB_KEY_dead_longsolidusoverlay => Key::Dead(None), + keysyms::XKB_KEY_dead_grave => Key::Dead(Some('`')), + keysyms::XKB_KEY_dead_acute => Key::Dead(Some('´')), + keysyms::XKB_KEY_dead_circumflex => Key::Dead(Some('^')), + keysyms::XKB_KEY_dead_tilde => Key::Dead(Some('~')), + keysyms::XKB_KEY_dead_macron => Key::Dead(Some('¯')), + keysyms::XKB_KEY_dead_breve => Key::Dead(Some('˘')), + keysyms::XKB_KEY_dead_abovedot => Key::Dead(Some('˙')), + keysyms::XKB_KEY_dead_diaeresis => Key::Dead(Some('¨')), + keysyms::XKB_KEY_dead_abovering => Key::Dead(Some('°')), + keysyms::XKB_KEY_dead_doubleacute => Key::Dead(Some('˝')), + keysyms::XKB_KEY_dead_caron => Key::Dead(Some('ˇ')), + keysyms::XKB_KEY_dead_cedilla => Key::Dead(Some('¸')), + keysyms::XKB_KEY_dead_ogonek => Key::Dead(Some('˛')), + keysyms::XKB_KEY_dead_iota => Key::Dead(Some('ͺ')), + keysyms::XKB_KEY_dead_belowdot => Key::Dead(None), + keysyms::XKB_KEY_dead_hook => Key::Dead(None), + keysyms::XKB_KEY_dead_horn => Key::Dead(None), + keysyms::XKB_KEY_dead_abovecomma => Key::Dead(None), + keysyms::XKB_KEY_dead_abovereversedcomma => Key::Dead(None), + keysyms::XKB_KEY_dead_doublegrave => Key::Dead(None), + keysyms::XKB_KEY_dead_belowring => Key::Dead(Some('˳')), + keysyms::XKB_KEY_dead_belowmacron => Key::Dead(Some('ˍ')), + keysyms::XKB_KEY_dead_belowcircumflex => Key::Dead(None), + keysyms::XKB_KEY_dead_belowtilde => Key::Dead(Some('˷')), + keysyms::XKB_KEY_dead_belowbreve => Key::Dead(None), + keysyms::XKB_KEY_dead_belowdiaeresis => Key::Dead(None), + keysyms::XKB_KEY_dead_invertedbreve => Key::Dead(None), + keysyms::XKB_KEY_dead_belowcomma => Key::Dead(None), + keysyms::XKB_KEY_dead_a => Key::Dead(Some('a')), + keysyms::XKB_KEY_dead_A => Key::Dead(Some('A')), + keysyms::XKB_KEY_dead_e => Key::Dead(Some('e')), + keysyms::XKB_KEY_dead_E => Key::Dead(Some('E')), + keysyms::XKB_KEY_dead_i => Key::Dead(Some('i')), + keysyms::XKB_KEY_dead_I => Key::Dead(Some('I')), + keysyms::XKB_KEY_dead_o => Key::Dead(Some('o')), + keysyms::XKB_KEY_dead_O => Key::Dead(Some('O')), + keysyms::XKB_KEY_dead_u => Key::Dead(Some('u')), + keysyms::XKB_KEY_dead_U => Key::Dead(Some('U')), + keysyms::XKB_KEY_dead_small_schwa => Key::Dead(Some('ə')), + keysyms::XKB_KEY_dead_capital_schwa => Key::Dead(Some('Ə')), + 0 => Key::Unidentified(NativeKeyCode::Unidentified), _ => Key::Unidentified(NativeKeyCode::XkbSym(keysym)), } diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index b1ff556db40..affbec9a813 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -1,12 +1,9 @@ -use std::convert::TryInto; -use std::env; use std::ffi::CString; use std::fs::File; -use std::os::raw::c_char; +use std::os::raw::{c_char, c_int}; use std::os::unix::ffi::OsStringExt; -use std::ptr; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Mutex; +use std::{char, env, ptr, slice, str}; #[cfg(feature = "wayland")] use memmap2::MmapOptions; @@ -19,20 +16,15 @@ use x11_dl::xlib_xcb::xcb_connection_t; use xkbcommon_dl::x11::XKBCOMMON_X11_HANDLE as XKBXH; use xkbcommon_dl::{ - self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH, + self as ffi, xkb_compose_status, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, + XKBCOMMON_HANDLE as XKBH, }; use crate::{ event::ElementState, - keyboard::{Key, KeyCode, KeyLocation}, + keyboard::{Key, KeyCode, KeyLocation, ModifiersState}, }; -// TODO: Wire this up without using a static `Mutex>`. -#[cfg(feature = "x11")] -lazy_static! { - pub(crate) static ref X11_EVPROC_NEXT_COMPOSE: Mutex> = Mutex::new(None); -} - // TODO: Wire this up without using a static `AtomicBool`. static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); @@ -49,89 +41,82 @@ pub(crate) struct KbState { 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, + mod_indices: ModIndices, mods_state: ModifiersState, locked: bool, scratch_buffer: Vec, } -/// 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)] -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, +#[derive(Default)] +struct ModIndices { + ctrl: u32, + alt: u32, + shift: u32, + logo: u32, } -impl ModifiersState { - fn new() -> Self { - Self::default() +impl ModIndices { + unsafe fn from_keymap(xkb_keymap: *mut ffi::xkb_keymap) -> Self { + let ctrl = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char, + ); + let alt = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char, + ); + let shift = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char, + ); + let logo = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char, + ); + Self { + ctrl, + alt, + shift, + logo, + } } +} - fn update_with(&mut self, state: *mut ffi::xkb_state) { - self.ctrl = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.alt = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.shift = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.caps_lock = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_CAPS.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.logo = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.num_lock = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_NUM.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - } +unsafe fn xkb_state_to_modifiers( + state: *mut ffi::xkb_state, + indices: &ModIndices, +) -> ModifiersState { + let ctrl = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.ctrl, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + let alt = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.alt, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + let shift = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.shift, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + let logo = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.logo, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + + let mut mods = ModifiersState::empty(); + mods.set(ModifiersState::SHIFT, shift); + mods.set(ModifiersState::CONTROL, ctrl); + mods.set(ModifiersState::ALT, alt); + mods.set(ModifiersState::SUPER, logo); + mods } impl KbState { - pub(crate) fn update_modifiers( + pub(crate) fn update_state( &mut self, mods_depressed: u32, mods_latched: u32, @@ -156,7 +141,7 @@ impl KbState { }; 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); + self.mods_state = unsafe { xkb_state_to_modifiers(self.xkb_state, &self.mod_indices) }; } } @@ -171,105 +156,51 @@ impl KbState { if !self.ready() { return None; } - let size = - unsafe { (XKBH.xkb_state_key_get_utf8)(self.xkb_state, keycode, ptr::null_mut(), 0) } - + 1; - if size <= 1 { - return None; - }; - self.scratch_buffer.clear(); - let size = size.try_into().unwrap(); - self.scratch_buffer.reserve(size); - unsafe { - self.scratch_buffer.set_len(size); - (XKBH.xkb_state_key_get_utf8)( - self.xkb_state, - keycode, - self.scratch_buffer.as_mut_ptr() as *mut _, - size, - ); - }; - // remove the final `\0` - self.scratch_buffer.pop(); - Some(byte_slice_to_cached_string(&self.scratch_buffer)) - } - - fn compose_feed_normal(&mut self, keysym: u32) -> Option { - self.compose_feed(self.xkb_compose_state, keysym) - } - - fn compose_feed_2(&mut self, keysym: u32) -> Option { - self.compose_feed(self.xkb_compose_state_2, keysym) + let utf32 = unsafe { (XKBH.xkb_state_key_get_utf32)(self.xkb_state, keycode) }; + char_to_str(utf32) } - fn compose_feed( - &mut self, - xkb_compose_state: *mut ffi::xkb_compose_state, - keysym: u32, - ) -> Option { + fn compose_feed(&mut self, keysym: u32) -> Option { 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 { - self.compose_status(self.xkb_compose_state) - } - - #[allow(dead_code)] - fn compose_status_2(&mut self) -> Option { - self.compose_status(self.xkb_compose_state_2) + Some(unsafe { (XKBCH.xkb_compose_state_feed)(self.xkb_compose_state, keysym) }) } - fn compose_status( - &mut self, - xkb_compose_state: *mut ffi::xkb_compose_state, - ) -> Option { - if !self.ready() || xkb_compose_state.is_null() { + fn compose_status(&mut self) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { return None; } - Some(unsafe { (XKBCH.xkb_compose_state_get_status)(xkb_compose_state) }) + Some(unsafe { (XKBCH.xkb_compose_state_get_status)(self.xkb_compose_state) }) } - fn compose_get_utf8_normal(&mut self) -> Option<&'static str> { - self.compose_get_utf8(self.xkb_compose_state) - } - - fn compose_get_utf8_2(&mut self) -> Option<&'static str> { - self.compose_get_utf8(self.xkb_compose_state_2) - } - - fn compose_get_utf8( - &mut self, - xkb_compose_state: *mut ffi::xkb_compose_state, - ) -> Option<&'static str> { - if !self.ready() || xkb_compose_state.is_null() { + fn compose_get_utf8(&mut self) -> Option<&'static str> { + if !self.ready() || self.xkb_compose_state.is_null() { return None; } - let size = - unsafe { (XKBCH.xkb_compose_state_get_utf8)(xkb_compose_state, ptr::null_mut(), 0) } - + 1; - if size <= 1 { - return None; - }; - self.scratch_buffer.clear(); - let size = size.try_into().unwrap(); - self.scratch_buffer.reserve(size); - unsafe { - self.scratch_buffer.set_len(size); - (XKBCH.xkb_compose_state_get_utf8)( - xkb_compose_state, - self.scratch_buffer.as_mut_ptr() as *mut _, - size as usize, - ); - }; - // remove the final `\0` - self.scratch_buffer.pop(); - Some(byte_slice_to_cached_string(&self.scratch_buffer)) + self.scratch_buffer.truncate(0); + loop { + unsafe { + let size = (XKBCH.xkb_compose_state_get_utf8)( + self.xkb_compose_state, + self.scratch_buffer.as_mut_ptr() as *mut _, + self.scratch_buffer.capacity(), + ); + if size < 0 { + return None; + } + let size = size as usize; + if size >= self.scratch_buffer.capacity() { + self.scratch_buffer.reserve(size + 1); + continue; + } + self.scratch_buffer.set_len(size); + return Some(byte_slice_to_cached_string(&self.scratch_buffer)); + } + } } pub(crate) fn new() -> Result { @@ -283,6 +214,21 @@ impl KbState { return Err(Error::XKBNotFound); } + // let level = if log::log_enabled!(log::Level::Debug) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_DEBUG + // } else if log::log_enabled!(log::Level::Info) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_INFO + // } else if log::log_enabled!(log::Level::Warn) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_WARNING + // } else if log::log_enabled!(log::Level::Error) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_ERROR + // } else { + // ffi::xkb_log_level::XKB_LOG_LEVEL_CRITICAL + // }; + // unsafe { + // (XKBH.xkb_context_set_log_level)(context, level); + // } + let mut me = Self { #[cfg(feature = "x11")] xcb_connection: ptr::null_mut(), @@ -291,10 +237,10 @@ impl KbState { 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(), + mod_indices: Default::default(), + mods_state: ModifiersState::empty(), locked: false, - scratch_buffer: Vec::new(), + scratch_buffer: Vec::with_capacity(5), }; unsafe { me.init_compose() }; @@ -305,25 +251,14 @@ impl KbState { impl KbState { #[cfg(feature = "x11")] - pub(crate) fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result { + pub(crate) fn from_x11_xkb( + connection: *mut xcb_connection_t, + device_id: c_int, + ) -> Result { 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() }; + unsafe { me.init_with_x11_keymap(device_id) }; Ok(me) } @@ -385,33 +320,21 @@ impl KbState { ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, ); - if compose_table.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() { + if compose_state.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.mod_indices = ModIndices::from_keymap(keymap); self.xkb_state = state; - self.mods_state.update_with(state); + self.mods_state = xkb_state_to_modifiers(state, &self.mod_indices); } unsafe fn de_init(&mut self) { @@ -422,23 +345,20 @@ impl KbState { } #[cfg(feature = "x11")] - pub(crate) unsafe fn init_with_x11_keymap(&mut self) { + pub(crate) unsafe fn init_with_x11_keymap(&mut self, device_id: c_int) { if !self.xkb_keymap.is_null() { self.de_init(); } - // TODO: Support keyboards other than the "virtual core keyboard device". - let 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, - core_keyboard_id, + device_id, xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, ); assert_ne!(keymap, ptr::null_mut()); - let state = - (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, core_keyboard_id); + let state = (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, device_id); self.post_init(state, keymap); } @@ -521,9 +441,6 @@ impl Drop for KbState { 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); } @@ -547,207 +464,172 @@ pub enum Error { } impl KbState { - pub fn process_key_event(&mut self, keycode: u32, state: ElementState) -> KeyEventResults<'_> { - KeyEventResults::new(self, keycode, state == ElementState::Pressed) - } - - pub fn process_key_repeat_event(&mut self, keycode: u32) -> KeyEventResults<'_> { - KeyEventResults::new(self, keycode, false) - } - - fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option<&'static str> { - 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 final `\0` - self.scratch_buffer.pop(); - Some(byte_slice_to_cached_string(&self.scratch_buffer)) - } -} - -#[derive(Copy, Clone, Debug)] -enum XkbCompose { - Accepted(ffi::xkb_compose_status), - Ignored, - Uninitialized, -} - -pub(crate) struct KeyEventResults<'a> { - state: &'a mut KbState, - keycode: u32, - keysym: u32, - compose: Option, -} + pub fn process_key_event( + &mut self, + keycode: u32, + group: u32, + state: ElementState, + ) -> KeyEventResults { + let keysym = self.get_one_sym_raw(keycode); -impl<'a> KeyEventResults<'a> { - fn new(state: &'a mut KbState, keycode: u32, compose: bool) -> Self { - let keysym = state.get_one_sym_raw(keycode); + let (text, text_with_all_modifiers); - let compose = if compose { - Some(match state.compose_feed_normal(keysym) { + if state == ElementState::Pressed { + // This is a press or repeat event. Feed the keysym to the compose engine. + match self.compose_feed(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()) + // The keysym potentially affected the compose state. Check it. + match self.compose_status().unwrap() { + xkb_compose_status::XKB_COMPOSE_NOTHING => { + // There is no ongoing composing. Use the keysym on its own. + text = keysym_to_utf8_raw(keysym); + text_with_all_modifiers = self.get_utf8_raw(keycode); + } + xkb_compose_status::XKB_COMPOSE_COMPOSING => { + // Composing is ongoing and not yet completed. No text is produced. + text = None; + text_with_all_modifiers = None; + } + xkb_compose_status::XKB_COMPOSE_COMPOSED => { + // This keysym completed the sequence. The text is the result. + text = self.compose_get_utf8(); + // 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 opposite behaviour. + text_with_all_modifiers = text; + } + xkb_compose_status::XKB_COMPOSE_CANCELLED => { + // Before this keysym, composing was ongoing. This keysym was not a possible + // continuation of the sequence and thus aborted composing. The standard + // behavior on linux in this case is to ignore both the sequence and this keysym. + text = None; + text_with_all_modifiers = None; + } + } } - Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => XkbCompose::Ignored, - None => XkbCompose::Uninitialized, - }) + Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => { + // This keysym is a modifier and thus has no effect on the engine. Nor does it produce + // text. + text = None; + text_with_all_modifiers = None; + } + _ => { + // The compose engine is disabled. Use the keysym on its own. + text = keysym_to_utf8_raw(keysym); + text_with_all_modifiers = self.get_utf8_raw(keycode); + } + } } else { - None + // This is a key release. No text is produced. + text = None; + text_with_all_modifiers = None; + } + + let key_without_modifiers = { + // 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.xkb_keymap, + keycode, + group, + 0, + &mut keysyms, + ) + }; + let keysym = if keysym_count == 1 { + unsafe { *keysyms } + } else { + 0 + }; + keysym_to_key(keysym) }; - // let key_text = state.keysym_to_utf8_raw(keysym); - // unsafe { - // let layout_id = (XKBH.xkb_state_serialize_layout)(state.xkb_state, xkb_state_component::XKB_STATE_LAYOUT_EFFECTIVE); - // let layout_name_cstr = (XKBH.xkb_keymap_layout_get_name)(state.xkb_keymap, layout_id); - // let layout_name = std::ffi::CStr::from_ptr(layout_name_cstr as *mut _); - // debug!("KeyEventResults::new {:?}, {:?}", key_text, layout_name); - // } + let res = KeyEventResults { + keycode: super::keymap::raw_keycode_to_keycode(keycode), + location: super::keymap::keysym_location(keysym), + key: keysym_to_key(keysym), + key_without_modifiers, + text, + text_with_all_modifiers, + }; - KeyEventResults { - state, - keycode, - keysym, - compose, - } - } + // log::trace!("{:?}", res); - pub fn keycode(&mut self) -> KeyCode { - super::keymap::raw_keycode_to_keycode(self.keycode) + res } +} - pub fn key(&mut self) -> (Key<'static>, 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().nth(0).unwrap()), - ), - location, - ), - _ => (key, location), - } - } - _ => { - let composed_text = self.composed_text(); - - #[cfg(feature = "x11")] - if let Ok(Some(composed_text)) = composed_text { - *X11_EVPROC_NEXT_COMPOSE.lock().unwrap() = Some(composed_text); - } +#[derive(Debug)] +pub(crate) struct KeyEventResults { + pub keycode: KeyCode, + pub location: KeyLocation, + pub key: Key<'static>, + pub key_without_modifiers: Key<'static>, + pub text: Option<&'static str>, + pub text_with_all_modifiers: Option<&'static str>, +} - ( - composed_text - .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) - .map(Key::Character) - .unwrap_or(key), - location, - ) - } - }) +fn keysym_to_key(keysym: u32) -> Key<'static> { + let key = super::keymap::keysym_to_key(keysym); + if let Key::Unidentified(_) = key { + keysym_to_utf8_raw(keysym) + .map(Key::Character) + .unwrap_or(key) + } else { + key } +} - pub fn key_without_modifiers(&mut self) -> (Key<'static>, 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_utf8_raw(keysym: u32) -> Option<&'static str> { + let utf32 = unsafe { (XKBH.xkb_keysym_to_utf32)(keysym) }; + char_to_str(utf32) +} - fn keysym_to_key( - &mut self, - keysym: u32, - ) -> Result<(Key<'static>, KeyLocation), (Key<'static>, 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)) +fn char_to_str(utf32: u32) -> Option<&'static str> { + use std::cell::RefCell; + use std::collections::HashMap; + + if utf32 == 0 { + return None; + } + + if utf32 < 128 { + static ASCII: [u8; 128] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, + ]; + unsafe { + debug_assert_eq!(ASCII[utf32 as usize], utf32 as u8); + return Some(str::from_utf8_unchecked(slice::from_raw_parts( + &ASCII[utf32 as usize], + 1, + ))); } } - pub fn text(&mut self) -> Option<&'static str> { - self.composed_text() - .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) - } - - pub fn text_with_all_modifiers(&mut self) -> Option<&'static str> { - // 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)) + thread_local! { + static STRING_CACHE: RefCell> = RefCell::new(HashMap::new()); } - fn composed_text(&mut self) -> Result, ()> { - 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(()), - } + return STRING_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + if let Some(string) = cache.get(&utf32) { + Some(*string) } else { - Err(()) + let mut buf = [0; 4]; + let char = char::from_u32(utf32).unwrap(); + let string: &'static str = + Box::leak(char.encode_utf8(&mut buf).to_string().into_boxed_str()); + cache.insert(utf32, string); + Some(string) } - } + }); } fn byte_slice_to_cached_string(bytes: &[u8]) -> &'static str { @@ -758,7 +640,7 @@ fn byte_slice_to_cached_string(bytes: &[u8]) -> &'static str { static STRING_CACHE: RefCell> = RefCell::new(HashSet::new()); } - let string = std::str::from_utf8(bytes).unwrap(); + let string = str::from_utf8(bytes).unwrap(); STRING_CACHE.with(|cache| { let mut cache = cache.borrow_mut(); diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 6452ae65707..e06e0c82cbd 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -219,7 +219,7 @@ pub enum Event<'a> { /// The key modifiers have changed state Modifiers { /// current state of the modifiers - modifiers: xkb_state::ModifiersState, + modifiers: ModifiersState, }, /// A key event occurred Key { @@ -319,6 +319,7 @@ where current_repeat, details: repeat, }), + group: 0, }; (handler, source) }; @@ -368,6 +369,7 @@ struct RepeatDetails { struct KbdHandler { state: Rc>, + group: u32, callback: Rc>, repeat: Option, } @@ -379,7 +381,7 @@ struct KbdRepeat { } impl KbdRepeat { - fn start_repeat(&self, key: u32, keyboard: wl_keyboard::WlKeyboard, time: u32) { + fn start_repeat(&self, key: u32, group: u32, keyboard: wl_keyboard::WlKeyboard, time: u32) { // Start a new repetition, overwriting the previous ones self.timer_handle.cancel_all_timeouts(); @@ -391,6 +393,7 @@ impl KbdRepeat { *self.current_repeat.borrow_mut() = Some(RepeatData { keyboard, + group, keycode: key, gap, time: (time + self.details.delay) as u64 * 1000, @@ -548,24 +551,17 @@ impl KbdHandler { _ => unreachable!(), }; - let mut ker = state.process_key_event(key + 8, key_state); - - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); - + let ker = state.process_key_event(key + 8, self.group, key_state); let repeats = unsafe { state.key_repeats(key) }; ( - physical_key, - logical_key, - text, - location, + ker.keycode, + ker.key, + ker.text, + ker.location, key_state, - key_without_modifiers, - text_with_all_modifiers, + ker.key_without_modifiers, + ker.text_with_all_modifiers, repeats, ) }; @@ -574,7 +570,7 @@ impl KbdHandler { if let Some(ref mut repeat_handle) = self.repeat { if repeats { if state == ElementState::Pressed { - repeat_handle.start_repeat(key, object.clone(), time); + repeat_handle.start_repeat(key, self.group, object.clone(), time); } else { repeat_handle.stop_repeat(key); } @@ -609,8 +605,9 @@ impl KbdHandler { dispatch_data: client::DispatchData<'_>, ) { { + self.group = group; let mut state = self.state.borrow_mut(); - state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group); + state.update_state(mods_depressed, mods_latched, mods_locked, 0, 0, group); (&mut *self.callback.borrow_mut())( Event::Modifiers { modifiers: state.mods_state(), @@ -639,6 +636,7 @@ impl KbdHandler { struct RepeatData { keyboard: wl_keyboard::WlKeyboard, + group: u32, keycode: u32, /// Gap between key presses in microseconds. gap: u64, @@ -683,25 +681,23 @@ impl calloop::EventSource for RepeatSource { if let Some(ref mut data) = *current_repeat.borrow_mut() { // there is something to repeat let mut state = state.borrow_mut(); - let mut ker = state.process_key_repeat_event(data.keycode + 8); - - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); + let ker = state.process_key_event( + data.keycode + 8, + data.group, + ElementState::Pressed, + ); let new_time = data.gap + data.time; // Notify the callback. callback( Event::Repeat { time: (new_time / 1000) as u32, - physical_key, - logical_key, - text, - location, - key_without_modifiers, - text_with_all_modifiers, + physical_key: ker.keycode, + logical_key: ker.key, + text: ker.text, + location: ker.location, + key_without_modifiers: ker.key_without_modifiers, + text_with_all_modifiers: ker.text_with_all_modifiers, }, &mut data.keyboard, ); diff --git a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs index 1a57b441b02..d2fd21daf7e 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs @@ -9,7 +9,6 @@ use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::Attached; use crate::keyboard::ModifiersState; -use crate::platform_impl::platform::common::xkb_state; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::WindowId; @@ -89,14 +88,3 @@ impl KeyboardInner { } } } - -impl From for ModifiersState { - fn from(mods: xkb_state::ModifiersState) -> ModifiersState { - let mut wl_mods = ModifiersState::empty(); - wl_mods.set(ModifiersState::SHIFT, mods.shift); - wl_mods.set(ModifiersState::CONTROL, mods.ctrl); - wl_mods.set(ModifiersState::ALT, mods.alt); - wl_mods.set(ModifiersState::SUPER, mods.logo); - wl_mods - } -} diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 9dd110d9dd1..125a717baf6 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,6 +1,6 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; +use std::{collections::HashMap, rc::Rc, slice, sync::Arc}; -use libc::{c_char, c_int, c_long, c_ulong}; +use libc::{c_char, c_int, c_long, c_uint, c_ulong}; use parking_lot::MutexGuard; @@ -9,13 +9,12 @@ use super::{ GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension, }; -use util::modifiers::{ModifierKeyState, ModifierKeymap}; - use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, TouchPhase, WindowEvent}, + event::{DeviceEvent, Event, KeyEvent, RawKeyEvent, TouchPhase, WindowEvent}, event_loop::EventLoopWindowTarget as RootELW, keyboard::ModifiersState, + platform::unix::x11::EventLoopWindowTarget, platform_impl::platform::{ common::{keymap, xkb_state::KbState}, KeyEventExtra, @@ -25,41 +24,77 @@ use crate::{ /// The X11 documentation states: "Keycodes lie in the inclusive range [8,255]". const KEYCODE_OFFSET: u8 = 8; +pub(super) struct Seat { + kb_state: KbState, + /// The master keyboard of this seat + keyboard: c_int, + /// The master pointer of this seat + pointer: c_int, + /// The window that has this seats keyboard focus + focus: Option, + /// The latest modifiers state + current_modifiers: ModifiersState, + /// `Some` iff `current_modifiers` changed while `focus` was `None`. If so, it has the same + /// value as `current_modifiers`. + pending_modifiers: Option, +} + pub(super) struct EventProcessor { pub(super) dnd: Dnd, pub(super) ime_receiver: ImeReceiver, pub(super) randr_event_offset: c_int, - pub(super) devices: RefCell>, + pub(super) devices: HashMap, pub(super) xi2ext: XExtension, + pub(super) xkb_base_event: c_int, pub(super) target: Rc>, - pub(super) kb_state: KbState, - pub(super) mod_keymap: ModifierKeymap, - pub(super) device_mod_state: ModifierKeyState, + pub(super) seats: Vec, // Number of touch events currently in progress pub(super) num_touch: u32, pub(super) first_touch: Option, - // Currently focused window belonging to this process - pub(super) active_window: Option, } impl EventProcessor { - pub(super) fn init_device(&self, device: c_int) { - let wt = get_xtarget(&self.target); - let mut devices = self.devices.borrow_mut(); + pub(super) fn init_device( + target: &RootELW, + devices: &mut HashMap, + seats: &mut Vec, + device: c_int, + ) { + let wt = get_xtarget(target); if let Some(info) = DeviceInfo::get(&wt.xconn, device) { for info in info.iter() { - devices.insert(DeviceId(info.deviceid), Device::new(&self, info)); + let device_id = DeviceId(info.deviceid); + if info._use == ffi::XIMasterKeyboard { + if devices.contains_key(&device_id) { + seats.retain(|s| s.keyboard != info.deviceid); + } + let xconn = &wt.xconn; + let connection = unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) }; + let kb_state = KbState::from_x11_xkb(connection, info.deviceid).unwrap(); + seats.push(Seat { + kb_state, + keyboard: info.deviceid, + pointer: info.attachment, + focus: None, + current_modifiers: ModifiersState::empty(), + pending_modifiers: None, + }); + } + devices.insert(device_id, Device::new(wt, info)); } } } - fn with_window(&self, window_id: ffi::Window, callback: F) -> Option + fn with_window( + wt: &EventLoopWindowTarget, + window_id: ffi::Window, + callback: F, + ) -> Option where F: Fn(&Arc) -> Ret, { let mut deleted = false; let window_id = WindowId(window_id); - let wt = get_xtarget(&self.target); let result = wt .windows .borrow() @@ -77,8 +112,8 @@ impl EventProcessor { result } - fn window_exists(&self, window_id: ffi::Window) -> bool { - self.with_window(window_id, |_| ()).is_some() + fn window_exists(wt: &EventLoopWindowTarget, window_id: ffi::Window) -> bool { + Self::with_window(wt, window_id, |_| ()).is_some() } pub(super) fn poll(&self) -> bool { @@ -135,27 +170,6 @@ impl EventProcessor { 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::ClientMessage => { @@ -318,7 +332,7 @@ impl EventProcessor { let xwindow = xev.window; let window_id = mkwid(xwindow); - if let Some(window) = self.with_window(xwindow, Arc::clone) { + if let Some(window) = Self::with_window(wt, xwindow, Arc::clone) { // So apparently... // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent @@ -481,7 +495,7 @@ impl EventProcessor { // effect is that we waste some time trying to query unsupported properties. wt.xconn.update_cached_wm_info(wt.root); - self.with_window(xev.window, |window| { + Self::with_window(wt, xev.window, |window| { window.invalidate_cached_frame_extents(); }); } @@ -513,7 +527,7 @@ impl EventProcessor { let xev: &ffi::XVisibilityEvent = xev.as_ref(); let xwindow = xev.window; - self.with_window(xwindow, |window| window.visibility_notify()); + Self::with_window(wt, xwindow, |window| window.visibility_notify()); } ffi::Expose => { @@ -529,39 +543,6 @@ impl EventProcessor { } } - ffi::KeyPress => { - // TODO: Is it possible to exclusively use XInput2 events here? - let xkev: &mut ffi::XKeyEvent = xev.as_mut(); - - let window = xkev.window; - let window_id = mkwid(window); - - let keycode = xkev.keycode; - // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with - // a keycode of 0. - if keycode == 0 { - let written = if let Some(ic) = wt.ime.borrow().get_context(window) { - wt.xconn.lookup_utf8(ic, xkev) - } else { - return; - }; - if super::super::common::xkb_state::X11_EVPROC_NEXT_COMPOSE - .lock() - .unwrap() - .take() - .map(|composed| composed == written) - .unwrap_or(false) - { - return; - } - let event = Event::WindowEvent { - window_id, - event: WindowEvent::ReceivedImeText(written), - }; - callback(event); - } - } - ffi::GenericEvent => { let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { e @@ -579,8 +560,7 @@ impl EventProcessor { MouseScrollDelta::LineDelta, Touch, WindowEvent::{ - AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput, - MouseWheel, + AxisMotion, CursorEntered, CursorLeft, CursorMoved, MouseInput, MouseWheel, }, }; @@ -594,8 +574,11 @@ impl EventProcessor { return; } - let modifiers = ModifiersState::from_x11(&xev.mods); - update_modifiers!(modifiers, None); + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); let state = if xev.evtype == ffi::XI_ButtonPress { Pressed @@ -603,33 +586,23 @@ impl EventProcessor { Released }; match xev.detail as u32 { - ffi::Button1 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Left, - modifiers, - }, - }), - ffi::Button2 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Middle, - modifiers, - }, - }), - ffi::Button3 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Right, - modifiers, - }, - }), + ffi::Button1 | ffi::Button2 | ffi::Button3 => { + let button = match xev.detail as u32 { + ffi::Button1 => Left, + ffi::Button2 => Middle, + ffi::Button3 => Right, + _ => unreachable!(), + }; + callback(Event::WindowEvent { + window_id, + event: MouseInput { + device_id, + state, + button, + modifiers: seat.current_modifiers, + }, + }) + } // Suppress emulated scroll wheel clicks, since we handle the real motion events for those. // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in @@ -648,7 +621,7 @@ impl EventProcessor { _ => unreachable!(), }, phase: TouchPhase::Moved, - modifiers, + modifiers: seat.current_modifiers, }, }); } @@ -660,7 +633,7 @@ impl EventProcessor { device_id, state, button: Other(x as u16), - modifiers, + modifiers: seat.current_modifiers, }, }), } @@ -671,10 +644,13 @@ impl EventProcessor { 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 seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); - let cursor_moved = self.with_window(xev.event, |window| { + let cursor_moved = Self::with_window(wt, xev.event, |window| { let mut shared_state_lock = window.shared_state.lock(); util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos) }); @@ -686,7 +662,7 @@ impl EventProcessor { event: CursorMoved { device_id, position, - modifiers, + modifiers: seat.current_modifiers, }, }); } else if cursor_moved.is_none() { @@ -702,11 +678,11 @@ impl EventProcessor { xev.valuators.mask_len as usize, ) }; - let mut devices = self.devices.borrow_mut(); - let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) { - Some(device) => device, - None => return, - }; + let physical_device = + match self.devices.get_mut(&DeviceId(xev.sourceid)) { + Some(device) => device, + None => return, + }; let mut value = xev.valuators.values; for i in 0..xev.valuators.mask_len * 8 { @@ -733,7 +709,7 @@ impl EventProcessor { } }, phase: TouchPhase::Moved, - modifiers, + modifiers: seat.current_modifiers, }, }); } else { @@ -761,8 +737,13 @@ impl EventProcessor { let window_id = mkwid(xev.event); let device_id = mkdid(xev.deviceid); + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) { - let mut devices = self.devices.borrow_mut(); for device_info in all_info.iter() { if device_info.deviceid == xev.sourceid // This is needed for resetting to work correctly on i3, and @@ -772,14 +753,14 @@ impl EventProcessor { || device_info.attachment == xev.sourceid { let device_id = DeviceId(device_info.deviceid); - if let Some(device) = devices.get_mut(&device_id) { + if let Some(device) = self.devices.get_mut(&device_id) { device.reset_scroll_position(device_info); } } } } - if self.window_exists(xev.event) { + if Self::window_exists(wt, xev.event) { callback(Event::WindowEvent { window_id, event: CursorEntered { device_id }, @@ -787,25 +768,12 @@ impl EventProcessor { 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, + modifiers: seat.current_modifiers, }, }); } @@ -815,7 +783,7 @@ impl EventProcessor { // Leave, FocusIn, and FocusOut can be received by a window that's already // been destroyed, which the user presumably doesn't want to deal with. - let window_closed = !self.window_exists(xev.event); + let window_closed = !Self::window_exists(wt, xev.event); if !window_closed { callback(Event::WindowEvent { window_id: mkwid(xev.event), @@ -833,43 +801,21 @@ impl EventProcessor { .focus(xev.event) .expect("Failed to focus input context"); - if self.active_window != Some(xev.event) { - self.active_window = Some(xev.event); + let seat = match find_seat(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, wt, Some(xev.event), &mut callback); - let window_id = mkwid(xev.event); + if let Some(focus) = seat.focus { let position = PhysicalPosition::new(xev.event_x, xev.event_y); - - callback(Event::WindowEvent { - window_id, - event: Focused(true), - }); - - // Issue key press events for all pressed keys - Self::handle_pressed_keys( - &wt, - window_id, - ElementState::Pressed, - &mut self.kb_state, - &self.mod_keymap, - &mut self.device_mod_state, - &mut callback, - ); - - // The deviceid for this event is for a keyboard instead of a pointer, - // so we have to do a little extra work. - let pointer_id = self - .devices - .borrow() - .get(&DeviceId(xev.deviceid)) - .map(|device| device.attachment) - .unwrap_or(2); - callback(Event::WindowEvent { - window_id, + window_id: mkwid(focus), event: CursorMoved { - device_id: mkdid(pointer_id), + device_id: mkdid(seat.pointer), position, - modifiers: self.device_mod_state.modifiers(), + modifiers: seat.current_modifiers, }, }); } @@ -877,33 +823,18 @@ impl EventProcessor { ffi::XI_FocusOut => { let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; - if !self.window_exists(xev.event) { - return; - } + wt.ime .borrow_mut() .unfocus(xev.event) .expect("Failed to unfocus input context"); - if self.active_window.take() == Some(xev.event) { - let window_id = mkwid(xev.event); - - // Issue key release events for all pressed keys - Self::handle_pressed_keys( - &wt, - window_id, - ElementState::Released, - &mut self.kb_state, - &self.mod_keymap, - &mut self.device_mod_state, - &mut callback, - ); - - callback(Event::WindowEvent { - window_id, - event: Focused(false), - }) - } + let seat = match find_seat(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, wt, None, &mut callback); } ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => { @@ -915,9 +846,14 @@ impl EventProcessor { ffi::XI_TouchEnd => TouchPhase::Ended, _ => unreachable!(), }; - if self.window_exists(xev.event) { + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + + if Self::window_exists(wt, xev.event) { let id = xev.detail as u64; - let modifiers = self.device_mod_state.modifiers(); let location = PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); @@ -928,9 +864,9 @@ impl EventProcessor { callback(Event::WindowEvent { window_id, event: WindowEvent::CursorMoved { - device_id: mkdid(util::VIRTUAL_CORE_POINTER), + device_id: mkdid(seat.pointer), position: location.cast(), - modifiers, + modifiers: seat.current_modifiers, }, }); } @@ -1019,65 +955,46 @@ impl EventProcessor { // 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 let Some(active_window) = self.active_window { + let xkev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; + + let seat = match find_seat(&mut self.seats, xkev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xkev.mods, &xkev.group, &mut callback); + Self::update_seat_focus(seat, wt, Some(xkev.event), &mut callback); + + if let Some(focus) = seat.focus { 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 mut ker = self.kb_state.process_key_event(keycode, state); - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); + let ker = seat.kb_state.process_key_event( + keycode, + xkev.group.effective as u32, + state, + ); let repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat; - if let Some(modifier) = - self.mod_keymap.get_modifier(keycode as ffi::KeyCode) - { - let old_modifiers = self.device_mod_state.modifiers(); - - self.device_mod_state.key_event( - state, - keycode as ffi::KeyCode, - modifier, - ); - - if old_modifiers != self.device_mod_state.modifiers() { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged( - self.device_mod_state.modifiers(), - ), - }); - } - } - callback(Event::WindowEvent { - window_id, + window_id: mkwid(focus), event: WindowEvent::KeyboardInput { device_id, event: KeyEvent { - physical_key, - logical_key, - text, - location, + physical_key: ker.keycode, + logical_key: ker.key, + text: ker.text, + location: ker.location, state, repeat, platform_specific: KeyEventExtra { - key_without_modifiers, - text_with_all_modifiers, + key_without_modifiers: ker.key_without_modifiers, + text_with_all_modifiers: ker.text_with_all_modifiers, }, }, is_synthetic: false, @@ -1087,44 +1004,6 @@ impl EventProcessor { } ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => { - // This is horrible, but I couldn't manage to respect keyboard layout changes - // in any other way. In fact, getting this to work at all proved so frustrating - // that I (@maroider) lost motivation to work on the keyboard event rework for - // some months. Thankfully, @ArturKovacs offered to help debug the problem - // over discord, and the following is the result of that debugging session. - // - // Without the XKB extension, the X.Org server sends us the `MappingNotify` - // event when there's been a change in the keyboard layout. This stops - // being the case when we select ourselves some XKB events with `XkbSelectEvents` - // and the "core keyboard device (0x100)" (we haven't tried with any other - // devices). We managed to reproduce this on both our machines. - // - // With the XKB extension active, it would seem like we're supposed to use the - // `XkbStateNotify` event to detect keyboard layout changes, but the `group` - // never changes value (it is always `0`). This worked for @ArturKovacs, but - // not for me. We also tried to use the `group` given to us in keypress events, - // but it remained constant there, too. - // - // We also tried to see if there was some other event that got fired when the - // keyboard layout changed, and we found a mysterious event with the value - // `85` (`0x55`). We couldn't find any reference to it in the X11 headers or - // in the X.Org server source. - // - // `KeymapNotify` did briefly look interesting based purely on the name, but - // it is only useful for checking what keys are pressed when we receive the - // event. - // - // So instead of any vaguely reasonable approach, we get this: reloading the - // keymap on *every* keypress. That's peak efficiency right there! - // - // FIXME: Someone please save our souls! Or at least our wasted CPU cycles. - // - // If you do manage to find a solution, remember to re-enable (and handle) the - // `XkbStateNotify` event with `XkbSelectEventDetails` with a mask of - // `XkbAllStateComponentsMask & !XkbPointerButtonMask` like in - // . - unsafe { self.kb_state.init_with_x11_keymap() }; - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; let state = match xev.evtype { @@ -1155,7 +1034,12 @@ impl EventProcessor { unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } { if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) { - self.init_device(info.deviceid); + Self::init_device( + &self.target, + &mut self.devices, + &mut self.seats, + info.deviceid, + ); callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Added, @@ -1166,8 +1050,10 @@ impl EventProcessor { device_id: mkdid(info.deviceid), event: DeviceEvent::Removed, }); - let mut devices = self.devices.borrow_mut(); - devices.remove(&DeviceId(info.deviceid)); + self.devices.remove(&DeviceId(info.deviceid)); + if info._use == ffi::XIMasterKeyboard { + self.seats.retain(|s| s.keyboard != info.deviceid); + } } } } @@ -1175,68 +1061,95 @@ impl EventProcessor { _ => {} } } - _ => { - 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(); - if let Some(prev_list) = prev_list { - let new_list = wt.xconn.available_monitors(); - for new_monitor in new_list { - prev_list - .iter() - .find(|prev_monitor| prev_monitor.name == new_monitor.name) - .map(|prev_monitor| { - if new_monitor.scale_factor != prev_monitor.scale_factor { - for (window_id, window) in wt.windows.borrow().iter() { - if let Some(window) = window.upgrade() { - // Check if the window is on this monitor - let monitor = window.current_monitor(); - if monitor.name == new_monitor.name { - let (width, height) = - window.inner_size_physical(); - let (new_width, new_height) = window - .adjust_for_dpi( - prev_monitor.scale_factor, - new_monitor.scale_factor, - width, - height, - &*window.shared_state.lock(), - ); - - let window_id = crate::window::WindowId( - crate::platform_impl::platform::WindowId::X( - *window_id, - ), + _ 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(); + if let Some(prev_list) = prev_list { + let new_list = wt.xconn.available_monitors(); + for new_monitor in new_list { + prev_list + .iter() + .find(|prev_monitor| prev_monitor.name == new_monitor.name) + .map(|prev_monitor| { + if new_monitor.scale_factor != prev_monitor.scale_factor { + for (window_id, window) in wt.windows.borrow().iter() { + if let Some(window) = window.upgrade() { + // Check if the window is on this monitor + let monitor = window.current_monitor(); + if monitor.name == new_monitor.name { + let (width, height) = window.inner_size_physical(); + let (new_width, new_height) = window + .adjust_for_dpi( + prev_monitor.scale_factor, + new_monitor.scale_factor, + width, + height, + &*window.shared_state.lock(), + ); + + let window_id = crate::window::WindowId( + crate::platform_impl::platform::WindowId::X( + *window_id, + ), + ); + let old_inner_size = + PhysicalSize::new(width, height); + let mut new_inner_size = + PhysicalSize::new(new_width, new_height); + + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ScaleFactorChanged { + scale_factor: new_monitor.scale_factor, + new_inner_size: &mut new_inner_size, + }, + }); + + if new_inner_size != old_inner_size { + let (new_width, new_height) = + new_inner_size.into(); + window.set_inner_size_physical( + new_width, new_height, ); - let old_inner_size = - PhysicalSize::new(width, height); - let mut new_inner_size = - PhysicalSize::new(new_width, new_height); - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_monitor.scale_factor, - new_inner_size: &mut new_inner_size, - }, - }); - - if new_inner_size != old_inner_size { - let (new_width, new_height) = - new_inner_size.into(); - window.set_inner_size_physical( - new_width, new_height, - ); - } } } } } - }); + } + }); + } + } + } + _ if event_type == self.xkb_base_event => { + let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) }; + let seat = match find_seat(&mut self.seats, xev.device as c_int) { + Some(state) => state, + _ => return, + }; + match xev.xkb_type { + ffi::XkbMapNotify | ffi::XkbNewKeyboardNotify => { + unsafe { + seat.kb_state.init_with_x11_keymap(xev.device as c_int); } + Self::modifiers_changed(seat, &mut callback); + } + ffi::XkbStateNotify => { + let xev = unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; + Self::update_seat_kb( + seat, + xev.base_mods as u32, + xev.latched_mods as u32, + xev.locked_mods as u32, + xev.base_group as u32, + xev.latched_group as u32, + xev.locked_group as u32, + &mut callback, + ); } + _ => {} } } + _ => {} } match self.ime_receiver.try_recv() { @@ -1247,68 +1160,128 @@ impl EventProcessor { } } - fn handle_pressed_keys( - wt: &super::EventLoopWindowTarget, - window_id: crate::window::WindowId, - state: ElementState, - kb_state: &mut KbState, - mod_keymap: &ModifierKeymap, - device_mod_state: &mut ModifierKeyState, + /// Updates the window that has this seat's keyboard focus. + /// + /// This function must be called whenever an event occurs that could potentially imply a + /// change of the focus. Some non-multi-seat-aware window managers treat all focus changes + /// as changes of the core-seat focus. In these cases the X server will not send FocusIn/Out + /// events for additional seats. By calling this function on every keyboard input, we update + /// the focus as necessary. + fn update_seat_focus( + seat: &mut Seat, + wt: &EventLoopWindowTarget, + mut focus: Option, callback: &mut F, ) where F: FnMut(Event<'_, T>), { - let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); - - // Update modifiers state and emit key events based on which keys are currently pressed. - for keycode in wt - .xconn - .query_keymap() - .into_iter() - .filter(|k| *k >= KEYCODE_OFFSET) - { - let keycode = keycode as u32; - - let mut ker = kb_state.process_key_event(keycode, state); - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); - - if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) { - let old_modifiers = device_mod_state.modifiers(); - - device_mod_state.key_event(state, keycode as ffi::KeyCode, modifier); - - if old_modifiers != device_mod_state.modifiers() { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged(device_mod_state.modifiers()), - }); + if seat.focus == focus { + return; + } + if let Some(new) = focus { + if !Self::window_exists(wt, new) { + if seat.focus.is_none() { + return; } + focus = None; } - + } + if seat.focus.is_some() != focus.is_some() { + let mask = if focus.is_none() { + 0 + } else { + ffi::XkbModifierStateMask + }; + wt.xconn + .select_xkb_event_details( + seat.keyboard as c_uint, + ffi::XkbStateNotify as c_uint, + mask, + ) + .unwrap() + .queue(); + } + if let Some(focus) = seat.focus { callback(Event::WindowEvent { - window_id, - event: WindowEvent::KeyboardInput { - device_id, - event: KeyEvent { - physical_key, - logical_key, - text, - location, - state, - repeat: false, - platform_specific: KeyEventExtra { - key_without_modifiers, - text_with_all_modifiers, - }, - }, - is_synthetic: true, - }, + window_id: mkwid(focus), + event: WindowEvent::Focused(false), }); } + seat.focus = focus; + if let Some(focus) = seat.focus { + callback(Event::WindowEvent { + window_id: mkwid(focus), + event: WindowEvent::Focused(true), + }); + if let Some(mods) = seat.pending_modifiers.take() { + callback(Event::WindowEvent { + window_id: mkwid(focus), + event: WindowEvent::ModifiersChanged(mods), + }); + } + } + } + + fn update_seat_kb_xi( + seat: &mut Seat, + mods: &ffi::XIModifierState, + group: &ffi::XIGroupState, + callback: &mut F, + ) where + F: FnMut(Event<'_, T>), + { + Self::update_seat_kb( + seat, + mods.base as u32, + mods.latched as u32, + mods.locked as u32, + group.base as u32, + group.latched as u32, + group.locked as u32, + callback, + ); + } + + fn update_seat_kb( + seat: &mut Seat, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + depressed_group: u32, + latched_group: u32, + locked_group: u32, + callback: &mut F, + ) where + F: FnMut(Event<'_, T>), + { + seat.kb_state.update_state( + mods_depressed, + mods_latched, + mods_locked, + depressed_group, + latched_group, + locked_group, + ); + Self::modifiers_changed(seat, callback); + } + + fn modifiers_changed(seat: &mut Seat, callback: &mut F) + where + F: FnMut(Event<'_, T>), + { + let new = seat.kb_state.mods_state(); + if seat.current_modifiers != new { + seat.current_modifiers = new; + if let Some(window) = seat.focus { + seat.pending_modifiers = None; + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::ModifiersChanged(new), + }); + } else { + seat.pending_modifiers = Some(new); + } + } } } @@ -1331,3 +1304,11 @@ fn is_first_touch(first: &mut Option, num: &mut u32, id: u64, phase: TouchP *first == Some(id) } + +fn find_seat(seats: &mut [Seat], kb: c_int) -> Option<&mut Seat> { + seats.iter_mut().find(|s| s.keyboard == kb) +} + +fn find_seat_by_pointer(seats: &mut [Seat], pointer: c_int) -> Option<&mut Seat> { + seats.iter_mut().find(|s| s.pointer == pointer) +} diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs index b95da711010..4e2db1ef5e0 100644 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ b/src/platform_impl/linux/x11/ime/mod.rs @@ -99,17 +99,6 @@ impl Ime { Ok(!self.is_destroyed()) } - pub fn get_context(&self, window: ffi::Window) -> Option { - if self.is_destroyed() { - return None; - } - if let Some(&Some(ref context)) = self.inner.contexts.get(&window) { - Some(context.ic) - } else { - None - } - } - pub fn remove_context(&mut self, window: ffi::Window) -> Result { if let Some(Some(context)) = self.inner.contexts.remove(&window) { unsafe { diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 6aa493e215a..3e905e62785 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -50,9 +50,7 @@ use self::{ dnd::{Dnd, DndState}, event_processor::EventProcessor, ime::{Ime, ImeCreationError, ImeReceiver, ImeSender}, - util::modifiers::ModifierKeymap, }; -use super::common::xkb_state::KbState; use crate::{ error::OsError as RootOsError, event::{Event, StartCause}, @@ -61,6 +59,11 @@ use crate::{ window::WindowAttributes, }; +use xkbcommon_dl::x11::{ + xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + XKBCOMMON_X11_HANDLE as XKBXH, +}; + const X_TOKEN: Token = Token(0); const USER_REDRAW_TOKEN: Token = Token(1); @@ -162,6 +165,26 @@ impl EventLoop { ext }; + let xkb_base_event = { + let mut base_event = 0; + + let result = unsafe { + (XKBXH.xkb_x11_setup_xkb_extension)( + (xconn.xlib_xcb.XGetXCBConnection)(xconn.display), + 1, // major + 0, // minor + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + ptr::null_mut(), // major out + ptr::null_mut(), // minor out + &mut base_event, + ptr::null_mut(), // base error + ) + }; + assert_eq!(result, 1, "Failed to initialize libxkbcommon"); + + base_event + }; + unsafe { let mut xinput_major_ver = ffi::XI_2_Major; let mut xinput_minor_ver = ffi::XI_2_Minor; @@ -180,9 +203,6 @@ impl EventLoop { 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()); let queue = Arc::new(NotificationQueue::new(waker)); @@ -195,10 +215,6 @@ impl EventLoop { let (redraw_sender, redraw_channel) = channel(queue, NotificationId::gen_next()); - let kb_state = - KbState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) }) - .unwrap(); - let target = Rc::new(RootELW { p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { ime, @@ -214,19 +230,17 @@ impl EventLoop { _marker: ::std::marker::PhantomData, }); - let event_processor = EventProcessor { + let mut event_processor = EventProcessor { target: target.clone(), dnd, devices: Default::default(), randr_event_offset, ime_receiver, xi2ext, - kb_state, - mod_keymap, - device_mod_state: Default::default(), + xkb_base_event: xkb_base_event as c_int, num_touch: 0, first_touch: None, - active_window: None, + seats: Default::default(), }; // Register for device hotplug events @@ -236,7 +250,12 @@ impl EventLoop { .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) .queue(); - event_processor.init_device(ffi::XIAllDevices); + EventProcessor::init_device( + &target, + &mut event_processor.devices, + &mut event_processor.seats, + ffi::XIAllDevices, + ); let result = EventLoop { poll, @@ -614,11 +633,19 @@ enum ScrollOrientation { } impl Device { - fn new(el: &EventProcessor, info: &ffi::XIDeviceInfo) -> Self { + fn new(wt: &EventLoopWindowTarget, info: &ffi::XIDeviceInfo) -> Self { let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() }; let mut scroll_axes = Vec::new(); - let wt = get_xtarget(&el.target); + if info._use == ffi::XIMasterKeyboard { + wt.xconn + .select_xkb_events( + info.deviceid as c_uint, + ffi::XkbNewKeyboardNotifyMask | ffi::XkbMapNotifyMask, + ) + .unwrap() + .queue(); + } if Device::physical_device(info) { // Register for global raw events diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs index 24aad57937a..daef20b69ef 100644 --- a/src/platform_impl/linux/x11/util/input.rs +++ b/src/platform_impl/linux/x11/util/input.rs @@ -1,30 +1,6 @@ -use std::{slice, str}; - use super::*; -use crate::keyboard::ModifiersState; pub const VIRTUAL_CORE_POINTER: c_int = 2; -pub const VIRTUAL_CORE_KEYBOARD: c_int = 3; - -// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to -// re-allocate (and make another round-trip) in the *vast* majority of cases. -// 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::CONTROL, mask & ffi::ControlMask != 0); - m.set(ModifiersState::SUPER, 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> { @@ -36,17 +12,9 @@ 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() { @@ -91,6 +59,23 @@ impl XConnection { } } + pub fn select_xkb_event_details( + &self, + device_id: c_uint, + event: c_uint, + mask: c_ulong, + ) -> Option> { + let status = unsafe { + (self.xlib.XkbSelectEventDetails)(self.display, device_id, event, mask, mask) + }; + if status == ffi::True { + Some(Flusher::new(self)) + } else { + error!("Could not select XKB events: The XKB extension is not initialized!"); + None + } + } + pub fn query_pointer( &self, window: ffi::Window, @@ -133,58 +118,8 @@ impl XConnection { win_x, win_y, buttons, - modifiers, - group, relative_to_window, }) } } - - fn lookup_utf8_inner( - &self, - ic: ffi::XIC, - key_event: &mut ffi::XKeyEvent, - buffer: *mut u8, - size: usize, - ) -> (ffi::KeySym, ffi::Status, c_int) { - let mut keysym: ffi::KeySym = 0; - let mut status: ffi::Status = 0; - let count = unsafe { - (self.xlib.Xutf8LookupString)( - ic, - key_event, - buffer as *mut c_char, - size as c_int, - &mut keysym, - &mut status, - ) - }; - (keysym, status, count) - } - - pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String { - // `assume_init` is safe here because the array consists of `MaybeUninit` values, - // which do not require initialization. - let mut buffer: [MaybeUninit; TEXT_BUFFER_SIZE] = - unsafe { MaybeUninit::uninit().assume_init() }; - // If the buffer overflows, we'll make a new one on the heap. - let mut vec; - - let (_, status, count) = - self.lookup_utf8_inner(ic, key_event, buffer.as_mut_ptr() as *mut u8, buffer.len()); - - let bytes = if status == ffi::XBufferOverflow { - vec = Vec::with_capacity(count as usize); - let (_, _, new_count) = - self.lookup_utf8_inner(ic, key_event, vec.as_mut_ptr(), vec.capacity()); - debug_assert_eq!(count, new_count); - - unsafe { vec.set_len(count as usize) }; - &vec[..count as usize] - } else { - unsafe { slice::from_raw_parts(buffer.as_ptr() as *const u8, count as usize) } - }; - - str::from_utf8(bytes).unwrap_or("").to_string() - } } diff --git a/src/platform_impl/linux/x11/util/keys.rs b/src/platform_impl/linux/x11/util/keys.rs deleted file mode 100644 index fc0c9d90620..00000000000 --- a/src/platform_impl/linux/x11/util/keys.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::{iter::Enumerate, ptr, slice::Iter}; - -use super::*; - -pub struct Keymap { - keys: [u8; 32], -} - -pub struct KeymapIter<'a> { - iter: Enumerate>, - index: usize, - item: Option, -} - -impl Keymap { - pub fn iter(&self) -> KeymapIter<'_> { - KeymapIter { - iter: self.keys.iter().enumerate(), - index: 0, - item: None, - } - } -} - -impl<'a> IntoIterator for &'a Keymap { - type Item = ffi::KeyCode; - type IntoIter = KeymapIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl Iterator for KeymapIter<'_> { - type Item = ffi::KeyCode; - - fn next(&mut self) -> Option { - if self.item.is_none() { - while let Some((index, &item)) = self.iter.next() { - if item != 0 { - self.index = index; - self.item = Some(item); - break; - } - } - } - - self.item.take().map(|item| { - debug_assert!(item != 0); - - let bit = first_bit(item); - - if item != bit { - // Remove the first bit; save the rest for further iterations - self.item = Some(item ^ bit); - } - - let shift = bit.trailing_zeros() + (self.index * 8) as u32; - shift as ffi::KeyCode - }) - } -} - -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]; - - unsafe { - (self.xlib.XQueryKeymap)(self.display, keys.as_mut_ptr() as *mut c_char); - } - - Keymap { keys } - } -} - -fn first_bit(b: u8) -> u8 { - 1 << b.trailing_zeros() -} diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs index df3d89138ab..b7ea4e76904 100644 --- a/src/platform_impl/linux/x11/util/mod.rs +++ b/src/platform_impl/linux/x11/util/mod.rs @@ -9,9 +9,7 @@ mod geometry; mod hint; mod icon; mod input; -pub mod keys; mod memory; -pub mod modifiers; mod randr; mod window_property; mod wm; diff --git a/src/platform_impl/linux/x11/util/modifiers.rs b/src/platform_impl/linux/x11/util/modifiers.rs deleted file mode 100644 index d7e10fa0b27..00000000000 --- a/src/platform_impl/linux/x11/util/modifiers.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::{collections::HashMap, slice}; - -use super::*; - -use crate::event::ElementState; -use crate::keyboard::ModifiersState; - -// Offsets within XModifierKeymap to each set of keycodes. -// We are only interested in Shift, Control, Alt, and Logo. -// -// There are 8 sets total. The order of keycode sets is: -// Shift, Lock, Control, Mod1 (Alt), Mod2, Mod3, Mod4 (Logo), Mod5 -// -// https://tronche.com/gui/x/xlib/input/XSetModifierMapping.html -const SHIFT_OFFSET: usize = 0; -const CONTROL_OFFSET: usize = 2; -const ALT_OFFSET: usize = 3; -const LOGO_OFFSET: usize = 6; -const NUM_MODS: usize = 8; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Modifier { - Alt, - Ctrl, - Shift, - Logo, -} - -#[derive(Debug, Default)] -pub struct ModifierKeymap { - // Maps keycodes to modifiers - keys: HashMap, -} - -#[derive(Clone, Debug, Default)] -pub struct ModifierKeyState { - // Contains currently pressed modifier keys and their corresponding modifiers - keys: HashMap, - state: ModifiersState, -} - -impl ModifierKeymap { - pub fn new() -> ModifierKeymap { - ModifierKeymap::default() - } - - pub fn get_modifier(&self, keycode: ffi::KeyCode) -> Option { - self.keys.get(&keycode).cloned() - } - - pub fn reset_from_x_connection(&mut self, xconn: &XConnection) { - unsafe { - let keymap = (xconn.xlib.XGetModifierMapping)(xconn.display); - - if keymap.is_null() { - panic!("failed to allocate XModifierKeymap"); - } - - self.reset_from_x_keymap(&*keymap); - - (xconn.xlib.XFreeModifiermap)(keymap); - } - } - - pub fn reset_from_x_keymap(&mut self, keymap: &ffi::XModifierKeymap) { - let keys_per_mod = keymap.max_keypermod as usize; - - let keys = unsafe { - slice::from_raw_parts(keymap.modifiermap as *const _, keys_per_mod * NUM_MODS) - }; - - self.keys.clear(); - - self.read_x_keys(keys, SHIFT_OFFSET, keys_per_mod, Modifier::Shift); - self.read_x_keys(keys, CONTROL_OFFSET, keys_per_mod, Modifier::Ctrl); - self.read_x_keys(keys, ALT_OFFSET, keys_per_mod, Modifier::Alt); - self.read_x_keys(keys, LOGO_OFFSET, keys_per_mod, Modifier::Logo); - } - - fn read_x_keys( - &mut self, - keys: &[ffi::KeyCode], - offset: usize, - keys_per_mod: usize, - modifier: Modifier, - ) { - let start = offset * keys_per_mod; - let end = start + keys_per_mod; - - for &keycode in &keys[start..end] { - if keycode != 0 { - self.keys.insert(keycode, modifier); - } - } - } -} - -impl ModifierKeyState { - pub fn update_keymap(&mut self, mods: &ModifierKeymap) { - self.keys.retain(|k, v| { - if let Some(m) = mods.get_modifier(*k) { - *v = m; - true - } else { - false - } - }); - - self.reset_state(); - } - - pub fn update_state( - &mut self, - state: &ModifiersState, - except: Option, - ) -> Option { - let mut new_state = *state; - - match except { - Some(Modifier::Alt) => new_state.set(ModifiersState::ALT, self.state.alt_key()), - Some(Modifier::Ctrl) => { - new_state.set(ModifiersState::CONTROL, self.state.control_key()) - } - Some(Modifier::Shift) => new_state.set(ModifiersState::SHIFT, self.state.shift_key()), - Some(Modifier::Logo) => new_state.set(ModifiersState::SUPER, self.state.super_key()), - None => (), - } - - if self.state == new_state { - None - } else { - self.keys.retain(|_k, v| get_modifier(&new_state, *v)); - self.state = new_state; - Some(new_state) - } - } - - pub fn modifiers(&self) -> ModifiersState { - self.state - } - - pub fn key_event(&mut self, state: ElementState, keycode: ffi::KeyCode, modifier: Modifier) { - match state { - ElementState::Pressed => self.key_press(keycode, modifier), - ElementState::Released => self.key_release(keycode), - } - } - - pub fn key_press(&mut self, keycode: ffi::KeyCode, modifier: Modifier) { - self.keys.insert(keycode, modifier); - - set_modifier(&mut self.state, modifier, true); - } - - pub fn key_release(&mut self, keycode: ffi::KeyCode) { - if let Some(modifier) = self.keys.remove(&keycode) { - if self.keys.values().find(|&&m| m == modifier).is_none() { - set_modifier(&mut self.state, modifier, false); - } - } - } - - fn reset_state(&mut self) { - let mut new_state = ModifiersState::default(); - - for &m in self.keys.values() { - set_modifier(&mut new_state, m, true); - } - - self.state = new_state; - } -} - -fn get_modifier(state: &ModifiersState, modifier: Modifier) -> bool { - match modifier { - Modifier::Alt => state.alt_key(), - Modifier::Ctrl => state.control_key(), - Modifier::Shift => state.shift_key(), - Modifier::Logo => state.super_key(), - } -} - -fn set_modifier(state: &mut ModifiersState, modifier: Modifier, value: bool) { - match modifier { - Modifier::Alt => state.set(ModifiersState::ALT, value), - Modifier::Ctrl => state.set(ModifiersState::CONTROL, value), - Modifier::Shift => state.set(ModifiersState::SHIFT, value), - Modifier::Logo => state.set(ModifiersState::SUPER, value), - } -}