From 7b0d091839a4f1315ba216175fb2787e86f7fa31 Mon Sep 17 00:00:00 2001 From: fujiwarat 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 +# Copyright (c) 2018-2020 Takao Fujiwara # 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 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 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 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 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 - * 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 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 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 - * Copyright (C) 2011-2019 Takao Fujiwara - * Copyright (C) 2008-2019 Red Hat, Inc. + * Copyright (C) 2011-2020 Takao Fujiwara + * 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 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 - * Copyright (C) 2008-2013 Red Hat, Inc. + * Copyright (C) 2015-2020 Takao Fujiwara + * 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 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 , 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 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 @@ vertical False True - - - - - - True - False - <big><b>IBus</b></big> -<small>The intelligent input bus</small> -Homepage: https://github.com/ibus/ibus/wiki - - - - - True - center - - - True - True - 1 - - - - - False - True - 0 - none - - - Start ibus on login - False - True - True - False - False - True - start - True - 6 - 12 - - - - - True - False - <b>Startup</b> - True - - - - - False - False - 2 - - 4 -- 2.24.1 From 3f098dc51a718f3bd427873607dcd47865523dce Mon Sep 17 00:00:00 2001 From: Aaron Muir Hamilton 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 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 @@ Custom font Custom font name for language panel + + true + Choose glyphs with input method's language on candidate window + 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. + 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 @@ 1 + + + True + False + 0 + none + + + vertical + True + False + 6 + 6 + 24 + + + Choose glyphs with input method's language on candidate window + False + True + True + False + Choose glyphs with the input method's language on the candidate window for the duplicated code points + False + start + True + + + False + False + 0 + + + + + + + True + False + <b>Fonts</b> + True + + + + + False + False + 2 + + 3 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 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 +# Copyright (c) 2013-2020 Takao Fujiwara # # 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 +# +# 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 . + +# 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 +# +# 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 . + + +# 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 ' + _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('') + if index >= 0: + author = escape(self._AUTHOR) + contents = '%s%s\ +%s%s%s\ +%s%s%s\ +%s%s' % ( + 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 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|\(\).*\(/ibus-engine-simple\)|\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 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 - * Copyright (C) 2008-2019 Red Hat, Inc. + * Copyright (C) 2020 Takao Fujiwara + * 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, "%s\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 - * Copyright (C) 2008-2019 Red Hat, Inc. + * Copyright (C) 2020 Takao Fujiwara + * 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, "%s\n", - path->mtime, - path->path); + if (!priv->file_hash_list) { + g_string_append_indent (output, indent); + g_string_append_printf (output, "%s\n", + path->mtime, + path->path); + } else { + g_string_append_indent (output, indent); + g_string_append_printf ( + output, + "\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, "\n", + priv->file_hash_list[i]); + } + g_string_append_indent (output, indent); + g_string_append_printf (output, "\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 - * Copyright (C) 2015-2019 Takao Fujiwara - * Copyright (C) 2015-2019 Red Hat, Inc. + * Copyright (C) 2015-2020 Takao Fujiwara + * 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