On X11, use events modifiers to detect state

While there's a separate event to deliver modifiers for keyboard,
unfortunately, it's not even remotely reflects the modifiers state.

Thus use events along side regular modifier updates to correctly
detect the state. Also, apply the modifiers from the regular
key event by converting their state to xkb modifiers state.

Links: https://github.com/alacritty/alacritty/issues/7549
Closes: #3388
This commit is contained in:
Kirill Chibisov
2024-02-18 01:39:42 +04:00
parent 9c033ce101
commit cb855b87cc
11 changed files with 1167 additions and 84 deletions

View File

@@ -1,2 +1 @@
pub mod keymap;
pub mod xkb_state;
pub mod xkb;

View File

@@ -0,0 +1,124 @@
//! XKB compose handling.
use std::env;
use std::ffi::CString;
use std::ops::Deref;
use std::os::unix::ffi::OsStringExt;
use std::ptr::NonNull;
use super::XkbContext;
use super::XKBCH;
use smol_str::SmolStr;
use xkbcommon_dl::{
xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, xkb_compose_state_flags,
xkb_compose_status, xkb_compose_table, xkb_keysym_t,
};
#[derive(Debug)]
pub struct XkbComposeTable {
table: NonNull<xkb_compose_table>,
}
impl XkbComposeTable {
pub fn new(context: &XkbContext) -> Option<Self> {
let locale = env::var_os("LC_ALL")
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.or_else(|| env::var_os("LC_CTYPE"))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.or_else(|| env::var_os("LANG"))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.unwrap_or_else(|| "C".into());
let locale = CString::new(locale.into_vec()).unwrap();
let table = unsafe {
(XKBCH.xkb_compose_table_new_from_locale)(
context.as_ptr(),
locale.as_ptr(),
xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
)
};
let table = NonNull::new(table)?;
Some(Self { table })
}
/// Create new state with the given compose table.
pub fn new_state(&self) -> Option<XkbComposeState> {
let state = unsafe {
(XKBCH.xkb_compose_state_new)(
self.table.as_ptr(),
xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
)
};
let state = NonNull::new(state)?;
Some(XkbComposeState { state })
}
}
impl Deref for XkbComposeTable {
type Target = NonNull<xkb_compose_table>;
fn deref(&self) -> &Self::Target {
&self.table
}
}
impl Drop for XkbComposeTable {
fn drop(&mut self) {
unsafe {
(XKBCH.xkb_compose_table_unref)(self.table.as_ptr());
}
}
}
#[derive(Debug)]
pub struct XkbComposeState {
state: NonNull<xkb_compose_state>,
}
impl XkbComposeState {
pub fn get_string(&mut self, scratch_buffer: &mut Vec<u8>) -> Option<SmolStr> {
super::make_string_with(scratch_buffer, |ptr, len| unsafe {
(XKBCH.xkb_compose_state_get_utf8)(self.state.as_ptr(), ptr, len)
})
}
#[inline]
pub fn feed(&mut self, keysym: xkb_keysym_t) -> ComposeStatus {
let feed_result = unsafe { (XKBCH.xkb_compose_state_feed)(self.state.as_ptr(), keysym) };
match feed_result {
xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED => ComposeStatus::Ignored,
xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED => {
ComposeStatus::Accepted(self.status())
}
}
}
#[inline]
pub fn reset(&mut self) {
unsafe {
(XKBCH.xkb_compose_state_reset)(self.state.as_ptr());
}
}
#[inline]
pub fn status(&mut self) -> xkb_compose_status {
unsafe { (XKBCH.xkb_compose_state_get_status)(self.state.as_ptr()) }
}
}
impl Drop for XkbComposeState {
fn drop(&mut self) {
unsafe {
(XKBCH.xkb_compose_state_unref)(self.state.as_ptr());
};
}
}
#[derive(Copy, Clone, Debug)]
pub enum ComposeStatus {
Accepted(xkb_compose_status),
Ignored,
None,
}

View File

@@ -1,6 +1,24 @@
//! Convert XKB keys to Winit keys.
//! XKB keymap.
use std::ffi::c_char;
use std::ops::Deref;
use std::ptr::{self, NonNull};
#[cfg(x11_platform)]
use x11_dl::xlib_xcb::xcb_connection_t;
#[cfg(wayland_platform)]
use {memmap2::MmapOptions, std::os::unix::io::OwnedFd};
use xkb::XKB_MOD_INVALID;
use xkbcommon_dl::{
self as xkb, xkb_keycode_t, xkb_keymap, xkb_keymap_compile_flags, xkb_keysym_t,
xkb_layout_index_t, xkb_mod_index_t,
};
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey};
#[cfg(x11_platform)]
use crate::platform_impl::common::xkb::XKBXH;
use crate::platform_impl::common::xkb::{XkbContext, XKBH};
/// Map the raw X11-style keycode to the `KeyCode` enum.
///
@@ -894,3 +912,140 @@ pub fn keysym_location(keysym: u32) -> KeyLocation {
_ => KeyLocation::Standard,
}
}
#[derive(Debug)]
pub struct XkbKeymap {
keymap: NonNull<xkb_keymap>,
_mods_indices: ModsIndices,
pub _core_keyboard_id: i32,
}
impl XkbKeymap {
#[cfg(wayland_platform)]
pub fn from_fd(context: &XkbContext, fd: OwnedFd, size: usize) -> Option<Self> {
let map = unsafe { MmapOptions::new().len(size).map_copy_read_only(&fd).ok()? };
let keymap = unsafe {
let keymap = (XKBH.xkb_keymap_new_from_string)(
(*context).as_ptr(),
map.as_ptr() as *const _,
xkb::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
);
NonNull::new(keymap)?
};
Some(Self::new_inner(keymap, 0))
}
#[cfg(x11_platform)]
pub fn from_x11_keymap(
context: &XkbContext,
xcb: *mut xcb_connection_t,
core_keyboard_id: i32,
) -> Option<Self> {
let keymap = unsafe {
(XKBXH.xkb_x11_keymap_new_from_device)(
context.as_ptr(),
xcb,
core_keyboard_id,
xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
)
};
let keymap = NonNull::new(keymap)?;
Some(Self::new_inner(keymap, core_keyboard_id))
}
fn new_inner(keymap: NonNull<xkb_keymap>, _core_keyboard_id: i32) -> Self {
let mods_indices = ModsIndices {
shift: mod_index_for_name(keymap, xkb::XKB_MOD_NAME_SHIFT),
caps: mod_index_for_name(keymap, xkb::XKB_MOD_NAME_CAPS),
ctrl: mod_index_for_name(keymap, xkb::XKB_MOD_NAME_CTRL),
alt: mod_index_for_name(keymap, xkb::XKB_MOD_NAME_ALT),
num: mod_index_for_name(keymap, xkb::XKB_MOD_NAME_NUM),
mod3: mod_index_for_name(keymap, b"Mod3\0"),
logo: mod_index_for_name(keymap, xkb::XKB_MOD_NAME_LOGO),
mod5: mod_index_for_name(keymap, b"Mod5\0"),
};
Self {
keymap,
_mods_indices: mods_indices,
_core_keyboard_id,
}
}
#[cfg(x11_platform)]
pub fn mods_indices(&self) -> ModsIndices {
self._mods_indices
}
pub fn first_keysym_by_level(
&mut self,
layout: xkb_layout_index_t,
keycode: xkb_keycode_t,
) -> xkb_keysym_t {
unsafe {
let mut keysyms = ptr::null();
let count = (XKBH.xkb_keymap_key_get_syms_by_level)(
self.keymap.as_ptr(),
keycode,
layout,
// NOTE: The level should be zero to ignore modifiers.
0,
&mut keysyms,
);
if count == 1 {
*keysyms
} else {
0
}
}
}
/// Check whether the given key repeats.
pub fn key_repeats(&mut self, keycode: xkb_keycode_t) -> bool {
unsafe { (XKBH.xkb_keymap_key_repeats)(self.keymap.as_ptr(), keycode) == 1 }
}
}
impl Drop for XkbKeymap {
fn drop(&mut self) {
unsafe {
(XKBH.xkb_keymap_unref)(self.keymap.as_ptr());
};
}
}
impl Deref for XkbKeymap {
type Target = NonNull<xkb_keymap>;
fn deref(&self) -> &Self::Target {
&self.keymap
}
}
/// Modifier index in the keymap.
#[derive(Default, Debug, Clone, Copy)]
pub struct ModsIndices {
pub shift: Option<xkb_mod_index_t>,
pub caps: Option<xkb_mod_index_t>,
pub ctrl: Option<xkb_mod_index_t>,
pub alt: Option<xkb_mod_index_t>,
pub num: Option<xkb_mod_index_t>,
pub mod3: Option<xkb_mod_index_t>,
pub logo: Option<xkb_mod_index_t>,
pub mod5: Option<xkb_mod_index_t>,
}
fn mod_index_for_name(keymap: NonNull<xkb_keymap>, name: &[u8]) -> Option<xkb_mod_index_t> {
unsafe {
let mod_index =
(XKBH.xkb_keymap_mod_get_index)(keymap.as_ptr(), name.as_ptr() as *const c_char);
if mod_index == XKB_MOD_INVALID {
None
} else {
Some(mod_index)
}
}
}

View File

@@ -0,0 +1,461 @@
use std::convert::TryInto;
use std::ops::Deref;
use std::os::raw::c_char;
use std::ptr::{self, NonNull};
use std::sync::atomic::{AtomicBool, Ordering};
use log::warn;
use once_cell::sync::Lazy;
use smol_str::SmolStr;
#[cfg(wayland_platform)]
use std::os::unix::io::OwnedFd;
use xkbcommon_dl::{
self as xkb, xkb_compose_status, xkb_context, xkb_context_flags, xkbcommon_compose_handle,
xkbcommon_handle, XkbCommon, XkbCommonCompose,
};
#[cfg(x11_platform)]
use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::xkbcommon_x11_handle};
use crate::event::ElementState;
use crate::event::KeyEvent;
use crate::keyboard::{Key, KeyLocation};
use crate::platform_impl::KeyEventExtra;
mod compose;
mod keymap;
mod state;
use compose::{ComposeStatus, XkbComposeState, XkbComposeTable};
use keymap::XkbKeymap;
#[cfg(x11_platform)]
pub use keymap::raw_keycode_to_physicalkey;
pub use keymap::{physicalkey_to_scancode, scancode_to_keycode};
pub use state::XkbState;
// TODO: Wire this up without using a static `AtomicBool`.
static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false);
static XKBH: Lazy<&'static XkbCommon> = Lazy::new(xkbcommon_handle);
static XKBCH: Lazy<&'static XkbCommonCompose> = Lazy::new(xkbcommon_compose_handle);
#[cfg(feature = "x11")]
static XKBXH: Lazy<&'static xkb::x11::XkbCommonX11> = Lazy::new(xkbcommon_x11_handle);
#[inline(always)]
pub fn reset_dead_keys() {
RESET_DEAD_KEYS.store(true, Ordering::SeqCst);
}
#[derive(Debug)]
pub enum Error {
/// libxkbcommon is not available
XKBNotFound,
}
#[derive(Debug)]
pub struct Context {
// NOTE: field order matters.
#[cfg(x11_platform)]
pub core_keyboard_id: i32,
state: Option<XkbState>,
keymap: Option<XkbKeymap>,
compose_state1: Option<XkbComposeState>,
compose_state2: Option<XkbComposeState>,
_compose_table: Option<XkbComposeTable>,
context: XkbContext,
scratch_buffer: Vec<u8>,
}
impl Context {
pub fn new() -> Result<Self, Error> {
if xkb::xkbcommon_option().is_none() {
return Err(Error::XKBNotFound);
}
let context = XkbContext::new()?;
let mut compose_table = XkbComposeTable::new(&context);
let mut compose_state1 = compose_table.as_ref().and_then(|table| table.new_state());
let mut compose_state2 = compose_table.as_ref().and_then(|table| table.new_state());
// Disable compose if anything compose related failed to initialize.
if compose_table.is_none() || compose_state1.is_none() || compose_state2.is_none() {
compose_state2 = None;
compose_state1 = None;
compose_table = None;
}
Ok(Self {
state: None,
keymap: None,
compose_state1,
compose_state2,
#[cfg(x11_platform)]
core_keyboard_id: 0,
_compose_table: compose_table,
context,
scratch_buffer: Vec::with_capacity(8),
})
}
#[cfg(feature = "x11")]
pub fn from_x11_xkb(xcb: *mut xcb_connection_t) -> Result<Self, Error> {
let result = unsafe {
(XKBXH.xkb_x11_setup_xkb_extension)(
xcb,
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(),
)
};
if result != 1 {
return Err(Error::XKBNotFound);
}
let mut this = Self::new()?;
this.core_keyboard_id = unsafe { (XKBXH.xkb_x11_get_core_keyboard_device_id)(xcb) };
this.set_keymap_from_x11(xcb);
Ok(this)
}
pub fn state_mut(&mut self) -> Option<&mut XkbState> {
self.state.as_mut()
}
pub fn keymap_mut(&mut self) -> Option<&mut XkbKeymap> {
self.keymap.as_mut()
}
#[cfg(wayland_platform)]
pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) {
let keymap = XkbKeymap::from_fd(&self.context, fd, size);
let state = keymap.as_ref().and_then(XkbState::new_wayland);
if keymap.is_none() || state.is_none() {
warn!("failed to update xkb keymap");
}
self.state = state;
self.keymap = keymap;
}
#[cfg(x11_platform)]
pub fn set_keymap_from_x11(&mut self, xcb: *mut xcb_connection_t) {
let keymap = XkbKeymap::from_x11_keymap(&self.context, xcb, self.core_keyboard_id);
let state = keymap
.as_ref()
.and_then(|keymap| XkbState::new_x11(xcb, keymap));
if keymap.is_none() || state.is_none() {
warn!("failed to update xkb keymap");
}
self.state = state;
self.keymap = keymap;
}
/// Key builder context with the user provided xkb state.
pub fn key_context(&mut self) -> Option<KeyContext<'_>> {
let state = self.state.as_mut()?;
let keymap = self.keymap.as_mut()?;
let compose_state1 = self.compose_state1.as_mut();
let compose_state2 = self.compose_state2.as_mut();
let scratch_buffer = &mut self.scratch_buffer;
Some(KeyContext {
state,
keymap,
compose_state1,
compose_state2,
scratch_buffer,
})
}
/// Key builder context with the user provided xkb state.
///
/// Should be used when the original context must not be altered.
#[cfg(x11_platform)]
pub fn key_context_with_state<'a>(
&'a mut self,
state: &'a mut XkbState,
) -> Option<KeyContext<'a>> {
let keymap = self.keymap.as_mut()?;
let compose_state1 = self.compose_state1.as_mut();
let compose_state2 = self.compose_state2.as_mut();
let scratch_buffer = &mut self.scratch_buffer;
Some(KeyContext {
state,
keymap,
compose_state1,
compose_state2,
scratch_buffer,
})
}
}
pub struct KeyContext<'a> {
pub state: &'a mut XkbState,
pub keymap: &'a mut XkbKeymap,
compose_state1: Option<&'a mut XkbComposeState>,
compose_state2: Option<&'a mut XkbComposeState>,
scratch_buffer: &'a mut Vec<u8>,
}
impl<'a> KeyContext<'a> {
pub fn process_key_event(
&mut self,
keycode: u32,
state: ElementState,
repeat: bool,
) -> KeyEvent {
let mut event =
KeyEventResults::new(self, keycode, !repeat && state == ElementState::Pressed);
let physical_key = keymap::raw_keycode_to_physicalkey(keycode);
let (logical_key, location) = event.key();
let text = event.text();
let (key_without_modifiers, _) = event.key_without_modifiers();
let text_with_all_modifiers = event.text_with_all_modifiers();
let platform_specific = KeyEventExtra {
text_with_all_modifiers,
key_without_modifiers,
};
KeyEvent {
physical_key,
logical_key,
text,
location,
state,
repeat,
platform_specific,
}
}
fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option<SmolStr> {
self.scratch_buffer.clear();
self.scratch_buffer.reserve(8);
loop {
let bytes_written = unsafe {
(XKBH.xkb_keysym_to_utf8)(
keysym,
self.scratch_buffer.as_mut_ptr().cast(),
self.scratch_buffer.capacity(),
)
};
if bytes_written == 0 {
return None;
} else if bytes_written == -1 {
self.scratch_buffer.reserve(8);
} else {
unsafe {
self.scratch_buffer
.set_len(bytes_written.try_into().unwrap())
};
break;
}
}
// Remove the null-terminator
self.scratch_buffer.pop();
byte_slice_to_smol_str(self.scratch_buffer)
}
}
struct KeyEventResults<'a, 'b> {
context: &'a mut KeyContext<'b>,
keycode: u32,
keysym: u32,
compose: ComposeStatus,
}
impl<'a, 'b> KeyEventResults<'a, 'b> {
fn new(context: &'a mut KeyContext<'b>, keycode: u32, compose: bool) -> Self {
let keysym = context.state.get_one_sym_raw(keycode);
let compose = if let Some(state) = context.compose_state1.as_mut().filter(|_| compose) {
if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) {
state.reset();
context.compose_state2.as_mut().unwrap().reset();
}
state.feed(keysym)
} else {
ComposeStatus::None
};
KeyEventResults {
context,
keycode,
keysym,
compose,
}
}
pub fn key(&mut self) -> (Key, KeyLocation) {
let (key, location) = match self.keysym_to_key(self.keysym) {
Ok(known) => return known,
Err(undefined) => undefined,
};
if let ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSING) = self.compose {
let compose_state = self.context.compose_state2.as_mut().unwrap();
// 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.
compose_state.feed(self.keysym);
if matches!(compose_state.feed(self.keysym), ComposeStatus::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`.
let text = compose_state.get_string(self.context.scratch_buffer);
let key = Key::Dead(text.and_then(|s| s.chars().next()));
(key, location)
} else {
(key, location)
}
} else {
let key = self
.composed_text()
.unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym))
.map(Key::Character)
.unwrap_or(key);
(key, location)
}
}
pub fn key_without_modifiers(&mut self) -> (Key, KeyLocation) {
// This will become a pointer to an array which libxkbcommon owns, so we don't need to deallocate it.
let layout = self.context.state.layout(self.keycode);
let keysym = self
.context
.keymap
.first_keysym_by_level(layout, self.keycode);
match self.keysym_to_key(keysym) {
Ok((key, location)) => (key, location),
Err((key, location)) => {
let key = self
.context
.keysym_to_utf8_raw(keysym)
.map(Key::Character)
.unwrap_or(key);
(key, location)
}
}
}
fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> {
let location = keymap::keysym_location(keysym);
let key = keymap::keysym_to_key(keysym);
if matches!(key, Key::Unidentified(_)) {
Err((key, location))
} else {
Ok((key, location))
}
}
pub fn text(&mut self) -> Option<SmolStr> {
self.composed_text()
.unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym))
}
// 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.
pub fn text_with_all_modifiers(&mut self) -> Option<SmolStr> {
match self.composed_text() {
Ok(text) => text,
Err(_) => self
.context
.state
.get_utf8_raw(self.keycode, self.context.scratch_buffer),
}
}
fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> {
match self.compose {
ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSED) => {
let state = self.context.compose_state1.as_mut().unwrap();
Ok(state.get_string(self.context.scratch_buffer))
}
_ => Err(()),
}
}
}
#[derive(Debug)]
pub struct XkbContext {
context: NonNull<xkb_context>,
}
impl XkbContext {
pub fn new() -> Result<Self, Error> {
let context = unsafe { (XKBH.xkb_context_new)(xkb_context_flags::XKB_CONTEXT_NO_FLAGS) };
let context = match NonNull::new(context) {
Some(context) => context,
None => return Err(Error::XKBNotFound),
};
Ok(Self { context })
}
}
impl Drop for XkbContext {
fn drop(&mut self) {
unsafe {
(XKBH.xkb_context_unref)(self.context.as_ptr());
}
}
}
impl Deref for XkbContext {
type Target = NonNull<xkb_context>;
fn deref(&self) -> &Self::Target {
&self.context
}
}
/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and
/// `xkb_state_key_get_utf8`.
fn make_string_with<F>(scratch_buffer: &mut Vec<u8>, mut f: F) -> Option<SmolStr>
where
F: FnMut(*mut c_char, usize) -> i32,
{
let size = f(ptr::null_mut(), 0);
if size == 0 {
return None;
}
let size = usize::try_from(size).unwrap();
scratch_buffer.clear();
// The allocated buffer must include space for the null-terminator.
scratch_buffer.reserve(size + 1);
unsafe {
let written = f(
scratch_buffer.as_mut_ptr().cast(),
scratch_buffer.capacity(),
);
if usize::try_from(written).unwrap() != size {
// This will likely never happen.
return None;
}
scratch_buffer.set_len(size);
};
byte_slice_to_smol_str(scratch_buffer)
}
// NOTE: This is track_caller so we can have more informative line numbers when logging
#[track_caller]
fn byte_slice_to_smol_str(bytes: &[u8]) -> Option<SmolStr> {
std::str::from_utf8(bytes)
.map(SmolStr::new)
.map_err(|e| {
log::warn!(
"UTF-8 received from libxkbcommon ({:?}) was invalid: {e}",
bytes
)
})
.ok()
}

View File

@@ -0,0 +1,189 @@
//! XKB state.
use std::os::raw::c_char;
use std::ptr::NonNull;
use smol_str::SmolStr;
#[cfg(x11_platform)]
use x11_dl::xlib_xcb::xcb_connection_t;
use xkbcommon_dl::{
self as xkb, xkb_keycode_t, xkb_keysym_t, xkb_layout_index_t, xkb_state, xkb_state_component,
};
use crate::platform_impl::common::xkb::keymap::XkbKeymap;
#[cfg(x11_platform)]
use crate::platform_impl::common::xkb::XKBXH;
use crate::platform_impl::common::xkb::{make_string_with, XKBH};
#[derive(Debug)]
pub struct XkbState {
state: NonNull<xkb_state>,
modifiers: ModifiersState,
}
impl XkbState {
#[cfg(wayland_platform)]
pub fn new_wayland(keymap: &XkbKeymap) -> Option<Self> {
let state = NonNull::new(unsafe { (XKBH.xkb_state_new)(keymap.as_ptr()) })?;
Some(Self::new_inner(state))
}
#[cfg(x11_platform)]
pub fn new_x11(xcb: *mut xcb_connection_t, keymap: &XkbKeymap) -> Option<Self> {
let state = unsafe {
(XKBXH.xkb_x11_state_new_from_device)(keymap.as_ptr(), xcb, keymap._core_keyboard_id)
};
let state = NonNull::new(state)?;
Some(Self::new_inner(state))
}
fn new_inner(state: NonNull<xkb_state>) -> Self {
let modifiers = ModifiersState::default();
let mut this = Self { state, modifiers };
this.reload_modifiers();
this
}
pub fn get_one_sym_raw(&mut self, keycode: xkb_keycode_t) -> xkb_keysym_t {
unsafe { (XKBH.xkb_state_key_get_one_sym)(self.state.as_ptr(), keycode) }
}
pub fn layout(&mut self, key: xkb_keycode_t) -> xkb_layout_index_t {
unsafe { (XKBH.xkb_state_key_get_layout)(self.state.as_ptr(), key) }
}
#[cfg(x11_platform)]
pub fn depressed_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
unsafe {
(XKBH.xkb_state_serialize_mods)(
self.state.as_ptr(),
xkb_state_component::XKB_STATE_MODS_DEPRESSED,
)
}
}
#[cfg(x11_platform)]
pub fn latched_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
unsafe {
(XKBH.xkb_state_serialize_mods)(
self.state.as_ptr(),
xkb_state_component::XKB_STATE_MODS_LATCHED,
)
}
}
#[cfg(x11_platform)]
pub fn locked_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
unsafe {
(XKBH.xkb_state_serialize_mods)(
self.state.as_ptr(),
xkb_state_component::XKB_STATE_MODS_LOCKED,
)
}
}
pub fn get_utf8_raw(
&mut self,
keycode: xkb_keycode_t,
scratch_buffer: &mut Vec<u8>,
) -> Option<SmolStr> {
make_string_with(scratch_buffer, |ptr, len| unsafe {
(XKBH.xkb_state_key_get_utf8)(self.state.as_ptr(), keycode, ptr, len)
})
}
pub fn modifiers(&self) -> ModifiersState {
self.modifiers
}
pub fn update_modifiers(
&mut self,
mods_depressed: u32,
mods_latched: u32,
mods_locked: u32,
depressed_group: u32,
latched_group: u32,
locked_group: u32,
) {
let mask = unsafe {
(XKBH.xkb_state_update_mask)(
self.state.as_ptr(),
mods_depressed,
mods_latched,
mods_locked,
depressed_group,
latched_group,
locked_group,
)
};
if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) {
// Effective value of mods have changed, we need to update our state.
self.reload_modifiers();
}
}
/// Reload the modifiers.
fn reload_modifiers(&mut self) {
self.modifiers.ctrl = self.mod_name_is_active(xkb::XKB_MOD_NAME_CTRL);
self.modifiers.alt = self.mod_name_is_active(xkb::XKB_MOD_NAME_ALT);
self.modifiers.shift = self.mod_name_is_active(xkb::XKB_MOD_NAME_SHIFT);
self.modifiers.caps_lock = self.mod_name_is_active(xkb::XKB_MOD_NAME_CAPS);
self.modifiers.logo = self.mod_name_is_active(xkb::XKB_MOD_NAME_LOGO);
self.modifiers.num_lock = self.mod_name_is_active(xkb::XKB_MOD_NAME_NUM);
}
/// Check if the modifier is active within xkb.
fn mod_name_is_active(&mut self, name: &[u8]) -> bool {
unsafe {
(XKBH.xkb_state_mod_name_is_active)(
self.state.as_ptr(),
name.as_ptr() as *const c_char,
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
) > 0
}
}
}
impl Drop for XkbState {
fn drop(&mut self) {
unsafe {
(XKBH.xkb_state_unref)(self.state.as_ptr());
}
}
}
/// Represents the current state of the keyboard modifiers
///
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
///
/// For some modifiers, this means that the key is currently pressed, others are toggled
/// (like caps lock).
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct ModifiersState {
/// The "control" key
pub ctrl: bool,
/// The "alt" key
pub alt: bool,
/// The "shift" key
pub shift: bool,
/// The "Caps lock" key
pub caps_lock: bool,
/// The "logo" key
///
/// Also known as the "windows" key on most keyboards
pub logo: bool,
/// The "Num lock" key
pub num_lock: bool,
}
impl From<ModifiersState> for crate::keyboard::ModifiersState {
fn from(mods: ModifiersState) -> crate::keyboard::ModifiersState {
let mut to_mods = crate::keyboard::ModifiersState::empty();
to_mods.set(crate::keyboard::ModifiersState::SHIFT, mods.shift);
to_mods.set(crate::keyboard::ModifiersState::CONTROL, mods.ctrl);
to_mods.set(crate::keyboard::ModifiersState::ALT, mods.alt);
to_mods.set(crate::keyboard::ModifiersState::SUPER, mods.logo);
to_mods
}
}

View File

@@ -521,7 +521,7 @@ impl Window {
#[inline]
pub fn reset_dead_keys(&self) {
common::xkb_state::reset_dead_keys()
common::xkb::reset_dead_keys()
}
#[inline]
@@ -659,11 +659,11 @@ impl KeyEventExtModifierSupplement for KeyEvent {
impl PhysicalKeyExtScancode for PhysicalKey {
fn from_scancode(scancode: u32) -> PhysicalKey {
common::keymap::scancode_to_keycode(scancode)
common::xkb::scancode_to_keycode(scancode)
}
fn to_scancode(self) -> Option<u32> {
common::keymap::physicalkey_to_scancode(self)
common::xkb::physicalkey_to_scancode(self)
}
}

View File

@@ -17,7 +17,7 @@ use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
use crate::event::{ElementState, WindowEvent};
use crate::keyboard::ModifiersState;
use crate::platform_impl::common::xkb_state::KbdState;
use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::seat::WinitSeatState;
use crate::platform_impl::wayland::state::WinitState;
@@ -43,14 +43,10 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
WlKeymapFormat::NoKeymap => {
warn!("non-xkb compatible keymap")
}
WlKeymapFormat::XkbV1 => unsafe {
seat_state
.keyboard_state
.as_mut()
.unwrap()
.xkb_state
.init_with_fd(fd, size as usize);
},
WlKeymapFormat::XkbV1 => {
let context = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_context;
context.set_keymap_from_fd(fd, size as usize);
}
_ => unreachable!(),
},
WEnum::Unknown(value) => {
@@ -155,7 +151,12 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
RepeatInfo::Disable => return,
};
if !keyboard_state.xkb_state.key_repeats(key) {
if !keyboard_state
.xkb_context
.keymap_mut()
.unwrap()
.key_repeats(key)
{
return;
}
@@ -221,7 +222,11 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
if keyboard_state.repeat_info != RepeatInfo::Disable
&& keyboard_state.xkb_state.key_repeats(key)
&& keyboard_state
.xkb_context
.keymap_mut()
.unwrap()
.key_repeats(key)
&& Some(key) == keyboard_state.current_repeat
{
keyboard_state.current_repeat = None;
@@ -237,9 +242,14 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
group,
..
} => {
let xkb_state = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_state;
let xkb_context = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_context;
let xkb_state = match xkb_context.state_mut() {
Some(state) => state,
None => return,
};
xkb_state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group);
seat_state.modifiers = xkb_state.mods_state().into();
seat_state.modifiers = xkb_state.modifiers().into();
// HACK: part of the workaround from `WlKeyboardEvent::Enter`.
let window_id = match *data.window_id.lock().unwrap() {
@@ -285,7 +295,7 @@ pub struct KeyboardState {
pub loop_handle: LoopHandle<'static, WinitState>,
/// The state of the keyboard.
pub xkb_state: KbdState,
pub xkb_context: Context,
/// The information about the repeat rate obtained from the compositor.
pub repeat_info: RepeatInfo,
@@ -302,7 +312,7 @@ impl KeyboardState {
Self {
keyboard,
loop_handle,
xkb_state: KbdState::new().unwrap(),
xkb_context: Context::new().unwrap(),
repeat_info: RepeatInfo::default(),
repeat_token: None,
current_repeat: None,
@@ -385,16 +395,13 @@ fn key_input(
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
let event = keyboard_state
.xkb_state
.process_key_event(keycode, state, repeat);
event_sink.push_window_event(
WindowEvent::KeyboardInput {
if let Some(mut key_context) = keyboard_state.xkb_context.key_context() {
let event = key_context.process_key_event(keycode, state, repeat);
let event = WindowEvent::KeyboardInput {
device_id,
event,
is_synthetic: false,
},
window_id,
);
};
event_sink.push_window_event(event, window_id);
}
}

View File

@@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex};
use x11_dl::xinput2::{
self, XIDeviceEvent, XIEnterEvent, XIFocusInEvent, XIFocusOutEvent, XIHierarchyEvent,
XILeaveEvent, XIRawEvent,
XILeaveEvent, XIModifierState, XIRawEvent,
};
use x11_dl::xlib::{
self, Display as XDisplay, Window as XWindow, XAnyEvent, XClientMessageEvent, XConfigureEvent,
@@ -14,9 +14,10 @@ use x11_dl::xlib::{
XReparentEvent, XSelectionEvent, XVisibilityEvent, XkbAnyEvent,
};
use x11rb::protocol::xinput;
use x11rb::protocol::xproto::{self, ConnectionExt as _};
use x11rb::protocol::xproto::{self, ConnectionExt as _, ModMask};
use x11rb::x11_utils::ExtensionInformation;
use x11rb::x11_utils::Serialize;
use xkbcommon_dl::xkb_mod_mask_t;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event::{
@@ -26,7 +27,8 @@ use crate::event::{
use crate::event::{InnerSizeWriter, MouseButton};
use crate::event_loop::EventLoopWindowTarget as RootELW;
use crate::keyboard::ModifiersState;
use crate::platform_impl::platform::common::{keymap, xkb_state::KbdState};
use crate::platform_impl::common::xkb::{self, XkbState};
use crate::platform_impl::platform::common::xkb::Context;
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
use crate::platform_impl::platform::x11::EventLoopWindowTarget;
use crate::platform_impl::platform::EventLoopWindowTarget as PlatformEventLoopWindowTarget;
@@ -47,7 +49,7 @@ pub struct EventProcessor<T: 'static> {
pub xi2ext: ExtensionInformation,
pub xkbext: ExtensionInformation,
pub target: RootELW<T>,
pub kb_state: KbdState,
pub xkb_context: Context,
// Number of touch events currently in progress
pub num_touch: u32,
// This is the last pressed key that is repeatable (if it hasn't been
@@ -178,10 +180,12 @@ impl<T: 'static> EventProcessor<T> {
};
let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
self.update_mods_from_xinput2_event(&xev.mods, &xev.group, &mut callback);
self.xinput2_button_input(xev, state, &mut callback);
}
xinput2::XI_Motion => {
let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
self.update_mods_from_xinput2_event(&xev.mods, &xev.group, &mut callback);
self.xinput2_mouse_motion(xev, &mut callback);
}
xinput2::XI_Enter => {
@@ -190,6 +194,7 @@ impl<T: 'static> EventProcessor<T> {
}
xinput2::XI_Leave => {
let xev: &XILeaveEvent = unsafe { &*(xev.data as *const _) };
self.update_mods_from_xinput2_event(&xev.mods, &xev.group, &mut callback);
self.xinput2_mouse_left(xev, &mut callback);
}
xinput2::XI_FocusIn => {
@@ -891,7 +896,12 @@ impl<T: 'static> EventProcessor<T> {
// Only keys that can repeat should change the held_key_press state since a
// continuously held repeatable key may continue repeating after the press of a
// non-repeatable key.
let repeat = if self.kb_state.key_repeats(keycode) {
let key_repeats = self
.xkb_context
.keymap_mut()
.map(|k| k.key_repeats(keycode))
.unwrap_or(false);
let repeat = if key_repeats {
let is_latest_held = self.held_key_press == Some(keycode);
if state == ElementState::Pressed {
@@ -910,20 +920,29 @@ impl<T: 'static> EventProcessor<T> {
false
};
// Always update the modifiers.
self.udpate_mods_from_core_event(xev.state as u16, &mut callback);
if keycode != 0 && !self.is_composing {
let event = self.kb_state.process_key_event(keycode, state, repeat);
callback(
&self.target,
Event::WindowEvent {
if let Some(mut key_processor) = self.xkb_context.key_context() {
let event = key_processor.process_key_event(keycode, state, repeat);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {
device_id,
event,
is_synthetic: false,
},
},
);
} else if let Some(ic) = wt
};
callback(&self.target, event);
}
return;
}
let wt = Self::window_target(&self.target);
if let Some(ic) = wt
.ime
.as_ref()
.and_then(|ime| ime.borrow().get_context(window as XWindow))
@@ -1219,8 +1238,19 @@ impl<T: 'static> EventProcessor<T> {
};
callback(&self.target, event);
let modifiers: crate::keyboard::ModifiersState = self.kb_state.mods_state().into();
self.send_modifiers(modifiers, &mut callback);
// Issue key press events for all pressed keys
Self::handle_pressed_keys(
&self.target,
window_id,
ElementState::Pressed,
&mut self.xkb_context,
&mut callback,
);
if let Some(state) = self.xkb_context.state_mut() {
let mods = state.modifiers().into();
self.send_modifiers(mods, &mut callback);
}
// The deviceid for this event is for a keyboard instead of a pointer,
// so we have to do a little extra work.
@@ -1239,15 +1269,6 @@ impl<T: 'static> EventProcessor<T> {
},
};
callback(&self.target, event);
// Issue key press events for all pressed keys
Self::handle_pressed_keys(
&self.target,
window_id,
ElementState::Pressed,
&mut self.kb_state,
&mut callback,
);
}
fn xinput2_unfocused<F>(&mut self, xev: &XIFocusOutEvent, mut callback: F)
@@ -1275,12 +1296,14 @@ impl<T: 'static> EventProcessor<T> {
wt.update_listen_device_events(false);
self.send_modifiers(ModifiersState::empty(), &mut callback);
// Issue key release events for all pressed keys
Self::handle_pressed_keys(
&self.target,
window_id,
ElementState::Released,
&mut self.kb_state,
&mut self.xkb_context,
&mut callback,
);
@@ -1288,8 +1311,6 @@ impl<T: 'static> EventProcessor<T> {
// window regains focus.
self.held_key_press = None;
self.send_modifiers(ModifiersState::empty(), &mut callback);
if let Some(window) = self.with_window(window, Arc::clone) {
window.shared_state_lock().has_focus = false;
}
@@ -1442,7 +1463,7 @@ impl<T: 'static> EventProcessor<T> {
if keycode < KEYCODE_OFFSET as u32 {
return;
}
let physical_key = keymap::raw_keycode_to_physicalkey(keycode);
let physical_key = xkb::raw_keycode_to_physicalkey(keycode);
callback(
&self.target,
@@ -1507,17 +1528,25 @@ impl<T: 'static> EventProcessor<T> {
let keycodes_changed = util::has_flag(xev.changed, keycodes_changed_flag);
let geometry_changed = util::has_flag(xev.changed, geometry_changed_flag);
if xev.device == self.kb_state.core_keyboard_id
if xev.device == self.xkb_context.core_keyboard_id
&& (keycodes_changed || geometry_changed)
{
unsafe { self.kb_state.init_with_x11_keymap() };
let modifiers = self.kb_state.mods_state();
self.send_modifiers(modifiers.into(), &mut callback);
let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection();
self.xkb_context.set_keymap_from_x11(xcb);
if let Some(state) = self.xkb_context.state_mut() {
let mods = state.modifiers().into();
self.send_modifiers(mods, &mut callback);
}
}
}
xlib::XkbMapNotify => {
unsafe { self.kb_state.init_with_x11_keymap() };
self.send_modifiers(self.kb_state.mods_state().into(), &mut callback);
let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection();
self.xkb_context.set_keymap_from_x11(xcb);
if let Some(state) = self.xkb_context.state_mut() {
let mods = state.modifiers().into();
self.send_modifiers(mods, &mut callback);
}
}
xlib::XkbStateNotify => {
let xev = unsafe { &*(xev as *const _ as *const xlib::XkbStateNotifyEvent) };
@@ -1525,24 +1554,122 @@ impl<T: 'static> EventProcessor<T> {
// Set the timestamp.
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
// NOTE: Modifiers could update without a prior event updating them,
// thus diffing the state before and after is not reliable.
self.kb_state.update_modifiers(
xev.base_mods,
xev.latched_mods,
xev.locked_mods,
xev.base_group as u32,
xev.latched_group as u32,
xev.locked_group as u32,
);
self.send_modifiers(self.kb_state.mods_state().into(), &mut callback);
if let Some(state) = self.xkb_context.state_mut() {
state.update_modifiers(
xev.base_mods,
xev.latched_mods,
xev.locked_mods,
xev.base_group as u32,
xev.latched_group as u32,
xev.locked_group as u32,
);
let mods = state.modifiers().into();
self.send_modifiers(mods, &mut callback);
}
}
_ => {}
}
}
pub fn update_mods_from_xinput2_event<F>(
&mut self,
mods: &XIModifierState,
group: &XIModifierState,
mut callback: F,
) where
F: FnMut(&RootELW<T>, Event<T>),
{
if let Some(state) = self.xkb_context.state_mut() {
state.update_modifiers(
mods.base as u32,
mods.latched as u32,
mods.locked as u32,
group.base as u32,
group.latched as u32,
group.locked as u32,
);
let mods = state.modifiers();
self.send_modifiers(mods.into(), &mut callback);
}
}
pub fn udpate_mods_from_core_event<F>(&mut self, state: u16, mut callback: F)
where
F: FnMut(&RootELW<T>, Event<T>),
{
let xkb_mask = self.xkb_mod_mask_from_core(state);
let xkb_state = match self.xkb_context.state_mut() {
Some(xkb_state) => xkb_state,
None => return,
};
// NOTE: this is inspired by Qt impl.
let mut depressed = xkb_state.depressed_modifiers() & xkb_mask;
let latched = xkb_state.latched_modifiers() & xkb_mask;
let locked = xkb_state.locked_modifiers() & xkb_mask;
// Set modifiers in depressed if they don't appear in any of the final masks.
depressed |= !(depressed | latched | locked) & xkb_mask;
xkb_state.update_modifiers(
depressed,
latched,
locked,
0,
0,
// Bits 13 and 14 report the state keyboard group.
((state >> 13) & 3) as u32,
);
let mods = xkb_state.modifiers();
self.send_modifiers(mods.into(), &mut callback);
}
pub fn xkb_mod_mask_from_core(&mut self, state: u16) -> xkb_mod_mask_t {
let mods_indices = match self.xkb_context.keymap_mut() {
Some(keymap) => keymap.mods_indices(),
None => return 0,
};
// Build the XKB modifiers from the regular state.
let mut depressed = 0u32;
if let Some(shift) = mods_indices
.shift
.filter(|_| ModMask::SHIFT.intersects(state))
{
depressed |= 1 << shift;
}
if let Some(caps) = mods_indices
.caps
.filter(|_| ModMask::LOCK.intersects(state))
{
depressed |= 1 << caps;
}
if let Some(ctrl) = mods_indices
.ctrl
.filter(|_| ModMask::CONTROL.intersects(state))
{
depressed |= 1 << ctrl;
}
if let Some(alt) = mods_indices.alt.filter(|_| ModMask::M1.intersects(state)) {
depressed |= 1 << alt;
}
if let Some(num) = mods_indices.num.filter(|_| ModMask::M2.intersects(state)) {
depressed |= 1 << num;
}
if let Some(mod3) = mods_indices.mod3.filter(|_| ModMask::M3.intersects(state)) {
depressed |= 1 << mod3;
}
if let Some(logo) = mods_indices.logo.filter(|_| ModMask::M4.intersects(state)) {
depressed |= 1 << logo;
}
if let Some(mod5) = mods_indices.mod5.filter(|_| ModMask::M5.intersects(state)) {
depressed |= 1 << mod5;
}
depressed
}
/// Send modifiers for the active window.
///
/// The event won't be send when the `modifiers` match the previosly `sent` modifiers value.
@@ -1571,7 +1698,7 @@ impl<T: 'static> EventProcessor<T> {
target: &RootELW<T>,
window_id: crate::window::WindowId,
state: ElementState,
kb_state: &mut KbdState,
xkb_context: &mut Context,
callback: &mut F,
) where
F: FnMut(&RootELW<T>, Event<T>),
@@ -1580,14 +1707,33 @@ impl<T: 'static> EventProcessor<T> {
// Update modifiers state and emit key events based on which keys are currently pressed.
let window_target = Self::window_target(target);
let xcb = window_target
.xconn
.xcb_connection()
.get_raw_xcb_connection();
let keymap = match xkb_context.keymap_mut() {
Some(keymap) => keymap,
None => return,
};
// Send the keys using the sythetic state to not alter the main state.
let mut xkb_state = match XkbState::new_x11(xcb, keymap) {
Some(xkb_state) => xkb_state,
None => return,
};
let mut key_processor = match xkb_context.key_context_with_state(&mut xkb_state) {
Some(key_processor) => key_processor,
None => return,
};
for keycode in window_target
.xconn
.query_keymap()
.into_iter()
.filter(|k| *k >= KEYCODE_OFFSET)
{
let keycode = keycode as u32;
let event = kb_state.process_key_event(keycode, state, false);
let event = key_processor.process_key_event(keycode as u32, state, false);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {

View File

@@ -30,12 +30,13 @@ use x11rb::protocol::xproto::{self, ConnectionExt as _};
use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::xcb_ffi::ReplyOrIdError;
use super::{common::xkb_state::KbdState, ControlFlow, OsError};
use super::{ControlFlow, OsError};
use crate::{
error::{EventLoopError, OsError as RootOsError},
event::{Event, StartCause, WindowEvent},
event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus,
platform_impl::common::xkb::Context,
platform_impl::{
platform::{min_timeout, WindowId},
PlatformSpecificWindowBuilderAttributes,
@@ -285,8 +286,8 @@ impl<T: 'static> EventLoop<T> {
// Create a channel for sending user events.
let (user_sender, user_channel) = mpsc::channel();
let kb_state =
KbdState::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
let xkb_context =
Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
let window_target = EventLoopWindowTarget {
ime,
@@ -327,7 +328,7 @@ impl<T: 'static> EventLoop<T> {
ime_event_receiver,
xi2ext,
xkbext,
kb_state,
xkb_context,
num_touch: 0,
held_key_press: None,
first_touch: None,