Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fcitx5-gtk for openSUSE:Factory 
checked in at 2023-03-19 00:31:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fcitx5-gtk (Old)
 and      /work/SRC/openSUSE:Factory/.fcitx5-gtk.new.31432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fcitx5-gtk"

Sun Mar 19 00:31:18 2023 rev:9 rq:1072806 version:5.0.22

Changes:
--------
--- /work/SRC/openSUSE:Factory/fcitx5-gtk/fcitx5-gtk.changes    2022-12-02 
13:13:12.909877575 +0100
+++ /work/SRC/openSUSE:Factory/.fcitx5-gtk.new.31432/fcitx5-gtk.changes 
2023-03-19 00:31:22.316317130 +0100
@@ -1,0 +2,8 @@
+Fri Mar 17 17:25:41 UTC 2023 - Dirk Müller <dmuel...@suse.com>
+
+- update to 5.0.22:
+  * Implement notify-focus-out signal
+  * Change GtkIMContext.reset to always commit preedit
+  * preedit when focus out need to happen before has_focus set
+
+-------------------------------------------------------------------

Old:
----
  fcitx5-gtk-5.0.21.tar.xz

New:
----
  fcitx5-gtk-5.0.22.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fcitx5-gtk.spec ++++++
--- /var/tmp/diff_new_pack.cyhvd9/_old  2023-03-19 00:31:22.932320052 +0100
+++ /var/tmp/diff_new_pack.cyhvd9/_new  2023-03-19 00:31:22.936320071 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package fcitx5-gtk
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           fcitx5-gtk
-Version:        5.0.21
+Version:        5.0.22
 Release:        0
 Summary:        Gtk im module for fcitx5 and glib based dbus client library
 License:        LGPL-2.1-or-later

++++++ fcitx5-gtk-5.0.21.tar.xz -> fcitx5-gtk-5.0.22.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/CMakeLists.txt 
new/fcitx5-gtk-5.0.22/CMakeLists.txt
--- old/fcitx5-gtk-5.0.21/CMakeLists.txt        2022-11-24 12:33:33.205390200 
+0100
+++ new/fcitx5-gtk-5.0.22/CMakeLists.txt        2023-03-11 05:01:57.579068200 
+0100
@@ -1,5 +1,5 @@
 cmake_minimum_required(VERSION 3.6)
-project(fcitx5-gtk VERSION 5.0.21)
+project(fcitx5-gtk VERSION 5.0.22)
 
 find_package(ECM REQUIRED 1.0.0)
 set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" 
${CMAKE_MODULE_PATH})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/fcitx-gclient/fcitxgclient.c 
new/fcitx5-gtk-5.0.22/fcitx-gclient/fcitxgclient.c
--- old/fcitx5-gtk-5.0.21/fcitx-gclient/fcitxgclient.c  2022-11-12 
18:57:28.047163500 +0100
+++ new/fcitx5-gtk-5.0.22/fcitx-gclient/fcitxgclient.c  2022-12-20 
16:06:57.910911800 +0100
@@ -162,6 +162,8 @@
     "      <arg name=\"state\" type=\"u\"/>\n"
     "      <arg name=\"type\" type=\"b\"/>\n"
     "    </signal>\n"
+    "    <signal name=\"NotifyFocusOut\">\n"
+    "    </signal>\n"
     "  </interface>\n"
     "</node>\n";
 
@@ -175,6 +177,7 @@
     UPDATED_FORMATTED_PREEDIT_SIGNAL,
     UPDATE_CLIENT_SIDE_UI_SIGNAL,
     CURRENT_IM_SIGNAL,
+    NOTIFY_FOCUS_OUT_SIGNAL,
     LAST_SIGNAL
 };
 
@@ -356,6 +359,15 @@
         "current-im", FCITX_G_TYPE_CLIENT, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
         fcitx_marshall_VOID__STRING_STRING_STRING, G_TYPE_NONE, 3,
         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+    /**
+     * FcitxGClient::notify-focus-out:
+     * @self: A #FcitxGClient
+     *
+     * Emit when focus out happens on server side
+     */
+    signals[NOTIFY_FOCUS_OUT_SIGNAL] = g_signal_new(
+        "notify-focus-out", FCITX_G_TYPE_CLIENT, G_SIGNAL_RUN_LAST, 0, NULL,
+        NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
 }
 
 static void fcitx_g_client_init(FcitxGClient *self) {
@@ -1045,6 +1057,8 @@
         g_ptr_array_free(aux_up_strings, TRUE);
         g_ptr_array_free(aux_down_strings, TRUE);
         g_ptr_array_free(candidate_list, TRUE);
+    } else if (g_strcmp0(signal_name, "NotifyFocusOut") == 0) {
+        g_signal_emit(user_data, signals[NOTIFY_FOCUS_OUT_SIGNAL], 0);
     }
 }
 
@@ -1062,7 +1076,7 @@
 
 /**
  * fcitx_g_client_new_with_watcher:
- * @connection: the FcitxGWatcher to be used with this client
+ * @watcher: the FcitxGWatcher to be used with this client
  *
  * New a #FcitxGClient
  *
@@ -1086,7 +1100,7 @@
 }
 
 /**
- * fcitx_g_client_set_display:
+ * fcitx_g_client_set_program:
  * @self: A #FcitxGClient
  * @program: program name
  *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/gtk2/fcitximcontext.cpp 
new/fcitx5-gtk-5.0.22/gtk2/fcitximcontext.cpp
--- old/fcitx5-gtk-5.0.21/gtk2/fcitximcontext.cpp       2022-11-12 
19:02:35.280998700 +0100
+++ new/fcitx5-gtk-5.0.22/gtk2/fcitximcontext.cpp       2023-02-02 
21:40:20.115213900 +0100
@@ -17,6 +17,7 @@
 #include "fcitx-gclient/fcitxgclient.h"
 #include "fcitx-gclient/fcitxgwatcher.h"
 #include "fcitximcontext.h"
+#include "utils.h"
 #include <gdk/gdk.h>
 #include <gdk/gdkkeysyms.h>
 #include <gdk/gdkx.h>
@@ -29,32 +30,15 @@
 #define NEW_GDK_WINDOW_GET_DISPLAY
 #endif
 
-static constexpr uint32_t HandledMask = (1 << 24);
-static constexpr uint32_t IgnoredMask = (1 << 25);
-static constexpr unsigned int MAX_CACHED_EVENTS = 30;
+using namespace fcitx::gtk;
 
 extern "C" {
 
-static bool get_boolean_env(const char *name, bool defval) {
-    const char *value = getenv(name);
-
-    if (value == nullptr) {
-        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;
-    }
-
-    return true;
-}
-
 struct _FcitxIMContext {
     GtkIMContext parent;
 
     GdkWindow *client_window;
+    gulong button_press_signal;
     GdkRectangle area;
     FcitxGClient *client;
     GtkIMContext *slave;
@@ -66,6 +50,7 @@
     gboolean support_surrounding_text;
     gboolean is_inpreedit;
     gchar *preedit_string;
+    gchar *commit_preedit_string;
     gchar *surrounding_text;
     int cursor_pos;
     guint64 capability_from_toolkit;
@@ -107,6 +92,9 @@
                                                 PangoAttrList **attrs,
                                                 gint *cursor_pos);
 
+static void fcitx_im_context_commit_string(FcitxIMContext *context,
+                                           const gchar *str);
+static void fcitx_im_context_commit_preedit(FcitxIMContext *context);
 static gboolean _set_cursor_location_internal(FcitxIMContext *fcitxcontext);
 static gboolean _defer_request_surrounding_text(FcitxIMContext *fcitxcontext);
 static void _slave_commit_cb(GtkIMContext *slave, gchar *string,
@@ -136,6 +124,8 @@
                                                           GPtrArray *array,
                                                           int cursor_pos,
                                                           void *user_data);
+static void _fcitx_im_context_notify_focus_out_cb(FcitxGClient *client,
+                                                  void *user_data);
 static void _fcitx_im_context_process_key_cb(GObject *source_object,
                                              GAsyncResult *res,
                                              gpointer user_data);
@@ -220,25 +210,6 @@
     return FCITX_IM_CONTEXT(obj);
 }
 
-static gboolean check_app_name(const gchar *pattern) {
-    bool result = FALSE;
-    const gchar *prgname = g_get_prgname();
-    if (!prgname) {
-        return FALSE;
-    }
-    gchar **p;
-    gchar **apps = g_strsplit(pattern, ",", 0);
-    for (p = apps; *p != NULL; p++) {
-        if (g_regex_match_simple(*p, prgname, (GRegexCompileFlags)0,
-                                 (GRegexMatchFlags)0)) {
-            result = TRUE;
-            break;
-        }
-    }
-    g_strfreev(apps);
-    return result;
-}
-
 ///
 static void fcitx_im_context_class_init(FcitxIMContextClass *klass, gpointer) {
     GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(klass);
@@ -337,6 +308,7 @@
     context->last_anchor_pos = -1;
     context->last_cursor_pos = -1;
     context->preedit_string = NULL;
+    context->commit_preedit_string = NULL;
     context->attrlist = NULL;
     context->last_updated_capability =
         (guint64)fcitx::FcitxCapabilityFlag_SurroundingText;
@@ -402,6 +374,9 @@
     g_signal_connect(context->client, "update-formatted-preedit",
                      G_CALLBACK(_fcitx_im_context_update_formatted_preedit_cb),
                      context);
+    g_signal_connect(context->client, "notify-focus-out",
+                     G_CALLBACK(_fcitx_im_context_notify_focus_out_cb),
+                     context);
 
     context->xkbComposeState =
         xkbComposeTable
@@ -429,6 +404,7 @@
     g_clear_object(&context->client);
 
     g_clear_pointer(&context->preedit_string, g_free);
+    g_clear_pointer(&context->commit_preedit_string, g_free);
     g_clear_pointer(&context->surrounding_text, g_free);
     g_clear_pointer(&context->attrlist, pango_attr_list_unref);
     /* https://github.com/GNOME/glib/blob/main/glib/gqueue.c#L164
@@ -441,15 +417,47 @@
     G_OBJECT_CLASS(parent_class)->finalize(obj);
 }
 
+static gboolean
+fcitx_im_context_button_press_event_cb(GtkWidget *, GdkEventButton *event,
+                                       FcitxIMContext *context) {
+    if (event->button != 1)
+        return FALSE;
+
+    if (!context->has_focus) {
+        return FALSE;
+    }
+
+    fcitx_im_context_reset(GTK_IM_CONTEXT(context));
+    return FALSE;
+}
+
 ///
 static void fcitx_im_context_set_client_window(GtkIMContext *context,
                                                GdkWindow *client_window) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
+
+    GtkWidget *oldwidget = nullptr;
+    if (fcitxcontext->client_window) {
+        gdk_window_get_user_data(fcitxcontext->client_window,
+                                 (gpointer *)&oldwidget);
+    }
+
+    g_clear_signal_handler(&fcitxcontext->button_press_signal, oldwidget);
     g_clear_object(&fcitxcontext->client_window);
     if (!client_window) {
         return;
     }
+
     fcitxcontext->client_window = GDK_WINDOW(g_object_ref(client_window));
+
+    GtkWidget *widget = nullptr;
+    gdk_window_get_user_data(fcitxcontext->client_window, (gpointer *)&widget);
+    /* firefox needs GtkWidget instead of GtkWindow */
+    if (GTK_IS_WIDGET(widget)) {
+        fcitxcontext->button_press_signal = g_signal_connect(
+            widget, "button-press-event",
+            G_CALLBACK(fcitx_im_context_button_press_event_cb), fcitxcontext);
+    }
 }
 
 static gboolean
@@ -571,89 +579,101 @@
     context->attrlist = pango_attr_list_new();
 
     GString *gstr = g_string_new(NULL);
+    GString *commit_gstr = g_string_new(NULL);
 
-    unsigned int i = 0;
-    for (i = 0; i < array->len; i++) {
-        size_t bytelen = strlen(gstr->str);
-        FcitxGPreeditItem *preedit =
-            (FcitxGPreeditItem *)g_ptr_array_index(array, i);
-        const gchar *s = preedit->string;
-        gint type = preedit->type;
-
-        PangoAttribute *pango_attr = NULL;
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Underline)) {
-            pango_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Strike)) {
-            pango_attr = pango_attr_strikethrough_new(true);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Bold)) {
-            pango_attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Italic)) {
-            pango_attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
+    if (array) {
+        for (unsigned int i = 0; i < array->len; i++) {
+            size_t bytelen = strlen(gstr->str);
+            FcitxGPreeditItem *preedit =
+                (FcitxGPreeditItem *)g_ptr_array_index(array, i);
+            const gchar *s = preedit->string;
+            gint type = preedit->type;
+
+            PangoAttribute *pango_attr = NULL;
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Underline)) {
+                pango_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Strike)) {
+                pango_attr = pango_attr_strikethrough_new(true);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Bold)) {
+                pango_attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Italic)) {
+                pango_attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
 
-        if (type & (guint32)fcitx::FcitxTextFormatFlag_HighLight) {
-            gboolean hasColor = false;
-            GdkColor fg;
-            GdkColor bg;
-            memset(&fg, 0, sizeof(GdkColor));
-            memset(&bg, 0, sizeof(GdkColor));
-
-            if (context->client_window) {
-                GtkWidget *widget;
-                gdk_window_get_user_data(context->client_window,
-                                         (gpointer *)&widget);
-                if (GTK_IS_WIDGET(widget)) {
-                    hasColor = true;
-                    GtkStyle *style = gtk_widget_get_style(widget);
-                    fg = style->text[GTK_STATE_SELECTED];
-                    bg = style->base[GTK_STATE_SELECTED];
+            if (type & (guint32)fcitx::FcitxTextFormatFlag_HighLight) {
+                gboolean hasColor = false;
+                GdkColor fg;
+                GdkColor bg;
+                memset(&fg, 0, sizeof(GdkColor));
+                memset(&bg, 0, sizeof(GdkColor));
+
+                if (context->client_window) {
+                    GtkWidget *widget;
+                    gdk_window_get_user_data(context->client_window,
+                                             (gpointer *)&widget);
+                    if (GTK_IS_WIDGET(widget)) {
+                        hasColor = true;
+                        GtkStyle *style = gtk_widget_get_style(widget);
+                        fg = style->text[GTK_STATE_SELECTED];
+                        bg = style->base[GTK_STATE_SELECTED];
+                    }
                 }
-            }
 
-            if (!hasColor) {
-                fg.red = 0xffff;
-                fg.green = 0xffff;
-                fg.blue = 0xffff;
-                bg.red = 0x43ff;
-                bg.green = 0xacff;
-                bg.blue = 0xe8ff;
-            }
+                if (!hasColor) {
+                    fg.red = 0xffff;
+                    fg.green = 0xffff;
+                    fg.blue = 0xffff;
+                    bg.red = 0x43ff;
+                    bg.green = 0xacff;
+                    bg.blue = 0xe8ff;
+                }
 
-            pango_attr = pango_attr_foreground_new(fg.red, fg.green, fg.blue);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-            pango_attr = pango_attr_background_new(bg.red, bg.green, bg.blue);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
+                pango_attr =
+                    pango_attr_foreground_new(fg.red, fg.green, fg.blue);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+                pango_attr =
+                    pango_attr_background_new(bg.red, bg.green, bg.blue);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            gstr = g_string_append(gstr, s);
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_DontCommit) == 0) {
+                commit_gstr = g_string_append(commit_gstr, s);
+            }
         }
-        gstr = g_string_append(gstr, s);
     }
 
     gchar *str = g_string_free(gstr, FALSE);
 
     context->preedit_string = str;
