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] autotool: Add some new features
Date: Sun, 31 May 2026 02:07:54 GMT	[thread overview]
Message-ID: <178019327428.1.7975090710454445502.rpms-ibus-ebbfbeae2909@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/ibus
            Branch : autotool
            Commit : ebbfbeae2909e68c3b6f8165302a2620fb55e45f
            Author : Takao Fujiwara <tfujiwar@redhat.com>
            Date   : 2022-07-07T08:47:31+09:00
            Stats  : +1942/-213 in 3 file(s)
            URL    : https://src.fedoraproject.org/rpms/ibus/c/ebbfbeae2909e68c3b6f8165302a2620fb55e45f?branch=autotool

            Log:
            Add some new features

- Add IBUS_CAP_OSK to IBusCapabilite
- Update ibus restart for --service-file option
- Update manpage for ibus im-module command
- Implement new process_key_event for GTK4
- Add focus_in_id()/focus_out_id() class methods in IBusEngine

---
diff --git a/ibus-1385349-segv-bus-proxy.patch b/ibus-1385349-segv-bus-proxy.patch
index 7a2b140..1f16094 100644
--- a/ibus-1385349-segv-bus-proxy.patch
+++ b/ibus-1385349-segv-bus-proxy.patch
@@ -1,6 +1,6 @@
-From fd69784c0ed45fe11b801f3a563231735920896a Mon Sep 17 00:00:00 2001
+From c093fec83da277c79f31e09b1b910d35bd4135c8 Mon Sep 17 00:00:00 2001
 From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 23 May 2022 21:50:16 +0900
+Date: Sat, 25 Jun 2022 19:46:01 +0900
 Subject: [PATCH] Fix SEGV in bus_panel_proxy_focus_in()
 
 rhbz#1350291 SEGV in BUS_IS_CONNECTION(skip_connection) in
@@ -42,12 +42,12 @@ BUG=rhbz#1767976
 BUG=rhbz#1797120
 ---
  bus/dbusimpl.c         | 47 ++++++++++++++++++++++++---
- bus/engineproxy.c      | 51 ++++++++++++++++++++++-------
+ bus/engineproxy.c      | 44 +++++++++++++++++++------
  client/x11/main.c      |  8 ++++-
  src/ibusbus.c          |  6 ++++
  ui/gtk3/extension.vala |  4 +++
  ui/gtk3/switcher.vala  | 73 +++++++++++++++++++++++++-----------------
- 6 files changed, 142 insertions(+), 47 deletions(-)
+ 6 files changed, 137 insertions(+), 45 deletions(-)
 
 diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c
 index 59787a80..af2fbde2 100644
@@ -138,10 +138,10 @@ index 59787a80..af2fbde2 100644
      if (incoming) {
          /* is incoming message */
 diff --git a/bus/engineproxy.c b/bus/engineproxy.c
-index 2d98995c..bbbe5532 100644
+index fd1f34fb..57c061ba 100644
 --- a/bus/engineproxy.c
 +++ b/bus/engineproxy.c
-@@ -660,20 +660,33 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+@@ -690,10 +690,12 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
      g_return_if_reached ();
  }
  
@@ -153,16 +153,14 @@ index 2d98995c..bbbe5532 100644
 +                               GDBusConnection *connection,
 +                               GError         **error)
  {
-+    GDBusProxyFlags flags;
-+    BusEngineProxy *engine;
-+
+     GDBusProxyFlags flags;
+     BusEngineProxy *engine;
+@@ -704,12 +706,20 @@ bus_engine_proxy_new_internal (const gchar     *path,
      g_assert (path);
      g_assert (IBUS_IS_ENGINE_DESC (desc));
      g_assert (G_IS_DBUS_CONNECTION (connection));
 +    g_assert (error && *error == NULL);
  
--    GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
--    BusEngineProxy *engine =
 +    /* rhbz#1601577 engine == NULL if connection is closed. */
 +    if (g_dbus_connection_is_closed (connection)) {
 +        *error = g_error_new (G_DBUS_ERROR,
@@ -170,19 +168,19 @@ index 2d98995c..bbbe5532 100644
 +                              "Connection is closed.");
 +        return NULL;
 +    }
-+    flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
-+    engine =
-         (BusEngineProxy *) g_initable_new (BUS_TYPE_ENGINE_PROXY,
-                                            NULL,
--                                           NULL,
-+                                           error,
-                                            "desc",              desc,
-                                            "g-connection",      connection,
-                                            "g-interface-name",  IBUS_INTERFACE_ENGINE,
-@@ -681,12 +694,19 @@ bus_engine_proxy_new_internal (const gchar     *path,
-                                            "g-default-timeout", g_gdbus_timeout,
-                                            "g-flags",           flags,
-                                            NULL);
+     flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
+     engine = (BusEngineProxy *) g_initable_new (
+             BUS_TYPE_ENGINE_PROXY,
+             NULL,
+-            NULL,
++            error,
+             "desc",              desc,
+             "g-connection",      connection,
+             "g-interface-name",  IBUS_INTERFACE_ENGINE,
+@@ -717,6 +727,12 @@ bus_engine_proxy_new_internal (const gchar     *path,
+             "g-default-timeout", g_gdbus_timeout,
+             "g-flags",           flags,
+             NULL);
 +    /* FIXME: rhbz#1601577 */
 +    if (!engine) {
 +        /* show abrt local variable */
@@ -192,6 +190,7 @@ index 2d98995c..bbbe5532 100644
      const gchar *layout = ibus_engine_desc_get_layout (desc);
      if (layout != NULL && layout[0] != '\0') {
          engine->keymap = ibus_keymap_get (layout);
+@@ -736,6 +752,7 @@ bus_engine_proxy_new_internal (const gchar     *path,
      }
      return engine;
  }
@@ -199,7 +198,7 @@ index 2d98995c..bbbe5532 100644
  
  typedef struct {
      GTask           *task;
-@@ -748,23 +768,30 @@ create_engine_ready_cb (BusFactoryProxy    *factory,
+@@ -798,23 +815,30 @@ create_engine_ready_cb (BusFactoryProxy    *factory,
                          GAsyncResult       *res,
                          EngineProxyNewData *data)
  {
@@ -401,5 +400,5 @@ index a4529c88..29a70dd5 100644
  #if VALA_0_34
          seat.ungrab();
 -- 
-2.35.1
+2.35.3
 

diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 2e08e8c..dc9f150 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -1782,9 +1782,83 @@ index 101c2b3d..452b14c8 100644
 -- 
 2.35.3
 
-From 78ce092914e00d6af3b2b90263dd6f41de75741e Mon Sep 17 00:00:00 2001
+From b94f0c1cea5d0e423fef3bcc13b23f212f04c930 Mon Sep 17 00:00:00 2001
 From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Sat, 25 Jun 2022 20:48:50 +0900
+Date: Thu, 7 Jul 2022 08:13:57 +0900
+Subject: [PATCH] src: Add IBUS_CAP_OSK to IBusCapabilite
+
+Some IMEs' behavior is different between the on-screen keyboard and
+the direct physical keyboard and this flag is useful for the IMEs.
+
+Also fix src/ibusaccelgroup.c for gtkdoc-mkhtml.
+If the API comment of IBusCapabilite is updated, XML & HTML files
+are rebuilt and gtk-doc-1.33.2 no longer accepts HTML tags in
+the comments.
+---
+ src/ibusaccelgroup.c | 14 +++++++-------
+ src/ibustypes.h      |  2 ++
+ 2 files changed, 9 insertions(+), 7 deletions(-)
+
+diff --git a/src/ibusaccelgroup.c b/src/ibusaccelgroup.c
+index ef2d3976..aec1c7e4 100644
+--- a/src/ibusaccelgroup.c
++++ b/src/ibusaccelgroup.c
+@@ -267,14 +267,14 @@ is_keycode (const gchar *string)
+  *     modifier mask, %NULL
+  *
+  * Parses a string representing an accelerator. The format looks like
+- * “<Control>a” or “<Shift><Alt>F1” or “<Release>z” (the last one is
+- * for key release).
++ * “&lt;Control&gt;a” or “&lt;Shift&gt;&lt;Alt&gt;F1” or “&lt;Release%gt;z”
++ * (the last one is for key release).
+  *
+  * The parser is fairly liberal and allows lower or upper case, and also
+- * abbreviations such as “<Ctl>” and “<Ctrl>”. Key names are parsed using
+- * gdk_keyval_from_name(). For character keys the name is not the symbol,
+- * but the lowercase name, e.g. one would use “<Ctrl>minus” instead of
+- * “<Ctrl>-”.
++ * abbreviations such as “&lt;Ctl&gt;” and “&lt;Ctrl&gt;”. Key names are
++ * parsed using gdk_keyval_from_name(). For character keys the name is not the
++ * symbol, but the lowercase name, e.g. one would use “&lt;Ctrl&gt;minus”
++ * instead of “&lt;Ctrl&gt;-”.
+  *
+  * If the parse fails, @accelerator_key and @accelerator_mods will
+  * be set to 0 (zero).
+@@ -403,7 +403,7 @@ out:
+  *
+  * Converts an accelerator keyval and modifier mask into a string
+  * parseable by gtk_accelerator_parse(). For example, if you pass in
+- * #IBUS_KEY_q and #IBUS_CONTROL_MASK, this function returns “<Control>q”.
++ * #IBUS_KEY_q and #IBUS_CONTROL_MASK, this function returns “&lt;Control&gt;q”.
+  *
+  * If you need to display accelerators in the user interface,
+  * see gtk_accelerator_get_label().
+diff --git a/src/ibustypes.h b/src/ibustypes.h
+index 990659ac..60bcb92b 100644
+--- a/src/ibustypes.h
++++ b/src/ibustypes.h
+@@ -108,6 +108,7 @@ typedef enum
+  * @IBUS_CAP_PROPERTY: UI is capable to have property.
+  * @IBUS_CAP_SURROUNDING_TEXT: Client can provide surround text,
+  *  or IME can handle surround text.
++ * @IBUS_CAP_OSK: UI is owned by on-screen keyboard.
+  *
+  * Capability flags of UI.
+  */
+@@ -118,6 +119,7 @@ typedef enum {
+     IBUS_CAP_FOCUS              = 1 << 3,
+     IBUS_CAP_PROPERTY           = 1 << 4,
+     IBUS_CAP_SURROUNDING_TEXT   = 1 << 5,
++    IBUS_CAP_OSK                = 1 << 6,
+ } IBusCapabilite;
+ 
+ /**
+-- 
+2.35.3
+
+From 4e48e7237d73d20f0426265dbb6b692b14891932 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 7 Jul 2022 08:21:24 +0900
 Subject: [PATCH] tools: Enable ibus restart in GNOME desktop
 
 If ibus-daemon is called via systemd, IBus restart API cannot restart
@@ -1792,11 +1866,13 @@ ibus-daemon but just terminates it.
 Now ibus restart command checks the systemd avaiability and restart
 ibus-daemon via systemd.
 ibus start command is also added to launch ibus-daemon with systemd.
+
+BUG=https://github.com/ibus/ibus/issues/2407
 ---
  tools/Makefile.am |   3 +-
  tools/ibus.1.in   |  30 +++++-
- tools/main.vala   | 231 ++++++++++++++++++++++++++++++++++++++++++++--
- 3 files changed, 252 insertions(+), 12 deletions(-)
+ tools/main.vala   | 261 ++++++++++++++++++++++++++++++++++++++++++++--
+ 3 files changed, 330 insertions(+), 44 deletions(-)
 
 diff --git a/tools/Makefile.am b/tools/Makefile.am
 index 5c18d3d6..e380a9aa 100644
@@ -1820,7 +1896,7 @@ index 5c18d3d6..e380a9aa 100644
  	--pkg=posix \
  	--pkg=config \
 diff --git a/tools/ibus.1.in b/tools/ibus.1.in
-index 525d972e..0c16a0d2 100644
+index 525d972e..fe1b7157 100644
 --- a/tools/ibus.1.in
 +++ b/tools/ibus.1.in
 @@ -3,7 +3,7 @@
@@ -1828,7 +1904,7 @@ index 525d972e..0c16a0d2 100644
  .\" Copyright (c) Peng Huang <shawn.p.huang@gmail.com>, 2013.
  .\"
 -.TH "IBUS" 1 "May 2017" "@VERSION@" "User Commands"
-+.TH "IBUS" 1 "Jun 2022" "@VERSION@" "User Commands"
++.TH "IBUS" 1 "Jul 2022" "@VERSION@" "User Commands"
  .SH NAME
  .B ibus
  \- command line utility for ibus
@@ -1879,7 +1955,7 @@ index 525d972e..0c16a0d2 100644
  .B \-\-system
  is not given.
 diff --git a/tools/main.vala b/tools/main.vala
-index 26e7fd88..71134334 100644
+index 26e7fd88..407eaf74 100644
 --- a/tools/main.vala
 +++ b/tools/main.vala
 @@ -3,7 +3,7 @@
@@ -1891,7 +1967,7 @@ index 26e7fd88..71134334 100644
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
-@@ -27,17 +27,22 @@ private const string IBUS_SCHEMAS_GENERAL_HOTKEY =
+@@ -27,17 +27,23 @@ private const string IBUS_SCHEMAS_GENERAL_HOTKEY =
  private const string IBUS_SCHEMAS_PANEL = "org.freedesktop.ibus.panel";
  private const string IBUS_SCHEMAS_PANEL_EMOJI =
          "org.freedesktop.ibus.panel.emoji";
@@ -1905,6 +1981,7 @@ index 26e7fd88..71134334 100644
  string engine_id = null;
 +bool verbose = false;
 +string daemon_type = null;
++string systemd_service_file = null;
  
  class EngineList {
      public IBus.EngineDesc[] data = {};
@@ -1914,7 +1991,7 @@ index 26e7fd88..71134334 100644
  IBus.Bus? get_bus() {
      var bus = new IBus.Bus();
      if (!bus.is_connected ())
-@@ -45,6 +50,123 @@ IBus.Bus? get_bus() {
+@@ -45,6 +51,131 @@ IBus.Bus? get_bus() {
      return bus;
  }
  
@@ -1933,14 +2010,14 @@ index 26e7fd88..71134334 100644
 +get_ibus_systemd_object_path(GLib.DBusConnection connection,
 +                             bool                verbose) {
 +    string object_path = null;
-+    const string service_file = SYSTEMD_SESSION_GNOME_FILE;
++    assert(systemd_service_file != null);
 +    try {
 +        var variant = connection.call_sync (
 +                "org.freedesktop.systemd1",
 +                "/org/freedesktop/systemd1",
 +                "org.freedesktop.systemd1.Manager",
 +                "GetUnit",
-+                new GLib.Variant("(s)", service_file),
++                new GLib.Variant("(s)", systemd_service_file),
 +                new GLib.VariantType("(o)"),
 +                GLib.DBusCallFlags.NONE,
 +                -1,
@@ -1949,13 +2026,14 @@ index 26e7fd88..71134334 100644
 +        if (verbose) {
 +            stderr.printf("Succeed to get an object path \"%s\" for IBus " +
 +                          "systemd service file \"%s\".\n",
-+                          object_path, service_file);
++                          object_path, systemd_service_file);
 +        }
 +        return object_path;
 +    } catch (GLib.Error e) {
 +        if (verbose) {
 +            stderr.printf("IBus systemd service file \"%s\" is not installed " +
-+                          "in your system: %s\n", service_file, e.message);
++                          "in your system: %s\n",
++                          systemd_service_file, e.message);
 +        }
 +    }
 +    return null;
@@ -1968,24 +2046,29 @@ index 26e7fd88..71134334 100644
 +                              bool                verbose) {
 +    string? state = null;
 +    try {
-+        var variant = connection.call_sync (
-+                "org.freedesktop.systemd1",
-+                object_path,
-+                "org.freedesktop.DBus.Properties",
-+                "Get",
-+                new GLib.Variant("(ss)",
-+                                 "org.freedesktop.systemd1.Unit",
-+                                 "ActiveState"),
-+                new GLib.VariantType("(v)"),
-+                GLib.DBusCallFlags.NONE,
-+                -1,
-+                null);
-+        GLib.Variant child = null;
-+        variant.get("(v)", ref child);
-+        state = child.dup_string();
-+        if (verbose) {
-+            stderr.printf("Succeed to get the state \"%s\" for an object " +
-+                          "path \"%s\".\n", state, object_path);
++        while (true) {
++            var variant = connection.call_sync (
++                    "org.freedesktop.systemd1",
++                    object_path,
++                    "org.freedesktop.DBus.Properties",
++                    "Get",
++                    new GLib.Variant("(ss)",
++                                     "org.freedesktop.systemd1.Unit",
++                                     "ActiveState"),
++                    new GLib.VariantType("(v)"),
++                    GLib.DBusCallFlags.NONE,
++                    -1,
++                    null);
++            GLib.Variant child = null;
++            variant.get("(v)", ref child);
++            state = child.dup_string();
++            if (verbose) {
++                stderr.printf("systemd state is \"%s\" for an object " +
++                              "path \"%s\".\n", state, object_path);
++            }
++            if (state != "activating")
++                break;
++            Posix.sleep(1);
 +        }
 +    } catch (GLib.Error e) {
 +        if (verbose)
@@ -2003,8 +2086,8 @@ index 26e7fd88..71134334 100644
 +                         bool                restart,
 +                         bool                verbose) {
 +    string object_path = null;
-+    const string service_file = SYSTEMD_SESSION_GNOME_FILE;
 +    string method = "StartUnit";
++    assert(systemd_service_file != null);
 +    if (restart)
 +        method = "RestartUnit";
 +    try {
@@ -2013,7 +2096,7 @@ index 26e7fd88..71134334 100644
 +                "/org/freedesktop/systemd1",
 +                "org.freedesktop.systemd1.Manager",
 +                method,
-+                new GLib.Variant("(ss)", service_file, "fail"),
++                new GLib.Variant("(ss)", systemd_service_file, "fail"),
 +                new GLib.VariantType("(o)"),
 +                GLib.DBusCallFlags.NONE,
 +                -1,
@@ -2022,13 +2105,15 @@ index 26e7fd88..71134334 100644
 +        if (verbose) {
 +            stderr.printf("Succeed to restart IBus daemon via IBus systemd " +
 +                          "service file \"%s\": \"%s\"\n",
-+                          service_file, object_path);
++                          systemd_service_file, object_path);
 +        }
 +        return true;
 +    } catch (GLib.Error e) {
 +        if (verbose) {
-+            stderr.printf("Failed to restart IBus daemon via IBus systemd " +
-+                          "service file \"%s\": %s\n", service_file, e.message);
++            stderr.printf("Failed to %s IBus daemon via IBus systemd " +
++                          "service file \"%s\": %s\n",
++                          restart ? "restart" : "start",
++                          systemd_service_file, e.message);
 +        }
 +    }
 +    return false;
@@ -2038,7 +2123,7 @@ index 26e7fd88..71134334 100644
  int list_engine(string[] argv) {
      const OptionEntry[] options = {
          { "name-only", 0, 0, OptionArg.NONE, out name_only,
-@@ -99,6 +221,7 @@ int list_engine(string[] argv) {
+@@ -99,6 +230,7 @@ int list_engine(string[] argv) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2046,7 +2131,7 @@ index 26e7fd88..71134334 100644
  private int exec_setxkbmap(IBus.EngineDesc engine) {
      string layout = engine.get_layout();
      string variant = engine.get_layout_variant();
-@@ -149,6 +272,7 @@ private int exec_setxkbmap(IBus.EngineDesc engine) {
+@@ -149,6 +281,7 @@ private int exec_setxkbmap(IBus.EngineDesc engine) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2054,7 +2139,7 @@ index 26e7fd88..71134334 100644
  int get_set_engine(string[] argv) {
      var bus = get_bus();
      string engine = null;
-@@ -182,20 +306,100 @@ int get_set_engine(string[] argv) {
+@@ -182,20 +315,121 @@ int get_set_engine(string[] argv) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2074,6 +2159,9 @@ index 26e7fd88..71134334 100644
 +        { "type", 0, 0, OptionArg.STRING, out daemon_type,
 +          N_("Start or restart daemon with \"direct\" or \"systemd\" TYPE."),
 +          "TYPE" },
++        { "service-file", 0, 0, OptionArg.STRING, out systemd_service_file,
++          N_("Start or restart daemon with SYSTEMD_SERVICE file."),
++          "SYSTEMD_SERVICE" },
 +        { "verbose", 0, 0, OptionArg.NONE, out verbose,
 +          N_("Show debug messages."), null },
 +        { null }
@@ -2094,6 +2182,8 @@ index 26e7fd88..71134334 100644
 +        stderr.printf("type argument must be \"direct\" or \"systemd\"\n");
 +        return Posix.EXIT_FAILURE;
 +    }
++    if (systemd_service_file == null)
++        systemd_service_file = SYSTEMD_SESSION_GNOME_FILE;
 +
 +    do {
 +        if (daemon_type == "direct")
@@ -2101,19 +2191,35 @@ index 26e7fd88..71134334 100644
 +        GLib.DBusConnection? connection = get_session_bus(verbose);
 +        if (connection == null)
 +            break;
-+        string? object_path = get_ibus_systemd_object_path(connection, verbose);
++        string? object_path = null;
++        if (restart) {
++            object_path = get_ibus_systemd_object_path(connection, verbose);
++            if (object_path == null)
++                break;
++            if (!is_running_daemon_via_systemd(connection,
++                                               object_path,
++                                               verbose)) {
++                break;
++            }
++        }
++        if (!start_daemon_via_systemd(connection, restart, verbose))
++            break;
++        // Do not check the systemd state in case of restart because
++        // the systemd file validation is already done and also stopping
++        // daemon and starting daemon take time and the state could be
++        // "inactive" with the time lag.
++        if (restart)
++            return Posix.EXIT_SUCCESS;
++        object_path = get_ibus_systemd_object_path(connection, verbose);
 +        if (object_path == null)
 +            break;
-+        if (restart &&
-+            !is_running_daemon_via_systemd(connection, object_path, verbose))
++        if (!is_running_daemon_via_systemd(connection, object_path, verbose))
 +            break;
-+        if (start_daemon_via_systemd(connection, restart, verbose))
-+            return Posix.EXIT_SUCCESS;
-         return Posix.EXIT_FAILURE;
++        return Posix.EXIT_SUCCESS;
 +    } while (false);
 +
 +    if (daemon_type == "systemd")
-+        return Posix.EXIT_FAILURE;
+         return Posix.EXIT_FAILURE;
 +    if (restart) {
 +        var bus = get_bus();
 +        if (bus == null) {
@@ -2160,7 +2266,7 @@ index 26e7fd88..71134334 100644
  int exit_daemon(string[] argv) {
      var bus = get_bus();
      if (bus == null) {
-@@ -206,11 +410,13 @@ int exit_daemon(string[] argv) {
+@@ -206,11 +440,13 @@ int exit_daemon(string[] argv) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2174,7 +2280,7 @@ index 26e7fd88..71134334 100644
  int read_cache (string[] argv) {
      const OptionEntry[] options = {
          { "system", 0, 0, OptionArg.NONE, out is_system,
-@@ -251,6 +457,7 @@ int read_cache (string[] argv) {
+@@ -251,6 +487,7 @@ int read_cache (string[] argv) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2182,7 +2288,7 @@ index 26e7fd88..71134334 100644
  int write_cache (string[] argv) {
      const OptionEntry[] options = {
          { "system", 0, 0, OptionArg.NONE, out is_system,
-@@ -283,12 +490,14 @@ int write_cache (string[] argv) {
+@@ -283,12 +520,14 @@ int write_cache (string[] argv) {
              Posix.EXIT_SUCCESS : Posix.EXIT_FAILURE;
  }
  
@@ -2197,7 +2303,7 @@ index 26e7fd88..71134334 100644
  private int read_config_options(string[] argv) {
      const OptionEntry[] options = {
          { "engine-id", 0, 0, OptionArg.STRING, out engine_id,
-@@ -309,6 +518,7 @@ private int read_config_options(string[] argv) {
+@@ -309,6 +548,7 @@ private int read_config_options(string[] argv) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2205,7 +2311,7 @@ index 26e7fd88..71134334 100644
  private GLib.SList<string> get_ibus_schemas() {
      string[] ids = {};
      if (engine_id != null) {
-@@ -342,6 +552,7 @@ private GLib.SList<string> get_ibus_schemas() {
+@@ -342,6 +582,7 @@ private GLib.SList<string> get_ibus_schemas() {
      return ibus_schemas;
  }
  
@@ -2213,7 +2319,7 @@ index 26e7fd88..71134334 100644
  int read_config(string[] argv) {
      if (read_config_options(argv) == Posix.EXIT_FAILURE)
          return Posix.EXIT_FAILURE;
-@@ -370,6 +581,7 @@ int read_config(string[] argv) {
+@@ -370,6 +611,7 @@ int read_config(string[] argv) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2221,7 +2327,7 @@ index 26e7fd88..71134334 100644
  int reset_config(string[] argv) {
      if (read_config_options(argv) == Posix.EXIT_FAILURE)
          return Posix.EXIT_FAILURE;
-@@ -401,6 +613,7 @@ int reset_config(string[] argv) {
+@@ -401,6 +643,7 @@ int reset_config(string[] argv) {
      return Posix.EXIT_SUCCESS;
  }
  
@@ -2229,7 +2335,7 @@ index 26e7fd88..71134334 100644
  #if EMOJI_DICT
  int emoji_dialog(string[] argv) {
      string cmd = Config.LIBEXECDIR + "/ibus-ui-emojier";
-@@ -427,11 +640,13 @@ int emoji_dialog(string[] argv) {
+@@ -427,11 +670,13 @@ int emoji_dialog(string[] argv) {
  }
  #endif
  
@@ -2243,7 +2349,7 @@ index 26e7fd88..71134334 100644
  delegate int EntryFunc(string[] argv);
  
  struct CommandEntry {
-@@ -440,12 +655,14 @@ struct CommandEntry {
+@@ -440,12 +685,14 @@ struct CommandEntry {
      unowned EntryFunc entry;
  }
  
@@ -2258,7 +2364,7 @@ index 26e7fd88..71134334 100644
      { "version", N_("Show version"), print_version },
      { "read-cache", N_("Show the content of registry cache"), read_cache },
      { "write-cache", N_("Create registry cache"), write_cache },
-@@ -460,6 +677,7 @@ const CommandEntry commands[]  = {
+@@ -460,6 +707,7 @@ const CommandEntry commands[]  = {
  
  static string program_name;
  
@@ -2266,7 +2372,7 @@ index 26e7fd88..71134334 100644
  void print_usage(FileStream stream) {
      stream.printf(_("Usage: %s COMMAND [OPTION...]\n\n"), program_name);
      stream.printf(_("Commands:\n"));
-@@ -470,6 +688,7 @@ void print_usage(FileStream stream) {
+@@ -470,6 +718,7 @@ void print_usage(FileStream stream) {
      }
  }
  
@@ -2277,67 +2383,9 @@ index 26e7fd88..71134334 100644
 -- 
 2.35.3
 
-From 6203b6c4e1d41e731bdccb1338ed45c93bc56903 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 21 Jun 2022 00:49:46 +0900
-Subject: [PATCH] src/tests: Unset G_MESSAGES_DEBUG for gsettings in
- xkb-latin-layouts
-
-gsettings cannot get the key value when G_MESSAGES_DEBUG is enabled.
-Add denylist.txt to engine/Makefile.am
----
- engine/Makefile.am          | 3 ++-
- src/tests/xkb-latin-layouts | 7 +++++++
- 2 files changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/engine/Makefile.am b/engine/Makefile.am
-index 03867f52..7256fbc8 100644
---- a/engine/Makefile.am
-+++ b/engine/Makefile.am
-@@ -4,7 +4,7 @@
- #
- # Copyright (c) 2010-2016, Google Inc. All rights reserved.
- # Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
--# Copyright (c) 2013-2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2013-2021 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
-@@ -88,6 +88,7 @@ CLEANFILES = \
- 	$(NULL)
- 
- EXTRA_DIST = \
-+	denylist.txt \
- 	gensimple.py \
- 	iso639converter.py \
- 	simple.xml.in \
-diff --git a/src/tests/xkb-latin-layouts b/src/tests/xkb-latin-layouts
-index f8dced6b..92464234 100755
---- a/src/tests/xkb-latin-layouts
-+++ b/src/tests/xkb-latin-layouts
-@@ -82,9 +82,16 @@ finit()
- 
- test_xkb_keymaps()
- {
-+    # G_MESSAGES_DEBUG=all or G_MESSAGES_DEBUG=GLib-GIO-DEBUG would append
-+    # debug messages to gsettings output and could not get the result correctly.
-+    backup_G_MESSAGES_DEBUG="$G_MESSAGES_DEBUG"
-+    unset G_MESSAGES_DEBUG
-     # Loop over top level schemas since "gsettings list-recursively" only
-     # looks for direct children.
-     xkb_latin_layouts=`gsettings get org.freedesktop.ibus.general xkb-latin-layouts`
-+    if [ x"$backup_G_MESSAGES_DEBUG" != x ] ; then
-+        export G_MESSAGES_DEBUG=$backup_G_MESSAGES_DEBUG
-+    fi
-     while read keymap ; do
-         eval keymap="$keymap"
-         HAS_VARIANT=$($ECHO "$keymap" | grep '(' 2> /dev/null) ||:
--- 
-2.35.3
-
-From 65a70a49416a5a2f0fe75815cafce68ca39ee1f1 Mon Sep 17 00:00:00 2001
+From 5b441fabc9d766e694b992e0e2f28924d00a7402 Mon Sep 17 00:00:00 2001
 From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 29 Jun 2022 15:36:12 +0900
+Date: Thu, 7 Jul 2022 08:22:26 +0900
 Subject: [PATCH] tools: Add ibus im-module command
 
 ibus im-module command can retrive gtk-im-module value from an
@@ -2345,14 +2393,15 @@ instance of GtkIMMultiContext.
 The GTK version can be specified by --type option and the default
 is --type=gtk3 and GTK3 im-ibus.so is dlopened.
 ---
- client/gtk2/ibusim.c            | 20 +++++++
- client/gtk4/ibusim.c            | 20 +++++++
- tools/IBusIMModule-1.0.metadata |  1 +
- tools/Makefile.am               | 92 ++++++++++++++++++++++++++++++---
- tools/ibusimmodule.c            | 84 ++++++++++++++++++++++++++++++
- tools/ibusimmodule.h            | 36 +++++++++++++
- tools/main.vala                 | 15 ++++++
- 7 files changed, 262 insertions(+), 6 deletions(-)
+ client/gtk2/ibusim.c            |  20 ++++++
+ client/gtk4/ibusim.c            |  20 ++++++
+ tools/IBusIMModule-1.0.metadata |   1 +
+ tools/Makefile.am               | 116 ++++++++++++++++++++++++++------
+ tools/ibus.1.in                 |  10 +++
+ tools/ibusimmodule.c            |  87 ++++++++++++++++++++++++
+ tools/ibusimmodule.h            |  36 ++++++++++
+ tools/main.vala                 |  11 +++
+ 8 files changed, 308 insertions(+), 40 deletions(-)
  create mode 100644 tools/IBusIMModule-1.0.metadata
  create mode 100644 tools/ibusimmodule.c
  create mode 100644 tools/ibusimmodule.h
@@ -2421,42 +2470,110 @@ index 00000000..14adc9ee
 @@ -0,0 +1 @@
 +IBusIMModule cheader_filename="ibusimmodule.h" name="IBusIMModule"
 diff --git a/tools/Makefile.am b/tools/Makefile.am
-index e380a9aa..f96f2ba2 100644
+index e380a9aa..a9262ee0 100644
 --- a/tools/Makefile.am
 +++ b/tools/Makefile.am
-@@ -22,6 +22,12 @@
- # USA
- 
+@@ -24,9 +24,19 @@
  NULL =
-+noinst_LTLIBRARIES =
+ 
+ libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
++libibusimmodule = libibusimmodule.la
++ibusimmodule_gir = IBusIMModule-1.0.gir
++ibus_immodule_vapi = ibus-immodule-1.0.vapi
+ libibus_emoji_dialog = \
+     $(top_builddir)/ui/gtk3/libibus-emoji-dialog-@IBUS_API_VERSION@.la
+ 
++noinst_LTLIBRARIES = $(libibusimmodule)
 +noinst_DATA =
 +INTROSPECTION_GIRS =
 +MAINTAINERCLEANFILES =
 +DISTCLEANFILES =
 +VAPIGEN_VAPIS =
++
+ # force include config.h before gi18n.h.
+ AM_CPPFLAGS = \
+ 	-I$(top_srcdir)/src \
+@@ -47,22 +57,26 @@ AM_CFLAGS = \
+ 	$(NULL)
+ 
+ AM_LDADD = \
+-	@GOBJECT2_LIBS@ \
+-	@GLIB2_LIBS@ \
+-	@GIO2_LIBS@ \
+-	@GTHREAD2_LIBS@ \
+-	$(libibus) \
+-	$(NULL)
++    @GOBJECT2_LIBS@ \
++    @GLIB2_LIBS@ \
++    @GIO2_LIBS@ \
++    @GTHREAD2_LIBS@ \
++    $(libibus) \
++    $(libibusimmodule) \
++    $(NULL)
  
- libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
- libibus_emoji_dialog = \
-@@ -57,6 +63,8 @@ AM_LDADD = \
  AM_VALAFLAGS = \
- 	--vapidir=$(top_builddir)/bindings/vala \
- 	--vapidir=$(top_srcdir)/bindings/vala \
-+	--vapidir=$(builddir) \
-+	--vapidir=$(srcdir) \
- 	--pkg=gio-2.0 \
- 	--pkg=ibus-1.0 \
- 	--pkg=posix \
-@@ -91,14 +99,17 @@ man_onedir = $(mandir)/man1
+-	--vapidir=$(top_builddir)/bindings/vala \
+-	--vapidir=$(top_srcdir)/bindings/vala \
+-	--pkg=gio-2.0 \
+-	--pkg=ibus-1.0 \
+-	--pkg=posix \
+-	--pkg=config \
+-	--target-glib="$(VALA_TARGET_GLIB_VERSION)" \
+-	$(NULL)
++    --vapidir=$(top_builddir)/bindings/vala \
++    --vapidir=$(top_srcdir)/bindings/vala \
++    --vapidir=$(builddir) \
++    --vapidir=$(srcdir) \
++    --pkg=gio-2.0 \
++    --pkg=ibus-1.0 \
++    --pkg=ibus-immodule-1.0 \
++    --pkg=posix \
++    --pkg=config \
++    --target-glib="$(VALA_TARGET_GLIB_VERSION)" \
++    $(NULL)
+ 
+ bin_PROGRAMS = ibus
+ 
+@@ -79,9 +93,27 @@ bash_completion_DATA= \
+ 	$(NULL)
+ bash_completiondir=@datadir@/bash-completion/completions
+ 
++libibusimmodule_la_SOURCES = \
++    ibusimmodule.c                                      \
++    ibusimmodule.h                                      \
++    $(NULL)
++libibusimmodule_la_CFLAGS = \
++    @GLIB2_CFLAGS@                                      \
++    -DGTK2_IM_MODULEDIR=\"$(GTK2_IM_MODULEDIR)\"        \
++    -DGTK3_IM_MODULEDIR=\"$(GTK3_IM_MODULEDIR)\"        \
++    -DGTK4_IM_MODULEDIR=\"$(GTK4_IM_MODULEDIR)\"        \
++    $(NULL)
++libibusimmodule_la_LIBADD = \
++    @GLIB2_LIBS@                                        \
++    $(NULL)
++libibusimmodule_la_LDFLAGS = \
++    -no-undefined                                       \
++    -export-symbols-regex "ibus_.*"                     \
++    $(NULL)
++
+ man_one_in_files = ibus.1.in
+ man_one_files = $(man_one_in_files:.1.in=.1)
+-man_one_DATA =$(man_one_files:.1=.1.gz) 
++man_one_DATA =$(man_one_files:.1=.1.gz)
+ man_onedir = $(mandir)/man1
+ %.1: %.1.in
+ 	$(AM_V_GEN) sed \
+@@ -91,14 +123,17 @@ man_onedir = $(mandir)/man1
  	$(AM_V_GEN) gzip -c $< > $@.tmp && mv $@.tmp $@
  
  EXTRA_DIST = \
 -	$(man_one_in_files) \
 -	ibus.bash \
 -	$(NULL)
++    $(ibus_immodule_vapi) \
++    $(ibusimmodule_gir) \
 +    $(man_one_in_files) \
 +    ibus.bash \
-+    ibusimmodule.c \
-+    ibusimmodule.h \
 +    IBusIMModule-1.0.metadata \
 +    $(NULL)
  
@@ -2470,38 +2587,12 @@ index e380a9aa..f96f2ba2 100644
  
  if ENABLE_EMOJI_DICT
  if ENABLE_UI
-@@ -108,4 +119,73 @@ AM_VALAFLAGS += \
+@@ -108,4 +143,43 @@ AM_VALAFLAGS += \
  endif
  endif
  
 +if HAVE_INTROSPECTION
 +BUILT_SOURCES = $(INTROSPECTION_GIRS) $(VAPIGEN_VAPIS)
-+libibusimmodule = libibusimmodule.la
-+noinst_LTLIBRARIES += $(libibusimmodule)
-+
-+AM_LDADD += $(libibusimmodule)
-+AM_VALAFLAGS += \
-+    --define=LIB_IBUS_MODULE \
-+    --pkg=ibus-immodule-1.0 \
-+    $(NULL)
-+
-+libibusimmodule_la_SOURCES = \
-+    ibusimmodule.c                                      \
-+    ibusimmodule.h                                      \
-+    $(NULL)
-+libibusimmodule_la_CFLAGS = \
-+    @GLIB2_CFLAGS@                                      \
-+    -DGTK2_IM_MODULEDIR=\"$(GTK2_IM_MODULEDIR)\"        \
-+    -DGTK3_IM_MODULEDIR=\"$(GTK3_IM_MODULEDIR)\"        \
-+    -DGTK4_IM_MODULEDIR=\"$(GTK4_IM_MODULEDIR)\"        \
-+    $(NULL)
-+libibusimmodule_la_LIBADD = \
-+    @GLIB2_LIBS@                                        \
-+    $(NULL)
-+libibusimmodule_la_LDFLAGS = \
-+    -no-undefined                                       \
-+    -export-symbols-regex "ibus_.*"                     \
-+    $(NULL)
 +
 +-include $(INTROSPECTION_MAKEFILE)
 +INTROSPECTION_SCANNER_ARGS =
@@ -2523,33 +2614,50 @@ index e380a9aa..f96f2ba2 100644
 +    -I$(builddir)                                       \
 +    $(NULL)
 +
-+ibusimmodule_gir = IBusIMModule-1.0.gir
 +INTROSPECTION_GIRS += $(ibusimmodule_gir)
 +noinst_DATA += $(ibusimmodule_gir)
-+EXTRA_DIST += $(ibusimmodule_gir)
 +MAINTAINERCLEANFILES += $(ibusimmodule_gir)
 +DISTCLEANFILES += $(ibusimmodule_gir)
 +
 +-include $(VAPIGEN_MAKEFILE)
 +ibus-immodule-1.0.vapi: $(ibusimmodule_gir) IBusIMModule-1.0.metadata
-+ibus_immodule_vapi = ibus-immodule-1.0.vapi
 +ibus_immodule_1_0_vapi_DEPS = glib-2.0
 +ibus_immodule_1_0_vapi_METADATADIRS = $(srcdir)
 +ibus_immodule_1_0_vapi_FILES = IBusIMModule-1.0.gir
 +VAPIGEN_VAPIS += $(ibus_immodule_vapi)
 +noinst_DATA += $(ibus_immodule_vapi)
-+EXTRA_DIST += $(ibus_immodule_vapi)
 +MAINTAINERCLEANFILES += $(ibus_immodule_vapi)
 +DISTCLEANFILES += $(ibus_immodule_vapi)
-+
 +endif
++
  -include $(top_srcdir)/git.mk
+diff --git a/tools/ibus.1.in b/tools/ibus.1.in
+index fe1b7157..84ef5fff 100644
+--- a/tools/ibus.1.in
++++ b/tools/ibus.1.in
+@@ -128,6 +128,16 @@ option enables to match annotations with a partial string. These settings
+ are available with
+ .B ibus\-setup (1)
+ utility.
++.TP
++\fBim-module\fR [\fB\-\-type=TYPE|\-\-help\fR]
++Show an internal im-module value in a virtual GTK application. If IBus is
++installed and configured properly, the output is "ibus". This sub-command
++is useful for some users who build IBus from the source codes and check
++the configurations. Currently the sub-command supports GTK applications only
++and the default is GTK3. If you wish to check a GTK4 application, you can
++specify
++.B \-\-type=gtk4
++option and you can choose one of "gtk2", "gtk3" and "gtk4".
+ 
+ .SH BUGS
+ If you find a bug, please report it at https://github.com/ibus/ibus/issues
 diff --git a/tools/ibusimmodule.c b/tools/ibusimmodule.c
 new file mode 100644
-index 00000000..1587af3d
+index 00000000..20ccc748
 --- /dev/null
 +++ b/tools/ibusimmodule.c
-@@ -0,0 +1,84 @@
+@@ -0,0 +1,87 @@
 +#include <glib.h>
 +#include <glib/gi18n-lib.h>
 +#include <dlfcn.h>
@@ -2558,6 +2666,10 @@ index 00000000..1587af3d
 +#define DEFAULT_IM_MODULE_TYPE "gtk3"
 +#endif
 +
++#define OPTION_TYPE_MESSAGE \
++  N_("Type im-module TYPE = \"gtk2\",  \"gtk3\", \"gtk4\". Default is " \
++     "\"gtk3\".")
++
 +typedef const char * (* IBusIMGetContextIdFunc) (int *argc, char ***argv);
 +
 +static char *im_module_type;
@@ -2568,8 +2680,7 @@ index 00000000..1587af3d
 +{
 +    static const GOptionEntry options[3] = {
 +        { "type", (char)0, (int)0, G_OPTION_ARG_STRING, &im_module_type,
-+         N_ ("Type im-module TYPE = \"gtk2\",  \"gtk3\", \"gtk4\". " \
-+             "Default is \"" DEFAULT_IM_MODULE_TYPE "\"."),
++          OPTION_TYPE_MESSAGE,
 +         "TYPE"},
 +        { NULL }
 +    };
@@ -2677,22 +2788,18 @@ index 00000000..e762a747
 +
 +#endif
 diff --git a/tools/main.vala b/tools/main.vala
-index 71134334..587f3a09 100644
+index 407eaf74..1fed2440 100644
 --- a/tools/main.vala
 +++ b/tools/main.vala
-@@ -641,6 +641,19 @@ int emoji_dialog(string[] argv) {
+@@ -671,6 +671,15 @@ int emoji_dialog(string[] argv) {
  #endif
  
  
 +int read_im_module(string[] argv) {
-+#if LIB_IBUS_MODULE
 +    string? im_module = IBusIMModule.im_module_get_id(argv);
 +    if (im_module == null)
 +        return Posix.EXIT_FAILURE;
 +    print("%s\n".printf(im_module));
-+#else
-+    print("%s\n".printf(_("Not supported in your system.")));
-+#endif
 +    return Posix.EXIT_SUCCESS;
 +}
 +
@@ -2700,7 +2807,7 @@ index 71134334..587f3a09 100644
  int print_help(string[] argv) {
      print_usage(stdout);
      return Posix.EXIT_SUCCESS;
-@@ -672,6 +685,8 @@ const CommandEntry commands[]  = {
+@@ -702,6 +711,8 @@ const CommandEntry commands[]  = {
  #if EMOJI_DICT
      { "emoji", N_("Save emoji on dialog to clipboard"), emoji_dialog },
  #endif
@@ -2712,3 +2819,1619 @@ index 71134334..587f3a09 100644
 -- 
 2.35.3
 
+From 7e42b437c901bb56ca2f776aad5fe65d0d8c246a Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 7 Jul 2022 08:22:42 +0900
+Subject: [PATCH] client/gtk2: Implement new process_key_event for GTK4
+
+---
+ client/gtk2/ibusimcontext.c | 147 ++++++++++++++++++++++++++++++------
+ src/ibustypes.h             |   4 +
+ 2 files changed, 127 insertions(+), 24 deletions(-)
+
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index c7f23293..bc14df00 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -111,13 +111,13 @@ static guint    _signal_delete_surrounding_id = 0;
+ static guint    _signal_retrieve_surrounding_id = 0;
+ 
+ #if GTK_CHECK_VERSION (3, 98, 4)
+-static gboolean _use_sync_mode = TRUE;
++static char _use_sync_mode = 2;
+ #else
+ static const gchar *_no_snooper_apps = NO_SNOOPER_APPS;
+ static gboolean _use_key_snooper = ENABLE_SNOOPER;
+ static guint    _key_snooper_id = 0;
+ 
+-static gboolean _use_sync_mode = FALSE;
++static char _use_sync_mode = 0;
+ #endif
+ 
+ static const gchar *_discard_password_apps  = "";
+@@ -375,12 +375,15 @@ ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
+    return FALSE;
+ }
+ 
+-struct _ProcessKeyEventData {
++typedef struct {
+     GdkEvent *event;
+     IBusIMContext *ibusimcontext;
+-};
++} ProcessKeyEventData;
+ 
+-typedef struct _ProcessKeyEventData ProcessKeyEventData;
++typedef struct {
++    GMainLoop *loop;
++    gboolean    retval;
++} ProcessKeyEventReplyData;
+ 
+ static void
+ _process_key_event_done (GObject      *object,
+@@ -395,12 +398,12 @@ _process_key_event_done (GObject      *object,
+     IBusIMContext *ibusimcontext = data->ibusimcontext;
+ #endif
+     GError *error = NULL;
++    gboolean retval;
+ 
+     g_slice_free (ProcessKeyEventData, data);
+-    gboolean retval = ibus_input_context_process_key_event_async_finish (
+-            context,
+-            res,
+-            &error);
++    retval = ibus_input_context_process_key_event_async_finish (context,
++                                                                res,
++                                                                &error);
+ 
+     if (error != NULL) {
+         g_warning ("Process Key Event failed: %s.", error->message);
+@@ -431,6 +434,27 @@ _process_key_event_done (GObject      *object,
+ #endif
+ }
+ 
++static void
++_process_key_event_reply_done (GObject      *object,
++                               GAsyncResult *res,
++                               gpointer      user_data)
++{
++    IBusInputContext *context = (IBusInputContext *)object;
++    ProcessKeyEventReplyData *data = (ProcessKeyEventReplyData *)user_data;
++    GError *error = NULL;
++    gboolean retval = ibus_input_context_process_key_event_async_finish (
++            context,
++            res,
++            &error);
++    if (error != NULL) {
++        g_warning ("Process Key Event failed: %s.", error->message);
++        g_error_free (error);
++    }
++    g_return_if_fail (data);
++    data->retval = retval;
++    g_main_loop_quit (data->loop);
++}
++
+ static gboolean
+ _process_key_event (IBusInputContext *context,
+ #if GTK_CHECK_VERSION (3, 98, 4)
+@@ -462,13 +486,45 @@ _process_key_event (IBusInputContext *context,
+ #endif
+     keycode = hardware_keycode;
+ 
+-    if (_use_sync_mode) {
++    switch (_use_sync_mode) {
++    case 1: {
+         retval = ibus_input_context_process_key_event (context,
++                                                       keyval,
++                                                       keycode - 8,
++                                                       state);
++        break;
++    }
++    case 2: {
++        GMainLoop *loop = g_main_loop_new (NULL, TRUE);
++        ProcessKeyEventReplyData *data = NULL;
++
++        if (loop)
++            data = g_slice_new0 (ProcessKeyEventReplyData);
++        if (!data) {
++            g_warning ("Cannot wait for the reply of the process key event.");
++            retval = ibus_input_context_process_key_event (context,
++                                                           keyval,
++                                                           keycode - 8,
++                                                           state);
++            if (loop)
++                g_main_loop_quit (loop);
++            break;
++        }
++        data->loop = loop;
++        ibus_input_context_process_key_event_async (context,
+             keyval,
+             keycode - 8,
+-            state);
++            state,
++            -1,
++            NULL,
++            _process_key_event_reply_done,
++            data);
++        g_main_loop_run (loop);
++        retval = data->retval;
++        g_slice_free (ProcessKeyEventReplyData, data);
++        break;
+     }
+-    else {
++    default: {
+         ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData);
+ #if GTK_CHECK_VERSION (3, 98, 4)
+         data->event = gdk_event_ref (event);
+@@ -487,6 +543,7 @@ _process_key_event (IBusInputContext *context,
+ 
+         retval = TRUE;
+     }
++    }
+ 
+     /* GTK4 does not provide gtk_key_snooper_install() and also
+      * GtkIMContextClass->filter_keypress() cannot send the updated
+@@ -676,24 +733,47 @@ _key_snooper_cb (GtkWidget   *widget,
+ #endif
+ 
+ static gboolean
+-_get_boolean_env(const gchar *name,
+-                 gboolean     defval)
++_get_boolean_env (const gchar *name,
++                  gboolean     defval)
+ {
+     const gchar *value = g_getenv (name);
+ 
+     if (value == NULL)
+-      return defval;
++        return defval;
+ 
+     if (g_strcmp0 (value, "") == 0 ||
+         g_strcmp0 (value, "0") == 0 ||
+         g_strcmp0 (value, "false") == 0 ||
+         g_strcmp0 (value, "False") == 0 ||
+-        g_strcmp0 (value, "FALSE") == 0)
+-      return FALSE;
++        g_strcmp0 (value, "FALSE") == 0) {
++        return FALSE;
++    }
+ 
+     return TRUE;
+ }
+ 
++static char
++_get_char_env (const gchar *name,
++               char         defval)
++{
++    const gchar *value = g_getenv (name);
++
++    if (value == NULL)
++        return defval;
++
++    if (g_strcmp0 (value, "") == 0 ||
++        g_strcmp0 (value, "0") == 0 ||
++        g_strcmp0 (value, "false") == 0 ||
++        g_strcmp0 (value, "False") == 0 ||
++        g_strcmp0 (value, "FALSE") == 0) {
++        return 0;
++    } else if (!g_strcmp0 (value, "2")) {
++        return 2;
++    }
++
++    return 1;
++}
++
+ static void
+ daemon_name_appeared (GDBusConnection *connection,
+                       const gchar     *name,
+@@ -777,11 +857,11 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+     g_assert (_signal_retrieve_surrounding_id != 0);
+ 
+ #if GTK_CHECK_VERSION (3, 98, 4)
+-    _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", TRUE);
++    _use_sync_mode = _get_char_env ("IBUS_ENABLE_SYNC_MODE", 2);
+ #else
+     _use_key_snooper = !_get_boolean_env ("IBUS_DISABLE_SNOOPER",
+                                           !(ENABLE_SNOOPER));
+-    _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", FALSE);
++    _use_sync_mode = (char)_get_char_env ("IBUS_ENABLE_SYNC_MODE", 0);
+ #endif
+     _use_discard_password = _get_boolean_env ("IBUS_DISCARD_PASSWORD", FALSE);
+ 
+@@ -904,6 +984,8 @@ ibus_im_context_init (GObject *obj)
+ #else
+     ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS;
+ #endif
++    if (_use_sync_mode != 1)
++        ibusimcontext->caps |= IBUS_CAP_SYNC_PROCESS_KEY;
+ 
+     ibusimcontext->events_queue = g_queue_new ();
+ 
+@@ -1246,7 +1328,7 @@ ibus_im_context_reset (GtkIMContext *context)
+          * IBus uses button-press-event instead until GTK is fixed.
+          * https://gitlab.gnome.org/GNOME/gtk/issues/1534
+          */
+-        if (_use_sync_mode)
++        if (_use_sync_mode == 1)
+             ibus_im_context_clear_preedit_text (ibusimcontext);
+         ibus_input_context_reset (ibusimcontext->ibuscontext);
+     }
+@@ -1361,7 +1443,7 @@ ibus_im_context_set_client_window (GtkIMContext *context,
+ 
+     if (ibusimcontext->client_window) {
+ #if !GTK_CHECK_VERSION (3, 98, 4)
+-        if (ibusimcontext->use_button_press_event && !_use_sync_mode)
++        if (ibusimcontext->use_button_press_event && _use_sync_mode != 1)
+             _connect_button_press_event (ibusimcontext, FALSE);
+ #endif
+         g_object_unref (ibusimcontext->client_window);
+@@ -1371,7 +1453,7 @@ ibus_im_context_set_client_window (GtkIMContext *context,
+     if (client != NULL) {
+         ibusimcontext->client_window = g_object_ref (client);
+ #if !GTK_CHECK_VERSION (3, 98, 4)
+-        if (!ibusimcontext->use_button_press_event && !_use_sync_mode)
++        if (!ibusimcontext->use_button_press_event && _use_sync_mode != 1)
+             _connect_button_press_event (ibusimcontext, TRUE);
+ #endif
+     }
+@@ -1993,7 +2075,7 @@ _ibus_context_update_preedit_text_cb (IBusInputContext  *ibuscontext,
+ #if !GTK_CHECK_VERSION (3, 98, 4)
+     if (!ibusimcontext->use_button_press_event &&
+         mode == IBUS_ENGINE_PREEDIT_COMMIT &&
+-        !_use_sync_mode) {
++        _use_sync_mode != 1) {
+         if (ibusimcontext->client_window) {
+             _connect_button_press_event (ibusimcontext, TRUE);
+         }
+@@ -2200,6 +2282,8 @@ _create_input_context_done (IBusBus       *bus,
+ static void
+ _create_input_context (IBusIMContext *ibusimcontext)
+ {
++    gchar *prgname = g_strdup (g_get_prgname());
++    gchar *client_name;
+     IDEBUG ("%s", __FUNCTION__);
+ 
+     g_assert (ibusimcontext->ibuscontext == NULL);
+@@ -2208,11 +2292,24 @@ _create_input_context (IBusIMContext *ibusimcontext)
+ 
+     ibusimcontext->cancellable = g_cancellable_new ();
+ 
++    if (!prgname)
++        prgname = g_strdup_printf ("(%d)", getpid ());
++    client_name = g_strdup_printf ("%s:%s",
++#if GTK_CHECK_VERSION (3, 98, 4)
++                                   "gtk4-im",
++#elif GTK_CHECK_VERSION (2, 91, 0)
++                                   "gtk3-im",
++#else
++                                   "gtk-im",
++#endif
++                                   prgname);
++    g_free (prgname);
+     ibus_bus_create_input_context_async (_bus,
+-            "gtk-im", -1,
++            client_name, -1,
+             ibusimcontext->cancellable,
+             (GAsyncReadyCallback)_create_input_context_done,
+             g_object_ref (ibusimcontext));
++    g_free (client_name);
+ }
+ 
+ /* Callback functions for slave context */
+@@ -2329,6 +2426,8 @@ _create_fake_input_context_done (IBusBus       *bus,
+                       NULL);
+ 
+     guint32 caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
++    if (_use_sync_mode != 1)
++        caps |= IBUS_CAP_SYNC_PROCESS_KEY;
+     ibus_input_context_set_capabilities (_fake_context, caps);
+ 
+     /* focus in/out the fake context */
+diff --git a/src/ibustypes.h b/src/ibustypes.h
+index 60bcb92b..a8eee319 100644
+--- a/src/ibustypes.h
++++ b/src/ibustypes.h
+@@ -109,6 +109,9 @@ typedef enum
+  * @IBUS_CAP_SURROUNDING_TEXT: Client can provide surround text,
+  *  or IME can handle surround text.
+  * @IBUS_CAP_OSK: UI is owned by on-screen keyboard.
++ * @IBUS_CAP_SYNC_PROCESS_KEY: Asynchronous process key events are not
++ *  supported and the ibus_engine_forward_key_event() should not be
++ *  used for the return value of #IBusEngine::process_key_event().
+  *
+  * Capability flags of UI.
+  */
+@@ -120,6 +123,7 @@ typedef enum {
+     IBUS_CAP_PROPERTY           = 1 << 4,
+     IBUS_CAP_SURROUNDING_TEXT   = 1 << 5,
+     IBUS_CAP_OSK                = 1 << 6,
++    IBUS_CAP_SYNC_PROCESS_KEY   = 1 << 7,
+ } IBusCapabilite;
+ 
+ /**
+-- 
+2.35.3
+
+From b14cab3753c6510a0a48eec673aa6eac89c793a1 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 7 Jul 2022 08:22:45 +0900
+Subject: [PATCH] src/ibusengine: Add focus_in_id()/focus_out_id() class
+ methods
+
+IBusEngine constructor now has a 'has-focus-id' property and if it's %TRUE,
+IBusEngine::focus_in_id()/focus_out_id() are called instaed of
+IBusEngine::focus_in()/focus_out() and the class method has an object_path
+argument for the unique input context ID and a client argument for
+the client application type likes ibus-gtk, ibus-gtk4, xim.
+---
+ bus/engineproxy.c       | 304 +++++++++++++++++++++++++++++-----------
+ bus/engineproxy.h       |  12 +-
+ bus/ibusimpl.c          |  13 ++
+ bus/ibusimpl.h          |  13 +-
+ bus/inputcontext.c      |  50 +++++--
+ src/ibusengine.c        | 217 ++++++++++++++++++++++++++--
+ src/ibusengine.h        |  10 +-
+ src/ibusmarshalers.list |   1 +
+ 8 files changed, 505 insertions(+), 115 deletions(-)
+
+diff --git a/bus/engineproxy.c b/bus/engineproxy.c
+index 2d98995c..fd1f34fb 100644
+--- a/bus/engineproxy.c
++++ b/bus/engineproxy.c
+@@ -2,7 +2,7 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2015-2022 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * Copyright (C) 2008-2016 Red Hat, Inc.
+  *
+  * This library is free software; you can redistribute it and/or
+@@ -59,6 +59,7 @@ struct _BusEngineProxy {
+ 
+     /* cached properties */
+     IBusPropList *prop_list;
++    gboolean has_focus_id;
+ };
+ 
+ struct _BusEngineProxyClass {
+@@ -105,30 +106,35 @@ static IBusText *text_empty = NULL;
+ static IBusPropList *prop_list_empty = NULL;
+ 
+ /* functions prototype */
+-static void     bus_engine_proxy_set_property   (BusEngineProxy      *engine,
+-                                                 guint                prop_id,
+-                                                 const GValue        *value,
+-                                                 GParamSpec          *pspec);
+-static void     bus_engine_proxy_get_property   (BusEngineProxy      *engine,
+-                                                 guint                prop_id,
+-                                                 GValue              *value,
+-                                                 GParamSpec          *pspec);
++static void     bus_engine_proxy_set_property   (BusEngineProxy    *engine,
++                                                 guint              prop_id,
++                                                 const GValue      *value,
++                                                 GParamSpec        *pspec);
++static void     bus_engine_proxy_get_property   (BusEngineProxy    *engine,
++                                                 guint              prop_id,
++                                                 GValue            *value,
++                                                 GParamSpec        *pspec);
+ static void     bus_engine_proxy_real_register_properties
+-                                                (BusEngineProxy      *engine,
+-                                                 IBusPropList        *prop_list);
++                                                (BusEngineProxy    *engine,
++                                                 IBusPropList      *prop_list);
+ static void     bus_engine_proxy_real_update_property
+-                                                (BusEngineProxy      *engine,
+-                                                 IBusProperty        *prop);
+-static void     bus_engine_proxy_real_destroy   (IBusProxy           *proxy);
+-static void     bus_engine_proxy_g_signal       (GDBusProxy          *proxy,
+-                                                 const gchar         *sender_name,
+-                                                 const gchar         *signal_name,
+-                                                 GVariant            *parameters);
++                                                (BusEngineProxy    *engine,
++                                                 IBusProperty      *prop);
++static void     bus_engine_proxy_real_destroy   (IBusProxy         *proxy);
++static void     bus_engine_proxy_g_signal       (GDBusProxy        *proxy,
++                                                 const gchar       *sender_name,
++                                                 const gchar       *signal_name,
++                                                 GVariant          *parameters);
+ static void     bus_engine_proxy_initable_iface_init
+-                                                (GInitableIface      *initable_iface);
++                                                (GInitableIface
++                                                               *initable_iface);
++static void     bus_engine_proxy_get_has_focus_id
++                                                (BusEngineProxy    *engine);
+ 
+ G_DEFINE_TYPE_WITH_CODE (BusEngineProxy, bus_engine_proxy, IBUS_TYPE_PROXY,
+-                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, bus_engine_proxy_initable_iface_init)
++                         G_IMPLEMENT_INTERFACE (
++                                 G_TYPE_INITABLE,
++                                 bus_engine_proxy_initable_iface_init)
+                         );
+ 
+ static GInitableIface *parent_initable_iface = NULL;
+@@ -138,8 +144,10 @@ bus_engine_proxy_class_init (BusEngineProxyClass *class)
+ {
+     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ 
+-    gobject_class->set_property = (GObjectSetPropertyFunc)bus_engine_proxy_set_property;
+-    gobject_class->get_property = (GObjectGetPropertyFunc)bus_engine_proxy_get_property;
++    gobject_class->set_property =
++            (GObjectSetPropertyFunc)bus_engine_proxy_set_property;
++    gobject_class->get_property =
++            (GObjectGetPropertyFunc)bus_engine_proxy_get_property;
+ 
+     class->register_properties = bus_engine_proxy_real_register_properties;
+     class->update_property = bus_engine_proxy_real_update_property;
+@@ -147,8 +155,9 @@ bus_engine_proxy_class_init (BusEngineProxyClass *class)
+     IBUS_PROXY_CLASS (class)->destroy = bus_engine_proxy_real_destroy;
+     G_DBUS_PROXY_CLASS (class)->g_signal = bus_engine_proxy_g_signal;
+ 
+-    parent_initable_iface =
+-            (GInitableIface *)g_type_interface_peek (bus_engine_proxy_parent_class, G_TYPE_INITABLE);
++    parent_initable_iface = (GInitableIface *)g_type_interface_peek (
++            bus_engine_proxy_parent_class,
++            G_TYPE_INITABLE);
+ 
+     /* install properties */
+     g_object_class_install_property (gobject_class,
+@@ -164,7 +173,9 @@ bus_engine_proxy_class_init (BusEngineProxyClass *class)
+                         G_PARAM_STATIC_NICK
+                         ));
+ 
+-    /* install glib signals that will be sent when corresponding D-Bus signals are sent from an engine process. */
++    /* install glib signals that will be sent when corresponding D-Bus signals
++     * are sent from an engine process.
++     */
+     engine_signals[COMMIT_TEXT] =
+         g_signal_new (I_("commit-text"),
+             G_TYPE_FROM_CLASS (class),
+@@ -473,7 +484,8 @@ bus_engine_proxy_real_destroy (IBusProxy *proxy)
+         engine->prop_list = NULL;
+     }
+ 
+-    IBUS_PROXY_CLASS (bus_engine_proxy_parent_class)->destroy ((IBusProxy *)engine);
++    IBUS_PROXY_CLASS (bus_engine_proxy_parent_class)->destroy (
++            (IBusProxy *)engine);
+ }
+ 
+ static void
+@@ -486,7 +498,8 @@ _g_object_unref_if_floating (gpointer instance)
+ /**
+  * bus_engine_proxy_g_signal:
+  *
+- * Handle all D-Bus signals from the engine process. This function emits corresponding glib signal for the D-Bus signal.
++ * Handle all D-Bus signals from the engine process. This function emits
++ * corresponding glib signal for the D-Bus signal.
+  */
+ static void
+ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+@@ -522,7 +535,9 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+         }
+     }
+ 
+-    /* Handle D-Bus signals with parameters. Deserialize them and emit a glib signal. */
++    /* Handle D-Bus signals with parameters. Deserialize them and emit a glib
++     * signal.
++     */
+     if (g_strcmp0 (signal_name, "CommitText") == 0) {
+         GVariant *arg0 = NULL;
+         g_variant_get (parameters, "(v)", &arg0);
+@@ -568,7 +583,8 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+         gboolean visible = FALSE;
+         guint mode = 0;
+ 
+-        g_variant_get (parameters, "(vubu)", &arg0, &cursor_pos, &visible, &mode);
++        g_variant_get (parameters, "(vubu)",
++                       &arg0, &cursor_pos, &visible, &mode);
+         g_return_if_fail (arg0 != NULL);
+ 
+         IBusText *text = IBUS_TEXT (ibus_serializable_deserialize (arg0));
+@@ -594,7 +610,11 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+         g_variant_unref (arg0);
+         g_return_if_fail (text != NULL);
+ 
+-        g_signal_emit (engine, engine_signals[UPDATE_AUXILIARY_TEXT], 0, text, visible);
++        g_signal_emit (engine,
++                       engine_signals[UPDATE_AUXILIARY_TEXT],
++                       0,
++                       text,
++                       visible);
+         _g_object_unref_if_floating (text);
+         return;
+     }
+@@ -606,11 +626,16 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+         g_variant_get (parameters, "(vb)", &arg0, &visible);
+         g_return_if_fail (arg0 != NULL);
+ 
+-        IBusLookupTable *table = IBUS_LOOKUP_TABLE (ibus_serializable_deserialize (arg0));
++        IBusLookupTable *table =
++                IBUS_LOOKUP_TABLE (ibus_serializable_deserialize (arg0));
+         g_variant_unref (arg0);
+         g_return_if_fail (table != NULL);
+ 
+-        g_signal_emit (engine, engine_signals[UPDATE_LOOKUP_TABLE], 0, table, visible);
++        g_signal_emit (engine,
++                       engine_signals[UPDATE_LOOKUP_TABLE],
++                       0,
++                       table,
++                       visible);
+         _g_object_unref_if_floating (table);
+         return;
+     }
+@@ -620,11 +645,15 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+         g_variant_get (parameters, "(v)", &arg0);
+         g_return_if_fail (arg0 != NULL);
+ 
+-        IBusPropList *prop_list = IBUS_PROP_LIST (ibus_serializable_deserialize (arg0));
++        IBusPropList *prop_list =
++                IBUS_PROP_LIST (ibus_serializable_deserialize (arg0));
+         g_variant_unref (arg0);
+         g_return_if_fail (prop_list != NULL);
+ 
+-        g_signal_emit (engine, engine_signals[REGISTER_PROPERTIES], 0, prop_list);
++        g_signal_emit (engine,
++                       engine_signals[REGISTER_PROPERTIES],
++                       0,
++                       prop_list);
+         _g_object_unref_if_floating (prop_list);
+         return;
+     }
+@@ -634,7 +663,8 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
+         g_variant_get (parameters, "(v)", &arg0);
+         g_return_if_fail (arg0 != NULL);
+ 
+-        IBusProperty *prop = IBUS_PROPERTY (ibus_serializable_deserialize (arg0));
++        IBusProperty *prop =
++                IBUS_PROPERTY (ibus_serializable_deserialize (arg0));
+         g_variant_unref (arg0);
+         g_return_if_fail (prop != NULL);
+ 
+@@ -665,26 +695,45 @@ bus_engine_proxy_new_internal (const gchar     *path,
+                                IBusEngineDesc  *desc,
+                                GDBusConnection *connection)
+ {
++    GDBusProxyFlags flags;
++    BusEngineProxy *engine;
++    BusIBusImpl *ibus = BUS_DEFAULT_IBUS;
++    GHashTable *hash_table = NULL;
++    EngineFocusCategory category = ENGINE_FOCUS_CATEGORY_NONE;
++
+     g_assert (path);
+     g_assert (IBUS_IS_ENGINE_DESC (desc));
+     g_assert (G_IS_DBUS_CONNECTION (connection));
+ 
+-    GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
+-    BusEngineProxy *engine =
+-        (BusEngineProxy *) g_initable_new (BUS_TYPE_ENGINE_PROXY,
+-                                           NULL,
+-                                           NULL,
+-                                           "desc",              desc,
+-                                           "g-connection",      connection,
+-                                           "g-interface-name",  IBUS_INTERFACE_ENGINE,
+-                                           "g-object-path",     path,
+-                                           "g-default-timeout", g_gdbus_timeout,
+-                                           "g-flags",           flags,
+-                                           NULL);
++    flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
++    engine = (BusEngineProxy *) g_initable_new (
++            BUS_TYPE_ENGINE_PROXY,
++            NULL,
++            NULL,
++            "desc",              desc,
++            "g-connection",      connection,
++            "g-interface-name",  IBUS_INTERFACE_ENGINE,
++            "g-object-path",     path,
++            "g-default-timeout", g_gdbus_timeout,
++            "g-flags",           flags,
++            NULL);
+     const gchar *layout = ibus_engine_desc_get_layout (desc);
+     if (layout != NULL && layout[0] != '\0') {
+         engine->keymap = ibus_keymap_get (layout);
+     }
++    if (ibus)
++        hash_table = bus_ibus_impl_get_engine_focus_id_table (ibus);
++    if (hash_table) {
++        category = (EngineFocusCategory)GPOINTER_TO_INT (
++                g_hash_table_lookup (hash_table,
++                                     ibus_engine_desc_get_name (desc)));
++        if (category == ENGINE_FOCUS_CATEGORY_HAS_ID)
++            engine->has_focus_id = TRUE;
++        else if (category == ENGINE_FOCUS_CATEGORY_NO_ID)
++            engine->has_focus_id = FALSE;
++        else
++            bus_engine_proxy_get_has_focus_id (engine);
++    }
+     return engine;
+ }
+ 
+@@ -740,7 +789,8 @@ engine_proxy_new_data_free (EngineProxyNewData *data)
+ /**
+  * create_engine_ready_cb:
+  *
+- * A callback function to be called when bus_factory_proxy_create_engine finishes.
++ * A callback function to be called when bus_factory_proxy_create_engine
++ * finishes.
+  * Create an BusEngineProxy object and call the GAsyncReadyCallback.
+  */
+ static void
+@@ -775,8 +825,10 @@ create_engine_ready_cb (BusFactoryProxy    *factory,
+ /**
+  * notify_factory_cb:
+  *
+- * A callback function to be called when bus_component_start() emits "notify::factory" signal within 5 seconds.
+- * Call bus_factory_proxy_create_engine to create the engine proxy asynchronously.
++ * A callback function to be called when bus_component_start() emits
++ * "notify::factory" signal within 5 seconds.
++ * Call bus_factory_proxy_create_engine to create the engine proxy
++ * asynchronously.
+  */
+ static void
+ notify_factory_cb (BusComponent       *component,
+@@ -798,22 +850,25 @@ notify_factory_cb (BusComponent       *component,
+             data->handler_id = 0;
+         }
+ 
+-        /* We *have to* disconnect the cancelled_cb here, since g_dbus_proxy_call
+-         * calls create_engine_ready_cb even if the proxy call is cancelled, and
+-         * in this case, create_engine_ready_cb itself will return error using
+-         * g_task_return_error(). */
++        /* We *have to* disconnect the cancelled_cb here, since
++         * g_dbus_proxy_call calls create_engine_ready_cb even if the proxy
++         * call is cancelled, and in this case, create_engine_ready_cb itself
++         * will return error using g_task_return_error().
++         */
+         if (data->cancellable && data->cancelled_handler_id != 0) {
+-            g_cancellable_disconnect (data->cancellable, data->cancelled_handler_id);
++            g_cancellable_disconnect (data->cancellable, 
++                                      data->cancelled_handler_id);
+             data->cancelled_handler_id = 0;
+         }
+ 
+         /* Create engine from factory. */
+-        bus_factory_proxy_create_engine (data->factory,
+-                                         data->desc,
+-                                         data->timeout,
+-                                         data->cancellable,
+-                                         (GAsyncReadyCallback) create_engine_ready_cb,
+-                                         data);
++        bus_factory_proxy_create_engine (
++                data->factory,
++                data->desc,
++                data->timeout,
++                data->cancellable,
++                (GAsyncReadyCallback) create_engine_ready_cb,
++                data);
+     }
+     /* If factory is NULL, we will continue wait for
+      * factory notify signal or timeout */
+@@ -822,7 +877,8 @@ notify_factory_cb (BusComponent       *component,
+ /**
+  * timeout_cb:
+  *
+- * A callback function to be called when bus_component_start() does not emit "notify::factory" signal within 5 seconds.
++ * A callback function to be called when bus_component_start() does not emit
++ * "notify::factory" signal within 5 seconds.
+  * Call the GAsyncReadyCallback and stop the 5 sec timer.
+  */
+ static gboolean
+@@ -927,16 +983,18 @@ bus_engine_proxy_new (IBusEngineDesc      *desc,
+         /* The factory is ready. We'll create the engine proxy directly. */
+         g_object_ref (data->factory);
+ 
+-        /* We don't have to connect to cancelled_cb here, since g_dbus_proxy_call
+-         * calls create_engine_ready_cb even if the proxy call is cancelled, and
+-         * in this case, create_engine_ready_cb itself can return error using
+-         * g_task_return_error(). */
+-        bus_factory_proxy_create_engine (data->factory,
+-                                         data->desc,
+-                                         timeout,
+-                                         cancellable,
+-                                         (GAsyncReadyCallback) create_engine_ready_cb,
+-                                         data);
++        /* We don't have to connect to cancelled_cb here, since
++         * g_dbus_proxy_call calls create_engine_ready_cb even if the proxy
++         * call is cancelled, and in this case, create_engine_ready_cb itself
++         * can return error using g_task_return_error().
++         */
++        bus_factory_proxy_create_engine (
++                data->factory,
++                data->desc,
++                timeout,
++                cancellable,
++                (GAsyncReadyCallback) create_engine_ready_cb,
++                data);
+     }
+ }
+ 
+@@ -978,8 +1036,11 @@ bus_engine_proxy_process_key_event (BusEngineProxy      *engine,
+ {
+     g_assert (BUS_IS_ENGINE_PROXY (engine));
+ 
+-    if (keycode != 0 && bus_ibus_impl_is_use_sys_layout (BUS_DEFAULT_IBUS) == FALSE) {
+-        /* Since use_sys_layout is false, we don't rely on XKB. Try to convert keyval from keycode by using our own mapping. */
++    if (keycode != 0 &&
++        bus_ibus_impl_is_use_sys_layout (BUS_DEFAULT_IBUS) == FALSE) {
++        /* Since use_sys_layout is false, we don't rely on XKB. Try to convert
++         * keyval from keycode by using our own mapping.
++         */
+         IBusKeymap *keymap = engine->keymap;
+         if (keymap == NULL)
+             keymap = BUS_DEFAULT_KEYMAP;
+@@ -1142,7 +1203,8 @@ void bus_engine_proxy_set_surrounding_text (BusEngineProxy *engine,
+         g_strcmp0 (text->text, engine->surrounding_text->text) != 0 ||
+         cursor_pos != engine->surrounding_cursor_pos ||
+         anchor_pos != engine->selection_anchor_pos) {
+-        GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
++        GVariant *variant =
++                ibus_serializable_serialize ((IBusSerializable *)text);
+         if (engine->surrounding_text)
+             g_object_unref (engine->surrounding_text);
+         engine->surrounding_text = (IBusText *) g_object_ref_sink (text);
+@@ -1201,6 +1263,61 @@ bus_engine_proxy_set_content_type (BusEngineProxy *engine,
+     g_variant_unref (content_type);
+ }
+ 
++static void
++_get_has_focus_id_cb (GObject        *object,
++                      GAsyncResult   *res,
++                      gpointer        user_data)
++{
++    GHashTable *hash_table = (GHashTable*)user_data;
++    BusEngineProxy *engine;
++    GError *error = NULL;
++    GVariant *result;
++
++    g_return_if_fail (BUS_IS_ENGINE_PROXY (object));
++    engine = BUS_ENGINE_PROXY (object);
++    result = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
++
++    if (result != NULL) {
++        GVariant *variant = NULL;
++        gpointer value;
++        g_variant_get (result, "(v)", &variant);
++        engine->has_focus_id = g_variant_get_boolean (variant);
++        g_variant_unref (variant);
++        g_variant_unref (result);
++        value =  GINT_TO_POINTER (engine->has_focus_id
++                                  ? ENGINE_FOCUS_CATEGORY_HAS_ID
++                                  : ENGINE_FOCUS_CATEGORY_NO_ID);
++        g_hash_table_replace (
++            hash_table,
++            (gpointer)ibus_engine_desc_get_name (engine->desc),
++            value);
++    }
++    g_hash_table_unref (hash_table);
++}
++
++static void
++bus_engine_proxy_get_has_focus_id (BusEngineProxy *engine)
++{
++    BusIBusImpl *ibus = BUS_DEFAULT_IBUS;
++    GHashTable *hash_table;
++
++    g_assert (BUS_IS_ENGINE_PROXY (engine));
++    g_assert (ibus);
++
++    hash_table = bus_ibus_impl_get_engine_focus_id_table (ibus);
++    g_assert (hash_table);
++    g_dbus_proxy_call ((GDBusProxy *) engine,
++                       "org.freedesktop.DBus.Properties.Get",
++                       g_variant_new ("(ss)",
++                                      IBUS_INTERFACE_ENGINE,
++                                      "FocusId"),
++                       G_DBUS_CALL_FLAGS_NONE,
++                       -1,
++                       NULL,
++                       _get_has_focus_id_cb,
++                       g_hash_table_ref (hash_table));
++}
++
+ /* a macro to generate a function to call a nullary D-Bus method. */
+ #define DEFINE_FUNCTION(Name, name)                         \
+     void                                                    \
+@@ -1223,11 +1340,24 @@ DEFINE_FUNCTION (CursorDown, cursor_down)
+ #undef DEFINE_FUNCTION
+ 
+ void
+-bus_engine_proxy_focus_in (BusEngineProxy *engine)
++bus_engine_proxy_focus_in (BusEngineProxy *engine,
++                           const gchar    *object_path,
++                           const gchar    *client)
+ {
+     g_assert (BUS_IS_ENGINE_PROXY (engine));
+-    if (!engine->has_focus) {
+-        engine->has_focus = TRUE;
++    if (engine->has_focus)
++        return;
++    engine->has_focus = TRUE;
++    if (engine->has_focus_id) {
++        g_dbus_proxy_call ((GDBusProxy *)engine,
++                           "FocusInId",
++                           g_variant_new ("(ss)", object_path, client),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           -1,
++                           NULL,
++                           NULL,
++                           NULL);
++    } else {
+         g_dbus_proxy_call ((GDBusProxy *)engine,
+                            "FocusIn",
+                            NULL,
+@@ -1240,11 +1370,23 @@ bus_engine_proxy_focus_in (BusEngineProxy *engine)
+ }
+ 
+ void
+-bus_engine_proxy_focus_out (BusEngineProxy *engine)
++bus_engine_proxy_focus_out (BusEngineProxy *engine,
++                            const gchar    *object_path)
+ {
+     g_assert (BUS_IS_ENGINE_PROXY (engine));
+-    if (engine->has_focus) {
+-        engine->has_focus = FALSE;
++    if (!engine->has_focus)
++        return;
++    engine->has_focus = FALSE;
++    if (engine->has_focus_id) {
++        g_dbus_proxy_call ((GDBusProxy *)engine,
++                           "FocusOutId",
++                           g_variant_new ("(s)", object_path),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           -1,
++                           NULL,
++                           NULL,
++                           NULL);
++    } else {
+         g_dbus_proxy_call ((GDBusProxy *)engine,
+                            "FocusOut",
+                            NULL,
+diff --git a/bus/engineproxy.h b/bus/engineproxy.h
+index a3006b47..957b7851 100644
+--- a/bus/engineproxy.h
++++ b/bus/engineproxy.h
+@@ -2,7 +2,7 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara@gmail.com>
++ * Copyright (C) 2018-2022 Takao Fujiwara <takao.fujiwara@gmail.com>
+  * Copyright (C) 2008-2018 Red Hat, Inc.
+  *
+  * This library is free software; you can redistribute it and/or
+@@ -126,20 +126,26 @@ void            bus_engine_proxy_set_cursor_location
+ /**
+  * bus_engine_proxy_focus_in:
+  * @engine: A #BusEngineProxy.
++ * @object_path: An object path.
++ * @client: A client name.
+  *
+  * Call "FocusIn" method of an engine asynchronously. Do nothing if
+  * the engine already has a focus.
+  */
+-void            bus_engine_proxy_focus_in    (BusEngineProxy     *engine);
++void            bus_engine_proxy_focus_in    (BusEngineProxy     *engine,
++                                              const gchar        *object_path,
++                                              const gchar        *client);
+ 
+ /**
+  * bus_engine_proxy_focus_out:
+  * @engine: A #BusEngineProxy.
++ * @object_path: An object path.
+  *
+  * Call "FocusOut" method of an engine asynchronously. Do nothing if
+  * the engine does not have a focus.
+  */
+-void            bus_engine_proxy_focus_out   (BusEngineProxy     *engine);
++void            bus_engine_proxy_focus_out   (BusEngineProxy     *engine,
++                                              const gchar        *object_path);
+ 
+ /**
+  * bus_engine_proxy_reset:
+diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
+index 49a138fe..8a443545 100644
+--- a/bus/ibusimpl.c
++++ b/bus/ibusimpl.c
+@@ -72,6 +72,8 @@ struct _BusIBusImpl {
+      * IBusEngineDesc object. */
+     GHashTable *engine_table;
+ 
++    GHashTable *engine_focus_id_table;
++
+     BusInputContext *focused_context;
+     BusPanelProxy   *panel;
+     BusPanelProxy   *emoji_extension;
+@@ -596,6 +598,7 @@ bus_ibus_impl_init (BusIBusImpl *ibus)
+     ibus->use_global_engine = TRUE;
+     ibus->global_engine_name = NULL;
+     ibus->global_previous_engine_name = NULL;
++    ibus->engine_focus_id_table = g_hash_table_new (g_str_hash, g_str_equal);
+ 
+     /* focus the fake_context, if use_global_engine is enabled. */
+     if (ibus->use_global_engine)
+@@ -2384,3 +2387,13 @@ bus_ibus_impl_get_focused_input_context (BusIBusImpl *ibus)
+ 
+     return ibus->focused_context;
+ }
++
++GHashTable *
++bus_ibus_impl_get_engine_focus_id_table (BusIBusImpl *ibus)
++{
++
++    g_assert (BUS_IS_IBUS_IMPL (ibus));
++
++    return ibus->engine_focus_id_table;
++}
++
+diff --git a/bus/ibusimpl.h b/bus/ibusimpl.h
+index 0bb18daf..cbe6856d 100644
+--- a/bus/ibusimpl.h
++++ b/bus/ibusimpl.h
+@@ -2,7 +2,8 @@
+ /* vim:set et sts=4: */
+ /* bus - The Input Bus
+  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2008-2013 Red Hat, Inc.
++ * Copyright (C) 2022 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2008-2022 Red Hat, Inc.
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+@@ -58,6 +59,13 @@ G_BEGIN_DECLS
+ typedef struct _BusIBusImpl BusIBusImpl;
+ typedef struct _BusIBusImplClass BusIBusImplClass;
+ 
++typedef enum
++{
++    ENGINE_FOCUS_CATEGORY_NONE = 0,
++    ENGINE_FOCUS_CATEGORY_NO_ID,
++    ENGINE_FOCUS_CATEGORY_HAS_ID
++} EngineFocusCategory;
++
+ GType            bus_ibus_impl_get_type             (void);
+ 
+ /**
+@@ -81,6 +89,7 @@ gboolean         bus_ibus_impl_is_embed_preedit_text
+                                                     (BusIBusImpl        *ibus);
+ BusInputContext *bus_ibus_impl_get_focused_input_context
+                                                     (BusIBusImpl        *ibus);
+-
++GHashTable      *bus_ibus_impl_get_engine_focus_id_table
++                                                    (BusIBusImpl        *ibus);
+ G_END_DECLS
+ #endif
+diff --git a/bus/inputcontext.c b/bus/inputcontext.c
+index 8d84fd05..72041ecb 100644
+--- a/bus/inputcontext.c
++++ b/bus/inputcontext.c
+@@ -846,7 +846,8 @@ _ic_process_key_event_reply_cb (GObject               *source,
+ /**
+  * _ic_process_key_event:
+  *
+- * Implement the "ProcessKeyEvent" method call of the org.freedesktop.IBus.InputContext interface.
++ * Implement the "ProcessKeyEvent" method call of the
++ * org.freedesktop.IBus.InputContext interface.
+  */
+ static void
+ _ic_process_key_event  (BusInputContext       *context,
+@@ -860,11 +861,13 @@ _ic_process_key_event  (BusInputContext       *context,
+     g_variant_get (parameters, "(uuu)", &keyval, &keycode, &modifiers);
+     if (G_UNLIKELY (!context->has_focus)) {
+         /* workaround: set focus if context does not have focus */
+-        BusInputContext *focused_context = bus_ibus_impl_get_focused_input_context (BUS_DEFAULT_IBUS);
++        BusInputContext *focused_context =
++                bus_ibus_impl_get_focused_input_context (BUS_DEFAULT_IBUS);
+         if (focused_context == NULL ||
+             focused_context->fake == TRUE ||
+             context->fake == FALSE) {
+-            /* grab focus, if context is a real IC or current focused IC is fake */
++            /* grab focus, if context is a real IC or current focused IC is
++             * fake */
+             bus_input_context_focus_in (context);
+         }
+     }
+@@ -914,7 +917,8 @@ _ic_process_key_event  (BusInputContext       *context,
+                                             data);
+     }
+     else {
+-        g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE));
++        g_dbus_method_invocation_return_value (invocation,
++                                               g_variant_new ("(b)", FALSE));
+     }
+ }
+ 
+@@ -1438,7 +1442,9 @@ bus_input_context_focus_in (BusInputContext *context)
+     context->prev_modifiers = 0;
+ 
+     if (context->engine) {
+-        bus_engine_proxy_focus_in (context->engine);
++        const gchar *path =
++                ibus_service_get_object_path ((IBusService *)context);
++        bus_engine_proxy_focus_in (context->engine, path, context->client);
+         bus_engine_proxy_enable (context->engine);
+         bus_engine_proxy_set_capabilities (context->engine, context->capabilities);
+         bus_engine_proxy_set_cursor_location (context->engine, context->x, context->y, context->w, context->h);
+@@ -1538,7 +1544,9 @@ bus_input_context_focus_out (BusInputContext *context)
+     bus_input_context_register_properties (context, props_empty);
+ 
+     if (context->engine) {
+-        bus_engine_proxy_focus_out (context->engine);
++        const gchar *path =
++            ibus_service_get_object_path ((IBusService *)context);
++        bus_engine_proxy_focus_out (context->engine, path);
+     }
+ 
+     context->has_focus = FALSE;
+@@ -2376,11 +2384,19 @@ bus_input_context_enable (BusInputContext *context)
+     if (context->engine == NULL)
+         return;
+ 
+-    bus_engine_proxy_focus_in (context->engine);
+-    bus_engine_proxy_enable (context->engine);
+-    bus_engine_proxy_set_capabilities (context->engine, context->capabilities);
+-    bus_engine_proxy_set_cursor_location (context->engine, context->x, context->y, context->w, context->h);
+-    bus_engine_proxy_set_content_type (context->engine, context->purpose, context->hints);
++    {
++        const gchar *path =
++                ibus_service_get_object_path ((IBusService *)context);
++        bus_engine_proxy_focus_in (context->engine, path, context->client);
++        bus_engine_proxy_enable (context->engine);
++        bus_engine_proxy_set_capabilities (context->engine,
++                                           context->capabilities);
++        bus_engine_proxy_set_cursor_location (context->engine,
++                                              context->x, context->y,
++                                              context->w, context->h);
++        bus_engine_proxy_set_content_type (context->engine,
++                                           context->purpose, context->hints);
++    }
+ }
+ 
+ void
+@@ -2397,7 +2413,9 @@ bus_input_context_disable (BusInputContext *context)
+     bus_input_context_register_properties (context, props_empty);
+ 
+     if (context->engine) {
+-        bus_engine_proxy_focus_out (context->engine);
++        const gchar *path =
++            ibus_service_get_object_path ((IBusService *)context);
++        bus_engine_proxy_focus_out (context->engine, path);
+         bus_engine_proxy_disable (context->engine);
+     }
+ }
+@@ -2445,6 +2463,8 @@ bus_input_context_unset_engine (BusInputContext *context)
+ 
+     if (context->engine) {
+         gint i;
++        const gchar *path =
++            ibus_service_get_object_path ((IBusService *)context);
+         /* uninstall signal handlers for the engine. */
+         for (i = 0; i < G_N_ELEMENTS(engine_signals); i++) {
+             g_signal_handlers_disconnect_by_func (context->engine,
+@@ -2453,7 +2473,7 @@ bus_input_context_unset_engine (BusInputContext *context)
+         /* focus out engine so that the next call of
+            bus_engine_proxy_focus_in() will take effect and trigger
+            RegisterProperties. */
+-        bus_engine_proxy_focus_out (context->engine);
++        bus_engine_proxy_focus_out (context->engine, path);
+         g_object_unref (context->engine);
+         context->engine = NULL;
+     }
+@@ -2488,7 +2508,9 @@ bus_input_context_set_engine (BusInputContext *context,
+                               context);
+         }
+         if (context->has_focus) {
+-            bus_engine_proxy_focus_in (context->engine);
++            const gchar *path =
++                    ibus_service_get_object_path ((IBusService *)context);
++            bus_engine_proxy_focus_in (context->engine, path, context->client);
+             bus_engine_proxy_enable (context->engine);
+             bus_engine_proxy_set_capabilities (context->engine, context->capabilities);
+             bus_engine_proxy_set_cursor_location (context->engine, context->x, context->y, context->w, context->h);
+diff --git a/src/ibusengine.c b/src/ibusengine.c
+index 7e844838..7c797103 100644
+--- a/src/ibusengine.c
++++ b/src/ibusengine.c
+@@ -2,7 +2,7 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2018-2021 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2018-2022 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * Copyright (C) 2008-2021 Red Hat, Inc.
+  *
+  * This library is free software; you can redistribute it and/or
+@@ -37,7 +37,9 @@
+ enum {
+     PROCESS_KEY_EVENT,
+     FOCUS_IN,
++    FOCUS_IN_ID,
+     FOCUS_OUT,
++    FOCUS_OUT_ID,
+     RESET,
+     ENABLE,
+     DISABLE,
+@@ -61,6 +63,7 @@ enum {
+ enum {
+     PROP_0,
+     PROP_ENGINE_NAME,
++    PROP_HAS_FOCUS_ID,
+ };
+ 
+ 
+@@ -82,6 +85,7 @@ struct _IBusEnginePrivate {
+     GHashTable            *extension_keybindings;
+     gboolean               enable_extension;
+     gchar                 *current_extension_name;
++    gboolean               has_focus_id;
+ };
+ 
+ 
+@@ -104,7 +108,8 @@ static void      ibus_engine_service_method_call
+                                                GDBusConnection    *connection,
+                                                const gchar        *sender,
+                                                const gchar        *object_path,
+-                                               const gchar        *interface_name,
++                                               const gchar
++                                                                *interface_name,
+                                                const gchar        *method_name,
+                                                GVariant           *parameters,
+                                                GDBusMethodInvocation
+@@ -132,7 +137,12 @@ static gboolean  ibus_engine_process_key_event
+                                               guint               keycode,
+                                               guint               state);
+ static void      ibus_engine_focus_in        (IBusEngine         *engine);
++static void      ibus_engine_focus_in_id     (IBusEngine         *engine,
++                                              const gchar        *object_path,
++                                              const gchar        *client);
+ static void      ibus_engine_focus_out       (IBusEngine         *engine);
++static void      ibus_engine_focus_out_id    (IBusEngine         *engine,
++                                              const gchar        *object_path);
+ static void      ibus_engine_reset           (IBusEngine         *engine);
+ static void      ibus_engine_enable          (IBusEngine         *engine);
+ static void      ibus_engine_disable         (IBusEngine         *engine);
+@@ -170,7 +180,8 @@ static void      ibus_engine_set_surrounding_text
+ static void      ibus_engine_process_hand_writing_event
+                                              (IBusEngine         *engine,
+                                               const gdouble      *coordinates,
+-                                              guint               coordinates_len);
++                                              guint
++                                                               coordinates_len);
+ static void      ibus_engine_cancel_hand_writing
+                                              (IBusEngine         *engine,
+                                               guint               n_strokes);
+@@ -230,7 +241,15 @@ static const gchar introspection_xml[] =
+     "      <arg direction='in'  type='u' name='state' />"
+     "    </method>"
+     "    <method name='FocusIn' />"
++    "    <method name='FocusInId'>"
++    "      <arg direction='in'  type='s' name='object_path' />"
++    "      <arg direction='in'  type='s' name='client' />"
++    "    </method>"
++    "    <method name='FocusIn' />"
+     "    <method name='FocusOut' />"
++    "    <method name='FocusOutId'>"
++    "      <arg direction='in'  type='s' name='object_path' />"
++    "    </method>"
+     "    <method name='Reset' />"
+     "    <method name='Enable' />"
+     "    <method name='Disable' />"
+@@ -283,6 +302,7 @@ static const gchar introspection_xml[] =
+     "    </signal>"
+     /* FIXME properties */
+     "    <property name='ContentType' type='(uu)' access='write' />"
++    "    <property name='FocusId' type='(b)' access='read' />"
+     "  </interface>"
+     "</node>";
+ 
+@@ -324,7 +344,9 @@ ibus_engine_class_init (IBusEngineClass *class)
+ 
+     class->process_key_event = ibus_engine_process_key_event;
+     class->focus_in     = ibus_engine_focus_in;
++    class->focus_in_id  = ibus_engine_focus_in_id;
+     class->focus_out    = ibus_engine_focus_out;
++    class->focus_out_id = ibus_engine_focus_out_id;
+     class->reset        = ibus_engine_reset;
+     class->enable       = ibus_engine_enable;
+     class->disable      = ibus_engine_disable;
+@@ -360,6 +382,15 @@ ibus_engine_class_init (IBusEngineClass *class)
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS));
+ 
++    g_object_class_install_property (gobject_class,
++                    PROP_HAS_FOCUS_ID,
++                    g_param_spec_boolean ("has-focus-id",
++                        "has focus id",
++                        "Has focus ID",
++                        FALSE,
++                        G_PARAM_READWRITE |
++                        G_PARAM_CONSTRUCT_ONLY));
++
+     /* install signals */
+     /**
+      * IBusEngine::process-key-event:
+@@ -378,7 +409,8 @@ ibus_engine_class_init (IBusEngineClass *class)
+      * Returns: %TRUE for successfully process the key; %FALSE otherwise.
+      * See also:  ibus_input_context_process_key_event().
+      *
+-     * <note><para>Argument @user_data is ignored in this function.</para></note>
++     * <note><para>Argument @user_data is ignored in this function.</para>
++     * </note>
+      */
+     engine_signals[PROCESS_KEY_EVENT] =
+         g_signal_new (I_("process-key-event"),
+@@ -402,7 +434,8 @@ ibus_engine_class_init (IBusEngineClass *class)
+      * in extended class to receive this signal.
+      *
+      * See also: ibus_input_context_focus_in()
+-     * <note><para>Argument @user_data is ignored in this function.</para></note>
++     * <note><para>Argument @user_data is ignored in this function.</para>
++     * </note>
+      */
+     engine_signals[FOCUS_IN] =
+         g_signal_new (I_("focus-in"),
+@@ -414,6 +447,58 @@ ibus_engine_class_init (IBusEngineClass *class)
+             G_TYPE_NONE,
+             0);
+ 
++    /**
++     * IBusEngine::focus-in-id:
++     * @engine: An IBusEngine.
++     * @object_path: An object path.
++     * @client: An client name.
++     *
++     * Emitted when the client application get the focus.
++     * Implement the member function IBusEngineClass::focus_in
++     * in extended class to receive this signal.
++     * @object_path is a unique id by input context.
++     * @client indicates a client type:
++     * 'fake':    focus is on desktop background or other programs where no
++     *            input is possible
++     * 'xim':     old X11 programs like xterm, emacs, ...
++     *            GTK3 programs in a Gnome Xorg session when GTK_IM_MODULE
++     *            is unset also use xim
++     * 'gtk-im:&lt;client-name&gt;':  Gtk2 input module is used
++     * 'gtk3-im:&lt;client-name&gt;': Gtk3 input module is used
++     * 'gtk4-im:&lt;client-name&gt;': Gtk4 input module is used
++     *            In case of the Gtk input modules, the name of the
++     *            client is also shown after the “:”, for example
++     *            like 'gtk3-im:firefox', 'gtk4-im:gnome-text-editor', …
++     * 'gnome-shell': Entries handled by gnome-shell
++     *                (like the command line dialog opened with Alt+F2
++     *                or the search field when pressing the Super key.)
++     *                When GTK_IM_MODULE is unset in a Gnome Wayland session
++     *                all programs which would show 'gtk3-im' or 'gtk4-im'
++     *                with GTK_IM_MODULE=ibus then show 'gnome-shell'
++     *                instead.
++     * 'Qt':      Qt4 programs like keepassx-2.0.3 …
++     * 'QIBusInputContext': Qt5 programs like keepassxc-2.7.1, anki-2.1.15
++     *                      telegram-desktop-3.7.3, 
++     *
++     * You need to set #IBusEngine::has-focus-id property to %TRUE when you
++     * construct an #IBusEngine to use this class method.
++     *
++     * See also: ibus_input_context_focus_in()
++     * <note><para>Argument @user_data is ignored in this function.</para>
++     * </note>
++     */
++    engine_signals[FOCUS_IN_ID] =
++        g_signal_new (I_("focus-in-id"),
++            G_TYPE_FROM_CLASS (gobject_class),
++            G_SIGNAL_RUN_LAST,
++            G_STRUCT_OFFSET (IBusEngineClass, focus_in_id),
++            NULL, NULL,
++            _ibus_marshal_VOID__STRING_STRING,
++            G_TYPE_NONE,
++            2,
++            G_TYPE_STRING,
++            G_TYPE_STRING);
++
+     /**
+      * IBusEngine::focus-out:
+      * @engine: An IBusEngine.
+@@ -423,7 +508,8 @@ ibus_engine_class_init (IBusEngineClass *class)
+      * in extended class to receive this signal.
+      *
+      * See also: ibus_input_context_focus_out()
+-     * <note><para>Argument @user_data is ignored in this function.</para></note>
++     * <note><para>Argument @user_data is ignored in this function.</para>
++     * </note>
+      */
+     engine_signals[FOCUS_OUT] =
+         g_signal_new (I_("focus-out"),
+@@ -435,6 +521,33 @@ ibus_engine_class_init (IBusEngineClass *class)
+             G_TYPE_NONE,
+             0);
+ 
++    /**
++     * IBusEngine::focus-out-id:
++     * @engine: An IBusEngine.
++     * @object_path: An object path.
++     *
++     * Emitted when the client application  lost the focus.
++     * Implement the member function IBusEngineClass::focus_out
++     * in extended class to receive this signal.
++     * @object_path is a unique id by input context.
++     * You need to set #IBusEngine::has-focus-id property to %TRUE when you
++     * construct an #IBusEngine to use this class method.
++     *
++     * See also: ibus_input_context_focus_out()
++     * <note><para>Argument @user_data is ignored in this function.</para>
++     * </note>
++     */
++    engine_signals[FOCUS_OUT_ID] =
++        g_signal_new (I_("focus-out-id"),
++            G_TYPE_FROM_CLASS (gobject_class),
++            G_SIGNAL_RUN_LAST,
++            G_STRUCT_OFFSET (IBusEngineClass, focus_out_id),
++            NULL, NULL,
++            _ibus_marshal_VOID__STRING,
++            G_TYPE_NONE,
++            1,
++            G_TYPE_STRING);
++
+     /**
+      * IBusEngine::reset:
+      * @engine: An IBusEngine.
+@@ -872,6 +985,9 @@ ibus_engine_set_property (IBusEngine   *engine,
+     case PROP_ENGINE_NAME:
+         engine->priv->engine_name = g_value_dup_string (value);
+         break;
++    case PROP_HAS_FOCUS_ID:
++        engine->priv->has_focus_id = g_value_get_boolean (value);
++        break;
+     default:
+         G_OBJECT_WARN_INVALID_PROPERTY_ID (engine, prop_id, pspec);
+     }
+@@ -887,7 +1003,9 @@ ibus_engine_get_property (IBusEngine *engine,
+     case PROP_ENGINE_NAME:
+         g_value_set_string (value, engine->priv->engine_name);
+         break;
+-
++    case PROP_HAS_FOCUS_ID:
++        g_value_set_boolean (value, engine->priv->has_focus_id);
++        break;
+     default:
+         G_OBJECT_WARN_INVALID_PROPERTY_ID (engine, prop_id, pspec);
+     }
+@@ -1176,6 +1294,30 @@ ibus_engine_service_method_call (IBusService           *service,
+         }
+     }
+ 
++    if (g_strcmp0 (method_name, "FocusInId") == 0) {
++        gchar *object_path = NULL;
++        gchar *client = NULL;
++        g_variant_get (parameters, "(&s&s)", &object_path, &client);
++        g_signal_emit (engine,
++                       engine_signals[FOCUS_IN_ID],
++                       0,
++                       object_path,
++                       client);
++        g_dbus_method_invocation_return_value (invocation, NULL);
++        return;
++    }
++
++    if (g_strcmp0 (method_name, "FocusOutId") == 0) {
++        gchar *object_path = NULL;
++        g_variant_get (parameters, "(&s)", &object_path);
++        g_signal_emit (engine,
++                       engine_signals[FOCUS_OUT_ID],
++                       0,
++                       object_path);
++        g_dbus_method_invocation_return_value (invocation, NULL);
++        return;
++    }
++
+     if (g_strcmp0 (method_name, "CandidateClicked") == 0) {
+         guint index, button, state;
+         g_variant_get (parameters, "(uuu)", &index, &button, &state);
+@@ -1303,6 +1445,23 @@ ibus_engine_service_method_call (IBusService           *service,
+     g_return_if_reached ();
+ }
+ 
++/**
++ * _ibus_engine_has_focus_id:
++ *
++ * Implement the "FocusId" method call of the org.freedesktop.IBus interface.
++ */
++static GVariant *
++_ibus_engine_has_focus_id (IBusEngine      *engine,
++                           GDBusConnection *connection,
++                           GError         **error)
++{
++    if (error) {
++        *error = NULL;
++    }
++
++    return g_variant_new_boolean (engine->priv->has_focus_id);
++}
++
+ static GVariant *
+ ibus_engine_service_get_property (IBusService        *service,
+                                   GDBusConnection    *connection,
+@@ -1312,7 +1471,18 @@ ibus_engine_service_get_property (IBusService        *service,
+                                   const gchar        *property_name,
+                                   GError            **error)
+ {
+-    return IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
++    int i;
++    static const struct {
++        const gchar *method_name;
++        GVariant * (* method_callback) (IBusEngine *,
++                                        GDBusConnection *,
++                                        GError **);
++    } methods [] =  {
++        { "FocusId",                    _ibus_engine_has_focus_id },
++    };
++
++    if (g_strcmp0 (interface_name, IBUS_INTERFACE_ENGINE) != 0) {
++        return IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
+                 service_get_property (service,
+                                       connection,
+                                       sender,
+@@ -1320,6 +1490,19 @@ ibus_engine_service_get_property (IBusService        *service,
+                                       interface_name,
+                                       property_name,
+                                       error);
++    }
++
++    for (i = 0; i < G_N_ELEMENTS (methods); i++) {
++        if (g_strcmp0 (methods[i].method_name, property_name) == 0) {
++            return methods[i].method_callback ((IBusEngine *) service,
++                                               connection,
++                                               error);
++        }
++    }
++
++    g_warning ("service_get_property received an unknown property: %s",
++               property_name ? property_name : "(null)");
++    return NULL;
+ }
+ 
+ static gboolean
+@@ -1386,31 +1569,39 @@ ibus_engine_process_key_event (IBusEngine *engine,
+ static void
+ ibus_engine_focus_in (IBusEngine *engine)
+ {
+-    // g_debug ("focus-in");
++}
++
++static void
++ibus_engine_focus_in_id (IBusEngine  *engine,
++                         const gchar *object_path,
++                         const gchar *client)
++{
+ }
+ 
+ static void
+ ibus_engine_focus_out (IBusEngine *engine)
+ {
+-    // g_debug ("focus-out");
++}
++
++static void
++ibus_engine_focus_out_id (IBusEngine  *engine,
++                          const gchar *object_path)
++{
+ }
+ 
+ static void
+ ibus_engine_reset (IBusEngine *engine)
+ {
+-    // g_debug ("reset");
+ }
+ 
+ static void
+ ibus_engine_enable (IBusEngine *engine)
+ {
+-    // g_debug ("enable");
+ }
+ 
+ static void
+ ibus_engine_disable (IBusEngine *engine)
+ {
+-    // g_debug ("disable");
+ }
+ 
+ static void
+diff --git a/src/ibusengine.h b/src/ibusengine.h
+index 43eaa554..6af0e856 100644
+--- a/src/ibusengine.h
++++ b/src/ibusengine.h
+@@ -2,7 +2,8 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2008-2013 Red Hat, Inc.
++ * Copyright (C) 2012-2022 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2008-2022 Red Hat, Inc.
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+@@ -153,10 +154,15 @@ struct _IBusEngineClass {
+                                     (IBusEngine     *engine,
+                                      guint           purpose,
+                                      guint           hints);
++    void        (* focus_in_id)     (IBusEngine     *engine,
++                                     const gchar    *object_path,
++                                     const gchar    *client);
++    void        (* focus_out_id)    (IBusEngine     *engine,
++                                     const gchar    *object_path);
+ 
+     /*< private >*/
+     /* padding */
+-    gpointer pdummy[4];
++    gpointer pdummy[2];
+ };
+ 
+ GType        ibus_engine_get_type       (void);
+diff --git a/src/ibusmarshalers.list b/src/ibusmarshalers.list
+index aa9ea82a..7489f842 100644
+--- a/src/ibusmarshalers.list
++++ b/src/ibusmarshalers.list
+@@ -19,6 +19,7 @@ VOID:OBJECT,UINT,UINT
+ VOID:OBJECT,BOOLEAN
+ VOID:BOXED,BOOLEAN
+ VOID:BOXED
++VOID:STRING,STRING
+ VOID:STRING,STRING,VARIANT
+ VOID:STRING,STRING,STRING
+ VOID:UINT
+-- 
+2.35.3
+
+From 59c13597918db98ec8120ccdac09f1e2dc576e1b Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 7 Jul 2022 08:22:48 +0900
+Subject: [PATCH] src/tests: Unset G_MESSAGES_DEBUG for gsettings in
+ xkb-latin-layouts
+
+gsettings cannot get the key value when G_MESSAGES_DEBUG is enabled.
+Add denylist.txt to engine/Makefile.am
+---
+ engine/Makefile.am          | 3 ++-
+ src/tests/xkb-latin-layouts | 7 +++++++
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/engine/Makefile.am b/engine/Makefile.am
+index 03867f52..7256fbc8 100644
+--- a/engine/Makefile.am
++++ b/engine/Makefile.am
+@@ -4,7 +4,7 @@
+ #
+ # Copyright (c) 2010-2016, Google Inc. All rights reserved.
+ # Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2013-2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2013-2021 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
+@@ -88,6 +88,7 @@ CLEANFILES = \
+ 	$(NULL)
+ 
+ EXTRA_DIST = \
++	denylist.txt \
+ 	gensimple.py \
+ 	iso639converter.py \
+ 	simple.xml.in \
+diff --git a/src/tests/xkb-latin-layouts b/src/tests/xkb-latin-layouts
+index f8dced6b..92464234 100755
+--- a/src/tests/xkb-latin-layouts
++++ b/src/tests/xkb-latin-layouts
+@@ -82,9 +82,16 @@ finit()
+ 
+ test_xkb_keymaps()
+ {
++    # G_MESSAGES_DEBUG=all or G_MESSAGES_DEBUG=GLib-GIO-DEBUG would append
++    # debug messages to gsettings output and could not get the result correctly.
++    backup_G_MESSAGES_DEBUG="$G_MESSAGES_DEBUG"
++    unset G_MESSAGES_DEBUG
+     # Loop over top level schemas since "gsettings list-recursively" only
+     # looks for direct children.
+     xkb_latin_layouts=`gsettings get org.freedesktop.ibus.general xkb-latin-layouts`
++    if [ x"$backup_G_MESSAGES_DEBUG" != x ] ; then
++        export G_MESSAGES_DEBUG=$backup_G_MESSAGES_DEBUG
++    fi
+     while read keymap ; do
+         eval keymap="$keymap"
+         HAS_VARIANT=$($ECHO "$keymap" | grep '(' 2> /dev/null) ||:
+-- 
+2.35.3
+

diff --git a/ibus.spec b/ibus.spec
index 47585f1..6958ddf 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -39,7 +39,7 @@
 
 Name:           ibus
 Version:        1.5.26
-Release:        12%{?dist}
+Release:        13%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPLv2+
 URL:            https://github.com/ibus/%name/wiki
@@ -524,6 +524,13 @@ dconf update || :
 %{_datadir}/installed-tests/ibus
 
 %changelog
+* Thu Jul 07 2022 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.26-13
+- Add IBUS_CAP_OSK to IBusCapabilite
+- Update ibus restart for --service-file option
+- Update manpage for ibus im-module command
+- Implement new process_key_event for GTK4
+- Add focus_in_id()/focus_out_id() class methods in IBusEngine
+
 * Wed Jun 29 2022 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.26-12
 - Add ibus im-module command
 

                 reply	other threads:[~2026-05-31  2:07 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=178019327428.1.7975090710454445502.rpms-ibus-ebbfbeae2909@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