Blob Blame History Raw
From 7b0d091839a4f1315ba216175fb2787e86f7fa31 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Tue, 3 Mar 2020 17:08:30 +0900
Subject: [PATCH] src/tests: Delete graves in substitution in
 ibus-desktop-testing-runner

Delete the single quotations to enclose grave chracters because
DASH saves the single quoted '`id -u`' as the raw string in the command
substitution not to be extracted.

BUG=https://github.com/ibus/ibus/issues/2189
---
 src/tests/ibus-desktop-testing-runner.in | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/tests/ibus-desktop-testing-runner.in b/src/tests/ibus-desktop-testing-runner.in
index 0d9a847c..f9238e69 100755
--- a/src/tests/ibus-desktop-testing-runner.in
+++ b/src/tests/ibus-desktop-testing-runner.in
@@ -4,7 +4,7 @@
 #
 # ibus - The Input Bus
 #
-# Copyright (c) 2018-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
+# Copyright (c) 2018-2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
 # Copyright (c) 2018 Red Hat, Inc.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -31,7 +31,8 @@
 # POSIX sh has no 'echo -e'
 : ${ECHO:='/usr/bin/echo'}
 # POSIX sh has $UID
-: ${UID:='`id -u`'}
+# DASH saves the graves in '``' as characters not to be extracted
+: ${UID:=`id -u`}
 
 
 PROGNAME=`basename $0`
@@ -170,7 +171,7 @@ _EOF
 run_dbus_daemon()
 {
     # Use dbus-launch --exit-with-session later instead of --sh-syntax
-    export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$UID/bus
+    export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$UID/bus"
 }
 
 run_desktop()
-- 
2.24.1

From 8ce25208c3f4adfd290a032c6aa739d2b7580eb1 Mon Sep 17 00:00:00 2001
From: Carlos Garnacho <carlosg@gnome.org>
Date: Thu, 12 Mar 2020 16:02:16 +0900
Subject: [PATCH] src: Use WAYLAND_DISPLAY on Wayland sessions to make up
 IBus socket name

In Wayland sessions, GNOME Shell 3.36 is leveraging 2 X11 Display
connections so one is used to set up all services for a "X11 session"
before user applications connected to the other display might require it.
This allows seamlessly starting Xwayland on demand to X11 user applications.

IBus here belongs to the first described connection, it is started
explicitly on that display by GNOME Shell as it is necessary to set up
ibus-x11 before any other X11 client might want to use it.

However the use of this "secondary" display results in IBus daemon left
unable to talk to applications, as the socket name is dependent on the
DISPLAY envvar and ibus/applications don't agree on its content.

For wayland sessions, make it look for WAYLAND_DISPLAY, as that'll have
the similar "per session bus" behavior that this seems to look after.

BUG=https://gitlab.gnome.org/GNOME/gnome-shell/issues/2341
---
 src/ibusshare.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/ibusshare.c b/src/ibusshare.c
index 0d50d3f5..e0ef2ce0 100644
--- a/src/ibusshare.c
+++ b/src/ibusshare.c
@@ -100,6 +100,7 @@ ibus_get_socket_path (void)
         gchar *display;
         gchar *displaynumber = "0";
         /* gchar *screennumber = "0"; */
+        gboolean is_wayland = FALSE;
         gchar *p;
 
         path = g_strdup (g_getenv ("IBUS_ADDRESS_FILE"));
@@ -108,13 +109,19 @@ ibus_get_socket_path (void)
         }
 
         if (_display == NULL) {
-            display = g_strdup (g_getenv ("DISPLAY"));
+            display = g_strdup (g_getenv ("WAYLAND_DISPLAY"));
+            if (display)
+                is_wayland = TRUE;
+            else
+                display = g_strdup (g_getenv ("DISPLAY"));
         }
         else {
             display = g_strdup (_display);
         }
 
-        if (display) {
+        if (is_wayland) {
+            displaynumber = display;
+        } else if (display) {
             p = display;
             hostname = display;
             for (; *p != ':' && *p != '\0'; p++);
-- 
2.24.1

From 5765bfd69fb2ab1174378fbb0d8cac7f2bd2610f Mon Sep 17 00:00:00 2001
From: Changwoo Ryu <cwryu@debian.org>
Date: Wed, 15 Apr 2020 17:43:14 +0900
Subject: [PATCH] client/gtk2: Remove glib_check_version() in gtk immodule

In the gtk2/gtk3 immodule, glib_check_version() is being used to make sure
that the installed glib version is not older than the glib version which ibus
is built with.

But there is no reason why glib version is checked in runtime. Library
compatibility is already being checked more precisely by packaging systems and
linkers.

This version check can break the ibus gtk immodule when used with an older but
compatible version of glib, such as glib 2.62.x which is compatible with
2.64.x.

BUG=https://github.com/ibus/ibus/issues/2200
---
 client/gtk2/ibusim.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/client/gtk2/ibusim.c b/client/gtk2/ibusim.c
index bfacd0f0..d70800d3 100644
--- a/client/gtk2/ibusim.c
+++ b/client/gtk2/ibusim.c
@@ -41,9 +41,7 @@ static const GtkIMContextInfo *info_list[] = {
 G_MODULE_EXPORT const gchar*
 g_module_check_init (GModule *module)
 {
-    return glib_check_version (GLIB_MAJOR_VERSION,
-                               GLIB_MINOR_VERSION,
-                               0);
+    return null;
 }
 
 G_MODULE_EXPORT void
-- 
2.24.1

From 8da016764cee9616cca4658d1fb311d6b3bfc0df Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Wed, 15 Apr 2020 17:55:03 +0900
Subject: [PATCH] src/tests: Fix to get focus events with su in
 ibus-desktop-testing-runner

GtkWindow haven't received focus events in any test cases since Fedora 31
whenever Ansible runs ibus-desktop-testing-runner after `su root`.
Seems su command does not run systemd automatically and now systemd
requires XDG_RUNTIME_DIR and Ansible requires root access with ssh.
This fix requires to restart sshd with modified /etc/ssh/sshd_config
with "PermitRootLogin yes" in order to run with su command.

Ansible with ibus-desktop-testin-runner has worked fine if root console
login is used without this patch because PAM runs systemd by login.
---
 src/tests/ibus-desktop-testing-runner.in | 36 ++++++++++++++++++++++--
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/src/tests/ibus-desktop-testing-runner.in b/src/tests/ibus-desktop-testing-runner.in
index f9238e69..f760fd5b 100755
--- a/src/tests/ibus-desktop-testing-runner.in
+++ b/src/tests/ibus-desktop-testing-runner.in
@@ -49,6 +49,7 @@ PID_XORG=0
 PID_GNOME_SESSION=0
 TESTING_RUNNER="default"
 TESTS=""
+TIMEOUT=300
 GREEN='\033[0;32m'
 RED='\033[0;31m'
 NC='\033[0m'
@@ -84,6 +85,7 @@ usage()
 "-r, --runner=RUNNER              Run TESTS programs with a test RUNNER.\n"    \
 "                                 RUNNDER = gnome or default.\n"               \
 "                                 default is an embedded runner.\n"            \
+"-T, --timeout=TIMEOUT            Set timeout (default TIMEOUT is 300 sec).\n" \
 "-o, --output=OUTPUT_FILE         OUtput the log to OUTPUT_FILE\n"             \
 "-O, --result=RESULT_FILE         OUtput the result to RESULT_FILE\n"          \
 ""
@@ -92,8 +94,8 @@ usage()
 parse_args()
 {
     # This is GNU getopt. "sudo port getopt" in BSD?
-    ARGS=`getopt -o hvb:s:cd:t:r:o:O: --long \
-          help,version,builddir:,srcdir:,no-graphics,desktop:,tests:,runner:,output:,result:\
+    ARGS=`getopt -o hvb:s:cd:t:r:T:o:O: --long \
+          help,version,builddir:,srcdir:,no-graphics,desktop:,tests:,runner:,timeout:,output:,result:\
         -- "$@"`;
     eval set -- "$ARGS"
     while [ 1 ] ; do
@@ -106,6 +108,7 @@ parse_args()
         -d | --desktop )     DESKTOP_COMMAND="$2"; shift 2;;
         -t | --tests )       TESTS="$2"; shift 2;;
         -r | --runner )      TESTING_RUNNER="$2"; shift 2;;
+        -T | --timeout )     TIMEOUT="$2"; shift 2;;
         -o | --output )      TEST_LOG="$2"; shift 2;;
         -O | --result )      RESULT_LOG="$2"; shift 2;;
         -- )                 shift; break;;
@@ -166,11 +169,37 @@ _EOF
     fi
     # `su` command does not run loginctl
     export XDG_SESSION_TYPE='x11'
+    export XDG_SESSION_CLASS=user
+    # `su` command does not get focus in events without this variable.
+    # Need to restart sshd after set "PermitRootLogin yes" in sshd_config
+    if [ "x$XDG_RUNTIME_DIR" = x ] ; then
+        export XDG_RUNTIME_DIR=/run/user/$UID
+        is_root_login=`grep "^PermitRootLogin" /etc/ssh/sshd_config | grep yes`
+        if [ "x$ANSIBLE" != x ] && [ "x$is_root_login" = x ] ; then
+            print_log -e "${RED}FAIL${NC}: No permission to get focus-in events in GtkWindow with ansible"
+            echo "su command does not configure necessary login info "         \
+                 "with systemd and GtkWindow cannot receive focus-events "     \
+                 "when ibus-desktop-testing-runner is executed by "            \
+                 "ansible-playbook." >> $TEST_LOG
+            echo "Enabling root login via sshd, restarting sshd, set "         \
+                 "XDG_RUNTIME_DIR can resolve the problem under "              \
+                 "ansible-playbook." >> $TEST_LOG
+            exit 255
+        fi
+    fi
+    #  Do we need XDG_SESSION_ID and XDG_SEAT?
+    #export XDG_CONFIG_DIRS=/etc/xdg
+    #export XDG_SESSION_ID=10
+    #export XDG_SESSION_DESKTOP=gnome
+    #export XDG_SEAT=seat0
 }
 
 run_dbus_daemon()
 {
     # Use dbus-launch --exit-with-session later instead of --sh-syntax
+    # GNOME uses a unix:abstract address and it effects gsettings set values
+    # in each test case.
+    # TODO: Should we comment out this line?
     export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$UID/bus"
 }
 
@@ -288,7 +317,8 @@ run_gnome_desktop_testing_runner()
             fail=1
             continue
         fi
-        gnome-desktop-testing-runner $tst 2>>$TEST_LOG 1>>$TEST_LOG
+        gnome-desktop-testing-runner --timeout=$TIMEOUT $tst \
+                2>>$TEST_LOG 1>>$TEST_LOG
         retval=$?
         read pass fail << EOF
         `count_case_result $retval $pass $fail`
-- 
2.24.1

From 0b9d9365988a96a2bc31c48624f9c2b8081601b6 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Wed, 22 Apr 2020 20:17:12 +0900
Subject: [PATCH] client/gtk2: Fix typo

---
 client/gtk2/ibusim.c                     | 4 ++--
 src/tests/ibus-desktop-testing-runner.in | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/client/gtk2/ibusim.c b/client/gtk2/ibusim.c
index d70800d3..55609ce7 100644
--- a/client/gtk2/ibusim.c
+++ b/client/gtk2/ibusim.c
@@ -2,7 +2,7 @@
 /* vim:set et ts=4: */
 /* ibus - The Input Bus
  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2010 Red Hat, Inc.
+ * 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
@@ -41,7 +41,7 @@ static const GtkIMContextInfo *info_list[] = {
 G_MODULE_EXPORT const gchar*
 g_module_check_init (GModule *module)
 {
-    return null;
+    return NULL;
 }
 
 G_MODULE_EXPORT void
diff --git a/src/tests/ibus-desktop-testing-runner.in b/src/tests/ibus-desktop-testing-runner.in
index f760fd5b..4232c549 100755
--- a/src/tests/ibus-desktop-testing-runner.in
+++ b/src/tests/ibus-desktop-testing-runner.in
@@ -173,7 +173,7 @@ _EOF
     # `su` command does not get focus in events without this variable.
     # Need to restart sshd after set "PermitRootLogin yes" in sshd_config
     if [ "x$XDG_RUNTIME_DIR" = x ] ; then
-        export XDG_RUNTIME_DIR=/run/user/$UID
+        export XDG_RUNTIME_DIR="/run/user/$UID"
         is_root_login=`grep "^PermitRootLogin" /etc/ssh/sshd_config | grep yes`
         if [ "x$ANSIBLE" != x ] && [ "x$is_root_login" = x ] ; then
             print_log -e "${RED}FAIL${NC}: No permission to get focus-in events in GtkWindow with ansible"
-- 
2.24.1

From 8c4125bc78ce3502b5aeb053e7029cc2594f83f2 Mon Sep 17 00:00:00 2001
From: Changwoo Ryu <cwryu@debian.org>
Date: Sun, 12 Apr 2020 05:28:15 +0900
Subject: [PATCH] src: Build the Emoji dictionaries in parallel

Instead of building Emoji dictionaries src/dicts/emoji-*.dict in sequence, a
pattern rule is specified for them. The make -jN option builds the
dictionaries in parallel.

The GNU make extensions like pattern rule and patsubst function are used for
it. But src/Makefile.am has had other GNU make extensions for a while, so
using more extensions should not make portability worse.

BUG=https://github.com/ibus/ibus/pull/2209
---
 src/Makefile.am | 55 ++++++++++++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 28 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index a8e3d07d..99de1ab7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -246,42 +246,41 @@ if ENABLE_EMOJI_DICT
 AM_CPPFLAGS += -DENABLE_EMOJI_DICT
 
 dictdir = $(pkgdatadir)/dicts
-dict_DATA = dicts/emoji-en.dict
 LANG_FILES = $(basename $(notdir $(wildcard $(EMOJI_ANNOTATION_DIR)/*.xml)))
+EMOJI_DICT_FILES = $(patsubst %,dicts/emoji-%.dict,$(LANG_FILES))
+dict_DATA = $(EMOJI_DICT_FILES)
 
 noinst_PROGRAMS += emoji-parser
 
-dicts/emoji-en.dict: emoji-parser
+dicts/emoji-%.dict: emoji-parser
 	$(AM_V_at)if test x"$(LANG_FILES)" = x ; then \
 	    echo "WARNING: Not found $(EMOJI_ANNOTATION_DIR)/en.xml" 1>&2; \
 	fi; \
-	for f in $(LANG_FILES) ; do \
-	    if test -f dicts/emoji-$$f.dict; then \
-	        echo "Already exists dicts/emoji-$$f.dict"; \
-	        continue; \
-	    fi; \
-	    if test -f \
-	    "$(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$$f.xml" ; then \
-	        xml_derived_option="--xml-derived $(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$$f.xml"; \
+	if test -f $@; then \
+	    echo "Already exists $@"; \
+	    exit 0; \
+	fi; \
+	if test -f \
+	    "$(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$*.xml" ; then \
+	        xml_derived_option="--xml-derived $(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$*.xml"; \
 	        plus_comment="derived"; \
-	    fi; \
-	    if test x"$$f" = xen ; then \
-	        $(builddir)/emoji-parser \
-	            --unicode-emoji-dir $(UNICODE_EMOJI_DIR) \
-	            --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \
-	            $$xml_derived_option \
-	            --xml-ascii $(top_srcdir)/data/annotations/en_ascii.xml \
-	            --out-category ibusemojigen.h \
-	            --out $@; \
-	    else \
-	        $(builddir)/emoji-parser \
-	            --unicode-emoji-dir $(UNICODE_EMOJI_DIR) \
-	            --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \
-	            $$xml_derived_option \
-	            --out dicts/emoji-$$f.dict; \
-	    fi; \
-	    echo "Generated $$plus_comment dicts/emoji-$$f.dict"; \
-	done
+	fi; \
+	if test x"$*" = xen ; then \
+	    $(builddir)/emoji-parser \
+	        --unicode-emoji-dir $(UNICODE_EMOJI_DIR) \
+	        --xml $(EMOJI_ANNOTATION_DIR)/$*.xml \
+	        $$xml_derived_option \
+	        --xml-ascii $(top_srcdir)/data/annotations/en_ascii.xml \
+	        --out-category ibusemojigen.h \
+	        --out $@; \
+	else \
+	    $(builddir)/emoji-parser \
+	        --unicode-emoji-dir $(UNICODE_EMOJI_DIR) \
+	        --xml $(EMOJI_ANNOTATION_DIR)/$*.xml \
+	        $$xml_derived_option \
+	        --out $@; \
+	fi; \
+	echo "Generated $$plus_comment $@"
 
 ibusemojigen.h: dicts/emoji-en.dict
 	$(NULL)
-- 
2.23.0.rc1

From 02105c4d486283e6b561181d9c934d4d23f2d65e Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 14 May 2020 15:48:34 +0900
Subject: [PATCH] bus: Fix SEGV in bus_panel_proxy_focus_in()

SEGV in BUS_IS_PANEL_PROXY() in bus_panel_proxy_focus_in()
Check if GDBusConnect is closed before bus_panel_proxy_new() is called.

BUG=rhbz#1349148
BUG=rhbz#1385349
---
 bus/ibusimpl.c | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
index 85761d30..e432e849 100644
--- a/bus/ibusimpl.c
+++ b/bus/ibusimpl.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) 2011-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
- * Copyright (C) 2008-2019 Red Hat, Inc.
+ * Copyright (C) 2011-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
@@ -464,13 +464,16 @@ _dbus_name_owner_changed_cb (BusDBusImpl   *dbus,
     else if (!g_strcmp0 (name, IBUS_SERVICE_PANEL_EXTENSION_EMOJI))
         panel_type = PANEL_TYPE_EXTENSION_EMOJI;
 
-    if (panel_type != PANEL_TYPE_NONE) {
+    do {
+        if (panel_type == PANEL_TYPE_NONE)
+            break;
         if (g_strcmp0 (new_name, "") != 0) {
             /* a Panel process is started. */
             BusConnection *connection;
             BusInputContext *context = NULL;
             BusPanelProxy   **panel = (panel_type == PANEL_TYPE_PANEL) ?
                                       &ibus->panel : &ibus->emoji_extension;
+            GDBusConnection *dbus_connection = NULL;
 
             if (*panel != NULL) {
                 ibus_proxy_destroy ((IBusProxy *)(*panel));
@@ -479,9 +482,21 @@ _dbus_name_owner_changed_cb (BusDBusImpl   *dbus,
                 g_assert (*panel == NULL);
             }
 
-            connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name);
+            connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS,
+                                                               new_name);
             g_return_if_fail (connection != NULL);
 
+            dbus_connection = bus_connection_get_dbus_connection (connection);
+            /* rhbz#1349148 rhbz#1385349
+             * Avoid SEGV of BUS_IS_PANEL_PROXY (ibus->panel)
+             * This function is called during destroying the connection
+             * in this case? */
+            if (dbus_connection == NULL ||
+                g_dbus_connection_is_closed (dbus_connection)) {
+                new_name = "";
+                break;
+            }
+
             *panel = bus_panel_proxy_new (connection, panel_type);
             if (panel_type == PANEL_TYPE_EXTENSION_EMOJI)
                 ibus->enable_emoji_extension = FALSE;
@@ -535,7 +550,7 @@ _dbus_name_owner_changed_cb (BusDBusImpl   *dbus,
                 }
             }
         }
-    }
+    } while (0);
 
     bus_ibus_impl_component_name_owner_changed (ibus, name, old_name, new_name);
 }
-- 
2.24.1

From f591381e3c892947ecaffe9131b9039ab9014498 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 14 May 2020 16:02:00 +0900
Subject: [PATCH] bus: Fix SEGV in bus_dbus_impl_name_owner_changed()

rhbz#1406699 SEGV in new_owner!=NULL in bus_dbus_impl_name_owner_changed()
which is called by bus_name_service_remove_owner()
If bus_connection_get_unique_name()==NULL, set new_owner="" in
bus_name_service_remove_owner()

rhbz#1432252 SEGV in old_owner!=NULL in bus_dbus_impl_name_owner_changed()
which is called by bus_name_service_set_primary_owner()
If bus_connection_get_unique_name()==NULL, set old_owner="" in
bus_name_service_set_primary_owner()

BUG=rhbz#1406699
BUG=rhbz#1432252
---
 bus/dbusimpl.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c
index b54ef817..59787a80 100644
--- a/bus/dbusimpl.c
+++ b/bus/dbusimpl.c
@@ -2,7 +2,8 @@
 /* vim:set et sts=4: */
 /* ibus - The Input Bus
  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2013 Red Hat, Inc.
+ * Copyright (C) 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
@@ -344,6 +345,8 @@ bus_name_service_set_primary_owner (BusNameService     *service,
                                     BusConnectionOwner *owner,
                                     BusDBusImpl        *dbus)
 {
+    gboolean has_old_owner = FALSE;
+
     g_assert (service != NULL);
     g_assert (owner != NULL);
     g_assert (dbus != NULL);
@@ -351,6 +354,13 @@ bus_name_service_set_primary_owner (BusNameService     *service,
     BusConnectionOwner *old = service->owners != NULL ?
             (BusConnectionOwner *)service->owners->data : NULL;
 
+    /* rhbz#1432252 If bus_connection_get_unique_name() == NULL,
+     * "Hello" method is not received yet.
+     */
+    if (old != NULL && bus_connection_get_unique_name (old->conn) != NULL) {
+        has_old_owner = TRUE;
+    }
+
     if (old != NULL) {
         g_signal_emit (dbus,
                        dbus_signals[NAME_LOST],
@@ -370,7 +380,8 @@ bus_name_service_set_primary_owner (BusNameService     *service,
                    0,
                    owner->conn,
                    service->name,
-                   old != NULL ? bus_connection_get_unique_name (old->conn) : "",
+                   has_old_owner ? bus_connection_get_unique_name (old->conn) :
+                           "",
                    bus_connection_get_unique_name (owner->conn));
 
     if (old != NULL && old->do_not_queue != 0) {
@@ -427,6 +438,7 @@ bus_name_service_remove_owner (BusNameService     *service,
                                BusDBusImpl        *dbus)
 {
     GSList *owners;
+    gboolean has_new_owner = FALSE;
 
     g_assert (service != NULL);
     g_assert (owner != NULL);
@@ -439,6 +451,13 @@ bus_name_service_remove_owner (BusNameService     *service,
         BusConnectionOwner *_new = NULL;
         if (owners->next != NULL) {
             _new = (BusConnectionOwner *)owners->next->data;
+            /* rhbz#1406699 If bus_connection_get_unique_name() == NULL,
+             * "Hello" method is not received yet.
+             */
+            if (_new != NULL &&
+                bus_connection_get_unique_name (_new->conn) != NULL) {
+                has_new_owner = TRUE;
+            }
         }
 
         if (dbus != NULL) {
@@ -447,7 +466,7 @@ bus_name_service_remove_owner (BusNameService     *service,
                            0,
                            owner->conn,
                            service->name);
-            if (_new != NULL) {
+            if (has_new_owner) {
                 g_signal_emit (dbus,
                                dbus_signals[NAME_ACQUIRED],
                                0,
@@ -460,7 +479,7 @@ bus_name_service_remove_owner (BusNameService     *service,
                     _new != NULL ? _new->conn : NULL,
                     service->name,
                     bus_connection_get_unique_name (owner->conn),
-                    _new != NULL ? bus_connection_get_unique_name (_new->conn) : "");
+                    has_new_owner ? bus_connection_get_unique_name (_new->conn) : "");
 
         }
     }
-- 
2.24.1

From 0da3cece88c8b94c0721aa7aca4f2d427aa22069 Mon Sep 17 00:00:00 2001
From: Neil Shepperd <nshepperd@gmail.com>
Date: Wed, 22 Jul 2020 15:31:29 +0900
Subject: [PATCH 1/9] src: Skip parsing of compose sequence with invalid
 keysyms
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Instead of continuing with the keysym defaulted to <Delete>, print a
warning and skip the broken XCompose rule entirely. Fixes a bug where
an ~/.XCompose file with a error breaks the delete key.

Tested locally with the example from launchpad bug below:

	   <\> <)> : "☭" # HAMMER AND SICKLE

This is incorrect syntax as neither <\> nor <)> are correct keysym
names. Before this patch, ibus-daemon -v prints a warning, but
pressing delete twice produces ☭ instead of its normal function. After
this patch, ibus-daemon -v prints a slightly better warning, and the
delete key is unaffected.

BUG=https://github.com/ibus/ibus/issues/2130
BUG=https://bugs.launchpad.net/ubuntu/+source/ibus/+bug/1849399
---
 src/ibuscomposetable.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/ibuscomposetable.c b/src/ibuscomposetable.c
index 3f439134..c50be2b3 100644
--- a/src/ibuscomposetable.c
+++ b/src/ibuscomposetable.c
@@ -216,8 +216,12 @@ parse_compose_sequence (IBusComposeData *compose_data,
             compose_data->sequence[n] = codepoint;
         }
 
-        if (codepoint == IBUS_KEY_VoidSymbol)
-            g_warning ("Could not get code point of keysym %s", match);
+        if (codepoint == IBUS_KEY_VoidSymbol) {
+            g_warning ("Could not get code point of keysym %s: %s",
+                       match, line);
+            g_free (match);
+            goto fail;
+        }
         g_free (match);
         n++;
     }
-- 
2.24.1

From 0ad5e9a6b6611b53c63e635e89b42e30e43ac701 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Wed, 5 Aug 2020 19:50:02 +0900
Subject: [PATCH 2/9] setup: Delete autostart setting

BUG=https://github.com/ibus/ibus/pull/2224
---
 setup/main.py  | 38 --------------------------------
 setup/setup.ui | 59 --------------------------------------------------
 2 files changed, 97 deletions(-)

diff --git a/setup/main.py b/setup/main.py
index f6adb098..d5e2078c 100644
--- a/setup/main.py
+++ b/setup/main.py
@@ -398,13 +398,6 @@ class Setup(object):
         self.__button_close = self.__builder.get_object("button_close")
         self.__button_close.connect("clicked", Gtk.main_quit)
 
-        # auto start ibus
-        self.__checkbutton_auto_start = self.__builder.get_object(
-                "checkbutton_auto_start")
-        self.__checkbutton_auto_start.set_active(self.__is_auto_start())
-        self.__checkbutton_auto_start.connect("toggled",
-                self.__checkbutton_auto_start_toggled_cb)
-
         self.__init_hotkeys()
         self.__init_panel()
         self.__init_general()
@@ -647,37 +640,6 @@ class Setup(object):
         # set new value
         model.set(iter, COLUMN_PRELOAD, data[DATA_PRELOAD])
 
-    def __is_auto_start(self):
-        link_file = path.join(GLib.get_user_config_dir(),
-                              "autostart/ibus.desktop")
-        ibus_desktop = path.join(os.getenv("IBUS_PREFIX"),
-                                 "share/applications/ibus.desktop")
-
-        if not path.exists(link_file):
-            return False
-        if not path.islink(link_file):
-            return False
-        if path.realpath(link_file) != ibus_desktop:
-            return False
-        return True
-
-    def __checkbutton_auto_start_toggled_cb(self, button):
-        auto_start_dir = path.join(GLib.get_user_config_dir(), "autostart")
-        if not path.isdir(auto_start_dir):
-            os.makedirs(auto_start_dir)
-
-        link_file = path.join(GLib.get_user_config_dir(),
-                              "autostart/ibus.desktop")
-        ibus_desktop = path.join(os.getenv("IBUS_PREFIX"),
-                                 "share/applications/ibus.desktop")
-        # unlink file
-        try:
-            os.unlink(link_file)
-        except:
-            pass
-        if self.__checkbutton_auto_start.get_active():
-            os.symlink(ibus_desktop, link_file)
-
     def __sigusr1_cb(self, *args):
         self.__window.present()
 
diff --git a/setup/setup.ui b/setup/setup.ui
index 56453054..250f43a0 100644
--- a/setup/setup.ui
+++ b/setup/setup.ui
@@ -1307,65 +1307,6 @@
                 <property name="orientation">vertical</property>
                 <property name="can_focus">False</property>
                 <property name="no_show_all">True</property>
-                <child>
-                  <placeholder/>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="label5">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">&lt;big&gt;&lt;b&gt;IBus&lt;/b&gt;&lt;/big&gt;
-&lt;small&gt;The intelligent input bus&lt;/small&gt;
-Homepage: https://github.com/ibus/ibus/wiki
-
-
-
-</property>
-                    <property name="use_markup">True</property>
-                    <property name="justify">center</property>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkFrame" id="frame1">
-                    <property name="can_focus">False</property>
-                    <property name="no_show_all">True</property>
-                    <property name="label_xalign">0</property>
-                    <property name="shadow_type">none</property>
-                    <child>
-                      <object class="GtkCheckButton" id="checkbutton_auto_start">
-                        <property name="label" translatable="yes">Start ibus on login</property>
-                        <property name="use_action_appearance">False</property>
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">False</property>
-                        <property name="use_action_appearance">False</property>
-                        <property name="use_underline">True</property>
-                        <property name="halign">start</property>
-                        <property name="draw_indicator">True</property>
-                        <property name="margin_top">6</property>
-                        <property name="margin_start">12</property>
-                      </object>
-                    </child>
-                    <child type="label">
-                      <object class="GtkLabel" id="label4">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="label" translatable="yes">&lt;b&gt;Startup&lt;/b&gt;</property>
-                        <property name="use_markup">True</property>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
               </object>
               <packing>
                 <property name="position">4</property>
-- 
2.24.1

From 3f098dc51a718f3bd427873607dcd47865523dce Mon Sep 17 00:00:00 2001
From: Aaron Muir Hamilton <aaron@correspondwith.me>
Date: Tue, 18 Aug 2020 13:45:32 +0900
Subject: [PATCH 3/9] ui/gtk3: Tell Pango about the engine language in the
 candidate panel

This helps Pango select the correct font variants for the engine
language, which is especially important between languages based on han
characters which have wildly different forms of the same character.

BUG=https://github.com/ibus/ibus/issues/2238
---
 ui/gtk3/candidatearea.vala  | 7 +++++++
 ui/gtk3/candidatepanel.vala | 9 +++++++++
 ui/gtk3/panel.vala          | 2 ++
 3 files changed, 18 insertions(+)

diff --git a/ui/gtk3/candidatearea.vala b/ui/gtk3/candidatearea.vala
index b22ab5da..0ab41217 100644
--- a/ui/gtk3/candidatearea.vala
+++ b/ui/gtk3/candidatearea.vala
@@ -32,6 +32,8 @@ class CandidateArea : Gtk.Box {
     private bool m_show_cursor;
     private ThemedRGBA m_rgba;
 
+    private Pango.Attribute m_language_attribute;
+
     private const string LABELS[] = {
         "1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.",
         "9.", "0.", "a.", "b.", "c.", "d.", "e.", "f."
@@ -102,6 +104,10 @@ class CandidateArea : Gtk.Box {
             m_labels[i].set_text(LABELS[i]);
     }
 
+    public void set_language(Pango.Attribute language_attribute) {
+        m_language_attribute = language_attribute.copy();
+    }
+
     public void set_candidates(IBus.Text[] candidates,
                                uint focus_candidate = 0,
                                bool show_cursor = true) {
@@ -115,6 +121,7 @@ class CandidateArea : Gtk.Box {
             bool visible = false;
             if (i < candidates.length) {
                 Pango.AttrList attrs = get_pango_attr_list_from_ibus_text(candidates[i]);
+                attrs.change(m_language_attribute.copy());
                 if (i == focus_candidate && show_cursor) {
                     Pango.Attribute pango_attr = Pango.attr_foreground_new(
                             (uint16)(m_rgba.selected_fg.red * uint16.MAX),
diff --git a/ui/gtk3/candidatepanel.vala b/ui/gtk3/candidatepanel.vala
index 8994cdb9..57544df3 100644
--- a/ui/gtk3/candidatepanel.vala
+++ b/ui/gtk3/candidatepanel.vala
@@ -34,6 +34,8 @@ public class CandidatePanel : Gtk.Box{
 
     private Gdk.Rectangle m_cursor_location;
 
+    private Pango.Attribute m_language_attribute;
+
     public signal void cursor_up();
     public signal void cursor_down();
     public signal void page_up();
@@ -112,8 +114,14 @@ public class CandidatePanel : Gtk.Box{
         m_candidate_area.set_labels(labels);
     }
 
+    public void set_language(Pango.Attribute language_attribute) {
+        m_candidate_area.set_language(language_attribute);
+        m_language_attribute = language_attribute.copy();
+    }
+
     private void set_attributes(Gtk.Label label, IBus.Text text) {
         Pango.AttrList attrs = get_pango_attr_list_from_ibus_text(text);
+        attrs.change(m_language_attribute.copy());
 
         Gtk.StyleContext context = label.get_style_context();
         Gdk.RGBA color;
@@ -154,6 +162,7 @@ public class CandidatePanel : Gtk.Box{
         if (text != null) {
             m_aux_label.set_text(text.get_text());
             Pango.AttrList attrs = get_pango_attr_list_from_ibus_text(text);
+            attrs.change(m_language_attribute.copy());
             m_aux_label.set_attributes(attrs);
             m_aux_label.show();
         } else {
diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
index 680c86ed..8d9f5cc0 100644
--- a/ui/gtk3/panel.vala
+++ b/ui/gtk3/panel.vala
@@ -822,6 +822,8 @@ class Panel : IBus.PanelService {
         if (!m_use_system_keyboard_layout)
             m_xkblayout.set_layout(engine);
 
+        m_candidate_panel.set_language(new Pango.AttrLanguage(Pango.Language.from_string(engine.get_language())));
+
         engine_contexts_insert(engine);
     }
 
-- 
2.24.1

From 79a09f1e75357a9f1b38e80717a59c19cca1645e Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Tue, 18 Aug 2020 13:45:38 +0900
Subject: [PATCH 4/9] setup: Add use-glyph-from-engine-lang

The setting can revert the previous setting to choose glpyhs
with the current locale on lookup window instead of the engine's language.

BUG=https://github.com/ibus/ibus/issues/2238
---
 data/dconf/org.freedesktop.ibus.gschema.xml |  9 ++++
 setup/main.py                               |  7 +++
 setup/setup.ui                              | 49 +++++++++++++++++++++
 ui/gtk3/panel.vala                          | 26 ++++++++++-
 4 files changed, 89 insertions(+), 2 deletions(-)

diff --git a/data/dconf/org.freedesktop.ibus.gschema.xml b/data/dconf/org.freedesktop.ibus.gschema.xml
index 7ae8f0f6..11a179a6 100644
--- a/data/dconf/org.freedesktop.ibus.gschema.xml
+++ b/data/dconf/org.freedesktop.ibus.gschema.xml
@@ -165,6 +165,15 @@
       <summary>Custom font</summary>
       <description>Custom font name for language panel</description>
     </key>
+    <key name="use-glyph-from-engine-lang" type="b">
+      <default>true</default>
+      <summary>Choose glyphs with input method's language on candidate window</summary>
+      <description>Some code points have the different glyphs and Pango
+                   determines the glyphs from the language attribute.
+                   Pango chooses glyphs from the IBus engine's language
+                   if the value is true and choose them from the desktop
+                   locale if the value is false.</description>
+    </key>
     <child name="emoji" schema="org.freedesktop.ibus.panel.emoji"/>
   </schema>
   <schema id="org.freedesktop.ibus.panel.emoji" path="/desktop/ibus/panel/emoji/">
diff --git a/setup/main.py b/setup/main.py
index d5e2078c..8c8d7a47 100644
--- a/setup/main.py
+++ b/setup/main.py
@@ -212,6 +212,13 @@ class Setup(object):
                                    'active',
                                    Gio.SettingsBindFlags.DEFAULT)
 
+        self.__checkbutton_glyph_from_engine_lang = self.__builder.get_object(
+                "checkbutton_use_glyph_from_engine_lang")
+        self.__settings_panel.bind('use-glyph-from-engine-lang',
+                                   self.__checkbutton_glyph_from_engine_lang,
+                                   'active',
+                                   Gio.SettingsBindFlags.DEFAULT)
+
     def __init_general(self):
         # embed preedit text
         self.__checkbutton_embed_preedit_text = self.__builder.get_object(
diff --git a/setup/setup.ui b/setup/setup.ui
index 250f43a0..a15b9083 100644
--- a/setup/setup.ui
+++ b/setup/setup.ui
@@ -1286,6 +1286,55 @@
                     <property name="position">1</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkFrame" id="frame1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkBox" id="vbox10">
+                        <property name="orientation">vertical</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <property name="margin_top">6</property>
+                        <property name="margin_start">24</property>
+                        <child>
+                          <object class="GtkCheckButton" id="checkbutton_use_glyph_from_engine_lang">
+                            <property name="label" translatable="yes">Choose glyphs with input method's language on candidate window</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="tooltip_text" translatable="yes">Choose glyphs with the input method's language on the candidate window for the duplicated code points</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="halign">start</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label20">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Fonts&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
               </object>
               <packing>
                 <property name="position">3</property>
diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
index 8d9f5cc0..dc98e722 100644
--- a/ui/gtk3/panel.vala
+++ b/ui/gtk3/panel.vala
@@ -47,6 +47,7 @@ class Panel : IBus.PanelService {
     private string m_current_context_path = "";
     private string m_real_current_context_path = "";
     private bool m_use_global_engine = true;
+    private bool m_use_engine_lang = true;
     private CandidatePanel m_candidate_panel;
     private Switcher m_switcher;
     private uint m_switcher_focus_set_engine_id;
@@ -197,6 +198,15 @@ class Panel : IBus.PanelService {
                                               ref m_css_provider);
         });
 
+        m_settings_panel.changed["use-glyph-from-engine-lang"].connect((key) =>
+        {
+                m_use_engine_lang = m_settings_panel.get_boolean(
+                        "use-glyph-from-engine-lang");
+                var engine = m_bus.get_global_engine();
+                if (engine != null)
+                    set_language_from_engine(engine);
+        });
+
         m_settings_panel.changed["show-icon-on-systray"].connect((key) => {
                 set_show_icon_on_systray();
         });
@@ -732,6 +742,8 @@ class Panel : IBus.PanelService {
         set_use_system_keyboard_layout();
         set_use_global_engine();
         set_use_xmodmap();
+        m_use_engine_lang = m_settings_panel.get_boolean(
+                        "use-glyph-from-engine-lang");
         update_engines(m_settings_general.get_strv("preload-engines"),
                        m_settings_general.get_strv("engines-order"));
         BindingCommon.unbind_switch_shortcut(
@@ -801,6 +813,17 @@ class Panel : IBus.PanelService {
         m_engine_contexts.replace(m_current_context_path, engine);
     }
 
+    private void set_language_from_engine(IBus.EngineDesc engine) {
+        if (m_use_engine_lang) {
+            m_candidate_panel.set_language(new Pango.AttrLanguage(
+                    Pango.Language.from_string(engine.get_language())));
+        } else {
+            m_candidate_panel.set_language(new Pango.AttrLanguage(
+                    Pango.Language.from_string(null)));
+        }
+
+    }
+
     private void set_engine(IBus.EngineDesc engine) {
         if (m_property_icon_delay_time_id > 0) {
             GLib.Source.remove(m_property_icon_delay_time_id);
@@ -822,8 +845,7 @@ class Panel : IBus.PanelService {
         if (!m_use_system_keyboard_layout)
             m_xkblayout.set_layout(engine);
 
-        m_candidate_panel.set_language(new Pango.AttrLanguage(Pango.Language.from_string(engine.get_language())));
-
+        set_language_from_engine(engine);
         engine_contexts_insert(engine);
     }
 
-- 
2.24.1

From 508527daaf90901b6a7fa062372516640f88aa75 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 21 Aug 2020 09:07:39 +0900
Subject: [PATCH 6/9] engine: Generate simple.xml with denylist

simple.xml was generated by allowlist (whitelist) and it will be
done by denylist (blacklist).

BUG=https://github.com/ibus/ibus/issues/2153
---
 engine/Makefile.am  |  20 ++-
 engine/denylist.txt |  27 ++++
 engine/gensimple.py | 367 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 407 insertions(+), 7 deletions(-)
 create mode 100644 engine/denylist.txt
 create mode 100755 engine/gensimple.py

diff --git a/engine/Makefile.am b/engine/Makefile.am
index 86f0e2b8..ca405496 100644
--- a/engine/Makefile.am
+++ b/engine/Makefile.am
@@ -4,6 +4,7 @@
 #
 # Copyright (c) 2010-2016, Google Inc. All rights reserved.
 # Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
+# Copyright (c) 2013-2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -78,20 +79,25 @@ component_DATA = \
 
 componentdir = $(pkgdatadir)/component
 
-CLEANFILES = \
+MAINTAINERCLEANFILES = \
 	simple.xml \
 	$(NULL)
 
 EXTRA_DIST = \
+	gensimple.py \
 	iso639converter.py \
-	simple.xml.in \
+	simple.xml \
 	$(NULL)
 
-simple.xml: simple.xml.in
-	$(AM_V_GEN) sed \
-		-e 's|@VERSION[@]|$(VERSION)|g' \
-		-e 's|@libexecdir[@]|$(libexecdir)|g' $< > $@.tmp && \
-		mv $@.tmp $@
+simple.xml:
+	$(srcdir)/gensimple.py \
+	    --input=$(datarootdir)/X11/xkb/rules/evdev.xml \
+	    --output=$@ \
+	    --version=$(VERSION).`date '+%Y%m%d'` \
+	    --exec-path=$(libexecdir)/ibus-engine-simple \
+	    --iso-path=$(datarootdir)/xml/iso-codes/iso_639.xml \
+	    --first-language \
+	$(NULL)
 
 $(libibus):
 	$(MAKE) -C $(top_builddir)/src
diff --git a/engine/denylist.txt b/engine/denylist.txt
new file mode 100644
index 00000000..e4cd0473
--- /dev/null
+++ b/engine/denylist.txt
@@ -0,0 +1,27 @@
+# vim:set fileencoding=utf-8 et sts=4 sw=4:
+#
+# ibus - Intelligent Input Bus for Linux / Unix OS
+#
+# Copyright © 2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is a deny list (black list) and used by gensimple.py.
+# gensimple.py generates the engine list with evdev.xml and if an engine name
+# is matched with any entries in this file, the engine is excluded from the
+# engine list.
+# Asterisk(*) character can be used to match any engines.
+# E.g. xkb:cn:*:* excludes xkb:cn::zho and xkb:cn:mon_trad:mvf
+xkb:cn:*:*
+xkb:nec_vndr/jp:*:*
diff --git a/engine/gensimple.py b/engine/gensimple.py
new file mode 100755
index 00000000..dc4ccf12
--- /dev/null
+++ b/engine/gensimple.py
@@ -0,0 +1,367 @@
+#!/usr/bin/python
+# vim:set fileencoding=utf-8 et sts=4 sw=4:
+#
+# ibus - Intelligent Input Bus for Linux / Unix OS
+#
+# Copyright © 2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This script generates simple.xml with /usr/share/X11/xkb/rules/evdev.xml,
+# /usr/share/xml/iso-codes/iso_639.xml and denylist.txt
+
+
+from xml.dom import minidom
+from xml.sax import make_parser as sax_make_parser
+from xml.sax.handler import feature_namespaces as sax_feature_namespaces
+from xml.sax.saxutils import XMLFilterBase, XMLGenerator, escape
+from xml.sax.xmlreader import AttributesImpl
+from xml.sax._exceptions import SAXParseException
+
+import codecs
+import getopt
+import io
+import os
+import sys
+
+VERSION='0.1'
+EVDEV_XML = '/usr/share/X11/xkb/rules/evdev.xml'
+EXEC_PATH='/usr/lib/ibus-engine-simple'
+ISO_PATH='/usr/share/xml/iso-codes/iso_639.xml'
+PY3K = sys.version_info >= (3, 0)
+
+if PY3K:
+    from io import StringIO
+else:
+    # io.StringIO does not work with XMLGenerator
+    from cStringIO import StringIO
+    # iso_639.xml includes UTF-8
+    reload(sys)
+    sys.setdefaultencoding('utf-8')
+
+def usage(prgname):
+    print('''\
+%s Version %s
+Usage:
+  %s [OPTION...]
+
+Options:
+  -h, --help                         Show this message
+  -i, --input=EVDEV_XML              Load EVDEV_XML file (default is:
+                                         %s)
+  -o, --output=FILE                  Output FILE (default is stdout)
+  -V, --version=VERSION              Set IBus VERSION (default is %s)
+  -e, --exec-path=EXEC_PATH          Set EXEC_PATH file (default is:
+                                         %s)
+  -I, --iso-path=ISO_PATH            Load ISO_PATH file (default is:
+                                         %s)
+  -1, --first-language               Pull first language only in language list
+''' % (prgname, VERSION, prgname, EVDEV_XML, VERSION, EXEC_PATH, ISO_PATH))
+
+
+class EvdevXML(XMLFilterBase):
+    def __init__(self, parser=None, downstream=None, iso639=None,
+                 denylist=None, author=None, first=False):
+        XMLFilterBase.__init__(self, parser)
+        self.__downstream = downstream
+        self.__iso639 = iso639
+        self.__denylist = denylist
+        self.__author = author
+        self.__first = first
+        self.__is_layout = False
+        self.__is_description = False
+        self.__is_config_item = False
+        self.__is_variant = False
+        self.__is_iso639 = False
+        self.__is_name = False
+        self.__layout = ''
+        self.__description = ''
+        self.__variant = ''
+        self.__list_iso639 = []
+    def startDocument(self):
+        if self.__downstream:
+            self.__downstream.startDocument()
+            self.__downstream.startElement('engines', AttributesImpl({}))
+    def endDocument(self):
+        if self.__downstream:
+            self.__downstream.endElement('engines')
+            self.__downstream.endDocument()
+    def startElement(self, name, attrs):
+        if name == 'layout':
+            self.__is_layout = True
+        elif name == 'description':
+            self.__is_description = True
+        elif name == 'configItem':
+            self.__is_config_item = True
+        elif name == 'languageList':
+            self.__list_iso639 = []
+        elif name == 'iso639Id':
+            self.__is_iso639 = True
+        elif name == 'variant':
+            self.__is_variant = True
+        elif name == 'name':
+            self.__is_name = True
+    def endElement(self, name):
+        if name == 'layout':
+            self.__is_layout = False
+            self.__layout = ''
+            self.__description = ''
+            self.__variant = ''
+            self.__list_iso639 = []
+        elif name == 'description':
+            self.__is_description = False
+        elif name == 'configItem':
+            self.save()
+            self.__is_config_item = False
+        elif name == 'iso639Id':
+            self.__is_iso639 = False
+        elif name == 'variant':
+            self.__is_variant = False
+        elif name == 'name':
+            self.__is_name = False
+    def characters(self, text):
+        if self.__is_description:
+            self.__description = text
+        elif self.__is_name:
+            if self.__is_variant and self.__is_config_item:
+                self.__variant = text
+            elif self.__is_layout and self.__is_config_item:
+                self.__layout = text
+        elif self.__is_iso639:
+                self.__list_iso639.append(text)
+    def save(self):
+        if not self.__downstream:
+            return
+        for iso in self.__list_iso639:
+            do_deny = False
+            for [xkb, layout, variant, lang] in self.__denylist:
+                if xkb == 'xkb' \
+                   and ( layout == self.__layout or layout == '*' ) \
+                   and ( variant == self.__variant or variant == '*' ) \
+                   and ( lang == iso or variant == '*' ):
+                    do_deny = True
+                    break
+            if do_deny:
+                continue
+            self.__downstream.startElement('engine', AttributesImpl({}))
+            self.__downstream.startElement('name', AttributesImpl({}))
+            name = 'xkb:%s:%s:%s' % (
+                self.__layout,
+                self.__variant,
+                iso
+            )
+            self.__downstream.characters(name)
+            self.__downstream.endElement('name')
+            self.__downstream.startElement('language', AttributesImpl({}))
+            iso639_1 = self.__iso639.code2to1(iso)
+            if iso639_1 != None:
+                iso = iso639_1
+            self.__downstream.characters(iso)
+            self.__downstream.endElement('language')
+            self.__downstream.startElement('license', AttributesImpl({}))
+            self.__downstream.characters('GPL')
+            self.__downstream.endElement('license')
+            if self.__author != None:
+                self.__downstream.startElement('author', AttributesImpl({}))
+                self.__downstream.characters(self.__author)
+                self.__downstream.endElement('author')
+            self.__downstream.startElement('layout', AttributesImpl({}))
+            self.__downstream.characters(self.__layout)
+            self.__downstream.endElement('layout')
+            self.__downstream.startElement('longname', AttributesImpl({}))
+            self.__downstream.characters(self.__description)
+            self.__downstream.endElement('longname')
+            self.__downstream.startElement('description', AttributesImpl({}))
+            self.__downstream.characters(self.__description)
+            self.__downstream.endElement('description')
+            self.__downstream.startElement('icon', AttributesImpl({}))
+            self.__downstream.characters('ibus-keyboard')
+            self.__downstream.endElement('icon')
+            self.__downstream.startElement('rank', AttributesImpl({}))
+            if self.__variant == '':
+                self.__downstream.characters('50')
+            else:
+                self.__downstream.characters('1')
+            self.__downstream.endElement('rank')
+            self.__downstream.endElement('engine')
+            if self.__first:
+                break
+
+
+class GenerateEngineXML():
+    _NAME = 'org.freedesktop.IBus.Simple'
+    _DESCRIPTION = 'A table based simple engine'
+    _AUTHOR = 'Peng Huang <shawn.p.huang@gmail.com>'
+    _HOMEPAGE = 'https://github.com/ibus/ibus/wiki'
+    _DOMAIN = 'ibus'
+    def __init__(self, path, iso639=None, denylist='', version='', exec='',
+                 first=False):
+        self.__path = path
+        self.__iso639 = iso639
+        self.__denylist = denylist
+        self.__version = version
+        self.__exec = exec
+        self.__first = first
+        self.__result = StringIO()
+        downstream = XMLGenerator(self.__result, 'utf-8')
+        self.__load(downstream)
+
+    def __load(self, downstream=None):
+        parser = sax_make_parser()
+        parser.setFeature(sax_feature_namespaces, 0)
+        self.__handler = EvdevXML(parser,
+                                  downstream,
+                                  self.__iso639,
+                                  self.__denylist,
+                                  self._AUTHOR,
+                                  self.__first)
+        parser.setContentHandler(self.__handler)
+        f = codecs.open(self.__path, 'r', encoding='utf-8')
+        try:
+            parser.parse(f)
+        except SAXParseException:
+            print('Error: Invalid file format: %s' % path)
+        finally:
+            f.close()
+    def write(self, output=None):
+        if output != None:
+            od = codecs.open(output, 'w', encoding='utf-8')
+        else:
+            if PY3K:
+                od = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
+            else:
+                od = codecs.getwriter('utf-8')(sys.stdout)
+        contents = self.__result.getvalue()
+        index = contents.find('<engines>')
+        if index >= 0:
+            author = escape(self._AUTHOR)
+            contents = '%s<component><name>%s</name>\
+<description>%s</description><exec>%s</exec><version>%s</version>\
+<author>%s</author><license>%s</license><homepage>%s</homepage>\
+<textdomain>%s</textdomain>%s</component>' % (
+        contents[:index],
+        self._NAME, self._DESCRIPTION,
+        self.__exec, self.__version, author, 'GPL',
+        self._HOMEPAGE, self._DOMAIN, contents[index:] )
+        parsed = minidom.parseString(contents)
+        # format with indent and encoding attribute in header
+        xml = parsed.toprettyxml(indent='    ', encoding='utf-8')
+        # convert byte to str
+        od.write(str(xml, 'utf-8'))
+        #od.write(contents)
+
+
+class ISO639XML(XMLFilterBase):
+    def __init__(self, parser=None):
+        self.__code2to1 = {}
+        self.__codetoname = {}
+        XMLFilterBase.__init__(self, parser)
+    def startElement(self, name, attrs):
+        if name != 'iso_639_entry':
+            return
+        n = attrs.get('name')
+        iso639_1 = attrs.get('iso_639_1_code')
+        iso639_2b = attrs.get('iso_639_2B_code')
+        iso639_2t = attrs.get('iso_639_2T_code')
+        if iso639_1 != None:
+            self.__codetoname[iso639_1] = n
+            if iso639_2b != None:
+                self.__code2to1[iso639_2b] = iso639_1
+                self.__codetoname[iso639_2b] = n
+            if iso639_2t != None and iso639_2b != iso639_2t:
+                self.__code2to1[iso639_2t] = iso639_1
+                self.__codetoname[iso639_2t] = n
+    def code2to1(self, iso639_2):
+        try:
+            return self.__code2to1[iso639_2]
+        except KeyError:
+            return None
+
+
+def parse_iso639(path):
+    f = codecs.open(path, 'r', encoding='utf-8')
+    parser = sax_make_parser()
+    parser.setFeature(sax_feature_namespaces, 0)
+    handler = ISO639XML(parser)
+    parser.setContentHandler(handler)
+    try:
+        parser.parse(f)
+    except SAXParseException:
+        print('Error: Invalid file format: %s' % path)
+    finally:
+        f.close()
+    return handler
+
+
+def parse_denylist(denyfile):
+    denylist = []
+    f = codecs.open(denyfile, 'r', encoding='utf-8')
+    for line in f.readlines():
+        if line == '\n' or line[0] == '#':
+            continue
+        line = line.rstrip()
+        entry = line.split(':')
+        if len(entry) != 4:
+            print('WARNING: format error: \'%s\' against \'%s\'' \
+                  % (line, 'xkb:layout:variant:lang'))
+            continue
+        denylist.append(entry)
+    f.close()
+    return denylist
+
+
+if __name__ == '__main__':
+    prgname = os.path.basename(sys.argv[0])
+    mydir = os.path.dirname(sys.argv[0])
+    try:
+        opts, args = getopt.getopt(sys.argv[1:],
+                                   'hi:o:V:e:I:1',
+                                   ['help', 'input=', 'output=', 'version=',
+                                    'exec-path=', 'iso-path=',
+                                    'first-language'])
+    except getopt.GetoptError as err:
+        print(err)
+        usage(prgname)
+        sys.exit(2)
+    if len(args) > 0:
+        usage(prgname)
+        sys.exit(2)
+    input = EVDEV_XML
+    output = None
+    version=VERSION
+    exec_path=EXEC_PATH
+    iso_path=ISO_PATH
+    first=False
+    for opt, arg in opts:
+        if opt in ('-h', '--help'):
+            usage(prgname)
+            sys.exit()
+        elif opt in ('-i', '--input'):
+            input = arg
+        elif opt in ('-o', '--output'):
+            output = arg
+        elif opt in ('-V', '--version'):
+            version = arg
+        elif opt in ('-e', '--exec-path'):
+            exec_path = arg
+        elif opt in ('-I', '--iso-path'):
+            iso_path = arg
+        elif opt in ('-1', '--first-langauge'):
+            first=True
+
+    iso639 = parse_iso639(iso_path)
+    denylist = parse_denylist('%s/%s' % ( mydir, 'denylist.txt'))
+    xml = GenerateEngineXML(input, iso639, denylist, version, exec_path, first)
+    xml.write(output)
-- 
2.24.1

From 6879879002af47d49d8740ca383a048d2ac8e904 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 21 Aug 2020 09:15:14 +0900
Subject: [PATCH 8/9] src/tests: Fix runtest with simple.xml

Now simple.xml.in is replaced with simple.xml and modify runtest
to copy simple.xml.

BUG=https://github.com/ibus/ibus/issues/2153
---
 src/tests/runtest | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/tests/runtest b/src/tests/runtest
index a6e4194b..11bcc6c2 100755
--- a/src/tests/runtest
+++ b/src/tests/runtest
@@ -91,6 +91,19 @@ func_copy_component () {
     fi
 }
 
+func_copy_simple_xml () {
+    file=$1
+    base=`func_basename $file`
+    libexecdir=`func_dirname $file`
+    # top_srcdir != top_builddir in make dist
+    libexecdir=`echo "$libexecdir" | sed -e "s|$top_srcdir|$top_builddir|"`
+    if test -f $file; then
+        mkdir -p components
+        sed "s|\(<exec>\).*\(/ibus-engine-simple</exec>\)|\1$libexecdir\2|" \
+                < $file > components/$base
+    fi
+}
+
 trap 'func_cleanup $tstdir' 1 2 3 15
 
 tst=$1; shift
@@ -127,7 +140,7 @@ run_test_case()
             exit -1
         fi
         # func_copy_component replaces s/$top_srcdir/%top_builddir/
-        func_copy_component "../$top_srcdir/engine/simple.xml"
+        func_copy_simple_xml "../$top_srcdir/engine/simple.xml"
         func_copy_component "../$top_srcdir/conf/memconf/memconf.xml"
 
         IBUS_COMPONENT_PATH=$PWD/components
-- 
2.24.1

From 59b902a809ed628bb4d5bbad2b8bcb79a8a23208 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 21 Aug 2020 09:46:46 +0900
Subject: [PATCH 9/9] src: Add file list in registry file

Under Fedora Silverblue, st_mtime in /usr/share/ibus/component is
1 Jan 1970 even if ibus engines are installed and ibus-daemon
cannot detect new engines.
Now IBusObservedPath saves hashed file list besides st_mtime of
the compnent directory to detect the installed engines newly.

BUG=https://github.com/ibus/ibus/issues/2132
---
 src/ibuscomponent.c    |   9 +-
 src/ibusobservedpath.c | 277 ++++++++++++++++++++++++++++++++++++-----
 src/ibusregistry.c     |   6 +-
 3 files changed, 254 insertions(+), 38 deletions(-)

diff --git a/src/ibuscomponent.c b/src/ibuscomponent.c
index 9837f47c..1404ada0 100644
--- a/src/ibuscomponent.c
+++ b/src/ibuscomponent.c
@@ -2,7 +2,8 @@
 /* vim:set et sts=4: */
 /* bus - The Input Bus
  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2019 Red Hat, Inc.
+ * 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
@@ -499,11 +500,7 @@ ibus_component_output (IBusComponent *component,
 
         for (p = component->priv->observed_paths; p != NULL; p = p->next ) {
             IBusObservedPath *path = (IBusObservedPath *) p->data;
-
-            g_string_append_indent (output, indent + 2);
-            g_string_append_printf (output, "<path mtime=\"%ld\" >%s</path>\n",
-                                    path->mtime,
-                                    path->path);
+            ibus_observed_path_output (path, output, indent + 2);
         }
 
         g_string_append_indent (output, indent + 1);
diff --git a/src/ibusobservedpath.c b/src/ibusobservedpath.c
index 5b79f1fe..42192431 100644
--- a/src/ibusobservedpath.c
+++ b/src/ibusobservedpath.c
@@ -2,7 +2,8 @@
 /* vim:set et sts=4: */
 /* ibus - The Input IBus
  * Copyright (C) 2008-2015 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2019 Red Hat, Inc.
+ * 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
@@ -30,9 +31,9 @@ enum {
 };
 
 
-/* IBusObservedPathPriv */
+/* IBusObservedPathPrivate */
 struct _IBusObservedPathPrivate {
-    gpointer pad;
+    guint   *file_hash_list;
 };
 typedef struct _IBusObservedPathPrivate IBusObservedPathPrivate;
 
@@ -52,7 +53,9 @@ static gboolean  ibus_observed_path_copy            (IBusObservedPath       *des
 static gboolean  ibus_observed_path_parse_xml_node  (IBusObservedPath       *path,
                                                      XMLNode                *node);
 
-G_DEFINE_TYPE (IBusObservedPath, ibus_observed_path, IBUS_TYPE_SERIALIZABLE)
+G_DEFINE_TYPE_WITH_PRIVATE (IBusObservedPath,
+                            ibus_observed_path,
+                            IBUS_TYPE_SERIALIZABLE)
 
 static void
 ibus_observed_path_class_init (IBusObservedPathClass *class)
@@ -84,7 +87,9 @@ static gboolean
 ibus_observed_path_serialize (IBusObservedPath *path,
                               GVariantBuilder  *builder)
 {
+    IBusObservedPathPrivate *priv = IBUS_OBSERVED_PATH_GET_PRIVATE (path);
     gboolean retval;
+    guint i;
 
     retval = IBUS_SERIALIZABLE_CLASS (ibus_observed_path_parent_class)->
             serialize ((IBusSerializable *)path, builder);
@@ -93,6 +98,15 @@ ibus_observed_path_serialize (IBusObservedPath *path,
     g_variant_builder_add (builder, "s", path->path);
     g_variant_builder_add (builder, "x", path->mtime);
 
+    if (!priv->file_hash_list) {
+        g_variant_builder_add (builder, "u", 0);
+        return TRUE;
+    }
+    for (i = 0; priv->file_hash_list[i]; i++);
+    g_variant_builder_add (builder, "u", i);
+    for (i = 0; priv->file_hash_list[i]; i++)
+        g_variant_builder_add (builder, "u", priv->file_hash_list[i]);
+
     return TRUE;
 }
 
@@ -100,7 +114,9 @@ static gint
 ibus_observed_path_deserialize (IBusObservedPath *path,
                                 GVariant         *variant)
 {
+    IBusObservedPathPrivate *priv = IBUS_OBSERVED_PATH_GET_PRIVATE (path);
     gint retval;
+    guint i, length = 0;
 
     retval = IBUS_SERIALIZABLE_CLASS (ibus_observed_path_parent_class)->
             deserialize ((IBusSerializable *)path, variant);
@@ -109,6 +125,15 @@ ibus_observed_path_deserialize (IBusObservedPath *path,
     ibus_g_variant_get_child_string (variant, retval++, &path->path);
     g_variant_get_child (variant, retval++, "x", &path->mtime);
 
+    if (g_variant_n_children (variant) < retval + 2)
+        return retval;
+    g_variant_get_child (variant, retval++, "u", &length);
+    if (!length)
+        return retval;
+    priv->file_hash_list = g_new0 (guint, length + 1);
+    for (i = 0; i < length; i++)
+        g_variant_get_child (variant, retval++, "u", &priv->file_hash_list[i]);
+
     return retval;
 }
 
@@ -116,14 +141,27 @@ static gboolean
 ibus_observed_path_copy (IBusObservedPath       *dest,
                          const IBusObservedPath *src)
 {
+    IBusObservedPathPrivate *dest_priv = IBUS_OBSERVED_PATH_GET_PRIVATE (dest);
+    IBusObservedPathPrivate *src_priv =
+            IBUS_OBSERVED_PATH_GET_PRIVATE ((IBusObservedPath *)src);
     gboolean retval;
+    guint i;
 
-    retval = IBUS_SERIALIZABLE_CLASS (ibus_observed_path_parent_class)->copy ((IBusSerializable *)dest, (IBusSerializable *)src);
+    retval = IBUS_SERIALIZABLE_CLASS (ibus_observed_path_parent_class)->
+            copy ((IBusSerializable *)dest, (IBusSerializable *)src);
     g_return_val_if_fail (retval, FALSE);
 
     dest->path = g_strdup (src->path);
     dest->mtime = src->mtime;
 
+    g_clear_pointer (&dest_priv->file_hash_list, g_free);
+    if (!src_priv->file_hash_list)
+        return TRUE;
+    for (i = 0; src_priv->file_hash_list[i]; i++);
+    dest_priv->file_hash_list = g_new0 (guint, i + 1);
+    for (i = 0; src_priv->file_hash_list[i]; i++)
+        dest_priv->file_hash_list[i] = src_priv->file_hash_list[i];
+
     return TRUE;
 }
 
@@ -137,23 +175,48 @@ ibus_observed_path_copy (IBusObservedPath       *dest,
 
 void
 ibus_observed_path_output (IBusObservedPath *path,
-                          GString         *output,
-                          gint             indent)
+                           GString          *output,
+                           gint              indent)
 {
+    IBusObservedPathPrivate *priv = IBUS_OBSERVED_PATH_GET_PRIVATE (path);
+    guint i;
+
     g_assert (IBUS_IS_OBSERVED_PATH (path));
     g_assert (output);
 
-    g_string_append_indent (output, indent);
-    g_string_append_printf (output, "<path mtime=\"%ld\" >%s</path>\n",
-                                    path->mtime,
-                                    path->path);
+    if (!priv->file_hash_list) {
+        g_string_append_indent (output, indent);
+        g_string_append_printf (output, "<path mtime=\"%ld\" >%s</path>\n",
+                                path->mtime,
+                                path->path);
+    } else {
+        g_string_append_indent (output, indent);
+        g_string_append_printf (
+                output,
+                "<path mtime=\"%ld\" type=\"dir\" path=\"%s\">\n",
+                path->mtime,
+                path->path);
+        for (i = 0; priv->file_hash_list[i]; i++) {
+            g_string_append_indent (output, indent + 1);
+            g_string_append_printf (output, "<file hash=\"%u\" />\n",
+                                    priv->file_hash_list[i]);
+        }
+        g_string_append_indent (output, indent);
+        g_string_append_printf (output, "</path>\n");
+    }
 }
 
 gboolean
 ibus_observed_path_check_modification (IBusObservedPath *path)
 {
+    IBusObservedPathPrivate *priv = IBUS_OBSERVED_PATH_GET_PRIVATE (path);
     gchar *real_path = NULL;
     struct stat buf;
+    gboolean retval = FALSE;
+    GDir *dir = NULL;
+    const gchar *name;
+    guint i = 0;
+    guint file_num = 0;
 
     g_assert (IBUS_IS_OBSERVED_PATH (path));
 
@@ -169,11 +232,71 @@ ibus_observed_path_check_modification (IBusObservedPath *path)
         buf.st_mtime = 0;
     }
 
-    g_free (real_path);
 
-    if (path->mtime == buf.st_mtime)
-        return FALSE;
-    return TRUE;
+    if (path->mtime != buf.st_mtime) {
+        retval = TRUE;
+        goto end_check_modification;
+    }
+
+    /* If an ibus engine is installed, normal file system updates
+     * the directory mtime of "/usr/share/ibus/component" and
+     * path->mtime of the cache file and buf.st_mtime of the current directory
+     * could have the different values.
+     *
+     * But under a special file system, the buf.st_mtime is not updated
+     * even if an ibus engine is installed, likes Fedora Silverblue
+     * and ibus_observed_path_check_modification() could not detect
+     * the installed ibus engines.
+     * Now path->priv->file_hash_list reserves the hash list of the files
+     * in the observed directory and if a new ibus engine is installed,
+     * the hash of the compose file does not exists in the cache's
+     * file_hash_list and ibus-daemon regenerate the cache successfully.
+     */
+    if (!priv->file_hash_list) {
+        /* If the cache version is old, ibus_registry_load_cache() returns
+         * FALSE and ibus_registry_check_modification() and this are not
+         * called.
+         * If the cache version is the latest, the cache file includes the
+         * filled file_hash_list for directories with ibus_observed_path_new()
+         * when the cache was generated.
+         * Then if file_hash_list is null, it's a simple file in ibus
+         * components and return here simply.
+         */
+        goto end_check_modification;
+    }
+    dir = g_dir_open (real_path, 0, NULL);
+    g_return_val_if_fail (dir, FALSE);
+
+    while ((name = g_dir_read_name (dir)) != NULL) {
+        guint current_hash;
+        gboolean has_file = FALSE;
+
+        if (!g_str_has_suffix (name, ".xml"))
+            continue;
+        current_hash = g_str_hash (name);
+        for (i = 0; priv->file_hash_list[i]; i++) {
+            if (current_hash == priv->file_hash_list[i]) {
+                has_file = TRUE;
+                break;
+            }
+        }
+        if (!has_file) {
+            retval = TRUE;
+            goto end_check_modification;
+        }
+        file_num++;
+    }
+    if (!retval) {
+        for (i = 0; priv->file_hash_list[i]; i++);
+        if (file_num != i)
+            retval = TRUE;
+    }
+
+end_check_modification:
+    if (dir)
+        g_dir_close (dir);
+    g_free (real_path);
+    return retval;
 }
 
 static void
@@ -224,7 +347,7 @@ ibus_observed_path_traverse (IBusObservedPath *path,
             paths = g_list_append (paths, sub);
             paths = g_list_concat (paths,
                                    ibus_observed_path_traverse (sub, dir_only));
-        } else if (!dir_only) {
+        } else if (sub->is_exist && !dir_only) {
             paths = g_list_append (paths, sub);
         }
     }
@@ -233,36 +356,102 @@ ibus_observed_path_traverse (IBusObservedPath *path,
     return paths;
 }
 
+
+static gboolean
+ibus_observed_path_parse_file (IBusObservedPath *path,
+                               XMLNode          *node,
+                               int              *nth)
+{
+    IBusObservedPathPrivate *priv = IBUS_OBSERVED_PATH_GET_PRIVATE (path);
+    gchar **attr;
+
+    for (attr = node->attributes; attr[0]; attr += 2) {
+        guint hash = 0;
+
+        if (g_strcmp0 (*attr, "hash") == 0)
+            hash = atol (attr[1]);
+        else if (g_strcmp0 (*attr, "name") == 0)
+            hash = g_str_hash (attr[1]);
+        if (hash) {
+            if (!priv->file_hash_list) {
+                *nth = 0;
+                priv->file_hash_list = g_new0 (guint, *nth + 2);
+            } else {
+                priv->file_hash_list = g_renew (guint, priv->file_hash_list,
+                                                *nth + 2);
+            }
+            priv->file_hash_list[*nth] = hash;
+            priv->file_hash_list[*nth + 1] = 0;
+            *nth += 1;
+            continue;
+        }
+        g_warning ("Unkonwn attribute %s", attr[0]);
+    }
+
+    return TRUE;
+}
+
+
 static gboolean
 ibus_observed_path_parse_xml_node (IBusObservedPath *path,
                                    XMLNode          *node)
 {
+    gchar **attr;
+    const gchar *full_path = node->text;
+    GList *p;
+    int i = 0;
+
     g_assert (IBUS_IS_OBSERVED_PATH (path));
     g_assert (node);
 
-    if (G_UNLIKELY (g_strcmp0 (node->name, "path") != 0)) {
+    if (G_UNLIKELY (g_strcmp0 (node->name, "path") != 0))
         return FALSE;
+
+    for (attr = node->attributes; attr[0]; attr += 2) {
+        if (g_strcmp0 (*attr, "mtime") == 0) {
+            path->mtime = atol (attr[1]);
+            continue;
+        }
+        if (g_strcmp0 (*attr, "path") == 0) {
+            full_path = attr[1];
+            continue;
+        }
+        if (g_strcmp0 (*attr, "type") == 0) {
+            if (!g_strcmp0 (attr[1], "dir"))
+                path->is_dir = TRUE;
+            else if (!g_strcmp0 (attr[1], "file"))
+                path->is_dir = FALSE;
+            else
+                g_warning ("The type attribute can be \"dir\" or \"file\".");
+            continue;
+        }
+        g_warning ("Unkonwn attribute %s", attr[0]);
     }
 
-    if (node->text[0] == '~' && node->text[1] != G_DIR_SEPARATOR) {
-        g_warning ("Invalid path \"%s\"", node->text);
+    if (full_path[0] == '~' && full_path[1] != G_DIR_SEPARATOR) {
+        g_warning ("Invalid path \"%s\"", full_path);
         return FALSE;
     }
 
-    path->path = g_strdup (node->text);
+    path->path = g_strdup (full_path);
 
-    gchar **attr;
-    for (attr = node->attributes; attr[0]; attr += 2) {
-        if (g_strcmp0 (*attr, "mtime") == 0) {
-            path->mtime = atol (attr[1]);
+    if (!path->is_dir)
+        return TRUE;
+
+    for (i = 0, p = node->sub_nodes; p != NULL; p = p->next) {
+        XMLNode *sub_node = (XMLNode *)p->data;
+
+        if (G_UNLIKELY (g_strcmp0 (sub_node->name, "file") != 0)) {
+            g_warning ("Unkonwn tag %s", sub_node->name);
             continue;
         }
-        g_warning ("Unkonwn attribute %s", attr[0]);
+        ibus_observed_path_parse_file (path, sub_node, &i);
     }
 
     return TRUE;
 }
 
+
 IBusObservedPath *
 ibus_observed_path_new_from_xml_node (XMLNode *node,
                                      gboolean fill_stat)
@@ -288,16 +477,46 @@ IBusObservedPath *
 ibus_observed_path_new (const gchar *path,
                         gboolean     fill_stat)
 {
-    g_assert (path);
-
     IBusObservedPath *op;
+    IBusObservedPathPrivate *priv;
+    GList *file_list, *l;
+    guint i = 0;
 
+    g_assert (path);
     op = (IBusObservedPath *) g_object_new (IBUS_TYPE_OBSERVED_PATH, NULL);
     op->path = g_strdup (path);
 
-    if (fill_stat) {
-        ibus_observed_path_fill_stat (op);
+    priv = IBUS_OBSERVED_PATH_GET_PRIVATE (op);
+    l = file_list = ibus_observed_path_traverse (op, FALSE);
+    for (; l; l = l->next) {
+        IBusObservedPath *sub = l->data;
+        const gchar *file = NULL;
+
+        g_return_val_if_fail (sub && sub->path, op);
+
+        file = sub->path;
+        if (!g_str_has_suffix (file, ".xml"))
+            continue;
+        if (g_str_has_prefix (file, path)) {
+            file += strlen (path);
+            if (*file == '/')
+                file++;
+            /* Ignore sub directories */
+            if (strchr (file, '/'))
+                continue;
+        }
+        if (!i)
+            priv->file_hash_list = g_new0 (guint, i + 2);
+        else
+            priv->file_hash_list = g_renew (guint, priv->file_hash_list, i + 2);
+        priv->file_hash_list[i] = g_str_hash (file);
+        priv->file_hash_list[i + 1] = 0;
+        ++i;
     }
+    g_list_free_full (file_list, (GDestroyNotify)ibus_observed_path_destroy);
+
+    if (fill_stat)
+        ibus_observed_path_fill_stat (op);
 
     return op;
 }
diff --git a/src/ibusregistry.c b/src/ibusregistry.c
index 43990d5f..3386a5d1 100644
--- a/src/ibusregistry.c
+++ b/src/ibusregistry.c
@@ -2,8 +2,8 @@
 /* vim:set et sts=4: */
 /* bus - 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-2019 Red Hat, Inc.
+ * Copyright (C) 2015-2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2015-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
@@ -29,7 +29,7 @@
 #include "ibusregistry.h"
 
 #define IBUS_CACHE_MAGIC 0x49425553 /* "IBUS" */
-#define IBUS_CACHE_VERSION 0x00010512
+#define IBUS_CACHE_VERSION 0x00010522
 
 enum {
     CHANGED,
-- 
2.24.1

From 568d58dfadefa801b96058c1155daecff3d51605 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 21 Aug 2020 22:07:31 +0900
Subject: [PATCH] engine: Add layout_variant in gensimple.py

Missed to handle layout_variant and also add latam layouts to denylist

BUG=https://github.com/ibus/ibus/issues/2153
---
 engine/denylist.txt | 1 +
 engine/gensimple.py | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/engine/denylist.txt b/engine/denylist.txt
index e4cd0473..9cbc7fc3 100644
--- a/engine/denylist.txt
+++ b/engine/denylist.txt
@@ -24,4 +24,5 @@
 # Asterisk(*) character can be used to match any engines.
 # E.g. xkb:cn:*:* excludes xkb:cn::zho and xkb:cn:mon_trad:mvf
 xkb:cn:*:*
+xkb:latam:*:*
 xkb:nec_vndr/jp:*:*
diff --git a/engine/gensimple.py b/engine/gensimple.py
index dc4ccf12..18f7dc8c 100755
--- a/engine/gensimple.py
+++ b/engine/gensimple.py
@@ -180,6 +180,11 @@ class EvdevXML(XMLFilterBase):
             self.__downstream.startElement('layout', AttributesImpl({}))
             self.__downstream.characters(self.__layout)
             self.__downstream.endElement('layout')
+            if self.__variant != '':
+                self.__downstream.startElement('layout_variant',
+                                               AttributesImpl({}))
+                self.__downstream.characters(self.__variant)
+                self.__downstream.endElement('layout_variant')
             self.__downstream.startElement('longname', AttributesImpl({}))
             self.__downstream.characters(self.__description)
             self.__downstream.endElement('longname')
-- 
2.24.1