public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Takao Fujiwara <tfujiwar@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/ibus] f44: Backport upstream patches
Date: Wed, 03 Jun 2026 03:16:24 GMT	[thread overview]
Message-ID: <178045658422.1.7619523802956667475.rpms-ibus-2f7e8dde9a6c@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/ibus
            Branch : f44
            Commit : 2f7e8dde9a6cf91645d060ed161e11aa06ebcc82
            Author : Takao Fujiwara <tfujiwar@redhat.com>
            Date   : 2026-06-03T12:11:38+09:00
            Stats  : +1663/-27 in 3 file(s)
            URL    : https://src.fedoraproject.org/rpms/ibus/c/2f7e8dde9a6cf91645d060ed161e11aa06ebcc82?branch=f44

            Log:
            Backport upstream patches

- Make an IBusText own an updated IBusAttrList reference
- Resolves: #2444787 SEGV with wrong anchor in surrounding-text in Wayland
- Updates: #2480408 IBus callbacks in Wayland to clarify SEGVs

---
diff --git a/ibus-1385349-segv-bus-proxy.patch b/ibus-1385349-segv-bus-proxy.patch
index 9084ee0..e7c7b30 100644
--- a/ibus-1385349-segv-bus-proxy.patch
+++ b/ibus-1385349-segv-bus-proxy.patch
@@ -1,6 +1,6 @@
 From 656390df46ba963afe51420ecb9399b6dde9a2bd Mon Sep 17 00:00:00 2001
 From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Fri, 12 Jul 2024 23:30:25 +0900
+Date: Thu, 30 Apr 2026 09:00:00 +0900
 Subject: [PATCH] Fix SEGV in bus_panel_proxy_focus_in()
 
 rhbz#1350291 SEGV in BUS_IS_CONNECTION(skip_connection) in
@@ -39,27 +39,34 @@ rhbz#2239633 SEGV with g_object_unref() in
 ibus_portal_context_handle_destroy()
 Connect "handle-destroy" signal after g_list_prepend().
 
-BUG=rhbz#1350291
-BUG=rhbz#1601577
-BUG=rhbz#1663528
-BUG=rhbz#1795499
-BUG=rhbz#1771238
-BUG=rhbz#1767976
-BUG=rhbz#2151344
-BUG=rhbz#2195895
-BUG=rhbz#2239633
+rhbz#2480408 SEGV with zwp_input_method_context_v1_preedit_string() in
+_context_hide_preedit_text_cb()
+If the `zwp_input_method_context_v1` is deactivate, the `IBusIMContext`
+is should be cleared and the "hide-preedit-text" signal is also
+disconnected.
+
+Closes: rhbz#1350291
+Closes: rhbz#1601577
+Closes: rhbz#1663528
+Closes: rhbz#1795499
+Closes: rhbz#1771238
+Closes: rhbz#1767976
+Closes: rhbz#2151344
+Closes: rhbz#2195895
+Closes: rhbz#2239633
+Closes: rhbz#2480408
 ---
- bus/dbusimpl.c         | 47 ++++++++++++++++++++++++++++++++++++----
- bus/engineproxy.c      | 44 ++++++++++++++++++++++++++++---------
- bus/panelproxy.c       |  9 +++++++-
- client/x11/main.c      | 49 +++++++++++++++++++++++++++++++++++++-----
- portal/portal.c        | 25 ++++++++++++++++-----
- src/ibusbus.c          |  6 ++++++
- ui/gtk3/switcher.vala  | 48 +++++++++++++++++++++++++----------------
- 7 files changed, 188 insertions(+), 44 deletions(-)
+ bus/engineproxy.c              | 44 +++++++++++++++++++++++-------
+ bus/panelproxy.c               |  9 ++++++-
+ client/wayland/ibuswaylandim.c | 20 +++++++++++++-
+ client/x11/main.c              | 49 ++++++++++++++++++++++++++++++----
+ portal/portal.c                | 25 +++++++++++++----
+ src/ibusbus.c                  |  6 +++++
+ ui/gtk3/switcher.vala          | 48 ++++++++++++++++++++-------------
+ 8 files changed, 203 insertions(+), 45 deletions(-)
 
 diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c
-index 70792be8..9c535bf2 100644
+index 70792be..9c535bf 100644
 --- a/bus/dbusimpl.c
 +++ b/bus/dbusimpl.c
 @@ -634,6 +634,7 @@ static void
@@ -147,7 +154,7 @@ index 70792be8..9c535bf2 100644
      if (incoming) {
          /* is incoming message */
 diff --git a/bus/engineproxy.c b/bus/engineproxy.c
-index 37736b69..7f34604c 100644
+index 37736b6..7f34604 100644
 --- a/bus/engineproxy.c
 +++ b/bus/engineproxy.c
 @@ -775,10 +775,12 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
@@ -247,7 +254,7 @@ index 37736b69..7f34604c 100644
      /* FIXME: set destroy callback ? */
      g_task_return_pointer (data->task, engine, NULL);
 diff --git a/bus/panelproxy.c b/bus/panelproxy.c
-index 4c2c8885..a7bec193 100644
+index 4c2c888..a7bec19 100644
 --- a/bus/panelproxy.c
 +++ b/bus/panelproxy.c
 @@ -124,6 +124,8 @@ bus_panel_proxy_new (BusConnection *connection,
@@ -280,8 +287,81 @@ index 4c2c8885..a7bec193 100644
      panel = BUS_PANEL_PROXY (obj);
      panel->panel_type = panel_type;
      return panel;
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index 5762f10..f1e3b9e 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -387,7 +387,7 @@ ibus_wayland_im_commit_text (IBusWaylandIM *wlim,
+                              const char    *str)
+ {
+     IBusWaylandIMPrivate *priv;
+-    g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
++    g_assert (IBUS_IS_WAYLAND_IM (wlim));
+     priv = ibus_wayland_im_get_instance_private (wlim);
+     switch (priv->version) {
+     case INPUT_METHOD_V1:
+@@ -822,6 +822,14 @@ _context_commit_text_cb (IBusInputContext *context,
+                          IBusText         *text,
+                          IBusWaylandIM    *wlim)
+ {
++    IBusWaylandIMPrivate *priv;
++
++    g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
++    priv = ibus_wayland_im_get_instance_private (wlim);
++    /* FIXME: rhbz#2480408 If priv->ibuscontext exists,
++     * priv->context also should exists.
++     */
++    g_assert (priv->ibuscontext);
+     ibus_wayland_im_commit_text (wlim, text->text);
+ }
+ 
+@@ -838,6 +846,8 @@ _context_forward_key_event_cb (IBusInputContext *context,
+ 
+     g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
+     priv = ibus_wayland_im_get_instance_private (wlim);
++    /* FIXME: rhbz#2480408 */
++    g_assert (priv->ibuscontext);
+     if (modifiers & IBUS_RELEASE_MASK)
+         state = WL_KEYBOARD_KEY_STATE_RELEASED;
+     else
+@@ -1055,6 +1065,8 @@ _context_show_preedit_text_cb (IBusInputContext *context,
+     const char *commit = "";
+     g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
+     priv = ibus_wayland_im_get_instance_private (wlim);
++    /* FIXME: rhbz#2480408 */
++    g_assert (priv->ibuscontext);
+     /* CURSOR is byte offset.  */
+     cursor =
+         g_utf8_offset_to_pointer (priv->preedit_text->text,
+@@ -1098,6 +1110,8 @@ _context_hide_preedit_text_cb (IBusInputContext *context,
+     IBusWaylandIMPrivate *priv;
+     g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
+     priv = ibus_wayland_im_get_instance_private (wlim);
++    /* FIXME: rhbz#2480408 */
++    g_assert (priv->ibuscontext);
+     switch (priv->version) {
+     case INPUT_METHOD_V1:
+         zwp_input_method_context_v1_preedit_string (priv->context,
+@@ -1137,6 +1151,8 @@ _context_update_preedit_text_cb (IBusInputContext *context,
+     priv->preedit_cursor_pos = cursor_pos;
+     priv->preedit_mode = mode;
+ 
++    /* FIXME: rhbz#2480408 */
++    g_assert (priv->ibuscontext);
+     if (visible)
+         _context_show_preedit_text_cb (context, wlim);
+     else
+@@ -1161,6 +1177,8 @@ _context_delete_surrounding_text_cb (IBusInputContext *context,
+     g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
+     priv = ibus_wayland_im_get_instance_private (wlim);
+ 
++    /* FIXME: rhbz#2480408 */
++    g_assert (priv->ibuscontext);
+     if (!priv->surrounding_text)
+         return;
+ 
 diff --git a/client/x11/main.c b/client/x11/main.c
-index 5fadd43d..26d4a7f1 100644
+index accebde..cac4cd6 100644
 --- a/client/x11/main.c
 +++ b/client/x11/main.c
 @@ -45,6 +45,7 @@
@@ -428,7 +508,7 @@ index 5fadd43d..26d4a7f1 100644
      ibus_input_context_reset (x11ic->context);
  
 diff --git a/portal/portal.c b/portal/portal.c
-index 5cd38779..5110baad 100644
+index 5cd3877..5110baa 100644
 --- a/portal/portal.c
 +++ b/portal/portal.c
 @@ -92,6 +92,11 @@ static void portal_context_g_signal (GDBusProxy        *proxy,
@@ -487,7 +567,7 @@ index 5cd38779..5110baad 100644
                  g_object_unref (portal_context);
              }
 diff --git a/src/ibusbus.c b/src/ibusbus.c
-index 540d1da3..defdb73b 100644
+index 540d1da..defdb73 100644
 --- a/src/ibusbus.c
 +++ b/src/ibusbus.c
 @@ -750,6 +750,12 @@ ibus_bus_destroy (IBusObject *object)
@@ -504,7 +584,7 @@ index 540d1da3..defdb73b 100644
          bus->priv->monitor = NULL;
      }
 diff --git a/ui/gtk3/switcher.vala b/ui/gtk3/switcher.vala
-index d4a5fa11..788eea06 100644
+index d4a5fa1..788eea0 100644
 --- a/ui/gtk3/switcher.vala
 +++ b/ui/gtk3/switcher.vala
 @@ -251,27 +251,37 @@ class Switcher : Gtk.Window {
@@ -565,5 +645,5 @@ index d4a5fa11..788eea06 100644
          seat.ungrab();
  
 -- 
-2.51.0
+2.53.0
 

diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index e69de29..05d93de 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -0,0 +1,1549 @@
+From 2e901bb74d8d7b775a15eddde9f794ffb2c253cb Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 19 May 2026 19:31:38 +0900
+Subject: [PATCH 01/15] Update test builds which is part of Meson builds
+
+bindings/pygobject/test-override-ibus: Add gi.require_versions().
+bus/test-matchrule: Support the TAP test.
+bus/test-stress: Support the TAP test.
+src/ibuscomposetable: Replace g_print() with g_debug() for TAP comments.
+src/tests/ibus-keypress: Prepend "# " for TAP comments.
+src/tests/ibus-registry: Support the TAP test.
+src/tests/ibus-util: Support the TAP test.
+src/tests/runtest:
+ - Expect the relative paths for Autotool and absolute paths for Meson.
+ - Support Python tests.
+ - Cease warnings with `glib-compile-schemas` command.
+ - Enable subtests with TAP for Meson and set G_TEST_ROOT_PROCESS
+   to consist test cases in Plan and ones in test results.
+src/tests/xkb-latin-layouts: Support the TAP test.
+---
+ bindings/pygobject/test-override-ibus.py |   2 +
+ bus/test-matchrule.c                     |  26 ++++--
+ bus/test-stress.c                        |  17 ++--
+ src/ibuscomposetable.c                   |  38 +++++---
+ src/tests/ibus-keypress.c                |   6 +-
+ src/tests/ibus-registry.c                |  15 +++-
+ src/tests/ibus-util.c                    |  14 ++-
+ src/tests/runtest                        | 109 ++++++++++++++++++-----
+ src/tests/xkb-latin-layouts              |  21 +++--
+ 9 files changed, 284 insertions(+), 96 deletions(-)
+
+diff --git a/bindings/pygobject/test-override-ibus.py b/bindings/pygobject/test-override-ibus.py
+index 087a6558..8853ff64 100644
+--- a/bindings/pygobject/test-override-ibus.py
++++ b/bindings/pygobject/test-override-ibus.py
+@@ -31,6 +31,8 @@ tests_builddir = os.path.abspath(os.path.dirname(__file__))
+ sys.path = [path for path in sys.path if path != tests_builddir]
+ sys.path.append(tests_builddir)
+ 
++import gi
++gi.require_versions({'GLib': '2.0', 'IBus': '1.0'})
+ from gi.repository import GLib, IBus
+ 
+ class TestOverride(unittest.TestCase):
+diff --git a/bus/test-matchrule.c b/bus/test-matchrule.c
+index d85fc90c..15a4ed45 100644
+--- a/bus/test-matchrule.c
++++ b/bus/test-matchrule.c
+@@ -16,15 +16,13 @@ struct _BusMatchRule {
+     GList *recipients;
+ };
+ 
+-int
+-main(gint argc, gchar **argv)
++static void
++test (void)
+ {
+     BusMatchRule *rule, *rule1;
+-#if !GLIB_CHECK_VERSION(2,35,0)
+-    g_type_init ();
+-#endif
+ 
+-    rule = bus_match_rule_new (" type='signal' , interface = 'org.freedesktop.IBus' ");
++    rule = bus_match_rule_new (" type='signal' ,"
++                               " interface = 'org.freedesktop.IBus' ");
+     g_assert (rule->message_type == G_DBUS_MESSAGE_TYPE_SIGNAL);
+     g_assert (g_strcmp0 (rule->interface, "org.freedesktop.IBus") == 0 );
+     g_object_unref (rule);
+@@ -55,12 +53,22 @@ main(gint argc, gchar **argv)
+     g_object_unref (rule);
+     g_object_unref (rule1);
+ 
+-    rule = bus_match_rule_new ("type='method_call',interface='org.freedesktop.IBus ");
++    rule = bus_match_rule_new ("type='method_call',"
++                               "interface='org.freedesktop.IBus ");
+     g_assert (rule == NULL);
+ 
+     rule = bus_match_rule_new ("eavesdrop=true");
+     g_assert (rule != NULL);
+     g_object_unref (rule);
+-    
+-    return 0;
++}
++
++int
++main (int argc, char *argv[])
++{
++    g_test_init (&argc, &argv, NULL);
++#if !GLIB_CHECK_VERSION(2,35,0)
++    g_type_init ();
++#endif
++    g_test_add_func ("/test-matchrule", test);
++    return g_test_run ();
+ }
+diff --git a/bus/test-stress.c b/bus/test-stress.c
+index 1c277e74..3908e60e 100644
+--- a/bus/test-stress.c
++++ b/bus/test-stress.c
+@@ -48,8 +48,8 @@ _sleep (guint millisecond)
+    Key kind are a-z and space.
+    Check ibus-daemon and ibus engine crash.
+ */
+-gint
+-main (gint argc, gchar **argv)
++static void
++test (void)
+ {
+     GTimer *timer;
+     GRand *rnd;
+@@ -59,9 +59,6 @@ main (gint argc, gchar **argv)
+     int count = 0;
+     int send_key_num = 0;
+ 
+-    setlocale (LC_ALL, "");
+-    ibus_init ();
+-
+     /* need to set active engine */
+     client = bus_test_client_new ();
+     if (client == NULL) {
+@@ -112,6 +109,14 @@ main (gint argc, gchar **argv)
+     g_print ("%f sec\n", g_timer_elapsed (timer, NULL));
+     g_timer_destroy (timer);
+     g_rand_free (rnd);
++}
+ 
+-    return 0;
++int
++main (int argc, char *argv[])
++{
++    g_test_init (&argc, &argv, NULL);
++    setlocale (LC_ALL, "");
++    ibus_init ();
++    g_test_add_func ("/test-stress", test);
++    return g_test_run ();
+ }
+diff --git a/src/ibuscomposetable.c b/src/ibuscomposetable.c
+index 116d0131..93262f3a 100644
+--- a/src/ibuscomposetable.c
++++ b/src/ibuscomposetable.c
+@@ -1,7 +1,7 @@
+ /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+ /* ibus - The Input Bus
+  * Copyright (C) 2013-2014 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2013-2025 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2013-2026 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+@@ -711,6 +711,7 @@ ibus_compose_list_check_duplicated_with_own (GList  *compose_list,
+     IBusComposeData *compose_data_a, *compose_data_b;
+     int i;
+     GList *removed_list = NULL;
++    GString *string;
+ 
+     if (!compose_list)
+         return NULL;
+@@ -729,18 +730,33 @@ ibus_compose_list_check_duplicated_with_own (GList  *compose_list,
+                     break;
+                 }
+             }
++            string = g_string_new (NULL);
++            if (is_different_value) {
++                g_string_append (
++                        string,
++                        "Deleting different outputs for same sequence.");
++            } else {
++                g_string_append (
++                        string,
++                        "Deleting same compose output for same sequence.");
++            }
++            g_string_append (string, "\n{");
++            for (i = 0; compose_data_a->values[i]; i++) {
++                g_string_append_printf (string,
++                                        "U+%X, ", compose_data_a->values[i]);
++            }
++            g_string_append (string, "}");
++            g_string_append (string, " {");
++            for (i = 0; compose_data_b->values[i]; i++) {
++                g_string_append_printf (string,
++                                        "U+%X, ", compose_data_b->values[i]);
++            }
++            g_string_append (string, "}\n");
+             if (is_different_value)
+-                g_warning ("Deleting different outputs for same sequence.");
++                g_warning ("%s", string->str);
+             else
+-                g_debug ("Deleting same compose output for same sequence.");
+-            g_print ("{");
+-            for (i = 0; compose_data_a->values[i]; i++)
+-                g_print ("U+%X, ", compose_data_a->values[i]);
+-            g_print ("}");
+-            g_print (" {");
+-            for (i = 0; compose_data_b->values[i]; i++)
+-                g_print ("U+%X, ", compose_data_b->values[i]);
+-            g_print ("}\n");
++                g_debug ("%s", string->str);
++            g_string_free (string, TRUE);
+             removed_list = g_list_append (removed_list, compose_data_a);
+         }
+     }
+diff --git a/src/tests/ibus-keypress.c b/src/tests/ibus-keypress.c
+index b9068d32..78296381 100644
+--- a/src/tests/ibus-keypress.c
++++ b/src/tests/ibus-keypress.c
+@@ -1,7 +1,7 @@
+ /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+- * Copyright (C) 2025 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2025-2026 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * Copyright (c) 2025 Peter Hutterer <peter.hutterer@who-t.net>
+  * Copyright (C) 2025 Red Hat, Inc.
+  *
+@@ -628,7 +628,7 @@ window_inserted_text_cb (GtkEntryBuffer *buffer,
+     if (!(code = g_utf8_get_char (chars)))
+         return;
+     if (code != test_results[i][j++]) {
+-        test = RED "FAIL" NC;
++        test = "# " RED "FAIL" NC;
+         g_test_fail_printf ("%05d:%05d %s expected: %04X typed: %04X",
+                             i, j - 1,
+                             test,
+@@ -637,7 +637,7 @@ window_inserted_text_cb (GtkEntryBuffer *buffer,
+     } else if (test_results[i][j]) {
+         return;
+     } else {
+-        g_print (GREEN "PASS" NC " ");
++        g_print ("# " GREEN "PASS" NC " ");
+         for (k = 0; k < j; k++)
+             g_print ("%lc(%X) ", test_results[i][k], test_results[i][k]);
+         g_print ("\n");
+diff --git a/src/tests/ibus-registry.c b/src/tests/ibus-registry.c
+index 4c612926..1a109e7f 100644
+--- a/src/tests/ibus-registry.c
++++ b/src/tests/ibus-registry.c
+@@ -1,10 +1,17 @@
+ #include <ibus.h>
+ 
+-int main()
++static void
++test (void)
+ {
+-    ibus_init ();
+-
+     IBusRegistry *registry = ibus_registry_new ();
+     g_object_unref (registry);
+-    return 0;
++}
++
++int
++main(int argc, char *argv[])
++{
++    g_test_init (&argc, &argv, NULL);
++    ibus_init ();
++    g_test_add_func ("/ibus-registry", test);
++    return g_test_run ();
+ }
+diff --git a/src/tests/ibus-util.c b/src/tests/ibus-util.c
+index 4110e529..9f2ab2b6 100644
+--- a/src/tests/ibus-util.c
++++ b/src/tests/ibus-util.c
+@@ -4,13 +4,19 @@
+ 
+ #include "ibus.h"
+ 
+-int main (int argc, char **argv)
++static void
++test (void)
+ {
+     gchar *name;
+-    setlocale(LC_ALL, "C");
+-
+     g_assert_cmpstr (name = ibus_get_language_name ("eng"), ==, "English");
+     g_free (name);
++}
+ 
+-    return 0;
++int
++main (int argc, char *argv[])
++{
++    g_test_init (&argc, &argv, NULL);
++    setlocale(LC_ALL, "C");
++    g_test_add_func ("/ibus-util", test);
++    return g_test_run ();
+ }
+diff --git a/src/tests/runtest b/src/tests/runtest
+index 71559fc1..7bd98575 100755
+--- a/src/tests/runtest
++++ b/src/tests/runtest
+@@ -17,10 +17,10 @@
+ #
+ #   $ top_builddir=<...> top_srcdir=<...> builddir=<...> ./runtest ibus-foo
+ 
+-: ${top_builddir:=../..}
+-: ${top_srcdir:=../..}
+-: ${builddir:=.}
+-: ${srcdir:=.}
++: ${top_builddir:='../..'}
++: ${top_srcdir:='../..'}
++: ${builddir:='.'}
++: ${srcdir:='.'}
+ : ${DISABLE_GUI_TESTS:=''}
+ : ${DISABLE_DAEMONIZE_IN_TESTS:=''}
+ : ${TEST_GTK_VERSION:=0}
+@@ -37,7 +37,7 @@ ibus-engine-switch
+ ibus-compose
+ ibus-keypress
+ test-stress
+-xkb-latin-layouts
++test-override-ibus.py
+ "
+ IBUS_SCHEMA_FILE='org.freedesktop.ibus.gschema.xml'
+ GTK_QUERY_MODULE=gtk-query-immodules-3.0-32
+@@ -50,6 +50,22 @@ if ! command -v $GTK_QUERY_MODULE > /dev/null 2>&1 ; then
+     GTK_QUERY_MODULE=gtk-query-immodules-3.0
+ fi
+ 
++if echo "$top_builddir" | grep -q '^/' ; then
++    # absolute path for meson
++    test_top_builddir="$top_builddir"
++else
++    # relative path for autotool and default value
++    test_top_builddir="../$top_builddir"
++fi
++
++if echo "$top_srcdir" | grep -q '^/' ; then
++    # absolute path for meson
++    test_top_srcdir="$top_srcdir"
++else
++    # relative path for autotool and default value
++    test_top_srcdir="../$top_srcdir"
++fi
++
+ retval=0
+ 
+ # Portable replacement of basename.
+@@ -120,21 +136,26 @@ run_test_case()
+     backup_dir=$PWD
+     cd $tstdir
+ 
++    if echo "$tst" | grep -q 'python' ; then
++        name=`func_basename "$1"`
++    else
++        name=`func_basename "$tst"`
++    fi
+     need_bus=no
+     for t in $BUS_REQUIRED_TESTS; do
+-        if test $t = `func_basename $tst`; then
++        if test "$t" = "$name"; then
+             need_bus=yes
+         fi
+     done
+ 
+     if test $need_bus = yes; then
+-        if test ! -f "../$top_builddir/conf/memconf/ibus-memconf" ; then
++        if test ! -f "$test_top_builddir/conf/memconf/ibus-memconf" ; then
+             echo "NOT FOUNND ibus-memconf. Need configure --enable-memconf"
+             exit 1
+         fi
+         # func_copy_component replaces s/$top_srcdir/%top_builddir/
+-        func_copy_component "../$top_srcdir/engine/simple.xml"
+-        func_copy_component "../$top_srcdir/conf/memconf/memconf.xml"
++        func_copy_component "$test_top_srcdir/engine/simple.xml"
++        func_copy_component "$test_top_srcdir/conf/memconf/memconf.xml"
+ 
+         IBUS_COMPONENT_PATH=$PWD/components
+         export IBUS_COMPONENT_PATH
+@@ -144,10 +165,10 @@ run_test_case()
+ 
+         test $TEST_GTK -eq 1 && case $TEST_GTK_VERSION in
+         4)
+-            if test -f "../$top_builddir/client/gtk4/.libs/libim-ibus.so" ; then
+-                IM_IBUS_SO="../$top_builddir/client/gtk4/.libs/libim-ibus.so"
+-            elif test -f "../$top_builddir/client/gtk4/libim-ibus.so" ; then
+-                IM_IBUS_SO="../$top_builddir/client/gtk4/libim-ibus.so"
++            if test -f "$test_top_builddir/client/gtk4/.libs/libim-ibus.so" ; then
++                IM_IBUS_SO="$test_top_builddir/client/gtk4/.libs/libim-ibus.so"
++            elif test -f "$test_top_builddir/client/gtk4/libim-ibus.so" ; then
++                IM_IBUS_SO="$test_top_builddir/client/gtk4/libim-ibus.so"
+             else
+                 echo "NOT FOUND $top_builddir/client/gtk4/libim-ibus.so"
+                 exit 1
+@@ -157,10 +178,10 @@ run_test_case()
+             export GTK_EXE_PREFIX="$PWD"
+             ;;
+         3)
+-            if test -f "../$top_builddir/client/gtk3/.libs/im-ibus.so" ; then
+-                IM_IBUS_SO="../$top_builddir/client/gtk3/.libs/im-ibus.so"
+-            elif test -f "../$top_builddir/client/gtk3/im-ibus.so" ; then
+-                IM_IBUS_SO="../$top_builddir/client/gtk3/im-ibus.so"
++            if test -f "$test_top_builddir/client/gtk3/.libs/im-ibus.so" ; then
++                IM_IBUS_SO="$test_top_builddir/client/gtk3/.libs/im-ibus.so"
++            elif test -f "$test_top_builddir/client/gtk3/im-ibus.so" ; then
++                IM_IBUS_SO="$test_top_builddir/client/gtk3/im-ibus.so"
+             else
+                 echo "NOT FOUND $top_builddir/client/gtk3/im-ibus.so"
+                 exit 1
+@@ -175,8 +196,8 @@ run_test_case()
+             ;;
+         esac || :
+ 
+-        cp "../$top_srcdir/data/dconf/$IBUS_SCHEMA_FILE" $PWD
+-        glib-compile-schemas $PWD
++        cp "$test_top_srcdir/data/dconf/$IBUS_SCHEMA_FILE" $PWD
++        glib-compile-schemas $PWD 2>/dev/null
+         if test $? -ne 0 ; then
+             echo "FAILED glib-compile-schemas"
+             retval=1
+@@ -198,10 +219,10 @@ run_test_case()
+             --verbose
+         '
+         if test x"$DISABLE_DAEMONIZE_IN_TESTS" = x ; then
+-            ../$top_builddir/bus/ibus-daemon \
++            $test_top_builddir/bus/ibus-daemon \
+                 $DAEMON_ARGS --daemonize;
+         else
+-            ../$top_builddir/bus/ibus-daemon \
++            $test_top_builddir/bus/ibus-daemon \
+                 $DAEMON_ARGS &
+         fi
+ 
+@@ -219,7 +240,26 @@ run_test_case()
+ 
+     export IBUS_COMPOSE_CACHE_DIR=$PWD
+ 
+-    "../$tst" ../$top_srcdir/src/tests ${1+"$@"}
++    if echo "$tst" | grep -q '^/' ; then
++        if echo "$tst" | grep -q 'python' ; then
++            interpreter=$tst
++            tst=$1
++            shift
++            if echo "$tst" | grep -q '^/' ; then
++                # absolute path for Meson
++                "$interpreter" "$tst" ${1+"$@"}
++            else
++                # relative path for autotools
++                "$interpreter" "../$tst" ${1+"$@"}
++            fi
++        else
++            # absolute path for Meson
++            "$tst" $test_top_srcdir/src/tests ${1+"$@"}
++        fi
++    else
++        # relative path for each execution
++        "../$tst" $test_top_srcdir/src/tests ${1+"$@"}
++    fi
+ 
+     retval=`expr $retval \| $?`
+ 
+@@ -237,6 +277,16 @@ if test x"$ENVS" = x ; then
+ else
+     LANG_backup=$LANG
+     i=1
++    # mesonbuild/mtest.py:TAPParser.parse_line() says:
++    # 'version number must be on the first line'
++    echo "TAP version 14"
++    NUM_CASES=`wc -l $envfile  | sed -e 's/^\([0-9]*\) .*/\1/'`
++    # Meson checks #Plan with the number of test cases
++    # like "1..Number"
++    # https://testanything.org/tap-version-14-specification.html
++    echo "1..$NUM_CASES"
++    # Set subtest for GTestLog to hide the "TAP version" output
++    export G_TEST_ROOT_PROCESS=1
+     # "for e in $ENVS" separates the variable by space but not line feed.
+     # "echo $ENVS | while read e" does not forward `retval`
+     # to the parent process.
+@@ -248,14 +298,25 @@ else
+         unset NO_LOAD_COMPOSE_FILE
+         unset COMPOSE_FILE
+         export $e
+-        echo "Run `func_basename $tst` on $e"
+-        echo "======================="
++        # TAP info should start with "# ".
++        echo "# Run `func_basename $tst` on $e"
++        echo "# ======================="
+         run_test_case $@
++        name="/"`basename "$1"`":$LANG:$COMPOSE_FILE:$NO_LOAD_COMPOSE_FILE"
++        # Meson compares the numer of tests in #Plan and the number of
++        # #TestPoint like "ok Number Description"
++        # https://testanything.org/tap-version-14-specification.html
++        if test $retval -eq 0; then
++            echo "ok $i $name"
++        else
++            echo "not ok $i $name"
++        fi
+         echo ""
+         i=`expr $i + 1`
+     done << EOF_ENVS
+     `echo "$ENVS"`
+ EOF_ENVS
++    unset G_TEST_ROOT_PROCESS
+     export LANG=$LANG_backup
+ fi
+ 
+diff --git a/src/tests/xkb-latin-layouts b/src/tests/xkb-latin-layouts
+index 45c99358..dd49ff97 100755
+--- a/src/tests/xkb-latin-layouts
++++ b/src/tests/xkb-latin-layouts
+@@ -1,7 +1,7 @@
+ #!/bin/bash
+ 
+ PROGNAME=`basename $0`
+-VERSION=0.1
++VERSION=0.2
+ # POSIX sh has no 'echo -e'
+ : ${ECHO:='/usr/bin/echo'}
+ TMPDIR=
+@@ -25,15 +25,14 @@ usage()
+ parse_args()
+ {
+     # This is GNU getopt. "sudo port getopt" in BSD?
+-    ARGS=`getopt -o hD:Tv --long \
+-          help,schemasdir:,tap,version\
++    ARGS=`getopt -o hD:v --long \
++          help,schemasdir:,version\
+         -- "$@"`;
+     eval set -- "$ARGS"
+     while [ 1 ] ; do
+         case "$1" in
+         -h | --help )        usage; exit 0;;
+         -D | --schemasdir )  INSTALLED_SCHEMAS_DIR="$2"; shift 2;;
+-        -T | --tap )         shift;; # ignore the option
+         -v | --version )     $ECHO -e "$VERSION"; exit 0;;
+         -- )                 shift; break;;
+         * )                  shift;;
+@@ -98,11 +97,11 @@ test_xkb_keymaps()
+         if [ "x$HAS_VARIANT" != "x" ] ; then
+             layout=$($ECHO "$keymap" | sed -e 's/\([^(]*\)([^)]*)/\1/')
+             variant=$($ECHO "$keymap" | sed -e 's/[^(]*(\([^)]*\))/\1/')
+-            $ECHO setxkbmap -layout $layout -variant $variant
++            $ECHO "# setxkbmap -layout $layout -variant $variant"
+             setxkbmap -layout $layout -variant $variant
+         else
+             layout="$keymap"
+-            $ECHO setxkbmap -layout $layout
++            $ECHO "# setxkbmap -layout $layout"
+             setxkbmap -layout $layout
+         fi
+         if [ $? -ne 0 ] ; then
+@@ -121,22 +120,28 @@ EOF_READ_XKB
+ main()
+ {
+     if [ x"$DISPLAY" = x ] ; then
+-        echo "skip: No display. Maybe headless mode."
++        echo "# skip No display. Maybe headless mode."
+         exit 77
+     fi
+     if ! which setxkbmap > /dev/null ; then
+-        echo "skip: No setxkbmap"
++        echo "# skip No setxkbmap"
+         exit 77
+     fi
+ 
+     parse_args "$@"
+ 
++    # Meson checks TAP #Version, #Plan, #TestPoint
++    echo "TAP version 14"
++    echo "1..1"
++
+     if [ x"$INSTALLED_SCHEMAS_DIR" != x ] ; then
+         init
+     fi
+ 
+     test_xkb_keymaps
+ 
++    echo "ok 1 /$PROGNAME"
++
+     if [ x"$INSTALLED_SCHEMAS_DIR" != x ] ; then
+         finit
+     fi
+-- 
+2.54.0
+
+From 6d3a98b94f5f19774595af69c54282565fd31873 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 19 May 2026 20:03:38 +0900
+Subject: [PATCH 02/15] bus: Unset G_DISABLE_ASSERT for test builds
+
+Now GTest is implemented in test-matchrule and test-stress for TAP
+and G_DISABLE_ASSERT cannot be applied to the test builds.
+
+Also delete the unnecessary GTK2_CFLAGS.
+
+Closes: #2358
+---
+ bus/Makefile.am | 10 +++++-----
+ configure.ac    |  3 +++
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/bus/Makefile.am b/bus/Makefile.am
+index bd3481b9..7fbc9a29 100644
+--- a/bus/Makefile.am
++++ b/bus/Makefile.am
+@@ -3,7 +3,7 @@
+ # ibus - The Input Bus
+ #
+ # Copyright (c) 2007-2013 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2013-2022 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2013-2026 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ # Copyright (c) 2007-2022 Red Hat, Inc.
+ #
+ # This library is free software; you can redistribute it and/or
+@@ -22,6 +22,7 @@
+ # USA
+ 
+ NULL =
++CFLAGS = $(TEST_CFLAGS)
+ SUBDIRS = . services
+ 
+ libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
+@@ -87,6 +88,7 @@ ibus_daemon_SOURCES = \
+ 	$(NULL)
+ ibus_daemon_CFLAGS = \
+ 	$(AM_CFLAGS) \
++	$(PRODUCT_CFLAGS) \
+ 	$(NULL)
+ ibus_daemon_LDADD = \
+ 	$(AM_LDADD) \
+@@ -153,13 +155,11 @@ test_stress_SOURCES = \
+ 	$(NULL)
+ test_stress_CFLAGS = \
+ 	$(AM_CFLAGS) \
+-	@GTK2_CFLAGS@ \
+-	@X11_CFLAGS@ \
++	$(X11_CFLAGS) \
+ 	$(NULL)
+ test_stress_LDADD = \
+ 	$(AM_LDADD) \
+-	@GTK2_LIBS@ \
+-	@X11_LIBS@ \
++	$(X11_LIBS) \
+ 	$(NULL)
+ 
+ EXTRA_DIST =                \
+diff --git a/configure.ac b/configure.ac
+index 49020039..303798fb 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -160,18 +160,21 @@ if test x"$enable_product_build" = xauto; then
+     fi
+ fi
+ TEST_CFLAGS="$CFLAGS"
++PRODUCT_CFLAGS="$CFLAGS"
+ PRODUCT_LIB_CFLAGS=''
+ if test x"$enable_product_build" = xyes; then
+     if test $ac_compiler_gnu = yes; then
+         _CFLAGS="-DG_DISABLE_CAST_CHECKS -Wl,-z,relro -Wl,-z,now $CFLAGS"
+         TEST_CFLAGS="$_CFLAGS"
+         CFLAGS="-DG_DISABLE_ASSERT $_CFLAGS"
++        PRODUCT_CFLAGS="$CFLAGS"
+         PRODUCT_LIB_CFLAGS="-Wl,-Bsymbolic -fno-plt"
+     fi
+     AC_DEFINE(HAVE_PRODUCT_BUILD, 1, [Enable product build])
+ fi
+ AC_SUBST(PRODUCT_LIB_CFLAGS)
+ AC_SUBST(TEST_CFLAGS)
++AC_SUBST(PRODUCT_CFLAGS)
+ AC_MSG_RESULT([$enable_product_build])
+ 
+ # Check header filess.
+-- 
+2.54.0
+
+From aeca3e885b1aec02d9f14a5ab6c83f928eca6c9b Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 7 May 2026 09:35:47 +0900
+Subject: [PATCH 03/15] client/x11: Call setlocale()
+
+To enhance #2547, ibus-x11 replaces gtk_init() with gdk_init().
+Seems gdk_init() does not call setlocale() internally and ibus-x11
+causes some encoding errors when the multi-byte chars are converted
+to the text property of compound text.
+
+BUG=https://github.com/ibus/ibus/issues/2896
+Fixes: https://github.com/ibus/ibus/commit/b185b21
+---
+ client/x11/main.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/client/x11/main.c b/client/x11/main.c
+index 5fadd43d..accebde5 100644
+--- a/client/x11/main.c
++++ b/client/x11/main.c
+@@ -2,7 +2,7 @@
+ /* vim:set et sts=4: */
+ /* ibus
+  * Copyright (C) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2015-2025 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2015-2026 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * Copyright (C) 2007-2015 Red Hat, Inc.
+  *
+  * main.c:
+@@ -1368,6 +1368,11 @@ main (int argc, char **argv)
+     gdk_set_allowed_backends ("x11");
+ #endif
+ 
++    /* gdk_init() does not call setlocale() */
++    if (!setlocale (LC_ALL, "")) {
++        g_warning ("Failed to set locale with your environment variables. "
++                   "Some multi-byte characters won't be committed correctly.");
++    }
+     gdk_init (&argc, &argv);
+     XSetErrorHandler (_xerror_handler);
+     XSetIOErrorHandler (_xerror_io_handler);
+-- 
+2.54.0
+
+From 3768387b5c1f72c2c3840ebc4c6a46c3c711c71e Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Fri, 17 Apr 2026 19:54:22 +0900
+Subject: [PATCH 04/15] Disable Unicode input with Ctrl-Shift-u twice
+
+Once the Unicode module receives Ctrl-Shift-u key twice, the pre-edit
+should be disabled as the current IBus engines expect the nest format
+<emoji_preedit <engine_predit /> /> but not the reverse format.
+
+Closes: #2903
+---
+ bus/inputcontext.c        | 17 ++++++++++++-----
+ ui/gtk3/panelbinding.vala |  3 +++
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/bus/inputcontext.c b/bus/inputcontext.c
+index 5adcdf6e..7de594ad 100644
+--- a/bus/inputcontext.c
++++ b/bus/inputcontext.c
+@@ -2,7 +2,7 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2014 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2015-2025 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2015-2026 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * Copyright (C) 2008-2025 Red Hat, Inc.
+  *
+  * This library is free software; you can redistribute it and/or
+@@ -3473,10 +3473,17 @@ bus_input_context_update_preedit_text (BusInputContext *context,
+     }
+ 
+     context->preedit_cursor_pos = cursor_pos;
+-    if (use_extension) {
+-        context->preedit_visible = visible;
+-        context->preedit_mode = mode;
+-    }
++    /* The `visible` flag is set whether use_extension is enabled or not.
++     * I.e. If both an emoji preedit text and an engine preedit exist,
++     * the engine preedit is expected to exist in the emoji preedit like
++     * <emoji_preedit  <engine_preedit /> />, so if the emoji preedit is
++     * hidden, the engine preedit is also hidden in the nested state.
++     * If some engines would need the reverse case like
++     * <engine_preedit  <emoji_preedit /> />, probably another API is needed
++     * like ibus_engine_show_emoji_candidate_in_preedit().
++     */
++    context->preedit_visible = visible;
++    context->preedit_mode = mode;
+     context->ignore_focus_out = FALSE;
+     extension_visible = context->preedit_visible ||
+                         (context->emoji_extension != NULL);
+diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala
+index 56d7f09c..0f20d973 100644
+--- a/ui/gtk3/panelbinding.vala
++++ b/ui/gtk3/panelbinding.vala
+@@ -885,6 +885,9 @@ class PanelBinding : IBus.PanelService {
+         m_enable_extension = event.is_enabled;
+         if (!m_enable_extension) {
+             hide_emoji_lookup_table();
++            update_preedit_text_received(new IBus.Text.from_string(""),
++                                         0,
++                                         false);
+             return;
+         }
+         if (!m_loaded_emoji)
+-- 
+2.54.0
+
+From a8ff755971509f09e1d0ea52a10be1bfc13af39b Mon Sep 17 00:00:00 2001
+From: Danny Milosavljevic <dannym@friendly-machines.com>
+Date: Wed, 27 May 2026 13:38:47 +0900
+Subject: [PATCH 05/15] client/wayland: Fix serial handling
+
+Closes: #2870
+---
+ client/wayland/ibuswaylandim.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index e1bad27d..cf090e2e 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -2431,13 +2431,14 @@ registry_handle_global (void               *data,
+                  interface, name, version);
+         fflush (priv->log);
+     }
+-    priv->im_serial = 0;
+     if (!g_strcmp0 (interface, zwp_input_method_manager_v2_interface.name)) {
++        priv->im_serial = 0;
+         priv->version = INPUT_METHOD_V2;
+         priv->input_method_manager_v2 =
+                 wl_registry_bind (registry, name,
+                                   &zwp_input_method_manager_v2_interface, 1);
+     } else if (!g_strcmp0 (interface, zwp_input_method_v1_interface.name)) {
++        priv->im_serial = 0;
+         if (version >= 4)
+             version = 4;
+         priv->version = INPUT_METHOD_V1;
+-- 
+2.54.0
+
+From 18d8e37e112a155f3f80d36e4d6a845cc2e23bd8 Mon Sep 17 00:00:00 2001
+From: Danny Milosavljevic <dannym@friendly-machines.com>
+Date: Thu, 28 May 2026 09:51:11 +0900
+Subject: [PATCH 06/15] client/wayland: Guard against NULL seat in IMv2 code paths
+
+registry_handle_global_remove() sets priv->seat to NULL when the last
+seat is removed.  IBus engine callbacks (commit_text, forward_key_event,
+show/hide_preedit_text, delete_surrounding_text) and the modifier
+handler can fire asynchronously via D-Bus after seat removal, causing
+a NULL pointer dereference.
+
+Add NULL checks for priv->seat in all IMv2 branches that dereference it
+from these callback paths.
+
+Closes: #2877
+---
+ client/wayland/ibuswaylandim.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index cf090e2e..57b0ad21 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -334,6 +334,8 @@ ibus_wayland_im_commit_text (IBusWaylandIM *wlim,
+                                                    str);
+         break;
+     case INPUT_METHOD_V2:
++        if (!priv->seat)
++            break;
+         zwp_input_method_v2_commit_string (priv->seat->input_method_v2, str);
+         zwp_input_method_v2_commit (priv->seat->input_method_v2,
+                                     priv->im_serial);
+@@ -367,6 +369,8 @@ ibus_wayland_im_key (IBusWaylandIM *wlim,
+          * returns "Cannot send a keypress before defining a keymap"
+          * if `has_keymap` is %FALSE.
+          */
++        if (!priv->seat)
++            break;
+         if (priv->seat->has_keymap) {
+             zwp_virtual_keyboard_v1_key (priv->seat->virtual_keyboard,
+                                          time, key, state);
+@@ -663,6 +667,8 @@ _context_show_preedit_text_cb (IBusInputContext *context,
+                                                     commit);
+         break;
+     case INPUT_METHOD_V2:
++        if (!priv->seat)
++            break;
+         zwp_input_method_v2_set_preedit_string  (
+                 priv->seat->input_method_v2,
+                 priv->preedit_text->text,
+@@ -693,6 +699,8 @@ _context_hide_preedit_text_cb (IBusInputContext *context,
+                                                     "");
+         break;
+     case INPUT_METHOD_V2:
++        if (!priv->seat)
++            break;
+         zwp_input_method_v2_set_preedit_string  (priv->seat->input_method_v2,
+                                                  "", 0, 0);
+         zwp_input_method_v2_commit (priv->seat->input_method_v2,
+@@ -771,6 +779,8 @@ _context_delete_surrounding_text_cb (IBusInputContext *context,
+                 before_length + after_length);
+         break;
+     case INPUT_METHOD_V2:
++        if (!priv->seat)
++            break;
+         zwp_input_method_v2_delete_surrounding_text (
+                 priv->seat->input_method_v2,
+                 before_length,
+@@ -1201,7 +1211,7 @@ ibus_wayland_im_post_key (IBusWaylandIM *wlim,
+             return FALSE;
+         break;
+     case INPUT_METHOD_V2:
+-        if (!priv->seat->active)
++        if (!priv->seat || !priv->seat->active)
+             return FALSE;
+         break;
+     default:
+@@ -1760,6 +1770,8 @@ input_method_keyboard_modifiers (void                      *data,
+          * returns "Cannot send a modifier state before defining a keymap"
+          * if `has_keymap` is %FALSE.
+          */
++        if (!priv->seat)
++            break;
+         if (priv->seat->has_keymap) {
+             zwp_virtual_keyboard_v1_modifiers (priv->seat->virtual_keyboard,
+                                                mods_depressed, mods_latched,
+-- 
+2.54.0
+
+From 06b073dfef47b83ee4cf4969f7269b75dff387f7 Mon Sep 17 00:00:00 2001
+From: Danny Milosavljevic <dannym@friendly-machines.com>
+Date: Thu, 28 May 2026 10:24:42 +0900
+Subject: [PATCH 07/15] client/wayland: Fix preedit cursor_end in set_preedit_string
+
+cursor_end was set to strlen(text), making the cursor span from the
+cursor position to the end of the preedit string.  Per the
+zwp_input_method_v2 protocol, cursor_begin == cursor_end means a point
+cursor.  Pass cursor for both begin and end.
+
+Closes: #2880
+---
+ client/wayland/ibuswaylandim.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index 57b0ad21..69d10fe8 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -673,7 +673,7 @@ _context_show_preedit_text_cb (IBusInputContext *context,
+                 priv->seat->input_method_v2,
+                 priv->preedit_text->text,
+                 cursor,
+-                strlen (priv->preedit_text->text));
++                cursor);
+         zwp_input_method_v2_commit (priv->seat->input_method_v2,
+                                     priv->im_serial);
+         priv->hiding_preedit_text = FALSE;
+-- 
+2.54.0
+
+From 72b09de3b4c4c3695eb877a70cbe88c4f5c398cd Mon Sep 17 00:00:00 2001
+From: Danny Milosavljevic <dannym@friendly-machines.com>
+Date: Thu, 28 May 2026 10:33:09 +0900
+Subject: [PATCH 08/15] client/wayland: Fix typo "waylayd" -> "wayland" in Makefile.am
+
+The misspelled variable name caused the METADATADIRS and VAPIDIRS
+settings to be silently ignored by automake.
+
+Closes: #2881
+---
+ client/wayland/Makefile.am | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/client/wayland/Makefile.am b/client/wayland/Makefile.am
+index 25269557..3275d1c5 100644
+--- a/client/wayland/Makefile.am
++++ b/client/wayland/Makefile.am
+@@ -184,8 +184,8 @@ MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
+ DISTCLEANFILES += $(VAPIGEN_VAPIS)
+ $(VAPIGEN_VAPIS): $(INTROSPECTION_GIRS)
+ ibus_wayland_im_1_0_vapi_DEPS = glib-2.0 gobject-2.0 gio-2.0 ibus-1.0
+-ibus_waylayd_im_1_0_vapi_METADATADIRS = $(srcdir) $(top_srcdir)/bindings/vala
+-#ibus_waylayd_im_1_0_vapi_VAPIDIRS = $(top_srcdir)/bindings/vala
++ibus_wayland_im_1_0_vapi_METADATADIRS = $(srcdir) $(top_srcdir)/bindings/vala
++#ibus_wayland_im_1_0_vapi_VAPIDIRS = $(top_srcdir)/bindings/vala
+ ibus_wayland_im_1_0_vapi_FILES = \
+     $(INTROSPECTION_GIRS) \
+     $(srcdir)/ibus-wayland-im-custom.vala \
+-- 
+2.54.0
+
+From 614eb098016afe280e860732c60a50aefdb1ed65 Mon Sep 17 00:00:00 2001
+From: Danny Milosavljevic <dannym@friendly-machines.com>
+Date: Thu, 28 May 2026 10:47:34 +0900
+Subject: [PATCH 09/15] ui/gtk3: Fix display wait timeout from ~5 years to 3 minutes
+
+MAX_DISPLAY_IDLE_TIME included G_USEC_PER_SEC in the multiplication,
+making the iteration count 18 billion instead of the intended 18000.
+Each iteration sleeps 10ms, so the effective timeout was 5.7 years
+instead of the intended 3 minutes.
+
+The sleep duration already uses G_USEC_PER_SEC / SLEEP_DIV_PER_SEC,
+so the iteration count should just be SLEEP_DIV_PER_SEC * 60 * 3.
+
+Closes: #2882
+---
+ ui/gtk3/application.vala | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ui/gtk3/application.vala b/ui/gtk3/application.vala
+index dcabc892..aa331543 100644
+--- a/ui/gtk3/application.vala
++++ b/ui/gtk3/application.vala
+@@ -25,7 +25,7 @@ const string IBUS_WAYLAND_VERSION="1.1";
+ const ulong G_USEC_PER_SEC=1000000L;
+ const ulong SLEEP_DIV_PER_SEC = 100L;
+ const ulong MAX_DISPLAY_IDLE_TIME =
+-        G_USEC_PER_SEC * SLEEP_DIV_PER_SEC * 60 * 3;
++        SLEEP_DIV_PER_SEC * 60 * 3;
+ 
+ static string prgname;
+ static IBus.Bus bus;
+-- 
+2.54.0
+
+From 41f36c09b47daf34accecb5c0b870af63b3ba730 Mon Sep 17 00:00:00 2001
+From: Danny Milosavljevic <dannym@friendly-machines.com>
+Date: Thu, 28 May 2026 10:59:14 +0900
+Subject: [PATCH 10/15] ui/gtk3: Fix log timestamp losing microsecond precision
+
+get_microsecond() was divided by 1000 (giving milliseconds) then
+formatted with %06d, producing misleading output like "000846".
+Use get_microsecond() directly with %06d, matching the C counterpart
+in ibuswaylandim.c.  Also use '.' separator instead of ':' to
+distinguish sub-second part from the time fields.
+
+Closes: #2884
+---
+ ui/gtk3/application.vala | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/ui/gtk3/application.vala b/ui/gtk3/application.vala
+index aa331543..a9962685 100644
+--- a/ui/gtk3/application.vala
++++ b/ui/gtk3/application.vala
+@@ -216,9 +216,9 @@ class Application {
+         if (m_user == null)
+             m_user = "UNKNOW";
+         GLib.DateTime now = new GLib.DateTime.now_local();
+-        var msec = now.get_microsecond() / 1000;
+-        m_log.printf("Start %02d:%02d:%02d:%06d\n",
+-                     now.get_hour(), now.get_minute(), now.get_second(), msec);
++        m_log.printf("Start %02d:%02d:%02d.%06d\n",
++                     now.get_hour(), now.get_minute(), now.get_second(),
++                     now.get_microsecond());
+         m_log.flush();
+         return true;
+     }
+-- 
+2.54.0
+
+From e8d36baadfb138742403b484dbaaa53d1bdda3bc Mon Sep 17 00:00:00 2001
+From: Danny Milosavljevic <dannym@friendly-machines.com>
+Date: Thu, 28 May 2026 11:07:10 +0900
+Subject: [PATCH 11/15] client/wayland: Copy keysym into async key event
+
+_process_key_event_async() did not copy event->sym into the async
+event struct.  The callback _process_key_event_done() then called
+ibus_wayland_im_post_key() with sym=0, so ISO_Level*_Latch keysyms
+were not filtered in async mode.
+
+Closes: #2886
+---
+ client/wayland/ibuswaylandim.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index 69d10fe8..37fe4bad 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -1419,6 +1419,7 @@ _process_key_event_async (IBusWaylandIM       *wlim,
+     async_event->key_serial = event->key_serial;
+     async_event->time = event->time;
+     async_event->key = event->key;
++    async_event->sym = event->sym;
+     async_event->modifiers = event->modifiers & ~IBUS_RELEASE_MASK;
+     async_event->state = event->state;
+     async_event->wlim = wlim;
+-- 
+2.54.0
+
+From 526e509b968cb1334727dbdc9f91f44e8dc038b5 Mon Sep 17 00:00:00 2001
+From: matiwari <matiwari@redhat.com>
+Date: Thu, 28 May 2026 11:21:25 +0900
+Subject: [PATCH 12/15] ui/gtk3: remove unused m_candidates array
+
+The 'm_candidates' array was never read and retained references to obsolete
+Gtk.Label objects during panel rebuilds, contributing to a memory leak.
+
+Closes: #2471
+---
+ ui/gtk3/emojier.vala | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 097edbda..d899780f 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -358,7 +358,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
+      */
+     private bool m_candidate_panel_mode;
+     private IBus.LookupTable m_lookup_table;
+-    private Gtk.Label[] m_candidates;
+     private uint m_entry_notify_show_id;
+     private uint m_entry_notify_disable_id;
+     private Gtk.ProgressBar m_unicode_progress_bar;
+@@ -1668,7 +1667,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
+                         1, 1);
+             n++;
+ 
+-            m_candidates += label;
+         }
+         m_candidate_panel_is_visible = true;
+         if (!m_is_up_side_down) {
+-- 
+2.54.0
+
+From 5bbe88a1936246185a65f76e58cc85871401e59a Mon Sep 17 00:00:00 2001
+From: Tianhao Chai <cth451@gmail.com>
+Date: Wed, 27 May 2026 13:11:28 +0900
+Subject: [PATCH 13/15] src: make an IBusText own an updated IBusAttrList reference
+
+For all usages of IBusAttrList, the list is an owned reference within a
+IBusText struct. `ibus_panel_convert_text()` and
+`ibus_input_context_convert_text()` violate the invarient by assigning a
+**floating** IBusAttrList reference to anIBusText without sinking it.
+
+When GJS attempts to create a JS representation of an existing GObject,
+it unconditionally sinks the incoming reference.[1] For a non-floating
+reference this up-refs the object.
+
+For this floating IBusAttrLit, `g_object_ref_sink` converts it to a
+strong reference in-place leaving ref-count at 1. At this point the
+`IBusAttrList` is referenced by both the enclosing `IBusText` and the
+newly created GJS wrapper. When one of them is ref-downed the object
+is recycled, leaving the other reference dangling.
+
+To fix this we just need to maintain the list as a non-floating ref.
+We already have a `ibus_text_set_attributes()` that does the intended
+ref sinking, so just use that instead of manually assigning pointers.
+
+[1]: https://gitlab.gnome.org/GNOME/gjs/-/blob/db450465ab0161e131a92992c2509507aa6152aa/gi/object.cpp#L3597
+
+Closes: #2889
+---
+ src/ibusinputcontext.c | 12 ++++--------
+ src/ibuspanelservice.c | 12 ++++--------
+ 2 files changed, 8 insertions(+), 16 deletions(-)
+
+diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c
+index 68d12fb5..bdfd166d 100644
+--- a/src/ibusinputcontext.c
++++ b/src/ibusinputcontext.c
+@@ -569,10 +569,8 @@ ibus_input_context_convert_text (IBusInputContext *context,
+                        text->text, error->message);
+             g_error_free (error);
+         }
+-        if (new_attrs) {
+-            g_object_unref (text->attrs);
+-            text->attrs = new_attrs;
+-        }
++        if (new_attrs)
++            ibus_text_set_attributes (text, new_attrs);
+         break;
+     case IBUS_PREEDIT_FORMAT_HINT:
+         new_attrs = ibus_attr_list_copy_format_to_hint (text->attrs, &error);
+@@ -581,10 +579,8 @@ ibus_input_context_convert_text (IBusInputContext *context,
+                        text->text, error->message);
+             g_error_free (error);
+         }
+-        if (new_attrs) {
+-            g_object_unref (text->attrs);
+-            text->attrs = new_attrs;
+-        }
++        if (new_attrs)
++            ibus_text_set_attributes (text, new_attrs);
+         break;
+     default:
+         g_assert_not_reached ();
+diff --git a/src/ibuspanelservice.c b/src/ibuspanelservice.c
+index 16229822..f50cea19 100644
+--- a/src/ibuspanelservice.c
++++ b/src/ibuspanelservice.c
+@@ -1203,10 +1203,8 @@ ibus_panel_convert_text (IBusPanelService *panel,
+                        text->text, error->message);
+             g_error_free (error);
+         }
+-        if (new_attrs) {
+-            g_object_unref (text->attrs);
+-            text->attrs = new_attrs;
+-        }
++        if (new_attrs)
++            ibus_text_set_attributes (text, new_attrs);
+         break;
+     case IBUS_PREEDIT_FORMAT_HINT:
+         new_attrs = ibus_attr_list_copy_format_to_hint (text->attrs, &error);
+@@ -1215,10 +1213,8 @@ ibus_panel_convert_text (IBusPanelService *panel,
+                        text->text, error->message);
+             g_error_free (error);
+         }
+-        if (new_attrs) {
+-            g_object_unref (text->attrs);
+-            text->attrs = new_attrs;
+-        }
++        if (new_attrs)
++            ibus_text_set_attributes (text, new_attrs);
+         break;
+     default:
+         g_assert_not_reached ();
+-- 
+2.54.0
+
+From 067de1c2b7fa1d464686cce41331b37c0b9c2886 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 27 May 2026 13:26:43 +0900
+Subject: [PATCH 14/15] Add test-deserialize.py
+
+Add a test case to deserialize IBusText
+
+Closes: #2889
+---
+ bindings/pygobject/Makefile.am           |  41 ++++----
+ bindings/pygobject/test-deserialize.py   | 118 +++++++++++++++++++++++
+ bindings/pygobject/test-override-ibus.py |  11 +--
+ src/ibuspanelservice.c                   |   9 +-
+ src/tests/runtest                        |  12 ++-
+ 6 files changed, 167 insertions(+), 28 deletions(-)
+ create mode 100755 bindings/pygobject/test-deserialize.py
+ mode change 100644 => 100755 bindings/pygobject/test-override-ibus.py
+
+diff --git a/bindings/pygobject/Makefile.am b/bindings/pygobject/Makefile.am
+index 84bd37cb..255f5989 100644
+--- a/bindings/pygobject/Makefile.am
++++ b/bindings/pygobject/Makefile.am
+@@ -4,7 +4,7 @@
+ #
+ # Copyright (c) 2012 Daiki Ueno <ueno@unixuser.org>
+ # Copyright (c) 2014-2016 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2018-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2018-2026 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ # Copyright (c) 2012-2018 Red Hat, Inc.
+ #
+ # This library is free software; you can redistribute it and/or
+@@ -27,37 +27,40 @@ NULL =
+ if ENABLE_PYTHON2
+ py2_compile = PYTHON=$(PYTHON2) $(SHELL) $(py_compile)
+ overrides2dir = $(py2overridesdir)
+-overrides2_DATA =				\
+-	gi/overrides/IBus.py			\
+-	$(NULL)
++overrides2_DATA =                               \
++    gi/overrides/IBus.py                        \
++    $(NULL)
+ endif
+ 
+ overridesdir = $(pyoverridesdir)
+-overrides_PYTHON =				\
+-	gi/overrides/IBus.py			\
+-	$(NULL)
++overrides_PYTHON =                              \
++    gi/overrides/IBus.py                        \
++    $(NULL)
+ 
+ TESTS =
+ 
+ if ENABLE_TESTS
+-TESTS += test-override-ibus.py
++TESTS += test-deserialize.py test-override-ibus.py
+ endif
+ 
+ # IBus.Keymap() accesses keymap files
+ TESTS_ENVIRONMENT = \
+-	PYTHONPATH=$(top_srcdir)/tests:$${PYTHONPATH:+:$$PYTHONPATH} \
+-	LD_LIBRARY_PATH=$(top_builddir)/src/.libs:$$LD_LIBRARY_PATH \
+-	GI_TYPELIB_PATH=$(top_builddir)/src:$$GI_TYPELIB_PATH \
+-	IBUS_KEYMAP_PATH=$(top_srcdir)/data/keymaps \
+-	$(NULL)
++    top_srcdir=$(top_srcdir) \
++    PYTHONPATH=$(abs_top_srcdir)/tests:$${PYTHONPATH:+:$$PYTHONPATH} \
++    PYTHONMALLOC=malloc \
++    LD_LIBRARY_PATH=$(abs_top_builddir)/src/.libs:$$LD_LIBRARY_PATH \
++    GI_TYPELIB_PATH=$(abs_top_builddir)/src:$$GI_TYPELIB_PATH \
++    IBUS_KEYMAP_PATH=$(abs_top_srcdir)/data/keymaps \
++    $(NULL)
+ 
+-LOG_COMPILER = $(PYTHON) -B
++LOG_COMPILER = $(top_srcdir)/src/tests/runtest $(PYTHON)
+ 
+-EXTRA_DIST =					\
+-	gi/__init__.py				\
+-	gi/overrides/__init__.py		\
+-	test-override-ibus.py                   \
+-	$(NULL)
++EXTRA_DIST =                                    \
++    gi/__init__.py                              \
++    gi/overrides/__init__.py                    \
++    test-deserialize.py                         \
++    test-override-ibus.py                       \
++    $(NULL)
+ 
+ install-data-hook:
+ 	@$(NORMAL_INSTALL)
+diff --git a/bindings/pygobject/test-deserialize.py b/bindings/pygobject/test-deserialize.py
+new file mode 100755
+index 00000000..290914d7
+--- /dev/null
++++ b/bindings/pygobject/test-deserialize.py
+@@ -0,0 +1,118 @@
++#!/usr/bin/python
++# ibus - The Input Bus
++#
++# Copyright © 2026 Takao Fujiwara <takao.fujiwara1@gmail.com>
++#
++# This library is free software; you can redistribute it and/or
++# modify it under the terms of the GNU Lesser General Public
++# License as published by the Free Software Foundation; either
++# version 2.1 of the License, or (at your option) any later version.
++#
++# This library is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++# Lesser General Public License for more details.
++#
++# You should have received a copy of the GNU Lesser General Public
++# License along with this library; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
++# USA
++
++import sys
++import unittest
++from pathlib import Path
++
++# move the script path at the end, so the necessary modules in system
++# pygobject can be loaded first
++tests_builddir = Path(__file__).resolve().parent
++sys.path = [path for path in sys.path if Path(path) != tests_builddir]
++sys.path.append(str(tests_builddir))
++
++from gi import require_versions as gi_require_versions
++
++gi_require_versions({'IBus': '1.0', 'GLib':  '2.0', 'Gio': '2.0', })
++from gi.repository import IBus
++from gi.repository import GLib
++from gi.repository import Gio
++
++
++class TestOverride(unittest.TestCase):
++    __bus = None
++    __text = None
++    __attrs = None
++    __invocation = None
++
++    def setUp(self):
++        IBus.init()
++        self.__bus = IBus.Bus()
++
++    def update_preedit_text_cb(self,
++                               context,
++                               text,
++                               cursor,
++                               visible):
++        self.update_preedit_text_with_mode_cb(
++                context, text, cursor, visible, 0)
++
++    def update_preedit_text_with_mode_cb(self,
++                                         context,
++                                         text,
++                                         cursor,
++                                         visible,
++                                         mode):
++        self.__text = text
++        self.__attrs = self.__text.get_attributes()
++        print('# Got update-preedit-text-with-mode',
++              self.__text.get_text(), cursor, visible, mode)
++
++
++    def test_deserialized_input_context(self):
++        text = IBus.Text.new_from_string('test')
++        text.append_attribute(IBus.AttrType.HINT,
++                              IBus.AttrPreedit.WHOLE,
++                              0,
++                              text.get_length())
++        text_variant = text.serialize_object()
++        update_preedit_variant = GLib.Variant.new_tuple(
++            GLib.Variant.new_variant(text_variant),
++            GLib.Variant.new_uint32(0),
++            GLib.Variant.new_boolean(True),
++            GLib.Variant.new_uint32(0))
++        context = self.__bus.create_input_context('test')
++        context.connect('update-preedit-text-with-mode',
++                        self.update_preedit_text_with_mode_cb)
++        context.do_g_signal(context,
++                            'org.freedesktop.DBus',
++                            'UpdatePreeditTextWithMode',
++                            update_preedit_variant)
++        print('# Last', self.__attrs, self.__text.get_text())
++
++    def test_deserialized_panel(self):
++        text = IBus.Text.new_from_string('test')
++        text.append_attribute(IBus.AttrType.HINT,
++                              IBus.AttrPreedit.WHOLE,
++                              0,
++                              text.get_length())
++        text_variant = text.serialize_object()
++        update_preedit_variant = GLib.Variant.new_tuple(
++            GLib.Variant.new_variant(text_variant),
++            GLib.Variant.new_uint32(0),
++            GLib.Variant.new_boolean(True))
++        panel = IBus.PanelService.new(self.__bus.get_connection())
++        panel.connect('update-preedit-text',
++                      self.update_preedit_text_cb)
++        self.__invocation = Gio.DBusMethodInvocation()
++        panel.do_service_method_call(panel,
++                                     self.__bus.get_connection(),
++                                     'org.freedesktop.IBus.NullInvocation',
++                                     IBus.PATH_PANEL + '/DryTest',
++                                     IBus.INTERFACE_PANEL,
++                                     'UpdatePreeditText',
++                                     update_preedit_variant.ref(),
++                                     self.__invocation)
++        print('# Last', self.__attrs, self.__text.get_text())
++
++
++if __name__ == '__main__':
++    GLib.log_set_fatal_mask('GLib', GLib.LogLevelFlags.LEVEL_WARNING)
++    unittest.main()
+diff --git a/bindings/pygobject/test-override-ibus.py b/bindings/pygobject/test-override-ibus.py
+old mode 100644
+new mode 100755
+index 8853ff64..89073a08
+--- a/bindings/pygobject/test-override-ibus.py
++++ b/bindings/pygobject/test-override-ibus.py
+@@ -20,16 +20,15 @@
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ # USA
+ 
+-import unittest
+-
+-import os
+ import sys
++import unittest
++from pathlib import Path
+ 
+ # move the script path at the end, so the necessary modules in system
+ # pygobject can be loaded first
+-tests_builddir = os.path.abspath(os.path.dirname(__file__))
+-sys.path = [path for path in sys.path if path != tests_builddir]
+-sys.path.append(tests_builddir)
++tests_builddir = Path(__file__).resolve().parent
++sys.path = [path for path in sys.path if Path(path) != tests_builddir]
++sys.path.append(str(tests_builddir))
+ 
+ import gi
+ gi.require_versions({'GLib': '2.0', 'IBus': '1.0'})
+diff --git a/src/ibuspanelservice.c b/src/ibuspanelservice.c
+index f50cea19..14cca3ec 100644
+--- a/src/ibuspanelservice.c
++++ b/src/ibuspanelservice.c
+@@ -1255,6 +1255,7 @@ ibus_panel_service_service_method_call (IBusService           *service,
+         guint cursor = 0;
+         gboolean visible = FALSE;
+         IBusText *text;
++        gboolean is_dry_test;
+ 
+         g_variant_get (parameters, "(vub)", &variant, &cursor, &visible);
+         text = IBUS_TEXT (ibus_serializable_deserialize (variant));
+@@ -1263,7 +1264,13 @@ ibus_panel_service_service_method_call (IBusService           *service,
+ 
+         g_signal_emit (panel, panel_signals[UPDATE_PREEDIT_TEXT], 0, text, cursor, visible);
+         _g_object_unref_if_floating (text);
+-        g_dbus_method_invocation_return_value (invocation, NULL);
++        is_dry_test = !g_strcmp0 (object_path, IBUS_PATH_PANEL "/DryTest");
++        /* GDBusMethodInvocation does not provide instance methods for Python
++         * gobject-introspection binding so we cannot call
++         * g_dbus_method_invocation_return_value() and g_object_unref().
++         */
++        if (!is_dry_test)
++            g_dbus_method_invocation_return_value (invocation, NULL);
+         return;
+     }
+ 
+diff --git a/src/tests/runtest b/src/tests/runtest
+index 7bd98575..4e54420f 100755
+--- a/src/tests/runtest
++++ b/src/tests/runtest
+@@ -37,11 +37,13 @@ ibus-engine-switch
+ ibus-compose
+ ibus-keypress
+ test-stress
++test-deserialize.py
+ test-override-ibus.py
+ "
+ IBUS_SCHEMA_FILE='org.freedesktop.ibus.gschema.xml'
+ GTK_QUERY_MODULE=gtk-query-immodules-3.0-32
+ MACHINE=`uname -m`
++PYTHON_TEST_ARGS='-B'
+ 
+ if test x"$MACHINE" = xx86_64 ; then
+     GTK_QUERY_MODULE=gtk-query-immodules-3.0-64
+@@ -137,6 +139,12 @@ run_test_case()
+     cd $tstdir
+ 
+     if echo "$tst" | grep -q 'python' ; then
++        # Convert the relative path to the absolute path.
++        if echo "$tst" | grep -q "\./" ; then
++            tst=`realpath $tst`
++        elif ! echo "$tst" | grep -q "/" ; then
++            tst=`command -v $tst`
++        fi
+         name=`func_basename "$1"`
+     else
+         name=`func_basename "$tst"`
+@@ -247,10 +255,10 @@ run_test_case()
+             shift
+             if echo "$tst" | grep -q '^/' ; then
+                 # absolute path for Meson
+-                "$interpreter" "$tst" ${1+"$@"}
++                "$interpreter" $PYTHON_TEST_ARGS "$tst" ${1+"$@"}
+             else
+                 # relative path for autotools
+-                "$interpreter" "../$tst" ${1+"$@"}
++                "$interpreter" $PYTHON_TEST_ARGS "../$tst" ${1+"$@"}
+             fi
+         else
+             # absolute path for Meson
+-- 
+2.54.0
+
+From 26aaf13bd62b972cfee1f4b58ba168d214e99eca Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 26 May 2026 21:19:43 +0900
+Subject: [PATCH 15/15] client/wayland: Ignore wrong cursor and anchor in surrounding-text
+
+KDE Plasma could send wrong cursor and anchor with surrounding_text()
+signal in `zwp_input_method_context_v1_listener`.
+I'm not sure if it's caused by the compositor or IBus engines but
+ibus-wayland should ignore them.
+
+Closes: rhbz#2444787
+---
+ client/wayland/ibuswaylandim.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/client/wayland/ibuswaylandim.c b/client/wayland/ibuswaylandim.c
+index 37fe4bad..367b07c5 100644
+--- a/client/wayland/ibuswaylandim.c
++++ b/client/wayland/ibuswaylandim.c
+@@ -805,11 +805,21 @@ handle_surrounding_text (void                                  *data,
+ #if ENABLE_SURROUNDING
+     IBusWaylandIM *wlim = data;
+     IBusWaylandIMPrivate *priv;
++    size_t len;
+     IBusText *ibustext;
+ 
+     g_return_if_fail (IBUS_IS_WAYLAND_IM (wlim));
++    g_return_if_fail (text);
+     priv = ibus_wayland_im_get_instance_private (wlim);
+ 
++    len = strlen (text);
++    if (G_UNLIKELY (len > G_MAXUINT32 || len < cursor || len < anchor)) {
++        /* Should not show the text as a security reason. */
++        g_warning ("Received a wrong surrounding text length %zu < cursor %u "
++                   "anchor %u",
++                   len, cursor, anchor);
++        return;
++    }
+     ibustext = ibus_text_new_from_string (text);
+ 
+     if (priv->surrounding_text)
+-- 
+2.54.0
+

diff --git a/ibus.spec b/ibus.spec
index c4d3dff..7c44ad6 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -58,7 +58,7 @@
 Name:           ibus
 Version:        1.5.34
 # https://github.com/fedora-infra/rpmautospec/issues/101
-Release:        1%{?dist}
+Release:        2%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPL-2.1-or-later
 URL:            https://github.com/ibus/%name/wiki
@@ -67,6 +67,7 @@ Source1:        https://github.com/ibus/%name/releases/download/%{source_version
 Source2:        %{name}-xinput
 Source3:        %{name}.conf.5
 # Patch0:         %%{name}-HEAD.patch
+Patch0:         %{name}-HEAD.patch
 # Under testing #1349148 #1385349 #1350291 #1406699 #1432252 #1601577
 Patch1:         %{name}-1385349-segv-bus-proxy.patch
 
@@ -583,6 +584,12 @@ dconf update || :
 %{_datadir}/installed-tests/ibus
 
 %changelog
+* Wed Jun 03 2026 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.34-2
+- Backport upstream patches
+- Make an IBusText own an updated IBusAttrList reference
+- Resolves: #2444787 SEGV with wrong anchor in surrounding-text in Wayland
+- Updates: #2480408 IBus callbacks in Wayland to clarify SEGVs
+
 * Thu Apr 30 2026 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.34-1
 - Bump to 1.5.34
 

                 reply	other threads:[~2026-06-03  3:16 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178045658422.1.7619523802956667475.rpms-ibus-2f7e8dde9a6c@fedoraproject.org \
    --to=tfujiwar@redhat.com \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox