public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] rawhide: Fix CapsLock between focus changes in Wayland
@ 2026-07-19 10:41 Takao Fujiwara
0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-07-19 10:41 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/ibus
Branch : rawhide
Commit : c99a155c56a1bc30b53cf1a5f01c18a500357ff9
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date : 2026-07-19T19:40:09+09:00
Stats : +1847/-1568 in 3 file(s)
URL : https://src.fedoraproject.org/rpms/ibus/c/c99a155c56a1bc30b53cf1a5f01c18a500357ff9?branch=rawhide
Log:
Fix CapsLock between focus changes in Wayland
- Merge ibus-2444009-wayland-xkb-lv-tilde.patch
- Fix typo in ibus-compose
---
diff --git a/ibus-2444009-wayland-xkb-lv-tilde.patch b/ibus-2444009-wayland-xkb-lv-tilde.patch
deleted file mode 100644
index 4071cac..0000000
--- a/ibus-2444009-wayland-xkb-lv-tilde.patch
+++ /dev/null
@@ -1,1565 +0,0 @@
-From 1686cb59c17744b07d8afefed0bda97d6c4930b8 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 17 Jun 2026 19:49:31 +0900
-Subject: [PATCH 1/4] client/wayland: Fix latch key in Latvian(tilde) keymap
-
-"lv(tilde)" keymap has `ISO_Level3_Latch` and there are several issues.
-1. KDE Wayland does not handle `ISO_Level3_Latch` keysym but outputs the
- tilde even if the system keymap is "lv(tilde)". The previous change
- 0104486 fixes that issue in IBus side.
-2. There is a race when @IBusWaylandIM receives the system keymap from
- the Wayland compositor and the user keymap from the IBus XKB
- engine and the user keymap should not be overridden by the system one.
-3. @IBusWaylandIM can handle the compose keys only if `ISO_Level3_Latch`
- is held pressing at present. But the latch state should be held when
- the latch key is released but cleared when other keys are pressed.
-4. In case the system keymap is not "lv(tilde)", E.g. "us", the release
- event of the virtual `ISO_Level3_Latch` key, the Wayland compositor
- immediately sends to zero modifier state to clear the latch state.
- So probably @IBusWaylandIM should not send the release event of the
- latch key.
-5. XKB group ID can be switched by XKB option key likes `grp:toggle`
- but clicking keyboard icon in KDE panel does not work and maybe
- it's a bug in KDE compositor.
-6. Fix to clear modifiers with the key release of Shift + Latch key.
-7. Save CapsLock state
-
-Fixes: 0104486
-Closes: rhbz#2444009
----
- client/wayland/ibuswaylandim.c | 304 +++++++++++++++++++++++++++------
- 1 file changed, 248 insertions(+), 56 deletions(-)
-
-diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
-index d7d12d05..237f50ef 100644
---- a/client/wayland/ibuswaylandim.c
-+++ b/client/wayland/ibuswaylandim.c
-@@ -40,6 +40,9 @@
- #include "virtual-keyboard-unstable-v1-client-protocol.h"
- #include "ibuswaylandim.h"
-
-+/* keysym & keycode should not be logged for the security issue. */
-+/* #define IBUS_LOG_SHOW_KEYSYM */
-+
- enum {
- PROP_0 = 0,
- PROP_BUS,
-@@ -146,6 +149,7 @@ struct _IBusWaylandIMPrivate
- guint preedit_cursor_pos;
- guint preedit_mode;
- IBusModifierType modifiers;
-+ gboolean is_virtual_latch_state;
- gboolean hiding_preedit_text;
- IBusInputHints ibus_hints;
- IBusInputPurpose ibus_purpose;
-@@ -203,10 +207,6 @@ static char _use_sync_mode = 1;
-
- static guint wayland_im_signals[LAST_SIGNAL] = { 0 };
-
--static void input_method_deactivate
-- (void *data,
-- struct zwp_input_method_union *input_method,
-- struct zwp_input_method_context_v1 *context);
- static GObject *ibus_wayland_im_constructor (GType type,
- guint n_params,
- GObjectConstructParam
-@@ -228,6 +228,19 @@ static gboolean ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- xkb_keysym_t sym,
- gboolean
- filtered);
-+static void input_method_deactivate
-+ (void *data,
-+ struct zwp_input_method_union *input_method,
-+ struct zwp_input_method_context_v1 *context);
-+static void input_method_keyboard_modifiers
-+ (void *data,
-+ struct zwp_keyboard_union *keyboard,
-+ uint32_t key_serial,
-+ uint32_t
-+ mods_depressed,
-+ uint32_t mods_latched,
-+ uint32_t mods_locked,
-+ uint32_t group);
-
-
- static char
-@@ -1129,7 +1142,14 @@ _bus_global_engine_changed_cb (IBusBus *bus,
- keymap = create_user_xkb_keymap (priv->xkb_context, desc);
- if (keymap && !ibus_xkb_keymap_update_with_keymap (&priv->key_user,
- keymap)) {
-- xkb_keymap_unref (keymap);
-+ g_clear_pointer (&keymap, xkb_keymap_unref);
-+ }
-+ if (priv->verbose) {
-+ fprintf (priv->log, "New engine:%s keymap:%s state:%s\n",
-+ ibus_engine_desc_get_name (desc),
-+ keymap ? "TRUE" : "FALSE",
-+ priv->key_user.state ? "TRUE" : "FALSE");
-+ fflush (priv->log);
- }
- g_object_unref (desc);
- }
-@@ -1253,6 +1273,7 @@ _get_seat_with_name (GPtrArray *seats,
- return NULL;
- }
-
-+
- static gboolean
- ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- uint32_t key,
-@@ -1263,8 +1284,13 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- {
- IBusWaylandIMPrivate *priv;
- IBusXkbKeymap *active_key;
-+ xkb_mod_mask_t mods_depressed = 0, new_mods_depressed = 0;
-+ xkb_mod_mask_t mods_locked;
-+ xkb_layout_index_t group;
-+ xkb_keysym_t system_sym;
- uint32_t code = key + 8;
- uint32_t ch;
-+ gboolean clear_virtual_state = FALSE;
-
- g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), FALSE);
- priv = ibus_wayland_im_get_instance_private (wlim);
-@@ -1288,49 +1314,132 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- active_key = &priv->key_user;
- if (!active_key->state)
- return FALSE;
-+ mods_depressed = xkb_state_serialize_mods (active_key->state,
-+ XKB_STATE_DEPRESSED |
-+ XKB_STATE_LATCHED);
-+ new_mods_depressed = mods_depressed;
-+ mods_locked = xkb_state_serialize_mods (active_key->state,
-+ XKB_STATE_MODS_LOCKED);
-+ /* XKB group layout is configured in the system keymap but not the
-+ * user keymap which always includes a single layout.
-+ */
-+ group = xkb_state_serialize_layout (priv->key_sys.state,
-+ XKB_STATE_LAYOUT_LOCKED);
- if (!filtered) {
--#if 0
-- /* FIXME: Need to confirm any modifiers should be ignored. */
-- if (!ibus_accelerator_valid (
-- sym,
-- modifiers & IBUS_MODIFIER_MASK & ~IBUS_SHIFT_MASK)) {
-- filtered = TRUE;
-- }
--#else
-+ system_sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
- switch (sym) {
-- case IBUS_ISO_Level2_Latch:
-- case IBUS_ISO_Level3_Latch:
-- case IBUS_ISO_Level5_Latch:
-+ case IBUS_KEY_ISO_Level2_Latch:
- filtered = TRUE;
- break;
-+ /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
-+ * Level3_Shift is caused by Alt key in lv(tilde) keymap.
-+ */
-+ case IBUS_KEY_ISO_Level3_Latch:
-+ case IBUS_KEY_ISO_Level3_Shift:
-+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-+ new_mods_depressed |= active_key->mod5_mask;
-+ if (sym != system_sym)
-+ priv->is_virtual_latch_state = TRUE;
-+ filtered = TRUE;
-+ }
-+ break;
-+ /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
-+ */
-+ case IBUS_KEY_ISO_Level5_Latch:
-+ case IBUS_KEY_ISO_Level5_Shift:
-+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-+ new_mods_depressed |= active_key->mod3_mask;
-+ if (sym != system_sym)
-+ priv->is_virtual_latch_state = TRUE;
-+ filtered = TRUE;
-+ }
-+ break;
-+ case IBUS_KEY_Shift_L:
-+ case IBUS_KEY_Shift_R:
-+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
-+ new_mods_depressed |= active_key->shift_mask;
-+ else
-+ new_mods_depressed &= ~active_key->shift_mask;
-+ break;
- default:;
- }
-+#ifdef IBUS_LOG_SHOW_KEYSYM
-+ if (priv->verbose) {
-+ fprintf (priv->log, "%s key:%u sym:%x system_sym:%x modifiers:%x "
-+ "state:%s filtered:%d\n",
-+ G_STRFUNC,
-+ key, sym, system_sym, modifiers,
-+ state ? "press" : "release",
-+ filtered);
-+ fflush (priv->log);
-+ }
- #endif
- }
-- /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
-- * but forwards the key events to the focused application here.
-- * Because some applications treat the printable keys as control keys,
-- * E.g. game apps "hjkl" use the cursor move like VI mode.
-- * Unfortunately the Wayland input-method protocol does not provide
-- * the fallback logic after apps handle the key events like GTK3/2
-- * IM modules. But The input-method always should handles key events
-- * prior to apps.
-- */
-- if (!filtered && (state != WL_KEYBOARD_KEY_STATE_RELEASED) &&
-- !priv->use_sys_keymap) {
-- ch = xkb_state_key_get_utf32 (active_key->state, code);
-- if (!(modifiers & IBUS_MODIFIER_MASK & ~IBUS_SHIFT_MASK) &&
-- ch && ch != '\n' && ch != '\b' && ch != '\r' && ch != '\t' &&
-- ch != '\033' && ch != '\x7f') {
-- gchar buff[8] = { 0, };
-- buff[g_unichar_to_utf8 (ch, buff)] = '\0';
-- ibus_wayland_im_commit_text (wlim, buff);
-- filtered = TRUE;
-+ if (state != WL_KEYBOARD_KEY_STATE_RELEASED) {
-+ ch = ibus_keyval_to_unicode (sym);
-+ if (ch == 0 || g_unichar_iscntrl (ch))
-+ ch = xkb_state_key_get_utf32 (active_key->state, code);
-+/* No text with Control & Alt & Super keys */
-+#ifndef GDK_WINDOWING_QUARTZ
-+# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
-+ IBUS_CONTROL_MASK | IBUS_MOD1_MASK | IBUS_MOD4_MASK)
-+#else
-+# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
-+ IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
-+#endif
-+ if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
-+ !g_unichar_iscntrl (ch)) || filtered) {
-+ /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
-+ * but forwards the key events to the focused application here.
-+ * Because some applications treat the printable keys as control keys,
-+ * E.g. game apps "hjkl" use the cursor move like VI mode.
-+ * Unfortunately the Wayland input-method protocol does not provide
-+ * the fallback logic after apps handle the key events like GTK3/2
-+ * IM modules. But The input-method always should handles key events
-+ * prior to apps.
-+ */
-+ if (!filtered && !priv->use_sys_keymap) {
-+ gchar buff[8] = { 0, };
-+ buff[g_unichar_to_utf8 (ch, buff)] = '\0';
-+ ibus_wayland_im_commit_text (wlim, buff);
-+ filtered = TRUE;
-+ }
-+ /* If `filtered` is %TRUE, the keysym can be eaten for the compose
-+ * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
-+ * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)"
-+ * keymap. However if you configre multiple keymaps, AltGr-Shift
-+ * may produce the keymap switch although AltGr-Shift-V produces
-+ * `Greek_OMEGA`.
-+ * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
-+ */
-+ if (modifiers & IBUS_MOD3_MASK) {
-+ new_mods_depressed &= ~active_key->mod3_mask;
-+ clear_virtual_state = TRUE;
-+ }
-+ if (modifiers & IBUS_MOD5_MASK) {
-+ new_mods_depressed &= ~active_key->mod5_mask;
-+ clear_virtual_state = TRUE;
-+ }
-+ }
-+#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
-+ }
-+ if (priv->is_virtual_latch_state) {
-+ if (new_mods_depressed != mods_depressed) {
-+ input_method_keyboard_modifiers (wlim, NULL, 0,
-+ new_mods_depressed,
-+ 0,
-+ mods_locked,
-+ group);
- }
-+ if (clear_virtual_state)
-+ priv->is_virtual_latch_state = FALSE;
-+ }
-+ if (!priv->is_virtual_latch_state ||
-+ (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
-+ xkb_state_update_key (active_key->state, code,
-+ (state == WL_KEYBOARD_KEY_STATE_RELEASED)
-+ ? XKB_KEY_UP : XKB_KEY_DOWN);
- }
-- xkb_state_update_key (active_key->state, code,
-- (state == WL_KEYBOARD_KEY_STATE_RELEASED)
-- ? XKB_KEY_UP : XKB_KEY_DOWN);
- return filtered;
- }
-
-@@ -1777,12 +1886,32 @@ input_method_keyboard_key (void *data,
- /* xkb_key_get_syms() does not return the capital syms with Shift key. */
- if (priv->key_sys.state)
- event.sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
-- if (event.sym != IBUS_KEY_Multi_key && priv->key_user.state)
-- event.sym = xkb_state_key_get_one_sym (priv->key_user.state, code);
-+ switch (event.sym) {
-+ case IBUS_KEY_Multi_key:
-+ case IBUS_KEY_ISO_Next_Group:
-+ break;
-+ default:
-+ if (priv->key_user.state)
-+ event.sym = xkb_state_key_get_one_sym (priv->key_user.state, code);
-+ }
- event.modifiers = priv->modifiers;
- if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
- event.modifiers |= IBUS_RELEASE_MASK;
- event.wlim = wlim;
-+#ifdef IBUS_LOG_SHOW_KEYSYM
-+ if (priv->verbose) {
-+ fprintf (priv->log, "%s serial:%u time:%u key:%u "
-+ "sym:%x system_user:%x system_sym:%x "
-+ "modifiers:%x state:%s\n",
-+ G_STRFUNC,
-+ key_serial, time, key,
-+ event.sym,
-+ xkb_state_key_get_one_sym (priv->key_user.state, code),
-+ xkb_state_key_get_one_sym (priv->key_sys.state, code),
-+ event.modifiers, state ? "press" : "release");
-+ fflush (priv->log);
-+ }
-+#endif
-
- key_event_check_repeat (wlim, &event);
- switch (_use_sync_mode) {
-@@ -1796,6 +1925,29 @@ input_method_keyboard_key (void *data,
- }
-
-
-+/**
-+ * input_method_keyboard_modifiers:
-+ *
-+ * This is a common API of input_method_keyboard_modifiers_v1() and
-+ * input_method_keyboard_modifiers_v2().
-+ * If you configure multiple XKB layouts in the system with
-+ * /etc/vconsole.conf file, you can switch the active layout with
-+ * the XKB options and @group is changed with the shortcut key like
-+ * "grp:lalt_lshift_toggle" but If you click the keyboard icon in
-+ * Plasma KDE and change the active layout with GUI, @group is not
-+ * changed and I assume it's a bug [1].
-+ * Currently there are two cases of the combinations of the IBus keymap and
-+ * the system keymap supported by IBus:
-+ * 1. Always Same keymaps
-+ * E.g. IBus keymap is "lv(tilde)" and system one is "lv(tilde)"
-+ * 2. System keymap is "US" and switch IBus keymaps with Super-space key
-+ * E.g. IBus keymap is "lv(tilde)" and system one is "us"
-+ * So you should use both XKB option keys and IBus shortcutkeys to switch the
-+ * keymaps to change both XKB keymaps and IBus keymaps but you should not
-+ * click the keyboard icon in KDE.
-+ *
-+ * [1] https://bugs.kde.org/show_bug.cgi?id=518371
-+ */
- static void
- input_method_keyboard_modifiers (void *data,
- struct zwp_keyboard_union *keyboard,
-@@ -1816,27 +1968,53 @@ input_method_keyboard_modifiers (void *data,
- active_key = &priv->key_sys;
- else
- active_key = &priv->key_user;
-- if (priv->key_user.state) {
-- xkb_state_update_mask (priv->key_user.state, mods_depressed,
-- mods_latched, mods_locked, 0, 0, group);
-- }
-- /* Pressing XKB group key likes Alt_R with grp:toggle calls
-- * input_method_keyboard_modifiers() with the updated `group`
-- * and need to update priv->key_sys.state to switch the XKB group
-- * in the system keymap.
-- *
-- * XKB group key can switch `group` but clicking the keyboard icon
-- * of KDE does not change `group` at present. Maybe a bug in the
-- * KDE Wayland compositor.
-+ /* Do not reset Latch modifiers by system in case the system keymap
-+ * has no the Latch key.
-+ * In case the user keymap is "lv(tilde)" and the system one is "us",
-+ * input_method_keyboard_modifiers() clears state of Level3_Latch key
-+ * immediately after the Level3_Latch key is released because the "us"
-+ * keymap does not have the latch keys.
-+ * The behavior is different in case both user and system keymaps are
-+ * "lv(tilde)".
-+ * So `is_virtual_latch_state` keeps the virtual state of the latch keys
-+ * not to override the state of the "us" keymap.
- */
-- if (priv->key_sys.state) {
-- xkb_state_update_mask (priv->key_sys.state, mods_depressed,
-- mods_latched, mods_locked, 0, 0, group);
-+ if (!priv->is_virtual_latch_state || key_serial == 0) {
-+ if (priv->key_user.state) {
-+ xkb_state_update_mask (priv->key_user.state, mods_depressed,
-+ mods_latched, mods_locked, 0, 0, group);
-+ }
-+ /* Pressing XKB group key likes Alt_R with grp:toggle calls
-+ * input_method_keyboard_modifiers() with the updated `group`
-+ * and need to update priv->key_sys.state to switch the XKB group
-+ * in the system keymap.
-+ *
-+ * XKB group key can switch `group` but clicking the keyboard icon
-+ * of KDE does not change `group` at present. Maybe a bug in the
-+ * KDE Wayland compositor.
-+ */
-+ if (priv->key_sys.state) {
-+ xkb_state_update_mask (priv->key_sys.state, mods_depressed,
-+ mods_latched, mods_locked, 0, 0, group);
-+ }
- }
- if (active_key->state) {
-+ /* CapsLock needs XKB_STATE_MODS_LOCKED */
- mask = xkb_state_serialize_mods (active_key->state,
- XKB_STATE_DEPRESSED |
-- XKB_STATE_LATCHED);
-+ XKB_STATE_LATCHED |
-+ XKB_STATE_MODS_LOCKED);
-+ }
-+ if (priv->verbose) {
-+ struct xkb_state *state = active_key->state;
-+ fprintf (priv->log, "Update modifiers serial:%u depress:%X latch:%X "
-+ "lock:%X group:%X orig_depre:%X orig_latch:%X "
-+ "orig_lock:%X\n",
-+ key_serial, mods_depressed, mods_latched, mods_locked, group,
-+ xkb_state_serialize_mods (state, XKB_STATE_DEPRESSED),
-+ xkb_state_serialize_mods (state, XKB_STATE_LATCHED),
-+ xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED));
-+ fflush (priv->log);
- }
-
- priv->modifiers = 0;
-@@ -1863,6 +2041,9 @@ input_method_keyboard_modifiers (void *data,
- if (mask & active_key->meta_mask)
- priv->modifiers |= IBUS_META_MASK;
-
-+ if (!key_serial)
-+ return;
-+
- switch (priv->version) {
- case INPUT_METHOD_V1:
- zwp_input_method_context_v1_modifiers (priv->context, key_serial,
-@@ -2886,6 +3067,17 @@ ibus_wayland_im_constructor (GType type,
- keymap)) {
- g_clear_pointer (&keymap, xkb_keymap_unref);
- }
-+ if (priv->verbose) {
-+ if (!desc) {
-+ fprintf (priv->log, "Constructor has No global engine\n");
-+ } else {
-+ fprintf (priv->log, "Constructor engine %s keymap:%s state:%s\n",
-+ ibus_engine_desc_get_name (desc),
-+ keymap ? "TRUE" : "FALSE",
-+ priv->key_user.state ? "TRUE" : "FALSE");
-+ }
-+ fflush (priv->log);
-+ }
- ibus_bus_set_watch_ibus_signal (priv->ibusbus, TRUE);
- g_signal_connect (priv->ibusbus, "global-engine-changed",
- G_CALLBACK (_bus_global_engine_changed_cb),
---
-2.53.0
-
-From 5fe640b170e09579b8b1955c5cad4aa4a6d1c464 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 15 Jun 2026 11:50:38 +0900
-Subject: [PATCH 2/4] client/wayland: Refactor ibus_wayland_im_post_key()
-
-Separate 3 functions.
-
-Closes: rhbz#2444009
----
- client/wayland/ibuswaylandim.c | 371 +++++++++++++++++++++------------
- 1 file changed, 243 insertions(+), 128 deletions(-)
-
-diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
-index 237f50ef..2cc2079b 100644
---- a/client/wayland/ibuswaylandim.c
-+++ b/client/wayland/ibuswaylandim.c
-@@ -367,6 +367,81 @@ ibus_wayland_im_commit_text (IBusWaylandIM *wlim,
- }
-
-
-+static gboolean
-+ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
-+ uint32_t key,
-+ uint32_t modifiers,
-+ uint32_t state,
-+ xkb_keysym_t sym,
-+ IBusXkbKeymap *active_key,
-+ gboolean filtered,
-+ xkb_mod_mask_t *new_mods_depressed,
-+ gboolean *clear_virtual_state)
-+{
-+ IBusWaylandIMPrivate *priv;
-+ uint32_t code = key + 8;
-+ uint32_t ch;
-+
-+ g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), filtered);
-+ priv = ibus_wayland_im_get_instance_private (wlim);
-+
-+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
-+ return filtered;
-+
-+ ch = ibus_keyval_to_unicode (sym);
-+ if (ch == 0 || g_unichar_iscntrl (ch))
-+ ch = xkb_state_key_get_utf32 (active_key->state, code);
-+
-+/* No text with Control & Alt & Super keys */
-+#ifndef GDK_WINDOWING_QUARTZ
-+# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
-+ IBUS_CONTROL_MASK | IBUS_MOD1_MASK | IBUS_MOD4_MASK)
-+#else
-+# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
-+ IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
-+#endif
-+ if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
-+ !g_unichar_iscntrl (ch)) || filtered) {
-+ /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
-+ * but forwards the key events to the focused application here.
-+ * Because some applications treat the printable keys as control keys,
-+ * E.g. game apps "hjkl" use the cursor move like VI mode.
-+ * Unfortunately the Wayland input-method protocol does not provide
-+ * the fallback logic after apps handle the key events like GTK3/2
-+ * IM modules. But The input-method always should handles key events
-+ * prior to apps.
-+ */
-+ if (!filtered && !priv->use_sys_keymap) {
-+ gchar buff[8] = { 0, };
-+ buff[g_unichar_to_utf8 (ch, buff)] = '\0';
-+ ibus_wayland_im_commit_text (wlim, buff);
-+ filtered = TRUE;
-+ }
-+ /* If `filtered` is %TRUE, the keysym can be eaten for the compose
-+ * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
-+ * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)" keymap.
-+ * However if you configre multiple keymaps, AltGr-Shift may produce
-+ * the keymap switch although AltGr-Shift-V produces `Greek_OMEGA`.
-+ * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
-+ */
-+ if (modifiers & IBUS_MOD3_MASK) {
-+ if (new_mods_depressed)
-+ *new_mods_depressed &= ~active_key->mod3_mask;
-+ if (clear_virtual_state)
-+ *clear_virtual_state = TRUE;
-+ }
-+ if (modifiers & IBUS_MOD5_MASK) {
-+ if (new_mods_depressed)
-+ *new_mods_depressed &= ~active_key->mod5_mask;
-+ if (clear_virtual_state)
-+ *clear_virtual_state = TRUE;
-+ }
-+ }
-+#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
-+ return filtered;
-+}
-+
-+
- static void
- ibus_wayland_im_key (IBusWaylandIM *wlim,
- uint32_t key_serial,
-@@ -431,6 +506,149 @@ ibus_wayland_im_keysym (IBusWaylandIM *wlim,
- }
-
-
-+/**
-+ * ibus_wayland_im_update_virtual_depressed:
-+ *
-+ * If the IBus keymap is different from the compositor keymap,
-+ * IBus needs to maintain the modifiers state by itself to call
-+ * xkb_state_update_mask(), E.g. IBus keymap is "lv(tilde)" and the system
-+ * one is "us", because input_method_keyboard_modifiers()
-+ * is not called by the Wayland compositor in that case.
-+ * So this API updates @mods_depressed with ISO level3 and level5
-+ * latch and shift states, and send it to
-+ * ibus_wayland_im_update_virtual_xkb_state().
-+ */
-+static gboolean
-+ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
-+ uint32_t key,
-+ uint32_t modifiers,
-+ uint32_t state,
-+ xkb_keysym_t sym,
-+ IBusXkbKeymap *active_key,
-+ gboolean filtered,
-+ xkb_mod_mask_t *mods_depressed)
-+{
-+ IBusWaylandIMPrivate *priv;
-+ uint32_t code = key + 8;
-+ xkb_mod_mask_t new_mods_depressed;
-+ xkb_keysym_t system_sym;
-+
-+ if (filtered)
-+ return filtered;
-+
-+ g_assert (IBUS_IS_WAYLAND_IM (wlim));
-+ g_assert (active_key);
-+ g_assert (mods_depressed);
-+
-+ priv = ibus_wayland_im_get_instance_private (wlim);
-+ new_mods_depressed = *mods_depressed;
-+
-+ if ((modifiers & ~IBUS_RELEASE_MASK ) != new_mods_depressed) {
-+ g_warning ("IBus modifiers %X is different from XKB depressed %X",
-+ modifiers, new_mods_depressed);
-+ }
-+ system_sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
-+ switch (sym) {
-+ case IBUS_KEY_ISO_Level2_Latch:
-+ filtered = TRUE;
-+ break;
-+ /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
-+ * Level3_Shift is caused by Alt key in lv(tilde) keymap.
-+ */
-+ case IBUS_KEY_ISO_Level3_Latch:
-+ case IBUS_KEY_ISO_Level3_Shift:
-+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-+ new_mods_depressed |= active_key->mod5_mask;
-+ if (sym != system_sym)
-+ priv->is_virtual_latch_state = TRUE;
-+ filtered = TRUE;
-+ }
-+ break;
-+ /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
-+ */
-+ case IBUS_KEY_ISO_Level5_Latch:
-+ case IBUS_KEY_ISO_Level5_Shift:
-+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-+ new_mods_depressed |= active_key->mod3_mask;
-+ if (sym != system_sym)
-+ priv->is_virtual_latch_state = TRUE;
-+ filtered = TRUE;
-+ }
-+ break;
-+ case IBUS_KEY_Shift_L:
-+ case IBUS_KEY_Shift_R:
-+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
-+ new_mods_depressed |= active_key->shift_mask;
-+ else
-+ new_mods_depressed &= ~active_key->shift_mask;
-+ break;
-+ default:;
-+ }
-+#ifdef IBUS_LOG_SHOW_KEYSYM
-+ if (priv->verbose) {
-+ fprintf (priv->log, "%s key:%u sym:%x system_sym:%x modifiers:%x "
-+ "state:%s filtered:%d\n",
-+ G_STRFUNC,
-+ key, sym, system_sym, modifiers, state ? "press" : "release",
-+ filtered);
-+ fflush (priv->log);
-+ }
-+#endif
-+ *mods_depressed = new_mods_depressed;
-+ return filtered;
-+}
-+
-+
-+/**
-+ * ibus_wayland_im_update_virtual_xkb_state:
-+ *
-+ * Update virtual IBus xkb_state by KeyPress and KeyRelease.
-+ */
-+static void
-+ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
-+ uint32_t key,
-+ uint32_t state,
-+ IBusXkbKeymap *active_key,
-+ xkb_mod_mask_t mods_depressed,
-+ xkb_mod_mask_t new_mods_depressed,
-+ gboolean clear_virtual_state)
-+{
-+ IBusWaylandIMPrivate *priv;
-+ uint32_t code = key + 8;
-+ xkb_mod_mask_t mods_locked;
-+ xkb_layout_index_t group;
-+
-+ g_assert (IBUS_IS_WAYLAND_IM (wlim));
-+
-+ priv = ibus_wayland_im_get_instance_private (wlim);
-+ mods_locked = xkb_state_serialize_mods (active_key->state,
-+ XKB_STATE_MODS_LOCKED);
-+ /* XKB group layout is configured in the system keymap but not the
-+ * user keymap which always includes a single layout.
-+ */
-+ group = xkb_state_serialize_layout (priv->key_sys.state,
-+ XKB_STATE_LAYOUT_LOCKED);
-+
-+ if (priv->is_virtual_latch_state) {
-+ if (new_mods_depressed != mods_depressed) {
-+ input_method_keyboard_modifiers (wlim, NULL, 0,
-+ new_mods_depressed,
-+ 0,
-+ mods_locked,
-+ group);
-+ }
-+ if (clear_virtual_state)
-+ priv->is_virtual_latch_state = FALSE;
-+ }
-+ if (!priv->is_virtual_latch_state ||
-+ (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
-+ xkb_state_update_key (active_key->state, code,
-+ (state == WL_KEYBOARD_KEY_STATE_RELEASED)
-+ ? XKB_KEY_UP : XKB_KEY_DOWN);
-+ }
-+}
-+
-+
- static void
- _context_commit_text_cb (IBusInputContext *context,
- IBusText *text,
-@@ -1284,12 +1502,7 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- {
- IBusWaylandIMPrivate *priv;
- IBusXkbKeymap *active_key;
-- xkb_mod_mask_t mods_depressed = 0, new_mods_depressed = 0;
-- xkb_mod_mask_t mods_locked;
-- xkb_layout_index_t group;
-- xkb_keysym_t system_sym;
-- uint32_t code = key + 8;
-- uint32_t ch;
-+ xkb_mod_mask_t mods_depressed, new_mods_depressed;
- gboolean clear_virtual_state = FALSE;
-
- g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), FALSE);
-@@ -1318,128 +1531,30 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- XKB_STATE_DEPRESSED |
- XKB_STATE_LATCHED);
- new_mods_depressed = mods_depressed;
-- mods_locked = xkb_state_serialize_mods (active_key->state,
-- XKB_STATE_MODS_LOCKED);
-- /* XKB group layout is configured in the system keymap but not the
-- * user keymap which always includes a single layout.
-- */
-- group = xkb_state_serialize_layout (priv->key_sys.state,
-- XKB_STATE_LAYOUT_LOCKED);
-- if (!filtered) {
-- system_sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
-- switch (sym) {
-- case IBUS_KEY_ISO_Level2_Latch:
-- filtered = TRUE;
-- break;
-- /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
-- * Level3_Shift is caused by Alt key in lv(tilde) keymap.
-- */
-- case IBUS_KEY_ISO_Level3_Latch:
-- case IBUS_KEY_ISO_Level3_Shift:
-- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-- new_mods_depressed |= active_key->mod5_mask;
-- if (sym != system_sym)
-- priv->is_virtual_latch_state = TRUE;
-- filtered = TRUE;
-- }
-- break;
-- /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
-- */
-- case IBUS_KEY_ISO_Level5_Latch:
-- case IBUS_KEY_ISO_Level5_Shift:
-- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-- new_mods_depressed |= active_key->mod3_mask;
-- if (sym != system_sym)
-- priv->is_virtual_latch_state = TRUE;
-- filtered = TRUE;
-- }
-- break;
-- case IBUS_KEY_Shift_L:
-- case IBUS_KEY_Shift_R:
-- if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
-- new_mods_depressed |= active_key->shift_mask;
-- else
-- new_mods_depressed &= ~active_key->shift_mask;
-- break;
-- default:;
-- }
--#ifdef IBUS_LOG_SHOW_KEYSYM
-- if (priv->verbose) {
-- fprintf (priv->log, "%s key:%u sym:%x system_sym:%x modifiers:%x "
-- "state:%s filtered:%d\n",
-- G_STRFUNC,
-- key, sym, system_sym, modifiers,
-- state ? "press" : "release",
-- filtered);
-- fflush (priv->log);
-- }
--#endif
-- }
-- if (state != WL_KEYBOARD_KEY_STATE_RELEASED) {
-- ch = ibus_keyval_to_unicode (sym);
-- if (ch == 0 || g_unichar_iscntrl (ch))
-- ch = xkb_state_key_get_utf32 (active_key->state, code);
--/* No text with Control & Alt & Super keys */
--#ifndef GDK_WINDOWING_QUARTZ
--# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
-- IBUS_CONTROL_MASK | IBUS_MOD1_MASK | IBUS_MOD4_MASK)
--#else
--# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
-- IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
--#endif
-- if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
-- !g_unichar_iscntrl (ch)) || filtered) {
-- /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
-- * but forwards the key events to the focused application here.
-- * Because some applications treat the printable keys as control keys,
-- * E.g. game apps "hjkl" use the cursor move like VI mode.
-- * Unfortunately the Wayland input-method protocol does not provide
-- * the fallback logic after apps handle the key events like GTK3/2
-- * IM modules. But The input-method always should handles key events
-- * prior to apps.
-- */
-- if (!filtered && !priv->use_sys_keymap) {
-- gchar buff[8] = { 0, };
-- buff[g_unichar_to_utf8 (ch, buff)] = '\0';
-- ibus_wayland_im_commit_text (wlim, buff);
-- filtered = TRUE;
-- }
-- /* If `filtered` is %TRUE, the keysym can be eaten for the compose
-- * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
-- * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)"
-- * keymap. However if you configre multiple keymaps, AltGr-Shift
-- * may produce the keymap switch although AltGr-Shift-V produces
-- * `Greek_OMEGA`.
-- * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
-- */
-- if (modifiers & IBUS_MOD3_MASK) {
-- new_mods_depressed &= ~active_key->mod3_mask;
-- clear_virtual_state = TRUE;
-- }
-- if (modifiers & IBUS_MOD5_MASK) {
-- new_mods_depressed &= ~active_key->mod5_mask;
-- clear_virtual_state = TRUE;
-- }
-- }
--#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
-- }
-- if (priv->is_virtual_latch_state) {
-- if (new_mods_depressed != mods_depressed) {
-- input_method_keyboard_modifiers (wlim, NULL, 0,
-- new_mods_depressed,
-- 0,
-- mods_locked,
-- group);
-- }
-- if (clear_virtual_state)
-- priv->is_virtual_latch_state = FALSE;
-- }
-- if (!priv->is_virtual_latch_state ||
-- (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
-- xkb_state_update_key (active_key->state, code,
-- (state == WL_KEYBOARD_KEY_STATE_RELEASED)
-- ? XKB_KEY_UP : XKB_KEY_DOWN);
-- }
-+ filtered = ibus_wayland_im_update_virtual_depressed (wlim,
-+ key,
-+ modifiers,
-+ state,
-+ sym,
-+ active_key,
-+ filtered,
-+ &new_mods_depressed);
-+ filtered = ibus_wayland_im_commit_key_event (wlim,
-+ key,
-+ modifiers,
-+ state,
-+ sym,
-+ active_key,
-+ filtered,
-+ &new_mods_depressed,
-+ &clear_virtual_state);
-+ ibus_wayland_im_update_virtual_xkb_state (wlim,
-+ key,
-+ state,
-+ active_key,
-+ mods_depressed,
-+ new_mods_depressed,
-+ clear_virtual_state);
- return filtered;
- }
-
---
-2.53.0
-
-From 36ab2bc5bf6881632d09d0be23b95401dfdea2a4 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 17 Jun 2026 19:51:08 +0900
-Subject: [PATCH 3/4] client/wayland: Refactor ISO_Level[3|5]_Shift state
-
-The states of ISO Shift keys should be cleared when the key is released
-against ISO Latch keys. E.g. "RALT" key in "lv(tilde)" keymap.
-
-The state of the "RALT" key is strange in "de(T3)" keymap, i.e.
-"ISO_Level3_Shift" keysym for the keypress and ISO_Level5_Latch keysm
-for keyrelease and IBus could not get the correct dead keys and
-clear the state. Now the "is_pressed_mod5" flag checks if the
-key is pressed and add a workaround.
-
-Closes: rhbz#2444009
----
- client/wayland/ibuswaylandim.c | 139 ++++++++++++++++++++++++++++++---
- 1 file changed, 127 insertions(+), 12 deletions(-)
-
-diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
-index 2cc2079b..571c7b05 100644
---- a/client/wayland/ibuswaylandim.c
-+++ b/client/wayland/ibuswaylandim.c
-@@ -64,6 +64,13 @@ typedef enum
- INPUT_METHOD_V2,
- } IMProtocolVersion;
-
-+typedef enum {
-+ IBUS_KEY_ISO_LEVEL_INVALID,
-+ IBUS_KEY_ISO_LEVEL_2,
-+ IBUS_KEY_ISO_LEVEL_3,
-+ IBUS_KEY_ISO_LEVEL_5
-+} IBusKeyIsoLevelValue;
-+
- struct zwp_input_method_context_union {
- union {
- struct zwp_input_method_context_v1 *context_v1;
-@@ -163,6 +170,8 @@ struct _IBusWaylandIMPrivate
-
- IBusXkbKeymap key_user;
- IBusXkbKeymap key_sys;
-+ gboolean is_pressed_mod3;
-+ gboolean is_pressed_mod5;
-
- uint32_t im_serial;
- int32_t repeat_rate;
-@@ -341,6 +350,28 @@ ibus_wayland_source_new (struct wl_display *display)
- }
-
-
-+static void
-+ibus_wayland_im_reset_modifiers (IBusWaylandIM *wlim)
-+{
-+ IBusWaylandIMPrivate *priv;
-+ xkb_layout_index_t group = 0;
-+
-+ g_assert (IBUS_IS_WAYLAND_IM (wlim));
-+
-+ priv = ibus_wayland_im_get_instance_private (wlim);
-+ priv->is_virtual_latch_state = FALSE;
-+ priv->is_pressed_mod3 = FALSE;
-+ priv->is_pressed_mod5 = FALSE;
-+ if (priv->key_user.state && priv->key_sys.state) {
-+ group = xkb_state_serialize_layout (priv->key_sys.state,
-+ XKB_STATE_LAYOUT_LOCKED);
-+ input_method_keyboard_modifiers (wlim, NULL, 0, 0, 0, 0, group);
-+ } else {
-+ priv->modifiers = 0;
-+ }
-+}
-+
-+
- static void
- ibus_wayland_im_commit_text (IBusWaylandIM *wlim,
- const char *str)
-@@ -526,7 +557,8 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
- xkb_keysym_t sym,
- IBusXkbKeymap *active_key,
- gboolean filtered,
-- xkb_mod_mask_t *mods_depressed)
-+ xkb_mod_mask_t *mods_depressed,
-+ gboolean *clear_virtual_state)
- {
- IBusWaylandIMPrivate *priv;
- uint32_t code = key + 8;
-@@ -539,6 +571,7 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
- g_assert (IBUS_IS_WAYLAND_IM (wlim));
- g_assert (active_key);
- g_assert (mods_depressed);
-+ g_assert (clear_virtual_state);
-
- priv = ibus_wayland_im_get_instance_private (wlim);
- new_mods_depressed = *mods_depressed;
-@@ -554,25 +587,87 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
- break;
- /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
- * Level3_Shift is caused by Alt key in lv(tilde) keymap.
-+ * Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
- */
- case IBUS_KEY_ISO_Level3_Latch:
- case IBUS_KEY_ISO_Level3_Shift:
-- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-- new_mods_depressed |= active_key->mod5_mask;
-- if (sym != system_sym)
-- priv->is_virtual_latch_state = TRUE;
-- filtered = TRUE;
-- }
-- break;
-- /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
-- */
- case IBUS_KEY_ISO_Level5_Latch:
- case IBUS_KEY_ISO_Level5_Shift:
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-- new_mods_depressed |= active_key->mod3_mask;
-+ switch (sym) {
-+ case IBUS_KEY_ISO_Level3_Latch:
-+ case IBUS_KEY_ISO_Level3_Shift:
-+ priv->is_pressed_mod5 = TRUE;
-+ new_mods_depressed |= active_key->mod5_mask;
-+ break;
-+ case IBUS_KEY_ISO_Level5_Latch:
-+ case IBUS_KEY_ISO_Level5_Shift:
-+ priv->is_pressed_mod3 = TRUE;
-+ new_mods_depressed |= active_key->mod3_mask;
-+ break;
-+ default:
-+ g_assert_not_reached ();
-+ }
- if (sym != system_sym)
- priv->is_virtual_latch_state = TRUE;
- filtered = TRUE;
-+ } else {
-+ IBusKeyIsoLevelValue current_level = IBUS_KEY_ISO_LEVEL_INVALID;
-+ gboolean *is_pressed_current_mod = NULL;
-+ gboolean *is_pressed_reverse_mod = NULL;
-+ xkb_mod_mask_t current_mode_mask = 0;
-+ xkb_mod_mask_t reverse_mode_mask = 0;
-+ switch (sym) {
-+ case IBUS_KEY_ISO_Level3_Latch:
-+ case IBUS_KEY_ISO_Level3_Shift:
-+ current_level = IBUS_KEY_ISO_LEVEL_3;
-+ is_pressed_current_mod = &priv->is_pressed_mod5;
-+ is_pressed_reverse_mod = &priv->is_pressed_mod3;
-+ current_mode_mask = active_key->mod5_mask;
-+ reverse_mode_mask = active_key->mod3_mask;
-+ break;
-+ case IBUS_KEY_ISO_Level5_Latch:
-+ case IBUS_KEY_ISO_Level5_Shift:
-+ current_level = IBUS_KEY_ISO_LEVEL_5;
-+ is_pressed_current_mod = &priv->is_pressed_mod3;
-+ is_pressed_reverse_mod = &priv->is_pressed_mod5;
-+ current_mode_mask = active_key->mod3_mask;
-+ reverse_mode_mask = active_key->mod5_mask;
-+ break;
-+ default:;
-+ }
-+ g_assert (current_level != IBUS_KEY_ISO_LEVEL_INVALID);
-+ g_assert (is_pressed_current_mod && is_pressed_reverse_mod);
-+ if (G_LIKELY (*is_pressed_current_mod)) {
-+ *is_pressed_current_mod = FALSE;
-+ } else if (*is_pressed_reverse_mod &&
-+ (new_mods_depressed & active_key->mod3_mask)) {
-+ /* The unlikely case !priv->is_pressed_mod3 and
-+ * priv->is_pressed_mod5 means that "de(T3)" keymap gives
-+ * the pressed "Level3_Shift" keysym and
-+ * the released "Level5_Latch" keysym for the key <RALT>.
-+ * Maybe a bug in "de(T3)" so need to clear the MOD5 state
-+ * by the pressed "Level3_Shift" keysym.
-+ */
-+ *is_pressed_reverse_mod = FALSE;
-+ new_mods_depressed &= ~reverse_mode_mask;
-+ *clear_virtual_state = TRUE;
-+ g_debug ("Got a wrong released %s key without the "
-+ "pressed one. Maybe a bug in XKB.",
-+ current_level == IBUS_KEY_ISO_LEVEL_3 ?
-+ "Level3" : "Level5");
-+ }
-+ /* The "lv(tilde)" keymap gives the "Level3_Shift" keysym
-+ * with the key <RALT>.
-+ */
-+ if ((current_level == IBUS_KEY_ISO_LEVEL_3 &&
-+ sym == IBUS_KEY_ISO_Level3_Shift) ||
-+ (current_level == IBUS_KEY_ISO_LEVEL_5 &&
-+ sym == IBUS_KEY_ISO_Level5_Shift)) {
-+ new_mods_depressed &= ~current_mode_mask;
-+ if (sym != system_sym)
-+ clear_virtual_state = TRUE;
-+ }
- }
- break;
- case IBUS_KEY_Shift_L:
-@@ -646,6 +741,23 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
- (state == WL_KEYBOARD_KEY_STATE_RELEASED)
- ? XKB_KEY_UP : XKB_KEY_DOWN);
- }
-+ if (priv->is_virtual_latch_state &&
-+ (new_mods_depressed != mods_depressed) &&
-+ (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
-+ xkb_mod_mask_t new2_mods_depressed;
-+ new2_mods_depressed = xkb_state_serialize_mods (active_key->state,
-+ XKB_STATE_DEPRESSED |
-+ XKB_STATE_LATCHED);
-+ /* "de(T3)" keymap gives the pressed "Level3_Shift" keysym and
-+ * sets both MOD3(Level5) and MOD5(Level3) states after
-+ * xkb_state_update_key() is called with the keypress.
-+ * Maybe a bug in "de(T3)" and sets MOD5(Level3) again here.
-+ */
-+ if (G_UNLIKELY (new_mods_depressed != new2_mods_depressed)) {
-+ xkb_state_update_mask (active_key->state, new_mods_depressed,
-+ 0, mods_locked, 0, 0, group);
-+ }
-+ }
- }
-
-
-@@ -1351,6 +1463,7 @@ _bus_global_engine_changed_cb (IBusBus *bus,
- g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
- priv = ibus_wayland_im_get_instance_private (wlim);
- desc = ibus_bus_get_global_engine (bus);
-+ ibus_wayland_im_reset_modifiers (wlim);
- g_assert (desc);
- g_assert (!g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name));
- /* Always update priv->key_user even if priv->use_sys_keymap is %FALSE
-@@ -1538,7 +1651,8 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- sym,
- active_key,
- filtered,
-- &new_mods_depressed);
-+ &new_mods_depressed,
-+ &clear_virtual_state);
- filtered = ibus_wayland_im_commit_key_event (wlim,
- key,
- modifiers,
-@@ -2411,6 +2525,7 @@ _create_input_context_done (GObject *object,
- TRUE);
- }
- ibus_input_context_focus_in (priv->ibuscontext);
-+ ibus_wayland_im_reset_modifiers (wlim);
- g_signal_emit (wlim,
- wayland_im_signals[IBUS_FOCUS_IN],
- 0,
---
-2.53.0
-
-From 507c11b947f59be10b997ad4522024fcc2552845 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 15 Jun 2026 11:50:53 +0900
-Subject: [PATCH 4/4] client/wayland: Fix infinite dead_diaeresis with
- fr(ergol) keymap
-
-Typing the key <AD09> twice gives different keysyms between KeyPress
-and KeyRelease events and causes the reverse order of KeyPress and
-KeyRelease of the "dead_diaeresis" keysym.
-Now ibus-wayland checks the special order not to cause the key repeating.
-Also ibus-wayland releases the state of "ISO_Level5_Latch" with typing
-the key <AD09> twice.
-
-Closes: #2894
----
- client/wayland/ibuswaylandim.c | 245 +++++++++++++++++++++++----------
- 1 file changed, 172 insertions(+), 73 deletions(-)
-
-diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
-index 571c7b05..9c69439a 100644
---- a/client/wayland/ibuswaylandim.c
-+++ b/client/wayland/ibuswaylandim.c
-@@ -71,6 +71,12 @@ typedef enum {
- IBUS_KEY_ISO_LEVEL_5
- } IBusKeyIsoLevelValue;
-
-+typedef enum {
-+ IBUS_KEY_ISO_LEVEL_STATE_RELEASE,
-+ IBUS_KEY_ISO_LEVEL_STATE_SHIFT,
-+ IBUS_KEY_ISO_LEVEL_STATE_LATCH
-+} IBusKeyIsoLevelState;
-+
- struct zwp_input_method_context_union {
- union {
- struct zwp_input_method_context_v1 *context_v1;
-@@ -170,12 +176,14 @@ struct _IBusWaylandIMPrivate
-
- IBusXkbKeymap key_user;
- IBusXkbKeymap key_sys;
-- gboolean is_pressed_mod3;
-- gboolean is_pressed_mod5;
-+ IBusKeyIsoLevelState iso_level3_state;
-+ IBusKeyIsoLevelState iso_level5_state;
-
- uint32_t im_serial;
- int32_t repeat_rate;
- int32_t repeat_delay;
-+ xkb_keysym_t pressed_dead_key;
-+ xkb_keysym_t released_dead_key_wo_press;
-
- GCancellable *cancellable;
- };
-@@ -360,8 +368,10 @@ ibus_wayland_im_reset_modifiers (IBusWaylandIM *wlim)
-
- priv = ibus_wayland_im_get_instance_private (wlim);
- priv->is_virtual_latch_state = FALSE;
-- priv->is_pressed_mod3 = FALSE;
-- priv->is_pressed_mod5 = FALSE;
-+ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
-+ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
-+ priv->pressed_dead_key = 0;
-+ priv->released_dead_key_wo_press = 0;
- if (priv->key_user.state && priv->key_sys.state) {
- group = xkb_state_serialize_layout (priv->key_sys.state,
- XKB_STATE_LAYOUT_LOCKED);
-@@ -407,7 +417,8 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
- IBusXkbKeymap *active_key,
- gboolean filtered,
- xkb_mod_mask_t *new_mods_depressed,
-- gboolean *clear_virtual_state)
-+ gboolean *clear_virtual_state,
-+ gboolean *is_invalid_key)
- {
- IBusWaylandIMPrivate *priv;
- uint32_t code = key + 8;
-@@ -416,9 +427,6 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
- g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), filtered);
- priv = ibus_wayland_im_get_instance_private (wlim);
-
-- if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
-- return filtered;
--
- ch = ibus_keyval_to_unicode (sym);
- if (ch == 0 || g_unichar_iscntrl (ch))
- ch = xkb_state_key_get_utf32 (active_key->state, code);
-@@ -431,23 +439,28 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
- # define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
- IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
- #endif
-- if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
-- !g_unichar_iscntrl (ch)) || filtered) {
-- /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
-- * but forwards the key events to the focused application here.
-- * Because some applications treat the printable keys as control keys,
-- * E.g. game apps "hjkl" use the cursor move like VI mode.
-- * Unfortunately the Wayland input-method protocol does not provide
-- * the fallback logic after apps handle the key events like GTK3/2
-- * IM modules. But The input-method always should handles key events
-- * prior to apps.
-- */
-- if (!filtered && !priv->use_sys_keymap) {
-- gchar buff[8] = { 0, };
-- buff[g_unichar_to_utf8 (ch, buff)] = '\0';
-- ibus_wayland_im_commit_text (wlim, buff);
-- filtered = TRUE;
-- }
-+ if ((state == WL_KEYBOARD_KEY_STATE_RELEASED) ||
-+ (modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK)) {
-+ return filtered;
-+ }
-+#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
-+
-+ /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
-+ * but forwards the key events to the focused application here.
-+ * Because some applications treat the printable keys as control keys,
-+ * E.g. game apps "hjkl" use the cursor move like VI mode.
-+ * Unfortunately the Wayland input-method protocol does not provide
-+ * the fallback logic after apps handle the key events like GTK3/2
-+ * IM modules. But The input-method always should handles key events
-+ * prior to apps.
-+ */
-+ if (!filtered && !g_unichar_iscntrl (ch) && !priv->use_sys_keymap) {
-+ gchar buff[8] = { 0, };
-+ buff[g_unichar_to_utf8 (ch, buff)] = '\0';
-+ ibus_wayland_im_commit_text (wlim, buff);
-+ filtered = TRUE;
-+ }
-+ if (!g_unichar_iscntrl (ch) || IS_DEAD_KEY (sym)) {
- /* If `filtered` is %TRUE, the keysym can be eaten for the compose
- * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
- * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)" keymap.
-@@ -456,19 +469,42 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
- * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
- */
- if (modifiers & IBUS_MOD3_MASK) {
-+ /* With the "fr(ergol)" keymap, Typing the key <AD09> twice
-+ * produces the "ISO_Level5_Latch" keysym for the first KeyPress
-+ * and the "dead_diaeresis" keysym for the first KeyReleease
-+ * because the release key event has MOD3(level5) state.
-+ * And the second KeyPress has the "dead_diaeresis" keysym and
-+ * the second KeyRelease has the "ISO_Level5_Latch" keysym.
-+ * The latch state should be cleared with the dead keys.
-+ * I downgrade the latch state to the shift state here and
-+ * the shift state will be cleared by the released
-+ * "ISO_Level5_Latch" keysym but not the pressed dead keys
-+ * because xkb_state_update_key() will revert the MOD3(level5)
-+ * state of the depressed xkb_state_serialize_mods()
-+ * with the dead keys.
-+ */
-+ if (priv->iso_level5_state == IBUS_KEY_ISO_LEVEL_STATE_LATCH) {
-+ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
-+ if (is_invalid_key)
-+ *is_invalid_key = TRUE;
-+ } else if (clear_virtual_state) {
-+ *clear_virtual_state = TRUE;
-+ }
- if (new_mods_depressed)
- *new_mods_depressed &= ~active_key->mod3_mask;
-- if (clear_virtual_state)
-- *clear_virtual_state = TRUE;
- }
- if (modifiers & IBUS_MOD5_MASK) {
-+ if (priv->iso_level3_state == IBUS_KEY_ISO_LEVEL_STATE_LATCH) {
-+ priv->iso_level3_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
-+ if (is_invalid_key)
-+ *is_invalid_key = TRUE;
-+ } else if (clear_virtual_state) {
-+ *clear_virtual_state = TRUE;
-+ }
- if (new_mods_depressed)
- *new_mods_depressed &= ~active_key->mod5_mask;
-- if (clear_virtual_state)
-- *clear_virtual_state = TRUE;
- }
- }
--#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
- return filtered;
- }
-
-@@ -558,7 +594,8 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
- IBusXkbKeymap *active_key,
- gboolean filtered,
- xkb_mod_mask_t *mods_depressed,
-- gboolean *clear_virtual_state)
-+ gboolean *clear_virtual_state,
-+ gboolean *is_invalid_key)
- {
- IBusWaylandIMPrivate *priv;
- uint32_t code = key + 8;
-@@ -572,6 +609,7 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
- g_assert (active_key);
- g_assert (mods_depressed);
- g_assert (clear_virtual_state);
-+ g_assert (is_invalid_key);
-
- priv = ibus_wayland_im_get_instance_private (wlim);
- new_mods_depressed = *mods_depressed;
-@@ -596,13 +634,19 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- switch (sym) {
- case IBUS_KEY_ISO_Level3_Latch:
-+ priv->iso_level3_state = IBUS_KEY_ISO_LEVEL_STATE_LATCH;
-+ new_mods_depressed |= active_key->mod5_mask;
-+ break;
- case IBUS_KEY_ISO_Level3_Shift:
-- priv->is_pressed_mod5 = TRUE;
-+ priv->iso_level3_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
- new_mods_depressed |= active_key->mod5_mask;
- break;
- case IBUS_KEY_ISO_Level5_Latch:
-+ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_LATCH;
-+ new_mods_depressed |= active_key->mod3_mask;
-+ break;
- case IBUS_KEY_ISO_Level5_Shift:
-- priv->is_pressed_mod3 = TRUE;
-+ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
- new_mods_depressed |= active_key->mod3_mask;
- break;
- default:
-@@ -613,61 +657,70 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
- filtered = TRUE;
- } else {
- IBusKeyIsoLevelValue current_level = IBUS_KEY_ISO_LEVEL_INVALID;
-- gboolean *is_pressed_current_mod = NULL;
-- gboolean *is_pressed_reverse_mod = NULL;
-- xkb_mod_mask_t current_mode_mask = 0;
-- xkb_mod_mask_t reverse_mode_mask = 0;
-+ IBusKeyIsoLevelState *current_state = NULL;
-+ IBusKeyIsoLevelState *reverse_state = NULL;
- switch (sym) {
- case IBUS_KEY_ISO_Level3_Latch:
- case IBUS_KEY_ISO_Level3_Shift:
- current_level = IBUS_KEY_ISO_LEVEL_3;
-- is_pressed_current_mod = &priv->is_pressed_mod5;
-- is_pressed_reverse_mod = &priv->is_pressed_mod3;
-- current_mode_mask = active_key->mod5_mask;
-- reverse_mode_mask = active_key->mod3_mask;
-+ current_state = &priv->iso_level3_state;
-+ reverse_state = &priv->iso_level5_state;
- break;
- case IBUS_KEY_ISO_Level5_Latch:
- case IBUS_KEY_ISO_Level5_Shift:
- current_level = IBUS_KEY_ISO_LEVEL_5;
-- is_pressed_current_mod = &priv->is_pressed_mod3;
-- is_pressed_reverse_mod = &priv->is_pressed_mod5;
-- current_mode_mask = active_key->mod3_mask;
-- reverse_mode_mask = active_key->mod5_mask;
-+ current_state = &priv->iso_level5_state;
-+ reverse_state = &priv->iso_level3_state;
- break;
- default:;
- }
- g_assert (current_level != IBUS_KEY_ISO_LEVEL_INVALID);
-- g_assert (is_pressed_current_mod && is_pressed_reverse_mod);
-- if (G_LIKELY (*is_pressed_current_mod)) {
-- *is_pressed_current_mod = FALSE;
-- } else if (*is_pressed_reverse_mod &&
-- (new_mods_depressed & active_key->mod3_mask)) {
-+ g_assert (current_state && reverse_state);
-+ /* The "lv(tilde)" keymap gives the "Level3_Shift" keysym
-+ * with the key <RALT>.
-+ *
-+ * Handle the "ISO_Level5_Latch" keysym in the "fr(ergol)" keymap.
-+ * Do not use keysym but `current_state` because the latch state
-+ * can be changed to the shift state with some key conditions.
-+ */
-+ if (*current_state == IBUS_KEY_ISO_LEVEL_STATE_SHIFT) {
-+ *current_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
-+ if (current_level == IBUS_KEY_ISO_LEVEL_3)
-+ new_mods_depressed &= ~active_key->mod5_mask;
-+ else if (current_level == IBUS_KEY_ISO_LEVEL_5)
-+ new_mods_depressed &= ~active_key->mod3_mask;
-+ else
-+ g_assert_not_reached ();
-+ if (sym != system_sym)
-+ *clear_virtual_state = TRUE;
-+ } else if (!*current_state && *reverse_state &&
-+ (((current_level == IBUS_KEY_ISO_LEVEL_3) &&
-+ (new_mods_depressed & active_key->mod3_mask)) ||
-+ ((current_level == IBUS_KEY_ISO_LEVEL_5) &&
-+ (new_mods_depressed & active_key->mod5_mask))
-+ )) {
- /* The unlikely case !priv->is_pressed_mod3 and
- * priv->is_pressed_mod5 means that "de(T3)" keymap gives
- * the pressed "Level3_Shift" keysym and
- * the released "Level5_Latch" keysym for the key <RALT>.
-- * Maybe a bug in "de(T3)" so need to clear the MOD5 state
-- * by the pressed "Level3_Shift" keysym.
-+ * This case can happen if the normal keysym and shift keysym
-+ * are different.
- */
-- *is_pressed_reverse_mod = FALSE;
-- new_mods_depressed &= ~reverse_mode_mask;
-+ *reverse_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
-+ *is_invalid_key = TRUE;
-+ if (current_level == IBUS_KEY_ISO_LEVEL_3)
-+ new_mods_depressed &= ~active_key->mod3_mask;
-+ else if (current_level == IBUS_KEY_ISO_LEVEL_5)
-+ new_mods_depressed &= ~active_key->mod5_mask;
-+ else
-+ g_assert_not_reached ();
- *clear_virtual_state = TRUE;
- g_debug ("Got a wrong released %s key without the "
-- "pressed one. Maybe a bug in XKB.",
-+ "pressed one. Maybe the next Shift state will "
-+ "produce the delayed pressed one.",
- current_level == IBUS_KEY_ISO_LEVEL_3 ?
- "Level3" : "Level5");
- }
-- /* The "lv(tilde)" keymap gives the "Level3_Shift" keysym
-- * with the key <RALT>.
-- */
-- if ((current_level == IBUS_KEY_ISO_LEVEL_3 &&
-- sym == IBUS_KEY_ISO_Level3_Shift) ||
-- (current_level == IBUS_KEY_ISO_LEVEL_5 &&
-- sym == IBUS_KEY_ISO_Level5_Shift)) {
-- new_mods_depressed &= ~current_mode_mask;
-- if (sym != system_sym)
-- clear_virtual_state = TRUE;
-- }
- }
- break;
- case IBUS_KEY_Shift_L:
-@@ -706,7 +759,8 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
- IBusXkbKeymap *active_key,
- xkb_mod_mask_t mods_depressed,
- xkb_mod_mask_t new_mods_depressed,
-- gboolean clear_virtual_state)
-+ gboolean clear_virtual_state,
-+ gboolean is_invalid_key)
- {
- IBusWaylandIMPrivate *priv;
- uint32_t code = key + 8;
-@@ -735,8 +789,9 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
- if (clear_virtual_state)
- priv->is_virtual_latch_state = FALSE;
- }
-- if (!priv->is_virtual_latch_state ||
-- (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
-+ if (G_LIKELY (!is_invalid_key) &&
-+ (!priv->is_virtual_latch_state ||
-+ (state != WL_KEYBOARD_KEY_STATE_RELEASED))) {
- xkb_state_update_key (active_key->state, code,
- (state == WL_KEYBOARD_KEY_STATE_RELEASED)
- ? XKB_KEY_UP : XKB_KEY_DOWN);
-@@ -751,7 +806,8 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
- /* "de(T3)" keymap gives the pressed "Level3_Shift" keysym and
- * sets both MOD3(Level5) and MOD5(Level3) states after
- * xkb_state_update_key() is called with the keypress.
-- * Maybe a bug in "de(T3)" and sets MOD5(Level3) again here.
-+ * FIXME: Should set `is_invalid_key` in this case of the "de(T3)"
-+ * keymap too not to call xkb_state_update_key() here?
- */
- if (G_UNLIKELY (new_mods_depressed != new2_mods_depressed)) {
- xkb_state_update_mask (active_key->state, new_mods_depressed,
-@@ -1617,6 +1673,7 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- IBusXkbKeymap *active_key;
- xkb_mod_mask_t mods_depressed, new_mods_depressed;
- gboolean clear_virtual_state = FALSE;
-+ gboolean is_invalid_key = FALSE;
-
- g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), FALSE);
- priv = ibus_wayland_im_get_instance_private (wlim);
-@@ -1652,7 +1709,8 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- active_key,
- filtered,
- &new_mods_depressed,
-- &clear_virtual_state);
-+ &clear_virtual_state,
-+ &is_invalid_key);
- filtered = ibus_wayland_im_commit_key_event (wlim,
- key,
- modifiers,
-@@ -1661,14 +1719,16 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
- active_key,
- filtered,
- &new_mods_depressed,
-- &clear_virtual_state);
-+ &clear_virtual_state,
-+ &is_invalid_key);
- ibus_wayland_im_update_virtual_xkb_state (wlim,
- key,
- state,
- active_key,
- mods_depressed,
- new_mods_depressed,
-- clear_virtual_state);
-+ clear_virtual_state,
-+ is_invalid_key);
- return filtered;
- }
-
-@@ -2047,6 +2107,21 @@ key_event_check_repeat (IBusWaylandIM *wlim,
- g_clear_pointer (&repeating_event.ibus_object_path, g_free);
- if (!priv->ibuscontext)
- return FALSE;
-+ if (IS_DEAD_KEY (event->sym)) {
-+ /* With "fr(ergol)" keymap, in case that key <AD09> is typed
-+ * twice, the second pressed keysym is "dead_diaeresis" and
-+ * the second released keysym is "ISO_Level5_Latch".
-+ * The keysym "dead_diaeresis" should not be auto-repeated
-+ * in this case.
-+ */
-+ if (priv->released_dead_key_wo_press == event->sym) {
-+ priv->released_dead_key_wo_press = 0;
-+ return TRUE;
-+ }
-+ priv->pressed_dead_key = event->sym;
-+ } else {
-+ priv->released_dead_key_wo_press = 0;
-+ }
- source = g_timeout_source_new (priv->repeat_delay);
- g_source_attach (source, NULL);
- g_source_unref (source);
-@@ -2060,6 +2135,30 @@ key_event_check_repeat (IBusWaylandIM *wlim,
- g_source_set_callback (source, _process_key_event_repeat_delay_cb,
- &repeating_event, NULL);
- } else {
-+ if (event->sym != repeating_event.sym) {
-+ if (IS_DEAD_KEY (event->sym)) {
-+ if (event->sym == priv->pressed_dead_key) {
-+ priv->pressed_dead_key = 0;
-+ } else if (!priv->pressed_dead_key) {
-+ /* With "fr(ergol)" keymap, in case that key <AD09> is
-+ * typed twice, the first pressed keysym is
-+ * "ISO_Level5_Latch" and the first released keysym is
-+ * "dead_diaeresis" with the MOD3(the level5) mask,
-+ * the second pressed keysym is "dead_diaeresis" and
-+ * the second released keysym is "ISO_Level5_Latch".
-+ * So the first released "dead_diaeresis" is invalid and
-+ * the second pressed "dead_diaeresis" should be released
-+ * immediately.
-+ */
-+ priv->released_dead_key_wo_press = event->sym;
-+ }
-+ }
-+ } else if (IS_DEAD_KEY (repeating_event.sym)) {
-+ if (repeating_event.sym == priv->pressed_dead_key)
-+ priv->pressed_dead_key = 0;
-+ } else {
-+ priv->released_dead_key_wo_press = 0;
-+ }
- if (repeating_event.repeat_rate_id) {
- g_source_remove (repeating_event.repeat_rate_id);
- repeating_event.repeat_rate_id = 0;
---
-2.53.0
-
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 9fd19c2..0e34d54 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -1,7 +1,7 @@
From 44e22e5e7602bc79040df42cbfa836ba32da01ef Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 16 Jul 2026 12:37:28 +0900
-Subject: [PATCH] src/tests: Fix CI issues in GNOME 50
+Subject: [PATCH 1/2] src/tests: Fix CI issues in GNOME 50
- 100-ibus-ci.rules: Disable authentication dialogs in gnome-shell
- ibus-compose:
@@ -704,3 +704,1843 @@ index 4e54420f..dee675cb 100755
--
2.54.0
+From 1a0d9a6bc3afe241838c97ca381e1bd0b376a2b5 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Sun, 19 Jul 2026 18:10:01 +0900
+Subject: [PATCH 2/2] src/tests/ibus-compose: Fix typo
+
+Fixes: 44e22e5e
+---
+ meson.build | 1 +
+ src/tests/ibus-compose.c | 4 ++--
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 7cd906d7..c57b002d 100644
+--- a/meson.build
++++ b/meson.build
+@@ -391,6 +391,7 @@ install_emptydir(get_option('datadir') / 'ibus' / 'engine')
+ summary({
+ 'Install prefix': get_option('prefix'),
+ 'CFLAGS': get_option('c_args'),
++ 'LDFLAGS': get_option('c_link_args'),
+ 'Build endianness': endian,
+ 'PYTHON': python.full_path(),
+ 'Build PyGObject': enable_pygobject,
+diff --git a/src/tests/ibus-compose.c b/src/tests/ibus-compose.c
+index c36b4c81..d0c573e5 100644
+--- a/src/tests/ibus-compose.c
++++ b/src/tests/ibus-compose.c
+@@ -144,7 +144,7 @@ take_screenshot (void)
+ dt = g_date_time_new_from_unix_utc (t);
+ g_return_if_fail (dt);
+
+- while (FALSE) {
++ do {
+ prgname = g_path_get_basename (g_get_prgname ());
+ casename = g_path_get_basename (g_test_get_path ());
+ ibus_break_if_fail (prgname);
+@@ -173,7 +173,7 @@ take_screenshot (void)
+ std_output ? std_output : "",
+ std_error);
+ }
+- }
++ } while (FALSE);
+
+ g_free (prgname);
+ g_free (casename);
+--
+2.54.0
+
+From 84aaf61fc56735c65eb9c4686aa773cb3b05faa2 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 29 Jun 2026 12:50:56 +0900
+Subject: [PATCH 1/5] client/wayland: Fix latch key in Latvian(tilde) keymap
+
+"lv(tilde)" keymap has `ISO_Level3_Latch` and there are several issues.
+1. KDE Wayland does not handle `ISO_Level3_Latch` keysym but outputs the
+ tilde even if the system keymap is "lv(tilde)". The previous change
+ 0104486 fixes that issue in IBus side.
+2. There is a race when @IBusWaylandIM receives the system keymap from
+ the Wayland compositor and the user keymap from the IBus XKB
+ engine and the user keymap should not be overridden by the system one.
+3. @IBusWaylandIM can handle the compose keys only if `ISO_Level3_Latch`
+ is held pressing at present. But the latch state should be held when
+ the latch key is released but cleared when other keys are pressed.
+4. In case the system keymap is not "lv(tilde)", E.g. "us", the release
+ event of the virtual `ISO_Level3_Latch` key, the Wayland compositor
+ immediately sends to zero modifier state to clear the latch state.
+ So probably @IBusWaylandIM should not send the release event of the
+ latch key.
+5. XKB group ID can be switched by XKB option key likes `grp:toggle`
+ but clicking keyboard icon in KDE panel does not work and maybe
+ it's a bug in KDE compositor.
+6. Fix to clear modifiers with the key release of Shift + Latch key.
+7. Save CapsLock state
+
+Fixes: 0104486
+Fixes: #2869
+Closes: rhbz#2444009
+---
+ client/wayland/ibuswaylandim.c | 360 ++++++++++++++++++++++++++-------
+ 1 file changed, 285 insertions(+), 75 deletions(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index d7d12d05..95b330f8 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -40,6 +40,9 @@
+ #include "virtual-keyboard-unstable-v1-client-protocol.h"
+ #include "ibuswaylandim.h"
+
++/* keysym & keycode should not be logged for the security issue. */
++/* #define IBUS_LOG_SHOW_KEYSYM */
++
+ enum {
+ PROP_0 = 0,
+ PROP_BUS,
+@@ -146,6 +149,7 @@ struct _IBusWaylandIMPrivate
+ guint preedit_cursor_pos;
+ guint preedit_mode;
+ IBusModifierType modifiers;
++ gboolean is_virtual_latch_state;
+ gboolean hiding_preedit_text;
+ IBusInputHints ibus_hints;
+ IBusInputPurpose ibus_purpose;
+@@ -203,10 +207,6 @@ static char _use_sync_mode = 1;
+
+ static guint wayland_im_signals[LAST_SIGNAL] = { 0 };
+
+-static void input_method_deactivate
+- (void *data,
+- struct zwp_input_method_union *input_method,
+- struct zwp_input_method_context_v1 *context);
+ static GObject *ibus_wayland_im_constructor (GType type,
+ guint n_params,
+ GObjectConstructParam
+@@ -228,6 +228,19 @@ static gboolean ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ xkb_keysym_t sym,
+ gboolean
+ filtered);
++static void input_method_deactivate
++ (void *data,
++ struct zwp_input_method_union *input_method,
++ struct zwp_input_method_context_v1 *context);
++static void input_method_keyboard_modifiers
++ (void *data,
++ struct zwp_keyboard_union *keyboard,
++ uint32_t key_serial,
++ uint32_t
++ mods_depressed,
++ uint32_t mods_latched,
++ uint32_t mods_locked,
++ uint32_t group);
+
+
+ static char
+@@ -1079,7 +1092,7 @@ ibus_xkb_keymap_update_with_keymap (IBusXkbKeymap *ibus_keymap,
+ xkb_state_unref (ibus_keymap->state);
+ if (ibus_keymap->keymap)
+ xkb_keymap_unref (ibus_keymap->keymap);
+- ibus_keymap->keymap = keymap;
++ ibus_keymap->keymap = xkb_keymap_ref (keymap);
+ ibus_keymap->state = state;
+
+ /* xkb_map_mod_get_index() can return any xkb_mod_index_t value, including
+@@ -1115,6 +1128,7 @@ _bus_global_engine_changed_cb (IBusBus *bus,
+ IBusWaylandIMPrivate *priv;
+ IBusEngineDesc *desc;
+ struct xkb_keymap *keymap;
++ gboolean has_keymap = FALSE;
+
+ g_return_if_fail (IBUS_IS_BUS (bus));
+ g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
+@@ -1127,10 +1141,18 @@ _bus_global_engine_changed_cb (IBusBus *bus,
+ * immediately without checking the Gsettings.
+ */
+ keymap = create_user_xkb_keymap (priv->xkb_context, desc);
+- if (keymap && !ibus_xkb_keymap_update_with_keymap (&priv->key_user,
+- keymap)) {
++ if (keymap) {
++ has_keymap = ibus_xkb_keymap_update_with_keymap (&priv->key_user,
++ keymap);
+ xkb_keymap_unref (keymap);
+ }
++ if (priv->verbose) {
++ fprintf (priv->log, "New engine:%s keymap:%s state:%s\n",
++ ibus_engine_desc_get_name (desc),
++ has_keymap ? "TRUE" : "FALSE",
++ priv->key_user.state ? "TRUE" : "FALSE");
++ fflush (priv->log);
++ }
+ g_object_unref (desc);
+ }
+
+@@ -1145,6 +1167,7 @@ input_method_keyboard_keymap (void *data,
+ IBusWaylandIM *wlim = data;
+ IBusWaylandIMPrivate *priv;
+ struct xkb_keymap *keymap;
++ gboolean has_keymap = FALSE;
+
+ if (!IBUS_IS_WAYLAND_IM (wlim)) {
+ close (fd);
+@@ -1194,25 +1217,25 @@ input_method_keyboard_keymap (void *data,
+ return;
+ }
+ keymap = create_system_xkb_keymap (priv->xkb_context, format, fd, size);
+- if (keymap &&
+- !ibus_xkb_keymap_update_with_keymap (&priv->key_sys,
+- xkb_keymap_ref (keymap))) {
+- xkb_keymap_unref (keymap);
+- g_clear_pointer (&keymap, xkb_keymap_unref);
+- }
+- if (keymap && !priv->key_user.state &&
+- !ibus_xkb_keymap_update_with_keymap (&priv->key_user,
+- xkb_keymap_ref (keymap))) {
+- xkb_keymap_unref (keymap);
+- g_clear_pointer (&keymap, xkb_keymap_unref);
+- }
+- if (keymap)
++ if (keymap) {
++ has_keymap = ibus_xkb_keymap_update_with_keymap (&priv->key_sys,
++ keymap);
++ if (!has_keymap)
++ g_clear_pointer (&keymap, xkb_keymap_unref);
++ }
++ if (has_keymap && !priv->key_user.state) {
++ has_keymap = ibus_xkb_keymap_update_with_keymap (&priv->key_user,
++ keymap);
++ if (!has_keymap)
++ g_clear_pointer (&keymap, xkb_keymap_unref);
++ }
++ if (has_keymap)
+ xkb_keymap_unref (keymap);
+ if (priv->verbose) {
+ fprintf (priv->log, "System keymap format:%u fd:%d size:%u "
+ "keymap:%s state:%s\n",
+ format, fd, size,
+- keymap ? "TRUE" : "FALSE",
++ has_keymap ? "TRUE" : "FALSE",
+ priv->key_sys.state ? "TRUE" : "FALSE");
+ fflush (priv->log);
+ }
+@@ -1253,6 +1276,7 @@ _get_seat_with_name (GPtrArray *seats,
+ return NULL;
+ }
+
++
+ static gboolean
+ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ uint32_t key,
+@@ -1263,8 +1287,13 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ {
+ IBusWaylandIMPrivate *priv;
+ IBusXkbKeymap *active_key;
++ xkb_mod_mask_t mods_depressed = 0, new_mods_depressed = 0;
++ xkb_mod_mask_t mods_locked;
++ xkb_layout_index_t group = 0;
++ xkb_keysym_t system_sym = sym;
+ uint32_t code = key + 8;
+ uint32_t ch;
++ gboolean clear_virtual_state = FALSE;
+
+ g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), FALSE);
+ priv = ibus_wayland_im_get_instance_private (wlim);
+@@ -1288,49 +1317,135 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ active_key = &priv->key_user;
+ if (!active_key->state)
+ return FALSE;
++ mods_depressed = xkb_state_serialize_mods (active_key->state,
++ XKB_STATE_DEPRESSED |
++ XKB_STATE_LATCHED);
++ new_mods_depressed = mods_depressed;
++ mods_locked = xkb_state_serialize_mods (active_key->state,
++ XKB_STATE_MODS_LOCKED);
++ /* XKB group layout is configured in the system keymap but not the
++ * user keymap which always includes a single layout.
++ */
++ if (priv->key_sys.state) {
++ group = xkb_state_serialize_layout (priv->key_sys.state,
++ XKB_STATE_LAYOUT_LOCKED);
++ }
+ if (!filtered) {
+-#if 0
+- /* FIXME: Need to confirm any modifiers should be ignored. */
+- if (!ibus_accelerator_valid (
+- sym,
+- modifiers & IBUS_MODIFIER_MASK & ~IBUS_SHIFT_MASK)) {
+- filtered = TRUE;
+- }
+-#else
++ if (priv->key_sys.state)
++ system_sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
+ switch (sym) {
+- case IBUS_ISO_Level2_Latch:
+- case IBUS_ISO_Level3_Latch:
+- case IBUS_ISO_Level5_Latch:
++ case IBUS_KEY_ISO_Level2_Latch:
+ filtered = TRUE;
+ break;
++ /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
++ * Level3_Shift is caused by Alt key in lv(tilde) keymap.
++ */
++ case IBUS_KEY_ISO_Level3_Latch:
++ case IBUS_KEY_ISO_Level3_Shift:
++ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
++ new_mods_depressed |= active_key->mod5_mask;
++ if (sym != system_sym)
++ priv->is_virtual_latch_state = TRUE;
++ filtered = TRUE;
++ }
++ break;
++ /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
++ */
++ case IBUS_KEY_ISO_Level5_Latch:
++ case IBUS_KEY_ISO_Level5_Shift:
++ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
++ new_mods_depressed |= active_key->mod3_mask;
++ if (sym != system_sym)
++ priv->is_virtual_latch_state = TRUE;
++ filtered = TRUE;
++ }
++ break;
++ case IBUS_KEY_Shift_L:
++ case IBUS_KEY_Shift_R:
++ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
++ new_mods_depressed |= active_key->shift_mask;
++ else
++ new_mods_depressed &= ~active_key->shift_mask;
++ break;
+ default:;
+ }
++#ifdef IBUS_LOG_SHOW_KEYSYM
++ if (priv->verbose) {
++ fprintf (priv->log, "%s key:%u sym:%x system_sym:%x modifiers:%x "
++ "state:%s filtered:%d\n",
++ G_STRFUNC,
++ key, sym, system_sym, modifiers,
++ state ? "press" : "release",
++ filtered);
++ fflush (priv->log);
++ }
+ #endif
+ }
+- /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
+- * but forwards the key events to the focused application here.
+- * Because some applications treat the printable keys as control keys,
+- * E.g. game apps "hjkl" use the cursor move like VI mode.
+- * Unfortunately the Wayland input-method protocol does not provide
+- * the fallback logic after apps handle the key events like GTK3/2
+- * IM modules. But The input-method always should handles key events
+- * prior to apps.
+- */
+- if (!filtered && (state != WL_KEYBOARD_KEY_STATE_RELEASED) &&
+- !priv->use_sys_keymap) {
+- ch = xkb_state_key_get_utf32 (active_key->state, code);
+- if (!(modifiers & IBUS_MODIFIER_MASK & ~IBUS_SHIFT_MASK) &&
+- ch && ch != '\n' && ch != '\b' && ch != '\r' && ch != '\t' &&
+- ch != '\033' && ch != '\x7f') {
+- gchar buff[8] = { 0, };
+- buff[g_unichar_to_utf8 (ch, buff)] = '\0';
+- ibus_wayland_im_commit_text (wlim, buff);
+- filtered = TRUE;
++ if (state != WL_KEYBOARD_KEY_STATE_RELEASED) {
++ ch = ibus_keyval_to_unicode (sym);
++ if (ch == 0 || g_unichar_iscntrl (ch))
++ ch = xkb_state_key_get_utf32 (active_key->state, code);
++/* No text with Control & Alt & Super keys */
++#ifndef GDK_WINDOWING_QUARTZ
++# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
++ IBUS_CONTROL_MASK | IBUS_MOD1_MASK | IBUS_MOD4_MASK)
++#else
++# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
++ IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
++#endif
++ if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
++ !g_unichar_iscntrl (ch)) || filtered) {
++ /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
++ * but forwards the key events to the focused application here.
++ * Because some applications treat the printable keys as control keys,
++ * E.g. game apps "hjkl" use the cursor move like VI mode.
++ * Unfortunately the Wayland input-method protocol does not provide
++ * the fallback logic after apps handle the key events like GTK3/2
++ * IM modules. But the input-method always should handle key events
++ * prior to apps.
++ */
++ if (!filtered && !priv->use_sys_keymap) {
++ gchar buff[8] = { 0, };
++ g_unichar_to_utf8 (ch, buff);
++ ibus_wayland_im_commit_text (wlim, buff);
++ filtered = TRUE;
++ }
++ /* If `filtered` is %TRUE, the keysym can be eaten for the compose
++ * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
++ * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)"
++ * keymap. However if you configure multiple keymaps, AltGr-Shift
++ * may produce the keymap switch although AltGr-Shift-V produces
++ * `Greek_OMEGA`.
++ * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
++ */
++ if (modifiers & IBUS_MOD3_MASK) {
++ new_mods_depressed &= ~active_key->mod3_mask;
++ clear_virtual_state = TRUE;
++ }
++ if (modifiers & IBUS_MOD5_MASK) {
++ new_mods_depressed &= ~active_key->mod5_mask;
++ clear_virtual_state = TRUE;
++ }
+ }
++#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
++ }
++ if (priv->is_virtual_latch_state) {
++ if (new_mods_depressed != mods_depressed) {
++ input_method_keyboard_modifiers (wlim, NULL, 0,
++ new_mods_depressed,
++ 0,
++ mods_locked,
++ group);
++ }
++ if (clear_virtual_state)
++ priv->is_virtual_latch_state = FALSE;
++ }
++ if (!priv->is_virtual_latch_state ||
++ (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
++ xkb_state_update_key (active_key->state, code,
++ (state == WL_KEYBOARD_KEY_STATE_RELEASED)
++ ? XKB_KEY_UP : XKB_KEY_DOWN);
+ }
+- xkb_state_update_key (active_key->state, code,
+- (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+- ? XKB_KEY_UP : XKB_KEY_DOWN);
+ return filtered;
+ }
+
+@@ -1777,12 +1892,36 @@ input_method_keyboard_key (void *data,
+ /* xkb_key_get_syms() does not return the capital syms with Shift key. */
+ if (priv->key_sys.state)
+ event.sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
+- if (event.sym != IBUS_KEY_Multi_key && priv->key_user.state)
+- event.sym = xkb_state_key_get_one_sym (priv->key_user.state, code);
++ switch (event.sym) {
++ case IBUS_KEY_Multi_key:
++ case IBUS_KEY_ISO_Next_Group:
++ break;
++ default:
++ if (priv->key_user.state)
++ event.sym = xkb_state_key_get_one_sym (priv->key_user.state, code);
++ }
+ event.modifiers = priv->modifiers;
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ event.modifiers |= IBUS_RELEASE_MASK;
+ event.wlim = wlim;
++#ifdef IBUS_LOG_SHOW_KEYSYM
++ if (priv->verbose) {
++ fprintf (priv->log, "%s serial:%u time:%u key:%u "
++ "sym:%x system_user:%x system_sym:%x "
++ "modifiers:%x state:%s\n",
++ G_STRFUNC,
++ key_serial, time, key,
++ event.sym,
++ priv->key_user.state ?
++ xkb_state_key_get_one_sym (priv->key_user.state, code) :
++ 0,
++ priv->key_sys.state ?
++ xkb_state_key_get_one_sym (priv->key_sys.state, code) :
++ 0,
++ event.modifiers, state ? "press" : "release");
++ fflush (priv->log);
++ }
++#endif
+
+ key_event_check_repeat (wlim, &event);
+ switch (_use_sync_mode) {
+@@ -1796,6 +1935,29 @@ input_method_keyboard_key (void *data,
+ }
+
+
++/**
++ * input_method_keyboard_modifiers:
++ *
++ * This is a common API of input_method_keyboard_modifiers_v1() and
++ * input_method_keyboard_modifiers_v2().
++ * If you configure multiple XKB layouts in the system with
++ * /etc/vconsole.conf file, you can switch the active layout with
++ * the XKB options and @group is changed with the shortcut key like
++ * "grp:lalt_lshift_toggle" but If you click the keyboard icon in
++ * Plasma KDE and change the active layout with GUI, @group is not
++ * changed and I assume it's a bug [1].
++ * Currently there are two cases of the combinations of the IBus keymap and
++ * the system keymap supported by IBus:
++ * 1. Always Same keymaps
++ * E.g. IBus keymap is "lv(tilde)" and system one is "lv(tilde)"
++ * 2. System keymap is "US" and switch IBus keymaps with Super-space key
++ * E.g. IBus keymap is "lv(tilde)" and system one is "us"
++ * So you should use both XKB option keys and IBus shortcutkeys to switch the
++ * keymaps to change both XKB keymaps and IBus keymaps but you should not
++ * click the keyboard icon in KDE.
++ *
++ * [1] https://bugs.kde.org/show_bug.cgi?id=518371
++ */
+ static void
+ input_method_keyboard_modifiers (void *data,
+ struct zwp_keyboard_union *keyboard,
+@@ -1816,27 +1978,59 @@ input_method_keyboard_modifiers (void *data,
+ active_key = &priv->key_sys;
+ else
+ active_key = &priv->key_user;
+- if (priv->key_user.state) {
+- xkb_state_update_mask (priv->key_user.state, mods_depressed,
+- mods_latched, mods_locked, 0, 0, group);
+- }
+- /* Pressing XKB group key likes Alt_R with grp:toggle calls
+- * input_method_keyboard_modifiers() with the updated `group`
+- * and need to update priv->key_sys.state to switch the XKB group
+- * in the system keymap.
+- *
+- * XKB group key can switch `group` but clicking the keyboard icon
+- * of KDE does not change `group` at present. Maybe a bug in the
+- * KDE Wayland compositor.
++ /* Do not reset Latch modifiers by system in case the system keymap
++ * has no the Latch key.
++ * In case the user keymap is "lv(tilde)" and the system one is "us",
++ * input_method_keyboard_modifiers() clears state of Level3_Latch key
++ * immediately after the Level3_Latch key is released because the "us"
++ * keymap does not have the latch keys.
++ * The behavior is different in case both user and system keymaps are
++ * "lv(tilde)".
++ * So `is_virtual_latch_state` keeps the virtual state of the latch keys
++ * not to override the state of the "us" keymap.
+ */
+- if (priv->key_sys.state) {
+- xkb_state_update_mask (priv->key_sys.state, mods_depressed,
+- mods_latched, mods_locked, 0, 0, group);
++ if (!priv->is_virtual_latch_state || key_serial == 0) {
++ if (priv->key_user.state) {
++ xkb_state_update_mask (priv->key_user.state, mods_depressed,
++ mods_latched, mods_locked, 0, 0, group);
++ }
++ /* Pressing XKB group key likes Alt_R with grp:toggle calls
++ * input_method_keyboard_modifiers() with the updated `group`
++ * and need to update priv->key_sys.state to switch the XKB group
++ * in the system keymap.
++ *
++ * XKB group key can switch `group` but clicking the keyboard icon
++ * of KDE does not change `group` at present. Maybe a bug in the
++ * KDE Wayland compositor.
++ */
++ if (priv->key_sys.state) {
++ xkb_state_update_mask (priv->key_sys.state, mods_depressed,
++ mods_latched, mods_locked, 0, 0, group);
++ }
+ }
+ if (active_key->state) {
++ /* CapsLock needs XKB_STATE_MODS_LOCKED */
+ mask = xkb_state_serialize_mods (active_key->state,
+ XKB_STATE_DEPRESSED |
+- XKB_STATE_LATCHED);
++ XKB_STATE_LATCHED |
++ XKB_STATE_MODS_LOCKED);
++ }
++ if (priv->verbose) {
++ struct xkb_state *state = active_key->state;
++ fprintf (priv->log, "Update modifiers serial:%u depress:%X latch:%X "
++ "lock:%X group:%X orig_depre:%X orig_latch:%X "
++ "orig_lock:%X\n",
++ key_serial, mods_depressed, mods_latched, mods_locked, group,
++ state ?
++ xkb_state_serialize_mods (state, XKB_STATE_DEPRESSED) :
++ 0xFFFFFFFF,
++ state ?
++ xkb_state_serialize_mods (state, XKB_STATE_LATCHED) :
++ 0xFFFFFFFF,
++ state ?
++ xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED) :
++ 0xFFFFFFFF);
++ fflush (priv->log);
+ }
+
+ priv->modifiers = 0;
+@@ -1863,6 +2057,9 @@ input_method_keyboard_modifiers (void *data,
+ if (mask & active_key->meta_mask)
+ priv->modifiers |= IBUS_META_MASK;
+
++ if (!key_serial)
++ return;
++
+ switch (priv->version) {
+ case INPUT_METHOD_V1:
+ zwp_input_method_context_v1_modifiers (priv->context, key_serial,
+@@ -2834,6 +3031,7 @@ ibus_wayland_im_constructor (GType type,
+ IBusWaylandSeat *seat = NULL;
+ IBusEngineDesc *desc;
+ struct xkb_keymap *keymap = NULL;
++ gboolean has_keymap = FALSE;
+
+ object = G_OBJECT_CLASS (ibus_wayland_im_parent_class)->constructor (
+ type, n_params, params);
+@@ -2882,9 +3080,21 @@ ibus_wayland_im_constructor (GType type,
+ desc = ibus_bus_get_global_engine (priv->ibusbus);
+ if (desc)
+ keymap = create_user_xkb_keymap (priv->xkb_context, desc);
+- if (keymap && !ibus_xkb_keymap_update_with_keymap (&priv->key_user,
+- keymap)) {
+- g_clear_pointer (&keymap, xkb_keymap_unref);
++ if (keymap) {
++ has_keymap = ibus_xkb_keymap_update_with_keymap (&priv->key_user,
++ keymap);
++ xkb_keymap_unref (keymap);
++ }
++ if (priv->verbose) {
++ if (!desc) {
++ fprintf (priv->log, "Constructor has no global engine\n");
++ } else {
++ fprintf (priv->log, "Constructor engine %s keymap:%s state:%s\n",
++ ibus_engine_desc_get_name (desc),
++ has_keymap ? "TRUE" : "FALSE",
++ priv->key_user.state ? "TRUE" : "FALSE");
++ }
++ fflush (priv->log);
+ }
+ ibus_bus_set_watch_ibus_signal (priv->ibusbus, TRUE);
+ g_signal_connect (priv->ibusbus, "global-engine-changed",
+--
+2.54.0
+
+From df8230ff90ec3aafffe10af769ad66aa948ac349 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 29 Jun 2026 12:51:07 +0900
+Subject: [PATCH 2/5] client/wayland: Refactor ibus_wayland_im_post_key()
+
+Separate 3 functions.
+
+Fixes: #2869
+Closes: rhbz#2444009
+---
+ client/wayland/ibuswaylandim.c | 377 +++++++++++++++++++++------------
+ 1 file changed, 246 insertions(+), 131 deletions(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index 95b330f8..b16ff521 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -367,6 +367,81 @@ ibus_wayland_im_commit_text (IBusWaylandIM *wlim,
+ }
+
+
++static gboolean
++ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
++ uint32_t key,
++ uint32_t modifiers,
++ uint32_t state,
++ xkb_keysym_t sym,
++ IBusXkbKeymap *active_key,
++ gboolean filtered,
++ xkb_mod_mask_t *new_mods_depressed,
++ gboolean *clear_virtual_state)
++{
++ IBusWaylandIMPrivate *priv;
++ uint32_t code = key + 8;
++ uint32_t ch;
++
++ g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), filtered);
++ priv = ibus_wayland_im_get_instance_private (wlim);
++
++ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
++ return filtered;
++
++ ch = ibus_keyval_to_unicode (sym);
++ if (ch == 0 || g_unichar_iscntrl (ch))
++ ch = xkb_state_key_get_utf32 (active_key->state, code);
++
++/* No text with Control & Alt & Super keys */
++#ifndef GDK_WINDOWING_QUARTZ
++# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
++ IBUS_CONTROL_MASK | IBUS_MOD1_MASK | IBUS_MOD4_MASK)
++#else
++# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
++ IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
++#endif
++ if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
++ !g_unichar_iscntrl (ch)) || filtered) {
++ /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
++ * but forwards the key events to the focused application here.
++ * Because some applications treat the printable keys as control keys,
++ * E.g. game apps "hjkl" use the cursor move like VI mode.
++ * Unfortunately the Wayland input-method protocol does not provide
++ * the fallback logic after apps handle the key events like GTK3/2
++ * IM modules. But the input-method always should handle key events
++ * prior to apps.
++ */
++ if (!filtered && !priv->use_sys_keymap) {
++ gchar buff[8] = { 0, };
++ g_unichar_to_utf8 (ch, buff);
++ ibus_wayland_im_commit_text (wlim, buff);
++ filtered = TRUE;
++ }
++ /* If `filtered` is %TRUE, the keysym can be eaten for the compose
++ * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
++ * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)" keymap.
++ * However if you configure multiple keymaps, AltGr-Shift may produce
++ * the keymap switch although AltGr-Shift-V produces `Greek_OMEGA`.
++ * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
++ */
++ if (modifiers & IBUS_MOD3_MASK) {
++ if (new_mods_depressed)
++ *new_mods_depressed &= ~active_key->mod3_mask;
++ if (clear_virtual_state)
++ *clear_virtual_state = TRUE;
++ }
++ if (modifiers & IBUS_MOD5_MASK) {
++ if (new_mods_depressed)
++ *new_mods_depressed &= ~active_key->mod5_mask;
++ if (clear_virtual_state)
++ *clear_virtual_state = TRUE;
++ }
++ }
++#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
++ return filtered;
++}
++
++
+ static void
+ ibus_wayland_im_key (IBusWaylandIM *wlim,
+ uint32_t key_serial,
+@@ -431,6 +506,152 @@ ibus_wayland_im_keysym (IBusWaylandIM *wlim,
+ }
+
+
++/**
++ * ibus_wayland_im_update_virtual_depressed:
++ *
++ * If the IBus keymap is different from the compositor keymap,
++ * IBus needs to maintain the modifiers state by itself to call
++ * xkb_state_update_mask(), E.g. IBus keymap is "lv(tilde)" and the system
++ * one is "us", because input_method_keyboard_modifiers()
++ * is not called by the Wayland compositor in that case.
++ * So this API updates @mods_depressed with ISO level3 and level5
++ * latch and shift states, and send it to
++ * ibus_wayland_im_update_virtual_xkb_state().
++ */
++static gboolean
++ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
++ uint32_t key,
++ uint32_t modifiers,
++ uint32_t state,
++ xkb_keysym_t sym,
++ IBusXkbKeymap *active_key,
++ gboolean filtered,
++ xkb_mod_mask_t *mods_depressed)
++{
++ IBusWaylandIMPrivate *priv;
++ uint32_t code = key + 8;
++ xkb_mod_mask_t new_mods_depressed;
++ xkb_keysym_t system_sym = sym;
++
++ if (filtered)
++ return filtered;
++
++ g_assert (IBUS_IS_WAYLAND_IM (wlim));
++ g_assert (active_key);
++ g_assert (mods_depressed);
++
++ priv = ibus_wayland_im_get_instance_private (wlim);
++ new_mods_depressed = *mods_depressed;
++
++ if ((modifiers & ~IBUS_RELEASE_MASK ) != new_mods_depressed) {
++ g_warning ("IBus modifiers %X is different from XKB depressed %X",
++ modifiers, new_mods_depressed);
++ }
++ if (priv->key_sys.state)
++ system_sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
++ switch (sym) {
++ case IBUS_KEY_ISO_Level2_Latch:
++ filtered = TRUE;
++ break;
++ /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
++ * Level3_Shift is caused by Alt key in lv(tilde) keymap.
++ */
++ case IBUS_KEY_ISO_Level3_Latch:
++ case IBUS_KEY_ISO_Level3_Shift:
++ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
++ new_mods_depressed |= active_key->mod5_mask;
++ if (sym != system_sym)
++ priv->is_virtual_latch_state = TRUE;
++ filtered = TRUE;
++ }
++ break;
++ /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
++ */
++ case IBUS_KEY_ISO_Level5_Latch:
++ case IBUS_KEY_ISO_Level5_Shift:
++ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
++ new_mods_depressed |= active_key->mod3_mask;
++ if (sym != system_sym)
++ priv->is_virtual_latch_state = TRUE;
++ filtered = TRUE;
++ }
++ break;
++ case IBUS_KEY_Shift_L:
++ case IBUS_KEY_Shift_R:
++ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
++ new_mods_depressed |= active_key->shift_mask;
++ else
++ new_mods_depressed &= ~active_key->shift_mask;
++ break;
++ default:;
++ }
++#ifdef IBUS_LOG_SHOW_KEYSYM
++ if (priv->verbose) {
++ fprintf (priv->log, "%s key:%u sym:%x system_sym:%x modifiers:%x "
++ "state:%s filtered:%d\n",
++ G_STRFUNC,
++ key, sym, system_sym, modifiers, state ? "press" : "release",
++ filtered);
++ fflush (priv->log);
++ }
++#endif
++ *mods_depressed = new_mods_depressed;
++ return filtered;
++}
++
++
++/**
++ * ibus_wayland_im_update_virtual_xkb_state:
++ *
++ * Update virtual IBus xkb_state by KeyPress and KeyRelease.
++ */
++static void
++ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
++ uint32_t key,
++ uint32_t state,
++ IBusXkbKeymap *active_key,
++ xkb_mod_mask_t mods_depressed,
++ xkb_mod_mask_t new_mods_depressed,
++ gboolean clear_virtual_state)
++{
++ IBusWaylandIMPrivate *priv;
++ uint32_t code = key + 8;
++ xkb_mod_mask_t mods_locked;
++ xkb_layout_index_t group = 0;
++
++ g_assert (IBUS_IS_WAYLAND_IM (wlim));
++
++ priv = ibus_wayland_im_get_instance_private (wlim);
++ mods_locked = xkb_state_serialize_mods (active_key->state,
++ XKB_STATE_MODS_LOCKED);
++ /* XKB group layout is configured in the system keymap but not the
++ * user keymap which always includes a single layout.
++ */
++ if (priv->key_sys.state) {
++ group = xkb_state_serialize_layout (priv->key_sys.state,
++ XKB_STATE_LAYOUT_LOCKED);
++ }
++
++ if (priv->is_virtual_latch_state) {
++ if (new_mods_depressed != mods_depressed) {
++ input_method_keyboard_modifiers (wlim, NULL, 0,
++ new_mods_depressed,
++ 0,
++ mods_locked,
++ group);
++ }
++ if (clear_virtual_state)
++ priv->is_virtual_latch_state = FALSE;
++ }
++ if (!priv->is_virtual_latch_state ||
++ (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
++ xkb_state_update_key (active_key->state, code,
++ (state == WL_KEYBOARD_KEY_STATE_RELEASED)
++ ? XKB_KEY_UP : XKB_KEY_DOWN);
++ }
++}
++
++
+ static void
+ _context_commit_text_cb (IBusInputContext *context,
+ IBusText *text,
+@@ -1287,12 +1508,7 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ {
+ IBusWaylandIMPrivate *priv;
+ IBusXkbKeymap *active_key;
+- xkb_mod_mask_t mods_depressed = 0, new_mods_depressed = 0;
+- xkb_mod_mask_t mods_locked;
+- xkb_layout_index_t group = 0;
+- xkb_keysym_t system_sym = sym;
+- uint32_t code = key + 8;
+- uint32_t ch;
++ xkb_mod_mask_t mods_depressed, new_mods_depressed;
+ gboolean clear_virtual_state = FALSE;
+
+ g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), FALSE);
+@@ -1321,131 +1537,30 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ XKB_STATE_DEPRESSED |
+ XKB_STATE_LATCHED);
+ new_mods_depressed = mods_depressed;
+- mods_locked = xkb_state_serialize_mods (active_key->state,
+- XKB_STATE_MODS_LOCKED);
+- /* XKB group layout is configured in the system keymap but not the
+- * user keymap which always includes a single layout.
+- */
+- if (priv->key_sys.state) {
+- group = xkb_state_serialize_layout (priv->key_sys.state,
+- XKB_STATE_LAYOUT_LOCKED);
+- }
+- if (!filtered) {
+- if (priv->key_sys.state)
+- system_sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
+- switch (sym) {
+- case IBUS_KEY_ISO_Level2_Latch:
+- filtered = TRUE;
+- break;
+- /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
+- * Level3_Shift is caused by Alt key in lv(tilde) keymap.
+- */
+- case IBUS_KEY_ISO_Level3_Latch:
+- case IBUS_KEY_ISO_Level3_Shift:
+- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+- new_mods_depressed |= active_key->mod5_mask;
+- if (sym != system_sym)
+- priv->is_virtual_latch_state = TRUE;
+- filtered = TRUE;
+- }
+- break;
+- /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
+- */
+- case IBUS_KEY_ISO_Level5_Latch:
+- case IBUS_KEY_ISO_Level5_Shift:
+- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+- new_mods_depressed |= active_key->mod3_mask;
+- if (sym != system_sym)
+- priv->is_virtual_latch_state = TRUE;
+- filtered = TRUE;
+- }
+- break;
+- case IBUS_KEY_Shift_L:
+- case IBUS_KEY_Shift_R:
+- if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+- new_mods_depressed |= active_key->shift_mask;
+- else
+- new_mods_depressed &= ~active_key->shift_mask;
+- break;
+- default:;
+- }
+-#ifdef IBUS_LOG_SHOW_KEYSYM
+- if (priv->verbose) {
+- fprintf (priv->log, "%s key:%u sym:%x system_sym:%x modifiers:%x "
+- "state:%s filtered:%d\n",
+- G_STRFUNC,
+- key, sym, system_sym, modifiers,
+- state ? "press" : "release",
+- filtered);
+- fflush (priv->log);
+- }
+-#endif
+- }
+- if (state != WL_KEYBOARD_KEY_STATE_RELEASED) {
+- ch = ibus_keyval_to_unicode (sym);
+- if (ch == 0 || g_unichar_iscntrl (ch))
+- ch = xkb_state_key_get_utf32 (active_key->state, code);
+-/* No text with Control & Alt & Super keys */
+-#ifndef GDK_WINDOWING_QUARTZ
+-# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
+- IBUS_CONTROL_MASK | IBUS_MOD1_MASK | IBUS_MOD4_MASK)
+-#else
+-# define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
+- IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
+-#endif
+- if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
+- !g_unichar_iscntrl (ch)) || filtered) {
+- /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
+- * but forwards the key events to the focused application here.
+- * Because some applications treat the printable keys as control keys,
+- * E.g. game apps "hjkl" use the cursor move like VI mode.
+- * Unfortunately the Wayland input-method protocol does not provide
+- * the fallback logic after apps handle the key events like GTK3/2
+- * IM modules. But the input-method always should handle key events
+- * prior to apps.
+- */
+- if (!filtered && !priv->use_sys_keymap) {
+- gchar buff[8] = { 0, };
+- g_unichar_to_utf8 (ch, buff);
+- ibus_wayland_im_commit_text (wlim, buff);
+- filtered = TRUE;
+- }
+- /* If `filtered` is %TRUE, the keysym can be eaten for the compose
+- * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
+- * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)"
+- * keymap. However if you configure multiple keymaps, AltGr-Shift
+- * may produce the keymap switch although AltGr-Shift-V produces
+- * `Greek_OMEGA`.
+- * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
+- */
+- if (modifiers & IBUS_MOD3_MASK) {
+- new_mods_depressed &= ~active_key->mod3_mask;
+- clear_virtual_state = TRUE;
+- }
+- if (modifiers & IBUS_MOD5_MASK) {
+- new_mods_depressed &= ~active_key->mod5_mask;
+- clear_virtual_state = TRUE;
+- }
+- }
+-#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
+- }
+- if (priv->is_virtual_latch_state) {
+- if (new_mods_depressed != mods_depressed) {
+- input_method_keyboard_modifiers (wlim, NULL, 0,
+- new_mods_depressed,
+- 0,
+- mods_locked,
+- group);
+- }
+- if (clear_virtual_state)
+- priv->is_virtual_latch_state = FALSE;
+- }
+- if (!priv->is_virtual_latch_state ||
+- (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
+- xkb_state_update_key (active_key->state, code,
+- (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+- ? XKB_KEY_UP : XKB_KEY_DOWN);
+- }
++ filtered = ibus_wayland_im_update_virtual_depressed (wlim,
++ key,
++ modifiers,
++ state,
++ sym,
++ active_key,
++ filtered,
++ &new_mods_depressed);
++ filtered = ibus_wayland_im_commit_key_event (wlim,
++ key,
++ modifiers,
++ state,
++ sym,
++ active_key,
++ filtered,
++ &new_mods_depressed,
++ &clear_virtual_state);
++ ibus_wayland_im_update_virtual_xkb_state (wlim,
++ key,
++ state,
++ active_key,
++ mods_depressed,
++ new_mods_depressed,
++ clear_virtual_state);
+ return filtered;
+ }
+
+--
+2.54.0
+
+From 056b3e59cc91bdd97efc8b28860bf21e36416afd Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 17 Jun 2026 19:51:08 +0900
+Subject: [PATCH 3/5] client/wayland: Refactor ISO_Level[3|5]_Shift state
+
+The states of ISO Shift keys should be cleared when the key is released
+against ISO Latch keys. E.g. "RALT" key in "lv(tilde)" keymap.
+
+The state of the "RALT" key is strange in "de(T3)" keymap, i.e.
+"ISO_Level3_Shift" keysym for the keypress and ISO_Level5_Latch keysm
+for keyrelease and IBus could not get the correct dead keys and
+clear the state. Now the "is_pressed_mod5" flag checks if the
+key is pressed and add a workaround.
+
+Fixes: #2869
+Closes: rhbz#2444009
+---
+ client/wayland/ibuswaylandim.c | 139 ++++++++++++++++++++++++++++++---
+ 1 file changed, 127 insertions(+), 12 deletions(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index b16ff521..97e3ce38 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -64,6 +64,13 @@ typedef enum
+ INPUT_METHOD_V2,
+ } IMProtocolVersion;
+
++typedef enum {
++ IBUS_KEY_ISO_LEVEL_INVALID,
++ IBUS_KEY_ISO_LEVEL_2,
++ IBUS_KEY_ISO_LEVEL_3,
++ IBUS_KEY_ISO_LEVEL_5
++} IBusKeyIsoLevelValue;
++
+ struct zwp_input_method_context_union {
+ union {
+ struct zwp_input_method_context_v1 *context_v1;
+@@ -163,6 +170,8 @@ struct _IBusWaylandIMPrivate
+
+ IBusXkbKeymap key_user;
+ IBusXkbKeymap key_sys;
++ gboolean is_pressed_mod3;
++ gboolean is_pressed_mod5;
+
+ uint32_t im_serial;
+ int32_t repeat_rate;
+@@ -341,6 +350,28 @@ ibus_wayland_source_new (struct wl_display *display)
+ }
+
+
++static void
++ibus_wayland_im_reset_modifiers (IBusWaylandIM *wlim)
++{
++ IBusWaylandIMPrivate *priv;
++ xkb_layout_index_t group = 0;
++
++ g_assert (IBUS_IS_WAYLAND_IM (wlim));
++
++ priv = ibus_wayland_im_get_instance_private (wlim);
++ priv->is_virtual_latch_state = FALSE;
++ priv->is_pressed_mod3 = FALSE;
++ priv->is_pressed_mod5 = FALSE;
++ if (priv->key_user.state && priv->key_sys.state) {
++ group = xkb_state_serialize_layout (priv->key_sys.state,
++ XKB_STATE_LAYOUT_LOCKED);
++ input_method_keyboard_modifiers (wlim, NULL, 0, 0, 0, 0, group);
++ } else {
++ priv->modifiers = 0;
++ }
++}
++
++
+ static void
+ ibus_wayland_im_commit_text (IBusWaylandIM *wlim,
+ const char *str)
+@@ -526,7 +557,8 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ xkb_keysym_t sym,
+ IBusXkbKeymap *active_key,
+ gboolean filtered,
+- xkb_mod_mask_t *mods_depressed)
++ xkb_mod_mask_t *mods_depressed,
++ gboolean *clear_virtual_state)
+ {
+ IBusWaylandIMPrivate *priv;
+ uint32_t code = key + 8;
+@@ -539,6 +571,7 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ g_assert (IBUS_IS_WAYLAND_IM (wlim));
+ g_assert (active_key);
+ g_assert (mods_depressed);
++ g_assert (clear_virtual_state);
+
+ priv = ibus_wayland_im_get_instance_private (wlim);
+ new_mods_depressed = *mods_depressed;
+@@ -555,25 +588,87 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ break;
+ /* Level3_latch is caused by TLDE key in lv(tilde) keymap.
+ * Level3_Shift is caused by Alt key in lv(tilde) keymap.
++ * Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
+ */
+ case IBUS_KEY_ISO_Level3_Latch:
+ case IBUS_KEY_ISO_Level3_Shift:
+- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+- new_mods_depressed |= active_key->mod5_mask;
+- if (sym != system_sym)
+- priv->is_virtual_latch_state = TRUE;
+- filtered = TRUE;
+- }
+- break;
+- /* Level5_Latch is caused by Shift+Alt key in de(T3) keymap.
+- */
+ case IBUS_KEY_ISO_Level5_Latch:
+ case IBUS_KEY_ISO_Level5_Shift:
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+- new_mods_depressed |= active_key->mod3_mask;
++ switch (sym) {
++ case IBUS_KEY_ISO_Level3_Latch:
++ case IBUS_KEY_ISO_Level3_Shift:
++ priv->is_pressed_mod5 = TRUE;
++ new_mods_depressed |= active_key->mod5_mask;
++ break;
++ case IBUS_KEY_ISO_Level5_Latch:
++ case IBUS_KEY_ISO_Level5_Shift:
++ priv->is_pressed_mod3 = TRUE;
++ new_mods_depressed |= active_key->mod3_mask;
++ break;
++ default:
++ g_assert_not_reached ();
++ }
+ if (sym != system_sym)
+ priv->is_virtual_latch_state = TRUE;
+ filtered = TRUE;
++ } else {
++ IBusKeyIsoLevelValue current_level = IBUS_KEY_ISO_LEVEL_INVALID;
++ gboolean *is_pressed_current_mod = NULL;
++ gboolean *is_pressed_reverse_mod = NULL;
++ xkb_mod_mask_t current_mode_mask = 0;
++ xkb_mod_mask_t reverse_mode_mask = 0;
++ switch (sym) {
++ case IBUS_KEY_ISO_Level3_Latch:
++ case IBUS_KEY_ISO_Level3_Shift:
++ current_level = IBUS_KEY_ISO_LEVEL_3;
++ is_pressed_current_mod = &priv->is_pressed_mod5;
++ is_pressed_reverse_mod = &priv->is_pressed_mod3;
++ current_mode_mask = active_key->mod5_mask;
++ reverse_mode_mask = active_key->mod3_mask;
++ break;
++ case IBUS_KEY_ISO_Level5_Latch:
++ case IBUS_KEY_ISO_Level5_Shift:
++ current_level = IBUS_KEY_ISO_LEVEL_5;
++ is_pressed_current_mod = &priv->is_pressed_mod3;
++ is_pressed_reverse_mod = &priv->is_pressed_mod5;
++ current_mode_mask = active_key->mod3_mask;
++ reverse_mode_mask = active_key->mod5_mask;
++ break;
++ default:;
++ }
++ g_assert (current_level != IBUS_KEY_ISO_LEVEL_INVALID);
++ g_assert (is_pressed_current_mod && is_pressed_reverse_mod);
++ if (G_LIKELY (*is_pressed_current_mod)) {
++ *is_pressed_current_mod = FALSE;
++ } else if (*is_pressed_reverse_mod &&
++ (new_mods_depressed & active_key->mod3_mask)) {
++ /* The unlikely case !priv->is_pressed_mod3 and
++ * priv->is_pressed_mod5 means that "de(T3)" keymap gives
++ * the pressed "Level3_Shift" keysym and
++ * the released "Level5_Latch" keysym for the key <RALT>.
++ * Maybe a bug in "de(T3)" so need to clear the MOD5 state
++ * by the pressed "Level3_Shift" keysym.
++ */
++ *is_pressed_reverse_mod = FALSE;
++ new_mods_depressed &= ~reverse_mode_mask;
++ *clear_virtual_state = TRUE;
++ g_debug ("Got a wrong released %s key without the "
++ "pressed one. Maybe a bug in XKB.",
++ current_level == IBUS_KEY_ISO_LEVEL_3 ?
++ "Level3" : "Level5");
++ }
++ /* The "lv(tilde)" keymap gives the "Level3_Shift" keysym
++ * with the key <RALT>.
++ */
++ if ((current_level == IBUS_KEY_ISO_LEVEL_3 &&
++ sym == IBUS_KEY_ISO_Level3_Shift) ||
++ (current_level == IBUS_KEY_ISO_LEVEL_5 &&
++ sym == IBUS_KEY_ISO_Level5_Shift)) {
++ new_mods_depressed &= ~current_mode_mask;
++ if (sym != system_sym)
++ clear_virtual_state = TRUE;
++ }
+ }
+ break;
+ case IBUS_KEY_Shift_L:
+@@ -649,6 +744,23 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
+ (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ ? XKB_KEY_UP : XKB_KEY_DOWN);
+ }
++ if (priv->is_virtual_latch_state &&
++ (new_mods_depressed != mods_depressed) &&
++ (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
++ xkb_mod_mask_t new2_mods_depressed;
++ new2_mods_depressed = xkb_state_serialize_mods (active_key->state,
++ XKB_STATE_DEPRESSED |
++ XKB_STATE_LATCHED);
++ /* "de(T3)" keymap gives the pressed "Level3_Shift" keysym and
++ * sets both MOD3(Level5) and MOD5(Level3) states after
++ * xkb_state_update_key() is called with the keypress.
++ * Maybe a bug in "de(T3)" and sets MOD5(Level3) again here.
++ */
++ if (G_UNLIKELY (new_mods_depressed != new2_mods_depressed)) {
++ xkb_state_update_mask (active_key->state, new_mods_depressed,
++ 0, mods_locked, 0, 0, group);
++ }
++ }
+ }
+
+
+@@ -1355,6 +1467,7 @@ _bus_global_engine_changed_cb (IBusBus *bus,
+ g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
+ priv = ibus_wayland_im_get_instance_private (wlim);
+ desc = ibus_bus_get_global_engine (bus);
++ ibus_wayland_im_reset_modifiers (wlim);
+ g_assert (desc);
+ g_assert (!g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name));
+ /* Always update priv->key_user even if priv->use_sys_keymap is %FALSE
+@@ -1544,7 +1657,8 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ sym,
+ active_key,
+ filtered,
+- &new_mods_depressed);
++ &new_mods_depressed,
++ &clear_virtual_state);
+ filtered = ibus_wayland_im_commit_key_event (wlim,
+ key,
+ modifiers,
+@@ -2427,6 +2541,7 @@ _create_input_context_done (GObject *object,
+ TRUE);
+ }
+ ibus_input_context_focus_in (priv->ibuscontext);
++ ibus_wayland_im_reset_modifiers (wlim);
+ g_signal_emit (wlim,
+ wayland_im_signals[IBUS_FOCUS_IN],
+ 0,
+--
+2.54.0
+
+From 22ca37a5d43ac7cf049780da5e1452505406c072 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 25 Jun 2026 22:16:42 +0900
+Subject: [PATCH 4/5] client/wayland: Fix infinite dead_diaeresis with fr(ergol) keymap
+
+Typing the key <AD09> twice gives different keysyms between KeyPress
+and KeyRelease events and causes the reverse order of KeyPress and
+KeyRelease of the "dead_diaeresis" keysym.
+Now ibus-wayland checks the special order not to cause the key repeating.
+Also ibus-wayland releases the state of "ISO_Level5_Latch" with typing
+the key <AD09> twice.
+
+Fixes: #2869
+Closes: #2894
+---
+ client/wayland/ibuswaylandim.c | 245 +++++++++++++++++++++++----------
+ 1 file changed, 172 insertions(+), 73 deletions(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index 97e3ce38..efeb6783 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -71,6 +71,12 @@ typedef enum {
+ IBUS_KEY_ISO_LEVEL_5
+ } IBusKeyIsoLevelValue;
+
++typedef enum {
++ IBUS_KEY_ISO_LEVEL_STATE_RELEASE,
++ IBUS_KEY_ISO_LEVEL_STATE_SHIFT,
++ IBUS_KEY_ISO_LEVEL_STATE_LATCH
++} IBusKeyIsoLevelState;
++
+ struct zwp_input_method_context_union {
+ union {
+ struct zwp_input_method_context_v1 *context_v1;
+@@ -170,12 +176,14 @@ struct _IBusWaylandIMPrivate
+
+ IBusXkbKeymap key_user;
+ IBusXkbKeymap key_sys;
+- gboolean is_pressed_mod3;
+- gboolean is_pressed_mod5;
++ IBusKeyIsoLevelState iso_level3_state;
++ IBusKeyIsoLevelState iso_level5_state;
+
+ uint32_t im_serial;
+ int32_t repeat_rate;
+ int32_t repeat_delay;
++ xkb_keysym_t pressed_dead_key;
++ xkb_keysym_t released_dead_key_wo_press;
+
+ GCancellable *cancellable;
+ };
+@@ -360,8 +368,10 @@ ibus_wayland_im_reset_modifiers (IBusWaylandIM *wlim)
+
+ priv = ibus_wayland_im_get_instance_private (wlim);
+ priv->is_virtual_latch_state = FALSE;
+- priv->is_pressed_mod3 = FALSE;
+- priv->is_pressed_mod5 = FALSE;
++ priv->iso_level3_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
++ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
++ priv->pressed_dead_key = 0;
++ priv->released_dead_key_wo_press = 0;
+ if (priv->key_user.state && priv->key_sys.state) {
+ group = xkb_state_serialize_layout (priv->key_sys.state,
+ XKB_STATE_LAYOUT_LOCKED);
+@@ -407,7 +417,8 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
+ IBusXkbKeymap *active_key,
+ gboolean filtered,
+ xkb_mod_mask_t *new_mods_depressed,
+- gboolean *clear_virtual_state)
++ gboolean *clear_virtual_state,
++ gboolean *is_invalid_key)
+ {
+ IBusWaylandIMPrivate *priv;
+ uint32_t code = key + 8;
+@@ -416,9 +427,6 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
+ g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), filtered);
+ priv = ibus_wayland_im_get_instance_private (wlim);
+
+- if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+- return filtered;
+-
+ ch = ibus_keyval_to_unicode (sym);
+ if (ch == 0 || g_unichar_iscntrl (ch))
+ ch = xkb_state_key_get_utf32 (active_key->state, code);
+@@ -431,23 +439,28 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
+ # define _IBUS_NO_TEXT_INPUT_MOD_MASK (\
+ IBUS_CONTROL_MASK | IBUS_MOD2_MASK | IBUS_MOD4_MASK)
+ #endif
+- if ((!filtered && !(modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK) &&
+- !g_unichar_iscntrl (ch)) || filtered) {
+- /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
+- * but forwards the key events to the focused application here.
+- * Because some applications treat the printable keys as control keys,
+- * E.g. game apps "hjkl" use the cursor move like VI mode.
+- * Unfortunately the Wayland input-method protocol does not provide
+- * the fallback logic after apps handle the key events like GTK3/2
+- * IM modules. But the input-method always should handle key events
+- * prior to apps.
+- */
+- if (!filtered && !priv->use_sys_keymap) {
+- gchar buff[8] = { 0, };
+- g_unichar_to_utf8 (ch, buff);
+- ibus_wayland_im_commit_text (wlim, buff);
+- filtered = TRUE;
+- }
++ if ((state == WL_KEYBOARD_KEY_STATE_RELEASED) ||
++ (modifiers & _IBUS_NO_TEXT_INPUT_MOD_MASK)) {
++ return filtered;
++ }
++#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
++
++ /* In case `use_sys_keymap` is %TRUE, IBus does not commit ASCII chars
++ * but forwards the key events to the focused application here.
++ * Because some applications treat the printable keys as control keys,
++ * E.g. game apps "hjkl" use the cursor move like VI mode.
++ * Unfortunately the Wayland input-method protocol does not provide
++ * the fallback logic after apps handle the key events like GTK3/2
++ * IM modules. But the input-method always should handle key events
++ * prior to apps.
++ */
++ if (!filtered && !g_unichar_iscntrl (ch) && !priv->use_sys_keymap) {
++ gchar buff[8] = { 0, };
++ g_unichar_to_utf8 (ch, buff);
++ ibus_wayland_im_commit_text (wlim, buff);
++ filtered = TRUE;
++ }
++ if (!g_unichar_iscntrl (ch) || IS_DEAD_KEY (sym)) {
+ /* If `filtered` is %TRUE, the keysym can be eaten for the compose
+ * preedit by IBus XKB engine. E.g. AltGr-Shift-V key produces
+ * `Level3_Shift' and `Greek_OMEGA` keysym with "us(symbolic)" keymap.
+@@ -456,19 +469,42 @@ ibus_wayland_im_commit_key_event (IBusWaylandIM *wlim,
+ * I'm not clarified with "level3(ralt_switch)" in us XKB keymaps.
+ */
+ if (modifiers & IBUS_MOD3_MASK) {
++ /* With the "fr(ergol)" keymap, Typing the key <AD09> twice
++ * produces the "ISO_Level5_Latch" keysym for the first KeyPress
++ * and the "dead_diaeresis" keysym for the first KeyReleease
++ * because the release key event has MOD3(level5) state.
++ * And the second KeyPress has the "dead_diaeresis" keysym and
++ * the second KeyRelease has the "ISO_Level5_Latch" keysym.
++ * The latch state should be cleared with the dead keys.
++ * I downgrade the latch state to the shift state here and
++ * the shift state will be cleared by the released
++ * "ISO_Level5_Latch" keysym but not the pressed dead keys
++ * because xkb_state_update_key() will revert the MOD3(level5)
++ * state of the depressed xkb_state_serialize_mods()
++ * with the dead keys.
++ */
++ if (priv->iso_level5_state == IBUS_KEY_ISO_LEVEL_STATE_LATCH) {
++ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
++ if (is_invalid_key)
++ *is_invalid_key = TRUE;
++ } else if (clear_virtual_state) {
++ *clear_virtual_state = TRUE;
++ }
+ if (new_mods_depressed)
+ *new_mods_depressed &= ~active_key->mod3_mask;
+- if (clear_virtual_state)
+- *clear_virtual_state = TRUE;
+ }
+ if (modifiers & IBUS_MOD5_MASK) {
++ if (priv->iso_level3_state == IBUS_KEY_ISO_LEVEL_STATE_LATCH) {
++ priv->iso_level3_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
++ if (is_invalid_key)
++ *is_invalid_key = TRUE;
++ } else if (clear_virtual_state) {
++ *clear_virtual_state = TRUE;
++ }
+ if (new_mods_depressed)
+ *new_mods_depressed &= ~active_key->mod5_mask;
+- if (clear_virtual_state)
+- *clear_virtual_state = TRUE;
+ }
+ }
+-#undef _IBUS_NO_TEXT_INPUT_MOD_MASK
+ return filtered;
+ }
+
+@@ -558,7 +594,8 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ IBusXkbKeymap *active_key,
+ gboolean filtered,
+ xkb_mod_mask_t *mods_depressed,
+- gboolean *clear_virtual_state)
++ gboolean *clear_virtual_state,
++ gboolean *is_invalid_key)
+ {
+ IBusWaylandIMPrivate *priv;
+ uint32_t code = key + 8;
+@@ -572,6 +609,7 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ g_assert (active_key);
+ g_assert (mods_depressed);
+ g_assert (clear_virtual_state);
++ g_assert (is_invalid_key);
+
+ priv = ibus_wayland_im_get_instance_private (wlim);
+ new_mods_depressed = *mods_depressed;
+@@ -597,13 +635,19 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ switch (sym) {
+ case IBUS_KEY_ISO_Level3_Latch:
++ priv->iso_level3_state = IBUS_KEY_ISO_LEVEL_STATE_LATCH;
++ new_mods_depressed |= active_key->mod5_mask;
++ break;
+ case IBUS_KEY_ISO_Level3_Shift:
+- priv->is_pressed_mod5 = TRUE;
++ priv->iso_level3_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
+ new_mods_depressed |= active_key->mod5_mask;
+ break;
+ case IBUS_KEY_ISO_Level5_Latch:
++ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_LATCH;
++ new_mods_depressed |= active_key->mod3_mask;
++ break;
+ case IBUS_KEY_ISO_Level5_Shift:
+- priv->is_pressed_mod3 = TRUE;
++ priv->iso_level5_state = IBUS_KEY_ISO_LEVEL_STATE_SHIFT;
+ new_mods_depressed |= active_key->mod3_mask;
+ break;
+ default:
+@@ -614,61 +658,70 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ filtered = TRUE;
+ } else {
+ IBusKeyIsoLevelValue current_level = IBUS_KEY_ISO_LEVEL_INVALID;
+- gboolean *is_pressed_current_mod = NULL;
+- gboolean *is_pressed_reverse_mod = NULL;
+- xkb_mod_mask_t current_mode_mask = 0;
+- xkb_mod_mask_t reverse_mode_mask = 0;
++ IBusKeyIsoLevelState *current_state = NULL;
++ IBusKeyIsoLevelState *reverse_state = NULL;
+ switch (sym) {
+ case IBUS_KEY_ISO_Level3_Latch:
+ case IBUS_KEY_ISO_Level3_Shift:
+ current_level = IBUS_KEY_ISO_LEVEL_3;
+- is_pressed_current_mod = &priv->is_pressed_mod5;
+- is_pressed_reverse_mod = &priv->is_pressed_mod3;
+- current_mode_mask = active_key->mod5_mask;
+- reverse_mode_mask = active_key->mod3_mask;
++ current_state = &priv->iso_level3_state;
++ reverse_state = &priv->iso_level5_state;
+ break;
+ case IBUS_KEY_ISO_Level5_Latch:
+ case IBUS_KEY_ISO_Level5_Shift:
+ current_level = IBUS_KEY_ISO_LEVEL_5;
+- is_pressed_current_mod = &priv->is_pressed_mod3;
+- is_pressed_reverse_mod = &priv->is_pressed_mod5;
+- current_mode_mask = active_key->mod3_mask;
+- reverse_mode_mask = active_key->mod5_mask;
++ current_state = &priv->iso_level5_state;
++ reverse_state = &priv->iso_level3_state;
+ break;
+ default:;
+ }
+ g_assert (current_level != IBUS_KEY_ISO_LEVEL_INVALID);
+- g_assert (is_pressed_current_mod && is_pressed_reverse_mod);
+- if (G_LIKELY (*is_pressed_current_mod)) {
+- *is_pressed_current_mod = FALSE;
+- } else if (*is_pressed_reverse_mod &&
+- (new_mods_depressed & active_key->mod3_mask)) {
++ g_assert (current_state && reverse_state);
++ /* The "lv(tilde)" keymap gives the "Level3_Shift" keysym
++ * with the key <RALT>.
++ *
++ * Handle the "ISO_Level5_Latch" keysym in the "fr(ergol)" keymap.
++ * Do not use keysym but `current_state` because the latch state
++ * can be changed to the shift state with some key conditions.
++ */
++ if (*current_state == IBUS_KEY_ISO_LEVEL_STATE_SHIFT) {
++ *current_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
++ if (current_level == IBUS_KEY_ISO_LEVEL_3)
++ new_mods_depressed &= ~active_key->mod5_mask;
++ else if (current_level == IBUS_KEY_ISO_LEVEL_5)
++ new_mods_depressed &= ~active_key->mod3_mask;
++ else
++ g_assert_not_reached ();
++ if (sym != system_sym)
++ *clear_virtual_state = TRUE;
++ } else if (!*current_state && *reverse_state &&
++ (((current_level == IBUS_KEY_ISO_LEVEL_3) &&
++ (new_mods_depressed & active_key->mod3_mask)) ||
++ ((current_level == IBUS_KEY_ISO_LEVEL_5) &&
++ (new_mods_depressed & active_key->mod5_mask))
++ )) {
+ /* The unlikely case !priv->is_pressed_mod3 and
+ * priv->is_pressed_mod5 means that "de(T3)" keymap gives
+ * the pressed "Level3_Shift" keysym and
+ * the released "Level5_Latch" keysym for the key <RALT>.
+- * Maybe a bug in "de(T3)" so need to clear the MOD5 state
+- * by the pressed "Level3_Shift" keysym.
++ * This case can happen if the normal keysym and shift keysym
++ * are different.
+ */
+- *is_pressed_reverse_mod = FALSE;
+- new_mods_depressed &= ~reverse_mode_mask;
++ *reverse_state = IBUS_KEY_ISO_LEVEL_STATE_RELEASE;
++ *is_invalid_key = TRUE;
++ if (current_level == IBUS_KEY_ISO_LEVEL_3)
++ new_mods_depressed &= ~active_key->mod3_mask;
++ else if (current_level == IBUS_KEY_ISO_LEVEL_5)
++ new_mods_depressed &= ~active_key->mod5_mask;
++ else
++ g_assert_not_reached ();
+ *clear_virtual_state = TRUE;
+ g_debug ("Got a wrong released %s key without the "
+- "pressed one. Maybe a bug in XKB.",
++ "pressed one. Maybe the next Shift state will "
++ "produce the delayed pressed one.",
+ current_level == IBUS_KEY_ISO_LEVEL_3 ?
+ "Level3" : "Level5");
+ }
+- /* The "lv(tilde)" keymap gives the "Level3_Shift" keysym
+- * with the key <RALT>.
+- */
+- if ((current_level == IBUS_KEY_ISO_LEVEL_3 &&
+- sym == IBUS_KEY_ISO_Level3_Shift) ||
+- (current_level == IBUS_KEY_ISO_LEVEL_5 &&
+- sym == IBUS_KEY_ISO_Level5_Shift)) {
+- new_mods_depressed &= ~current_mode_mask;
+- if (sym != system_sym)
+- clear_virtual_state = TRUE;
+- }
+ }
+ break;
+ case IBUS_KEY_Shift_L:
+@@ -707,7 +760,8 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
+ IBusXkbKeymap *active_key,
+ xkb_mod_mask_t mods_depressed,
+ xkb_mod_mask_t new_mods_depressed,
+- gboolean clear_virtual_state)
++ gboolean clear_virtual_state,
++ gboolean is_invalid_key)
+ {
+ IBusWaylandIMPrivate *priv;
+ uint32_t code = key + 8;
+@@ -738,8 +792,9 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
+ if (clear_virtual_state)
+ priv->is_virtual_latch_state = FALSE;
+ }
+- if (!priv->is_virtual_latch_state ||
+- (state != WL_KEYBOARD_KEY_STATE_RELEASED)) {
++ if (G_LIKELY (!is_invalid_key) &&
++ (!priv->is_virtual_latch_state ||
++ (state != WL_KEYBOARD_KEY_STATE_RELEASED))) {
+ xkb_state_update_key (active_key->state, code,
+ (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ ? XKB_KEY_UP : XKB_KEY_DOWN);
+@@ -754,7 +809,8 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
+ /* "de(T3)" keymap gives the pressed "Level3_Shift" keysym and
+ * sets both MOD3(Level5) and MOD5(Level3) states after
+ * xkb_state_update_key() is called with the keypress.
+- * Maybe a bug in "de(T3)" and sets MOD5(Level3) again here.
++ * FIXME: Should set `is_invalid_key` in this case of the "de(T3)"
++ * keymap too not to call xkb_state_update_key() here?
+ */
+ if (G_UNLIKELY (new_mods_depressed != new2_mods_depressed)) {
+ xkb_state_update_mask (active_key->state, new_mods_depressed,
+@@ -1623,6 +1679,7 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ IBusXkbKeymap *active_key;
+ xkb_mod_mask_t mods_depressed, new_mods_depressed;
+ gboolean clear_virtual_state = FALSE;
++ gboolean is_invalid_key = FALSE;
+
+ g_return_val_if_fail (IBUS_IS_WAYLAND_IM (wlim), FALSE);
+ priv = ibus_wayland_im_get_instance_private (wlim);
+@@ -1658,7 +1715,8 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ active_key,
+ filtered,
+ &new_mods_depressed,
+- &clear_virtual_state);
++ &clear_virtual_state,
++ &is_invalid_key);
+ filtered = ibus_wayland_im_commit_key_event (wlim,
+ key,
+ modifiers,
+@@ -1667,14 +1725,16 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+ active_key,
+ filtered,
+ &new_mods_depressed,
+- &clear_virtual_state);
++ &clear_virtual_state,
++ &is_invalid_key);
+ ibus_wayland_im_update_virtual_xkb_state (wlim,
+ key,
+ state,
+ active_key,
+ mods_depressed,
+ new_mods_depressed,
+- clear_virtual_state);
++ clear_virtual_state,
++ is_invalid_key);
+ return filtered;
+ }
+
+@@ -2053,6 +2113,21 @@ key_event_check_repeat (IBusWaylandIM *wlim,
+ g_clear_pointer (&repeating_event.ibus_object_path, g_free);
+ if (!priv->ibuscontext)
+ return FALSE;
++ if (IS_DEAD_KEY (event->sym)) {
++ /* With "fr(ergol)" keymap, in case that key <AD09> is typed
++ * twice, the second pressed keysym is "dead_diaeresis" and
++ * the second released keysym is "ISO_Level5_Latch".
++ * The keysym "dead_diaeresis" should not be auto-repeated
++ * in this case.
++ */
++ if (priv->released_dead_key_wo_press == event->sym) {
++ priv->released_dead_key_wo_press = 0;
++ return TRUE;
++ }
++ priv->pressed_dead_key = event->sym;
++ } else {
++ priv->released_dead_key_wo_press = 0;
++ }
+ source = g_timeout_source_new (priv->repeat_delay);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+@@ -2066,6 +2141,30 @@ key_event_check_repeat (IBusWaylandIM *wlim,
+ g_source_set_callback (source, _process_key_event_repeat_delay_cb,
+ &repeating_event, NULL);
+ } else {
++ if (event->sym != repeating_event.sym) {
++ if (IS_DEAD_KEY (event->sym)) {
++ if (event->sym == priv->pressed_dead_key) {
++ priv->pressed_dead_key = 0;
++ } else if (!priv->pressed_dead_key) {
++ /* With "fr(ergol)" keymap, in case that key <AD09> is
++ * typed twice, the first pressed keysym is
++ * "ISO_Level5_Latch" and the first released keysym is
++ * "dead_diaeresis" with the MOD3(the level5) mask,
++ * the second pressed keysym is "dead_diaeresis" and
++ * the second released keysym is "ISO_Level5_Latch".
++ * So the first released "dead_diaeresis" is invalid and
++ * the second pressed "dead_diaeresis" should be released
++ * immediately.
++ */
++ priv->released_dead_key_wo_press = event->sym;
++ }
++ }
++ } else if (IS_DEAD_KEY (repeating_event.sym)) {
++ if (repeating_event.sym == priv->pressed_dead_key)
++ priv->pressed_dead_key = 0;
++ } else {
++ priv->released_dead_key_wo_press = 0;
++ }
+ if (repeating_event.repeat_rate_id) {
+ g_source_remove (repeating_event.repeat_rate_id);
+ repeating_event.repeat_rate_id = 0;
+--
+2.54.0
+
+From b60b54044406ff508fd025de32c10d78829ff2a3 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Sun, 19 Jul 2026 18:15:42 +0900
+Subject: [PATCH 5/5] client/wayland: Fix CapsLock between focus changes
+
+ibus_wayland_im_reset_modifiers() should not reset the state of CapsLock
+and ibus_wayland_im_update_virtual_depressed() should compare
+`priv->modifiers` and `(mods_depressed & mods_locked)`.
+
+Fixes: #2869
+---
+ client/wayland/ibuswaylandim.c | 33 ++++++++++++++++++++++++---------
+ 1 file changed, 24 insertions(+), 9 deletions(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index efeb6783..8b1e7b53 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -362,6 +362,7 @@ static void
+ ibus_wayland_im_reset_modifiers (IBusWaylandIM *wlim)
+ {
+ IBusWaylandIMPrivate *priv;
++ xkb_mod_mask_t mods_locked = 0;
+ xkb_layout_index_t group = 0;
+
+ g_assert (IBUS_IS_WAYLAND_IM (wlim));
+@@ -373,11 +374,15 @@ ibus_wayland_im_reset_modifiers (IBusWaylandIM *wlim)
+ priv->pressed_dead_key = 0;
+ priv->released_dead_key_wo_press = 0;
+ if (priv->key_user.state && priv->key_sys.state) {
++ mods_locked = xkb_state_serialize_mods (priv->key_sys.state,
++ XKB_STATE_LOCKED);
+ group = xkb_state_serialize_layout (priv->key_sys.state,
+ XKB_STATE_LAYOUT_LOCKED);
+- input_method_keyboard_modifiers (wlim, NULL, 0, 0, 0, 0, group);
++ input_method_keyboard_modifiers (wlim, NULL, 0, 0, 0,
++ mods_locked,
++ group);
+ } else {
+- priv->modifiers = 0;
++ priv->modifiers &= IBUS_LOCK_MASK;
+ }
+ }
+
+@@ -600,6 +605,7 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+ IBusWaylandIMPrivate *priv;
+ uint32_t code = key + 8;
+ xkb_mod_mask_t new_mods_depressed;
++ xkb_mod_mask_t mods_locked;
+ xkb_keysym_t system_sym = sym;
+
+ if (filtered)
+@@ -613,10 +619,13 @@ ibus_wayland_im_update_virtual_depressed (IBusWaylandIM *wlim,
+
+ priv = ibus_wayland_im_get_instance_private (wlim);
+ new_mods_depressed = *mods_depressed;
++ mods_locked = xkb_state_serialize_mods (active_key->state,
++ XKB_STATE_LOCKED);
+
+- if ((modifiers & ~IBUS_RELEASE_MASK ) != new_mods_depressed) {
++ if ((modifiers & ~IBUS_RELEASE_MASK ) !=
++ (new_mods_depressed | mods_locked)) {
+ g_warning ("IBus modifiers %X is different from XKB depressed %X",
+- modifiers, new_mods_depressed);
++ modifiers, new_mods_depressed | mods_locked);
+ }
+ if (priv->key_sys.state)
+ system_sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
+@@ -772,7 +781,7 @@ ibus_wayland_im_update_virtual_xkb_state (IBusWaylandIM *wlim,
+
+ priv = ibus_wayland_im_get_instance_private (wlim);
+ mods_locked = xkb_state_serialize_mods (active_key->state,
+- XKB_STATE_MODS_LOCKED);
++ XKB_STATE_LOCKED);
+ /* XKB group layout is configured in the system keymap but not the
+ * user keymap which always includes a single layout.
+ */
+@@ -2220,9 +2229,15 @@ input_method_keyboard_key (void *data,
+ /* xkb_key_get_syms() does not return the capital syms with Shift key. */
+ if (priv->key_sys.state)
+ event.sym = xkb_state_key_get_one_sym (priv->key_sys.state, code);
++ /* The position of Caps_Lock, Control, ISO_Next_Group, Multi_key keysyms
++ * can be customzied by each desktop configuration.
++ */
+ switch (event.sym) {
+- case IBUS_KEY_Multi_key:
++ case IBUS_KEY_Caps_Lock:
++ case IBUS_KEY_Control_L:
++ case IBUS_KEY_Control_R:
+ case IBUS_KEY_ISO_Next_Group:
++ case IBUS_KEY_Multi_key:
+ break;
+ default:
+ if (priv->key_user.state)
+@@ -2337,11 +2352,11 @@ input_method_keyboard_modifiers (void *data,
+ }
+ }
+ if (active_key->state) {
+- /* CapsLock needs XKB_STATE_MODS_LOCKED */
++ /* CapsLock needs XKB_STATE_LOCKED */
+ mask = xkb_state_serialize_mods (active_key->state,
+ XKB_STATE_DEPRESSED |
+ XKB_STATE_LATCHED |
+- XKB_STATE_MODS_LOCKED);
++ XKB_STATE_LOCKED);
+ }
+ if (priv->verbose) {
+ struct xkb_state *state = active_key->state;
+@@ -2356,7 +2371,7 @@ input_method_keyboard_modifiers (void *data,
+ xkb_state_serialize_mods (state, XKB_STATE_LATCHED) :
+ 0xFFFFFFFF,
+ state ?
+- xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED) :
++ xkb_state_serialize_mods (state, XKB_STATE_LOCKED) :
+ 0xFFFFFFFF);
+ fflush (priv->log);
+ }
+--
+2.54.0
+
diff --git a/ibus.spec b/ibus.spec
index d98f5ce..ccc91a5 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -48,7 +48,7 @@
Name: ibus
Version: 1.5.35~alpha2
# https://github.com/fedora-infra/rpmautospec/issues/101
-Release: 3%{?dist}
+Release: 4%{?dist}
Summary: Intelligent Input Bus for Linux OS
License: LGPL-2.1-or-later
URL: https://github.com/ibus/%name/wiki
@@ -58,7 +58,6 @@ Source2: %{name}-xinput
Source3: %{name}.conf.5
# Patch: %%{name}-HEAD.patch
Patch: %{name}-HEAD.patch
-Patch: %{name}-2444009-wayland-xkb-lv-tilde.patch
# Under testing #1349148 #1385349 #1350291 #1406699 #1432252 #1601577
Patch: %{name}-1385349-segv-bus-proxy.patch
@@ -545,6 +544,11 @@ dconf update || :
%{_datadir}/installed-tests/ibus
%changelog
+* Sun Jul 19 2026 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.35~alpha2-4
+- Fix typo in ibus-compose
+- Merge ibus-2444009-wayland-xkb-lv-tilde.patch
+- Fix CapsLock between focus changes in Wayland
+
* Thu Jul 16 2026 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.35~alpha2-3
- Fix CI in GNOME 50
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-19 10:41 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-19 10:41 [rpms/ibus] rawhide: Fix CapsLock between focus changes in Wayland Takao Fujiwara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox