Blob Blame History Raw
From 7edaefdc1d80aefdbbc2dc52526c20715759da83 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Wed, 22 Aug 2018 17:20:53 +0900
Subject: [PATCH] ui/gtk3: Do not clear unicode data when emoji annotation lang
 is changed

---
 ui/gtk3/emojier.vala | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
index 85dcdceb..637ae049 100644
--- a/ui/gtk3/emojier.vala
+++ b/ui/gtk3/emojier.vala
@@ -440,13 +440,17 @@ public class IBusEmojier : Gtk.ApplicationWindow {
         m_emoji_to_emoji_variants_dict =
                 new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
                                                                GLib.str_equal);
-        m_unicode_to_data_dict =
+        if (m_unicode_to_data_dict == null) {
+            m_unicode_to_data_dict =
                 new GLib.HashTable<unichar, IBus.UnicodeData>(
                         GLib.direct_hash,
                         GLib.direct_equal);
-        m_name_to_unicodes_dict =
+        }
+        if (m_name_to_unicodes_dict == null) {
+            m_name_to_unicodes_dict =
                 new GLib.HashTable<string, GLib.SList<unichar>>(GLib.str_hash,
                                                                 GLib.str_equal);
+        }
     }
 
 
-- 
2.17.1

From 28d22176aee6be97d88dd6c60fa5395c79563ec0 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 30 Aug 2018 12:57:33 +0900
Subject: [PATCH] ui/gtk3: Fix SEGV when type ASCII on emojier

Emojier still included Gtk.Entry, accepted key events in Wayland,
reset the lookup table and it caused SEGV because IBus.Text
is NULL in the lookup table in Emojier.get_current_candidate().
Now Gtk.Entry is deleted completely.

BUG=rhbz#1618682
---
 ui/gtk3/emojier.vala | 139 +------------------------------------------
 1 file changed, 1 insertion(+), 138 deletions(-)

diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
index 637ae049..0f455800 100644
--- a/ui/gtk3/emojier.vala
+++ b/ui/gtk3/emojier.vala
@@ -283,7 +283,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
 
     private ThemedRGBA m_rgba;
     private Gtk.Box m_vbox;
-    private EEntry m_entry;
     /* If emojier is emoji category list or Unicode category list,
      * m_annotation is "" and preedit is also "".
      * If emojier is candidate mode, m_annotation is an annotation and
@@ -367,23 +366,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
         m_vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
         add(m_vbox);
 
-        m_entry = new EEntry();
-        m_entry.set_placeholder_text(_("Type annotation or choose emoji"));
-        //m_vbox.add(m_entry);
-        m_entry.changed.connect(() => {
-            update_candidate_window();
-        });
-        m_entry.icon_release.connect((icon_pos, event) => {
-            hide_candidate_panel();
-        });
-
-        /* Set the accessible role of the label to a status bar so it
-         * will emit name changed events that can be used by screen
-         * readers.
-         */
-        Atk.Object obj = m_entry.get_accessible();
-        obj.set_role (Atk.Role.STATUSBAR);
-
         // The constructor of IBus.LookupTable does not support more than
         // 16 pages.
         m_lookup_table = new IBus.LookupTable(1, 0, true, true);
@@ -1806,18 +1788,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
                 m_lookup_table.cursor_up();
             else if (keyval == Gdk.Key.Right)
                 m_lookup_table.cursor_down();
-        } else if (m_entry.get_text().length > 0) {
-            int step = 0;
-            if (keyval == Gdk.Key.Left)
-                step = -1;
-            else if (keyval == Gdk.Key.Right)
-                step = 1;
-            GLib.Signal.emit_by_name(
-                    m_entry, "move-cursor",
-                    Gtk.MovementStep.VISUAL_POSITIONS,
-                    step,
-                    (modifiers & Gdk.ModifierType.SHIFT_MASK) != 0
-                            ? true : false);
         } else {
             // For Gdk.Key.f and Gdk.Key.b
             if (keyval == Gdk.Key.Left)
@@ -1880,20 +1850,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
             }
             return true;
         }
-        if (m_entry.get_text().length > 0) {
-            int step = 0;
-            if (keyval == Gdk.Key.Home)
-                step = -1;
-            else if (keyval == Gdk.Key.End)
-                step = 1;
-            GLib.Signal.emit_by_name(
-                    m_entry, "move-cursor",
-                    Gtk.MovementStep.DISPLAY_LINE_ENDS,
-                    step,
-                    (modifiers & Gdk.ModifierType.SHIFT_MASK) != 0
-                            ? true : false);
-            return true;
-        }
         return category_list_cursor_move(keyval);
     }
 
@@ -1941,28 +1897,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
     }
 
 
-    private void entry_enter_keyval(uint keyval) {
-        unichar ch = IBus.keyval_to_unicode(keyval);
-        if (ch.iscntrl())
-            return;
-        string str = ch.to_string();
-
-        // what gtk_entry_commit_cb() do
-        if (m_entry.get_selection_bounds(null, null)) {
-            m_entry.delete_selection();
-        } else {
-            if (m_entry.get_overwrite_mode()) {
-               uint text_length = m_entry.get_buffer().get_length();
-               if (m_entry.cursor_position < text_length)
-                   m_entry.delete_from_cursor(Gtk.DeleteType.CHARS, 1);
-            }
-        }
-        int pos = m_entry.get_position();
-        m_entry.insert_text(str, -1, ref pos);
-        m_entry.set_position(pos);
-    }
-
-
     private Gdk.Rectangle get_monitor_geometry() {
         Gdk.Rectangle monitor_area = { 0, };
 
@@ -2245,10 +2179,7 @@ public class IBusEmojier : Gtk.ApplicationWindow {
         /* Let gtk recalculate the window size. */
         resize(1, 1);
 
-        m_entry.set_text("");
-
         show_category_list();
-        m_entry.set_activates_default(true);
         show_all();
 
         /* Some window managers, e.g. MATE, GNOME, Plasma desktops,
@@ -2289,13 +2220,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
         m_loop.run();
         m_loop = null;
 
-        // Need focus-out on Gtk.Entry to send the emoji to applications.
-        Gdk.Event fevent = new Gdk.Event(Gdk.EventType.FOCUS_CHANGE);
-        fevent.focus_change.in = 0;
-        fevent.focus_change.window  = get_window();
-        m_entry.send_focus_change(fevent);
-        fevent.focus_change.window  = null;
-
         hide();
         // Make sure the switcher is hidden before returning from this function.
         while (Gtk.events_pending())
@@ -2357,36 +2281,9 @@ public class IBusEmojier : Gtk.ApplicationWindow {
                 hide();
             }
             return true;
-        case Gdk.Key.BackSpace:
-            if (m_entry.get_text().length > 0) {
-                if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
-                    GLib.Signal.emit_by_name(m_entry, "delete-from-cursor",
-                                             Gtk.DeleteType.WORD_ENDS, -1);
-                } else {
-                    GLib.Signal.emit_by_name(m_entry, "backspace");
-                }
-                return true;
-            }
-            break;
-        case Gdk.Key.Delete:
-        case Gdk.Key.KP_Delete:
-            if (m_entry.get_text().length > 0) {
-                if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
-                    GLib.Signal.emit_by_name(m_entry, "delete-from-cursor",
-                                             Gtk.DeleteType.WORD_ENDS, 1);
-                } else {
-                    GLib.Signal.emit_by_name(m_entry, "delete-from-cursor",
-                                             Gtk.DeleteType.CHARS, 1);
-                }
-                return true;
-            }
-            break;
         case Gdk.Key.space:
         case Gdk.Key.KP_Space:
-            if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
-                if (m_entry.get_text().length > 0)
-                    entry_enter_keyval(keyval);
-            } else if (m_candidate_panel_is_visible) {
+            if (m_candidate_panel_is_visible) {
                 enter_notify_disable_with_timer();
                 m_lookup_table.cursor_down();
                 show_candidate_panel();
@@ -2436,10 +2333,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
             key_press_cursor_home_end(Gdk.Key.End, modifiers);
             show_all();
             return true;
-        case Gdk.Key.Insert:
-        case Gdk.Key.KP_Insert:
-            GLib.Signal.emit_by_name(m_entry, "toggle-overwrite");
-            return true;
         }
 
         if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
@@ -2470,27 +2363,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
                 key_press_cursor_home_end(Gdk.Key.End, modifiers);
                 show_all();
                 return true;
-            case Gdk.Key.u:
-                if (m_entry.get_text().length > 0) {
-                    GLib.Signal.emit_by_name(m_entry,
-                                             "delete-from-cursor",
-                                             Gtk.DeleteType.PARAGRAPH_ENDS,
-                                             -1);
-                    return true;
-                }
-                break;
-            case Gdk.Key.a:
-                if (m_entry.get_text().length > 0) {
-                    m_entry.select_region(0, -1);
-                    return true;
-                }
-                break;
-            case Gdk.Key.x:
-                if (m_entry.get_text().length > 0) {
-                    GLib.Signal.emit_by_name(m_entry, "cut-clipboard");
-                    return true;
-                }
-                break;
             case Gdk.Key.C:
             case Gdk.Key.c:
                 if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
@@ -2503,19 +2375,11 @@ public class IBusEmojier : Gtk.ApplicationWindow {
                         clipboard.store();
                         return true;
                     }
-                } else if (m_entry.get_text().length > 0) {
-                    GLib.Signal.emit_by_name(m_entry, "copy-clipboard");
-                    return true;
                 }
                 break;
-            case Gdk.Key.v:
-                GLib.Signal.emit_by_name(m_entry, "paste-clipboard");
-                return true;
             }
             return false;
         }
-
-        entry_enter_keyval(keyval);
         return true;
     }
 
@@ -2595,7 +2459,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
 
         uint32 timestamp = event.get_time();
         present_with_time(timestamp);
-        m_entry.set_activates_default(true);
     }
 
 
-- 
2.17.1

From e6badb494e0a31b7aca3a5078a5dc5b27b83390d Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 30 Aug 2018 12:57:46 +0900
Subject: [PATCH] ui/gtk3: Support Shift-Space to insert a Space on Emojier
 preedit

Implemented Shift-Space on preedit since Shift-Space had worked on
Emojier's GtkEntry in the previous release.
---
 ui/gtk3/panelbinding.vala | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala
index 981b5509..4ebff8da 100644
--- a/ui/gtk3/panelbinding.vala
+++ b/ui/gtk3/panelbinding.vala
@@ -548,6 +548,19 @@ class PanelBinding : IBus.PanelService {
     }
 
 
+    private bool key_press_keyval(uint keyval) {
+        unichar ch = IBus.keyval_to_unicode(keyval);
+        if (ch.iscntrl())
+                return false;
+        string str = ch.to_string();
+        m_preedit.append_text(str);
+        string annotation = m_preedit.get_text();
+        m_emojier.set_annotation(annotation);
+        m_preedit.set_emoji("");
+        return true;
+    }
+
+
     private bool key_press_enter() {
         if (m_extension_name != "unicode" && is_emoji_lookup_table()) {
             // Check if variats exist
@@ -899,6 +912,12 @@ class PanelBinding : IBus.PanelService {
             break;
         case Gdk.Key.space:
         case Gdk.Key.KP_Space:
+            if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
+                if (!key_press_keyval(keyval))
+                    return true;
+                show_candidate = is_emoji_lookup_table();
+                break;
+            }
             show_candidate = key_press_space();
             if (m_extension_name == "unicode") {
                 key_press_enter();
@@ -979,14 +998,8 @@ class PanelBinding : IBus.PanelService {
                 show_candidate = key_press_control_keyval(keyval, modifiers);
                 break;
             }
-            unichar ch = IBus.keyval_to_unicode(keyval);
-            if (ch.iscntrl())
+            if (!key_press_keyval(keyval))
                 return true;
-            string str = ch.to_string();
-            m_preedit.append_text(str);
-            string annotation = m_preedit.get_text();
-            m_emojier.set_annotation(annotation);
-            m_preedit.set_emoji("");
             show_candidate = is_emoji_lookup_table();
             break;
         }
-- 
2.17.1

From 809d880337e75b7cee429292a238bf53899bef6a Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 30 Aug 2018 12:58:57 +0900
Subject: [PATCH] ui/gtk3: Do not move Emojier popup with the active
 candidate in Xorg

Probably I think it's not useful to change the popup position frequently.
The popup size is always slightly changed with the emoji annotation length.
---
 ui/gtk3/emojier.vala | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
index 0f455800..9811fde5 100644
--- a/ui/gtk3/emojier.vala
+++ b/ui/gtk3/emojier.vala
@@ -1944,7 +1944,15 @@ public class IBusEmojier : Gtk.ApplicationWindow {
             x = 0;
 
         bool changed = false;
-        if (window_right_bottom.y > monitor_bottom) {
+        // Do not up side down frequently.
+        // The first pos does not show the lookup table yet but the
+        // preedit only and the second pos shows the lookup table.
+        if (m_lookup_table.get_cursor_pos() != 1) {
+            if (m_is_up_side_down)
+                y = m_cursor_location.y - allocation.height;
+            else
+                y = cursor_right_bottom.y;
+        } else if (window_right_bottom.y > monitor_bottom) {
             y = m_cursor_location.y - allocation.height;
             // Do not up side down in Wayland
             if (m_input_context_path == "") {
-- 
2.17.1

From 1c6565e205528a45e88a84ba2a328f9035875c8d Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 14 Sep 2018 16:15:41 +0900
Subject: [PATCH] ui/gtk3: Fix SEGV when commit an emoji on Emojier in Wayland

Just pressing Space key without emoji annotations can launch Emojier
popup and the popup takes a focus in Wayland and the chosen emoji is
output when the original text application gets the focus after Emojier
popup release the focus. Emojier disabled Ctrl-Shift-e after got the focus.
But currently GNOME Wayland has a bug not to send focus-in until a
key press or mouse click happens [1] and Emojier causes a SEGV.
Now Emojier disables Ctrl-Shift-e immediately when an emoji is chosen
whether focus-in comes or not and fixes the SEGV.

[1] https://gitlab.gnome.org/GNOME/gnome-shell/issues/573

BUG=rhbz#1625187
---
 ui/gtk3/emojier.vala      | 63 +++++++-------------------------------
 ui/gtk3/emojierapp.vala   |  2 +-
 ui/gtk3/panelbinding.vala | 64 ++++++++++++++++++++++++++-------------
 3 files changed, 55 insertions(+), 74 deletions(-)

diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
index 9811fde5..e23ef889 100644
--- a/ui/gtk3/emojier.vala
+++ b/ui/gtk3/emojier.vala
@@ -21,17 +21,6 @@
  */
 
 public class IBusEmojier : Gtk.ApplicationWindow {
-    private class EEntry : Gtk.SearchEntry {
-        public EEntry() {
-            GLib.Object(
-                name : "IBusEmojierEntry",
-                margin_start : 6,
-                margin_end : 6,
-                margin_top : 6,
-                margin_bottom : 6
-            );
-        }
-    }
     private class EListBox : Gtk.ListBox {
         public EListBox() {
             GLib.Object(
@@ -330,6 +319,7 @@ public class IBusEmojier : Gtk.ApplicationWindow {
     private uint m_redraw_window_id;
 
     public signal void candidate_clicked(uint index, uint button, uint state);
+    public signal void commit_text(string text);
 
     public IBusEmojier() {
         GLib.Object(
@@ -380,12 +370,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
             adjust_window_position();
         });
 
-        candidate_clicked.connect((i, b, s) => {
-            if (m_input_context_path != "")
-                candidate_panel_select_index(i, b);
-        });
-
-
         if (m_annotation_to_emojis_dict == null) {
             reload_emoji_dict();
         }
@@ -1641,34 +1625,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
     }
 
 
-    private void candidate_panel_select_index(uint index,
-                                              uint button) {
-        if (button == BUTTON_CLOSE_BUTTON) {
-            hide();
-            if (m_candidate_panel_mode &&
-                m_lookup_table.get_number_of_candidates() > 0) {
-                // Call remove_all_children() instead of show_category_list()
-                // so that show_category_list do not remove children with
-                // PageUp/PageDown.
-                remove_all_children();
-            }
-            m_result = "";
-            return;
-        }
-        string text = m_lookup_table.get_candidate(index).text;
-        unowned GLib.SList<string>? emojis =
-                m_emoji_to_emoji_variants_dict.lookup(text);
-        if (m_show_emoji_variant && emojis != null &&
-            m_backward_index < 0) {
-            show_emoji_variants(emojis);
-            show_all();
-        } else {
-            m_result = text;
-            hide();
-        }
-    }
-
-
     private void candidate_panel_cursor_down() {
         enter_notify_disable_with_timer();
         uint ncandidates = m_lookup_table.get_number_of_candidates();
@@ -1762,7 +1718,8 @@ public class IBusEmojier : Gtk.ApplicationWindow {
     }
 
 
-    public bool has_variants(uint index) {
+    public bool has_variants(uint index,
+                             bool need_commit_signal) {
         if (index >= m_lookup_table.get_number_of_candidates())
             return false;
         string text = m_lookup_table.get_candidate(index).text;
@@ -1773,6 +1730,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
             show_emoji_variants(emojis);
             return true;
         }
+        if (m_input_context_path != "")
+            m_result = text;
+        if (need_commit_signal)
+            commit_text(text);
         return false;
     }
 
@@ -1881,10 +1842,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
     }
 
 
-    public bool key_press_enter() {
+    public bool key_press_enter(bool need_commit_signal) {
         if (m_candidate_panel_is_visible) {
             uint index = m_lookup_table.get_cursor_pos();
-            return has_variants(index);
+            return has_variants(index, need_commit_signal);
         } else if (m_category_active_index >= 0) {
             Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
             EBoxRow row = gtkrow as EBoxRow;
@@ -2282,12 +2243,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
             return true;
         case Gdk.Key.Return:
         case Gdk.Key.KP_Enter:
-            if (key_press_enter()) {
+            if (key_press_enter(true))
                 show_all();
-            } else {
-                m_result = get_current_candidate();
+            else
                 hide();
-            }
             return true;
         case Gdk.Key.space:
         case Gdk.Key.KP_Space:
diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
index 787d448f..fab99d9e 100644
--- a/ui/gtk3/emojierapp.vala
+++ b/ui/gtk3/emojierapp.vala
@@ -65,7 +65,7 @@ public class EmojiApplication : Gtk.Application {
         uint ncandidates = m_emojier.get_number_of_candidates();
         if (ncandidates > 0 && ncandidates >= index) {
             m_emojier.set_cursor_pos(index);
-            show_candidate = m_emojier.has_variants(index);
+            show_candidate = m_emojier.has_variants(index, false);
         } else {
             return;
         }
diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala
index 4ebff8da..01c43b0d 100644
--- a/ui/gtk3/panelbinding.vala
+++ b/ui/gtk3/panelbinding.vala
@@ -447,13 +447,19 @@ class PanelBinding : IBus.PanelService {
     }
 
 
-    private void commit_text_update_favorites(IBus.Text text) {
+    private void commit_text_update_favorites(IBus.Text text,
+                                              bool      disable_extension) {
         commit_text(text);
-        IBus.ExtensionEvent event = new IBus.ExtensionEvent(
+
+        // If disable_extension is false, the extension event is already
+        // sent before the focus-in is received.
+        if (disable_extension) {
+            IBus.ExtensionEvent event = new IBus.ExtensionEvent(
                     "name", m_extension_name,
                     "is-enabled", false,
                     "is-extension", true);
-        panel_extension(event);
+            panel_extension(event);
+        }
         string committed_string = text.text;
         string preedit_string = m_preedit.get_text();
         m_preedit.hide();
@@ -482,7 +488,7 @@ class PanelBinding : IBus.PanelService {
             prev_context_path != "" &&
             prev_context_path == m_current_context_path) {
             IBus.Text text = new IBus.Text.from_string(selected_string);
-            commit_text_update_favorites(text);
+            commit_text_update_favorites(text, false);
             m_emojier.reset();
             return true;
         }
@@ -564,13 +570,13 @@ class PanelBinding : IBus.PanelService {
     private bool key_press_enter() {
         if (m_extension_name != "unicode" && is_emoji_lookup_table()) {
             // Check if variats exist
-            if (m_emojier.key_press_enter()) {
+            if (m_emojier.key_press_enter(false)) {
                 convert_preedit_text();
                 return true;
             }
         }
         IBus.Text text = m_preedit.get_commit_text();
-        commit_text_update_favorites(text);
+        commit_text_update_favorites(text, true);
         return false;
     }
 
@@ -712,15 +718,10 @@ class PanelBinding : IBus.PanelService {
     }
 
 
-    private bool is_visible_wayland_lookup_table() {
-        return m_wayland_lookup_table_is_visible;
-    }
-
-
     private void hide_emoji_lookup_table() {
         if (m_emojier == null)
             return;
-        if (m_is_wayland)
+        if (m_wayland_lookup_table_is_visible)
             hide_wayland_lookup_table();
         else
             m_emojier.hide();
@@ -747,7 +748,7 @@ class PanelBinding : IBus.PanelService {
 
     private bool is_emoji_lookup_table() {
         if (m_is_wayland)
-            return is_visible_wayland_lookup_table();
+            return m_wayland_lookup_table_is_visible;
         else
             return m_emojier.get_visible();
     }
@@ -788,7 +789,8 @@ class PanelBinding : IBus.PanelService {
          */
         if (!input_context_path.has_suffix("InputContext_1")) {
             m_real_current_context_path = m_current_context_path;
-            this.emojier_focus_commit();
+            if (m_is_wayland)
+                this.emojier_focus_commit();
         }
     }
 
@@ -822,8 +824,18 @@ class PanelBinding : IBus.PanelService {
             // For title handling in gnome-shell
             m_application.add_window(m_emojier);
             m_emojier.candidate_clicked.connect((i, b, s) => {
+                candidate_clicked_lookup_table_real(i, b, s, true);
+            });
+            m_emojier.commit_text.connect((s) => {
                 if (!m_is_wayland)
-                    candidate_clicked_lookup_table(i, b, s);
+                    return;
+                // Currently emojier has a focus but the text input focus
+                // does not and commit the text later.
+                IBus.ExtensionEvent close_event = new IBus.ExtensionEvent(
+                        "name", m_extension_name,
+                        "is-enabled", false,
+                        "is-extension", true);
+                panel_extension(close_event);
             });
         }
         m_emojier.reset();
@@ -1041,9 +1053,10 @@ class PanelBinding : IBus.PanelService {
         show_preedit_and_candidate(show_candidate);
     }
 
-    public override void candidate_clicked_lookup_table(uint index,
-                                                        uint button,
-                                                        uint state) {
+    private void candidate_clicked_lookup_table_real(uint index,
+                                                     uint button,
+                                                     uint state,
+                                                     bool is_emojier) {
         if (button == IBusEmojier.BUTTON_CLOSE_BUTTON) {
             m_enable_extension = false;
             hide_emoji_lookup_table();
@@ -1061,17 +1074,26 @@ class PanelBinding : IBus.PanelService {
         uint ncandidates = m_emojier.get_number_of_candidates();
         if (ncandidates > 0 && ncandidates >= index) {
             m_emojier.set_cursor_pos(index);
-            show_candidate = m_emojier.has_variants(index);
-            m_preedit.set_emoji(m_emojier.get_current_candidate());
+            bool need_commit_signal = m_is_wayland && is_emojier;
+            show_candidate = m_emojier.has_variants(index, need_commit_signal);
+            if (!m_is_wayland)
+                m_preedit.set_emoji(m_emojier.get_current_candidate());
         } else {
             return;
         }
         if (!show_candidate) {
             IBus.Text text = m_preedit.get_commit_text();
-            commit_text_update_favorites(text);
             hide_emoji_lookup_table();
+            if (!is_emojier || !m_is_wayland)
+                commit_text_update_favorites(text, true);
             return;
         }
         show_preedit_and_candidate(show_candidate);
     }
+
+    public override void candidate_clicked_lookup_table(uint index,
+                                                        uint button,
+                                                        uint state) {
+        candidate_clicked_lookup_table_real(index, button, state, false);
+    }
 }
-- 
2.17.1

From c1b55431c076dfa3fc26a3a998bfcf729e9ba602 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 26 Oct 2018 18:44:35 +0900
Subject: [PATCH] src/tests: Fix make check in Fedora 29

ibus-bus and ibus-compose failed in Fedora 29.

1. In ibus-bus with runtest, ibus-daemon failed to restart in
start_exit_async() because it seems to have conflicting IO with runtest
and ibus-daemon failed to close a file descriptor in _restart_server().
The solution is to add a sleep in start_exit_async().
Also added ibus_get_address() in test_async_apis_finish() to check
if ibus-daemon finished to restart.

2. In ibus-compose, the GTK application could not get the ibus module.
The solution is to export GTK_IM_MODULE=ibus.

3. Added DISABLE_DAEMONIZE_IN_TESTS to get error messages in ibus-daemon.
% make DISABLE_DAEMONIZE_IN_TESTS=1 check
---
 bus/Makefile.am       |  1 +
 src/tests/Makefile.am |  1 +
 src/tests/ibus-bus.c  | 15 ++++++++++++++-
 src/tests/runtest     | 24 +++++++++++++++++-------
 4 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/bus/Makefile.am b/bus/Makefile.am
index bdae5c92..4383a874 100644
--- a/bus/Makefile.am
+++ b/bus/Makefile.am
@@ -124,6 +124,7 @@ TESTS_ENVIRONMENT = \
     srcdir=$(srcdir) \
     LD_LIBRARY_PATH="$(top_builddir)/src/.libs:$(top_builddir)/src" \
     DISABLE_GUI_TESTS="$(DISABLE_GUI_TESTS)" \
+    DISABLE_DAEMONIZE_IN_TESTS="$(DISABLE_DAEMONIZE_IN_TESTS)" \
     $(NULL)
 
 LOG_COMPILER = $(top_srcdir)/src/tests/runtest
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index c5fef3c8..e337a59b 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -73,6 +73,7 @@ TESTS_ENVIRONMENT = \
     srcdir=$(srcdir) \
     LD_LIBRARY_PATH="$(top_builddir)/src/.libs:$(top_builddir)/src" \
     DISABLE_GUI_TESTS="$(DISABLE_GUI_TESTS)" \
+    DISABLE_DAEMONIZE_IN_TESTS="$(DISABLE_DAEMONIZE_IN_TESTS)" \
     $(NULL)
 
 LOG_COMPILER = $(srcdir)/runtest
diff --git a/src/tests/ibus-bus.c b/src/tests/ibus-bus.c
index 7fa1bc4a..0bf9e612 100644
--- a/src/tests/ibus-bus.c
+++ b/src/tests/ibus-bus.c
@@ -820,6 +820,14 @@ finish_exit_async (GObject *source_object,
 static void
 start_exit_async (void)
 {
+    /* When `./runtest ibus-bus` runs, ibus-daemon sometimes failed to
+     * restart because closing a file descriptor was failed in
+     * bus/server.c:_restart_server() with a following error:
+     *     "inotify read(): Bad file descriptor"
+     * Now g_usleep() is added here to write down the buffer and not to
+     * fail to restart ibus-daemon.
+     */
+    g_usleep (G_USEC_PER_SEC);
     ibus_bus_exit_async (bus,
                          TRUE, /* restart */
                          -1, /* timeout */
@@ -831,6 +839,9 @@ start_exit_async (void)
 static gboolean
 test_async_apis_finish (gpointer user_data)
 {
+    /* INFO: g_warning() causes SEGV with runtest script */
+    if (ibus_get_address () == NULL)
+        g_warning ("ibus-daemon does not restart yet from start_exit_async().");
     ibus_quit ();
     return FALSE;
 }
@@ -906,7 +917,9 @@ call_next_async_function (void)
     };
     static guint index = 0;
 
-    // Use g_timeout_add to make sure test_async_apis finishes even if async_functions is empty.
+    /* Use g_timeout_add to make sure test_async_apis finishes even if
+     * async_functions is empty.
+     */
     if (index >= G_N_ELEMENTS (async_functions))
         g_timeout_add (1, test_async_apis_finish, NULL);
     else
diff --git a/src/tests/runtest b/src/tests/runtest
index d7f96ea3..ab39e9f2 100755
--- a/src/tests/runtest
+++ b/src/tests/runtest
@@ -22,6 +22,7 @@
 : ${builddir:=.}
 : ${srcdir:=.}
 : ${DISABLE_GUI_TESTS:=''}
+: ${DISABLE_DAEMONIZE_IN_TESTS:=''}
 
 BUS_REQUIRED_TESTS="
 ibus-bus
@@ -162,16 +163,25 @@ run_test_case()
         export GSETTINGS_SCHEMA_DIR=$PWD
 
         # Start ibus-daemon.
-        ../$top_builddir/bus/ibus-daemon \
-        --daemonize \
-        --cache=none \
-        --panel=disable \
-        --emoji-extension=disable \
-        --config=default \
-        --verbose;
+        DAEMON_ARGS='
+            --cache=none
+            --panel=disable
+            --emoji-extension=disable
+            --config=default
+            --verbose
+        '
+        if test x"$DISABLE_DAEMONIZE_IN_TESTS" = x ; then
+            ../$top_builddir/bus/ibus-daemon \
+                $DAEMON_ARGS --daemonize;
+        else
+            ../$top_builddir/bus/ibus-daemon \
+                $DAEMON_ARGS &
+        fi
 
         # Wait until all necessary components are up.
         sleep 1
+
+        export GTK_IM_MODULE=ibus
     fi
 
     "../$tst" ${1+"$@"}
-- 
2.17.1

From 3172c3b23faefe76b3b7adfc75f9be34a0fb2e02 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Wed, 31 Oct 2018 17:42:38 +0900
Subject: [PATCH] RHEL code reviews

---
 src/ibuskeymap.c          | 2 +-
 src/ibuspanelservice.c    | 6 +++++-
 src/tests/ibus-keypress.c | 2 +-
 util/IMdkit/FrameMgr.c    | 1 +
 4 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/ibuskeymap.c b/src/ibuskeymap.c
index 27a56754..5abfb99a 100644
--- a/src/ibuskeymap.c
+++ b/src/ibuskeymap.c
@@ -143,7 +143,7 @@ ibus_keymap_parse_line (gchar  *str,
     /* Do not assign *p1 to g_ascii_isalpha() directly for the syntax check */
     if (i == 0 &&
         strncmp (p2, "addupper", sizeof ("addupper") - 1) == 0 &&
-        (ch = *p1) && g_ascii_isalpha (ch)) {
+        (ch = *p1) && (ch >= 0) && g_ascii_isalpha (ch)) {
         gchar buf[] = "a";
         buf[0] = g_ascii_toupper(ch);
         keymap[keycode][0] = keymap[keycode][3] = keysym;
diff --git a/src/ibuspanelservice.c b/src/ibuspanelservice.c
index 9d87e19b..984cc890 100644
--- a/src/ibuspanelservice.c
+++ b/src/ibuspanelservice.c
@@ -1615,7 +1615,11 @@ ibus_panel_service_panel_extension_register_keys (IBusPanelService   *panel,
     va_start (var_args, first_property_name);
     do {
         keys = va_arg (var_args, IBusProcessKeyEventData *);
-        g_return_if_fail (keys != NULL);
+        if (keys == NULL) {
+            va_end (var_args);
+            g_warning ("Failed to va_arg for IBusProcessKeyEventData");
+            return;
+        }
         g_variant_builder_init (&child, G_VARIANT_TYPE ("av"));
         for (; keys; keys++) {
             if (keys->keyval == 0 && keys->keycode == 0 && keys->state == 0)
diff --git a/src/tests/ibus-keypress.c b/src/tests/ibus-keypress.c
index 3486523b..17920226 100644
--- a/src/tests/ibus-keypress.c
+++ b/src/tests/ibus-keypress.c
@@ -173,7 +173,7 @@ set_engine_cb (GObject      *object,
     IBusBus *bus = IBUS_BUS (object);
     GtkWidget *entry = GTK_WIDGET (data);
     GdkDisplay *display;
-    Display *xdisplay;
+    Display *xdisplay = NULL;
     GError *error = NULL;
     int i, j;
 
diff --git a/util/IMdkit/FrameMgr.c b/util/IMdkit/FrameMgr.c
index 084b8810..0e91b78e 100644
--- a/util/IMdkit/FrameMgr.c
+++ b/util/IMdkit/FrameMgr.c
@@ -1414,6 +1414,7 @@ static int FrameInstGetSize (FrameInst fi)
 	    break;
         }
         /*endswitch*/
+        assert (i >= 0);
         i = _FrameInstIncrement (fi->template, i);
     }
     /*endwhile*/
-- 
2.17.1

From a40631e166137c9042a68c2d76844e7afc53d388 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 9 Nov 2018 14:49:44 +0900
Subject: [PATCH] Detect mouse click to commit Hangul preedit

If preedit text is not committed with the mouse click, preedit text
is moved to the new cursor position in Hangul typing.
Since set_cursor_location() is received before the reset() signal is
sent to ibus-daemon and commit_text() signal is received from
ibus-daemon, UpdatePreeditTextWithMode D-Bus method is newly added
and now ibus clients commit the preedit.

BUG=https://github.com/ibus/ibus/issues/1980
---
 bus/ibusimpl.c              |  11 ++++
 bus/inputcontext.c          | 108 ++++++++++++++++++++++++-------
 bus/inputcontext.h          |  19 +++++-
 client/gtk2/ibusimcontext.c |  95 +++++++++++++++++++++++++---
 src/ibusinputcontext.c      | 122 ++++++++++++++++++++++++++++++++----
 src/ibusinputcontext.h      |  27 +++++++-
 6 files changed, 338 insertions(+), 44 deletions(-)

diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
index 80f3acfb..bbbb5770 100644
--- a/bus/ibusimpl.c
+++ b/bus/ibusimpl.c
@@ -815,6 +815,17 @@ bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
             engine = bus_input_context_get_engine (ibus->focused_context);
             if (engine) {
                 g_object_ref (engine);
+                /* _ic_focus_in() can be called before _ic_focus_out() is
+                 * called under the async processes of two ibus clients.
+                 * E.g. gedit is a little slower v.s. a simple GtkTextView
+                 * application is the fastest when you click a Hangul
+                 * preedit text between the applications.
+                 * preedit will be committed with focus-out in the ibus client
+                 * likes ibus-im.so
+                 * so do not commit preedit here in focus-in event.
+                 */
+                bus_input_context_clear_preedit_text (ibus->focused_context,
+                                                      FALSE);
                 bus_input_context_set_engine (ibus->focused_context, NULL);
                 bus_input_context_set_emoji_extension (ibus->focused_context,
                                                        NULL);
diff --git a/bus/inputcontext.c b/bus/inputcontext.c
index 4f98b849..1b8e7adb 100644
--- a/bus/inputcontext.c
+++ b/bus/inputcontext.c
@@ -73,6 +73,7 @@ struct _BusInputContext {
     guint     preedit_cursor_pos;
     gboolean  preedit_visible;
     guint     preedit_mode;
+    gboolean  client_commit_preedit;
 
     /* auxiliary text */
     IBusText *auxiliary_text;
@@ -212,6 +213,9 @@ static IBusPropList    *props_empty = NULL;
 static const gchar introspection_xml[] =
     "<node>"
     "  <interface name='org.freedesktop.IBus.InputContext'>"
+    /* properties */
+    "    <property name='ContentType' type='(uu)' access='write' />"
+    "    <property name='ClientCommitPreedit' type='(b)' access='write' />\n"
     /* methods */
     "    <method name='ProcessKeyEvent'>"
     "      <arg direction='in'  type='u' name='keyval' />"
@@ -273,6 +277,12 @@ static const gchar introspection_xml[] =
     "      <arg type='u' name='cursor_pos' />"
     "      <arg type='b' name='visible' />"
     "    </signal>"
+    "    <signal name='UpdatePreeditTextWithMode'>"
+    "      <arg type='v' name='text' />"
+    "      <arg type='u' name='cursor_pos' />"
+    "      <arg type='b' name='visible' />"
+    "      <arg type='u' name='mode' />"
+    "    </signal>"
     "    <signal name='ShowPreeditText'/>"
     "    <signal name='HidePreeditText'/>"
     "    <signal name='UpdateAuxiliaryText'>"
@@ -297,9 +307,6 @@ static const gchar introspection_xml[] =
     "    <signal name='UpdateProperty'>"
     "      <arg type='v' name='prop' />"
     "    </signal>"
-
-    /* properties */
-    "    <property name='ContentType' type='(uu)' access='write' />"
     "  </interface>"
     "</node>";
 
@@ -1069,6 +1076,12 @@ _ic_reset (BusInputContext       *context,
            GDBusMethodInvocation *invocation)
 {
     if (context->engine) {
+        if (context->preedit_mode == IBUS_ENGINE_PREEDIT_COMMIT) {
+            if (context->client_commit_preedit)
+               bus_input_context_clear_preedit_text (context, FALSE);
+            else
+               bus_input_context_clear_preedit_text (context, TRUE);
+        }
         bus_engine_proxy_reset (context->engine);
     }
     g_dbus_method_invocation_return_value (invocation, NULL);
@@ -1354,6 +1367,13 @@ _ic_set_content_type (BusInputContext *context,
     }
 }
 
+static void
+_ic_set_client_commit_preedit (BusInputContext *context,
+                               GVariant        *value)
+{
+    g_variant_get (value, "(b)", &context->client_commit_preedit);
+}
+
 static gboolean
 bus_input_context_service_set_property (IBusService     *service,
                                         GDBusConnection *connection,
@@ -1379,9 +1399,14 @@ bus_input_context_service_set_property (IBusService     *service,
     if (!bus_input_context_service_authorized_method (service, connection))
         return FALSE;
 
+    g_return_val_if_fail (BUS_IS_INPUT_CONTEXT (service), FALSE);
+
     if (g_strcmp0 (property_name, "ContentType") == 0) {
-        BusInputContext *context = (BusInputContext *) service;
-        _ic_set_content_type (context, value);
+        _ic_set_content_type (BUS_INPUT_CONTEXT (service), value);
+        return TRUE;
+    }
+    if (g_strcmp0 (property_name, "ClientCommitPreedit") == 0) {
+        _ic_set_client_commit_preedit (BUS_INPUT_CONTEXT (service), value);
         return TRUE;
     }
 
@@ -1453,22 +1478,44 @@ bus_input_context_focus_in (BusInputContext *context)
 
 /**
  * bus_input_context_clear_preedit_text:
+ * @context: A #BusInputContext
+ * @with_signal: %FALSE if the preedit is already updated in ibus clients
+ *               likes ibus-im.so. Otherwise %TRUE.
  *
- * Clear context->preedit_text. If the preedit mode is IBUS_ENGINE_PREEDIT_COMMIT, commit it before clearing.
+ * Clear context->preedit_text. If the preedit mode is
+ * IBUS_ENGINE_PREEDIT_COMMIT, commit it before clearing.
  */
-static void
-bus_input_context_clear_preedit_text (BusInputContext *context)
+void
+bus_input_context_clear_preedit_text (BusInputContext *context,
+                                      gboolean         with_signal)
 {
+    IBusText *preedit_text;
+    guint     preedit_mode;
+    gboolean  preedit_visible;
+
     g_assert (BUS_IS_INPUT_CONTEXT (context));
 
-    if (context->preedit_visible &&
-        context->preedit_mode == IBUS_ENGINE_PREEDIT_COMMIT) {
-        bus_input_context_commit_text (context, context->preedit_text);
+    if (!with_signal) {
+        g_object_unref (context->preedit_text);
+        context->preedit_mode = IBUS_ENGINE_PREEDIT_CLEAR;
+        context->preedit_text = (IBusText *) g_object_ref_sink (text_empty);
+        context->preedit_cursor_pos = 0;
+        context->preedit_visible = FALSE;
+        return;
     }
 
-    /* always clear preedit text */
+    /* always clear preedit text to reset the cursor position in the
+     * client application before commit the preeit text. */
+    preedit_text = g_object_ref (context->preedit_text);
+    preedit_mode = context->preedit_mode;
+    preedit_visible = context->preedit_visible;
     bus_input_context_update_preedit_text (context,
         text_empty, 0, FALSE, IBUS_ENGINE_PREEDIT_CLEAR, TRUE);
+
+    if (preedit_visible && preedit_mode == IBUS_ENGINE_PREEDIT_COMMIT) {
+        bus_input_context_commit_text (context, preedit_text);
+    }
+    g_object_unref (preedit_text);
 }
 
 void
@@ -1479,7 +1526,10 @@ bus_input_context_focus_out (BusInputContext *context)
     if (!context->has_focus)
         return;
 
-    bus_input_context_clear_preedit_text (context);
+    if (context->client_commit_preedit)
+        bus_input_context_clear_preedit_text (context, FALSE);
+    else
+        bus_input_context_clear_preedit_text (context, TRUE);
     bus_input_context_update_auxiliary_text (context, text_empty, FALSE);
     bus_input_context_update_lookup_table (context,
                                            lookup_table_empty,
@@ -2338,7 +2388,7 @@ bus_input_context_disable (BusInputContext *context)
 {
     g_assert (BUS_IS_INPUT_CONTEXT (context));
 
-    bus_input_context_clear_preedit_text (context);
+    bus_input_context_clear_preedit_text (context, TRUE);
     bus_input_context_update_auxiliary_text (context, text_empty, FALSE);
     bus_input_context_update_lookup_table (context,
                                            lookup_table_empty,
@@ -2385,7 +2435,7 @@ bus_input_context_unset_engine (BusInputContext *context)
 {
     g_assert (BUS_IS_INPUT_CONTEXT (context));
 
-    bus_input_context_clear_preedit_text (context);
+    bus_input_context_clear_preedit_text (context, TRUE);
     bus_input_context_update_auxiliary_text (context, text_empty, FALSE);
     bus_input_context_update_lookup_table (context,
                                            lookup_table_empty,
@@ -2807,14 +2857,26 @@ bus_input_context_update_preedit_text (BusInputContext *context,
     } else if (PREEDIT_CONDITION) {
         GVariant *variant = ibus_serializable_serialize (
                 (IBusSerializable *)context->preedit_text);
-        bus_input_context_emit_signal (context,
-                                       "UpdatePreeditText",
-                                       g_variant_new (
-                                               "(vub)",
-                                               variant,
-                                               context->preedit_cursor_pos,
-                                               extension_visible),
-                                       NULL);
+        if (context->client_commit_preedit) {
+            bus_input_context_emit_signal (
+                    context,
+                    "UpdatePreeditTextWithMode",
+                    g_variant_new ("(vubu)",
+                                   variant,
+                                   context->preedit_cursor_pos,
+                                   extension_visible,
+                                   context->preedit_mode),
+                    NULL);
+        } else {
+            bus_input_context_emit_signal (
+                    context,
+                    "UpdatePreeditText",
+                    g_variant_new ("(vub)",
+                                   variant,
+                                   context->preedit_cursor_pos,
+                                   extension_visible),
+                    NULL);
+        }
     } else {
         g_signal_emit (context,
                        context_signals[UPDATE_PREEDIT_TEXT],
diff --git a/bus/inputcontext.h b/bus/inputcontext.h
index a46d5c06..7105fff8 100644
--- a/bus/inputcontext.h
+++ b/bus/inputcontext.h
@@ -2,8 +2,8 @@
 /* vim:set et sts=4: */
 /* ibus - The Input Bus
  * Copyright (C) 2008-2014 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
- * Copyright (C) 2008-2014 Red Hat, Inc.
+ * Copyright (C) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2018 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
@@ -377,5 +377,20 @@ void                 bus_input_context_update_lookup_table
 void                 bus_input_context_panel_extension_received
                                                 (BusInputContext    *context,
                                                  IBusExtensionEvent *event);
+
+/**
+ * bus_input_context_clear_preedit_text:
+ *
+ * Clear context->preedit_text. If the preedit mode is
+ * IBUS_ENGINE_PREEDIT_COMMIT and with_signal is %TRUE, commit it before
+ * clearing.
+ * If with_signal is %FALSE, this just clears the preedit coditions
+ * and the actual preedit is handled in ibus clients.
+ */
+void                 bus_input_context_clear_preedit_text
+                                                (BusInputContext    *context,
+                                                 gboolean
+                                                                   with_signal);
+
 G_END_DECLS
 #endif
diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
index e4de52d9..73a0eaec 100644
--- a/client/gtk2/ibusimcontext.c
+++ b/client/gtk2/ibusimcontext.c
@@ -2,8 +2,8 @@
 /* vim:set et sts=4: */
 /* ibus - The Input Bus
  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
- * Copyright (C) 2008-2017 Red Hat, Inc.
+ * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2018 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
@@ -61,6 +61,7 @@ struct _IBusIMContext {
     PangoAttrList   *preedit_attrs;
     gint             preedit_cursor_pos;
     gboolean         preedit_visible;
+    guint            preedit_mode;
 
     GdkRectangle     cursor_area;
     gboolean         has_focus;
@@ -132,8 +133,14 @@ static void     ibus_im_context_set_surrounding
                                              gint           len,
                                              gint           cursor_index);
 
-
 /* static methods*/
+static void     _ibus_context_update_preedit_text_cb
+                                           (IBusInputContext   *ibuscontext,
+                                            IBusText           *text,
+                                            gint                cursor_pos,
+                                            gboolean            visible,
+                                            guint               mode,
+                                            IBusIMContext      *ibusimcontext);
 static void     _create_input_context       (IBusIMContext      *context);
 static gboolean _set_cursor_location_internal
                                             (IBusIMContext      *context);
@@ -744,6 +751,7 @@ ibus_im_context_init (GObject *obj)
     ibusimcontext->preedit_attrs = NULL;
     ibusimcontext->preedit_cursor_pos = 0;
     ibusimcontext->preedit_visible = FALSE;
+    ibusimcontext->preedit_mode = IBUS_ENGINE_PREEDIT_CLEAR;
 
     // Init cursor area
     ibusimcontext->cursor_area.x = -1;
@@ -854,6 +862,24 @@ ibus_im_context_finalize (GObject *obj)
     G_OBJECT_CLASS(parent_class)->finalize (obj);
 }
 
+static void
+ibus_im_context_clear_preedit_text (IBusIMContext *ibusimcontext)
+{
+    g_assert (ibusimcontext->ibuscontext);
+    if (ibusimcontext->preedit_visible &&
+        ibusimcontext->preedit_mode == IBUS_ENGINE_PREEDIT_COMMIT) {
+        gchar *preedit_string = g_strdup (ibusimcontext->preedit_string);
+        _ibus_context_update_preedit_text_cb (ibusimcontext->ibuscontext,
+                                              ibus_text_new_from_string (""),
+                                              0,
+                                              FALSE,
+                                              IBUS_ENGINE_PREEDIT_CLEAR,
+                                              ibusimcontext);
+        g_signal_emit (ibusimcontext, _signal_commit_id, 0, preedit_string);
+        g_free (preedit_string);
+    }
+}
+
 static gboolean
 ibus_im_context_filter_keypress (GtkIMContext *context,
                                  GdkEventKey  *event)
@@ -1003,6 +1029,7 @@ ibus_im_context_focus_out (GtkIMContext *context)
 
     ibusimcontext->has_focus = FALSE;
     if (ibusimcontext->ibuscontext) {
+        ibus_im_context_clear_preedit_text (ibusimcontext);
         ibus_input_context_focus_out (ibusimcontext->ibuscontext);
     }
 
@@ -1022,6 +1049,12 @@ ibus_im_context_reset (GtkIMContext *context)
     IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
 
     if (ibusimcontext->ibuscontext) {
+        /* Commented out ibus_im_context_clear_preedit_text().
+         * Hangul needs to receive the reset callback with button press
+         * but other IMEs should avoid to receive the reset callback
+         * so the signal would need to be customized with GtkSetting.
+         * IBus uses button-press-event instead.
+         */
         ibus_input_context_reset (ibusimcontext->ibuscontext);
     }
     gtk_im_context_reset (ibusimcontext->slave);
@@ -1068,21 +1101,67 @@ ibus_im_context_get_preedit_string (GtkIMContext   *context,
 }
 
 
+static gboolean
+ibus_im_context_button_press_event_cb (GtkWidget      *widget,
+                                       GdkEventButton *event,
+                                       IBusIMContext  *ibusimcontext)
+{
+    if (event->button != 1)
+        return FALSE;
+
+    if (ibusimcontext->preedit_visible &&
+        ibusimcontext->preedit_mode == IBUS_ENGINE_PREEDIT_COMMIT) {
+        ibus_im_context_clear_preedit_text (ibusimcontext);
+        if (ibusimcontext->ibuscontext)
+            ibus_input_context_reset (ibusimcontext->ibuscontext);
+    }
+    return FALSE;
+}
+
 static void
 ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client)
 {
+    IBusIMContext *ibusimcontext;
+#if !GTK_CHECK_VERSION (3, 93, 0)
+    GtkWidget *widget;
+#endif
+
     IDEBUG ("%s", __FUNCTION__);
 
-    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
+    ibusimcontext = IBUS_IM_CONTEXT (context);
 
     if (ibusimcontext->client_window) {
+#if !GTK_CHECK_VERSION (3, 93, 0)
+        gdk_window_get_user_data (ibusimcontext->client_window,
+                                  (gpointer *)&widget);
+        /* firefox needs GtkWidget instead of GtkWindow */
+        if (GTK_IS_WIDGET (widget)) {
+            g_signal_handlers_disconnect_by_func (
+                    widget,
+                    (GCallback)ibus_im_context_button_press_event_cb,
+                    ibusimcontext);
+        }
+#endif
         g_object_unref (ibusimcontext->client_window);
         ibusimcontext->client_window = NULL;
     }
 
-    if (client != NULL)
+    if (client != NULL) {
         ibusimcontext->client_window = g_object_ref (client);
+#if !GTK_CHECK_VERSION (3, 93, 0)
+        gdk_window_get_user_data (ibusimcontext->client_window,
+                                  (gpointer *)&widget);
 
+        /* firefox needs GtkWidget instead of GtkWindow */
+        if (GTK_IS_WIDGET (widget)) {
+            g_signal_connect (
+                    widget,
+                    "button-press-event",
+                    G_CALLBACK (ibus_im_context_button_press_event_cb),
+                    ibusimcontext);
+        }
+#endif
+    }
     if (ibusimcontext->slave)
         gtk_im_context_set_client_window (ibusimcontext->slave, client);
 }
@@ -1530,6 +1609,7 @@ _ibus_context_update_preedit_text_cb (IBusInputContext  *ibuscontext,
                                       IBusText          *text,
                                       gint               cursor_pos,
                                       gboolean           visible,
+                                      guint              mode,
                                       IBusIMContext     *ibusimcontext)
 {
     IDEBUG ("%s", __FUNCTION__);
@@ -1586,6 +1666,7 @@ _ibus_context_update_preedit_text_cb (IBusInputContext  *ibuscontext,
 
     flag = ibusimcontext->preedit_visible != visible;
     ibusimcontext->preedit_visible = visible;
+    ibusimcontext->preedit_mode = mode;
 
     if (ibusimcontext->preedit_visible) {
         if (flag) {
@@ -1676,7 +1757,7 @@ _create_input_context_done (IBusBus       *bus,
         g_error_free (error);
     }
     else {
-
+        ibus_input_context_set_client_commit_preedit (context, TRUE);
         ibusimcontext->ibuscontext = context;
 
         g_signal_connect (ibusimcontext->ibuscontext,
@@ -1692,7 +1773,7 @@ _create_input_context_done (IBusBus       *bus,
                           G_CALLBACK (_ibus_context_delete_surrounding_text_cb),
                           ibusimcontext);
         g_signal_connect (ibusimcontext->ibuscontext,
-                          "update-preedit-text",
+                          "update-preedit-text-with-mode",
                           G_CALLBACK (_ibus_context_update_preedit_text_cb),
                           ibusimcontext);
         g_signal_connect (ibusimcontext->ibuscontext,
diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c
index ae7048ad..a809ef08 100644
--- a/src/ibusinputcontext.c
+++ b/src/ibusinputcontext.c
@@ -2,7 +2,8 @@
 /* vim:set et sts=4: */
 /* ibus - The Input Bus
  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2013 Red Hat, Inc.
+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2018 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
@@ -39,6 +40,7 @@ enum {
     FORWARD_KEY_EVENT,
     DELETE_SURROUNDING_TEXT,
     UPDATE_PREEDIT_TEXT,
+    UPDATE_PREEDIT_TEXT_WITH_MODE,
     SHOW_PREEDIT_TEXT,
     HIDE_PREEDIT_TEXT,
     UPDATE_AUXILIARY_TEXT,
@@ -217,6 +219,34 @@ ibus_input_context_class_init (IBusInputContextClass *class)
             G_TYPE_UINT,
             G_TYPE_BOOLEAN);
 
+    /**
+     * IBusInputContext::update-preedit-text-with-mode:
+     * @context: An IBusInputContext.
+     * @text: Text to be updated.
+     * @cursor_pos: Cursor position.
+     * @visible: Whether the update is visible.
+     * @mode: Preedit mode.
+     *
+     * Emitted to update preedit text with the mode.
+     *
+     * (Note: The text object is floating, and it will be released after the
+     *  signal. If signal handler wants to keep the object, the handler should
+     *  use g_object_ref_sink() to get the ownership of the object.)
+     */
+    context_signals[UPDATE_PREEDIT_TEXT_WITH_MODE] =
+        g_signal_new (I_("update-preedit-text-with-mode"),
+            G_TYPE_FROM_CLASS (class),
+            G_SIGNAL_RUN_LAST,
+            0,
+            NULL, NULL,
+            _ibus_marshal_VOID__OBJECT_UINT_BOOLEAN_UINT,
+            G_TYPE_NONE,
+            4,
+            IBUS_TYPE_TEXT,
+            G_TYPE_UINT,
+            G_TYPE_BOOLEAN,
+            G_TYPE_UINT);
+
     /**
      * IBusInputContext::show-preedit-text:
      * @context: An IBusInputContext.
@@ -542,6 +572,28 @@ ibus_input_context_g_signal (GDBusProxy  *proxy,
             g_object_unref (text);
         return;
     }
+    if (g_strcmp0 (signal_name, "UpdatePreeditTextWithMode") == 0) {
+        GVariant *variant = NULL;
+        gint32 cursor_pos;
+        gboolean visible;
+        guint mode = 0;
+        g_variant_get (parameters,
+                       "(vubu)", &variant, &cursor_pos, &visible, &mode);
+        IBusText *text = IBUS_TEXT (ibus_serializable_deserialize (variant));
+        g_variant_unref (variant);
+
+        g_signal_emit (context,
+                       context_signals[UPDATE_PREEDIT_TEXT_WITH_MODE],
+                       0,
+                       text,
+                       cursor_pos,
+                       visible,
+                       mode);
+
+        if (g_object_is_floating (text))
+            g_object_unref (text);
+        return;
+    }
 
     /* lookup signal in table */
     gint i;
@@ -1043,10 +1095,11 @@ ibus_input_context_set_surrounding_text (IBusInputContext   *context,
                                          guint32             cursor_pos,
                                          guint32             anchor_pos)
 {
+    IBusInputContextPrivate *priv;
+
     g_assert (IBUS_IS_INPUT_CONTEXT (context));
     g_assert (IBUS_IS_TEXT (text));
 
-    IBusInputContextPrivate *priv;
     priv = IBUS_INPUT_CONTEXT_GET_PRIVATE (context);
 
     if (cursor_pos != priv->surrounding_cursor_pos ||
@@ -1090,12 +1143,15 @@ ibus_input_context_set_content_type (IBusInputContext *context,
                                      guint             purpose,
                                      guint             hints)
 {
+    GVariant *cached_content_type;
+    GVariant *content_type;
+
     g_assert (IBUS_IS_INPUT_CONTEXT (context));
 
-    GVariant *cached_content_type =
+    cached_content_type =
         g_dbus_proxy_get_cached_property ((GDBusProxy *) context,
                                           "ContentType");
-    GVariant *content_type = g_variant_new ("(uu)", purpose, hints);
+    content_type = g_variant_new ("(uu)", purpose, hints);
 
     g_variant_ref_sink (content_type);
     if (cached_content_type == NULL ||
@@ -1142,18 +1198,22 @@ ibus_input_context_get_engine_async_finish (IBusInputContext   *context,
                                             GAsyncResult       *res,
                                             GError            **error)
 {
+    GVariant *variant;
+    GVariant *engine_desc_variant;
+    IBusEngineDesc *desc;
+
     g_assert (IBUS_IS_INPUT_CONTEXT (context));
     g_assert (G_IS_ASYNC_RESULT (res));
     g_assert (error == NULL || *error == NULL);
 
-    GVariant *variant = g_dbus_proxy_call_finish ((GDBusProxy *) context,
-                                                   res, error);
+    variant = g_dbus_proxy_call_finish ((GDBusProxy *) context, res, error);
     if (variant == NULL) {
         return NULL;
     }
 
-    GVariant *engine_desc_variant = g_variant_get_child_value (variant, 0);
-    IBusEngineDesc *desc = IBUS_ENGINE_DESC (ibus_serializable_deserialize (engine_desc_variant));
+    engine_desc_variant = g_variant_get_child_value (variant, 0);
+    desc = IBUS_ENGINE_DESC (
+            ibus_serializable_deserialize (engine_desc_variant));
     g_variant_unref (engine_desc_variant);
     g_variant_unref (variant);
 
@@ -1163,9 +1223,13 @@ ibus_input_context_get_engine_async_finish (IBusInputContext   *context,
 IBusEngineDesc *
 ibus_input_context_get_engine (IBusInputContext *context)
 {
-    g_assert (IBUS_IS_INPUT_CONTEXT (context));
     GVariant *result = NULL;
     GError *error = NULL;
+    GVariant *engine_desc_variant;
+    IBusEngineDesc *desc;
+
+    g_assert (IBUS_IS_INPUT_CONTEXT (context));
+
     result = g_dbus_proxy_call_sync ((GDBusProxy *) context,
                                      "GetEngine",               /* method_name */
                                      NULL,                      /* parameters */
@@ -1189,8 +1253,9 @@ ibus_input_context_get_engine (IBusInputContext *context)
         return NULL;
     }
 
-    GVariant *engine_desc_variant = g_variant_get_child_value (result, 0);
-    IBusEngineDesc *desc = IBUS_ENGINE_DESC (ibus_serializable_deserialize (engine_desc_variant));
+    engine_desc_variant = g_variant_get_child_value (result, 0);
+    desc = IBUS_ENGINE_DESC (
+            ibus_serializable_deserialize (engine_desc_variant));
     g_variant_unref (engine_desc_variant);
     g_variant_unref (result);
 
@@ -1214,6 +1279,41 @@ ibus_input_context_set_engine (IBusInputContext *context,
                        );
 }
 
+void
+ibus_input_context_set_client_commit_preedit (IBusInputContext *context,
+                                              gboolean          client_commit)
+{
+    GVariant *cached_content_type;
+    GVariant *var_client_commit;
+
+    g_assert (IBUS_IS_INPUT_CONTEXT (context));
+
+    cached_content_type =
+        g_dbus_proxy_get_cached_property ((GDBusProxy *) context,
+                                          "ClientCommitPreedit");
+    var_client_commit = g_variant_new ("(b)", client_commit);
+
+    g_variant_ref_sink (var_client_commit);
+    if (cached_content_type == NULL) {
+        g_dbus_proxy_call ((GDBusProxy *) context,
+                           "org.freedesktop.DBus.Properties.Set",
+                           g_variant_new ("(ssv)",
+                                          IBUS_INTERFACE_INPUT_CONTEXT,
+                                          "ClientCommitPreedit",
+                                          var_client_commit),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           -1,
+                           NULL, /* cancellable */
+                           NULL, /* callback */
+                           NULL  /* user_data */
+                           );
+    }
+
+    if (cached_content_type != NULL)
+        g_variant_unref (cached_content_type);
+    g_variant_unref (var_client_commit);
+}
+
 #define DEFINE_FUNC(name, Name)                                         \
     void                                                                \
     ibus_input_context_##name (IBusInputContext *context)               \
diff --git a/src/ibusinputcontext.h b/src/ibusinputcontext.h
index a77cf92f..09992148 100644
--- a/src/ibusinputcontext.h
+++ b/src/ibusinputcontext.h
@@ -2,7 +2,8 @@
 /* vim:set et sts=4: */
 /* ibus - The Input Bus
  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2013 Red Hat, Inc.
+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2018 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
@@ -498,5 +499,29 @@ void         ibus_input_context_set_content_type
                                              guint               purpose,
                                              guint               hints);
 
+/**
+ * ibus_input_context_set_client_commit_preedit:
+ * @context: An #IBusInputContext.
+ * @client_commit: %TRUE if your input context commits pre-edit texts
+ *     with Space or Enter key events or mouse click events. %FALSE if
+ *     ibus-daemon commits pre-edit texts with those events.
+ *     The default is %FALSE. The behavior is decided with
+ *     ibus_engine_update_preedit_text_with_mode() to commit, clear or
+ *     keep the pre-edit text and this API is important in ibus-hangul.
+ *
+ * Set whether #IBusInputContext commits pre-edit texts or not.
+ * If %TRUE, 'update-preedit-text-with-mode' signal is emitted
+ * instead of 'update-preedit-text' signal.
+ * If your client receives the 'update-preedit-text-with-mode' signal,
+ * the client needs to implement commit_text() of pre-edit text when
+ * GtkIMContextClass.focus_out() is called in case an IME desires that
+ * behavior but it depends on each IME.
+ *
+ * See also ibus_engine_update_preedit_text_with_mode().
+ */
+void         ibus_input_context_set_client_commit_preedit (
+                                             IBusInputContext   *context,
+                                             gboolean            client_commit);
+
 G_END_DECLS
 #endif
-- 
2.17.1

From 7b3b8c8b0c6a41ab524e0be9474825da9cba96ac Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Tue, 13 Nov 2018 14:27:52 +0900
Subject: [PATCH] client/gtk2: Do not delete IBUS_CAP_SURROUNDING_TEXT

retrieve-surrounding signal could be failed with the first typing
on firefox. It could be a bug in firefox but now IBusIMContext does not
delete IBUS_CAP_SURROUNDING_TEXT in the capabilities as a workaround
when retrieve-surrounding signal is failed.
Also added retrieve-surrounding signal after some committing text.

BUG=https://github.com/ibus/ibus/issues/2054
---
 client/gtk2/ibusimcontext.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
index 73a0eaec..82af51a1 100644
--- a/client/gtk2/ibusimcontext.c
+++ b/client/gtk2/ibusimcontext.c
@@ -298,6 +298,7 @@ ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
         IBusText *text = ibus_text_new_from_unichar (ch);
         g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
         g_object_unref (text);
+        _request_surrounding_text (ibusimcontext);
         return TRUE;
     }
    return FALSE;
@@ -386,9 +387,12 @@ _request_surrounding_text (IBusIMContext *context)
         g_signal_emit (context, _signal_retrieve_surrounding_id, 0,
                        &return_value);
         if (!return_value) {
-            context->caps &= ~IBUS_CAP_SURROUNDING_TEXT;
-            ibus_input_context_set_capabilities (context->ibuscontext,
-                                                 context->caps);
+            /* #2054 firefox::IMContextWrapper::GetCurrentParagraph() could
+             * fail with the first typing on firefox but it succeeds with
+             * the second typing.
+             */
+            g_warning ("%s has no capability of surrounding-text feature",
+                       g_get_prgname ());
         }
     }
 }
@@ -877,6 +881,7 @@ ibus_im_context_clear_preedit_text (IBusIMContext *ibusimcontext)
                                               ibusimcontext);
         g_signal_emit (ibusimcontext, _signal_commit_id, 0, preedit_string);
         g_free (preedit_string);
+        _request_surrounding_text (ibusimcontext);
     }
 }
 
-- 
2.17.1