+    context->commit_preedit_string = g_string_free(commit_gstr, FALSE);
     context->cursor_pos = g_utf8_pointer_to_offset(str, str + cursor_pos);
 
     if (context->preedit_string != NULL && context->preedit_string[0] == 0) {
         g_clear_pointer(&context->preedit_string, g_free);
     }
+    if (context->commit_preedit_string != NULL &&
+        context->commit_preedit_string[0] == 0) {
+        g_clear_pointer(&context->commit_preedit_string, g_free);
+    }
 }
 
 static void _fcitx_im_context_update_formatted_preedit_cb(FcitxGClient *,
@@ -675,6 +695,7 @@
 
         g_clear_pointer(&context->preedit_string, g_free);
     }
+    g_clear_pointer(&context->commit_preedit_string, g_free);
     g_clear_pointer(&context->attrlist, pango_attr_list_unref);
 
     if (context->use_preedit) {
@@ -703,6 +724,16 @@
     }
 }
 
+static void _fcitx_im_context_notify_focus_out_cb(FcitxGClient *,
+                                                  void *user_data) {
+    FcitxIMContext *context = FCITX_IM_CONTEXT(user_data);
+    if (!context->has_focus) {
+        return;
+    }
+
+    fcitx_im_context_commit_preedit(context);
+}
+
 ///
 static void fcitx_im_context_focus_in(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
@@ -753,6 +784,29 @@
     return;
 }
 
+static void fcitx_im_context_commit_string(FcitxIMContext *context,
+                                           const gchar *str) {
+    g_signal_emit(context, _signal_commit_id, 0, str);
+
+    // Better request surrounding after commit.
+    gdk_threads_add_idle_full(
+        G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)_defer_request_surrounding_text,
+        g_object_ref(context), (GDestroyNotify)g_object_unref);
+}
+
+static void fcitx_im_context_commit_preedit(FcitxIMContext *context) {
+    if (!context->has_focus) {
+        return;
+    }
+
+    if (context->commit_preedit_string) {
+        fcitx_im_context_commit_string(context, 
context->commit_preedit_string);
+    }
+
+    _fcitx_im_context_update_formatted_preedit_cb(context->client, nullptr, 0,
+                                                  context);
+}
+
 static void fcitx_im_context_focus_out(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
 
@@ -764,6 +818,8 @@
                                  (gpointer *)&_focus_im_context);
     _focus_im_context = NULL;
 
+    fcitx_im_context_commit_preedit(fcitxcontext);
+
     fcitxcontext->has_focus = false;
     fcitxcontext->last_key_code = 0;
     fcitxcontext->last_is_release = false;
@@ -772,13 +828,6 @@
         fcitx_g_client_focus_out(fcitxcontext->client);
     }
 
-    fcitxcontext->cursor_pos = 0;
-    if (fcitxcontext->preedit_string != NULL) {
-        g_clear_pointer(&fcitxcontext->preedit_string, g_free);
-        g_signal_emit(fcitxcontext, _signal_preedit_changed_id, 0);
-        g_signal_emit(fcitxcontext, _signal_preedit_end_id, 0);
-    }
-
     gtk_im_context_focus_out(fcitxcontext->slave);
 
     return;
@@ -982,6 +1031,7 @@
         }
         flags |= (guint64)fcitx::FcitxCapabilityFlag_KeyEventOrderFix;
         flags |= (guint64)fcitx::FcitxCapabilityFlag_ReportKeyRepeat;
+        flags |= (guint64)fcitx::FcitxCapabilityFlag_ClientUnfocusCommit;
 
         // always run this code against all gtk version
         // seems visibility != PASSWORD hint
@@ -1021,6 +1071,8 @@
 static void fcitx_im_context_reset(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
 
+    fcitx_im_context_commit_preedit(fcitxcontext);
+
     if (fcitx_g_client_is_valid(fcitxcontext->client)) {
         fcitx_g_client_reset(fcitxcontext->client);
     }
@@ -1125,12 +1177,7 @@
 void _fcitx_im_context_commit_string_cb(FcitxGClient *, char *str,
                                         void *user_data) {
     FcitxIMContext *context = FCITX_IM_CONTEXT(user_data);
-    g_signal_emit(context, _signal_commit_id, 0, str);
-
-    // Better request surrounding after commit.
-    gdk_threads_add_idle_full(
-        G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)_defer_request_surrounding_text,
-        g_object_ref(context), (GDestroyNotify)g_object_unref);
+    fcitx_im_context_commit_string(context, str);
 }
 
 void _fcitx_im_context_forward_key_cb(FcitxGClient *, guint keyval, guint 
state,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/gtk2/utils.cpp 
new/fcitx5-gtk-5.0.22/gtk2/utils.cpp
--- old/fcitx5-gtk-5.0.21/gtk2/utils.cpp        1970-01-01 01:00:00.000000000 
+0100
+++ new/fcitx5-gtk-5.0.22/gtk2/utils.cpp        2023-03-19 00:31:23.204321343 
+0100
@@ -0,0 +1 @@
+symbolic link to ../gtk3/utils.cpp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/gtk2/utils.h 
new/fcitx5-gtk-5.0.22/gtk2/utils.h
--- old/fcitx5-gtk-5.0.21/gtk2/utils.h  1970-01-01 01:00:00.000000000 +0100
+++ new/fcitx5-gtk-5.0.22/gtk2/utils.h  2023-03-19 00:31:23.212321381 +0100
@@ -0,0 +1 @@
+symbolic link to ../gtk3/utils.h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/gtk3/fcitximcontext.cpp 
new/fcitx5-gtk-5.0.22/gtk3/fcitximcontext.cpp
--- old/fcitx5-gtk-5.0.21/gtk3/fcitximcontext.cpp       2022-11-12 
18:39:05.962027800 +0100
+++ new/fcitx5-gtk-5.0.22/gtk3/fcitximcontext.cpp       2023-02-02 
21:40:40.028492000 +0100
@@ -38,48 +38,13 @@
 
 using namespace fcitx::gtk;
 
-static constexpr uint32_t HandledMask = (1 << 24);
-static constexpr uint32_t IgnoredMask = (1 << 25);
-static constexpr unsigned int MAX_CACHED_EVENTS = 30;
-
-static const uint64_t purpose_related_capability =
-    fcitx::FcitxCapabilityFlag_Alpha | fcitx::FcitxCapabilityFlag_Digit |
-    fcitx::FcitxCapabilityFlag_Number | fcitx::FcitxCapabilityFlag_Dialable |
-    fcitx::FcitxCapabilityFlag_Url | fcitx::FcitxCapabilityFlag_Email |
-    fcitx::FcitxCapabilityFlag_Password;
-
-static const uint64_t hints_related_capability =
-    fcitx::FcitxCapabilityFlag_SpellCheck |
-    fcitx::FcitxCapabilityFlag_NoSpellCheck |
-    fcitx::FcitxCapabilityFlag_WordCompletion |
-    fcitx::FcitxCapabilityFlag_Lowercase |
-    fcitx::FcitxCapabilityFlag_Uppercase |
-    fcitx::FcitxCapabilityFlag_UppercaseWords |
-    fcitx::FcitxCapabilityFlag_UppwercaseSentences |
-    fcitx::FcitxCapabilityFlag_NoOnScreenKeyboard;
-
 extern "C" {
 
-static bool get_boolean_env(const char *name, bool defval) {
-    const char *value = getenv(name);
-
-    if (value == nullptr) {
-        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;
-    }
-
-    return true;
-}
-
 struct _FcitxIMContext {
     GtkIMContext parent;
 
     GdkWindow *client_window;
+    gulong button_press_signal;
     GdkRectangle area;
     FcitxGClient *client;
     GtkIMContext *slave;
@@ -92,6 +57,7 @@
     gboolean is_inpreedit;
     gboolean is_wayland;
     gchar *preedit_string;
+    gchar *commit_preedit_string;
     gchar *surrounding_text;
     int cursor_pos;
     guint64 capability_from_toolkit;
@@ -135,6 +101,9 @@
                                                 PangoAttrList **attrs,
                                                 gint *cursor_pos);
 
+static void fcitx_im_context_commit_string(FcitxIMContext *context,
+                                           const gchar *str);
+static void fcitx_im_context_commit_preedit(FcitxIMContext *context);
 static gboolean _set_cursor_location_internal(FcitxIMContext *fcitxcontext);
 static gboolean _defer_request_surrounding_text(FcitxIMContext *fcitxcontext);
 static void _slave_commit_cb(GtkIMContext *slave, gchar *string,
@@ -164,6 +133,8 @@
                                                           GPtrArray *array,
                                                           int cursor_pos,
                                                           void *user_data);
+static void _fcitx_im_context_notify_focus_out_cb(FcitxGClient *client,
+                                                  void *user_data);
 static void _fcitx_im_context_process_key_cb(GObject *source_object,
                                              GAsyncResult *res,
                                              gpointer user_data);
@@ -259,25 +230,6 @@
     return FCITX_IM_CONTEXT(obj);
 }
 
-static gboolean check_app_name(const gchar *pattern) {
-    bool result = FALSE;
-    const gchar *prgname = g_get_prgname();
-    if (!prgname) {
-        return FALSE;
-    }
-    gchar **p;
-    gchar **apps = g_strsplit(pattern, ",", 0);
-    for (p = apps; *p != NULL; p++) {
-        if (g_regex_match_simple(*p, prgname, (GRegexCompileFlags)0,
-                                 (GRegexMatchFlags)0)) {
-            result = TRUE;
-            break;
-        }
-    }
-    g_strfreev(apps);
-    return result;
-}
-
 ///
 static void fcitx_im_context_class_init(FcitxIMContextClass *klass, gpointer) {
     GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(klass);
@@ -376,6 +328,7 @@
     context->last_anchor_pos = -1;
     context->last_cursor_pos = -1;
     context->preedit_string = NULL;
+    context->commit_preedit_string = NULL;
     context->attrlist = NULL;
     context->last_updated_capability =
         (guint64)fcitx::FcitxCapabilityFlag_SurroundingText;
@@ -465,6 +418,9 @@
     g_signal_connect(context->client, "update-formatted-preedit",
                      G_CALLBACK(_fcitx_im_context_update_formatted_preedit_cb),
                      context);
+    g_signal_connect(context->client, "notify-focus-out",
+                     G_CALLBACK(_fcitx_im_context_notify_focus_out_cb),
+                     context);
 
     context->xkbComposeState =
         xkbComposeTable
@@ -494,6 +450,7 @@
     g_clear_object(&context->client);
 
     g_clear_pointer(&context->preedit_string, g_free);
+    g_clear_pointer(&context->commit_preedit_string, g_free);
     g_clear_pointer(&context->surrounding_text, g_free);
     g_clear_pointer(&context->attrlist, pango_attr_list_unref);
     /* https://github.com/GNOME/glib/blob/main/glib/gqueue.c#L164
@@ -506,6 +463,20 @@
     G_OBJECT_CLASS(parent_class)->finalize(obj);
 }
 
+static gboolean
+fcitx_im_context_button_press_event_cb(GtkWidget *, GdkEventButton *event,
+                                       FcitxIMContext *context) {
+    if (event->button != 1)
+        return FALSE;
+
+    if (!context->has_focus) {
+        return FALSE;
+    }
+
+    fcitx_im_context_reset(GTK_IM_CONTEXT(context));
+    return FALSE;
+}
+
 ///
 static void fcitx_im_context_set_client_window(GtkIMContext *context,
                                                GdkWindow *client_window) {
@@ -515,6 +486,14 @@
     }
     delete fcitxcontext->candidate_window;
     fcitxcontext->candidate_window = nullptr;
+
+    GtkWidget *oldwidget = nullptr;
+    if (fcitxcontext->client_window) {
+        gdk_window_get_user_data(fcitxcontext->client_window,
+                                 (gpointer *)&oldwidget);
+    }
+
+    g_clear_signal_handler(&fcitxcontext->button_press_signal, oldwidget);
     g_clear_object(&fcitxcontext->client_window);
     if (!client_window) {
         return;
@@ -522,6 +501,15 @@
 
     fcitxcontext->client_window = GDK_WINDOW(g_object_ref(client_window));
 
+    GtkWidget *widget = nullptr;
+    gdk_window_get_user_data(fcitxcontext->client_window, (gpointer *)&widget);
+    /* firefox needs GtkWidget instead of GtkWindow */
+    if (GTK_IS_WIDGET(widget)) {
+        fcitxcontext->button_press_signal = g_signal_connect(
+            widget, "button-press-event",
+            G_CALLBACK(fcitx_im_context_button_press_event_cb), fcitxcontext);
+    }
+
     _fcitx_im_context_set_capability(fcitxcontext, FALSE);
 
     fcitxcontext->candidate_window = new Gtk3InputWindow(
@@ -649,108 +637,124 @@
     context->attrlist = pango_attr_list_new();
 
     GString *gstr = g_string_new(NULL);
+    GString *commit_gstr = g_string_new(NULL);
 
-    unsigned int i = 0;
-    for (i = 0; i < array->len; i++) {
-        size_t bytelen = strlen(gstr->str);
-        FcitxGPreeditItem *preedit =
-            (FcitxGPreeditItem *)g_ptr_array_index(array, i);
-        const gchar *s = preedit->string;
-        gint type = preedit->type;
-
-        PangoAttribute *pango_attr = NULL;
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Underline)) {
-            pango_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Strike)) {
-            pango_attr = pango_attr_strikethrough_new(true);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Bold)) {
-            pango_attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Italic)) {
-            pango_attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-
-        if (type & (guint32)fcitx::FcitxTextFormatFlag_HighLight) {
-            gboolean hasColor = false;
-            GdkColor fg;
-            GdkColor bg;
-            memset(&fg, 0, sizeof(GdkColor));
-            memset(&bg, 0, sizeof(GdkColor));
-
-            if (context->client_window) {
-                GtkWidget *widget;
-                gdk_window_get_user_data(context->client_window,
-                                         (gpointer *)&widget);
-                if (GTK_IS_WIDGET(widget)) {
-                    hasColor = true;
-                    GtkStyleContext *styleContext =
-                        gtk_widget_get_style_context(widget);
-                    GdkRGBA fg_rgba, bg_rgba;
-                    hasColor =
-                        gtk_style_context_lookup_color(
-                            styleContext, "theme_selected_bg_color",
-                            &bg_rgba) &&
-                        gtk_style_context_lookup_color(
-                            styleContext, "theme_selected_fg_color", &fg_rgba);
-
-                    if (hasColor) {
-                        fg.pixel = 0;
-                        fg.red = CLAMP((gint)(fg_rgba.red * 65535), 0, 65535);
-                        fg.green =
-                            CLAMP((gint)(fg_rgba.green * 65535), 0, 65535);
-                        fg.blue = CLAMP((gint)(fg_rgba.blue * 65535), 0, 
65535);
-                        bg.pixel = 0;
-                        bg.red = CLAMP((gint)(bg_rgba.red * 65535), 0, 65535);
-                        bg.green =
-                            CLAMP((gint)(bg_rgba.green * 65535), 0, 65535);
-                        bg.blue = CLAMP((gint)(bg_rgba.blue * 65535), 0, 
65535);
+    if (array) {
+        for (unsigned int i = 0; i < array->len; i++) {
+            size_t bytelen = strlen(gstr->str);
+            FcitxGPreeditItem *preedit =
+                (FcitxGPreeditItem *)g_ptr_array_index(array, i);
+            const gchar *s = preedit->string;
+            gint type = preedit->type;
+
+            PangoAttribute *pango_attr = NULL;
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Underline)) {
+                pango_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Strike)) {
+                pango_attr = pango_attr_strikethrough_new(true);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Bold)) {
+                pango_attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Italic)) {
+                pango_attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+
+            if (type & (guint32)fcitx::FcitxTextFormatFlag_HighLight) {
+                gboolean hasColor = false;
+                GdkColor fg;
+                GdkColor bg;
+                memset(&fg, 0, sizeof(GdkColor));
+                memset(&bg, 0, sizeof(GdkColor));
+
+                if (context->client_window) {
+                    GtkWidget *widget;
+                    gdk_window_get_user_data(context->client_window,
+                                             (gpointer *)&widget);
+                    if (GTK_IS_WIDGET(widget)) {
+                        hasColor = true;
+                        GtkStyleContext *styleContext =
+                            gtk_widget_get_style_context(widget);
+                        GdkRGBA fg_rgba, bg_rgba;
+                        hasColor = gtk_style_context_lookup_color(
+                                       styleContext, "theme_selected_bg_color",
+                                       &bg_rgba) &&
+                                   gtk_style_context_lookup_color(
+                                       styleContext, "theme_selected_fg_color",
+                                       &fg_rgba);
+
+                        if (hasColor) {
+                            fg.pixel = 0;
+                            fg.red =
+                                CLAMP((gint)(fg_rgba.red * 65535), 0, 65535);
+                            fg.green =
+                                CLAMP((gint)(fg_rgba.green * 65535), 0, 65535);
+                            fg.blue =
+                                CLAMP((gint)(fg_rgba.blue * 65535), 0, 65535);
+                            bg.pixel = 0;
+                            bg.red =
+                                CLAMP((gint)(bg_rgba.red * 65535), 0, 65535);
+                            bg.green =
+                                CLAMP((gint)(bg_rgba.green * 65535), 0, 65535);
+                            bg.blue =
+                                CLAMP((gint)(bg_rgba.blue * 65535), 0, 65535);
+                        }
                     }
                 }
-            }
 
-            if (!hasColor) {
-                fg.red = 0xffff;
-                fg.green = 0xffff;
-                fg.blue = 0xffff;
-                bg.red = 0x43ff;
-                bg.green = 0xacff;
-                bg.blue = 0xe8ff;
-            }
+                if (!hasColor) {
+                    fg.red = 0xffff;
+                    fg.green = 0xffff;
+                    fg.blue = 0xffff;
+                    bg.red = 0x43ff;
+                    bg.green = 0xacff;
+                    bg.blue = 0xe8ff;
+                }
 
-            pango_attr = pango_attr_foreground_new(fg.red, fg.green, fg.blue);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-            pango_attr = pango_attr_background_new(bg.red, bg.green, bg.blue);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
+                pango_attr =
+                    pango_attr_foreground_new(fg.red, fg.green, fg.blue);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+                pango_attr =
+                    pango_attr_background_new(bg.red, bg.green, bg.blue);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            gstr = g_string_append(gstr, s);
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_DontCommit) == 0) {
+                commit_gstr = g_string_append(commit_gstr, s);
+            }
         }
-        gstr = g_string_append(gstr, s);
     }
 
     gchar *str = g_string_free(gstr, FALSE);
 
     context->preedit_string = str;
+    context->commit_preedit_string = g_string_free(commit_gstr, FALSE);
     context->cursor_pos = g_utf8_pointer_to_offset(str, str + cursor_pos);
 
     if (context->preedit_string != NULL && context->preedit_string[0] == 0) {
         g_clear_pointer(&context->preedit_string, g_free);
     }
+    if (context->commit_preedit_string != NULL &&
+        context->commit_preedit_string[0] == 0) {
+        g_clear_pointer(&context->commit_preedit_string, g_free);
+    }
 }
 
 static void _fcitx_im_context_update_formatted_preedit_cb(FcitxGClient *,
@@ -772,6 +776,7 @@
 
         g_clear_pointer(&context->preedit_string, g_free);
     }
+    g_clear_pointer(&context->commit_preedit_string, g_free);
     g_clear_pointer(&context->attrlist, pango_attr_list_unref);
 
     if (context->use_preedit) {
@@ -800,6 +805,16 @@
     }
 }
 
+static void _fcitx_im_context_notify_focus_out_cb(FcitxGClient *,
+                                                  void *user_data) {
+    FcitxIMContext *context = FCITX_IM_CONTEXT(user_data);
+    if (!context->has_focus) {
+        return;
+    }
+
+    fcitx_im_context_commit_preedit(context);
+}
+
 ///
 static void fcitx_im_context_focus_in(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
@@ -850,6 +865,29 @@
     return;
 }
 
+static void fcitx_im_context_commit_string(FcitxIMContext *context,
+                                           const gchar *str) {
+    g_signal_emit(context, _signal_commit_id, 0, str);
+
+    // Better request surrounding after commit.
+    gdk_threads_add_idle_full(
+        G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)_defer_request_surrounding_text,
+        g_object_ref(context), (GDestroyNotify)g_object_unref);
+}
+
+static void fcitx_im_context_commit_preedit(FcitxIMContext *context) {
+    if (!context->has_focus) {
+        return;
+    }
+
+    if (context->commit_preedit_string) {
+        fcitx_im_context_commit_string(context, 
context->commit_preedit_string);
+    }
+
+    _fcitx_im_context_update_formatted_preedit_cb(context->client, nullptr, 0,
+                                                  context);
+}
+
 static void fcitx_im_context_focus_out(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
 
@@ -861,6 +899,8 @@
                                  (gpointer *)&_focus_im_context);
     _focus_im_context = NULL;
 
+    fcitx_im_context_commit_preedit(fcitxcontext);
+
     fcitxcontext->has_focus = false;
     fcitxcontext->last_key_code = 0;
     fcitxcontext->last_is_release = false;
@@ -869,13 +909,6 @@
         fcitx_g_client_focus_out(fcitxcontext->client);
     }
 
-    fcitxcontext->cursor_pos = 0;
-    if (fcitxcontext->preedit_string != NULL) {
-        g_clear_pointer(&fcitxcontext->preedit_string, g_free);
-        g_signal_emit(fcitxcontext, _signal_preedit_changed_id, 0);
-        g_signal_emit(fcitxcontext, _signal_preedit_end_id, 0);
-    }
-
     gtk_im_context_focus_out(fcitxcontext->slave);
 
     return;
@@ -1101,6 +1134,7 @@
         }
         flags |= (guint64)fcitx::FcitxCapabilityFlag_KeyEventOrderFix;
         flags |= (guint64)fcitx::FcitxCapabilityFlag_ReportKeyRepeat;
+        flags |= (guint64)fcitx::FcitxCapabilityFlag_ClientUnfocusCommit;
 
         // always run this code against all gtk version
         // seems visibility != PASSWORD hint
@@ -1140,6 +1174,8 @@
 static void fcitx_im_context_reset(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
 
+    fcitx_im_context_commit_preedit(fcitxcontext);
+
     if (fcitx_g_client_is_valid(fcitxcontext->client)) {
         fcitx_g_client_reset(fcitxcontext->client);
     }
@@ -1244,12 +1280,7 @@
 void _fcitx_im_context_commit_string_cb(FcitxGClient *, char *str,
                                         void *user_data) {
     FcitxIMContext *context = FCITX_IM_CONTEXT(user_data);
-    g_signal_emit(context, _signal_commit_id, 0, str);
-
-    // Better request surrounding after commit.
-    gdk_threads_add_idle_full(
-        G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)_defer_request_surrounding_text,
-        g_object_ref(context), (GDestroyNotify)g_object_unref);
+    fcitx_im_context_commit_string(context, str);
 }
 
 void _fcitx_im_context_forward_key_cb(FcitxGClient *, guint keyval, guint 
state,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/gtk3/utils.h 
new/fcitx5-gtk-5.0.22/gtk3/utils.h
--- old/fcitx5-gtk-5.0.21/gtk3/utils.h  2021-01-25 22:23:36.273540700 +0100
+++ new/fcitx5-gtk-5.0.22/gtk3/utils.h  2022-12-25 19:30:09.284669200 +0100
@@ -7,6 +7,7 @@
 #ifndef _GTK3_UTILS_H_
 #define _GTK3_UTILS_H_
 
+#include "fcitxflags.h"
 #include <cairo.h>
 #include <glib-object.h>
 #include <memory>
@@ -43,6 +44,63 @@
            y <= rect.y + rect.height;
 }
 
+static inline gboolean check_app_name(const gchar *pattern) {
+    bool result = FALSE;
+    const gchar *prgname = g_get_prgname();
+    if (!prgname) {
+        return FALSE;
+    }
+    gchar **p;
+    gchar **apps = g_strsplit(pattern, ",", 0);
+    for (p = apps; *p != NULL; p++) {
+        if (g_regex_match_simple(*p, prgname, (GRegexCompileFlags)0,
+                                 (GRegexMatchFlags)0)) {
+            result = TRUE;
+            break;
+        }
+    }
+    g_strfreev(apps);
+    return result;
+}
+
+static inline bool get_boolean_env(const char *name, bool defval) {
+    const char *value = getenv(name);
+
+    if (value == nullptr) {
+        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;
+    }
+
+    return true;
+}
+
+constexpr int MAX_CACHED_HANDLED_EVENT = 40;
+
+constexpr uint64_t purpose_related_capability =
+    fcitx::FcitxCapabilityFlag_Alpha | fcitx::FcitxCapabilityFlag_Digit |
+    fcitx::FcitxCapabilityFlag_Number | fcitx::FcitxCapabilityFlag_Dialable |
+    fcitx::FcitxCapabilityFlag_Url | fcitx::FcitxCapabilityFlag_Email |
+    fcitx::FcitxCapabilityFlag_Password;
+
+constexpr uint64_t hints_related_capability =
+    fcitx::FcitxCapabilityFlag_SpellCheck |
+    fcitx::FcitxCapabilityFlag_NoSpellCheck |
+    fcitx::FcitxCapabilityFlag_WordCompletion |
+    fcitx::FcitxCapabilityFlag_Lowercase |
+    fcitx::FcitxCapabilityFlag_Uppercase |
+    fcitx::FcitxCapabilityFlag_UppercaseWords |
+    fcitx::FcitxCapabilityFlag_UppwercaseSentences |
+    fcitx::FcitxCapabilityFlag_NoOnScreenKeyboard;
+
+constexpr uint32_t HandledMask = (1 << 24);
+constexpr uint32_t IgnoredMask = (1 << 25);
+constexpr unsigned int MAX_CACHED_EVENTS = 30;
+
 } // namespace fcitx::gtk
 
 #endif // _GTK3_UTILS_H_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/gtk4/fcitximcontext.cpp 
new/fcitx5-gtk-5.0.22/gtk4/fcitximcontext.cpp
--- old/fcitx5-gtk-5.0.21/gtk4/fcitximcontext.cpp       2022-11-24 
08:28:11.710383200 +0100
+++ new/fcitx5-gtk-5.0.22/gtk4/fcitximcontext.cpp       2023-02-02 
21:40:52.698458700 +0100
@@ -36,24 +36,6 @@
 
 using namespace fcitx::gtk;
 
-constexpr int MAX_CACHED_HANDLED_EVENT = 40;
-
-static const uint64_t purpose_related_capability =
-    fcitx::FcitxCapabilityFlag_Alpha | fcitx::FcitxCapabilityFlag_Digit |
-    fcitx::FcitxCapabilityFlag_Number | fcitx::FcitxCapabilityFlag_Dialable |
-    fcitx::FcitxCapabilityFlag_Url | fcitx::FcitxCapabilityFlag_Email |
-    fcitx::FcitxCapabilityFlag_Password;
-
-static const uint64_t hints_related_capability =
-    fcitx::FcitxCapabilityFlag_SpellCheck |
-    fcitx::FcitxCapabilityFlag_NoSpellCheck |
-    fcitx::FcitxCapabilityFlag_WordCompletion |
-    fcitx::FcitxCapabilityFlag_Lowercase |
-    fcitx::FcitxCapabilityFlag_Uppercase |
-    fcitx::FcitxCapabilityFlag_UppercaseWords |
-    fcitx::FcitxCapabilityFlag_UppwercaseSentences |
-    fcitx::FcitxCapabilityFlag_NoOnScreenKeyboard;
-
 struct KeyPressCallbackData {
     KeyPressCallbackData(FcitxIMContext *context, GdkEvent *event)
         : context_(FCITX_IM_CONTEXT(g_object_ref(context))),
@@ -70,22 +52,6 @@
 
 extern "C" {
 
-static bool get_boolean_env(const char *name, bool defval) {
-    const char *value = getenv(name);
-
-    if (value == nullptr) {
-        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;
-    }
-
-    return true;
-}
-
 /* functions prototype */
 static void fcitx_im_context_class_init(FcitxIMContextClass *klass, gpointer);
 static void fcitx_im_context_class_fini(FcitxIMContextClass *klass, gpointer);
@@ -110,6 +76,9 @@
                                                 PangoAttrList **attrs,
                                                 int *cursor_pos);
 
+static void fcitx_im_context_commit_string(FcitxIMContext *context,
+                                           const char *str);
+static void fcitx_im_context_commit_preedit(FcitxIMContext *context);
 static gboolean _set_cursor_location_internal(FcitxIMContext *fcitxcontext);
 static gboolean _defer_request_surrounding_text(FcitxIMContext *fcitxcontext);
 static void _slave_commit_cb(GtkIMContext *slave, char *string,
@@ -125,8 +94,6 @@
                                              int offset_from_cursor,
                                              guint nchars,
                                              FcitxIMContext *context);
-static void _fcitx_im_context_commit_string(FcitxIMContext *context,
-                                            const char *str);
 static void _fcitx_im_context_commit_string_cb(FcitxGClient *client, char *str,
                                                void *user_data);
 static void _fcitx_im_context_forward_key_cb(FcitxGClient *client, guint 
keyval,
@@ -141,6 +108,8 @@
                                                           GPtrArray *array,
                                                           int cursor_pos,
                                                           void *user_data);
+static void _fcitx_im_context_notify_focus_out_cb(FcitxGClient *client,
+                                                  void *user_data);
 static void _fcitx_im_context_process_key_cb(GObject *source_object,
                                              GAsyncResult *res,
                                              gpointer user_data);
@@ -219,22 +188,6 @@
     return FCITX_IM_CONTEXT(obj);
 }
 
-static gboolean check_app_name(const char *pattern) {
-    bool result = FALSE;
-    const char *prgname = g_get_prgname();
-    char **p;
-    char **apps = g_strsplit(pattern, ",", 0);
-    for (p = apps; *p != NULL; p++) {
-        if (g_regex_match_simple(*p, prgname, (GRegexCompileFlags)0,
-                                 (GRegexMatchFlags)0)) {
-            result = TRUE;
-            break;
-        }
-    }
-    g_strfreev(apps);
-    return result;
-}
-
 ///
 static void fcitx_im_context_class_init(FcitxIMContextClass *klass, gpointer) {
     GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(klass);
@@ -310,6 +263,7 @@
     context->last_anchor_pos = -1;
     context->last_cursor_pos = -1;
     context->preedit_string = NULL;
+    context->commit_preedit_string = NULL;
     context->attrlist = NULL;
     context->last_updated_capability =
         (guint64)fcitx::FcitxCapabilityFlag_SurroundingText;
@@ -403,6 +357,9 @@
     g_signal_connect(context->client, "update-formatted-preedit",
                      G_CALLBACK(_fcitx_im_context_update_formatted_preedit_cb),
                      context);
+    g_signal_connect(context->client, "notify-focus-out",
+                     G_CALLBACK(_fcitx_im_context_notify_focus_out_cb),
+                     context);
 
     context->xkbComposeState =
         xkbComposeTable
@@ -431,6 +388,7 @@
     g_clear_object(&context->client);
 
     g_clear_pointer(&context->preedit_string, g_free);
+    g_clear_pointer(&context->commit_preedit_string, g_free);
     g_clear_pointer(&context->surrounding_text, g_free);
     g_clear_pointer(&context->attrlist, pango_attr_list_unref);
 
@@ -586,101 +544,116 @@
     context->attrlist = pango_attr_list_new();
 
     GString *gstr = g_string_new(NULL);
+    GString *commit_gstr = g_string_new(NULL);
 
-    unsigned int i = 0;
-    for (i = 0; i < array->len; i++) {
-        size_t bytelen = strlen(gstr->str);
-        FcitxGPreeditItem *preedit =
-            (FcitxGPreeditItem *)g_ptr_array_index(array, i);
-        const char *s = preedit->string;
-        int type = preedit->type;
-
-        PangoAttribute *pango_attr = NULL;
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Underline)) {
-            pango_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Strike)) {
-            pango_attr = pango_attr_strikethrough_new(true);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Bold)) {
-            pango_attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
-        if ((type & (guint32)fcitx::FcitxTextFormatFlag_Italic)) {
-            pango_attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-        }
+    if (array) {
+        for (unsigned int i = 0; i < array->len; i++) {
+            size_t bytelen = strlen(gstr->str);
+            FcitxGPreeditItem *preedit =
+                (FcitxGPreeditItem *)g_ptr_array_index(array, i);
+            const char *s = preedit->string;
+            int type = preedit->type;
+
+            PangoAttribute *pango_attr = NULL;
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Underline)) {
+                pango_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Strike)) {
+                pango_attr = pango_attr_strikethrough_new(true);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Bold)) {
+                pango_attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_Italic)) {
+                pango_attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
 
-        if (type & (guint32)fcitx::FcitxTextFormatFlag_HighLight) {
-            gboolean hasColor = false;
-            guint fg_red = 0, fg_green = 0, fg_blue = 0, bg_red = 0,
-                  bg_green = 0, bg_blue = 0;
-
-            if (context->client_widget) {
-                hasColor = true;
-                GtkStyleContext *styleContext =
-                    gtk_widget_get_style_context(context->client_widget);
-                GdkRGBA fg_rgba, bg_rgba;
-                hasColor =
-                    gtk_style_context_lookup_color(
-                        styleContext, "theme_selected_bg_color", &bg_rgba) &&
-                    gtk_style_context_lookup_color(
-                        styleContext, "theme_selected_fg_color", &fg_rgba);
-
-                if (!(fg_rgba.red == bg_rgba.red &&
-                      fg_rgba.green == bg_rgba.green &&
-                      fg_rgba.blue == bg_rgba.blue)) {
-                    hasColor = false;
+            if (type & (guint32)fcitx::FcitxTextFormatFlag_HighLight) {
+                gboolean hasColor = false;
+                guint fg_red = 0, fg_green = 0, fg_blue = 0, bg_red = 0,
+                      bg_green = 0, bg_blue = 0;
+
+                if (context->client_widget) {
+                    hasColor = true;
+                    GtkStyleContext *styleContext =
+                        gtk_widget_get_style_context(context->client_widget);
+                    GdkRGBA fg_rgba, bg_rgba;
+                    hasColor =
+                        gtk_style_context_lookup_color(
+                            styleContext, "theme_selected_bg_color",
+                            &bg_rgba) &&
+                        gtk_style_context_lookup_color(
+                            styleContext, "theme_selected_fg_color", &fg_rgba);
+
+                    if (!(fg_rgba.red == bg_rgba.red &&
+                          fg_rgba.green == bg_rgba.green &&
+                          fg_rgba.blue == bg_rgba.blue)) {
+                        hasColor = false;
+                    }
+                    if (hasColor) {
+                        fg_red = CLAMP((gint)(fg_rgba.red * 65535), 0, 65535);
+                        fg_green =
+                            CLAMP((gint)(fg_rgba.green * 65535), 0, 65535);
+                        fg_blue = CLAMP((gint)(fg_rgba.blue * 65535), 0, 
65535);
+                        bg_red = CLAMP((gint)(bg_rgba.red * 65535), 0, 65535);
+                        bg_green =
+                            CLAMP((gint)(bg_rgba.green * 65535), 0, 65535);
+                        bg_blue = CLAMP((gint)(bg_rgba.blue * 65535), 0, 
65535);
+                    }
                 }
-                if (hasColor) {
-                    fg_red = CLAMP((gint)(fg_rgba.red * 65535), 0, 65535);
-                    fg_green = CLAMP((gint)(fg_rgba.green * 65535), 0, 65535);
-                    fg_blue = CLAMP((gint)(fg_rgba.blue * 65535), 0, 65535);
-                    bg_red = CLAMP((gint)(bg_rgba.red * 65535), 0, 65535);
-                    bg_green = CLAMP((gint)(bg_rgba.green * 65535), 0, 65535);
-                    bg_blue = CLAMP((gint)(bg_rgba.blue * 65535), 0, 65535);
+
+                if (!hasColor) {
+                    fg_red = 0xffff;
+                    fg_green = 0xffff;
+                    fg_blue = 0xffff;
+                    bg_red = 0x43ff;
+                    bg_green = 0xacff;
+                    bg_blue = 0xe8ff;
                 }
-            }
 
-            if (!hasColor) {
-                fg_red = 0xffff;
-                fg_green = 0xffff;
-                fg_blue = 0xffff;
-                bg_red = 0x43ff;
-                bg_green = 0xacff;
-                bg_blue = 0xe8ff;
+                pango_attr =
+                    pango_attr_foreground_new(fg_red, fg_green, fg_blue);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+                pango_attr =
+                    pango_attr_background_new(bg_red, bg_green, bg_blue);
+                pango_attr->start_index = bytelen;
+                pango_attr->end_index = bytelen + strlen(s);
+                pango_attr_list_insert(context->attrlist, pango_attr);
+            }
+            gstr = g_string_append(gstr, s);
+            if ((type & (guint32)fcitx::FcitxTextFormatFlag_DontCommit) == 0) {
+                commit_gstr = g_string_append(commit_gstr, s);
             }
-
-            pango_attr = pango_attr_foreground_new(fg_red, fg_green, fg_blue);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
-            pango_attr = pango_attr_background_new(bg_red, bg_green, bg_blue);
-            pango_attr->start_index = bytelen;
-            pango_attr->end_index = bytelen + strlen(s);
-            pango_attr_list_insert(context->attrlist, pango_attr);
         }
-        gstr = g_string_append(gstr, s);
     }
 
     char *str = g_string_free(gstr, FALSE);
 
     context->preedit_string = str;
+    context->commit_preedit_string = g_string_free(commit_gstr, FALSE);
     context->cursor_pos = g_utf8_pointer_to_offset(str, str + cursor_pos);
 
     if (context->preedit_string != NULL && context->preedit_string[0] == 0) {
         g_clear_pointer(&context->preedit_string, g_free);
     }
+    if (context->commit_preedit_string != NULL &&
+        context->commit_preedit_string[0] == 0) {
+        g_clear_pointer(&context->commit_preedit_string, g_free);
+    }
 }
 
 static void _fcitx_im_context_update_formatted_preedit_cb(FcitxGClient *,
@@ -702,6 +675,7 @@
 
         g_clear_pointer(&context->preedit_string, g_free);
     }
+    g_clear_pointer(&context->commit_preedit_string, g_free);
     g_clear_pointer(&context->attrlist, pango_attr_list_unref);
 
     if (context->use_preedit) {
@@ -730,6 +704,16 @@
     }
 }
 
+static void _fcitx_im_context_notify_focus_out_cb(FcitxGClient *,
+                                                  void *user_data) {
+    FcitxIMContext *context = FCITX_IM_CONTEXT(user_data);
+    if (!context->has_focus) {
+        return;
+    }
+
+    fcitx_im_context_commit_preedit(context);
+}
+
 ///
 static void fcitx_im_context_focus_in(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
@@ -780,6 +764,31 @@
     return;
 }
 
+static void fcitx_im_context_commit_string(FcitxIMContext *context,
+                                           const gchar *str) {
+    context->ignore_reset = TRUE;
+    g_signal_emit(context, _signal_commit_id, 0, str);
+    context->ignore_reset = FALSE;
+
+    // Better request surrounding after commit.
+    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+                    (GSourceFunc)_defer_request_surrounding_text,
+                    g_object_ref(context), (GDestroyNotify)g_object_unref);
+}
+
+static void fcitx_im_context_commit_preedit(FcitxIMContext *context) {
+    if (!context->has_focus) {
+        return;
+    }
+
+    if (context->commit_preedit_string) {
+        fcitx_im_context_commit_string(context, 
context->commit_preedit_string);
+    }
+
+    _fcitx_im_context_update_formatted_preedit_cb(context->client, nullptr, 0,
+                                                  context);
+}
+
 static void fcitx_im_context_focus_out(GtkIMContext *context) {
     FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context);
 
@@ -791,6 +800,8 @@
                                  (gpointer *)&_focus_im_context);
     _focus_im_context = NULL;
 
+    fcitx_im_context_commit_preedit(fcitxcontext);
+
     fcitxcontext->has_focus = false;
     fcitxcontext->last_key_code = 0;
     fcitxcontext->last_is_release = false;
@@ -799,13 +810,6 @@
         fcitx_g_client_focus_out(fcitxcontext->client);
     }
 
-    fcitxcontext->cursor_pos = 0;
-    if (fcitxcontext->preedit_string != NULL) {
-        g_clear_pointer(&fcitxcontext->preedit_string, g_free);
-        g_signal_emit(fcitxcontext, _signal_preedit_changed_id, 0);
-        g_signal_emit(fcitxcontext, _signal_preedit_end_id, 0);
-    }
-
     gtk_im_context_focus_out(fcitxcontext->slave);
 
     return;
@@ -1078,6 +1082,7 @@
             }
         } while (0);
         flags |= (guint64)fcitx::FcitxCapabilityFlag_ReportKeyRepeat;
+        flags |= (guint64)fcitx::FcitxCapabilityFlag_ClientUnfocusCommit;
 
         // always run this code against all gtk version
         // seems visibility != PASSWORD hint
@@ -1107,6 +1112,7 @@
     if (fcitxcontext->ignore_reset) {
         return;
     }
+    fcitx_im_context_commit_preedit(fcitxcontext);
 
     if (fcitx_g_client_is_valid(fcitxcontext->client)) {
         fcitx_g_client_reset(fcitxcontext->client);
@@ -1209,21 +1215,10 @@
     return return_value;
 }
 
-void _fcitx_im_context_commit_string(FcitxIMContext *context, const char *str) 
{
-    context->ignore_reset = TRUE;
-    g_signal_emit(context, _signal_commit_id, 0, str);
-    context->ignore_reset = FALSE;
-}
-
 void _fcitx_im_context_commit_string_cb(FcitxGClient *, char *str,
                                         void *user_data) {
     FcitxIMContext *context = FCITX_IM_CONTEXT(user_data);
-    _fcitx_im_context_commit_string(context, str);
-
-    // Better request surrounding after commit.
-    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
-                    (GSourceFunc)_defer_request_surrounding_text,
-                    g_object_ref(context), (GDestroyNotify)g_object_unref);
+    fcitx_im_context_commit_string(context, str);
 }
 
 void _fcitx_im_context_forward_key_cb(FcitxGClient *, guint, guint, gboolean,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fcitx5-gtk-5.0.21/gtk4/fcitximcontextprivate.h 
new/fcitx5-gtk-5.0.22/gtk4/fcitximcontextprivate.h
--- old/fcitx5-gtk-5.0.21/gtk4/fcitximcontextprivate.h  2022-11-12 
18:12:58.154401800 +0100
+++ new/fcitx5-gtk-5.0.22/gtk4/fcitximcontextprivate.h  2022-12-16 
19:53:05.867176300 +0100
@@ -26,6 +26,7 @@
     gboolean is_inpreedit;
     gboolean is_wayland;
     char *preedit_string;
+    char *commit_preedit_string;
     char *surrounding_text;
     int cursor_pos;
     guint64 capability_from_toolkit;

Reply via email to