public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Takao Fujiwara <tfujiwar@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/ibus] autotool: Generate simple.xml with denylist
Date: Sun, 31 May 2026 02:07:26 GMT	[thread overview]
Message-ID: <178019324676.1.10509689177030989725.rpms-ibus-97d0736b818e@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/ibus
            Branch : autotool
            Commit : 97d0736b818ea976ec65682ff6407069d09e4ed1
            Author : Takao Fujiwara <tfujiwar@redhat.com>
            Date   : 2020-08-21T13:03:13+09:00
            Stats  : +1500/-12 in 4 file(s)
            URL    : https://src.fedoraproject.org/rpms/ibus/c/97d0736b818ea976ec65682ff6407069d09e4ed1?branch=autotool

            Log:
            Generate simple.xml with denylist

- Tell Pango about the engine language in the candidate panel
- Add file list in registry file for Silverblue

---
diff --git a/.gitignore b/.gitignore
index 352772b..0119b7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,3 +61,4 @@ ibus-1.3.6.tar.gz
 /ibus-1.5.20.tar.gz
 /ibus-1.5.21.tar.gz
 /ibus-1.5.22.tar.gz
+/ibus-simple-1.5.22.20200821.xml.gz

diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 1ed4aac..3b145c5 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -597,3 +597,1490 @@ index b54ef817..59787a80 100644
 -- 
 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
+

diff --git a/ibus.spec b/ibus.spec
index fa09803..38fe2f7 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -7,12 +7,6 @@
 
 %global with_pkg_config %(pkg-config --version >/dev/null 2>&1 && echo -n "1" || echo -n "0")
 
-%if (0%{?fedora} > 21 || 0%{?rhel} > 7)
-%global with_kde5 1
-%else
-%global with_kde5 0
-%endif
-
 %global ibus_api_version 1.0
 
 # for bytecompile in %%{_datadir}/ibus/setup
@@ -32,13 +26,14 @@
 
 Name:           ibus
 Version:        1.5.22
-Release:        11%{?dist}
+Release:        12%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPLv2+
 URL:            https://github.com/ibus/%name/wiki
 Source0:        https://github.com/ibus/%name/releases/download/%{version}/%{name}-%{version}.tar.gz
 Source1:        %{name}-xinput
 Source2:        %{name}.conf.5
+Source3:        %{name}-simple-1.5.22.20200821.xml.gz
 # Patch0:         %%{name}-HEAD.patch
 Patch0:         %{name}-HEAD.patch
 # Under testing #1349148 #1385349 #1350291 #1406699 #1432252 #1601577
@@ -71,9 +66,7 @@ BuildRequires:  vala
 BuildRequires:  iso-codes-devel
 BuildRequires:  libnotify-devel
 BuildRequires:  wayland-devel
-%if %with_kde5
 BuildRequires:  qt5-qtbase-devel
-%endif
 BuildRequires:  cldr-emoji-annotation
 BuildRequires:  unicode-emoji
 BuildRequires:  unicode-ucd
@@ -254,6 +247,10 @@ the functionality of the installed %{name} package.
 # cp client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c || :
 # cp client/gtk2/ibusim.c client/gtk3/ibusim.c || :
 cp client/gtk2/ibusim.c client/gtk3/ibusim.c || :
+cd engine
+cp %{SOURCE3} simple.xml.gz
+gunzip simple.xml.gz
+cd ..
 
 
 # prep test
@@ -285,9 +282,6 @@ autoreconf -f -i -v
     --enable-python-library \
 %endif
     --enable-wayland \
-%if ! %with_kde5
-    --disable-appindicator \
-%endif
     --enable-introspection \
     --enable-install-tests \
     %{nil}
@@ -464,6 +458,11 @@ dconf update || :
 %{_datadir}/installed-tests/ibus
 
 %changelog
+* Fri Aug 21 2020 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.22-12
+- Generate simple.xml with denylist
+- Tell Pango about the engine language in the candidate panel
+- Add file list in registry file for Silverblue
+
 * Tue Jul 28 2020 Adam Jackson <ajax@redhat.com> - 1.5.22-11
 - Require setxkbmap not xorg-x11-xkb-utils
 

diff --git a/sources b/sources
index 2691b3b..4629f12 100644
--- a/sources
+++ b/sources
@@ -1 +1,2 @@
 SHA512 (ibus-1.5.22.tar.gz) = 0abe89acc6da8cea484a6b9f807c08e94869072f374f9e8f4541a426636f818f1c3cb8b9237f97245771f9e4bf19184983d8ac924177dc4824ca6e8b5304425d
+SHA512 (ibus-simple-1.5.22.20200821.xml.gz) = f7532078bd409da0742538745d5d03942dd738a9bea935f75c9c0d74ad9891ff62fe7982a7c64ab957079583a12316d593f7f05534fa78a5c741eb5fb19f9da7

                 reply	other threads:[~2026-05-31  2:07 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178019324676.1.10509689177030989725.rpms-ibus-97d0736b818e@fedoraproject.org \
    --to=tfujiwar@redhat.com \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox