public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Enable IM gtk4 module
@ 2026-05-31  2:07 Takao Fujiwara
  0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31  2:07 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/ibus
            Branch : autotool
            Commit : 823ed8c386d7d06fc9f38a7bfac78ce1a377c04a
            Author : Takao Fujiwara <tfujiwar@redhat.com>
            Date   : 2021-01-22T09:41:31+09:00
            Stats  : +1516/-8 in 2 file(s)
            URL    : https://src.fedoraproject.org/rpms/ibus/c/823ed8c386d7d06fc9f38a7bfac78ce1a377c04a?branch=autotool

            Log:
            Enable IM gtk4 module

- Fix to rename xkb:de::ger to sync xkeyboard-config
- Enhance ibus-setup search engine

---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 0b66c2d..5e3aadf 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -421,3 +421,1468 @@ index 06370a27..798ad04d 100644
 -- 
 2.24.1
 
+From 5322c447c74a10ee54b482d6eff6da6d16a7c9ce Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Sat, 21 Nov 2020 07:41:23 +0900
+Subject: [PATCH] ui/gtk3: Warn deprecated IBus XKB engines /w dialog
+
+simple.xml is updated by IBus release and some XKB engines
+could be deprecated with xkeyboard-config updates.
+Now a warning dialog is launched when the deprecated engines are
+changed to xkb:us::eng engine.
+
+BUG=https://github.com/ibus/ibus/issues/2274
+---
+ ui/gtk3/panel.vala | 15 +++++++++++++++
+ 1 files changed, 33 insertions(+), 10 deletions(-)
+
+diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
+index 627e26ae..01f87e33 100644
+--- a/ui/gtk3/panel.vala
++++ b/ui/gtk3/panel.vala
+@@ -970,6 +970,21 @@ class Panel : IBus.PanelService {
+             names =  {"xkb:us::eng"};
+             m_settings_general.set_strv("preload-engines", names);
+             engines = m_bus.get_engines_by_names(names);
++            var message = _("Your input method %s does not exist in IBus " +
++                    "input methods so \"US\" layout was configured instead " +
++                    "of your input method. Please run `ibus-setup` command, " +
++                    "open \"Input Method\" tab, and configure your input " +
++                    "methods again.").printf(names[0]);
++            var dialog = new Gtk.MessageDialog(
++                    null,
++                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
++                    Gtk.MessageType.WARNING,
++                    Gtk.ButtonsType.CLOSE,
++                    message);
++            dialog.response.connect((id) => {
++                    dialog.destroy();
++            });
++            dialog.show();
+ 	}
+ 
+         if (m_engines.length == 0) {
+-- 
+2.28.0
+
+From aec2ac75ea673d3701904735378765476cad0f21 Mon Sep 17 00:00:00 2001
+From: Gunnar Hjalmarsson <gunnarhj@ubuntu.com>
+Date: Fri, 11 Dec 2020 16:27:36 +0900
+Subject: [PATCH] ui/gtk3: Warning dialog for any deprecated IBus XKB
+ engine
+
+This is a follow-up of commit 5322c447. That commit introduced
+a warning dialog in case of an empty engines list.
+
+This commit makes a differently worded warning dialog be shown
+in cases where the engines list is not empty but preload-engines
+includes at least one deprecated engine. It also fixes a variable
+confusion so a deprecated engine is mentioned in the first kind
+of warning dialog and not the US engine.
+
+BUG=https://github.com/ibus/ibus/issues/2274
+---
+ ui/gtk3/panel.vala | 30 +++++++++++++++++++-----------
+ 1 files changed, 43 insertions(+), 24 deletions(-)
+
+diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
+index 01f87e33..ab2005d7 100644
+--- a/ui/gtk3/panel.vala
++++ b/ui/gtk3/panel.vala
+@@ -966,25 +966,33 @@ class Panel : IBus.PanelService {
+         /* Fedora internal patch could save engines not in simple.xml
+          * likes 'xkb:cn::chi'.
+          */
+-        if (engines.length == 0) {
+-            names =  {"xkb:us::eng"};
+-            m_settings_general.set_strv("preload-engines", names);
+-            engines = m_bus.get_engines_by_names(names);
+-            var message = _("Your input method %s does not exist in IBus " +
+-                    "input methods so \"US\" layout was configured instead " +
+-                    "of your input method. Please run `ibus-setup` command, " +
+-                    "open \"Input Method\" tab, and configure your input " +
+-                    "methods again.").printf(names[0]);
++        if (engines.length < names.length) {
++            string message1;
++            if (engines.length == 0) {
++                string[] fallback_names = {"xkb:us::eng"};
++                m_settings_general.set_strv("preload-engines", fallback_names);
++                engines = m_bus.get_engines_by_names(fallback_names);
++                message1 = _("Your configured input method %s does not exist " +
++                             "in IBus input methods so \"US\" layout was " +
++                             "configured instead of your input method."
++                            ).printf(names[0]);
++            } else {
++                message1 = _("At least one of your configured input methods " +
++                             "does not exist in IBus input methods.");
++            }
++            var message2 = _("Please run `ibus-setup` command, open \"Input " +
++                             "Method\" tab, and configure your input methods " +
++                             "again.");
+             var dialog = new Gtk.MessageDialog(
+                     null,
+                     Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                     Gtk.MessageType.WARNING,
+                     Gtk.ButtonsType.CLOSE,
+-                    message);
++                    "%s %s", message1, message2);
+             dialog.response.connect((id) => {
+                     dialog.destroy();
+             });
+-            dialog.show();
++            dialog.show_all();
+ 	}
+ 
+         if (m_engines.length == 0) {
+-- 
+2.28.0
+
+From 5d68b00e0464d43ba2f77697f9dcfbff78d7b438 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 31 Dec 2020 17:28:45 +0900
+Subject: [PATCH] engine: Fix iso-path in gensimple.py
+
+Replace datarootdir with ISOCODES_PREFIX.
+---
+ engine/Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/engine/Makefile.am b/engine/Makefile.am
+index d810704b..1944c02b 100644
+--- a/engine/Makefile.am
++++ b/engine/Makefile.am
+@@ -104,7 +104,7 @@ simple.xml.in:
+ 	    --output=$@ \
+ 	    --version=$(VERSION).`date '+%Y%m%d'` \
+ 	    --exec-path=@libexecdir\@/ibus-engine-simple \
+-	    --iso-path=$(datarootdir)/xml/iso-codes/iso_639.xml \
++	    --iso-path=$(ISOCODES_PREFIX)/share/xml/iso-codes/iso_639.xml \
+ 	    --first-language \
+ 	$(NULL)
+ 
+-- 
+2.28.0
+
+From 29959e1d2521781c544879b284e03812a2a11b2e Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 31 Dec 2020 20:00:02 +0900
+Subject: [PATCH] engine: Fix input in gensimple.py
+
+Replace datarootdir with XKBCONFIG_BASE.
+---
+ configure.ac       | 7 +++++++
+ engine/Makefile.am | 2 +-
+ 2 files changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 5753057b..9ed5cb66 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -720,6 +720,13 @@ PKG_CHECK_MODULES(ISOCODES, [
+ ISOCODES_PREFIX=`$PKG_CONFIG iso-codes --variable=prefix`
+ AC_SUBST(ISOCODES_PREFIX)
+ 
++PKG_CHECK_MODULES(XKBCONFIG,
++    [xkeyboard-config],
++    [XKBCONFIG_BASE=`$PKG_CONFIG xkeyboard-config --variable=xkb_base`],
++    [XKBCONFIG_BASE='$(datarootdir)/X11/xkb']
++)
++AC_SUBST(XKBCONFIG_BASE)
++
+ AC_SUBST([GDBUS_CODEGEN], [`$PKG_CONFIG --variable gdbus_codegen gio-2.0`])
+ 
+ # OUTPUT files
+diff --git a/engine/Makefile.am b/engine/Makefile.am
+index 1944c02b..84bc7f6c 100644
+--- a/engine/Makefile.am
++++ b/engine/Makefile.am
+@@ -100,7 +100,7 @@ simple.xml: simple.xml.in
+ 
+ simple.xml.in:
+ 	$(srcdir)/gensimple.py \
+-	    --input=$(datarootdir)/X11/xkb/rules/evdev.xml \
++	    --input=$(XKBCONFIG_BASE)/rules/evdev.xml \
+ 	    --output=$@ \
+ 	    --version=$(VERSION).`date '+%Y%m%d'` \
+ 	    --exec-path=@libexecdir\@/ibus-engine-simple \
+-- 
+2.28.0
+
+From c7928b158741282e17a042f767bc5ae32c302c96 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Fri, 8 Jan 2021 18:45:55 +0900
+Subject: [PATCH] Add GTK4 IM module
+
+BUG=https://github.com/ibus/ibus/issues/2291
+---
+ client/Makefile.am          |   7 +-
+ client/gtk2/ibusimcontext.c | 352 +++++++++++++++++++++++++++++++-----
+ client/gtk4/Makefile.am     |  66 +++++++
+ client/gtk4/ibusim.c        |  52 ++++++
+ client/gtk4/ibusimcontext.c |   1 +
+ client/gtk4/ibusimcontext.h |   1 +
+ configure.ac                |  32 ++++
+ 7 files changed, 465 insertions(+), 46 deletions(-)
+ create mode 100644 client/gtk4/Makefile.am
+ create mode 100644 client/gtk4/ibusim.c
+ create mode 120000 client/gtk4/ibusimcontext.c
+ create mode 120000 client/gtk4/ibusimcontext.h
+
+diff --git a/client/Makefile.am b/client/Makefile.am
+index 546ca132..1744518c 100644
+--- a/client/Makefile.am
++++ b/client/Makefile.am
+@@ -3,7 +3,7 @@
+ # ibus - The Input Bus
+ #
+ # Copyright (c) 2007-2010 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2007-2010 Red Hat, Inc.
++# Copyright (c) 2007-2020 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
+@@ -28,6 +28,10 @@ if ENABLE_GTK3
+ GTK3 = gtk3
+ endif
+ 
++if ENABLE_GTK4
++GTK4 = gtk4
++endif
++
+ if ENABLE_XIM
+ X11 = x11
+ endif
+@@ -39,6 +43,7 @@ endif
+ SUBDIRS = \
+ 	$(GTK2) \
+ 	$(GTK3) \
++	$(GTK4) \
+ 	$(X11) \
+ 	$(WAYLAND) \
+ 	$(NULL)
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index 50290c55..a23fc2e3 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -2,8 +2,8 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2015-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
+- * Copyright (C) 2008-2019 Red Hat, Inc.
++ * Copyright (C) 2015-2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2008-2020 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
+@@ -32,8 +32,12 @@
+ #include "ibusimcontext.h"
+ 
+ #ifdef GDK_WINDOWING_WAYLAND
++#if GTK_CHECK_VERSION (3, 98, 4)
++#include <gdk/wayland/gdkwayland.h>
++#else
+ #include <gdk/gdkwayland.h>
+ #endif
++#endif
+ 
+ #if !GTK_CHECK_VERSION (2, 91, 0)
+ #  define DEPRECATED_GDK_KEYSYMS 1
+@@ -52,7 +56,11 @@ struct _IBusIMContext {
+ 
+     /* instance members */
+     GtkIMContext *slave;
++#if GTK_CHECK_VERSION (3, 98, 4)
++    GtkWidget *client_window;
++#else
+     GdkWindow *client_window;
++#endif
+ 
+     IBusInputContext *ibuscontext;
+ 
+@@ -73,7 +81,12 @@ struct _IBusIMContext {
+     GCancellable    *cancellable;
+     GQueue          *events_queue;
+ 
+-#if !GTK_CHECK_VERSION (3, 93, 0)
++#if GTK_CHECK_VERSION (3, 98, 4)
++    GdkSurface      *surface;
++    GdkDevice       *device;
++    double           x;
++    double           y;
++#else
+     gboolean         use_button_press_event;
+ #endif
+ };
+@@ -90,9 +103,11 @@ static guint    _signal_preedit_end_id = 0;
+ static guint    _signal_delete_surrounding_id = 0;
+ static guint    _signal_retrieve_surrounding_id = 0;
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+ static const gchar *_no_snooper_apps = NO_SNOOPER_APPS;
+ static gboolean _use_key_snooper = ENABLE_SNOOPER;
+ static guint    _key_snooper_id = 0;
++#endif
+ 
+ static gboolean _use_sync_mode = FALSE;
+ 
+@@ -101,8 +116,10 @@ static gboolean _use_discard_password = FALSE;
+ 
+ static GtkIMContext *_focus_im_context = NULL;
+ static IBusInputContext *_fake_context = NULL;
++#if !GTK_CHECK_VERSION (3, 98, 4)
+ static GdkWindow *_input_window = NULL;
+ static GtkWidget *_input_widget = NULL;
++#endif
+ 
+ /* functions prototype */
+ static void     ibus_im_context_class_init  (IBusIMContextClass    *class);
+@@ -114,7 +131,11 @@ static void     ibus_im_context_finalize    (GObject               *obj);
+ static void     ibus_im_context_reset       (GtkIMContext          *context);
+ static gboolean ibus_im_context_filter_keypress
+                                             (GtkIMContext           *context,
++#if GTK_CHECK_VERSION (3, 98, 4)
++                                             GdkEvent               *key);
++#else
+                                              GdkEventKey            *key);
++#endif
+ static void     ibus_im_context_focus_in    (GtkIMContext          *context);
+ static void     ibus_im_context_focus_out   (GtkIMContext          *context);
+ static void     ibus_im_context_get_preedit_string
+@@ -122,9 +143,15 @@ static void     ibus_im_context_get_preedit_string
+                                              gchar                  **str,
+                                              PangoAttrList          **attrs,
+                                              gint                   *cursor_pos);
++#if GTK_CHECK_VERSION (3, 98, 4)
++static void     ibus_im_context_set_client_widget
++                                            (GtkIMContext           *context,
++                                             GtkWidget              *client);
++#else
+ static void     ibus_im_context_set_client_window
+                                             (GtkIMContext           *context,
+                                              GdkWindow              *client);
++#endif
+ static void     ibus_im_context_set_cursor_location
+                                             (GtkIMContext           *context,
+                                              GdkRectangle           *area);
+@@ -239,6 +266,7 @@ ibus_im_context_new (void)
+     return IBUS_IM_CONTEXT (obj);
+ }
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+ static gboolean
+ _focus_in_cb (GtkWidget     *widget,
+               GdkEventFocus *event,
+@@ -260,22 +288,41 @@ _focus_out_cb (GtkWidget     *widget,
+     }
+     return FALSE;
+ }
++#endif /* end of GTK_CHECK_VERSION (3, 98, 4) */
+ 
+ static gboolean
+ ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
++#if GTK_CHECK_VERSION (3, 98, 4)
++                              GdkEvent      *event)
++#else
+                               GdkEventKey   *event)
++#endif
+ {
++    guint keyval = 0;
++    GdkModifierType state = 0;
+     int i;
+     GdkModifierType no_text_input_mask;
+     gunichar ch;
+ 
++#if GTK_CHECK_VERSION (3, 98, 4)
++    if (gdk_event_get_event_type (event) == GDK_KEY_RELEASE)
++        return FALSE;
++    keyval = gdk_key_event_get_keyval (event);
++    state = gdk_event_get_modifier_state (event);
++#else
+     if (event->type == GDK_KEY_RELEASE)
+         return FALSE;
++    keyval = event->keyval;
++    state = event->state;
++#endif
++
+     /* Ignore modifier key presses */
+     for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++)
+-        if (event->keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i])
++        if (keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i])
+             return FALSE;
+-#if GTK_CHECK_VERSION (3, 4, 0)
++#if GTK_CHECK_VERSION (3, 98, 4)
++    no_text_input_mask = GDK_MODIFIER_MASK;
++#elif GTK_CHECK_VERSION (3, 4, 0)
+     no_text_input_mask = gdk_keymap_get_modifier_mask (
+             gdk_keymap_get_for_display (gdk_display_get_default ()),
+             GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
+@@ -290,13 +337,13 @@ ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
+ 
+ #  undef _IBUS_NO_TEXT_INPUT_MOD_MASK
+ #endif
+-    if (event->state & no_text_input_mask ||
+-        event->keyval == GDK_KEY_Return ||
+-        event->keyval == GDK_KEY_ISO_Enter ||
+-        event->keyval == GDK_KEY_KP_Enter) {
++    if (state & no_text_input_mask ||
++        keyval == GDK_KEY_Return ||
++        keyval == GDK_KEY_ISO_Enter ||
++        keyval == GDK_KEY_KP_Enter) {
+         return FALSE;
+     }
+-    ch = ibus_keyval_to_unicode (event->keyval);
++    ch = ibus_keyval_to_unicode (keyval);
+     if (ch != 0 && !g_unichar_iscntrl (ch)) {
+         IBusText *text = ibus_text_new_from_unichar (ch);
+         g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
+@@ -307,14 +354,26 @@ ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
+    return FALSE;
+ }
+ 
++struct _ProcessKeyEventData {
++    GdkEvent *event;
++    IBusIMContext *ibusimcontext;
++};
++
++typedef struct _ProcessKeyEventData ProcessKeyEventData;
++
+ static void
+ _process_key_event_done (GObject      *object,
+                          GAsyncResult *res,
+                          gpointer      user_data)
+ {
+     IBusInputContext *context = (IBusInputContext *)object;
+-    GdkEventKey *event = (GdkEventKey *) user_data;
++
++    ProcessKeyEventData *data = (ProcessKeyEventData *)user_data;
++    GdkEvent *event = data->event;
++    IBusIMContext *ibusimcontext = data->ibusimcontext;
+     GError *error = NULL;
++
++    g_slice_free (ProcessKeyEventData, data);
+     gboolean retval = ibus_input_context_process_key_event_async_finish (
+             context,
+             res,
+@@ -326,46 +385,96 @@ _process_key_event_done (GObject      *object,
+     }
+ 
+     if (retval == FALSE) {
+-        event->state |= IBUS_IGNORED_MASK;
+-        gdk_event_put ((GdkEvent *)event);
++#if GTK_CHECK_VERSION (3, 98, 4)
++        g_return_if_fail (GTK_IS_IM_CONTEXT (ibusimcontext));
++        gtk_im_context_filter_key (
++                GTK_IM_CONTEXT (ibusimcontext),
++                gdk_event_get_event_type (event) == GDK_KEY_PRESS,
++                gdk_event_get_surface (event),
++                gdk_event_get_device (event),
++                gdk_event_get_time (event),
++                gdk_key_event_get_keycode (event),
++                gdk_event_get_modifier_state (event) | IBUS_IGNORED_MASK,
++                0);
++#else
++        ((GdkEventKey *)event)->state |= IBUS_IGNORED_MASK;
++        gdk_event_put (event);
++#endif
+     }
+-    gdk_event_free ((GdkEvent *)event);
++#if GTK_CHECK_VERSION (3, 98, 4)
++    gdk_event_unref (event);
++#else
++    gdk_event_free (event);
++#endif
+ }
+ 
+ static gboolean
+ _process_key_event (IBusInputContext *context,
+-                    GdkEventKey      *event)
++#if GTK_CHECK_VERSION (3, 98, 4)
++                    GdkEvent         *event,
++#else
++                    GdkEventKey      *event,
++#endif
++                    IBusIMContext    *ibusimcontext)
+ {
+-    guint state = event->state;
++    guint state;
++    guint keyval = 0;
++    guint16 hardware_keycode = 0;
++    guint keycode = 0;
+     gboolean retval = FALSE;
+ 
+-    if (event->type == GDK_KEY_RELEASE) {
++#if GTK_CHECK_VERSION (3, 98, 4)
++    GdkModifierType gdkstate = gdk_event_get_modifier_state (event);
++    state = (uint)gdkstate;
++    if (gdk_event_get_event_type (event) == GDK_KEY_RELEASE)
+         state |= IBUS_RELEASE_MASK;
+-    }
++    keyval = gdk_key_event_get_keyval (event);
++    hardware_keycode = gdk_key_event_get_keycode (event);
++#else
++    state = event->state;
++    if (event->type == GDK_KEY_RELEASE)
++        state |= IBUS_RELEASE_MASK;
++    keyval = event->keyval;
++    hardware_keycode = event->hardware_keycode;
++#endif
++    keycode = hardware_keycode;
+ 
+     if (_use_sync_mode) {
+         retval = ibus_input_context_process_key_event (context,
+-            event->keyval,
+-            event->hardware_keycode - 8,
++            keyval,
++            keycode - 8,
+             state);
+     }
+     else {
++        ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData);
++#if GTK_CHECK_VERSION (3, 98, 4)
++        data->event = gdk_event_ref (event);
++#else
++        data->event = gdk_event_copy ((GdkEvent *)event);
++#endif
++        data->ibusimcontext = ibusimcontext;
+         ibus_input_context_process_key_event_async (context,
+-            event->keyval,
+-            event->hardware_keycode - 8,
++            keyval,
++            keycode - 8,
+             state,
+             -1,
+             NULL,
+             _process_key_event_done,
+-            gdk_event_copy ((GdkEvent *) event));
++            data);
+ 
+         retval = TRUE;
+     }
+ 
++    /* GTK4 does not provide gtk_key_snooper_install() and also
++     * GtkIMContextClass->filter_keypress() cannot send the updated
++     * GdkEventKey so event->state is not updated here in GTK4.
++     */
++#if !GTK_CHECK_VERSION (3, 98, 4)
+     if (retval)
+         event->state |= IBUS_HANDLED_MASK;
+     else
+         event->state |= IBUS_IGNORED_MASK;
++#endif
+ 
+     return retval;
+ }
+@@ -425,6 +534,7 @@ _set_content_type (IBusIMContext *context)
+ }
+ 
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+ static gint
+ _key_snooper_cb (GtkWidget   *widget,
+                  GdkEventKey *event,
+@@ -526,7 +636,7 @@ _key_snooper_cb (GtkWidget   *widget,
+         ibusimcontext->time = event->time;
+     }
+ 
+-    retval = _process_key_event (ibuscontext, event);
++    retval = _process_key_event (ibuscontext, event, ibusimcontext);
+ 
+     if (ibusimcontext != NULL) {
+         /* unref ibusimcontext could call ibus_im_context_finalize here
+@@ -537,6 +647,7 @@ _key_snooper_cb (GtkWidget   *widget,
+ 
+     return retval;
+ }
++#endif
+ 
+ static gboolean
+ _get_boolean_env(const gchar *name,
+@@ -599,7 +710,11 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+     im_context_class->focus_out = ibus_im_context_focus_out;
+     im_context_class->filter_keypress = ibus_im_context_filter_keypress;
+     im_context_class->get_preedit_string = ibus_im_context_get_preedit_string;
++#if GTK_CHECK_VERSION (3, 98, 4)
++    im_context_class->set_client_widget = ibus_im_context_set_client_widget;
++#else
+     im_context_class->set_client_window = ibus_im_context_set_client_window;
++#endif
+     im_context_class->set_cursor_location = ibus_im_context_set_cursor_location;
+     im_context_class->set_use_preedit = ibus_im_context_set_use_preedit;
+     im_context_class->set_surrounding = ibus_im_context_set_surrounding;
+@@ -630,8 +745,10 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+         g_signal_lookup ("retrieve-surrounding", G_TYPE_FROM_CLASS (class));
+     g_assert (_signal_retrieve_surrounding_id != 0);
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+     _use_key_snooper = !_get_boolean_env ("IBUS_DISABLE_SNOOPER",
+                                           !(ENABLE_SNOOPER));
++#endif
+     _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", FALSE);
+     _use_discard_password = _get_boolean_env ("IBUS_DISCARD_PASSWORD", FALSE);
+ 
+@@ -656,6 +773,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+     g_strfreev (apps);                                                  \
+ }
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+     /* env IBUS_DISABLE_SNOOPER does not exist */
+     if (_use_key_snooper) {
+         /* disable snooper if app is in _no_snooper_apps */
+@@ -664,6 +782,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+                                         _no_snooper_apps,
+                                         FALSE);
+     }
++#endif
+     if (!_use_discard_password) {
+         CHECK_APP_IN_CSV_ENV_VARIABLES (_use_discard_password,
+                                         IBUS_DISCARD_PASSWORD_APPS,
+@@ -686,6 +805,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+     }
+ 
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+     /* always install snooper */
+     if (_key_snooper_id == 0) {
+ #pragma GCC diagnostic push
+@@ -693,6 +813,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+         _key_snooper_id = gtk_key_snooper_install (_key_snooper_cb, NULL);
+ #pragma GCC diagnostic pop
+     }
++#endif
+ 
+     _daemon_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                               ibus_bus_get_service_name (_bus),
+@@ -706,6 +827,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+ static void
+ ibus_im_context_class_fini (IBusIMContextClass *class)
+ {
++#if !GTK_CHECK_VERSION (3, 98, 4)
+     if (_key_snooper_id != 0) {
+         IDEBUG ("snooper is terminated.");
+ #pragma GCC diagnostic push
+@@ -714,6 +836,7 @@ ibus_im_context_class_fini (IBusIMContextClass *class)
+ #pragma GCC diagnostic pop
+         _key_snooper_id = 0;
+     }
++#endif
+ 
+     g_bus_unwatch_name (_daemon_name_watch_id);
+ }
+@@ -849,7 +972,11 @@ ibus_im_context_finalize (GObject *obj)
+         ibus_proxy_destroy ((IBusProxy *)ibusimcontext->ibuscontext);
+     }
+ 
++#if GTK_CHECK_VERSION (3, 98, 4)
++    ibus_im_context_set_client_widget ((GtkIMContext *)ibusimcontext, NULL);
++#else
+     ibus_im_context_set_client_window ((GtkIMContext *)ibusimcontext, NULL);
++#endif
+ 
+     if (ibusimcontext->slave) {
+         g_object_unref (ibusimcontext->slave);
+@@ -864,8 +991,13 @@ ibus_im_context_finalize (GObject *obj)
+         pango_attr_list_unref (ibusimcontext->preedit_attrs);
+     }
+ 
++#if GTK_CHECK_VERSION (3, 98, 4)
++    g_queue_free_full (ibusimcontext->events_queue,
++                       (GDestroyNotify)gdk_event_unref);
++#else
+     g_queue_free_full (ibusimcontext->events_queue,
+                        (GDestroyNotify)gdk_event_free);
++#endif
+ 
+     G_OBJECT_CLASS(parent_class)->finalize (obj);
+ }
+@@ -902,7 +1034,11 @@ ibus_im_context_clear_preedit_text (IBusIMContext *ibusimcontext)
+ 
+ static gboolean
+ ibus_im_context_filter_keypress (GtkIMContext *context,
++#if GTK_CHECK_VERSION (3, 98, 4)
++                                 GdkEvent     *event)
++#else
+                                  GdkEventKey  *event)
++#endif
+ {
+     IDEBUG ("%s", __FUNCTION__);
+ 
+@@ -917,6 +1053,15 @@ ibus_im_context_filter_keypress (GtkIMContext *context,
+     if (!ibusimcontext->has_focus)
+         return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
+ 
++#if GTK_CHECK_VERSION (3, 98, 4)
++    {
++        GdkModifierType state = gdk_event_get_modifier_state (event);
++        if (state & IBUS_HANDLED_MASK)
++            return TRUE;
++        if (state & IBUS_IGNORED_MASK)
++            return ibus_im_context_commit_event (ibusimcontext, event);
++    }
++#else
+     if (event->state & IBUS_HANDLED_MASK)
+         return TRUE;
+ 
+@@ -931,17 +1076,28 @@ ibus_im_context_filter_keypress (GtkIMContext *context,
+     if (ibusimcontext->client_window == NULL && event->window != NULL)
+         gtk_im_context_set_client_window ((GtkIMContext *)ibusimcontext,
+                                           event->window);
++#endif
+ 
+     _request_surrounding_text (ibusimcontext);
+ 
++#if GTK_CHECK_VERSION (3, 98, 4)
++    ibusimcontext->time = gdk_event_get_time (event);
++    ibusimcontext->surface= gdk_event_get_surface (event);
++    ibusimcontext->device = gdk_event_get_device (event);
++    gdk_event_get_position (event, &ibusimcontext->x, &ibusimcontext->y);
++#else
+     ibusimcontext->time = event->time;
++#endif
+ 
+     if (ibusimcontext->ibuscontext) {
+-        if (_process_key_event (ibusimcontext->ibuscontext, event))
++        if (_process_key_event (ibusimcontext->ibuscontext,
++                                event,
++                                ibusimcontext)) {
+             return TRUE;
+-        else
++        } else {
+             return gtk_im_context_filter_keypress (ibusimcontext->slave,
+                                                    event);
++        }
+     }
+ 
+     /* At this point we _should_ be waiting for the IBus context to be
+@@ -952,12 +1108,21 @@ ibus_im_context_filter_keypress (GtkIMContext *context,
+                           ibus_bus_is_connected (_bus) == FALSE,
+                           FALSE);
+     g_queue_push_tail (ibusimcontext->events_queue,
++#if GTK_CHECK_VERSION (3, 98, 4)
++                       gdk_event_ref (event));
++#else
+                        gdk_event_copy ((GdkEvent *)event));
++#endif
+ 
+     if (g_queue_get_length (ibusimcontext->events_queue) > MAX_QUEUED_EVENTS) {
+         g_warning ("Events queue growing too big, will start to drop.");
++#if GTK_CHECK_VERSION (3, 98, 4)
++        gdk_event_unref ((GdkEvent *)
++                         g_queue_pop_head (ibusimcontext->events_queue));
++#else
+         gdk_event_free ((GdkEvent *)
+                         g_queue_pop_head (ibusimcontext->events_queue));
++#endif
+     }
+ 
+     return TRUE;
+@@ -966,26 +1131,29 @@ ibus_im_context_filter_keypress (GtkIMContext *context,
+ static void
+ ibus_im_context_focus_in (GtkIMContext *context)
+ {
+-    IDEBUG ("%s", __FUNCTION__);
+-
+     IBusIMContext *ibusimcontext = (IBusIMContext *) context;
++    GtkWidget *widget = NULL;
++
++    IDEBUG ("%s", __FUNCTION__);
+ 
+     if (ibusimcontext->has_focus)
+         return;
+ 
+     /* don't set focus on password entry */
++#if GTK_CHECK_VERSION (3, 98, 4)
++    widget = ibusimcontext->client_window;
++#else
+     if (ibusimcontext->client_window != NULL) {
+-        GtkWidget *widget;
+-
+         gdk_window_get_user_data (ibusimcontext->client_window,
+                                   (gpointer *)&widget);
+ 
+-        if (GTK_IS_ENTRY (widget) &&
+-            !gtk_entry_get_visibility (GTK_ENTRY (widget))) {
+-            return;
+-        }
+     }
++#endif
+ 
++    if (widget && GTK_IS_ENTRY (widget) &&
++        !gtk_entry_get_visibility (GTK_ENTRY (widget))) {
++        return;
++    }
+     /* Do not call gtk_im_context_focus_out() here.
+      * google-chrome's notification popup window (Pushbullet)
+      * takes the focus and the popup window disappears.
+@@ -1014,10 +1182,10 @@ ibus_im_context_focus_in (GtkIMContext *context)
+ 
+     /* set_cursor_location_internal() will get origin from X server,
+      * it blocks UI. So delay it to idle callback. */
+-    gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
+-                               (GSourceFunc) _set_cursor_location_internal,
+-                               g_object_ref (ibusimcontext),
+-                               (GDestroyNotify) g_object_unref);
++    g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
++                     (GSourceFunc) _set_cursor_location_internal,
++                     g_object_ref (ibusimcontext),
++                     (GDestroyNotify) g_object_unref);
+ 
+     /* retrieve the initial surrounding-text (regardless of whether
+      * the current IBus engine needs surrounding-text) */
+@@ -1122,7 +1290,7 @@ ibus_im_context_get_preedit_string (GtkIMContext   *context,
+ }
+ 
+ 
+-#if !GTK_CHECK_VERSION (3, 93, 0)
++#if !GTK_CHECK_VERSION (3, 98, 4)
+ /* Use the button-press-event signal until GtkIMContext always emits the reset
+  * signal.
+  * https://gitlab.gnome.org/GNOME/gtk/merge_requests/460
+@@ -1171,8 +1339,15 @@ _connect_button_press_event (IBusIMContext *ibusimcontext,
+ }
+ #endif
+ 
++#if GTK_CHECK_VERSION (3, 98, 4)
+ static void
+-ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client)
++ibus_im_context_set_client_widget (GtkIMContext *context,
++                                   GtkWidget    *client)
++#else
++static void
++ibus_im_context_set_client_window (GtkIMContext *context,
++                                   GdkWindow    *client)
++#endif
+ {
+     IBusIMContext *ibusimcontext;
+ 
+@@ -1181,7 +1356,7 @@ ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client)
+     ibusimcontext = IBUS_IM_CONTEXT (context);
+ 
+     if (ibusimcontext->client_window) {
+-#if !GTK_CHECK_VERSION (3, 93, 0)
++#if !GTK_CHECK_VERSION (3, 98, 4)
+         if (ibusimcontext->use_button_press_event)
+             _connect_button_press_event (ibusimcontext, FALSE);
+ #endif
+@@ -1191,26 +1366,41 @@ ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client)
+ 
+     if (client != NULL) {
+         ibusimcontext->client_window = g_object_ref (client);
+-#if !GTK_CHECK_VERSION (3, 93, 0)
++#if !GTK_CHECK_VERSION (3, 98, 4)
+         if (!ibusimcontext->use_button_press_event)
+             _connect_button_press_event (ibusimcontext, TRUE);
+ #endif
+     }
++#if GTK_CHECK_VERSION (3, 98, 4)
++    if (ibusimcontext->slave)
++        gtk_im_context_set_client_widget (ibusimcontext->slave, client);
++#else
+     if (ibusimcontext->slave)
+         gtk_im_context_set_client_window (ibusimcontext->slave, client);
++#endif
+ }
+ 
+ static void
+ _set_rect_scale_factor_with_window (GdkRectangle *area,
++#if GTK_CHECK_VERSION (3, 98, 4)
++                                    GtkWidget    *window)
++#else
+                                     GdkWindow    *window)
++#endif
+ {
+ #if GTK_CHECK_VERSION (3, 10, 0)
+     int scale_factor;
+ 
+     g_assert (area);
++#if GTK_CHECK_VERSION (3, 98, 4)
++    g_assert (GTK_IS_WIDGET (window));
++
++    scale_factor = gtk_widget_get_scale_factor (window);
++#else
+     g_assert (GDK_IS_WINDOW (window));
+ 
+     scale_factor = gdk_window_get_scale_factor (window);
++#endif
+     area->x *= scale_factor;
+     area->y *= scale_factor;
+     area->width *= scale_factor;
+@@ -1230,6 +1420,7 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+ 
+     area = ibusimcontext->cursor_area;
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+ #ifdef GDK_WINDOWING_WAYLAND
+     if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) {
+         gdouble px, py;
+@@ -1253,10 +1444,14 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+             area.height);
+         return FALSE;
+     }
++#endif
+ #endif
+ 
+     if (area.x == -1 && area.y == -1 && area.width == 0 && area.height == 0) {
+-#if GTK_CHECK_VERSION (2, 91, 0)
++#if GTK_CHECK_VERSION (3, 98, 4)
++        area.x = 0;
++        area.y += gtk_widget_get_height (ibusimcontext->client_window);
++#elif GTK_CHECK_VERSION (2, 91, 0)
+         area.x = 0;
+         area.y += gdk_window_get_height (ibusimcontext->client_window);
+ #else
+@@ -1267,9 +1462,39 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+ #endif
+     }
+ 
++#if GTK_CHECK_VERSION (3, 93, 0)
++    {
++#if GTK_CHECK_VERSION (3, 98, 4)
++        GtkNative *native = gtk_widget_get_native (
++                ibusimcontext->client_window);
++        GtkRoot *root = gtk_widget_get_root (ibusimcontext->client_window);
++        double nx, ny;
++        double px, py;
++        gtk_native_get_surface_transform (native, &nx, &ny);
++        px = (double)area.x + ibusimcontext->x - nx;
++        py = (double)area.y + ibusimcontext->y - ny;
++        gtk_widget_translate_coordinates (ibusimcontext->client_window,
++                                          (GtkWidget *)root,
++                                          px, py,
++                                          &px, &py);
++        area.x = (int)px;
++        area.y = (int)py;
++#else
++        GtkNative *native = gtk_widget_get_native (
++                ibusimcontext->client_window);
++        GdkSurface *surface = gtk_native_get_surface (native);
++        int root_x = 0;
++        int root_y = 0;
++        gdk_surface_get_position (surface, &root_x, &root_y);
++        area.x += root_x;
++        area.y += root_y;
++#endif
++    }
++#else
+     gdk_window_get_root_coords (ibusimcontext->client_window,
+                                 area.x, area.y,
+                                 &area.x, &area.y);
++#endif
+     _set_rect_scale_factor_with_window (&area, ibusimcontext->client_window);
+     ibus_input_context_set_cursor_location (ibusimcontext->ibuscontext,
+                                             area.x,
+@@ -1326,7 +1551,11 @@ get_selection_anchor_point (IBusIMContext *ibusimcontext,
+     if (ibusimcontext->client_window == NULL) {
+         return cursor_pos;
+     }
++#if GTK_CHECK_VERSION (3, 98, 4)
++    widget = ibusimcontext->client_window;
++#else
+     gdk_window_get_user_data (ibusimcontext->client_window, (gpointer *)&widget);
++#endif
+ 
+     if (!GTK_IS_TEXT_VIEW (widget)){
+         return cursor_pos;
+@@ -1440,6 +1669,7 @@ _ibus_context_commit_text_cb (IBusInputContext *ibuscontext,
+     _request_surrounding_text (ibusimcontext);
+ }
+ 
++#if !GTK_CHECK_VERSION (3, 98, 4)
+ static gboolean
+ _key_is_modifier (guint keyval)
+ {
+@@ -1615,6 +1845,7 @@ _create_gdk_event (IBusIMContext *ibusimcontext,
+ out:
+     return event;
+ }
++#endif
+ 
+ static void
+ _ibus_context_forward_key_event_cb (IBusInputContext  *ibuscontext,
+@@ -1624,9 +1855,32 @@ _ibus_context_forward_key_event_cb (IBusInputContext  *ibuscontext,
+                                     IBusIMContext     *ibusimcontext)
+ {
+     IDEBUG ("%s", __FUNCTION__);
++#if GTK_CHECK_VERSION (3, 98, 4)
++    int group = 0;
++    g_return_if_fail (GTK_IS_IM_CONTEXT (ibusimcontext));
++    if (keycode == 0 && ibusimcontext->client_window) {
++        GdkDisplay *display = gtk_widget_get_display (ibusimcontext->client_window);
++        GdkKeymapKey *keys = NULL;
++        gint n_keys = 0;
++        if (!gdk_display_map_keyval (display, keyval, &keys, &n_keys))
++            g_warning ("Failed to parse keycode from keyval %x", keyval);
++       keycode = keys->keycode;
++       group = keys->group;
++    }
++    gtk_im_context_filter_key (
++        GTK_IM_CONTEXT (ibusimcontext),
++        (state & IBUS_RELEASE_MASK) ? FALSE : TRUE,
++        ibusimcontext->surface,
++        ibusimcontext->device,
++        ibusimcontext->time,
++        keycode,
++        (GdkModifierType)state,
++        group);
++#else
+     GdkEventKey *event = _create_gdk_event (ibusimcontext, keyval, keycode, state);
+     gdk_event_put ((GdkEvent *)event);
+     gdk_event_free ((GdkEvent *)event);
++#endif
+ }
+ 
+ static void
+@@ -1660,7 +1914,7 @@ _ibus_context_update_preedit_text_cb (IBusInputContext  *ibuscontext,
+         ibusimcontext->preedit_attrs = NULL;
+     }
+ 
+-#if !GTK_CHECK_VERSION (3, 93, 0)
++#if !GTK_CHECK_VERSION (3, 98, 4)
+     if (!ibusimcontext->use_button_press_event &&
+         mode == IBUS_ENGINE_PREEDIT_COMMIT) {
+         if (ibusimcontext->client_window) {
+@@ -1847,10 +2101,18 @@ _create_input_context_done (IBusBus       *bus,
+         }
+ 
+         if (!g_queue_is_empty (ibusimcontext->events_queue)) {
++#if GTK_CHECK_VERSION (3, 98, 4)
++            GdkEvent *event;
++#else
+             GdkEventKey *event;
++#endif
+             while ((event = g_queue_pop_head (ibusimcontext->events_queue))) {
+-                _process_key_event (context, event);
++                _process_key_event (context, event, ibusimcontext);
++#if GTK_CHECK_VERSION (3, 98, 4)
++                gdk_event_unref (event);
++#else
+                 gdk_event_free ((GdkEvent *)event);
++#endif
+             }
+         }
+     }
+diff --git a/client/gtk4/Makefile.am b/client/gtk4/Makefile.am
+new file mode 100644
+index 00000000..8d8b31db
+--- /dev/null
++++ b/client/gtk4/Makefile.am
+@@ -0,0 +1,66 @@
++# vim:set noet ts=4:
++#
++# ibus - The Input Bus
++#
++# Copyright (c) 2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2020 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
++# License as published by the Free Software Foundation; either
++# version 2.1 of the License, or (at your option) any later version.
++#
++# This library is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++# Lesser General Public License for more details.
++#
++# You should have received a copy of the GNU Lesser General Public
++# License along with this library; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
++# USA
++
++libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
++
++AM_CPPFLAGS = \
++	-I$(top_srcdir)/src \
++	-I$(top_builddir)/src \
++	$(NULL)
++
++immoduledir = @GTK4_IM_MODULEDIR@
++immodule_LTLIBRARIES = libim-ibus.la
++
++libim_ibus_la_SOURCES = \
++	ibusim.c \
++	ibusimcontext.c \
++	ibusimcontext.h \
++	$(NULL)
++
++libim_ibus_la_DEPENDENCIES = $(libibus)
++
++libim_ibus_la_CFLAGS = \
++	@GTK4_CFLAGS@ \
++	@DBUS_CFLAGS@ \
++	-DG_LOG_DOMAIN=\"IBUS\" \
++	$(NULL)
++
++libim_ibus_la_LIBADD = \
++	@GTK4_LIBS@ \
++	@DBUS_LIBS@ \
++	$(libibus) \
++	$(NULL)
++libim_ibus_la_LDFLAGS = \
++	-avoid-version \
++	-module \
++	$(NULL)
++
++$(libibus):
++	(cd $(top_builddir)/src; make )
++
++EXTRA_DIST = \
++	$(NULL)
++
++test: all
++	GTK_IM_MODULE=ibus gedit
++
++-include $(top_srcdir)/git.mk
+diff --git a/client/gtk4/ibusim.c b/client/gtk4/ibusim.c
+new file mode 100644
+index 00000000..5ecf9778
+--- /dev/null
++++ b/client/gtk4/ibusim.c
+@@ -0,0 +1,52 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et ts=4: */
++/* ibus - The Input Bus
++ * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2008-2020 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
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
++ * USA
++ */
++
++#include <gtk/gtk.h>
++#include <gtk/gtkimmodule.h>
++#include <ibus.h>
++#include "ibusimcontext.h"
++
++G_MODULE_EXPORT void
++g_io_im_ibus_load (GTypeModule *type_module)
++{
++    static gboolean inited = FALSE;
++
++    if (!inited) {
++        ibus_init ();
++        ibus_im_context_register_type (type_module);
++        g_io_extension_point_implement ("gtk-im-module",
++                                        IBUS_TYPE_IM_CONTEXT,
++                                        "ibus",
++                                        50);
++        inited = TRUE;
++    }
++    /* make module resident */
++    g_type_module_use (type_module);
++}
++
++G_MODULE_EXPORT void
++g_io_im_ibus_unload (GTypeModule *type_module)
++{
++    g_type_module_unuse (type_module);
++}
++
+diff --git a/client/gtk4/ibusimcontext.c b/client/gtk4/ibusimcontext.c
+new file mode 120000
+index 00000000..41896f05
+--- /dev/null
++++ b/client/gtk4/ibusimcontext.c
+@@ -0,0 +1 @@
++../gtk2/ibusimcontext.c
+\ No newline at end of file
+diff --git a/client/gtk4/ibusimcontext.h b/client/gtk4/ibusimcontext.h
+new file mode 120000
+index 00000000..29759883
+--- /dev/null
++++ b/client/gtk4/ibusimcontext.h
+@@ -0,0 +1 @@
++../gtk2/ibusimcontext.h
+\ No newline at end of file
+diff --git a/configure.ac b/configure.ac
+index 9ed5cb66..ffea3317 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -193,6 +193,15 @@ AC_ARG_ENABLE(gtk3,
+ )
+ AM_CONDITIONAL([ENABLE_GTK3], [test x"$enable_gtk3" = x"yes"])
+ 
++# --enable-gtk4 option.
++AC_ARG_ENABLE(gtk4,
++    AS_HELP_STRING([--enable-gtk4],
++                   [Build gtk4 im module]),
++    [enable_gtk4=$enableval],
++    [enable_gtk4=no]
++)
++AM_CONDITIONAL([ENABLE_GTK4], [test x"$enable_gtk4" = x"yes"])
++
+ # --disable-xim option.
+ AC_ARG_ENABLE(xim,
+     AS_HELP_STRING([--disable-xim],
+@@ -257,6 +266,18 @@ if test x"$enable_gdk3_wayland" != x"yes"; then
+ fi
+ AM_CONDITIONAL([ENABLE_GDK3_WAYLAND], [test x"$enable_gdk3_wayland" = x"yes"])
+ 
++if test x"$enable_gtk4" = x"yes"; then
++    # check for gtk4
++    PKG_CHECK_MODULES(GTK4, [
++        gtk4
++    ])
++
++    gtk4_binary_version=`$PKG_CONFIG --variable=gtk_binary_version gtk4`
++    GTK4_IM_MODULEDIR="$libdir"/gtk-4.0/$gtk4_binary_version/immodules
++else
++    enable_gtk4="no (disabled, use --enable-gtk4 to enable)"
++fi
++
+ if test x"$enable_xim" = x"yes"; then
+     # Check for x11
+     PKG_CHECK_MODULES(X11, [
+@@ -478,6 +499,14 @@ AC_ARG_WITH(gtk3-im-module-dir,
+ )
+ AC_SUBST(GTK3_IM_MODULEDIR)
+ 
++# Define gtk4 immodule dir.
++AC_ARG_WITH(gtk4-im-module-dir,
++    AS_HELP_STRING([--with-gtk4-im-module-dir[=DIR]],
++        [Select gtk4 immodule dir]),
++    GTK4_IM_MODULEDIR=$with_gtk4_im_module_dir
++)
++AC_SUBST(GTK4_IM_MODULEDIR)
++
+ if test x"$enable_python" = x"yes"; then
+     # Check for dbus-python.
+     AC_ARG_ENABLE(dbus-python-check,
+@@ -737,6 +766,7 @@ ibus.spec
+ client/Makefile
+ client/gtk2/Makefile
+ client/gtk3/Makefile
++client/gtk4/Makefile
+ client/x11/Makefile
+ client/wayland/Makefile
+ src/Makefile
+@@ -786,8 +816,10 @@ Build options:
+   Enable python2                $enable_python2
+   Gtk2 immodule dir             $GTK2_IM_MODULEDIR
+   Gtk3 immodule dir             $GTK3_IM_MODULEDIR
++  Gtk4 immodule dir             $GTK4_IM_MODULEDIR
+   Build gtk2 immodule           $enable_gtk2
+   Build gtk3 immodule           $enable_gtk3
++  Build gtk4 immodule           $enable_gtk4
+   Build XIM agent server        $enable_xim
+   Build wayland support         $enable_wayland
+   Build gdk3 wayland support    $enable_gdk3_wayland
+-- 
+2.28.0
+
+From d0a47c3c82b1a0c263ff8b95f9cf5c0a5d1f228d Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Fri, 8 Jan 2021 18:48:13 +0900
+Subject: [PATCH] client/gtk2: Add XTranslateCoordinates for GTK4 X11
+ coordinate
+
+GdkWindow had the absolute coordiante in the private class in GTK3
+but the absolute coordiane no longer exists in GTK4 for Wayland.
+Now get the toplevel window coordiante for the warkaround in GTK4
+but the coordiante of the inner widgets is no longer available too.
+
+BUG=https://gitlab.gnome.org/GNOME/gtk/-/issues/3024#note_987835
+---
+ client/gtk2/ibusimcontext.c | 47 ++++++++++++++++++++++++-------------
+ 1 file changed, 31 insertions(+), 16 deletions(-)
+
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index a23fc2e3..e153081d 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -39,6 +39,13 @@
+ #endif
+ #endif
+ 
++#ifdef GDK_WINDOWING_X11
++#if GTK_CHECK_VERSION (3, 98, 4)
++#include <gdk/x11/gdkx.h>
++#include <X11/Xlib.h>
++#endif
++#endif
++
+ #if !GTK_CHECK_VERSION (2, 91, 0)
+ #  define DEPRECATED_GDK_KEYSYMS 1
+ #endif
+@@ -1462,24 +1469,33 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+ #endif
+     }
+ 
+-#if GTK_CHECK_VERSION (3, 93, 0)
+-    {
+ #if GTK_CHECK_VERSION (3, 98, 4)
++#ifdef GDK_WINDOWING_X11
++    GdkDisplay *display = gtk_widget_get_display (ibusimcontext->client_window);
++    if (GDK_IS_X11_DISPLAY (display)) {
++        Display *xdisplay = gdk_x11_display_get_xdisplay (display);
++        Window root_window = gdk_x11_display_get_xrootwindow (display);
+         GtkNative *native = gtk_widget_get_native (
+                 ibusimcontext->client_window);
+-        GtkRoot *root = gtk_widget_get_root (ibusimcontext->client_window);
+-        double nx, ny;
+-        double px, py;
+-        gtk_native_get_surface_transform (native, &nx, &ny);
+-        px = (double)area.x + ibusimcontext->x - nx;
+-        py = (double)area.y + ibusimcontext->y - ny;
+-        gtk_widget_translate_coordinates (ibusimcontext->client_window,
+-                                          (GtkWidget *)root,
+-                                          px, py,
+-                                          &px, &py);
+-        area.x = (int)px;
+-        area.y = (int)py;
+-#else
++        GdkSurface *surface = gtk_native_get_surface (native);
++        /* The window is the toplevel window but not the inner text widget.
++         * Unfortunatelly GTK4 cannot get the coordinate of the text widget.
++         */
++        Window window = gdk_x11_surface_get_xid (surface);
++        Window child;
++        int x, y;
++        XTranslateCoordinates (xdisplay, window, root_window,
++                               0, 0, &x, &y, &child);
++        XWindowAttributes xwa;
++        XGetWindowAttributes (xdisplay, window, &xwa);
++        area.x = x - xwa.x + area.x;
++        area.y = y - xwa.y + area.y;
++        area.width = xwa.width;
++        area.height = xwa.height;
++    }
++#endif
++#elif GTK_CHECK_VERSION (3, 93, 0)
++    {
+         GtkNative *native = gtk_widget_get_native (
+                 ibusimcontext->client_window);
+         GdkSurface *surface = gtk_native_get_surface (native);
+@@ -1488,7 +1504,6 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+         gdk_surface_get_position (surface, &root_x, &root_y);
+         area.x += root_x;
+         area.y += root_y;
+-#endif
+     }
+ #else
+     gdk_window_get_root_coords (ibusimcontext->client_window,
+-- 
+2.28.0
+
+From ef4c5c7ef790ce1f80e94a5463e110ed4bae254e Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 20 Jan 2021 17:26:47 +0900
+Subject: [PATCH] setup: Search engine name directly
+
+Currently users can search language names only in the language list
+in ibus-setup. After a language is selected and open, engine names can
+be searched.
+Now engine names also can be searched in the language list.
+---
+ setup/enginedialog.py | 40 +++++++++++++++++++++-------------------
+ 1 file changed, 21 insertions(+), 19 deletions(-)
+
+diff --git a/setup/enginedialog.py b/setup/enginedialog.py
+index 72deada8..e1c322bf 100644
+--- a/setup/enginedialog.py
++++ b/setup/enginedialog.py
+@@ -4,7 +4,7 @@
+ # ibus - The Input Bus
+ #
+ # Copyright (c) 2015 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2015-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2015-2021 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ # Copyright (c) 2013-2015 Red Hat, Inc.
+ #
+ # This program is free software; you can redistribute it and/or
+@@ -112,15 +112,20 @@ class EngineDialog(Gtk.Dialog):
+             return False
+         if self.__filter_word == None:
+             return True
++        if row.back:
++            return True
+ 
+-        name = row.name.lower()
+-        untrans = row.untrans.lower()
+-        if self.__filter_word != None:
+-            word = self.__filter_word.lower()
+-            if name.startswith(word):
+-                return True
+-            if untrans.startswith(word):
+-                return True
++        word = self.__filter_word.lower()
++        if word in row.name.lower():
++            return True
++        if word in row.untrans.lower():
++            return True
++        if row.lang_info and row.name in self.__engines_for_lang:
++            for row_e in self.__engines_for_lang[row.name]:
++                if word in row_e.name.lower():
++                    return True
++                if word in row_e.untrans.lower():
++                    return True
+         return False
+ 
+ 
+@@ -129,11 +134,9 @@ class EngineDialog(Gtk.Dialog):
+             self.__show_more()
+             return
+         if row.back:
+-            self.__filter_entry.set_text('')
+             self.__show_lang_rows()
+             return
+         if row.lang_info:
+-            self.__filter_entry.set_text('')
+             self.__show_engines_for_lang(row)
+             return
+ 
+@@ -232,6 +235,7 @@ class EngineDialog(Gtk.Dialog):
+         description = i18n.gettext_engine_description(engine)
+         row = self.__list_box_row_new(longname)
+         row.untrans = engine.get_longname()
++        row.rank = engine.get_rank()
+         row.set_tooltip_text(description)
+         row.engine = engine
+         widget = self.__padded_label_new(longname,
+@@ -260,16 +264,13 @@ class EngineDialog(Gtk.Dialog):
+         lang = row.name
+ 
+         def cmp_engine(a, b):
+-            if a.get_rank() == b.get_rank():
+-                a_longname = i18n.gettext_engine_longname(a)
+-                b_longname = i18n.gettext_engine_longname(b)
+-                return locale.strcoll(a_longname, b_longname)
+-            return int(b.get_rank() - a.get_rank())
++            if a.rank == b.rank:
++                return locale.strcoll(a.name, b.name)
++            return int(b.rank - a.rank)
+ 
+         self.__engines_for_lang[lang].sort(
+                 key = functools.cmp_to_key(cmp_engine))
+-        for e in self.__engines_for_lang[lang]:
+-            row = self.__engine_row_new(e)
++        for row in self.__engines_for_lang[lang]:
+             self.__list.add(row)
+ 
+ 
+@@ -329,7 +330,8 @@ class EngineDialog(Gtk.Dialog):
+             if l not in self.__engines_for_lang:
+                 self.__engines_for_lang[l] = []
+             i18n.init_textdomain(e.get_textdomain())
+-            self.__engines_for_lang[l].append(e)
++            row = self.__engine_row_new(e)
++            self.__engines_for_lang[l].append(row)
+ 
+             # Retrieve Untranslated language names.
+             untrans = IBus.get_untranslated_language_name(e.get_language())
+-- 
+2.28.0
+

diff --git a/ibus.spec b/ibus.spec
index 56b637f..b5f74f2 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -12,13 +12,25 @@
 # for bytecompile in %%{_datadir}/ibus/setup
 %global __python %{__python3}
 
+%if (0%{?fedora} > 33 || 0%{?rhel} > 8)
+%bcond_without gtk4
+%else
+%bcond_with    gtk4
+%endif
+
 %if %with_pkg_config
 %{!?gtk2_binary_version: %global gtk2_binary_version %(pkg-config  --variable=gtk_binary_version gtk+-2.0)}
 %{!?gtk3_binary_version: %global gtk3_binary_version %(pkg-config  --variable=gtk_binary_version gtk+-3.0)}
+%if %{with gtk4}
+%{!?gtk4_binary_version: %global gtk4_binary_version %(pkg-config  --variable=gtk_binary_version gtk4)}
+%else
+%{!?gtk4_binary_version: %global gtk4_binary_version ?.?.?}
+%endif
 %global glib_ver %([ -a %{_libdir}/pkgconfig/glib-2.0.pc ] && pkg-config --modversion glib-2.0 | cut -d. -f 1,2 || echo -n "999")
 %else
 %{!?gtk2_binary_version: %global gtk2_binary_version ?.?.?}
 %{!?gtk3_binary_version: %global gtk3_binary_version ?.?.?}
+%{!?gtk4_binary_version: %global gtk4_binary_version ?.?.?}
 %global glib_ver 0
 %endif
 
@@ -26,7 +38,7 @@
 
 Name:           ibus
 Version:        1.5.23
-Release:        2%{?dist}
+Release:        3%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPLv2+
 URL:            https://github.com/ibus/%name/wiki
@@ -44,6 +56,9 @@ BuildRequires:  libtool
 BuildRequires:  glib2-doc
 BuildRequires:  gtk2-devel
 BuildRequires:  gtk3-devel
+%if %{with gtk4}
+BuildRequires:  gtk4-devel
+%endif
 BuildRequires:  dbus-glib-devel
 BuildRequires:  dbus-python-devel >= %{dbus_python_version}
 BuildRequires:  desktop-file-utils
@@ -103,12 +118,6 @@ Requires:               %{_sbindir}/alternatives
 Requires(post):         %{_sbindir}/alternatives
 Requires(postun):       %{_sbindir}/alternatives
 
-%if (0%{?fedora} > 29 || 0%{?rhel} > 8)
-# Obsoletes ibus-xkbc by ibus xkb engine
-Provides: ibus-xkbc = 1.3.4
-Obsoletes: ibus-xkbc < 1.3.4
-%endif
-
 %global _xinputconf %{_sysconfdir}/X11/xinit/xinput.d/ibus.conf
 
 %description
@@ -150,6 +159,17 @@ Requires(post): glib2 >= %{glib_ver}
 %description gtk3
 This package contains IBus IM module for GTK3
 
+%if %{with gtk4}
+%package gtk4
+Summary:        IBus IM module for GTK4
+Requires:       %{name}-libs%{?_isa}   = %{version}-%{release}
+Requires:       glib2 >= %{glib_ver}
+Requires(post): glib2 >= %{glib_ver}
+
+%description gtk4
+This package contains IBus IM module for GTK4
+%endif
+
 %package setup
 Summary:        IBus setup utility
 Requires:       %{name} = %{version}-%{release}
@@ -157,7 +177,7 @@ Requires:       %{name} = %{version}-%{release}
 Requires:       python3-gobject
 BuildRequires:  gobject-introspection-devel
 BuildRequires:  pygobject3-devel
-BuildRequires: make
+BuildRequires:  make
 BuildArch:      noarch
 
 %description setup
@@ -245,6 +265,8 @@ the functionality of the installed %{name} package.
 %autosetup -S git
 # cp client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c || :
 # cp client/gtk2/ibusim.c client/gtk3/ibusim.c || :
+# cp client/gtk2/ibusimcontext.c client/gtk4/ibusimcontext.c || :
+cp client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c || :
 
 
 # prep test
@@ -256,6 +278,11 @@ do
         abort
     fi
 done
+diff client/gtk2/ibusimcontext.c client/gtk4/ibusimcontext.c
+if test $? -ne 0 ; then
+    echo "Have to copy ibusimcontext.c into client/gtk4"
+    abort
+fi
 
 %build
 #autoreconf -f -i -v
@@ -266,6 +293,9 @@ autoreconf -f -i -v
     --disable-static \
     --enable-gtk2 \
     --enable-gtk3 \
+%if %{with gtk4}
+    --enable-gtk4 \
+%endif
     --enable-xim \
     --enable-gtk-doc \
     --enable-surrounding-text \
@@ -288,6 +318,9 @@ make install DESTDIR=$RPM_BUILD_ROOT INSTALL='install -p'
 rm -f $RPM_BUILD_ROOT%{_libdir}/libibus-*%{ibus_api_version}.la
 rm -f $RPM_BUILD_ROOT%{_libdir}/gtk-2.0/%{gtk2_binary_version}/immodules/im-ibus.la
 rm -f $RPM_BUILD_ROOT%{_libdir}/gtk-3.0/%{gtk3_binary_version}/immodules/im-ibus.la
+%if %{with gtk4}
+rm -f $RPM_BUILD_ROOT%{_libdir}/gtk-4.0/%{gtk4_binary_version}/immodules/libim-ibus.la
+%endif
 
 # install man page
 for S in %{SOURCE2}
@@ -398,6 +431,11 @@ dconf update || :
 %files gtk3
 %{_libdir}/gtk-3.0/%{gtk3_binary_version}/immodules/im-ibus.so
 
+%if %{with gtk4}
+%files gtk4
+%{_libdir}/gtk-4.0/%{gtk4_binary_version}/immodules/libim-ibus.so
+%endif
+
 # The setup package won't include icon files so that
 # gtk-update-icon-cache is executed in the main package only one time.
 %files setup
@@ -452,6 +490,11 @@ dconf update || :
 %{_datadir}/installed-tests/ibus
 
 %changelog
+* Wed Jan 20 2021 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.23-3
+- Enable IM gtk4 module
+- Fix to rename xkb:de::ger to sync xkeyboard-config
+- Enhance ibus-setup search engine
+
 * Fri Nov 20 2020 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.23-2
 - Bug 1898065 - Fix build failure of emoji-*.dict with CLDR 38
 - Fix build failure with Vala 0.50

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-05-31  2:07 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31  2:07 [rpms/ibus] autotool: Enable IM gtk4 module Takao Fujiwara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox