diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 67e321b4..d8f076be 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -31,6 +31,8 @@ pub(super) struct EventProcessor { pub(super) kb_state: KbdState, // Number of touch events currently in progress 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, // Currently focused window belonging to this process pub(super) active_window: Option, @@ -535,34 +537,54 @@ impl EventProcessor { } // 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 window = xkev.window; - let window_id = mkwid(window); - - let written = if let Some(ic) = wt.ime.borrow().get_context(window) { - wt.xconn.lookup_utf8(ic, xkev) - } else { - return; + let window = match self.active_window { + Some(window) => window, + None => return, }; - // If we're composing right now, send the string we've got from X11 via - // Ime::Commit. - if self.is_composing && xkev.keycode == 0 && !written.is_empty() { - let event = Event::WindowEvent { - window_id, - event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), - }; - callback(event); + let window_id = mkwid(window); + let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); - let event = Event::WindowEvent { - window_id, - event: WindowEvent::Ime(Ime::Commit(written)), - }; + 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 + }; - self.is_composing = false; - callback(event); + 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 { + window_id, + event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), + }; + callback(event); + + let event = Event::WindowEvent { + window_id, + event: WindowEvent::Ime(Ime::Commit(written)), + }; + + self.is_composing = false; + callback(event); + } } } @@ -1029,40 +1051,6 @@ 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 !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 => { let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index e0e166cb..4400b36b 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -188,6 +188,15 @@ impl EventLoop { 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 }; @@ -283,6 +292,7 @@ impl EventLoop { first_touch: None, active_window: None, is_composing: false, + got_key_release: true, }; // Register for device hotplug events diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 550b612c..e64d4178 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -245,6 +245,7 @@ impl UnownedWindow { | ffi::StructureNotifyMask | ffi::VisibilityChangeMask | ffi::KeyPressMask + | ffi::KeyReleaseMask | ffi::KeymapStateMask | ffi::ButtonPressMask | ffi::ButtonReleaseMask @@ -446,8 +447,6 @@ impl UnownedWindow { let mask = ffi::XI_MotionMask | ffi::XI_ButtonPressMask | ffi::XI_ButtonReleaseMask - | ffi::XI_KeyPressMask - | ffi::XI_KeyReleaseMask | ffi::XI_EnterMask | ffi::XI_LeaveMask | ffi::XI_FocusInMask diff --git a/src/window.rs b/src/window.rs index 1b370cd1..c61b5d99 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1074,6 +1074,7 @@ impl Window { /// /// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are combined. /// - **iOS / Android / Web / Orbital:** Unsupported. + /// - **X11**: Enabling IME will disable dead keys reporting during compose. /// /// [`Ime`]: crate::event::WindowEvent::Ime /// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput