On X11, fix IME not working

The change to xinput2 completely disabled IME support, thus we've got
a dead keys reporting, because nothing was eating the key events
anymore, however that's not what we really need, given that not
working IME makes it impossible for some users to type.

The proper solution is to not use Xlib at all for that and rely on
xcb and its tooling around the XIM and text compose stuff, so
we'll have full control over what is getting sent to the XIM/IC or not.

Fixes #2888.
This commit is contained in:
Kirill Chibisov 2023-06-30 19:59:24 +04:00 committed by GitHub
parent b0106898f7
commit a320702a71
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 59 deletions

View file

@ -31,6 +31,8 @@ pub(super) struct EventProcessor<T: 'static> {
pub(super) kb_state: KbdState, pub(super) kb_state: KbdState,
// Number of touch events currently in progress // Number of touch events currently in progress
pub(super) num_touch: u32, pub(super) num_touch: u32,
// Whether we've got a key release for the key press.
pub(super) got_key_release: bool,
pub(super) first_touch: Option<u64>, pub(super) first_touch: Option<u64>,
// Currently focused window belonging to this process // Currently focused window belonging to this process
pub(super) active_window: Option<ffi::Window>, pub(super) active_window: Option<ffi::Window>,
@ -535,21 +537,40 @@ impl<T: 'static> EventProcessor<T> {
} }
// Note that in compose/pre-edit sequences, we'll always receive KeyRelease events // Note that in compose/pre-edit sequences, we'll always receive KeyRelease events
ffi::KeyPress => { ty @ ffi::KeyPress | ty @ ffi::KeyRelease => {
let xkev: &mut ffi::XKeyEvent = xev.as_mut(); let xkev: &mut ffi::XKeyEvent = xev.as_mut();
let window = match self.active_window {
let window = xkev.window; Some(window) => window,
let window_id = mkwid(window); None => return,
let written = if let Some(ic) = wt.ime.borrow().get_context(window) {
wt.xconn.lookup_utf8(ic, xkev)
} else {
return;
}; };
// If we're composing right now, send the string we've got from X11 via let window_id = mkwid(window);
// Ime::Commit. let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
if self.is_composing && xkev.keycode == 0 && !written.is_empty() {
let keycode = xkev.keycode as _;
let repeat = ty == ffi::KeyPress && !self.got_key_release;
// Update state after the repeat setting.
let state = if ty == ffi::KeyPress {
self.got_key_release = false;
ElementState::Pressed
} else {
self.got_key_release = true;
ElementState::Released
};
if keycode != 0 && !self.is_composing {
let event = self.kb_state.process_key_event(keycode, state, repeat);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {
device_id,
event,
is_synthetic: false,
},
});
} else if let Some(ic) = wt.ime.borrow().get_context(window) {
let written = wt.xconn.lookup_utf8(ic, xkev);
if !written.is_empty() {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
@ -565,6 +586,7 @@ impl<T: 'static> EventProcessor<T> {
callback(event); callback(event);
} }
} }
}
ffi::GenericEvent => { ffi::GenericEvent => {
let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) {
@ -1029,40 +1051,6 @@ impl<T: 'static> EventProcessor<T> {
}); });
} }
} }
// 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 !self.is_composing => {
if let Some(active_window) = self.active_window {
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 repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat;
let event = self.kb_state.process_key_event(keycode, state, repeat);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {
device_id,
event,
is_synthetic: false,
},
});
}
}
ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => { ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };

View file

@ -188,6 +188,15 @@ impl<T: 'static> EventLoop<T> {
panic!("X server missing XKB extension"); panic!("X server missing XKB extension");
} }
// Enable detectable auto repeat.
let mut supported = 0;
unsafe {
(xconn.xlib.XkbSetDetectableAutoRepeat)(xconn.display, 1, &mut supported);
}
if supported == 0 {
warn!("Detectable auto repeart is not supported");
}
ext ext
}; };
@ -283,6 +292,7 @@ impl<T: 'static> EventLoop<T> {
first_touch: None, first_touch: None,
active_window: None, active_window: None,
is_composing: false, is_composing: false,
got_key_release: true,
}; };
// Register for device hotplug events // Register for device hotplug events

View file

@ -245,6 +245,7 @@ impl UnownedWindow {
| ffi::StructureNotifyMask | ffi::StructureNotifyMask
| ffi::VisibilityChangeMask | ffi::VisibilityChangeMask
| ffi::KeyPressMask | ffi::KeyPressMask
| ffi::KeyReleaseMask
| ffi::KeymapStateMask | ffi::KeymapStateMask
| ffi::ButtonPressMask | ffi::ButtonPressMask
| ffi::ButtonReleaseMask | ffi::ButtonReleaseMask
@ -446,8 +447,6 @@ impl UnownedWindow {
let mask = ffi::XI_MotionMask let mask = ffi::XI_MotionMask
| ffi::XI_ButtonPressMask | ffi::XI_ButtonPressMask
| ffi::XI_ButtonReleaseMask | ffi::XI_ButtonReleaseMask
| ffi::XI_KeyPressMask
| ffi::XI_KeyReleaseMask
| ffi::XI_EnterMask | ffi::XI_EnterMask
| ffi::XI_LeaveMask | ffi::XI_LeaveMask
| ffi::XI_FocusInMask | ffi::XI_FocusInMask

View file

@ -1074,6 +1074,7 @@ impl Window {
/// ///
/// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are combined. /// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are combined.
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
/// - **X11**: Enabling IME will disable dead keys reporting during compose.
/// ///
/// [`Ime`]: crate::event::WindowEvent::Ime /// [`Ime`]: crate::event::WindowEvent::Ime
/// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput /// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput