public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Fixed several bugs
@ 2026-05-31  2:06 Takao Fujiwara
  0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31  2:06 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/ibus
            Branch : autotool
            Commit : 3818b86c29bd09b4beecebbaf0e26ef8ae9ff36b
            Author : Takao Fujiwara <tfujiwar@redhat.com>
            Date   : 2017-09-14T19:10:26+09:00
            Stats  : +5074/-1887 in 4 file(s)
            URL    : https://src.fedoraproject.org/rpms/ibus/c/3818b86c29bd09b4beecebbaf0e26ef8ae9ff36b?branch=autotool

            Log:
            Fixed several bugs

- Fix scaling factor, mouse events on switcher, c-s-u on im-ibus,
  propertypanel position and menu
- Add ibus-portal
- Move ibus-emoji-dialog.vapi in the build
- Fixed some SEGVs #1406699 #1432252

---
diff --git a/ibus-1385349-segv-bus-proxy.patch b/ibus-1385349-segv-bus-proxy.patch
index ec9390d..a9c1a73 100644
--- a/ibus-1385349-segv-bus-proxy.patch
+++ b/ibus-1385349-segv-bus-proxy.patch
@@ -1,18 +1,20 @@
-From ddb7cb30f10b1d1e40ae4b6c46583941545412d8 Mon Sep 17 00:00:00 2001
+From 8ea0d3f25078c612b4b16c955c1c0c17e764d8c5 Mon Sep 17 00:00:00 2001
 From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 26 Jul 2017 21:41:13 +0900
+Date: Thu, 27 Jul 2017 18:56:01 +0900
 Subject: [PATCH] bus: Fix SEGV in bus_panel_proxy_focus_in()
 
 BUG=rhbz#1349148
 BUG=rhbz#1385349
 BUG=rhbz#1350291
+BUG=rhbz#1406699
+BUG=rhbz#1432252
 ---
- bus/dbusimpl.c | 14 +++++++++++---
+ bus/dbusimpl.c | 38 ++++++++++++++++++++++++++++++++------
  bus/ibusimpl.c | 22 +++++++++++++++++++---
- 2 files changed, 30 insertions(+), 6 deletions(-)
+ 2 files changed, 51 insertions(+), 9 deletions(-)
 
 diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c
-index b54ef81..4a60391 100644
+index b54ef81..e4dd868 100644
 --- a/bus/dbusimpl.c
 +++ b/bus/dbusimpl.c
 @@ -2,7 +2,8 @@
@@ -25,7 +27,80 @@ index b54ef81..4a60391 100644
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
-@@ -1464,13 +1465,20 @@ bus_dbus_impl_connection_filter_cb (GDBusConnection *dbus_connection,
+@@ -344,6 +345,8 @@ bus_name_service_set_primary_owner (BusNameService     *service,
+                                     BusConnectionOwner *owner,
+                                     BusDBusImpl        *dbus)
+ {
++    gboolean has_old_owner = FALSE;
++
+     g_assert (service != NULL);
+     g_assert (owner != NULL);
+     g_assert (dbus != NULL);
+@@ -351,6 +354,13 @@ bus_name_service_set_primary_owner (BusNameService     *service,
+     BusConnectionOwner *old = service->owners != NULL ?
+             (BusConnectionOwner *)service->owners->data : NULL;
+ 
++    /* rhbz#1432252 If bus_connection_get_unique_name() == NULL,
++     * "Hello" method is not received yet.
++     */
++    if (old != NULL && bus_connection_get_unique_name (old->conn) != NULL) {
++        has_old_owner = TRUE;
++    }
++
+     if (old != NULL) {
+         g_signal_emit (dbus,
+                        dbus_signals[NAME_LOST],
+@@ -370,7 +380,8 @@ bus_name_service_set_primary_owner (BusNameService     *service,
+                    0,
+                    owner->conn,
+                    service->name,
+-                   old != NULL ? bus_connection_get_unique_name (old->conn) : "",
++                   has_old_owner ? bus_connection_get_unique_name (old->conn) :
++                           "",
+                    bus_connection_get_unique_name (owner->conn));
+ 
+     if (old != NULL && old->do_not_queue != 0) {
+@@ -427,6 +438,7 @@ bus_name_service_remove_owner (BusNameService     *service,
+                                BusDBusImpl        *dbus)
+ {
+     GSList *owners;
++    gboolean has_new_owner = FALSE;
+ 
+     g_assert (service != NULL);
+     g_assert (owner != NULL);
+@@ -439,6 +451,13 @@ bus_name_service_remove_owner (BusNameService     *service,
+         BusConnectionOwner *_new = NULL;
+         if (owners->next != NULL) {
+             _new = (BusConnectionOwner *)owners->next->data;
++            /* rhbz#1406699 If bus_connection_get_unique_name() == NULL,
++             * "Hello" method is not received yet.
++             */
++            if (_new != NULL &&
++                bus_connection_get_unique_name (_new->conn) != NULL) {
++                has_new_owner = TRUE;
++            }
+         }
+ 
+         if (dbus != NULL) {
+@@ -447,7 +466,7 @@ bus_name_service_remove_owner (BusNameService     *service,
+                            0,
+                            owner->conn,
+                            service->name);
+-            if (_new != NULL) {
++            if (has_new_owner) {
+                 g_signal_emit (dbus,
+                                dbus_signals[NAME_ACQUIRED],
+                                0,
+@@ -460,7 +479,7 @@ bus_name_service_remove_owner (BusNameService     *service,
+                     _new != NULL ? _new->conn : NULL,
+                     service->name,
+                     bus_connection_get_unique_name (owner->conn),
+-                    _new != NULL ? bus_connection_get_unique_name (_new->conn) : "");
++                    has_new_owner ? bus_connection_get_unique_name (_new->conn) : "");
+ 
+         }
+     }
+@@ -1464,13 +1483,20 @@ bus_dbus_impl_connection_filter_cb (GDBusConnection *dbus_connection,
                                      gboolean         incoming,
                                      gpointer         user_data)
  {

diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index a05b0a4..41d4ad6 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -766,866 +766,441 @@ index 5ae6e83..fa80272 100644
 -- 
 2.9.3
 
-From 2686b46b29e12b4408033568a898949a731b7938 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 19 Jul 2017 21:02:20 +0900
-Subject: [PATCH] Integrate custom rendering to use HarfBuzz glyph info
-
-IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering
-on Cairo context.
-Current Pango changes fonts by emoji variants and draws the separated
-glyphs [1] but actually the emoji characters with variants can be drawn
-as one glyph so this class  manages Fontconfig fontsets to select a font,
-HarfBuzz to get glyphs for emoji variants, Cairo to draw glyphs.
-Need configure --enable-harfbuzz-for-emoji option to enable this feature.
-
-[1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669
-     https://bugzilla.gnome.org/show_bug.cgi?id=781123
+From 6a3301db85e77e0652f7e00894cce493b6a942f6 Mon Sep 17 00:00:00 2001
+From: Xiang Fan <sfanxiang@gmail.com>
+Date: Thu, 10 Aug 2017 11:24:39 +0900
+Subject: [PATCH 01/14] client/gtk2: include the scaling factor
+
+Scaling factor, which exists for HiDPI displays, needs to be included in
+the calculation of cursor location. This does not affect devices without
+a HiDPI display.
+
+Candidate windows would be misplaced to smaller coordinates without this
+patch.
+
+BUG=https://github.com/ibus/ibus/issues/1806
+
+Review URL: https://codereview.appspot.com/328250043
+
+Patch from Xiang Fan <sfanxiang@gmail.com>.
 ---
- .../vala}/IBusEmojiDialog-1.0.metadata             |   0
- bindings/vala/IBusFontSet-1.0.metadata             |   1 +
- bindings/vala/Makefile.am                          | 244 +++++-
- .../vala}/ibus-emoji-dialog-1.0.deps               |   0
- bindings/vala/ibus-fontset-1.0.deps                |   1 +
- configure.ac                                       |  29 +
- po/POTFILES.skip                                   |   5 +
- ui/gtk3/Makefile.am                                | 131 ++-
- ui/gtk3/emojier.vala                               | 119 ++-
- ui/gtk3/ibusemojidialog.h                          |  26 +
- ui/gtk3/ibusfontset.c                              | 923 +++++++++++++++++++++
- ui/gtk3/ibusfontset.h                              | 302 +++++++
- 12 files changed, 1675 insertions(+), 106 deletions(-)
- rename {ui/gtk3 => bindings/vala}/IBusEmojiDialog-1.0.metadata (100%)
- create mode 100644 bindings/vala/IBusFontSet-1.0.metadata
- rename {ui/gtk3 => bindings/vala}/ibus-emoji-dialog-1.0.deps (100%)
- create mode 100644 bindings/vala/ibus-fontset-1.0.deps
- create mode 100644 ui/gtk3/ibusfontset.c
- create mode 100644 ui/gtk3/ibusfontset.h
+ client/gtk2/ibusimcontext.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
 
-diff --git a/ui/gtk3/IBusEmojiDialog-1.0.metadata b/bindings/vala/IBusEmojiDialog-1.0.metadata
-similarity index 100%
-rename from ui/gtk3/IBusEmojiDialog-1.0.metadata
-rename to bindings/vala/IBusEmojiDialog-1.0.metadata
-diff --git a/bindings/vala/IBusFontSet-1.0.metadata b/bindings/vala/IBusFontSet-1.0.metadata
-new file mode 100644
-index 0000000..73037d7
---- /dev/null
-+++ b/bindings/vala/IBusFontSet-1.0.metadata
-@@ -0,0 +1 @@
-+IBusFontSet cheader_filename="ibusfontset.h"
-diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
-index 4e34afc..261e1f3 100644
---- a/bindings/vala/Makefile.am
-+++ b/bindings/vala/Makefile.am
-@@ -3,7 +3,8 @@
- # ibus - The Input Bus
- #
- # Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
--# Copyright (c) 2007-2016 Red Hat, Inc.
-+# Copyright (c) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2007-2017 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
-@@ -22,15 +23,47 @@
- 
- -include $(VAPIGEN_MAKEFILE)
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index 0df00620..41c7a3af 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -999,6 +999,24 @@ ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client)
+         gtk_im_context_set_client_window (ibusimcontext->slave, client);
+ }
  
-+libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
++static void
++_set_rect_scale_factor_with_window (GdkRectangle *area,
++                                    GdkWindow    *window)
++{
++#if GTK_CHECK_VERSION (3, 10, 0)
++    int scale_factor;
 +
-+noinst_LTLIBRARIES =
-+noinst_DATA =
-+INTROSPECTION_GIRS =
-+girdir = $(datadir)/gir-1.0
++    g_assert (area);
++    g_assert (GDK_IS_WINDOW (window));
 +
-+AM_CPPFLAGS = \
-+    -I$(top_srcdir)/src \
-+    -I$(top_builddir)/src \
-+    -include $(CONFIG_HEADER) \
-+    $(NULL)
-+AM_CFLAGS = \
-+    -DG_LOG_DOMAIN=\"IBUS\" \
-+    -DPKGDATADIR=\"$(pkgdatadir)\" \
-+    -DIBUS_DISABLE_DEPRECATED \
-+    -Wno-unused-variable \
-+    -Wno-unused-but-set-variable \
-+    -Wno-unused-function \
-+    $(NULL)
-+AM_VALAFLAGS = \
-+    --vapidir=$(builddir) \
-+    --vapidir=$(srcdir) \
-+    --pkg=posix \
-+    --pkg=gtk+-3.0 \
-+    --pkg=gdk-x11-3.0 \
-+    --pkg=ibus-1.0 \
-+    --pkg=config \
-+    --pkg=xi \
-+    --target-glib="$(VALA_TARGET_GLIB_VERSION)" \
-+    $(NULL)
++    scale_factor = gdk_window_get_scale_factor (window);
++    area->x *= scale_factor;
++    area->y *= scale_factor;
++    area->width *= scale_factor;
++    area->height *= scale_factor;
++#endif
++}
 +
- vapi_deps = \
- 	IBus-1.0.metadata \
--	IBus-1.0-custom.vala \
- 	$(top_builddir)/src/IBus-1.0.gir \
- 	$(NULL)
- 
- ibus-1.0.vapi: $(vapi_deps)
- 
--VAPIGEN_VAPIS = ibus-1.0.vapi
-+ibus_vapi = ibus-1.0.vapi
-+VAPIGEN_VAPIS = $(ibus_vapi)
- 
- ibus_1_0_vapi_DEPS = gio-2.0
- ibus_1_0_vapi_METADATADIRS = $(srcdir)
-@@ -40,18 +73,201 @@ ibus_1_0_vapi_FILES = \
- 	$(NULL)
+ static gboolean
+ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+ {
+@@ -1024,6 +1042,8 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+             window = parent;
+         }
  
- vapidir = $(datadir)/vala/vapi
--vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
-+vapi_DATA = $(ibus_vapi) $(ibus_vapi:.vapi=.deps)
++        _set_rect_scale_factor_with_window (&area,
++                                            ibusimcontext->client_window);
+         ibus_input_context_set_cursor_location_relative (
+             ibusimcontext->ibuscontext,
+             area.x,
+@@ -1049,6 +1069,7 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext)
+     gdk_window_get_root_coords (ibusimcontext->client_window,
+                                 area.x, area.y,
+                                 &area.x, &area.y);
++    _set_rect_scale_factor_with_window (&area, ibusimcontext->client_window);
+     ibus_input_context_set_cursor_location (ibusimcontext->ibuscontext,
+                                             area.x,
+                                             area.y,
+-- 
+2.13.4
+
+From c1b93f933f5cbd74f3e06575d26ed7432a5420fd Mon Sep 17 00:00:00 2001
+From: Mario Bodemann <mario.bodemann@gmail.com>
+Date: Tue, 15 Aug 2017 12:10:56 +0900
+Subject: [PATCH 02/14] Typo fix
+
+BUG=https://github.com/ibus/ibus/pull/1939
+
+Review URL: https://codereview.appspot.com/327080043
+
+Patch from Mario Bodemann <mario.bodemann@gmail.com>.
+---
+ ui/gtk3/ibus-emoji.7.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ui/gtk3/ibus-emoji.7.in b/ui/gtk3/ibus-emoji.7.in
+index 4ee86364..d5eae310 100644
+--- a/ui/gtk3/ibus-emoji.7.in
++++ b/ui/gtk3/ibus-emoji.7.in
+@@ -15,7 +15,7 @@
+ .SH "DESCRIPTION"
  
--MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
--DISTCLEANFILES = $(VAPIGEN_VAPIS)
-+MAINTAINERCLEANFILES = $(ibus_vapi)
-+DISTCLEANFILES = $(ibus_vapi)
+ .PP
+-IBus Emojier provides a GUI to select an emoji by typing an emoji annotaion
++IBus Emojier provides a GUI to select an emoji by typing an emoji annotation
+ or choosing a character with mouse click and it's designed to work as
+ an extended IBus lookup window using Space, Enter, and Arrow keys.
+ The text entry accepts an emoji annotation or Unicode points.
+-- 
+2.13.4
+
+From 203a3df5a239d644cf42b7bac03a268eb5babfc7 Mon Sep 17 00:00:00 2001
+From: Alexander Larsson <alexl@redhat.com>
+Date: Wed, 30 Aug 2017 11:38:09 +0900
+Subject: [PATCH 03/14] Initial version of ibus portal
+
+This adds a dbus service called org.freedesktop.portal.IBus on the
+session bus. It is a very limited service that only implements
+CreateInputContext and the InputContext interface (and Service.Destroy
+for lifetime access).
+
+It uses gdbus code generation for demarshalling the method calls which
+means it will verify that all arguments have the right type.
+
+Additionally all method calls to the input context object have to be
+from the client that created it, so each client is isolated.
+
+BUG=https://github.com/flatpak/flatpak/issues/675
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/326350043
+
+Patch from Alexander Larsson <alexl@redhat.com>.
+---
+ Makefile.am                                   |   1 +
+ configure.ac                                  |   5 +-
+ portal/Makefile.am                            |  95 ++++
+ portal/org.freedesktop.IBus.Portal.xml        | 132 +++++
+ portal/org.freedesktop.portal.IBus.service.in |   3 +
+ portal/portal.c                               | 698 ++++++++++++++++++++++++++
+ src/ibusshare.h                               |  14 +
+ 7 files changed, 947 insertions(+), 1 deletion(-)
+ create mode 100644 portal/Makefile.am
+ create mode 100644 portal/org.freedesktop.IBus.Portal.xml
+ create mode 100644 portal/org.freedesktop.portal.IBus.service.in
+ create mode 100644 portal/portal.c
+
+diff --git a/Makefile.am b/Makefile.am
+index f703d4c6..c8e802da 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -51,6 +51,7 @@ SUBDIRS = \
+ 	util \
+ 	conf \
+ 	client \
++	portal \
+ 	data \
+ 	m4 \
+ 	po \
+diff --git a/configure.ac b/configure.ac
+index cb48ad4c..14556a3a 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -153,7 +153,7 @@ PKG_CHECK_MODULES(GOBJECT2, [
+     gobject-2.0 >= glib_required_version
+ ])
+ PKG_CHECK_MODULES(GIO2, [
+-    gio-2.0 >= glib_required_version
++    gio-2.0 gio-unix-2.0 >= glib_required_version
+ ])
+ PKG_CHECK_MODULES(GTHREAD2, [
+     gthread-2.0 >= glib_required_version
+@@ -660,6 +660,8 @@ PKG_CHECK_MODULES(ISOCODES, [
+ ISOCODES_PREFIX=`$PKG_CONFIG iso-codes --variable=prefix`
+ AC_SUBST(ISOCODES_PREFIX)
  
--EXTRA_DIST = \
--	$(VAPIGEN_VAPIS) \
--	IBus-1.0.metadata \
--	IBus-1.0-custom.vala \
--	ibus-1.0.deps \
--	config.vapi \
--	xi.vapi \
--	$(NULL)
-+EXTRA_DIST =                                    \
-+    $(ibus_vapi)                                \
-+    IBus-1.0.metadata                           \
-+    IBus-1.0-custom.vala                        \
-+    IBusEmojiDialog-1.0.metadata                \
-+    IBusFontSet-1.0.metadata                    \
-+    ibus-1.0.deps                               \
-+    ibus-emoji-dialog-1.0.deps                  \
-+    ibus-fontset-1.0.deps                       \
-+    config.vapi                                 \
-+    xi.vapi                                     \
-+    $(NULL)
-+
-+if ENABLE_EMOJI_DICT
-+AM_VALAFLAGS += --define=EMOJI_DICT
-+
-+libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
-+noinst_LTLIBRARIES += $(libibus_emoji_dialog)
-+
-+libibus_emoji_dialog_1_0_la_SOURCES =           \
-+    candidatearea.vala                          \
-+    emojier.vala                                \
-+    iconwidget.vala                             \
-+    pango.vala                                  \
-+    separator.vala                              \
-+    $(NULL)
-+libibus_emoji_dialog_1_0_la_CFLAGS =            \
-+    $(AM_CFLAGS)                                \
-+    @GLIB2_CFLAGS@                              \
-+    @GIO2_CFLAGS@                               \
-+    @GTHREAD2_CFLAGS@                           \
-+    @GTK3_CFLAGS@                               \
-+    @X11_CFLAGS@                                \
-+    -DBINDIR=\"$(bindir)\"                      \
-+    $(NULL)
-+libibus_emoji_dialog_1_0_la_LIBADD =            \
-+    @GLIB2_LIBS@                                \
-+    @GIO2_LIBS@                                 \
-+    @GTHREAD2_LIBS@                             \
-+    @GTK3_LIBS@                                 \
-+    @X11_LIBS@                                  \
-+    -lXi                                        \
-+    $(libibus)                                  \
-+    $(NULL)
-+libibus_emoji_dialog_1_0_la_LDFLAGS =           \
-+    -no-undefined                               \
-+    -export-symbols-regex "ibus_.*"             \
-+    $(NULL)
-+
-+# per file setting is needed to avoid conflicting LN_S by calling
-+# duplicated times in parallel make
-+%.vala: $(ibus_vapi)
-+	if test ! -f $@ ; then                                              \
-+	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
-+	fi;
-+ibusfontset.c: $(ibus_vapi)
-+	if test ! -f $@ ; then                                              \
-+	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
-+	fi;
-+ibusfontset.h: $(ibus_vapi)
-+	if test ! -f $@ ; then                                              \
-+	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
-+	fi;
-+
++AC_SUBST([GDBUS_CODEGEN], [`$PKG_CONFIG --variable gdbus_codegen gio-2.0`])
++
+ # OUTPUT files
+ AC_CONFIG_FILES([ po/Makefile.in
+ Makefile
+@@ -674,6 +676,7 @@ src/Makefile
+ src/ibusversion.h
+ src/tests/Makefile
+ bus/Makefile
++portal/Makefile
+ engine/Makefile
+ util/Makefile
+ util/IMdkit/Makefile
+diff --git a/portal/Makefile.am b/portal/Makefile.am
+new file mode 100644
+index 00000000..954fc591
+--- /dev/null
++++ b/portal/Makefile.am
+@@ -0,0 +1,95 @@
++# vim:set noet ts=4:
++#
++# ibus - The Input Bus
++#
++# Copyright (c) 2007-2013 Peng Huang <shawn.p.huang@gmail.com>
++# Copyright (c) 2007-2013 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
++# 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, write to the Free Software
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
++# USA
++
++NULL =
 +
-+MAINTAINERCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES)
-+DISTCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES)
++libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
 +
-+if HAVE_INTROSPECTION
-+-include $(INTROSPECTION_MAKEFILE)
-+INTROSPECTION_SCANNER_ARGS =
-+INTROSPECTION_COMPILER_ARGS =                   \
-+    --includedir=$(srcdir)                      \
-+    --includedir=.                              \
-+    --includedir=$(top_srcdir)/src              \
-+    $(NULL)
++AM_CPPFLAGS = \
++	-I$(top_srcdir)/src   \
++	-I$(top_builddir)/src \
++	$(NULL)
 +
++AM_CFLAGS = \
++	@GLIB2_CFLAGS@ \
++	@GIO2_CFLAGS@ \
++	@GTHREAD2_CFLAGS@ \
++	-DG_LOG_DOMAIN=\"IBUS\" \
++	-DPKGDATADIR=\"$(pkgdatadir)\" \
++	-DLIBEXECDIR=\"$(libexecdir)\" \
++	-DBINDIR=\"@bindir@\" \
++	-DIBUS_DISABLE_DEPRECATED \
++	$(NULL)
++AM_LDADD = \
++	@GOBJECT2_LIBS@ \
++	@GLIB2_LIBS@ \
++	@GIO2_LIBS@ \
++	@GTHREAD2_LIBS@ \
++	$(libibus) \
++	$(NULL)
 +
-+emoji_headers =                                 \
-+    $(top_srcdir)/ui/gtk3/ibusemojidialog.h     \
-+    $(NULL)
++ibus_dbus_built_sources = ibus-portal-dbus.c ibus-portal-dbus.h
++BUILT_SOURCES = $(ibus_dbus_built_sources)
 +
-+IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
-+IBusEmojiDialog_1_0_gir_SCANNERFLAGS =          \
-+    --pkg-export=ibus-1.0                       \
-+    --pkg=gtk+-3.0                              \
-+    $(IBUS_GIR_SCANNERFLAGS)                    \
-+    $(NULL)
-+IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0
-+IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus)
-+IBusEmojiDialog_1_0_gir_FILES = $(emoji_headers)
-+IBusEmojiDialog_1_0_gir_CFLAGS =                \
-+    -I$(srcdir)                                 \
-+    -I$(builddir)                               \
-+    -I$(top_srcdir)/src                         \
-+    $(NULL)
++libexec_PROGRAMS = ibus-portal
++ibus_portal_DEPENDENCIES = \
++	$(libibus) \
++	$(NULL)
++ibus_portal_SOURCES = \
++	portal.c \
++	$(ibus_dbus_built_sources) \
++	$(NULL)
++ibus_portal_CFLAGS = \
++	$(AM_CFLAGS) \
++	$(NULL)
++ibus_portal_LDADD = \
++	$(AM_LDADD) \
++	$(NULL)
 +
-+ibus_emoji_dialog_gir = IBusEmojiDialog-1.0.gir
-+INTROSPECTION_GIRS += $(ibus_emoji_dialog_gir)
-+noinst_DATA += $(ibus_emoji_dialog_gir)
-+EXTRA_DIST += $(ibus_emoji_dialog_gir)
-+MAINTAINERCLEANFILES += $(ibus_emoji_dialog_gir)
-+DISTCLEANFILES += $(ibus_emoji_dialog_gir)
++EXTRA_DIST = \
++	$(NULL)
 +
-+ibus-emoji-dialog-1.0.vapi: $(ibus_emoji_dialog_gir) IBusEmojiDialog-1.0.metadata
-+ibus_emoji_dialog_vapi = ibus-emoji-dialog-1.0.vapi
-+ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0
-+ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir)
-+ibus_emoji_dialog_1_0_vapi_FILES = IBusEmojiDialog-1.0.gir
-+VAPIGEN_VAPIS += $(ibus_emoji_dialog_vapi)
-+noinst_DATA += $(ibus_emoji_dialog_vapi)
-+EXTRA_DIST += $(ibus_emoji_dialog_vapi)
-+MAINTAINERCLEANFILES += $(ibus_emoji_dialog_vapi)
-+DISTCLEANFILES += $(ibus_emoji_dialog_vapi)
++CLEANFILES = \
++	$(NULL)
 +
-+endif
-+#end of HAVE_INTROSPECTION
++$(libibus):
++	$(MAKE) -C $(top_builddir)/src
 +
++dbusservice_in_files = org.freedesktop.portal.IBus.service.in
++dbusservice_DATA = $(dbusservice_in_files:.service.in=.service)
++dbusservicedir=${datadir}/dbus-1/services
 +
-+if ENABLE_HARFBUZZ_FOR_EMOJI
-+libibus_fontset = libibus-fontset-1.0.la
-+noinst_LTLIBRARIES += $(libibus_fontset)
++org.freedesktop.portal.IBus.service: org.freedesktop.portal.IBus.service.in
++	$(AM_V_GEN) sed -e "s|\@libexecdir\@|$(libexecdir)|"  $< > $@.tmp && mv $@.tmp $@
 +
-+libibus_fontset_1_0_la_SOURCES =                \
-+    ibusfontset.c                               \
-+    $(NULL)
-+libibus_fontset_1_0_la_CFLAGS =                 \
-+    $(AM_CFLAGS)                                \
-+    @CAIRO_CFLAGS@                              \
-+    @FONTCONFIG_CFLAGS@                         \
-+    @GLIB2_CFLAGS@                              \
-+    @HARFBUZZ_CFLAGS@                           \
-+    @PANGO_CFLAGS@                              \
-+    $(NULL)
-+libibus_fontset_1_0_la_LIBADD =                 \
-+    @CAIRO_LIBS@                                \
-+    @FONTCONFIG_LIBS@                           \
-+    @GLIB2_LIBS@                                \
-+    @HARFBUZZ_LIBS@                             \
-+    @PANGO_LIBS@                                \
-+    $(NULL)
-+libibus_fontset_1_0_la_LDFLAGS =                \
-+    -no-undefined                               \
-+    -export-symbols-regex "ibus_.*"             \
-+    $(NULL)
++$(ibus_dbus_built_sources) : org.freedesktop.IBus.Portal.xml
++	$(AM_V_GEN) $(GDBUS_CODEGEN)                            \
++		--interface-prefix org.freedesktop.IBus.        \
++		--c-namespace IBusDbus                          \
++		--generate-c-code $(builddir)/ibus-portal-dbus  \
++		$^ \
++		$(NULL)
 +
-+MAINTAINERCLEANFILES += ibusfontset.c ibusfontset.h
-+DISTCLEANFILES += ibusfontset.c ibusfontset.h
++EXTRA_DIST += $(dbusservice_in_files)
++CLEANFILES += $(dbusservice_DATA)
 +
-+if HAVE_INTROSPECTION
-+IBusFontSet-1.0.gir: $(libibus_fontset) Makefile
-+IBusFontSet_1_0_gir_SCANNERFLAGS =              \
-+    --pkg-export=ibus-1.0                       \
-+    --pkg=cairo                                 \
-+    --pkg=fontconfig                            \
-+    --pkg=harfbuzz                              \
-+    $(IBUS_GIR_SCANNERFLAGS)                    \
-+    $(NULL)
-+IBusFontSet_1_0_gir_LIBS = $(libibus_fontset) $(libibus)
-+IBusFontSet_1_0_gir_INCLUDES = cairo-1.0 GLib-2.0 GObject-2.0
-+IBusFontSet_1_0_gir_FILES =                     \
-+    ibusfontset.h                               \
-+    $(NULL)
-+IBusFontSet_1_0_gir_CFLAGS =                    \
-+    -I$(srcdir)                                 \
-+    -I$(builddir)                               \
-+    -I$(top_srcdir)/src                         \
-+    $(NULL)
-+ibus_fontset_gir = IBusFontSet-1.0.gir
-+INTROSPECTION_GIRS += $(ibus_fontset_gir)
-+noinst_DATA += $(ibus_fontset_gir)
-+EXTRA_DIST += $(ibus_fontset_gir)
-+MAINTAINERCLEANFILES += $(ibus_fontset_gir)
-+DISTCLEANFILES += $(ibus_fontset_gir)
-+
-+ibus-fontset-1.0.vapi: $(ibus_fontset_gir) IBusFontSet-1.0.metadata
-+ibus_fontset_vapi = ibus-fontset-1.0.vapi
-+ibus_fontset_1_0_vapi_METADATADIRS = $(srcdir)
-+ibus_fontset_1_0_vapi_FILES = IBusFontSet-1.0.gir
-+VAPIGEN_VAPIS += $(ibus_fontset_vapi)
-+noinst_DATA += $(ibus_fontset_vapi)
-+EXTRA_DIST += $(ibus_fontset_vapi)
-+MAINTAINERCLEANFILES += $(ibus_fontset_vapi)
-+DISTCLEANFILES += $(ibus_fontset_vapi)
-+
-+endif
-+# end of HAVE_INTROSPECTION
-+endif
-+# end of ENABLE_HARFBUZZ_FOR_EMOJI
-+endif
-+# end of ENABLE_EMOJI_DICT
- 
- -include $(top_srcdir)/git.mk
-diff --git a/ui/gtk3/ibus-emoji-dialog-1.0.deps b/bindings/vala/ibus-emoji-dialog-1.0.deps
-similarity index 100%
-rename from ui/gtk3/ibus-emoji-dialog-1.0.deps
-rename to bindings/vala/ibus-emoji-dialog-1.0.deps
-diff --git a/bindings/vala/ibus-fontset-1.0.deps b/bindings/vala/ibus-fontset-1.0.deps
++-include $(top_srcdir)/git.mk
+diff --git a/portal/org.freedesktop.IBus.Portal.xml b/portal/org.freedesktop.IBus.Portal.xml
 new file mode 100644
-index 0000000..129fe16
+index 00000000..afce4daa
 --- /dev/null
-+++ b/bindings/vala/ibus-fontset-1.0.deps
-@@ -0,0 +1 @@
-+cairo
-diff --git a/configure.ac b/configure.ac
-index cb48ad4..d2aa222 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -653,6 +653,34 @@ https://github.com/fujiwarat/cldr-emoji-annotation)
-     enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
- fi
- 
-+AC_ARG_ENABLE(harfbuzz-for-emoji,
-+    AS_HELP_STRING([--enable-harfbuzz-for-emoji],
-+                   [Enable HarBuzz to draw emoji characters.
-+                    Current Pango has a problem to draw emoji variants and
-+                    this way enables to use HarfBuzz directly in GtkLabel.]),
-+    [enable_harfbuzz_for_emoji=$enableval],
-+    [enable_harfbuzz_for_emoji=no]
-+)
-+AM_CONDITIONAL([ENABLE_HARFBUZZ_FOR_EMOJI],
-+               [test x"$enable_harfbuzz_for_emoji" = x"yes"])
-+
-+if test x"$enable_harfbuzz_for_emoji" = x"yes"; then
-+    PKG_CHECK_MODULES(CAIRO, [
-+        cairo
-+    ])
-+    PKG_CHECK_MODULES(FONTCONFIG, [
-+        fontconfig
-+    ])
-+    PKG_CHECK_MODULES(HARFBUZZ, [
-+        harfbuzz
-+    ])
-+    PKG_CHECK_MODULES(PANGO, [
-+        pango
-+    ])
-+else
-+    enable_harfbuzz_for_emoji="no (disabled, use --enable-harfbuzz-for-emoji to enable)"
-+fi
-+
- # Check iso-codes.
- PKG_CHECK_MODULES(ISOCODES, [
-     iso-codes
-@@ -740,6 +768,7 @@ Build options:
-   Enable Emoji dict             $enable_emoji_dict
-   Unicode Emoji directory       $UNICODE_EMOJI_DIR
-   CLDR annotation directory     $EMOJI_ANNOTATION_DIR
-+  Enable HarfBuzz for Emoji     $enable_harfbuzz_for_emoji
-   Run test cases                $enable_tests
- ])
- 
-diff --git a/po/POTFILES.skip b/po/POTFILES.skip
-index 7190221..10b8829 100644
---- a/po/POTFILES.skip
-+++ b/po/POTFILES.skip
-@@ -2,6 +2,11 @@
- # Please keep this file in alphabetical order.
- # Files under ui/gtk2/ are not shipped in the distribution, but kept
- # in the git repository for reference.
-+bindings/vala/candidatearea.c
-+bindings/vala/emojier.c
-+bindings/vala/iconwidget.c
-+bindings/vala/pango.c
-+bindings/vala/separator.c
- ibus/_config.py
- tools/main.c
- ui/gtk2/candidatepanel.py
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index c79641a..cd1e9c2 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -81,10 +81,6 @@ AM_VALAFLAGS = \
- 	--target-glib="$(VALA_TARGET_GLIB_VERSION)" \
- 	$(NULL)
- 
--MAINTAINERCLEANFILES =
--DISTCLEANFILES =
--noinst_DATA =
--
- if ENABLE_LIBNOTIFY
- AM_CFLAGS += \
- 	@LIBNOTIFY_CFLAGS@ \
-@@ -158,9 +154,10 @@ man_seven_in_files = ibus-emoji.7.in
- EXTRA_DIST =                            \
-     $(emoji_headers)                    \
-     $(man_seven_in_files)               \
--    IBusEmojiDialog-1.0.metadata        \
-+    emojierapp.vala                     \
-     gtkpanel.xml.in                     \
--    ibus-emoji-dialog-1.0.deps          \
-+    ibusfontset.c                       \
-+    ibusfontset.h                       \
-     notification-item.xml               \
-     notification-watcher.xml            \
-     $(NULL)
-@@ -168,98 +165,70 @@ EXTRA_DIST =                            \
- if ENABLE_EMOJI_DICT
- AM_VALAFLAGS += --define=EMOJI_DICT
- 
--libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
--
--noinst_LTLIBRARIES = $(libibus_emoji_dialog)
--
--libibus_emoji_dialog_1_0_la_CFLAGS = $(AM_CFLAGS)
--libibus_emoji_dialog_1_0_la_LDFLAGS =   \
--    -no-undefined                       \
--    -export-symbols-regex "ibus_.*"     \
--    -version-info @LT_VERSION_INFO@     \
--    $(NULL)
--libibus_emoji_dialog_1_0_la_SOURCES =   \
--    candidatearea.vala                  \
--    emojier.vala                        \
--    iconwidget.vala                     \
--    pango.vala                          \
--    separator.vala                      \
--    $(NULL)
--
- libexec_PROGRAMS += ibus-ui-emojier
- 
--ibus_ui_emojier_SOURCES =                       \
--    $(libibus_emoji_dialog_1_0_la_SOURCES)      \
-+ibus_ui_emojier_VALASOURCES =                   \
-     emojierapp.vala                             \
-+    candidatearea.vala                          \
-+    emojier.vala                                \
-+    iconwidget.vala                             \
-+    pango.vala                                  \
-+    separator.vala                              \
-+    $(NULL)
-+ibus_ui_emojier_SOURCES =                       \
-+    $(ibus_ui_emojier_VALASOURCES:.vala=.c)     \
-     $(NULL)
- 
- ibus_ui_emojier_LDADD =                         \
-     $(AM_LDADD)                                 \
-     $(NULL)
- 
---include $(INTROSPECTION_MAKEFILE)
--INTROSPECTION_SCANNER_ARGS =
--INTROSPECTION_COMPILER_ARGS =      \
--    --includedir=$(srcdir)         \
--    --includedir=.                 \
--    --includedir=$(top_srcdir)/src \
--    $(NULL)
--
--if HAVE_INTROSPECTION
--introspection_sources =                \
--    $(emoji_headers)                   \
--    $(NULL)
--IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
--IBusEmojiDialog_1_0_gir_SCANNERFLAGS = \
--    --pkg-export=ibus-1.0              \
--    --pkg=gtk+-3.0                     \
--    $(IBUS_GIR_SCANNERFLAGS)           \
-+ibus_ui_emojier_VALAFLAGS =                     \
-+    $(AM_VALAFLAGS)                             \
-     $(NULL)
--IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
--IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0
--IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus)
--IBusEmojiDialog_1_0_gir_FILES =                      \
--    $(addprefix $(srcdir)/,$(introspection_sources)) \
--    $(NULL)
--IBusEmojiDialog_1_0_gir_CFLAGS =       \
--    -DIBUS_COMPILATION                 \
--    -I$(srcdir)                        \
--    -I$(builddir)                      \
--    -I$(top_srcdir)/src                \
--    $(NULL)
--INTROSPECTION_GIRS = IBusEmojiDialog-1.0.gir
--
--girdir = $(datadir)/gir-1.0
--noinst_DATA += $(INTROSPECTION_GIRS)
--CLEANFILES += $(INTROSPECTION_GIRS)
- 
--typelibsdir = $(libdir)/girepository-1.0
--noinst_DATA += $(INTROSPECTION_GIRS:.gir=.typelib)
--CLEANFILES += $(INTROSPECTION_GIRS:.gir=.typelib)
--
--
--if ENABLE_VAPIGEN
---include $(VAPIGEN_MAKEFILE)
-+# This line and foo_VALASOURCES line can delete the duplicated entries
-+# of emojier.c: emojier.vala
-+emojierapp.c: $(ibus_ui_emojier_VALASOURCES)
-+	$(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \
-+$(VALAFLAGS) -C $(ibus_ui_emojier_VALASOURCES)
-+	$(NULL)
-+# make dist creates .c files in a different srcdir
-+emojierapp.o: $(srcdir)/emojierapp.c
-+	$(AM_V_CC)source='$<' object='$@' libtool=no \
-+	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
-+	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
-+	$(NULL)
- 
--ibus-emoji-dialog-1.0.vapi: $(INTROSPECTION_GIRS) IBusEmojiDialog-1.0.metadata
-+if ENABLE_HARFBUZZ_FOR_EMOJI
-+ibus_ui_gtk3_SOURCES +=                         \
-+    ibusfontset.c                               \
-+    $(NULL)
- 
--VAPIGEN_VAPIS = ibus-emoji-dialog-1.0.vapi
-+ibus_ui_emojier_SOURCES +=                      \
-+    ibusfontset.c                               \
-+    $(NULL)
- 
--ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0
--ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir)
--ibus_emoji_dialog_1_0_vapi_FILES = $(INTROSPECTION_GIRS)
-+AM_CFLAGS += \
-+    @CAIRO_CFLAGS@                              \
-+    @FONTCONFIG_CFLAGS@                         \
-+    @HARFBUZZ_CFLAGS@                           \
-+    $(NULL)
- 
--vapidir = $(datadir)/vala/vapi
--noinst_DATA += $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
-+AM_LDADD += \
-+    @CAIRO_LIBS@                                \
-+    @FONTCONFIG_LIBS@                           \
-+    @HARFBUZZ_LIBS@                             \
-+    $(NULL)
- 
--MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
--DISTCLEANFILES += $(VAPIGEN_VAPIS)
--EXTRA_DIST += $(VAPIGEN_VAPIS)
-+AM_VALAFLAGS += \
-+    -D ENABLE_HARFBUZZ_FOR_EMOJI                \
-+    --pkg=cairo                                 \
-+    --pkg=ibus-fontset-1.0                      \
-+    $(NULL)
- 
--# end of ENABLE_VAPIGEN
--endif
--# end of HAVE_INTROSPECTION
- endif
-+# end of ENABLE_HARFBUZZ_FOR_EMOJI
- 
- man_seven_files = $(man_seven_in_files:.7.in=.7)
- man_seven_DATA =$(man_seven_files:.7=.7.gz)
-@@ -276,7 +245,7 @@ CLEANFILES += \
-     $(man_seven_files) \
-     $(NULL)
- 
--# end of ENABLE_EMOJI_DICT
- endif
-+# end of ENABLE_EMOJI_DICT
- 
- -include $(top_srcdir)/git.mk
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 9df59ac..492a42f 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -80,6 +80,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         }
-     }
-     private class EWhiteLabel : Gtk.Label {
-+#if ENABLE_HARFBUZZ_FOR_EMOJI
-+        IBus.RequisitionEx m_requisition;
-+#endif
-         public EWhiteLabel(string text) {
-             GLib.Object(
-                 name : "IBusEmojierWhiteLabel"
-@@ -87,8 +90,78 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             if (text != "")
-                 set_label(text);
-         }
-+#if ENABLE_HARFBUZZ_FOR_EMOJI
-+        private void get_preferred_size_with_hb(out int minimum_width,
-+                                                out int natural_width,
-+                                                out int minimum_height,
-+                                                out int natural_height) {
-+            minimum_width = 0;
-+            natural_width = 0;
-+            minimum_height = 0;
-+            natural_height = 0;
-+            var text = this.get_text();
-+            if (text == null || text == "")
-+                return;
-+            var context = this.get_pango_context();
-+            var language = context.get_language();
-+            update_fontset(language);
-+            Cairo.RectangleInt widest = Cairo.RectangleInt();
-+            m_requisition = m_fontset.get_preferred_size_hb(text, out widest);
-+            minimum_width = widest.width;
-+            natural_width = widest.width;
-+            minimum_height = widest.height;
-+            natural_height = widest.height;
-+        }
-+        public override void get_preferred_width(out int minimum_width,
-+                                                 out int natural_width) {
-+            get_preferred_size_with_hb(out minimum_width,
-+                                       out natural_width,
-+                                       null, null);
-+        }
-+        public override void get_preferred_height(out int minimum_height,
-+                                                  out int natural_height) {
-+            get_preferred_size_with_hb(null, null,
-+                                       out minimum_height,
-+                                       out natural_height);
-+        }
-+        public override bool draw(Cairo.Context cr) {
-+            if (m_fontset == null)
-+                return true;
-+            if (m_requisition == null)
-+                return true; 
-+            if (m_requisition.cairo_lines == null)
-+                return true; 
-+            var style_context = get_style_context();
-+            Gtk.Allocation allocation;
-+            get_allocation(out allocation);
-+            style_context.render_background(cr,
-+                                            0, 0,
-+                                            allocation.width,
-+                                            allocation.height);
-+            Gdk.RGBA *normal_fg = null;
-+            style_context.get(Gtk.StateFlags.NORMAL,
-+                              "color",
-+                              out normal_fg);
-+            cr.set_operator(Cairo.Operator.OVER);
-+            cr.set_source_rgba(normal_fg.red, normal_fg.green, normal_fg.blue,
-+                               normal_fg.alpha);
-+            cr.save();
-+            double x = 0.0;
-+            double y = 0.0;
-+            if (allocation.width > m_requisition.width)
-+                x = (allocation.width - m_requisition.width) / 2.0;
-+            if (allocation.height > m_requisition.height)
-+                y = (allocation.height - m_requisition.height) / 2.0;
-+            cr.translate(x, y);
-+            m_fontset.draw_cairo_with_requisition_ex(cr, m_requisition);
-+            cr.restore();
-+            normal_fg.free();
-+            normal_fg = null;
-+            return true;
-+        }
-+#endif
-     }
--    private class ESelectedLabel : Gtk.Label {
-+    private class ESelectedLabel : EWhiteLabel {
-         public ESelectedLabel(string text) {
-             GLib.Object(
-                 name : "IBusEmojierSelectedLabel"
-@@ -97,7 +170,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                 set_label(text);
-         }
-     }
--    private class EGoldLabel : Gtk.Label {
-+    private class EGoldLabel : EWhiteLabel {
-         public EGoldLabel(string text) {
-             GLib.Object(
-                 name : "IBusEmojierGoldLabel"
-@@ -212,6 +285,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             m_category_to_emojis_dict;
-     private static GLib.HashTable<string, GLib.SList<string>>?
-             m_emoji_to_emoji_variants_dict;
-+#if ENABLE_HARFBUZZ_FOR_EMOJI
-+    private static IBus.FontSet m_fontset;
-+#endif
- 
-     private ThemedRGBA m_rgba;
-     private Gtk.Box m_vbox;
-@@ -1139,6 +1215,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                     m_category_active_index = (int)list.length();
-         }
-         Gtk.Adjustment adjustment = m_list_box.get_adjustment();
-+        m_scrolled_window.set_hadjustment(new Gtk.Adjustment(0, 0, 0, 0, 0, 0));
-         m_scrolled_window.set_vadjustment(adjustment);
-         show_category_list();
-     }
-@@ -1156,7 +1233,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             else if (keyval == Gdk.Key.Right)
-                 m_lookup_table.cursor_down();
-             show_candidate_panel();
--        } else if (m_entry.get_text().len() > 0) {
-+        } else if (m_entry.get_text().length > 0) {
-             int step = 0;
-             if (keyval == Gdk.Key.Left)
-                 step = -1;
-@@ -1211,7 +1288,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             show_candidate_panel();
-             return true;
-         }
--        if (m_entry.get_text().len() > 0) {
-+        if (m_entry.get_text().length > 0) {
-             int step = 0;
-             if (keyval == Gdk.Key.Home)
-                 step = -1;
-@@ -1410,7 +1487,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             key_press_enter();
-             return true;
-         case Gdk.Key.BackSpace:
--            if (m_entry.get_text().len() > 0) {
-+            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);
-@@ -1422,7 +1499,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             break;
-         case Gdk.Key.Delete:
-         case Gdk.Key.KP_Delete:
--            if (m_entry.get_text().len() > 0) {
-+            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);
-@@ -1436,7 +1513,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         case Gdk.Key.space:
-         case Gdk.Key.KP_Space:
-             if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
--                if (m_entry.get_text().len() > 0)
-+                if (m_entry.get_text().length > 0)
-                     entry_enter_keyval(keyval);
-             } else if (m_candidate_panel_is_visible) {
-                 enter_notify_disable_with_timer();
-@@ -1512,7 +1589,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                     return true;
-                 break;
-             case Gdk.Key.u:
--                if (m_entry.get_text().len() > 0) {
-+                if (m_entry.get_text().length > 0) {
-                     GLib.Signal.emit_by_name(m_entry,
-                                              "delete-from-cursor",
-                                              Gtk.DeleteType.PARAGRAPH_ENDS,
-@@ -1521,13 +1598,13 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                 }
-                 break;
-             case Gdk.Key.a:
--                if (m_entry.get_text().len() > 0) {
-+                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().len() > 0) {
-+                if (m_entry.get_text().length > 0) {
-                     GLib.Signal.emit_by_name(m_entry, "cut-clipboard");
-                     return true;
-                 }
-@@ -1544,7 +1621,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                         clipboard.store();
-                         return true;
-                     }
--                } else if (m_entry.get_text().len() > 0) {
-+                } else if (m_entry.get_text().length > 0) {
-                     GLib.Signal.emit_by_name(m_entry, "copy-clipboard");
-                     return true;
-                 }
-@@ -1600,6 +1677,22 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     }
- 
- 
-+#if ENABLE_HARFBUZZ_FOR_EMOJI
-+    private static void update_fontset(Pango.Language language) {
-+        if (m_fontset != null) {
-+            m_fontset.set_family(m_emoji_font_family);
-+            m_fontset.set_size(m_emoji_font_size);
-+            m_fontset.set_language(language.to_string());
-+            m_fontset.update_fcfontset();
-+        } else {
-+            m_fontset = new IBus.FontSet.with_font(
-+                    m_emoji_font_family,
-+                    m_emoji_font_size,
-+                    language.to_string());
-+        }
-+    }
-+#endif
-+
-     public static bool has_loaded_emoji_dict() {
-         if (m_emoji_to_data_dict == null)
-             return false;
-@@ -1630,6 +1723,10 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         int font_size = font_desc.get_size() / Pango.SCALE;
-         if (font_size != 0)
-             m_emoji_font_size = font_size;
-+#if ENABLE_HARFBUZZ_FOR_EMOJI
-+        var widget = new Gtk.Label("");
-+        update_fontset(widget.get_pango_context().get_language());
-+#endif
-     }
- 
- 
-diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
-index 24d195c..ed8886a 100644
---- a/ui/gtk3/ibusemojidialog.h
-+++ b/ui/gtk3/ibusemojidialog.h
-@@ -170,5 +170,31 @@ void          ibus_emojier_set_favorites          (gchar**      favorites,
-                                                    favorite_annotations,
-                                                    int
-                                                    favorite_annotations_length);
-+
-+/**
-+ * ibus_emojier_set_partial_match:
-+ * @has_partial_match: Enable the partial match if %TRUE. Otherwise if %FALSE.
-+ *
-+ * Set partial match for emoji annotations.
-+ */
-+void          ibus_emojier_set_partial_match      (gboolean  has_partial_match);
-+
-+/**
-+ * ibus_emojier_set_partial_match_length:
-+ * @length: minimum lenght to match partially.
-+ *
-+ * Set the minimum lenght to match partially.
-+ */
-+void          ibus_emojier_set_partial_match_length
-+                                                  (gint         length);
-+
-+/**
-+ * ibus_emojier_set_partial_match_condition:
-+ * @condition: condition id between 0 and 2.
-+ *
-+ * Set the partial match condition with the integer.
-+ */
-+void          ibus_emojier_set_partial_match_condition
-+                                                  (gint         condition);
- G_END_DECLS
- #endif
-diff --git a/ui/gtk3/ibusfontset.c b/ui/gtk3/ibusfontset.c
++++ b/portal/org.freedesktop.IBus.Portal.xml
+@@ -0,0 +1,132 @@
++<?xml version="1.0"?>
++<!--
++ Copyright (C) 2017 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
++ License as published by the Free Software Foundation; either
++ version 2 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/>.
++
++ Author: Alexander Larsson <alexl@redhat.com>
++-->
++
++<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
++  <!--
++      org.freedesktop.IBus.Portal:
++      @short_description: Portal for ibus client access
++
++      This interface is a minimal interface to IBus that is safe to expose to
++      clients.
++  -->
++  <interface name="org.freedesktop.IBus.Portal">
++    <method name='CreateInputContext'>
++      <arg direction='in'  type='s' name='client_name' />
++      <arg direction='out' type='o' name='object_path' />
++    </method>
++  </interface>
++
++  <!-- This is a copy of the interface in inputcontext.c, they should be shared.
++       We want this for the code generator so that we can be sure we verify all
++       caller types, etc.
++  -->
++  <interface name='org.freedesktop.IBus.InputContext'>
++    <method name='ProcessKeyEvent'>
++      <arg direction='in'  type='u' name='keyval' />
++      <arg direction='in'  type='u' name='keycode' />
++      <arg direction='in'  type='u' name='state' />
++      <arg direction='out' type='b' name='handled' />
++    </method>
++    <method name='SetCursorLocation'>
++      <arg direction='in' type='i' name='x' />
++      <arg direction='in' type='i' name='y' />
++      <arg direction='in' type='i' name='w' />
++      <arg direction='in' type='i' name='h' />
++    </method>
++    <method name='SetCursorLocationRelative'>
++      <arg direction='in' type='i' name='x' />
++      <arg direction='in' type='i' name='y' />
++      <arg direction='in' type='i' name='w' />
++      <arg direction='in' type='i' name='h' />
++    </method>
++    <method name='ProcessHandWritingEvent'>
++      <arg direction='in' type='ad' name='coordinates' />
++    </method>
++    <method name='CancelHandWriting'>
++      <arg direction='in' type='u' name='n_strokes' />
++    </method>
++    <method name='FocusIn' />
++    <method name='FocusOut' />
++    <method name='Reset' />
++    <method name='SetCapabilities'>
++      <arg direction='in' type='u' name='caps' />
++    </method>
++    <method name='PropertyActivate'>
++      <arg direction='in' type='s' name='name' />
++      <arg direction='in' type='u' name='state' />
++    </method>
++    <method name='SetEngine'>
++      <arg direction='in' type='s' name='name' />
++    </method>
++    <method name='GetEngine'>
++      <arg direction='out' type='v' name='desc' />
++    </method>
++    <method name='SetSurroundingText'>
++      <arg direction='in' type='v' name='text' />
++      <arg direction='in' type='u' name='cursor_pos' />
++      <arg direction='in' type='u' name='anchor_pos' />
++    </method>
++
++    <signal name='CommitText'>
++      <arg type='v' name='text' />
++    </signal>
++    <signal name='ForwardKeyEvent'>
++      <arg type='u' name='keyval' />
++      <arg type='u' name='keycode' />
++      <arg type='u' name='state' />
++    </signal>
++    <signal name='UpdatePreeditText'>
++      <arg type='v' name='text' />
++      <arg type='u' name='cursor_pos' />
++      <arg type='b' name='visible' />
++    </signal>
++    <signal name='ShowPreeditText'/>
++    <signal name='HidePreeditText'/>
++    <signal name='UpdateAuxiliaryText'>
++      <arg type='v' name='text' />
++      <arg type='b' name='visible' />
++    </signal>
++    <signal name='ShowAuxiliaryText'/>
++    <signal name='HideAuxiliaryText'/>
++    <signal name='UpdateLookupTable'>
++      <arg type='v' name='table' />
++      <arg type='b' name='visible' />
++    </signal>
++    <signal name='ShowLookupTable'/>
++    <signal name='HideLookupTable'/>
++    <signal name='PageUpLookupTable'/>
++    <signal name='PageDownLookupTable'/>
++    <signal name='CursorUpLookupTable'/>
++    <signal name='CursorDownLookupTable'/>
++    <signal name='RegisterProperties'>
++      <arg type='v' name='props' />
++    </signal>
++    <signal name='UpdateProperty'>
++      <arg type='v' name='prop' />
++    </signal>
++
++    <property name='ContentType' type='(uu)' access='write' />
++  </interface>
++
++  <interface name='org.freedesktop.IBus.Service'>
++    <method name='Destroy' />
++  </interface>
++
++</node>
+diff --git a/portal/org.freedesktop.portal.IBus.service.in b/portal/org.freedesktop.portal.IBus.service.in
+new file mode 100644
+index 00000000..47ae9ffc
+--- /dev/null
++++ b/portal/org.freedesktop.portal.IBus.service.in
+@@ -0,0 +1,3 @@
++[D-BUS Service]
++Name=org.freedesktop.portal.IBus
++Exec=@libexecdir@/ibus-portal
+diff --git a/portal/portal.c b/portal/portal.c
 new file mode 100644
-index 0000000..7864a64
+index 00000000..0415f996
 --- /dev/null
-+++ b/ui/gtk3/ibusfontset.c
-@@ -0,0 +1,923 @@
++++ b/portal/portal.c
+@@ -0,0 +1,698 @@
 +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 +/* vim:set et sts=4: */
 +/* ibus - The Input Bus
-+ * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
 + * Copyright (C) 2017 Red Hat, Inc.
 + *
 + * This library is free software; you can redistribute it and/or
@@ -1643,1216 +1218,3091 @@ index 0000000..7864a64
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 + * USA
 + */
-+
-+#include <cairo-ft.h>
-+#include <fontconfig/fontconfig.h>
-+#include <ft2build.h>
-+#include FT_FREETYPE_H
++#include <config.h>
++#include <fcntl.h>
 +#include <glib.h>
-+#include <hb-ot.h>
-+#include <pango/pango.h>
-+
-+#include "ibusfontset.h"
-+
-+#define XPAD 2
-+#define YPAD 2
-+#define UNKNOWN_FONT_SIZE 7
-+#define IBUS_FONTSET_GET_PRIVATE(o)  \
-+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_FONTSET, IBusFontSetPrivate))
-+
-+
-+static FT_Library  m_ftlibrary;
-+static FcFontSet  *m_fcfontset;
-+static gchar      *m_family;
-+static guint       m_size;
-+static gchar      *m_language;
-+static GHashTable *m_scaled_font_table;
-+static GHashTable *m_hb_font_table;
-+
-+enum {
-+    PROP_0,
-+    PROP_FAMILY,
-+    PROP_SIZE,
-+    PROP_LANGUAGE
++#include <gio/gio.h>
++#include <ibus.h>
++#include <locale.h>
++#include <pwd.h>
++#include <signal.h>
++#include <stdlib.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++
++#include "ibus-portal-dbus.h"
++
++typedef struct _IBusPortal IBusPortal;
++typedef struct _IBusPortalClass IBusPortalClass;
++typedef struct _IBusPortalContext IBusPortalContext;
++typedef struct _IBusPortalContextClass IBusPortalContextClass;
++
++struct _IBusPortalContext
++{
++    IBusDbusInputContextSkeleton parent_instance;
++    IBusInputContext *context;
++    guint id;
++    char *owner;
++    char *object_path;
++    IBusDbusService *service;
++};
++
++struct _IBusPortalContextClass
++{
++    IBusDbusInputContextSkeletonClass parent_class;
 +};
 +
-+typedef struct {
-+    gunichar   ch;
-+    FcPattern *fcfont;
-+} FontPerChar;
++struct _IBusPortal
++{
++    IBusDbusPortalSkeleton parent_instance;
++
++};
++
++struct _IBusPortalClass
++{
++    IBusDbusPortalSkeletonClass parent_class;
++};
 +
-+struct _IBusFontSetPrivate {
-+    gchar     *family;
-+    guint      size;
-+    gchar     *language;
++enum
++{
++    PROP_CONTENT_TYPE = 1,
++    N_PROPERTIES
 +};
 +
-+static GObject * ibus_fontset_constructor   (GType                  type,
-+                                             guint                  n,
-+                                             GObjectConstructParam *args);
-+static void      ibus_fontset_destroy       (IBusFontSet           *fontset);
-+static void      ibus_fontset_set_property  (IBusFontSet           *fontset,
-+                                             guint                  prop_id,
-+                                             const GValue          *value,
-+                                             GParamSpec            *pspec);
-+static void      ibus_fontset_get_property  (IBusFontSet           *fontset,
-+                                             guint                  prop_id,
-+                                             GValue                *value,
-+                                             GParamSpec            *pspec);
-+static cairo_scaled_font_t *
-+                 ibus_fontset_cairo_scaled_font_new_with_font
-+                                            (const gchar           *family,
-+                                             guint                  size);
-+
-+G_DEFINE_BOXED_TYPE (IBusCairoLine,
-+                     ibus_cairo_line,
-+                     ibus_cairo_line_copy,
-+                     ibus_cairo_line_free);
-+G_DEFINE_BOXED_TYPE (IBusRequisitionEx,
-+                     ibus_requisition_ex,
-+                     ibus_requisition_ex_copy,
-+                     ibus_requisition_ex_free);
-+G_DEFINE_TYPE (IBusFontSet, ibus_fontset, IBUS_TYPE_OBJECT)
++static GMainLoop *loop = NULL;
++static IBusBus *ibus_bus;
++static IBusPortal *ibus_portal = NULL;
++static gboolean opt_verbose;
++static gboolean opt_replace;
++
++static GList *all_contexts = NULL;
++
++static guint next_context_id;
++
++GType ibus_portal_context_get_type (void) G_GNUC_CONST;
++static void ibus_portal_context_iface_init (IBusDbusInputContextIface *iface);
++
++static void portal_context_g_signal (GDBusProxy        *proxy,
++                                     const gchar       *sender_name,
++                                     const gchar       *signal_name,
++                                     GVariant          *parameters,
++                                     IBusPortalContext *portal_context);
++
++G_DEFINE_TYPE_WITH_CODE (IBusPortalContext,
++                         ibus_portal_context,
++                         IBUS_DBUS_TYPE_INPUT_CONTEXT_SKELETON,
++                         G_IMPLEMENT_INTERFACE (IBUS_DBUS_TYPE_INPUT_CONTEXT,
++                                 ibus_portal_context_iface_init));
 +
 +static void
-+ibus_fontset_class_init (IBusFontSetClass *class)
++_forward_method_cb (GObject *source_object,
++                    GAsyncResult *res,
++                    gpointer user_data)
 +{
-+    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-+    IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
-+    cairo_glyph_t dummy;
-+    IBusGlyph dummy2;
++    GDBusMethodInvocation *invocation = user_data;
++    IBusPortalContext *portal_context =
++            (IBusPortalContext *) g_dbus_method_invocation_get_user_data (
++                    invocation);
++    IBusEngineDesc *desc;
++    GError *error = NULL;
 +
-+    gobject_class->constructor = ibus_fontset_constructor;
-+    gobject_class->get_property =
-+            (GObjectGetPropertyFunc) ibus_fontset_get_property;
-+    gobject_class->set_property =
-+            (GObjectSetPropertyFunc) ibus_fontset_set_property;
-+    object_class->destroy = (IBusObjectDestroyFunc) ibus_fontset_destroy;
++    GVariant *variant = g_dbus_proxy_call_finish ((GDBusProxy *) source_object,
++                                                  res, &error);
++    if (variant == NULL) {
++        g_dbus_method_invocation_return_gerror (invocation, error);
++        g_error_free (error);
++        return;
++    }
 +
-+    /* install properties */
-+    /**
-+     * IBusFontSet:family:
-+     *
-+     * Font family of this IBusFontSet.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_FAMILY,
-+                    g_param_spec_string ("family",
-+                        "family",
-+                        "family",
-+                        "",
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
++    g_dbus_method_invocation_return_value (invocation, variant);
++}
 +
-+    /**
-+     * IBusFontSet:size:
-+     *
-+     * Font size of this IBusFontSet.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_SIZE,
-+                    g_param_spec_uint ("size",
-+                        "size",
-+                        "size",
-+                        0, G_MAXUINT16, 0,
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
++static gboolean
++_forward_method (IBusDbusInputContext  *object,
++                 GDBusMethodInvocation *invocation)
++{
++    IBusPortalContext *portal_context = (IBusPortalContext *)object;
++    GDBusMessage *message = g_dbus_method_invocation_get_message (invocation);
++
++    g_dbus_proxy_call (G_DBUS_PROXY (portal_context->context),
++                       g_dbus_method_invocation_get_method_name (invocation),
++                       g_dbus_message_get_body (message),
++                       G_DBUS_CALL_FLAGS_NONE,
++                       -1,
++                       NULL, /* cancellable */
++                       _forward_method_cb, invocation);
++    return TRUE;
++}
 +
-+    /**
-+     * IBusFontSet:language:
-+     *
-+     * Font language of this IBusFontSet.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_LANGUAGE,
-+                    g_param_spec_string ("language",
-+                        "language",
-+                        "language",
-+                        "",
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-+
-+    g_type_class_add_private (class, sizeof (IBusFontSetPrivate));
-+    FT_Init_FreeType (&m_ftlibrary);
-+    m_scaled_font_table = g_hash_table_new_full (
-+            g_str_hash, g_str_equal,
-+            g_free,
-+            (GDestroyNotify) cairo_scaled_font_destroy);
-+    m_hb_font_table = g_hash_table_new_full (
-+            g_str_hash, g_str_equal,
-+            g_free,
-+            (GDestroyNotify) hb_font_destroy);
-+
-+    /* hb_glyph_t is not available in Vala so override it with IBusGlyph. */
-+    g_assert (sizeof (dummy) == sizeof (dummy2));
-+    g_assert (sizeof (dummy.index) == sizeof (dummy2.index));
-+    g_assert (sizeof (dummy.x) == sizeof (dummy2.x));
-+    g_assert (sizeof (dummy.y) == sizeof (dummy2.y));
++static gboolean
++ibus_dbus_context_cancel_hand_writing (IBusDbusInputContext  *object,
++                                       GDBusMethodInvocation *invocation,
++                                       guint                  arg_n_strokes)
++{
++    return _forward_method (object, invocation);
 +}
 +
-+static void
-+ibus_fontset_init (IBusFontSet *fontset)
++static gboolean
++ibus_dbus_context_focus_in (IBusDbusInputContext  *object,
++                            GDBusMethodInvocation *invocation)
 +{
-+    fontset->priv = IBUS_FONTSET_GET_PRIVATE (fontset);
++    return _forward_method (object, invocation);
 +}
 +
++static gboolean
++ibus_dbus_context_focus_out (IBusDbusInputContext  *object,
++                             GDBusMethodInvocation *invocation)
++{
++    return _forward_method (object, invocation);
++}
 +
-+static GObject *
-+ibus_fontset_constructor (GType                   type,
-+                          guint                   n,
-+                          GObjectConstructParam  *args)
++static gboolean
++ibus_dbus_context_get_engine (IBusDbusInputContext  *object,
++                              GDBusMethodInvocation *invocation)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_process_hand_writing_event (IBusDbusInputContext  *object,
++                                              GDBusMethodInvocation *invocation,
++                                              GVariant
++                                                               *arg_coordinates)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_process_key_event (IBusDbusInputContext  *object,
++                                     GDBusMethodInvocation *invocation,
++                                     guint                  arg_keyval,
++                                     guint                  arg_keycode,
++                                     guint                  arg_state)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_property_activate (IBusDbusInputContext  *object,
++                                     GDBusMethodInvocation *invocation,
++                                     const gchar           *arg_name,
++                                     guint                  arg_state)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_reset (IBusDbusInputContext  *object,
++                         GDBusMethodInvocation *invocation)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_set_capabilities (IBusDbusInputContext  *object,
++                                    GDBusMethodInvocation *invocation,
++                                    guint                  arg_caps)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_set_cursor_location (IBusDbusInputContext  *object,
++                                       GDBusMethodInvocation *invocation,
++                                       gint                   arg_x,
++                                       gint                   arg_y,
++                                       gint                   arg_w,
++                                       gint                   arg_h)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_set_cursor_location_relative (IBusDbusInputContext  *object,
++                                                GDBusMethodInvocation
++                                                                    *invocation,
++                                                gint                   arg_x,
++                                                gint                   arg_y,
++                                                gint                   arg_w,
++                                                gint                   arg_h)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_set_engine (IBusDbusInputContext  *object,
++                              GDBusMethodInvocation *invocation,
++                              const gchar           *arg_name)
++{
++    return _forward_method (object, invocation);
++}
++
++static gboolean
++ibus_dbus_context_set_surrounding_text (IBusDbusInputContext  *object,
++                                        GDBusMethodInvocation *invocation,
++                                        GVariant              *arg_text,
++                                        guint                  arg_cursor_pos,
++                                        guint                  arg_anchor_pos)
++{
++    return _forward_method (object, invocation);
++}
++
++static void
++ibus_portal_context_iface_init (IBusDbusInputContextIface *iface)
++{
++    iface->handle_cancel_hand_writing = ibus_dbus_context_cancel_hand_writing;
++    iface->handle_focus_in = ibus_dbus_context_focus_in;
++    iface->handle_focus_out = ibus_dbus_context_focus_out;
++    iface->handle_get_engine = ibus_dbus_context_get_engine;
++    iface->handle_process_hand_writing_event =
++            ibus_dbus_context_process_hand_writing_event;
++    iface->handle_process_key_event = ibus_dbus_context_process_key_event;
++    iface->handle_property_activate = ibus_dbus_context_property_activate;
++    iface->handle_reset = ibus_dbus_context_reset;
++    iface->handle_set_capabilities = ibus_dbus_context_set_capabilities;
++    iface->handle_set_cursor_location = ibus_dbus_context_set_cursor_location;
++    iface->handle_set_cursor_location_relative =
++            ibus_dbus_context_set_cursor_location_relative;
++    iface->handle_set_engine = ibus_dbus_context_set_engine;
++    iface->handle_set_surrounding_text = ibus_dbus_context_set_surrounding_text;
++}
++
++static void
++ibus_portal_context_init (IBusPortalContext *portal_context)
 +{
-+    GObject *object;
-+    IBusFontSet *fontset;
-+    const gchar *family;
-+    guint size;
-+
-+    object = G_OBJECT_CLASS (ibus_fontset_parent_class)->constructor (
-+            type, n ,args);
-+    fontset = IBUS_FONTSET (object);
-+    family = ibus_fontset_get_family (fontset);
-+    size = ibus_fontset_get_size (fontset);
-+    ibus_fontset_update_fcfontset (fontset);
-+    if (family != NULL && size > 0) {
-+        /* cache the font */
-+        ibus_fontset_cairo_scaled_font_new_with_font (family,
-+                                                      size);
-+    }
-+    return object;
 +}
 +
 +static void
-+ibus_fontset_destroy (IBusFontSet *fontset)
++ibus_portal_context_finalize (GObject *object)
 +{
-+    g_clear_pointer (&fontset->priv->family, g_free);
-+    g_clear_pointer (&fontset->priv->language, g_free);
++    IBusPortalContext *portal_context = (IBusPortalContext *)object;
++
++    all_contexts = g_list_remove (all_contexts, portal_context);
++
++    g_dbus_interface_skeleton_unexport (
++            G_DBUS_INTERFACE_SKELETON (portal_context->service));
++    g_dbus_interface_skeleton_unexport (
++            G_DBUS_INTERFACE_SKELETON (portal_context));
++
++    g_free (portal_context->owner);
++    g_free (portal_context->object_path);
++    g_object_unref (portal_context->service);
++
++    g_signal_handlers_disconnect_by_func (
++            portal_context->context,
++            G_CALLBACK(portal_context_g_signal),
++            portal_context);
++    g_object_unref (portal_context->context);
++
++    G_OBJECT_CLASS (ibus_portal_context_parent_class)->finalize (object);
 +}
 +
 +static void
-+ibus_fontset_set_property (IBusFontSet  *fontset,
-+                           guint         prop_id,
-+                           const GValue *value,
-+                           GParamSpec   *pspec)
++ibus_portal_context_set_property (IBusPortalContext *portal_context,
++                                  guint              prop_id,
++                                  const GValue      *value,
++                                  GParamSpec        *pspec)
 +{
 +    switch (prop_id) {
-+    case PROP_FAMILY:
-+        ibus_fontset_set_family (fontset, g_value_get_string (value));
-+        break;
-+    case PROP_SIZE:
-+        ibus_fontset_set_size (fontset, g_value_get_uint (value));
-+        break;
-+    case PROP_LANGUAGE:
-+        ibus_fontset_set_language (fontset, g_value_get_string (value));
++    case PROP_CONTENT_TYPE:
++        g_dbus_proxy_call (G_DBUS_PROXY (portal_context->context),
++                           "org.freedesktop.DBus.Properties.Set",
++                           g_variant_new ("(ssv)",
++                                          IBUS_INTERFACE_INPUT_CONTEXT,
++                                          "ContentType",
++                                          g_value_get_variant (value)),
++                           G_DBUS_CALL_FLAGS_NONE,
++                           -1,
++                           NULL, /* cancellable */
++                           NULL, /* callback */
++                           NULL  /* user_data */
++                           );
 +        break;
 +    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec);
++        G_OBJECT_WARN_INVALID_PROPERTY_ID (portal_context, prop_id, pspec);
 +    }
 +}
 +
 +static void
-+ibus_fontset_get_property (IBusFontSet *fontset,
-+                           guint          prop_id,
-+                           GValue        *value,
-+                           GParamSpec    *pspec)
++ibus_portal_context_get_property (IBusPortalContext *portal_context,
++                                  guint              prop_id,
++                                  GValue            *value,
++                                  GParamSpec        *pspec)
 +{
 +    switch (prop_id) {
-+    case PROP_FAMILY:
-+        g_value_set_string (value, ibus_fontset_get_family (fontset));
-+        break;
-+    case PROP_SIZE:
-+        g_value_set_uint (value, ibus_fontset_get_size (fontset));
-+        break;
-+    case PROP_LANGUAGE:
-+        g_value_set_string (value, ibus_fontset_get_language (fontset));
++    case PROP_CONTENT_TYPE:
++        g_warning ("No support for setting content type");
 +        break;
 +    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec);
++        G_OBJECT_WARN_INVALID_PROPERTY_ID (portal_context, prop_id, pspec);
 +    }
 +}
 +
-+static cairo_scaled_font_t *
-+ibus_fontset_cairo_scaled_font_new_with_font (const gchar *family,
-+                                              guint        size)
++static gboolean
++ibus_portal_context_g_authorize_method (GDBusInterfaceSkeleton *interface,
++                                        GDBusMethodInvocation  *invocation)
 +{
-+    gchar *font_name;
-+    cairo_scaled_font_t *scaled_font = NULL;
-+    FcPattern *pattern, *resolved;
-+    FcResult result;
-+    cairo_font_options_t *font_options;
-+    double pixel_size = 0.;
-+    FcMatrix fc_matrix, *fc_matrix_val;
-+    cairo_font_face_t *cairo_face = NULL;
-+    cairo_matrix_t font_matrix;
-+    cairo_matrix_t ctm;
-+    int i;
++    IBusPortalContext *portal_context = (IBusPortalContext *)interface;
 +
-+    g_return_val_if_fail (family != NULL, NULL);
-+    g_return_val_if_fail (m_scaled_font_table != NULL, NULL);
-+
-+    font_name = g_strdup_printf ("%s %u", family, size);
-+    scaled_font = g_hash_table_lookup (m_scaled_font_table, font_name);
-+    if (scaled_font != NULL) {
-+        g_free (font_name);
-+        return scaled_font;
-+    }
-+    pattern = FcPatternCreate ();
-+    FcPatternAddString (pattern, FC_FAMILY, (FcChar8*) family);
-+    FcPatternAddDouble (pattern, FC_SIZE, (double) size);
-+    FcPatternAddDouble (pattern, FC_DPI, 96);
-+    FcConfigSubstitute(NULL, pattern, FcMatchPattern);
-+    font_options = cairo_font_options_create ();
-+    cairo_ft_font_options_substitute (font_options, pattern);
-+    FcDefaultSubstitute (pattern);
-+    resolved = FcFontMatch (NULL, pattern, &result);
-+    FcPatternDestroy (pattern);
-+    FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size);
-+    if (pixel_size == 0.)
-+        g_warning ("Failed to scaled the font: %s %u", family, size);
-+    cairo_face = cairo_ft_font_face_create_for_pattern (resolved);
-+    FcMatrixInit (&fc_matrix);
-+    for (i = 0;
-+         FcPatternGetMatrix (resolved, FC_MATRIX, i, &fc_matrix_val)
-+                 == FcResultMatch;
-+         i++) {
-+        FcMatrixMultiply (&fc_matrix, &fc_matrix, fc_matrix_val);
++    if (g_strcmp0 (g_dbus_method_invocation_get_sender (invocation),
++                   portal_context->owner) == 0) {
++        return TRUE;
 +    }
-+    FcPatternDestroy (resolved);
-+    cairo_matrix_init (&font_matrix,
-+                       fc_matrix.xx, -fc_matrix.yx,
-+                       -fc_matrix.xy, fc_matrix.yy,
-+                       0., 0.);
-+    if (pixel_size != 0.)
-+        cairo_matrix_scale (&font_matrix, pixel_size, pixel_size);
-+    cairo_matrix_init_identity (&ctm);
-+    scaled_font = cairo_scaled_font_create (cairo_face,
-+                                            &font_matrix, &ctm,
-+                                            font_options);
-+    cairo_font_face_destroy (cairo_face);
-+    if (font_name)
-+        g_hash_table_insert(m_scaled_font_table, font_name, scaled_font);
-+
-+    return scaled_font;
-+}
 +
-+static hb_font_t *
-+ibus_fontset_hb_font_new_with_font_path (const gchar *font_path)
-+{
-+    hb_font_t *hb_font;
-+    GError *error = NULL;
-+    GMappedFile *mf;
-+    char *font_data = NULL;
-+    gsize len;
-+    hb_blob_t *hb_blob;
-+    hb_face_t *hb_face;
-+
-+    g_return_val_if_fail (font_path != NULL, NULL);
-+    g_return_val_if_fail (m_hb_font_table != NULL, NULL);
-+
-+    hb_font = g_hash_table_lookup (m_hb_font_table, font_path);
-+    if (hb_font != NULL)
-+        return hb_font;
-+
-+    mf = g_mapped_file_new (font_path, FALSE, &error);
-+    if (mf == NULL) {
-+        g_warning ("Not found font %s", font_path);
-+        return NULL;
-+    }
-+    font_data = g_mapped_file_get_contents (mf);
-+    len = g_mapped_file_get_length (mf);
-+    if (len == 0) {
-+        g_warning ("zero size font %s", font_path);
-+        g_mapped_file_unref (mf);
-+        return NULL;
-+    }
-+    hb_blob = hb_blob_create (font_data, len,
-+                              HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
-+                              mf, (hb_destroy_func_t)g_mapped_file_unref);
-+    hb_face = hb_face_create (hb_blob, 0);
-+    hb_blob_destroy (hb_blob);
-+    hb_font = hb_font_create (hb_face);
-+    unsigned int upem = hb_face_get_upem (hb_face);
-+    hb_font_set_scale (hb_font, upem, upem);
-+    hb_face_destroy (hb_face);
-+    hb_ot_font_set_funcs (hb_font);
-+    g_hash_table_insert (m_hb_font_table, g_strdup (font_path), hb_font);
-+
-+    return hb_font;
++    g_dbus_method_invocation_return_error (invocation,
++                                           G_DBUS_ERROR,
++                                           G_DBUS_ERROR_FAILED,
++                                           "Access denied");
++    return FALSE;
 +}
 +
++
 +static void
-+get_font_extents_with_scaled_font (cairo_scaled_font_t *scaled_font,
-+                                   PangoRectangle      *font_rect)
++ibus_portal_context_class_init (IBusPortalContextClass *klass)
 +{
-+    cairo_font_extents_t font_extents;
++    GObjectClass *gobject_class;
++    GDBusInterfaceSkeletonClass *skeleton_class;
 +
-+    g_assert (scaled_font != NULL && font_rect != NULL);
++    gobject_class = G_OBJECT_CLASS (klass);
++    gobject_class->finalize  = ibus_portal_context_finalize;
++    gobject_class->set_property  =
++            (GObjectSetPropertyFunc) ibus_portal_context_set_property;
++    gobject_class->get_property  =
++            (GObjectGetPropertyFunc) ibus_portal_context_get_property;
 +
-+    cairo_scaled_font_extents (scaled_font, &font_extents);
-+    font_rect->x = 0;
-+    font_rect->y = - pango_units_from_double (font_extents.ascent);
-+    font_rect->width = 0;
-+    font_rect->height = pango_units_from_double (
-+            font_extents.ascent + font_extents.descent);
-+}
++    skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS(klass);
++    skeleton_class->g_authorize_method = ibus_portal_context_g_authorize_method;
 +
-+static void
-+get_glyph_extents_with_scaled_hb_font (const gchar          *str,
-+                                       cairo_scaled_font_t  *scaled_font,
-+                                       hb_font_t            *hb_font,
-+                                       PangoRectangle       *font_rect,
-+                                       IBusCairoLine       **cairo_lines,
-+                                       FcChar8              *fallback_family)
-+{
-+    gboolean has_unknown_glyph = FALSE;
-+    hb_buffer_t *hb_buffer;
-+    unsigned int len, n, i;
-+    hb_glyph_info_t *info;
-+    hb_glyph_position_t *pos;
-+    double x;
-+    cairo_glyph_t *glyph;
-+    cairo_text_extents_t text_extents = { 0, };
-+
-+    g_return_if_fail (str != NULL);
-+
-+    hb_buffer = hb_buffer_create ();
-+    hb_buffer_add_utf8 (hb_buffer, str, -1, 0, -1);
-+    hb_buffer_guess_segment_properties (hb_buffer);
-+    for (n = 0; *cairo_lines && (*cairo_lines)[n].scaled_font; n++);
-+    if (n == 0)
-+        *cairo_lines = g_new0 (IBusCairoLine, 2);
-+    else
-+        *cairo_lines = g_renew (IBusCairoLine, *cairo_lines, n + 2);
-+    (*cairo_lines)[n + 1].scaled_font = NULL;
-+    (*cairo_lines)[n + 1].num_glyphs = 0;
-+    (*cairo_lines)[n + 1].glyphs = NULL;
-+    hb_shape (hb_font, hb_buffer, NULL, 0);
-+    len = hb_buffer_get_length (hb_buffer);
-+    info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
-+    pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);
-+    (*cairo_lines)[n].scaled_font = scaled_font;
-+    (*cairo_lines)[n].num_glyphs = len;
-+    (*cairo_lines)[n].glyphs = (IBusGlyph*) cairo_glyph_allocate (len + 1);
-+    x = 0.;
-+    for (i = 0; i < len; i++) {
-+        hb_codepoint_t c = info[i].codepoint;
-+        if (c) {
-+            (*cairo_lines)[n].glyphs[i].index = info[i].codepoint;
-+            (*cairo_lines)[n].glyphs[i].x = x;
-+            (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE;
-+            glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]);
-+            cairo_scaled_font_glyph_extents (scaled_font, glyph,
-+                                             1, &text_extents);
-+            x += text_extents.width;
-+        } else {
-+            has_unknown_glyph = TRUE;
-+            c = g_utf8_get_char (str);
-+            (*cairo_lines)[n].glyphs[i].index = PANGO_GET_UNKNOWN_GLYPH (c);
-+            (*cairo_lines)[n].glyphs[i].x = x;
-+            (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE;
-+            glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]);
-+            cairo_scaled_font_glyph_extents (scaled_font, glyph,
-+                                             1, &text_extents);
-+            x += 10;
-+        }
-+    }
-+    (*cairo_lines)[n].glyphs[i].index = -1;
-+    (*cairo_lines)[n].glyphs[i].x = 0;
-+    (*cairo_lines)[n].glyphs[i].y = 0;
-+    glyph = (cairo_glyph_t *) (*cairo_lines)[n].glyphs;
-+    cairo_scaled_font_glyph_extents (scaled_font, glyph,
-+                                     len, &text_extents);
-+    if (text_extents.width) {
-+        font_rect->width = pango_units_from_double (text_extents.width);
-+    } else {
-+        font_rect->width = font_rect->height;
-+    }
-+    if (has_unknown_glyph && fallback_family != NULL) {
-+        cairo_scaled_font_t  *unknown_font;
-+        unknown_font = ibus_fontset_cairo_scaled_font_new_with_font (
-+            (const gchar *) fallback_family,
-+            UNKNOWN_FONT_SIZE);
-+        (*cairo_lines)[n].scaled_font = unknown_font;
-+    }
-+    hb_buffer_destroy (hb_buffer);
++    ibus_dbus_input_context_override_properties (gobject_class,
++                                                 PROP_CONTENT_TYPE);
 +}
 +
 +static void
-+get_string_extents_with_font (const gchar            *str,
-+                              FontPerChar            *buff,
-+                              cairo_rectangle_int_t  *rect,
-+                              IBusCairoLine         **cairo_lines)
++portal_context_g_signal (GDBusProxy        *proxy,
++                         const gchar       *sender_name,
++                         const gchar       *signal_name,
++                         GVariant          *parameters,
++                         IBusPortalContext *portal_context)
 +{
-+    FcChar8 *family = NULL;
-+    FcChar8 *font_path = NULL;
-+    guint size = 0;
-+    cairo_scaled_font_t *scaled_font = NULL;
-+    PangoRectangle font_rect = { 0, };
-+    hb_font_t *hb_font;
-+
-+    g_return_if_fail (str != NULL);
-+    g_return_if_fail (buff != NULL && buff->fcfont != NULL);
-+
-+    FcPatternGetString (buff->fcfont, FC_FAMILY, 0, &family);
-+    g_return_if_fail (family != NULL);
-+    size = m_size;
-+    if (size == 0) {
-+        g_warning ("Font size is not right for font %s.", family);
-+        size = 14;
-+    }
-+    scaled_font = ibus_fontset_cairo_scaled_font_new_with_font (
-+            (const gchar *) family,
-+            size);
-+    g_return_if_fail (scaled_font != NULL);
-+    get_font_extents_with_scaled_font (scaled_font, &font_rect);
-+
-+    FcPatternGetString (buff->fcfont, FC_FILE, 0, &font_path);
-+    g_return_if_fail (font_path != NULL);
-+    hb_font = ibus_fontset_hb_font_new_with_font_path (
-+            (const gchar *) font_path);
-+    if (hb_font == NULL)
++    GError *error = NULL;
++    GDBusConnection *connection;
++
++    if (g_strcmp0 (sender_name, IBUS_SERVICE_IBUS) != 0)
 +        return;
-+    get_glyph_extents_with_scaled_hb_font (str,
-+                                           scaled_font,
-+                                           hb_font,
-+                                           &font_rect,
-+                                           cairo_lines,
-+                                           family);
-+    rect->width += font_rect.width / PANGO_SCALE;
-+    rect->height += font_rect.height / PANGO_SCALE;
++
++    connection = g_dbus_interface_skeleton_get_connection (
++            G_DBUS_INTERFACE_SKELETON (portal_context));
++    if (!g_dbus_connection_emit_signal (connection,
++                                        portal_context->owner,
++                                        portal_context->object_path,
++                                        IBUS_INTERFACE_INPUT_CONTEXT,
++                                        signal_name,
++                                        parameters,
++                                        &error)) {
++        g_warning ("Unable to emit signal %s: %s", signal_name, error->message);
++        g_error_free (error);
++    }
++
++    g_signal_stop_emission_by_name (proxy, "g-signal");
 +}
 +
-+static FT_Face
-+ibus_fontset_get_ftface_from_fcfont (IBusFontSet *fontset,
-+                                     FcPattern   *fcfont)
++static gboolean
++ibus_portal_context_handle_destroy (IBusDbusService       *object,
++                                    GDBusMethodInvocation *invocation,
++                                    IBusPortalContext     *portal_context)
 +{
-+    FcChar8 *font_file = NULL;
-+    FT_Face ft_face;
-+    guint size = ibus_fontset_get_size (fontset);
-+
-+    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
-+    g_return_val_if_fail (fcfont != NULL, NULL);
-+
-+    size = ibus_fontset_get_size (fontset);
-+    FcPatternGetString (fcfont, FC_FILE, 0, &font_file);
-+    FT_New_Face (m_ftlibrary, (const gchar *) font_file, 0, &ft_face);
-+    FT_Set_Pixel_Sizes (ft_face, size, size);
-+    return ft_face;
++    g_object_unref (portal_context);
 +}
 +
-+void
-+_cairo_show_unknown_glyphs (cairo_t             *cr,
-+                            const cairo_glyph_t *glyphs,
-+                            guint                num_glyphs,
-+                            guint                width,
-+                            guint                height)
++static IBusPortalContext *
++ibus_portal_context_new (IBusInputContext *context,
++                         GDBusConnection  *connection,
++                         const char       *owner,
++                         GError          **error)
 +{
-+    gunichar ch;
-+    gboolean invalid_input;
-+    int rows = 2;
-+    int cols;
-+    int row, col;
-+    char buf[7];
-+    double cx = 0.;
-+    double cy;
-+    const double box_descent = 3.;
-+    double x0;
-+    double y0;
-+    const double digit_width = 5.;
-+    const double digit_height= 6.;
-+    char hexbox_string[2] = {0, 0};
-+
-+    g_assert (glyphs != NULL);
-+    g_assert (num_glyphs > 0);
-+
-+    ch = glyphs[0].index & ~PANGO_GLYPH_UNKNOWN_FLAG;
-+    invalid_input = G_UNLIKELY (glyphs[0].index == PANGO_GLYPH_INVALID_INPUT ||
-+                                ch > 0x10FFFF);
-+    if (G_UNLIKELY (invalid_input)) {
-+      g_warning ("Unsupported U+%06X",  ch);
-+      return;
++    IBusPortalContext *portal_context =
++            g_object_new (ibus_portal_context_get_type (), NULL);
++
++    g_signal_connect (context,
++                      "g-signal",
++                      G_CALLBACK(portal_context_g_signal),
++                      portal_context);
++
++    portal_context->id = ++next_context_id;
++    portal_context->context = g_object_ref (context);
++    portal_context->owner = g_strdup (owner);
++    portal_context->object_path =
++            g_strdup_printf (IBUS_PATH_INPUT_CONTEXT, portal_context->id);
++    portal_context->service = ibus_dbus_service_skeleton_new ();
++
++    g_signal_connect (portal_context->service,
++                      "handle-destroy",
++                      G_CALLBACK (ibus_portal_context_handle_destroy),
++                      portal_context);
++
++    if (!g_dbus_interface_skeleton_export (
++                G_DBUS_INTERFACE_SKELETON (portal_context->service),
++                connection, portal_context->object_path,
++                error) ||
++        !g_dbus_interface_skeleton_export (
++                G_DBUS_INTERFACE_SKELETON (portal_context),
++                connection, portal_context->object_path,
++                error)) {
++        g_object_unref (portal_context);
++        return NULL;
 +    }
 +
-+    cairo_save (cr);
-+
-+    cols = (ch > 0xffff ? 6 : 4) / rows;
-+    g_snprintf (buf, sizeof(buf), (ch > 0xffff) ? "%06X" : "%04X", ch);
-+    cy = (double) height;
-+    x0 = cx + box_descent + XPAD / 2;
-+    y0 = cy - box_descent - YPAD / 2;
-+
-+    for (row = 0; row < rows; row++) {
-+        double y = y0 - (rows - 1 - row) * (digit_height + YPAD);
-+        for (col = 0; col < cols; col++) {
-+            double x = x0 + col * (digit_width + XPAD);
-+            cairo_move_to (cr, x, y);
-+            hexbox_string[0] = buf[row * cols + col];
-+            cairo_show_text (cr, hexbox_string);
-+        }
-+    }
-+    cairo_move_to (cr, XPAD, YPAD);
-+    cairo_line_to (cr, width - XPAD, YPAD);
-+    cairo_line_to (cr, width - XPAD,
-+                       height - YPAD);
-+    cairo_line_to (cr, XPAD, height - YPAD);
-+    cairo_line_to (cr, XPAD, YPAD);
-+    cairo_set_line_width (cr, 1.);
-+    cairo_stroke (cr);
-+
-+    cairo_restore (cr);
++    all_contexts = g_list_prepend (all_contexts, portal_context);
++
++    return portal_context;
 +}
 +
-+IBusCairoLine *
-+ibus_cairo_line_copy (IBusCairoLine *cairo_lines)
-+{
-+    IBusCairoLine *ret;
-+    guint n, i, j, num_glyphs;
-+    if (!cairo_lines)
-+        return NULL;
++GType ibus_portal_get_type (void) G_GNUC_CONST;
++static void ibus_portal_iface_init (IBusDbusPortalIface *iface);
 +
-+    for (n = 0; cairo_lines[n].scaled_font; n++);
-+    ret = g_new0 (IBusCairoLine, n + 1);
-+    for (i = 0; i < n; i++) {
-+        ret[i].scaled_font = cairo_lines[i].scaled_font;
-+        num_glyphs = cairo_lines[i].num_glyphs;
-+        ret[i].num_glyphs = num_glyphs;
-+        ret[i].glyphs = (IBusGlyph *) cairo_glyph_allocate (num_glyphs + 1);
-+        for (j = 0; j < num_glyphs; j++) {
-+            ret[i].glyphs[j] = cairo_lines[i].glyphs[j];
-+        }
-+        ret[i].glyphs[j].index = -1;
-+        ret[i].glyphs[j].x = 0;
-+        ret[i].glyphs[j].y = 0;
-+    }
-+    ret[i].scaled_font = NULL;
-+    ret[i].num_glyphs = 0;
-+    ret[i].glyphs = NULL;
-+    return ret;
-+}
++G_DEFINE_TYPE_WITH_CODE (IBusPortal, ibus_portal,
++                         IBUS_DBUS_TYPE_PORTAL_SKELETON,
++                         G_IMPLEMENT_INTERFACE (IBUS_DBUS_TYPE_PORTAL,
++                                                ibus_portal_iface_init));
 +
-+void
-+ibus_cairo_line_free (IBusCairoLine *cairo_lines)
++
++static void
++create_input_context_done (IBusBus               *bus,
++                           GAsyncResult          *res,
++                           GDBusMethodInvocation *invocation)
 +{
-+    guint i;
-+    if (!cairo_lines)
++    GError *error = NULL;
++    IBusInputContext *context;
++    IBusPortalContext *portal_context;
++    char *object_path;
++
++    context = ibus_bus_create_input_context_async_finish (ibus_bus,
++                                                          res,
++                                                          &error);
++    if (context == NULL) {
++        g_dbus_method_invocation_return_gerror (invocation, error);
++        g_error_free (error);
++        return;
++    }
++
++    portal_context = ibus_portal_context_new (
++            context,
++            g_dbus_method_invocation_get_connection (invocation),
++            g_dbus_method_invocation_get_sender (invocation),
++            &error);
++    g_object_unref (context);
++
++    if (portal_context == NULL) {
++        g_dbus_method_invocation_return_gerror (invocation, error);
++        g_error_free (error);
++        g_object_unref (portal_context);
 +        return;
-+    for (i = 0; cairo_lines[i].scaled_font; i++) {
-+        g_free (cairo_lines[i].glyphs);
 +    }
-+    g_free (cairo_lines);
++
++    ibus_dbus_portal_complete_create_input_context (
++            IBUS_DBUS_PORTAL(ibus_portal),
++            invocation, portal_context->object_path);
 +}
 +
-+IBusRequisitionEx *
-+ibus_requisition_ex_copy (IBusRequisitionEx *req)
++static gboolean
++ibus_portal_handle_create_input_context (IBusDbusPortal        *object,
++                                         GDBusMethodInvocation *invocation,
++                                         const gchar           *arg_client_name)
 +{
-+    IBusRequisitionEx *ret;
-+    if (!req)
-+        return NULL;
-+    ret = g_new0 (IBusRequisitionEx, 1);
-+    ret->width = req->width;
-+    ret->height = req->height;
-+    ret->cairo_lines = ibus_cairo_line_copy (req->cairo_lines);
-+    return ret;
++    ibus_bus_create_input_context_async (
++            ibus_bus,
++            arg_client_name, -1,
++            NULL,
++            (GAsyncReadyCallback)create_input_context_done,
++            invocation);
++    return TRUE;
 +}
 +
-+void
-+ibus_requisition_ex_free (IBusRequisitionEx *req)
++static void
++ibus_portal_iface_init (IBusDbusPortalIface *iface)
 +{
-+    if (!req)
-+        return;
-+    g_clear_pointer (&req->cairo_lines, ibus_cairo_line_free);
-+    g_free (req);
++    iface->handle_create_input_context =
++            ibus_portal_handle_create_input_context;
 +}
 +
-+IBusFontSet *
-+ibus_fontset_new (const gchar *first_property_name, ...)
++static void
++ibus_portal_init (IBusPortal *portal)
 +{
-+    va_list var_args;
-+    IBusFontSet *fontset;
-+
-+    g_assert (first_property_name);
-+
-+    va_start (var_args, first_property_name);
-+    fontset = (IBusFontSet *)g_object_new_valist (IBUS_TYPE_FONTSET,
-+                                                  first_property_name,
-+                                                  var_args);
-+    va_end (var_args);
-+    g_assert (fontset->priv->family);
-+    g_assert (fontset->priv->language);
-+    return fontset;
 +}
 +
-+IBusFontSet *
-+ibus_fontset_new_with_font (const gchar *family,
-+                            guint        size,
-+                            const gchar *language)
++static void
++ibus_portal_class_init (IBusPortalClass *klass)
 +{
-+    return ibus_fontset_new ("family", family,
-+                             "size", size,
-+                             "language", language,
-+                             NULL);
 +}
 +
-+void
-+ibus_fontset_exit ()
++
++static void
++show_version_and_quit (void)
 +{
-+    g_clear_pointer (&m_ftlibrary, FT_Done_FreeType);
++    g_print ("%s - Version %s\n", g_get_application_name (), VERSION);
++    exit (EXIT_SUCCESS);
 +}
 +
-+const gchar *
-+ibus_fontset_get_family (IBusFontSet *fontset)
++static const GOptionEntry entries[] =
 +{
-+    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
-+    return fontset->priv->family;
-+}
++    { "version",   'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
++      show_version_and_quit, "Show the application's version.", NULL },
++    { "verbose",   'v', 0, G_OPTION_ARG_NONE, 
++      &opt_verbose,   "verbose.", NULL },
++    { "replace",   'r', 0, G_OPTION_ARG_NONE,
++      &opt_replace,   "Replace.", NULL },
++    { NULL },
++};
 +
-+void
-+ibus_fontset_set_family (IBusFontSet *fontset,
-+                         const gchar   *family)
++static void
++on_bus_acquired (GDBusConnection *connection,
++                 const gchar     *name,
++                 gpointer         user_data)
 +{
-+    g_return_if_fail (IBUS_IS_FONTSET (fontset));
-+    g_free (fontset->priv->family);
-+    fontset->priv->family = g_strdup (family);
++    GError *error = NULL;
++
++    ibus_portal = g_object_new (ibus_portal_get_type (), NULL);
++
++    if (!g_dbus_interface_skeleton_export (
++                G_DBUS_INTERFACE_SKELETON (ibus_portal),
++                connection,
++                IBUS_PATH_IBUS,
++                &error)) {
++        g_warning ("Error exporting portal: %s", error->message);
++        g_error_free (error);
++        return;
++    }
 +}
 +
-+guint
-+ibus_fontset_get_size (IBusFontSet *fontset)
++static void
++on_name_acquired (GDBusConnection *connection,
++                  const gchar     *name,
++                  gpointer         user_data)
 +{
-+    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), 0);
-+    return fontset->priv->size;
 +}
 +
-+void
-+ibus_fontset_set_size (IBusFontSet *fontset,
-+                       guint          size)
++static void
++on_name_lost (GDBusConnection *connection,
++              const gchar     *name,
++              gpointer         user_data)
 +{
-+    g_return_if_fail (IBUS_IS_FONTSET (fontset));
-+    fontset->priv->size = size;
++    g_main_loop_quit (loop);
 +}
 +
-+const gchar *
-+ibus_fontset_get_language (IBusFontSet *fontset)
++static void
++name_owner_changed (GDBusConnection *connection,
++                    const gchar     *sender_name,
++                    const gchar     *object_path,
++                    const gchar     *interface_name,
++                    const gchar     *signal_name,
++                    GVariant        *parameters,
++                    gpointer         user_data)
 +{
-+    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
-+    return fontset->priv->language;
++  const char *name, *from, *to;
++
++  g_variant_get (parameters, "(sss)", &name, &from, &to);
++
++  if (name[0] == ':' &&
++      g_strcmp0 (name, from) == 0 &&
++      g_strcmp0 (to, "") == 0)
++    {
++        GList *l, *next;
++        /* Client disconnected, free any input contexts it may have */
++        for (l = all_contexts; l != NULL; l = next) {
++            IBusPortalContext *portal_context = l->data;
++            next = l->next;
++
++            if (g_strcmp0 (portal_context->owner, name) == 0) {
++                g_object_unref (portal_context);
++            }
++        }
++    }
 +}
 +
-+void
-+ibus_fontset_set_language (IBusFontSet *fontset,
-+                           const gchar *language)
++static void
++_bus_disconnected_cb (IBusBus            *ibusbus)
 +{
-+    g_return_if_fail (IBUS_IS_FONTSET (fontset));
-+    g_free (fontset->priv->language);
-+    fontset->priv->language = g_strdup (language);
++    g_main_loop_quit (loop);
 +}
 +
-+gboolean
-+ibus_fontset_update_fcfontset (IBusFontSet *fontset)
++gint
++main (gint argc, gchar **argv)
 +{
-+    FcPattern *pattern;
-+    const gchar *family;
-+    guint size;
-+    const gchar *language;
-+    gboolean update_fontset = FALSE;
-+    FcResult result;
-+
-+    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), FALSE);
-+
-+    pattern = FcPatternCreate ();
-+    family = fontset->priv->family;
-+    size = fontset->priv->size;
-+    language = fontset->priv->language;
-+
-+    if (g_strcmp0 (m_family, family)) {
-+        g_free (m_family);
-+        m_family = g_strdup (family);
-+        update_fontset = TRUE;
-+    }
-+    if (m_size != size) {
-+        m_size = size;
-+        update_fontset = TRUE;
-+    }
-+    if (g_strcmp0 (m_language, language)) {
-+        g_free (m_language);
-+        m_language = g_strdup (language);
-+        update_fontset = TRUE;
-+    }
-+    if (!update_fontset && m_fcfontset != NULL)
-+        return FALSE;
++    GDBusConnection *session_bus = NULL;
++    guint owner_id;
 +
-+    if (m_fcfontset)
-+        g_clear_pointer (&m_fcfontset, FcFontSetDestroy);
-+
-+    if (g_strcmp0 (family, ""))
-+        FcPatternAddString (pattern, FC_FAMILY, (const FcChar8*) family);
-+    if (size > 0)
-+        FcPatternAddDouble (pattern, FC_SIZE, (double) size);
-+    if (g_strcmp0 (language, ""))
-+        FcPatternAddString (pattern, FC_LANG, (const FcChar8*) language);
-+    FcPatternAddInteger (pattern, FC_WEIGHT, FC_WEIGHT_NORMAL);
-+    FcPatternAddInteger (pattern, FC_WIDTH, FC_WIDTH_NORMAL);
-+    FcPatternAddInteger (pattern, FC_DPI, 96);
-+    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
-+    FcConfigSubstitute (NULL, pattern, FcMatchFont);
-+    FcDefaultSubstitute (pattern);
-+    m_fcfontset = FcFontSort (NULL, pattern, FcTrue, NULL, &result);
-+    FcPatternDestroy (pattern);
-+    if (result == FcResultNoMatch || m_fcfontset->nfont == 0) {
-+        g_warning ("No FcFontSet for %s", family ? family : "(null)");
-+        return FALSE;
++    setlocale (LC_ALL, "");
++
++    GOptionContext *context = g_option_context_new ("- ibus daemon");
++    g_option_context_add_main_entries (context, entries, "ibus-daemon");
++
++    GError *error = NULL;
++    if (!g_option_context_parse (context, &argc, &argv, &error)) {
++        g_printerr ("Option parsing failed: %s\n", error->message);
++        g_error_free (error);
++        exit (-1);
 +    }
-+    return TRUE;
++
++    /* Avoid even loading gvfs to avoid accidental confusion */
++    g_setenv ("GIO_USE_VFS", "local", TRUE);
++
++    ibus_init ();
++
++    ibus_set_log_handler (opt_verbose);
++
++    ibus_bus = ibus_bus_new ();
++    if (!ibus_bus_is_connected (ibus_bus)) {
++        g_printerr ("Not connected to the ibus bus\n");
++        exit (1);
++    }
++
++    g_signal_connect (ibus_bus, "disconnected",
++                      G_CALLBACK (_bus_disconnected_cb), NULL);
++
++    loop = g_main_loop_new (NULL, FALSE);
++
++    session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
++    if (session_bus == NULL) {
++        g_printerr ("No session bus: %s", error->message);
++        exit (-1);
++    }
++
++    g_dbus_connection_signal_subscribe (session_bus,
++                                        "org.freedesktop.DBus",
++                                        "org.freedesktop.DBus",
++                                        "NameOwnerChanged",
++                                        "/org/freedesktop/DBus",
++                                        NULL,
++                                        G_DBUS_SIGNAL_FLAGS_NONE,
++                                        name_owner_changed,
++                                        NULL, NULL);
++
++    owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
++                               IBUS_SERVICE_PORTAL,
++                               G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
++                               (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE
++                                            : 0),
++                               on_bus_acquired,
++                               on_name_acquired,
++                               on_name_lost,
++                               NULL,
++                               NULL);
++
++    g_main_loop_run (loop);
++
++    g_bus_unown_name (owner_id);
++    g_main_loop_unref (loop);
++
++    return 0;
 +}
+diff --git a/src/ibusshare.h b/src/ibusshare.h
+index bca477c0..f3e2011e 100644
+--- a/src/ibusshare.h
++++ b/src/ibusshare.h
+@@ -52,6 +52,13 @@
+ #define IBUS_SERVICE_IBUS       "org.freedesktop.IBus"
+ 
+ /**
++ * IBUS_SERVICE_PORTAL:
++ *
++ * Address of IBus portalservice.
++ */
++#define IBUS_SERVICE_PORTAL     "org.freedesktop.portal.IBus"
++
++/**
+  * IBUS_SERVICE_PANEL:
+  *
+  * Address of IBus panel service.
+@@ -122,6 +129,13 @@
+ #define IBUS_INTERFACE_IBUS     "org.freedesktop.IBus"
+ 
+ /**
++ * IBUS_INTERFACE_PORTAL:
++ *
++ * D-Bus interface for IBus portal.
++ */
++#define IBUS_INTERFACE_PORTAL   "org.freedesktop.IBus.Portal"
++
++/**
+  * IBUS_INTERFACE_INPUT_CONTEXT:
+  *
+  * D-Bus interface for IBus input context.
+-- 
+2.13.4
+
+From 35ce62474fa97a5460d72c360943700a413a07ae Mon Sep 17 00:00:00 2001
+From: Alexander Larsson <alexl@redhat.com>
+Date: Thu, 31 Aug 2017 12:03:37 +0900
+Subject: [PATCH 04/14] Support the portal in the gtk im modules
+
+This adds a new way to create an IbusBus, ibus_bus_new_async_client().
+This returns an object that is not guarantee to handle any calls
+that are not needed by a client, meaning CreateInputContext and
+handling the input context.
+
+If you are running in a flatpak, or if IBUS_USE_PORTAL is set, then
+instead of talking to the regular ibus bus we connect to
+org.freedesktop.portal.IBus on the session bus and use the
+limited org.freedesktop.IBus.Portal interface instead of the
+org.freedesktop.IBus interface.
+
+This allows flatpaks (or other sandbox systems) to safely use
+dbus clients (apps).
+
+BUG=https://github.com/flatpak/flatpak/issues/675
+
+Review URL: https://codereview.appspot.com/328410043
+
+Patch from Alexander Larsson <alexl@redhat.com>.
+---
+ client/gtk2/ibusimcontext.c |   4 +-
+ src/ibusbus.c               | 248 +++++++++++++++++++++++++++++++++++++++-----
+ src/ibusbus.h               |  23 ++++
+ src/ibusinputcontext.c      |  12 ++-
+ 4 files changed, 256 insertions(+), 31 deletions(-)
+
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index 41c7a3af..b4ca8828 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -583,7 +583,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+ 
+     /* init bus object */
+     if (_bus == NULL) {
+-        _bus = ibus_bus_new_async ();
++        _bus = ibus_bus_new_async_client ();
+ 
+         /* init the global fake context */
+         if (ibus_bus_is_connected (_bus)) {
+@@ -603,7 +603,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
+     }
+ 
+     _daemon_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+-                                              IBUS_SERVICE_IBUS,
++                                              ibus_bus_get_service_name (_bus),
+                                               G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                               daemon_name_appeared,
+                                               daemon_name_vanished,
+diff --git a/src/ibusbus.c b/src/ibusbus.c
+index 75406a37..fc0c9033 100644
+--- a/src/ibusbus.c
++++ b/src/ibusbus.c
+@@ -48,12 +48,14 @@ enum {
+ enum {
+     PROP_0 = 0,
+     PROP_CONNECT_ASYNC,
++    PROP_CLIENT_ONLY,
+ };
+ 
+ /* IBusBusPriv */
+ struct _IBusBusPrivate {
+     GFileMonitor *monitor;
+     GDBusConnection *connection;
++    gboolean connected;
+     gboolean watch_dbus_signal;
+     guint watch_dbus_signal_id;
+     gboolean watch_ibus_signal;
+@@ -62,7 +64,10 @@ struct _IBusBusPrivate {
+     gchar *unique_name;
+     gboolean connect_async;
+     gchar *bus_address;
++    gboolean use_portal;
++    gboolean client_only;
+     GCancellable *cancellable;
++    guint portal_name_watch_id;
+ };
+ 
+ static guint    bus_signals[LAST_SIGNAL] = { 0 };
+@@ -74,6 +79,7 @@ static GObject  *ibus_bus_constructor           (GType                   type,
+                                                  guint                   n_params,
+                                                  GObjectConstructParam  *params);
+ static void      ibus_bus_destroy               (IBusObject             *object);
++static void      ibus_bus_connect_async         (IBusBus                *bus);
+ static void      ibus_bus_watch_dbus_signal     (IBusBus                *bus);
+ static void      ibus_bus_unwatch_dbus_signal   (IBusBus                *bus);
+ static void      ibus_bus_watch_ibus_signal     (IBusBus                *bus);
+@@ -117,8 +123,10 @@ ibus_bus_class_init (IBusBusClass *class)
+     IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
+ 
+     gobject_class->constructor = ibus_bus_constructor;
+-    gobject_class->set_property = (GObjectSetPropertyFunc) ibus_bus_set_property;
+-    gobject_class->get_property = (GObjectGetPropertyFunc) ibus_bus_get_property;
++    gobject_class->set_property =
++            (GObjectSetPropertyFunc) ibus_bus_set_property;
++    gobject_class->get_property =
++            (GObjectGetPropertyFunc) ibus_bus_get_property;
+     ibus_object_class->destroy = ibus_bus_destroy;
+ 
+     /* install properties */
+@@ -128,13 +136,28 @@ ibus_bus_class_init (IBusBusClass *class)
+      * Whether the #IBusBus object should connect asynchronously to the bus.
+      *
+      */
+-    g_object_class_install_property (gobject_class,
+-                                     PROP_CONNECT_ASYNC,
+-                                     g_param_spec_boolean ("connect-async",
+-                                                           "Connect Async",
+-                                                           "Connect asynchronously to the bus",
+-                                                           FALSE,
+-                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
++    g_object_class_install_property (
++            gobject_class,
++            PROP_CONNECT_ASYNC,
++            g_param_spec_boolean ("connect-async",
++                                  "Connect Async",
++                                  "Connect asynchronously to the bus",
++                                  FALSE,
++                                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
++    /**
++     * IBusBus:client-only:
++     *
++     * Whether the #IBusBus object is for client use only.
++     *
++     */
++    g_object_class_install_property (
++            gobject_class,
++            PROP_CLIENT_ONLY,
++            g_param_spec_boolean ("client-only",
++                                  "ClientOnly",
++                                  "Client use only",
++                                  FALSE,
++                                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ 
+     /* install signals */
+     /**
+@@ -294,6 +317,8 @@ ibus_bus_close_connection (IBusBus *bus)
+     g_cancellable_cancel (bus->priv->cancellable);
+     g_cancellable_reset (bus->priv->cancellable);
+ 
++    bus->priv->connected = FALSE;
++
+     /* unref the old connection at first */
+     if (bus->priv->connection != NULL) {
+         g_signal_handlers_disconnect_by_func (bus->priv->connection,
+@@ -311,6 +336,8 @@ static void
+ ibus_bus_connect_completed (IBusBus *bus)
+ {
+     g_assert (bus->priv->connection);
 +
-+void
-+ibus_fontset_unref (IBusFontSet *fontset)
++    bus->priv->connected = TRUE;
+     /* FIXME */
+     ibus_bus_hello (bus);
+ 
+@@ -329,9 +356,38 @@ ibus_bus_connect_completed (IBusBus *bus)
+ }
+ 
+ static void
++_bus_connect_start_portal_cb (GObject      *source_object,
++                              GAsyncResult *res,
++                              gpointer      user_data)
 +{
-+    g_object_unref (fontset);
++    IBusBus *bus = IBUS_BUS (user_data);
++    GVariant *result;
++    GError *error = NULL;
++
++    result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
++                                            res,
++                                            &error);
++    if (result != NULL) {
++        ibus_bus_connect_completed (bus);
++        g_variant_unref (result);
++    } else {
++        g_error_free (error);
++
++        g_dbus_connection_close (bus->priv->connection, NULL, NULL, NULL);
++        g_object_unref (bus->priv->connection);
++        bus->priv->connection = NULL;
++
++        g_free (bus->priv->bus_address);
++        bus->priv->bus_address = NULL;
++    }
++
++    g_object_unref (bus);
 +}
 +
-+IBusRequisitionEx *
-+ibus_fontset_get_preferred_size_hb (IBusFontSet          *fontset,
-+                                    const gchar           *text,
-+                                    cairo_rectangle_int_t *widest)
-+{
-+    gchar *copied_text;
-+    gchar *p;
-+    FontPerChar *buff;
-+    IBusCairoLine *cairo_lines = NULL;
-+    IBusRequisitionEx *req = NULL;
-+    GString *str = NULL;
-+    int text_length;
-+    int i, n = 0;
-+
-+    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
-+    g_return_val_if_fail (m_fcfontset != NULL, NULL);
-+
-+    copied_text = g_strdup (text);
-+    text_length =  g_utf8_strlen (text, -1);
-+    buff = g_slice_alloc0 (sizeof (FontPerChar) * text_length);
-+    str = g_string_new (NULL);
-+
-+    for (p = copied_text; *p != '\0'; p = g_utf8_next_char (p)) {
-+        gunichar c = g_utf8_get_char (p);
-+        gboolean has_glyphs = FALSE;
-+        buff[n].ch = c;
-+        if ((c == 0xfe0eu || c == 0xfe0fu) && n > 0) {
-+            buff[n].fcfont = buff[n-1].fcfont;
-+            ++n;
-+            continue;
-+        }
-+        for (i = 0; i < m_fcfontset->nfont; i++) {
-+            if (g_unichar_iscntrl (c) && !g_unichar_isspace (c))
-+                break;
-+            FT_Face ft_face = ibus_fontset_get_ftface_from_fcfont (
-+                    fontset,
-+                    m_fcfontset->fonts[i]);
-+            if (FT_Get_Char_Index (ft_face, c) != 0) {
-+                buff[n].fcfont = m_fcfontset->fonts[i];
-+                if (n > 0 && buff[n - 1].fcfont != buff[n].fcfont) {
-+                    get_string_extents_with_font (str->str,
-+                                                  &buff[n - 1],
-+                                                  widest,
-+                                                  &cairo_lines);
-+                    g_string_free (str, TRUE);
-+                    str = g_string_new (NULL);
-+                    g_string_append_unichar (str, c);
-+                } else {
-+                    g_string_append_unichar (str, c);
-+                }
-+                ++n;
-+                has_glyphs = TRUE;
-+                FT_Done_Face (ft_face);
-+                break;
-+            }
-+            FT_Done_Face (ft_face);
-+        }
-+        if (!has_glyphs) {
-+            if (n > 0) {
-+                buff[n].fcfont = buff[n - 1].fcfont;
-+            } else {
-+                /* Search a font for non-glyph char to draw the code points
-+                 * likes Pango.
-+                 */
-+                for (i = 0; i < m_fcfontset->nfont; i++) {
-+                    FT_Face ft_face = ibus_fontset_get_ftface_from_fcfont (
-+                            fontset,
-+                            m_fcfontset->fonts[i]);
-+                    /* Check alphabets instead of space or digits
-+                     * because 'Noto Emoji Color' font's digits are
-+                     * white color and cannot change the font color.
-+                     * the font does not have alphabets.
-+                     */
-+                    if (FT_Get_Char_Index (ft_face, 'A') != 0) {
-+                        buff[n].fcfont = m_fcfontset->fonts[i];
-+                        FT_Done_Face (ft_face);
-+                        has_glyphs = TRUE;
-+                        break;
-+                    }
-+                    FT_Done_Face (ft_face);
-+                }
-+                if (!has_glyphs) {
-+                    buff[n].fcfont = m_fcfontset->fonts[0];
-+                    g_warning ("Not found fonts for unicode %04X at %d in %s",
-+                               c, n, text);
-+                }
-+            }
-+            n++;
-+            g_string_append_unichar (str, c);
++static void
+ _bus_connect_async_cb (GObject      *source_object,
+-                        GAsyncResult *res,
+-                        gpointer      user_data)
++                       GAsyncResult *res,
++                       gpointer      user_data)
+ {
+     g_return_if_fail (user_data != NULL);
+     g_return_if_fail (IBUS_IS_BUS (user_data));
+@@ -349,7 +405,26 @@ _bus_connect_async_cb (GObject      *source_object,
+     }
+ 
+     if (bus->priv->connection != NULL) {
+-        ibus_bus_connect_completed (bus);
++        if (bus->priv->use_portal) {
++            g_object_set_data (G_OBJECT (bus->priv->connection),
++                               "ibus-portal-connection",
++                               GINT_TO_POINTER (TRUE));
++            g_dbus_connection_call (
++                    bus->priv->connection,
++                    IBUS_SERVICE_PORTAL,
++                    IBUS_PATH_IBUS,
++                    "org.freedesktop.DBus.Peer",
++                    "Ping",
++                    g_variant_new ("()"),
++                    G_VARIANT_TYPE ("()"),
++                    G_DBUS_CALL_FLAGS_NONE,
++                    -1,
++                    bus->priv->cancellable,
++                    (GAsyncReadyCallback) _bus_connect_start_portal_cb,
++                    g_object_ref (bus));
++        } else {
++            ibus_bus_connect_completed (bus);
 +        }
+     }
+     else {
+         g_free (bus->priv->bus_address);
+@@ -360,21 +435,32 @@ _bus_connect_async_cb (GObject      *source_object,
+     g_object_unref (bus);
+ }
+ 
++static gchar *
++ibus_bus_get_bus_address (IBusBus *bus)
++{
++    if (_bus->priv->use_portal)
++        return g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, NULL);
++    else
++        return g_strdup (ibus_get_address ());
++}
++
+ static void
+ ibus_bus_connect_async (IBusBus *bus)
+ {
+-    const gchar *bus_address = ibus_get_address ();
++    gchar *bus_address = ibus_bus_get_bus_address (bus);
+ 
+     if (bus_address == NULL)
+         return;
+ 
+-    if (g_strcmp0 (bus->priv->bus_address, bus_address) == 0)
++    if (g_strcmp0 (bus->priv->bus_address, bus_address) == 0) {
++        g_free (bus_address);
+         return;
 +    }
-+    if (str->str) {
-+        get_string_extents_with_font (str->str,
-+                                      &buff[n - 1],
-+                                      widest,
-+                                      &cairo_lines);
-+        g_string_free (str, TRUE);
-+    }
-+    g_slice_free1 (sizeof (FontPerChar) * text_length, buff);
-+    g_free (copied_text);
-+    widest->width += XPAD * 2;
-+    widest->height += YPAD * 2;
-+    req = g_new0 (IBusRequisitionEx, 1);
-+    req->width = widest->width;
-+    req->height = widest->height;
-+    req->cairo_lines = cairo_lines;
-+    return req;
+ 
+     /* Close current connection and cancel ongoing connect request. */
+     ibus_bus_close_connection (bus);
+ 
+-    bus->priv->bus_address = g_strdup (bus_address);
++    bus->priv->bus_address = bus_address;
+     g_object_ref (bus);
+     g_dbus_connection_new_for_address (
+             bus_address,
+@@ -385,6 +471,28 @@ ibus_bus_connect_async (IBusBus *bus)
+             _bus_connect_async_cb, bus);
+ }
+ 
++static gboolean
++is_in_flatpak (void)
++{
++    static gboolean flatpak_info_read;
++    static gboolean in_flatpak;
++
++    if (flatpak_info_read)
++        return in_flatpak;
++
++    flatpak_info_read = TRUE;
++    if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS))
++        in_flatpak = TRUE;
++    return in_flatpak;
 +}
 +
-+void
-+ibus_fontset_draw_cairo_with_requisition_ex (IBusFontSet       *fontset,
-+                                             cairo_t           *cr,
-+                                             IBusRequisitionEx *ex)
++static gboolean
++ibus_bus_should_connect_portal (IBusBus *bus)
 +{
-+    IBusCairoLine *cairo_lines;
-+    int i;
++    return bus->priv->client_only &&
++        (is_in_flatpak () || g_getenv ("IBUS_USE_PORTAL") != NULL);
++}
++
+ static void
+ ibus_bus_connect (IBusBus *bus)
+ {
+@@ -431,7 +539,6 @@ ibus_bus_init (IBusBus *bus)
+ {
+     struct stat buf;
+     gchar *path;
+-    GFile *file;
+ 
+     bus->priv = IBUS_BUS_GET_PRIVATE (bus);
+ 
+@@ -443,6 +550,7 @@ ibus_bus_init (IBusBus *bus)
+     bus->priv->watch_ibus_signal_id = 0;
+     bus->priv->unique_name = NULL;
+     bus->priv->connect_async = FALSE;
++    bus->priv->client_only = FALSE;
+     bus->priv->bus_address = NULL;
+     bus->priv->cancellable = g_cancellable_new ();
+ 
+@@ -453,17 +561,12 @@ ibus_bus_init (IBusBus *bus)
+ 
+     if (stat (path, &buf) == 0) {
+         if (buf.st_uid != getuid ()) {
+-            g_warning ("The owner of %s is not %s!", path, ibus_get_user_name ());
++            g_warning ("The owner of %s is not %s!",
++                       path, ibus_get_user_name ());
+             return;
+         }
+     }
+ 
+-    file = g_file_new_for_path (ibus_get_socket_path ());
+-    bus->priv->monitor = g_file_monitor_file (file, 0, NULL, NULL);
+-
+-    g_signal_connect (bus->priv->monitor, "changed", (GCallback) _changed_cb, bus);
+-
+-    g_object_unref (file);
+     g_free (path);
+ }
+ 
+@@ -477,6 +580,9 @@ ibus_bus_set_property (IBusBus      *bus,
+     case PROP_CONNECT_ASYNC:
+         bus->priv->connect_async = g_value_get_boolean (value);
+         break;
++    case PROP_CLIENT_ONLY:
++        bus->priv->client_only = g_value_get_boolean (value);
++        break;
+     default:
+         G_OBJECT_WARN_INVALID_PROPERTY_ID (bus, prop_id, pspec);
+     }
+@@ -492,25 +598,73 @@ ibus_bus_get_property (IBusBus    *bus,
+     case PROP_CONNECT_ASYNC:
+         g_value_set_boolean (value, bus->priv->connect_async);
+         break;
++    case PROP_CLIENT_ONLY:
++        g_value_set_boolean (value, bus->priv->client_only);
++        break;
+     default:
+         G_OBJECT_WARN_INVALID_PROPERTY_ID (bus, prop_id, pspec);
+     }
+ }
+ 
++static void
++portal_name_appeared (GDBusConnection *connection,
++                      const gchar     *name,
++                      const gchar     *owner,
++                      gpointer         user_data)
++{
++    IBusBus *bus = IBUS_BUS (user_data);
++
++    if (bus->priv->connection == NULL)
++        ibus_bus_connect_async (bus);
++}
 +
-+    g_return_if_fail (IBUS_IS_FONTSET (fontset));
-+    g_return_if_fail (cr != NULL);
-+    g_return_if_fail (ex != NULL);
++static void
++portal_name_vanished (GDBusConnection *connection,
++                      const gchar     *name,
++                      gpointer         user_data)
++{
++    IBusBus *bus = IBUS_BUS (user_data);
 +
-+    cairo_lines = ex->cairo_lines;
-+    g_return_if_fail (cairo_lines != NULL);
++    if (bus->priv->connection)
++        g_dbus_connection_close (bus->priv->connection, NULL, NULL, NULL);
++}
 +
-+    for (i = 0; cairo_lines[i].scaled_font; i++) {
-+        const cairo_glyph_t *glyphs = (cairo_glyph_t *) cairo_lines[i].glyphs;
-+        guint num_glyphs = cairo_lines[i].num_glyphs;
 +
-+        cairo_ft_scaled_font_lock_face (cairo_lines[i].scaled_font);
-+        cairo_set_scaled_font (cr, cairo_lines[i].scaled_font);
-+        if (num_glyphs > 0 && glyphs[0].index & PANGO_GLYPH_UNKNOWN_FLAG) {
-+            _cairo_show_unknown_glyphs (cr, glyphs, num_glyphs,
-+                                        ex->width, ex->height);
+ static GObject*
+ ibus_bus_constructor (GType                  type,
+                       guint                  n_params,
+                       GObjectConstructParam *params)
+ {
+     GObject *object;
++    GFile *file;
+ 
+     /* share one IBusBus instance in whole application */
+     if (_bus == NULL) {
+-        object = G_OBJECT_CLASS (ibus_bus_parent_class)->constructor (type, n_params, params);
++        object = G_OBJECT_CLASS (ibus_bus_parent_class)->constructor (
++                type, n_params, params);
+         /* make bus object sink */
+         g_object_ref_sink (object);
+         _bus = IBUS_BUS (object);
+ 
++        _bus->priv->use_portal = ibus_bus_should_connect_portal (_bus);
++
++        if (!_bus->priv->use_portal) {
++            file = g_file_new_for_path (ibus_get_socket_path ());
++            _bus->priv->monitor = g_file_monitor_file (file, 0, NULL, NULL);
++            g_signal_connect (_bus->priv->monitor, "changed",
++                              (GCallback) _changed_cb, _bus);
++            g_object_unref (file);
 +        } else {
-+            cairo_show_glyphs (cr, glyphs, num_glyphs);
++            _bus->priv->portal_name_watch_id =
++                g_bus_watch_name (G_BUS_TYPE_SESSION,
++                                  IBUS_SERVICE_PORTAL,
++                                  G_BUS_NAME_WATCHER_FLAGS_NONE,
++                                  portal_name_appeared,
++                                  portal_name_vanished,
++                                  _bus, NULL);
 +        }
-+        cairo_ft_scaled_font_unlock_face (cairo_lines[i].scaled_font);
++
++
+         if (_bus->priv->connect_async)
+             ibus_bus_connect_async (_bus);
+         else
+@@ -561,6 +715,11 @@ ibus_bus_destroy (IBusObject *object)
+     g_object_unref (bus->priv->cancellable);
+     bus->priv->cancellable = NULL;
+ 
++    if (bus->priv->portal_name_watch_id) {
++        g_bus_unwatch_name (bus->priv->portal_name_watch_id);
++        bus->priv->portal_name_watch_id = 0;
 +    }
++
+     IBUS_OBJECT_CLASS (ibus_bus_parent_class)->destroy (object);
+ }
+ 
+@@ -656,6 +815,7 @@ ibus_bus_new (void)
+ {
+     IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS,
+                                            "connect-async", FALSE,
++                                           "client-only", FALSE,
+                                            NULL));
+ 
+     return bus;
+@@ -666,6 +826,18 @@ ibus_bus_new_async (void)
+ {
+     IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS,
+                                            "connect-async", TRUE,
++                                           "client-only", FALSE,
++                                           NULL));
++
++    return bus;
 +}
-diff --git a/ui/gtk3/ibusfontset.h b/ui/gtk3/ibusfontset.h
-new file mode 100644
-index 0000000..efcaa28
---- /dev/null
-+++ b/ui/gtk3/ibusfontset.h
-@@ -0,0 +1,302 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* ibus - The Input Bus
-+ * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2017 Red Hat, Inc.
++
++IBusBus *
++ibus_bus_new_async_client (void)
++{
++    IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS,
++                                           "connect-async", TRUE,
++                                           "client-only", TRUE,
+                                            NULL));
+ 
+     return bus;
+@@ -679,7 +851,7 @@ ibus_bus_is_connected (IBusBus *bus)
+     if (bus->priv->connection == NULL || g_dbus_connection_is_closed (bus->priv->connection))
+         return FALSE;
+ 
+-    return TRUE;
++    return bus->priv->connected;
+ }
+ 
+ IBusInputContext *
+@@ -795,9 +967,9 @@ ibus_bus_create_input_context_async (IBusBus            *bus,
+      * 2. New local IBusInputContext proxy of the remote IC
+      */
+     g_dbus_connection_call (bus->priv->connection,
+-            IBUS_SERVICE_IBUS,
++            ibus_bus_get_service_name (bus),
+             IBUS_PATH_IBUS,
+-            IBUS_INTERFACE_IBUS,
++            bus->priv->use_portal ? IBUS_INTERFACE_PORTAL : IBUS_INTERFACE_IBUS,
+             "CreateInputContext",
+             g_variant_new ("(s)", client_name),
+             G_VARIANT_TYPE("(o)"),
+@@ -1454,6 +1626,14 @@ ibus_bus_get_connection (IBusBus *bus)
+     return bus->priv->connection;
+ }
+ 
++const gchar *
++ibus_bus_get_service_name (IBusBus *bus)
++{
++    if (bus->priv->use_portal)
++        return IBUS_SERVICE_PORTAL;
++    return IBUS_SERVICE_IBUS;
++}
++
+ gboolean
+ ibus_bus_exit (IBusBus *bus,
+                gboolean restart)
+@@ -2369,6 +2549,13 @@ ibus_bus_call_sync (IBusBus            *bus,
+     g_assert (member != NULL);
+     g_return_val_if_fail (ibus_bus_is_connected (bus), NULL);
+ 
++    if (bus->priv->use_portal &&
++        g_strcmp0 (bus_name, IBUS_SERVICE_IBUS) == 0)  {
++        bus_name = IBUS_SERVICE_PORTAL;
++        if (g_strcmp0 (interface, IBUS_INTERFACE_IBUS) == 0)
++            interface = IBUS_INTERFACE_PORTAL;
++    }
++
+     GError *error = NULL;
+     GVariant *result;
+     result = g_dbus_connection_call_sync (bus->priv->connection,
+@@ -2436,6 +2623,13 @@ ibus_bus_call_async (IBusBus            *bus,
+     task = g_task_new (bus, cancellable, callback, user_data);
+     g_task_set_source_tag (task, source_tag);
+ 
++    if (bus->priv->use_portal &&
++        g_strcmp0 (bus_name, IBUS_SERVICE_IBUS) == 0)  {
++        bus_name = IBUS_SERVICE_PORTAL;
++        if (g_strcmp0 (interface, IBUS_INTERFACE_IBUS) == 0)
++            interface = IBUS_INTERFACE_PORTAL;
++    }
++
+     g_dbus_connection_call (bus->priv->connection,
+                             bus_name,
+                             path,
+diff --git a/src/ibusbus.h b/src/ibusbus.h
+index 9f65d36a..dff3dfb7 100644
+--- a/src/ibusbus.h
++++ b/src/ibusbus.h
+@@ -105,6 +105,19 @@ IBusBus     *ibus_bus_new               (void);
+  */
+ IBusBus     *ibus_bus_new_async         (void);
+ 
++/**
++ * ibus_bus_new_async_client:
 + *
-+ * 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.
++ * Creates a new #IBusBus instance for client use only. It will possibly
++ * be limited in what it can do.
 + *
-+ * 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.
++ * The instance will asynchronously connect to the IBus daemon.
 + *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
++ * Returns: A newly allocated #IBusBus instance, and the instance is not
++ * floating.
 + */
++IBusBus     *ibus_bus_new_async_client  (void);
 +
-+#ifndef __IBUS_HARFBUZZ_H_
-+#define __IBUS_HARFBUZZ_H_
-+
-+/**
-+ * SECTION: ibusfontset
-+ * @short_description: Object for HarfBuzz and Fontconfig.
-+ * @title: IBusFontSet
-+ * @stability: Unstable
+ 
+ /**
+  * ibus_bus_is_connected:
+@@ -128,6 +141,16 @@ GDBusConnection *
+              ibus_bus_get_connection    (IBusBus        *bus);
+ 
+ /**
++ * ibus_bus_get_service_name:
++ * @bus: An #IBusBus.
 + *
-+ * IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering
-+ * on Cairo context.
-+ * Current Pango changes fonts by emoji variants and draws the separated
-+ * glyphs [1] but actually the emoji characters with variants can be drawn
-+ * as one glyph so this class  manages Fontconfig fontsets to select a font,
-+ * HarfBuzz to get glyphs for emoji variants, Cairo to draw glyphs.
++ * Return the main service name to use for calls on the ibus connection.
 + *
-+ * [1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669
-+ *      https://bugzilla.gnome.org/show_bug.cgi?id=781123
++ * Returns: at dbus name.
 + */
++const gchar * ibus_bus_get_service_name (IBusBus        *bus);
 +
-+#include <ibus.h>
-+#include <cairo.h>
-+
-+#define IBUS_TYPE_CAIRO_LINE (ibus_cairo_line_get_type ())
-+#define IBUS_TYPE_REQUISITION_EX (ibus_requisition_ex_get_type ())
-+#define IBUS_TYPE_FONTSET (ibus_fontset_get_type ())
-+#define IBUS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_CAST (\
-+        (obj), \
-+        IBUS_TYPE_FONTSET, \
-+        IBusFontSet))
-+#define IBUS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (\
-+        (klass), \
-+        IBUS_TYPE_FONTSET, \
-+        IBusFontSetClass))
-+#define IBUS_IS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (\
-+        (obj), \
-+        IBUS_TYPE_FONTSET))
-+#define IBUS_IS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (\
-+        (klass), \
-+        IBUS_TYPE_FONTSET))
-+#define IBUS_FONTSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (\
-+        (obj), \
-+        IBUS_TYPE_FONTSET, \
-+        IBusFontSetClass))
-+
-+G_BEGIN_DECLS
-+
-+typedef struct _IBusGlyph IBusGlyph;
-+typedef struct _IBusCairoLine IBusCairoLine;
-+typedef struct _IBusRequisitionEx IBusRequisitionEx;
-+typedef struct _IBusFontSet IBusFontSet;
-+typedef struct _IBusFontSetPrivate IBusFontSetPrivate;
-+typedef struct _IBusFontSetClass IBusFontSetClass;
-+
-+struct _IBusGlyph {
-+    unsigned long index;
-+    double               x;
-+    double               y;
-+};
-+
-+struct _IBusCairoLine {
-+    IBusGlyph           *glyphs;
-+    guint                num_glyphs;
-+    cairo_scaled_font_t *scaled_font;
-+    gpointer             pdummy[5];
-+};
-+
-+struct _IBusRequisitionEx {
-+    guint                width;
-+    guint                height;
-+    IBusCairoLine       *cairo_lines;
-+    gpointer             pdummy[5];
-+};
-+
-+struct _IBusFontSet {
-+    IBusObject           parent_instance;
-+    IBusFontSetPrivate  *priv;
-+};
++/**
+  * ibus_bus_hello:
+  * @bus: An #IBusBus.
+  *
+diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c
+index 9a50acc0..ae7048ad 100644
+--- a/src/ibusinputcontext.c
++++ b/src/ibusinputcontext.c
+@@ -684,16 +684,20 @@ ibus_input_context_new (const gchar     *path,
+ {
+     g_assert (path != NULL);
+     g_assert (G_IS_DBUS_CONNECTION (connection));
++    const gchar *service_name = IBUS_SERVICE_IBUS;
+ 
+     GInitable *initable;
+ 
+     GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
+ 
++    if (g_object_get_data (G_OBJECT (connection), "ibus-portal-connection"))
++        service_name = IBUS_SERVICE_PORTAL;
++
+     initable = g_initable_new (IBUS_TYPE_INPUT_CONTEXT,
+                                cancellable,
+                                error,
+                                "g-connection",      connection,
+-                               "g-name",            IBUS_SERVICE_IBUS,
++                               "g-name",            service_name,
+                                "g-flags",           flags,
+                                "g-interface-name",  IBUS_INTERFACE_INPUT_CONTEXT,
+                                "g-object-path",     path,
+@@ -714,16 +718,20 @@ ibus_input_context_new_async (const gchar         *path,
+     g_assert (path != NULL);
+     g_assert (G_IS_DBUS_CONNECTION (connection));
+     g_assert (callback != NULL);
++    const gchar *service_name = IBUS_SERVICE_IBUS;
+ 
+     GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
+ 
++    if (g_object_get_data (G_OBJECT (connection), "ibus-portal-connection"))
++        service_name = IBUS_SERVICE_PORTAL;
++
+     g_async_initable_new_async (IBUS_TYPE_INPUT_CONTEXT,
+                                 G_PRIORITY_DEFAULT,
+                                 cancellable,
+                                 callback,
+                                 user_data,
+                                 "g-connection",      connection,
+-                                "g-name",            IBUS_SERVICE_IBUS,
++                                "g-name",            service_name,
+                                 "g-flags",           flags,
+                                 "g-interface-name",  IBUS_INTERFACE_INPUT_CONTEXT,
+                                 "g-object-path",     path,
+-- 
+2.13.4
+
+From 9772e800f3e6937510f2609c5ce9a6860c59cb81 Mon Sep 17 00:00:00 2001
+From: Alexander Larsson <alexl@redhat.com>
+Date: Mon, 4 Sep 2017 12:02:17 +0900
+Subject: [PATCH 05/14] test: Testing in flatpak
+
+Test with:
+flatpak-builder --force-clean app org.test.IBus.json
+flatpak-builder --run --nofilesystem=host app org.test.IBus.json zenity --entry
+
+BUG=https://github.com/flatpak/flatpak/issues/675
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/329090043
+
+Patch from Alexander Larsson <alexl@redhat.com>.
+---
+ test/org.test.IBus.json | 43 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 43 insertions(+)
+ create mode 100644 test/org.test.IBus.json
+
+diff --git a/test/org.test.IBus.json b/test/org.test.IBus.json
+new file mode 100644
+index 00000000..dc3caa62
+--- /dev/null
++++ b/test/org.test.IBus.json
+@@ -0,0 +1,43 @@
++{
++    "app-id": "org.test.IBus",
++    "runtime": "org.gnome.Platform",
++    "runtime-version": "3.24",
++    "sdk": "org.gnome.Sdk",
++    "command": "/usr/bin/zenity",
++    "finish-args": [
++        /* X11 + XShm access */
++        "--share=ipc", "--socket=x11",
++        /* Wayland access */
++        "--socket=wayland",
++        /* Needed for dconf to work */
++        "--filesystem=xdg-run/dconf", "--filesystem=~/.config/dconf:ro",
++        "--talk-name=ca.desrt.dconf", "--env=DCONF_USER_CONFIG_DIR=.config/dconf",
++        "--env=GTK_IM_MODULE_FILE=/app/lib/gtk-3.0/3.0.0/immodules.cache"
++    ],
++    "build-options" : {
++        "cflags": "-O2 -g",
++        "cxxflags": "-O2 -g",
++        "env": {
++            "V": "1"
++        }
++    },
++    "cleanup": ["/include", "/lib/pkgconfig",
++                "/share/pkgconfig", "/share/aclocal",
++                "/man", "/share/man", "/share/gtk-doc",
++                "/share/vala",
++                "*.la", "*.a"],
++    "modules": [
++        {
++            "name": "ibus",
++            "sources": [
++                {
++                    "type": "git",
++                    "url": "https://github.com/alexlarsson/ibus",
++                    "branch": "ibus-portal"
++                }
++            ],
++            "config-opts": ["--disable-emoji-dict", "--disable-dconf"],
++            "post-install": ["gtk-query-immodules-3.0 /app/lib/gtk-3.0/3.0.0/immodules/im-ibus.so > /app/lib/gtk-3.0/3.0.0/immodules.cache"]
++        }
++    ]
++}
+-- 
+2.13.4
+
+From 9937a0e4501ccf0cfd238ce7c97733c3099db3f7 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 4 Sep 2017 12:19:07 +0900
+Subject: [PATCH 06/14] bus: ibus-daemon activates ibus-portal
+
+When ibus-daemon restarts, ibus-portal exits with on_name_lost() and
+the clients wait for portal_name_appeared() until ibus-poral restarts.
+Now the clients can connect to ibus-daemon with this way and also
+they don't have to activate ibus-portal.
+
+BUG=https://github.com/flatpak/flatpak/issues/675
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/321530043
+---
+ bus/server.c    | 45 +++++++++++++++++++++++++++++++++++++++++++--
+ portal/portal.c |  6 +-----
+ 2 files changed, 44 insertions(+), 7 deletions(-)
+
+diff --git a/bus/server.c b/bus/server.c
+index ff3ea093..e2898274 100644
+--- a/bus/server.c
++++ b/bus/server.c
+@@ -93,6 +93,45 @@ bus_new_connection_cb (GDBusServer     *server,
+     return TRUE;
+ }
+ 
++static void
++_server_connect_start_portal_cb (GObject      *source_object,
++                                 GAsyncResult *res,
++                                 gpointer      user_data)
++{
++    GVariant *result;
++    GError *error = NULL;
 +
-+struct _IBusFontSetClass {
-+    IBusObjectClass parent_class;
-+    /* signals */
-+    /*< private >*/
-+    /* padding */
-+    gpointer        pdummy[10];
-+};
++    result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
++                                            res,
++                                            &error);
++    if (result != NULL) {
++        g_variant_unref (result);
++    } else {
++        g_print ("portal is not running: %s\n", error->message);
++        g_error_free (error);
++    }
++}
 +
-+GType           ibus_cairo_line_get_type        (void) G_GNUC_CONST;
++static void
++bus_acquired_handler (GDBusConnection *connection,
++                      const gchar     *name,
++                      gpointer         user_data)
++{
++    g_dbus_connection_call (connection,
++                            IBUS_SERVICE_PORTAL,
++                            IBUS_PATH_IBUS,
++                            "org.freedesktop.DBus.Peer",
++                            "Ping",
++                            g_variant_new ("()"),
++                            G_VARIANT_TYPE ("()"),
++                            G_DBUS_CALL_FLAGS_NONE,
++                            -1,
++                            NULL /* cancellable */,
++                            (GAsyncReadyCallback)
++                                    _server_connect_start_portal_cb,
++                            NULL);
++}
 +
-+/**
-+ * ibus_cairo_line_copy:
-+ * @cairo_lines: #IBusCairoLine
-+ *
-+ * Creates a copy of @cairo_liens, which should be freed with
-+ * ibus_cairo_line_free(). Primarily used by language bindings,
-+ * not that useful otherwise (since @req can just be copied
-+ * by assignment in C).
-+ *
-+ * Returns: the newly allocated #IBusCairoLine, which should
-+ *          be freed with ibus_cairo_line_free(), or %NULL
-+ *          if @cairo_lines was %NULL.
-+ **/
-+IBusCairoLine * ibus_cairo_line_copy            (IBusCairoLine *cairo_lines);
+ void
+ bus_server_init (void)
+ {
+@@ -134,8 +173,10 @@ bus_server_init (void)
+     ibus_write_address (address);
+ 
+     /* own a session bus name so that third parties can easily track our life-cycle */
+-    g_bus_own_name (G_BUS_TYPE_SESSION, IBUS_SERVICE_IBUS, G_BUS_NAME_OWNER_FLAGS_NONE,
+-                    NULL, NULL, NULL, NULL, NULL);
++    g_bus_own_name (G_BUS_TYPE_SESSION, IBUS_SERVICE_IBUS,
++                    G_BUS_NAME_OWNER_FLAGS_NONE,
++                    bus_acquired_handler,
++                    NULL, NULL, NULL, NULL);
+ }
+ 
+ const gchar *
+diff --git a/portal/portal.c b/portal/portal.c
+index 0415f996..cb24d257 100644
+--- a/portal/portal.c
++++ b/portal/portal.c
+@@ -101,10 +101,6 @@ _forward_method_cb (GObject *source_object,
+                     gpointer user_data)
+ {
+     GDBusMethodInvocation *invocation = user_data;
+-    IBusPortalContext *portal_context =
+-            (IBusPortalContext *) g_dbus_method_invocation_get_user_data (
+-                    invocation);
+-    IBusEngineDesc *desc;
+     GError *error = NULL;
+ 
+     GVariant *variant = g_dbus_proxy_call_finish ((GDBusProxy *) source_object,
+@@ -413,6 +409,7 @@ ibus_portal_context_handle_destroy (IBusDbusService       *object,
+                                     IBusPortalContext     *portal_context)
+ {
+     g_object_unref (portal_context);
++    return FALSE;
+ }
+ 
+ static IBusPortalContext *
+@@ -475,7 +472,6 @@ create_input_context_done (IBusBus               *bus,
+     GError *error = NULL;
+     IBusInputContext *context;
+     IBusPortalContext *portal_context;
+-    char *object_path;
+ 
+     context = ibus_bus_create_input_context_async_finish (ibus_bus,
+                                                           res,
+-- 
+2.13.4
+
+From 3e01bab972ad12b92c55a9dde554a0359c217290 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 6 Sep 2017 12:04:52 +0900
+Subject: [PATCH 07/14] portal: Add org.freedesktop.IBus.Portal.xml in
+ EXTRA_DIST
+
+Review URL: https://codereview.appspot.com/325370043
+---
+ portal/Makefile.am | 17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+diff --git a/portal/Makefile.am b/portal/Makefile.am
+index 954fc591..d1e2051a 100644
+--- a/portal/Makefile.am
++++ b/portal/Makefile.am
+@@ -65,12 +65,6 @@ ibus_portal_LDADD = \
+ 	$(AM_LDADD) \
+ 	$(NULL)
+ 
+-EXTRA_DIST = \
+-	$(NULL)
+-
+-CLEANFILES = \
+-	$(NULL)
+-
+ $(libibus):
+ 	$(MAKE) -C $(top_builddir)/src
+ 
+@@ -89,7 +83,14 @@ $(ibus_dbus_built_sources) : org.freedesktop.IBus.Portal.xml
+ 		$^ \
+ 		$(NULL)
+ 
+-EXTRA_DIST += $(dbusservice_in_files)
+-CLEANFILES += $(dbusservice_DATA)
++EXTRA_DIST = \
++    $(dbusservice_in_files) \
++    org.freedesktop.IBus.Portal.xml \
++    $(NULL)
++
++CLEANFILES = \
++    $(dbusservice_DATA) \
++    $(NULL)
++
+ 
+ -include $(top_srcdir)/git.mk
+-- 
+2.13.4
+
+From 21bac4733684ca6a74ddb02f457c0fe19eb9180d Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 6 Sep 2017 12:11:01 +0900
+Subject: [PATCH 08/14] ui/gtk3: Move ibus-emoji-dialog.vapi from ui/gtk3 to
+ bindings/vala
+
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/322590043
+---
+ .../vala}/IBusEmojiDialog-1.0.metadata             |   0
+ bindings/vala/Makefile.am                          | 161 +++++++++++++++++++--
+ .../vala}/ibus-emoji-dialog-1.0.deps               |   0
+ po/POTFILES.skip                                   |   5 +
+ ui/gtk3/Makefile.am                                | 113 ++++-----------
+ ui/gtk3/emojier.vala                               |  19 +--
+ ui/gtk3/ibusemojidialog.h                          |  26 ++++
+ 7 files changed, 213 insertions(+), 111 deletions(-)
+ rename {ui/gtk3 => bindings/vala}/IBusEmojiDialog-1.0.metadata (100%)
+ rename {ui/gtk3 => bindings/vala}/ibus-emoji-dialog-1.0.deps (100%)
+
+diff --git a/ui/gtk3/IBusEmojiDialog-1.0.metadata b/bindings/vala/IBusEmojiDialog-1.0.metadata
+similarity index 100%
+rename from ui/gtk3/IBusEmojiDialog-1.0.metadata
+rename to bindings/vala/IBusEmojiDialog-1.0.metadata
+diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
+index 4e34afc7..fc8e2f01 100644
+--- a/bindings/vala/Makefile.am
++++ b/bindings/vala/Makefile.am
+@@ -3,7 +3,8 @@
+ # ibus - The Input Bus
+ #
+ # Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2007-2016 Red Hat, Inc.
++# Copyright (c) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2007-2017 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
+@@ -22,15 +23,47 @@
+ 
+ -include $(VAPIGEN_MAKEFILE)
+ 
++libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
++
++noinst_LTLIBRARIES =
++noinst_DATA =
++INTROSPECTION_GIRS =
++girdir = $(datadir)/gir-1.0
++
++AM_CPPFLAGS = \
++    -I$(top_srcdir)/src \
++    -I$(top_builddir)/src \
++    -include $(CONFIG_HEADER) \
++    $(NULL)
++AM_CFLAGS = \
++    -DG_LOG_DOMAIN=\"IBUS\" \
++    -DPKGDATADIR=\"$(pkgdatadir)\" \
++    -DIBUS_DISABLE_DEPRECATED \
++    -Wno-unused-variable \
++    -Wno-unused-but-set-variable \
++    -Wno-unused-function \
++    $(NULL)
++AM_VALAFLAGS = \
++    --vapidir=$(builddir) \
++    --vapidir=$(srcdir) \
++    --pkg=posix \
++    --pkg=gtk+-3.0 \
++    --pkg=gdk-x11-3.0 \
++    --pkg=ibus-1.0 \
++    --pkg=config \
++    --pkg=xi \
++    --target-glib="$(VALA_TARGET_GLIB_VERSION)" \
++    $(NULL)
++
+ vapi_deps = \
+ 	IBus-1.0.metadata \
+-	IBus-1.0-custom.vala \
+ 	$(top_builddir)/src/IBus-1.0.gir \
+ 	$(NULL)
+ 
+ ibus-1.0.vapi: $(vapi_deps)
+ 
+-VAPIGEN_VAPIS = ibus-1.0.vapi
++ibus_vapi = ibus-1.0.vapi
++VAPIGEN_VAPIS = $(ibus_vapi)
+ 
+ ibus_1_0_vapi_DEPS = gio-2.0
+ ibus_1_0_vapi_METADATADIRS = $(srcdir)
+@@ -40,18 +73,118 @@ ibus_1_0_vapi_FILES = \
+ 	$(NULL)
+ 
+ vapidir = $(datadir)/vala/vapi
+-vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
++vapi_DATA = $(ibus_vapi) $(ibus_vapi:.vapi=.deps)
+ 
+-MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
+-DISTCLEANFILES = $(VAPIGEN_VAPIS)
++MAINTAINERCLEANFILES = $(ibus_vapi)
++DISTCLEANFILES = $(ibus_vapi)
+ 
+-EXTRA_DIST = \
+-	$(VAPIGEN_VAPIS) \
+-	IBus-1.0.metadata \
+-	IBus-1.0-custom.vala \
+-	ibus-1.0.deps \
+-	config.vapi \
+-	xi.vapi \
+-	$(NULL)
++EXTRA_DIST =                                    \
++    $(ibus_vapi)                                \
++    IBus-1.0.metadata                           \
++    IBus-1.0-custom.vala                        \
++    IBusEmojiDialog-1.0.metadata                \
++    ibus-1.0.deps                               \
++    ibus-emoji-dialog-1.0.deps                  \
++    config.vapi                                 \
++    xi.vapi                                     \
++    $(NULL)
++
++if ENABLE_EMOJI_DICT
++AM_VALAFLAGS += --define=EMOJI_DICT
++
++libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
++noinst_LTLIBRARIES += $(libibus_emoji_dialog)
++
++libibus_emoji_dialog_1_0_la_SOURCES =           \
++    candidatearea.vala                          \
++    emojier.vala                                \
++    iconwidget.vala                             \
++    pango.vala                                  \
++    separator.vala                              \
++    $(NULL)
++libibus_emoji_dialog_1_0_la_CFLAGS =            \
++    $(AM_CFLAGS)                                \
++    @GLIB2_CFLAGS@                              \
++    @GIO2_CFLAGS@                               \
++    @GTHREAD2_CFLAGS@                           \
++    @GTK3_CFLAGS@                               \
++    @X11_CFLAGS@                                \
++    -DBINDIR=\"$(bindir)\"                      \
++    $(NULL)
++libibus_emoji_dialog_1_0_la_LIBADD =            \
++    @GLIB2_LIBS@                                \
++    @GIO2_LIBS@                                 \
++    @GTHREAD2_LIBS@                             \
++    @GTK3_LIBS@                                 \
++    @X11_LIBS@                                  \
++    -lXi                                        \
++    $(libibus)                                  \
++    $(NULL)
++libibus_emoji_dialog_1_0_la_LDFLAGS =           \
++    -no-undefined                               \
++    -export-symbols-regex "ibus_.*"             \
++    $(NULL)
++
++# per file setting is needed to avoid conflicting LN_S by calling
++# duplicated times in parallel make
++%.vala: $(ibus_vapi)
++	if test ! -f $@ ; then                                              \
++	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
++	fi;
++
++MAINTAINERCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES)
++DISTCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES)
++
++if HAVE_INTROSPECTION
++-include $(INTROSPECTION_MAKEFILE)
++INTROSPECTION_SCANNER_ARGS =
++INTROSPECTION_COMPILER_ARGS =                   \
++    --includedir=$(srcdir)                      \
++    --includedir=.                              \
++    --includedir=$(top_srcdir)/src              \
++    $(NULL)
++
++
++emoji_headers =                                 \
++    $(top_srcdir)/ui/gtk3/ibusemojidialog.h     \
++    $(NULL)
++
++IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
++IBusEmojiDialog_1_0_gir_SCANNERFLAGS =          \
++    --pkg-export=ibus-1.0                       \
++    --pkg=gtk+-3.0                              \
++    $(IBUS_GIR_SCANNERFLAGS)                    \
++    $(NULL)
++IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0
++IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus)
++IBusEmojiDialog_1_0_gir_FILES = $(emoji_headers)
++IBusEmojiDialog_1_0_gir_CFLAGS =                \
++    -I$(srcdir)                                 \
++    -I$(builddir)                               \
++    -I$(top_srcdir)/src                         \
++    $(NULL)
++
++ibus_emoji_dialog_gir = IBusEmojiDialog-1.0.gir
++INTROSPECTION_GIRS += $(ibus_emoji_dialog_gir)
++noinst_DATA += $(ibus_emoji_dialog_gir)
++EXTRA_DIST += $(ibus_emoji_dialog_gir)
++MAINTAINERCLEANFILES += $(ibus_emoji_dialog_gir)
++DISTCLEANFILES += $(ibus_emoji_dialog_gir)
++
++ibus-emoji-dialog-1.0.vapi: $(ibus_emoji_dialog_gir) IBusEmojiDialog-1.0.metadata
++ibus_emoji_dialog_vapi = ibus-emoji-dialog-1.0.vapi
++ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0
++ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir)
++ibus_emoji_dialog_1_0_vapi_FILES = IBusEmojiDialog-1.0.gir
++VAPIGEN_VAPIS += $(ibus_emoji_dialog_vapi)
++noinst_DATA += $(ibus_emoji_dialog_vapi)
++EXTRA_DIST += $(ibus_emoji_dialog_vapi)
++MAINTAINERCLEANFILES += $(ibus_emoji_dialog_vapi)
++DISTCLEANFILES += $(ibus_emoji_dialog_vapi)
++
++endif
++#end of HAVE_INTROSPECTION
++endif
++# end of ENABLE_EMOJI_DICT
+ 
+ -include $(top_srcdir)/git.mk
+diff --git a/ui/gtk3/ibus-emoji-dialog-1.0.deps b/bindings/vala/ibus-emoji-dialog-1.0.deps
+similarity index 100%
+rename from ui/gtk3/ibus-emoji-dialog-1.0.deps
+rename to bindings/vala/ibus-emoji-dialog-1.0.deps
+diff --git a/po/POTFILES.skip b/po/POTFILES.skip
+index 7190221d..10b88298 100644
+--- a/po/POTFILES.skip
++++ b/po/POTFILES.skip
+@@ -2,6 +2,11 @@
+ # Please keep this file in alphabetical order.
+ # Files under ui/gtk2/ are not shipped in the distribution, but kept
+ # in the git repository for reference.
++bindings/vala/candidatearea.c
++bindings/vala/emojier.c
++bindings/vala/iconwidget.c
++bindings/vala/pango.c
++bindings/vala/separator.c
+ ibus/_config.py
+ tools/main.c
+ ui/gtk2/candidatepanel.py
+diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
+index c79641a5..786b80e6 100644
+--- a/ui/gtk3/Makefile.am
++++ b/ui/gtk3/Makefile.am
+@@ -81,10 +81,6 @@ AM_VALAFLAGS = \
+ 	--target-glib="$(VALA_TARGET_GLIB_VERSION)" \
+ 	$(NULL)
+ 
+-MAINTAINERCLEANFILES =
+-DISTCLEANFILES =
+-noinst_DATA =
+-
+ if ENABLE_LIBNOTIFY
+ AM_CFLAGS += \
+ 	@LIBNOTIFY_CFLAGS@ \
+@@ -158,9 +154,8 @@ man_seven_in_files = ibus-emoji.7.in
+ EXTRA_DIST =                            \
+     $(emoji_headers)                    \
+     $(man_seven_in_files)               \
+-    IBusEmojiDialog-1.0.metadata        \
++    emojierapp.vala                     \
+     gtkpanel.xml.in                     \
+-    ibus-emoji-dialog-1.0.deps          \
+     notification-item.xml               \
+     notification-watcher.xml            \
+     $(NULL)
+@@ -168,98 +163,40 @@ EXTRA_DIST =                            \
+ if ENABLE_EMOJI_DICT
+ AM_VALAFLAGS += --define=EMOJI_DICT
+ 
+-libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
+-
+-noinst_LTLIBRARIES = $(libibus_emoji_dialog)
+-
+-libibus_emoji_dialog_1_0_la_CFLAGS = $(AM_CFLAGS)
+-libibus_emoji_dialog_1_0_la_LDFLAGS =   \
+-    -no-undefined                       \
+-    -export-symbols-regex "ibus_.*"     \
+-    -version-info @LT_VERSION_INFO@     \
+-    $(NULL)
+-libibus_emoji_dialog_1_0_la_SOURCES =   \
+-    candidatearea.vala                  \
+-    emojier.vala                        \
+-    iconwidget.vala                     \
+-    pango.vala                          \
+-    separator.vala                      \
+-    $(NULL)
+-
+ libexec_PROGRAMS += ibus-ui-emojier
+ 
+-ibus_ui_emojier_SOURCES =                       \
+-    $(libibus_emoji_dialog_1_0_la_SOURCES)      \
++ibus_ui_emojier_VALASOURCES =                   \
+     emojierapp.vala                             \
++    candidatearea.vala                          \
++    emojier.vala                                \
++    iconwidget.vala                             \
++    pango.vala                                  \
++    separator.vala                              \
++    $(NULL)
++ibus_ui_emojier_SOURCES =                       \
++    $(ibus_ui_emojier_VALASOURCES:.vala=.c)     \
+     $(NULL)
+ 
+ ibus_ui_emojier_LDADD =                         \
+     $(AM_LDADD)                                 \
+     $(NULL)
+ 
+--include $(INTROSPECTION_MAKEFILE)
+-INTROSPECTION_SCANNER_ARGS =
+-INTROSPECTION_COMPILER_ARGS =      \
+-    --includedir=$(srcdir)         \
+-    --includedir=.                 \
+-    --includedir=$(top_srcdir)/src \
+-    $(NULL)
+-
+-if HAVE_INTROSPECTION
+-introspection_sources =                \
+-    $(emoji_headers)                   \
+-    $(NULL)
+-IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
+-IBusEmojiDialog_1_0_gir_SCANNERFLAGS = \
+-    --pkg-export=ibus-1.0              \
+-    --pkg=gtk+-3.0                     \
+-    $(IBUS_GIR_SCANNERFLAGS)           \
++ibus_ui_emojier_VALAFLAGS =                     \
++    $(AM_VALAFLAGS)                             \
+     $(NULL)
+-IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
+-IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0
+-IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus)
+-IBusEmojiDialog_1_0_gir_FILES =                      \
+-    $(addprefix $(srcdir)/,$(introspection_sources)) \
+-    $(NULL)
+-IBusEmojiDialog_1_0_gir_CFLAGS =       \
+-    -DIBUS_COMPILATION                 \
+-    -I$(srcdir)                        \
+-    -I$(builddir)                      \
+-    -I$(top_srcdir)/src                \
+-    $(NULL)
+-INTROSPECTION_GIRS = IBusEmojiDialog-1.0.gir
+-
+-girdir = $(datadir)/gir-1.0
+-noinst_DATA += $(INTROSPECTION_GIRS)
+-CLEANFILES += $(INTROSPECTION_GIRS)
+ 
+-typelibsdir = $(libdir)/girepository-1.0
+-noinst_DATA += $(INTROSPECTION_GIRS:.gir=.typelib)
+-CLEANFILES += $(INTROSPECTION_GIRS:.gir=.typelib)
+-
+-
+-if ENABLE_VAPIGEN
+--include $(VAPIGEN_MAKEFILE)
+-
+-ibus-emoji-dialog-1.0.vapi: $(INTROSPECTION_GIRS) IBusEmojiDialog-1.0.metadata
+-
+-VAPIGEN_VAPIS = ibus-emoji-dialog-1.0.vapi
+-
+-ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0
+-ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir)
+-ibus_emoji_dialog_1_0_vapi_FILES = $(INTROSPECTION_GIRS)
+-
+-vapidir = $(datadir)/vala/vapi
+-noinst_DATA += $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
+-
+-MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
+-DISTCLEANFILES += $(VAPIGEN_VAPIS)
+-EXTRA_DIST += $(VAPIGEN_VAPIS)
+-
+-# end of ENABLE_VAPIGEN
+-endif
+-# end of HAVE_INTROSPECTION
+-endif
++# This line and foo_VALASOURCES line can delete the duplicated entries
++# of emojier.c: emojier.vala
++emojierapp.c: $(ibus_ui_emojier_VALASOURCES)
++	$(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \
++$(VALAFLAGS) -C $(ibus_ui_emojier_VALASOURCES)
++	$(NULL)
++# make dist creates .c files in a different srcdir
++emojierapp.o: $(srcdir)/emojierapp.c
++	$(AM_V_CC)source='$<' object='$@' libtool=no \
++	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
++	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
++	$(NULL)
+ 
+ man_seven_files = $(man_seven_in_files:.7.in=.7)
+ man_seven_DATA =$(man_seven_files:.7=.7.gz)
+@@ -276,7 +213,7 @@ CLEANFILES += \
+     $(man_seven_files) \
+     $(NULL)
+ 
+-# end of ENABLE_EMOJI_DICT
+ endif
++# end of ENABLE_EMOJI_DICT
+ 
+ -include $(top_srcdir)/git.mk
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 9df59ac4..36ab4bab 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -1139,6 +1139,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                     m_category_active_index = (int)list.length();
+         }
+         Gtk.Adjustment adjustment = m_list_box.get_adjustment();
++        m_scrolled_window.set_hadjustment(new Gtk.Adjustment(0, 0, 0, 0, 0, 0));
+         m_scrolled_window.set_vadjustment(adjustment);
+         show_category_list();
+     }
+@@ -1156,7 +1157,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             else if (keyval == Gdk.Key.Right)
+                 m_lookup_table.cursor_down();
+             show_candidate_panel();
+-        } else if (m_entry.get_text().len() > 0) {
++        } else if (m_entry.get_text().length > 0) {
+             int step = 0;
+             if (keyval == Gdk.Key.Left)
+                 step = -1;
+@@ -1211,7 +1212,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             show_candidate_panel();
+             return true;
+         }
+-        if (m_entry.get_text().len() > 0) {
++        if (m_entry.get_text().length > 0) {
+             int step = 0;
+             if (keyval == Gdk.Key.Home)
+                 step = -1;
+@@ -1410,7 +1411,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             key_press_enter();
+             return true;
+         case Gdk.Key.BackSpace:
+-            if (m_entry.get_text().len() > 0) {
++            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);
+@@ -1422,7 +1423,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             break;
+         case Gdk.Key.Delete:
+         case Gdk.Key.KP_Delete:
+-            if (m_entry.get_text().len() > 0) {
++            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);
+@@ -1436,7 +1437,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+         case Gdk.Key.space:
+         case Gdk.Key.KP_Space:
+             if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
+-                if (m_entry.get_text().len() > 0)
++                if (m_entry.get_text().length > 0)
+                     entry_enter_keyval(keyval);
+             } else if (m_candidate_panel_is_visible) {
+                 enter_notify_disable_with_timer();
+@@ -1512,7 +1513,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                     return true;
+                 break;
+             case Gdk.Key.u:
+-                if (m_entry.get_text().len() > 0) {
++                if (m_entry.get_text().length > 0) {
+                     GLib.Signal.emit_by_name(m_entry,
+                                              "delete-from-cursor",
+                                              Gtk.DeleteType.PARAGRAPH_ENDS,
+@@ -1521,13 +1522,13 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                 }
+                 break;
+             case Gdk.Key.a:
+-                if (m_entry.get_text().len() > 0) {
++                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().len() > 0) {
++                if (m_entry.get_text().length > 0) {
+                     GLib.Signal.emit_by_name(m_entry, "cut-clipboard");
+                     return true;
+                 }
+@@ -1544,7 +1545,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                         clipboard.store();
+                         return true;
+                     }
+-                } else if (m_entry.get_text().len() > 0) {
++                } else if (m_entry.get_text().length > 0) {
+                     GLib.Signal.emit_by_name(m_entry, "copy-clipboard");
+                     return true;
+                 }
+diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
+index 24d195c8..ed8886a8 100644
+--- a/ui/gtk3/ibusemojidialog.h
++++ b/ui/gtk3/ibusemojidialog.h
+@@ -170,5 +170,31 @@ void          ibus_emojier_set_favorites          (gchar**      favorites,
+                                                    favorite_annotations,
+                                                    int
+                                                    favorite_annotations_length);
 +
 +/**
-+ * ibus_cairo_line_free:
-+ * @cairo_lines: #IBusCairoLine
++ * ibus_emojier_set_partial_match:
++ * @has_partial_match: Enable the partial match if %TRUE. Otherwise if %FALSE.
 + *
-+ * Free an #IBusCairoLine.
++ * Set partial match for emoji annotations.
 + */
-+void            ibus_cairo_line_free            (IBusCairoLine *cairo_lines);
-+
-+
-+GType           ibus_requisition_ex_get_type    (void) G_GNUC_CONST;
++void          ibus_emojier_set_partial_match      (gboolean  has_partial_match);
 +
 +/**
-+ * ibus_requisition_ex_copy:
-+ * @req: #IBusRequisitionEx
-+ *
-+ * Creates a copy of @req, which should be freed with
-+ * ibus_requisition_ex_free(). Primarily used by language bindings,
-+ * not that useful otherwise (since @req can just be copied
-+ * by assignment in C).
++ * ibus_emojier_set_partial_match_length:
++ * @length: minimum lenght to match partially.
 + *
-+ * Returns: the newly allocated #IBusRequisitionEx, which should
-+ *          be freed with ibus_requisition_ex_free(), or %NULL
-+ *          if @req was %NULL.
-+ **/
-+IBusRequisitionEx *
-+                ibus_requisition_ex_copy        (IBusRequisitionEx *req);
++ * Set the minimum lenght to match partially.
++ */
++void          ibus_emojier_set_partial_match_length
++                                                  (gint         length);
 +
 +/**
-+ * ibus_requisition_ex_free:
-+ * @req: #IBusRequisitionEx
++ * ibus_emojier_set_partial_match_condition:
++ * @condition: condition id between 0 and 2.
 + *
-+ * Free an #IBusRequisitionEx.
++ * Set the partial match condition with the integer.
 + */
-+void            ibus_requisition_ex_free        (IBusRequisitionEx *req);
++void          ibus_emojier_set_partial_match_condition
++                                                  (gint         condition);
+ G_END_DECLS
+ #endif
+-- 
+2.13.4
+
+From d788918b635275d0247e68f26f9c840100bca366 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 6 Sep 2017 12:17:30 +0900
+Subject: [PATCH 09/14] ui/gtk3: Switcher should ignore mouse until it moves
+
+BUG=https://github.com/ibus/ibus/issues/1929
+
+Review URL: https://codereview.appspot.com/329100043
+---
+ ui/gtk3/switcher.vala | 28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/ui/gtk3/switcher.vala b/ui/gtk3/switcher.vala
+index cf187555..269a68d4 100644
+--- a/ui/gtk3/switcher.vala
++++ b/ui/gtk3/switcher.vala
+@@ -91,6 +91,9 @@ class Switcher : Gtk.Window {
+     private uint m_popup_delay_time_id = 0;
+     private int m_root_x;
+     private int m_root_y;
++    private double m_mouse_init_x;
++    private double m_mouse_init_y;
++    private bool   m_mouse_moved;
+     private GLib.HashTable<string, string> m_xkb_languages =
+             new GLib.HashTable<string, string>(GLib.str_hash,
+                                                GLib.str_equal);
+@@ -221,6 +224,11 @@ class Switcher : Gtk.Window {
+                               Gdk.CURRENT_TIME);
+         if (status != Gdk.GrabStatus.SUCCESS)
+             warning("Grab pointer failed! status = %d", status);
++        // Probably we can delete m_popup_delay_time in 1.6
++        pointer.get_position_double(null,
++                                    out m_mouse_init_x,
++                                    out m_mouse_init_y);
++        m_mouse_moved = false;
+ 
+ 
+         m_loop = new GLib.MainLoop();
+@@ -263,12 +271,30 @@ class Switcher : Gtk.Window {
+             var button = new IBusEngineButton(engine, this);
+             var longname = engine.get_longname();
+             button.set_relief(Gtk.ReliefStyle.NONE);
++            button.add_events(Gdk.EventMask.POINTER_MOTION_MASK);
+             button.show();
+ 
+             button.enter_notify_event.connect((e) => {
++                // avoid gtk_button_update_state()
++                return true;
++            });
++            button.motion_notify_event.connect((e) => {
++#if VALA_0_24
++                Gdk.EventMotion pe = e;
++#else
++                Gdk.EventMotion *pe = &e;
++#endif
++                if (m_selected_engine == index)
++                    return false;
++                if (!m_mouse_moved &&
++                    m_mouse_init_x == pe.x_root &&
++                    m_mouse_init_y == pe.y_root) {
++                    return false;
++                }
++                m_mouse_moved = true;
+                 button.grab_focus();
+                 m_selected_engine = index;
+-                return true;
++                return false;
+             });
+ 
+             button.button_press_event.connect((e) => {
+-- 
+2.13.4
+
+From bbfb3d738b9d61d1eb0658a9ce56e3cd8c111ac4 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 6 Sep 2017 14:08:40 +0900
+Subject: [PATCH 10/14] client/gtk2: Do not send key events to
+ GtkIMContextSimple
+
+GtkIMContextSimple binds Ctrl-Shift-u but IBus clients do not now.
+
+BUG=https://github.com/ibus/ibus/issues/1889
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/327290043
+---
+ client/gtk2/ibusimcontext.c | 41 +++++++++++++++++++++++++++++++++++++++--
+ src/ibusenginesimple.c      | 23 ++---------------------
+ src/ibusenginesimple.h      | 24 ++++++++++++++++++++++--
+ 3 files changed, 63 insertions(+), 25 deletions(-)
+
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index b4ca8828..3ea46951 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -2,7 +2,8 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2008-2013 Red Hat, Inc.
++ * Copyright (C) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2008-2017 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
+@@ -247,6 +248,39 @@ _focus_out_cb (GtkWidget     *widget,
+     return FALSE;
+ }
+ 
++static gboolean
++ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
++                              GdkEventKey   *event)
++{
++    int i;
++    GdkModifierType no_text_input_mask;
++    gunichar ch;
 +
++    if (event->type == GDK_KEY_RELEASE)
++        return FALSE;
++    /* Ignore modifier key presses */
++    for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++)
++        if (event->keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i])
++            return FALSE;
++    no_text_input_mask = gdk_keymap_get_modifier_mask (
++            gdk_keymap_get_for_display (gdk_display_get_default ()),
++            GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
++    if (event->state & no_text_input_mask ||
++        event->keyval == GDK_KEY_Return ||
++        event->keyval == GDK_KEY_ISO_Enter ||
++        event->keyval == GDK_KEY_KP_Enter) {
++        return FALSE;
++    }
++    ch = ibus_keyval_to_unicode (event->keyval);
++    if (ch != 0 && !g_unichar_iscntrl (ch)) {
++        IBusText *text = ibus_text_new_from_unichar (ch);
++        g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
++        g_object_unref (text);
++        return TRUE;
++    }
++   return FALSE;
++}
 +
-+GType           ibus_fontset_get_type           (void);
+ static void
+ _process_key_event_done (GObject      *object,
+                          GAsyncResult *res,
+@@ -797,8 +831,11 @@ ibus_im_context_filter_keypress (GtkIMContext *context,
+     if (event->state & IBUS_HANDLED_MASK)
+         return TRUE;
+ 
++    /* Do not call gtk_im_context_filter_keypress() because
++     * gtk_im_context_simple_filter_keypress() binds Ctrl-Shift-u
++     */
+     if (event->state & IBUS_IGNORED_MASK)
+-        return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
++        return ibus_im_context_commit_event (ibusimcontext, event);
+ 
+     /* XXX it is a workaround for some applications do not set client
+      * window. */
+diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c
+index cddd932c..63785223 100644
+--- a/src/ibusenginesimple.c
++++ b/src/ibusenginesimple.c
+@@ -81,25 +81,6 @@ const IBusComposeTableCompact ibus_compose_table_compact = {
+ 
+ static GSList *global_tables;
+ 
+-static const guint16 ibus_compose_ignore[] = {
+-    IBUS_KEY_Shift_L,
+-    IBUS_KEY_Shift_R,
+-    IBUS_KEY_Control_L,
+-    IBUS_KEY_Control_R,
+-    IBUS_KEY_Caps_Lock,
+-    IBUS_KEY_Shift_Lock,
+-    IBUS_KEY_Meta_L,
+-    IBUS_KEY_Meta_R,
+-    IBUS_KEY_Alt_L,
+-    IBUS_KEY_Alt_R,
+-    IBUS_KEY_Super_L,
+-    IBUS_KEY_Super_R,
+-    IBUS_KEY_Hyper_L,
+-    IBUS_KEY_Hyper_R,
+-    IBUS_KEY_Mode_switch,
+-    IBUS_KEY_ISO_Level3_Shift
+-};
+-
+ /* functions prototype */
+ static void     ibus_engine_simple_destroy      (IBusEngineSimple   *simple);
+ static void     ibus_engine_simple_reset        (IBusEngine         *engine);
+@@ -1045,8 +1026,8 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
+     }
+ 
+     /* Ignore modifier key presses */
+-    for (i = 0; i < G_N_ELEMENTS (ibus_compose_ignore); i++)
+-        if (keyval == ibus_compose_ignore[i])
++    for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++)
++        if (keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i])
+             return FALSE;
+ 
+     if ((priv->in_hex_sequence || priv->in_emoji_sequence)
+diff --git a/src/ibusenginesimple.h b/src/ibusenginesimple.h
+index 8712659c..a5ef34fb 100644
+--- a/src/ibusenginesimple.h
++++ b/src/ibusenginesimple.h
+@@ -2,8 +2,8 @@
+ /* vim:set et sts=4: */
+ /* ibus - The Input Bus
+  * Copyright (C) 2008-2015 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright (C) 2015-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
+- * Copyright (C) 2008-2016 Red Hat, Inc.
++ * Copyright (C) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2008-2017 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
+@@ -40,6 +40,7 @@
+  */
+ 
+ #include "ibusengine.h"
++#include "ibuskeysyms.h"
+ 
+ G_BEGIN_DECLS
+ 
+@@ -94,6 +95,25 @@ struct _IBusEngineSimpleClass {
+     gpointer pdummy[8];
+ };
+ 
++static const guint16 IBUS_COMPOSE_IGNORE_KEYLIST[] = {
++    IBUS_KEY_Shift_L,
++    IBUS_KEY_Shift_R,
++    IBUS_KEY_Control_L,
++    IBUS_KEY_Control_R,
++    IBUS_KEY_Caps_Lock,
++    IBUS_KEY_Shift_Lock,
++    IBUS_KEY_Meta_L,
++    IBUS_KEY_Meta_R,
++    IBUS_KEY_Alt_L,
++    IBUS_KEY_Alt_R,
++    IBUS_KEY_Super_L,
++    IBUS_KEY_Super_R,
++    IBUS_KEY_Hyper_L,
++    IBUS_KEY_Hyper_R,
++    IBUS_KEY_Mode_switch,
++    IBUS_KEY_ISO_Level3_Shift
++};
 +
-+/**
-+ * ibus_fontset_new:
-+ * @first_property_name: 
-+ *
-+ * Creates a new #IBusFcFontSet.
-+ *
-+ * Returns: (transfer full): A newly allocated #IBusFontSet and includes
-+ * #FcFontSet internally. E.g. ibus_fontset_new ("family",
-+ * "Noto Emoji Color", "size", 16, "language", "ja-jp");
-+ */
-+IBusFontSet *   ibus_fontset_new                (const gchar
-+                                                           *first_property_name,
-+                                                                 ...);
+ GType   ibus_engine_simple_get_type       (void);
+ 
+ /**
+-- 
+2.13.4
+
+From d784e04c38eeb069f9a8da8b30743f4463fa34c3 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 7 Sep 2017 10:57:14 +0900
+Subject: [PATCH 11/14] client/gtk2: Fix a build failure with
+ GDK_MODIFIER_INTENT_NO_TEXT_INPUT
+
+BUG=https://github.com/ibus/ibus/issues/1942
+
+Review URL: https://codereview.appspot.com/327300043
+---
+ client/gtk2/ibusimcontext.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index 3ea46951..a806382d 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -262,9 +262,21 @@ ibus_im_context_commit_event (IBusIMContext *ibusimcontext,
+     for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++)
+         if (event->keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i])
+             return FALSE;
++#if GTK_CHECK_VERSION (3, 4, 0)
+     no_text_input_mask = gdk_keymap_get_modifier_mask (
+             gdk_keymap_get_for_display (gdk_display_get_default ()),
+             GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
++#else
++#  ifndef GDK_WINDOWING_QUARTZ
++#    define _IBUS_NO_TEXT_INPUT_MOD_MASK (GDK_MOD1_MASK | GDK_CONTROL_MASK)
++#  else
++#    define _IBUS_NO_TEXT_INPUT_MOD_MASK (GDK_MOD2_MASK | GDK_CONTROL_MASK)
++#  endif
++
++    no_text_input_mask = _IBUS_NO_TEXT_INPUT_MOD_MASK;
++
++#  undef _IBUS_NO_TEXT_INPUT_MOD_MASK
++#endif
+     if (event->state & no_text_input_mask ||
+         event->keyval == GDK_KEY_Return ||
+         event->keyval == GDK_KEY_ISO_Enter ||
+-- 
+2.13.4
+
+From 0632cbbbb573749bbca96a416fde1490810e52d2 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 13 Sep 2017 18:15:06 +0900
+Subject: [PATCH 13/14] ui/gtk3: Fix PropertyPanel position in workarea
+
+gdk_screen_get_monitor_workarea() no longer return the correct area
+from "_NET_WORKAREA" atom in GTK 3.22 and now use
+gdk_monitor_get_workarea() instead.
+
+Use gdk_seat_grab() instead of deprecated gdk_device_grab().
+
+Use gtk_menu_popup_at_rect() instead of deprecated gtk_menu_popup() and
+generate a new foreign GdkWindow with mouse cursor for the Qt Window.
+
+Also fixed some deprecated APIs.
+
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/330190043
+---
+ ui/gtk3/candidatepanel.vala    |  40 ++++++++-------
+ ui/gtk3/emojier.vala           |  24 ++++++---
+ ui/gtk3/handle.vala            |   7 +--
+ ui/gtk3/indicator.vala         |  68 ++++++++++++++++++++++++--
+ ui/gtk3/keybindingmanager.vala |   2 +-
+ ui/gtk3/panel.vala             | 108 +++++++++++++++++++++++++++++++----------
+ ui/gtk3/propertypanel.vala     |  21 +++++++-
+ ui/gtk3/switcher.vala          |  84 +++++++++++++++++++++++---------
+ 8 files changed, 270 insertions(+), 84 deletions(-)
+
+diff --git a/ui/gtk3/candidatepanel.vala b/ui/gtk3/candidatepanel.vala
+index 0e5e3bc2..ec2d3db4 100644
+--- a/ui/gtk3/candidatepanel.vala
++++ b/ui/gtk3/candidatepanel.vala
+@@ -3,7 +3,7 @@
+  * ibus - The Input Bus
+  *
+  * Copyright(c) 2011-2015 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright(c) 2015-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright(c) 2015-2017 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
+@@ -292,6 +292,26 @@ public class CandidatePanel : Gtk.Box{
+             adjust_window_position_vertical();
+     }
+ 
++    private Gdk.Rectangle get_monitor_geometry() {
++        Gdk.Rectangle monitor_area = { 0, };
++
++        // Use get_monitor_geometry() instead of get_monitor_area().
++        // get_monitor_area() excludes docks, but the lookup window should be
++        // shown over them.
++#if VALA_0_34
++        Gdk.Monitor monitor = Gdk.Display.get_default().get_monitor_at_point(
++                m_cursor_location.x,
++                m_cursor_location.y);
++        monitor_area = monitor.get_geometry();
++#else
++        Gdk.Screen screen = Gdk.Screen.get_default();
++        int monitor_num = screen.get_monitor_at_point(m_cursor_location.x,
++                                                      m_cursor_location.y);
++        screen.get_monitor_geometry(monitor_num, out monitor_area);
++#endif
++        return monitor_area;
++    }
 +
-+/**
-+ * ibus_fontset_new_with_font:
-+ * @family: font family
-+ * @size: font size
-+ * @language: font language
-+ *
-+ * Creates  a new #IBusFcFontSet.
-+ *
-+ * Returns: (transfer full): A newly allocated #IBusFcFontSet and includes
-+ * #FcFontSet internally.
-+ */
-+IBusFontSet *   ibus_fontset_new_with_font      (const gchar    *family,
-+                                                 guint           size,
-+                                                 const gchar    *language);
-+/**
-+ * ibus_fontset_get_family:
-+ * @fontset: #IBusFcFontSet
-+ *
-+ * Return the base font family of #FcFontSet
-+ *
-+ * Returns: Base font family of #FcFontSet
-+ */
-+const gchar *   ibus_fontset_get_family         (IBusFontSet    *fontset);
+     private void adjust_window_position_horizontal() {
+         Gdk.Point cursor_right_bottom = {
+                 m_cursor_location.x + m_cursor_location.width,
+@@ -305,14 +325,7 @@ public class CandidatePanel : Gtk.Box{
+             cursor_right_bottom.y + allocation.height
+         };
+ 
+-        Gdk.Screen screen = Gdk.Screen.get_default();
+-        int monitor_num = screen.get_monitor_at_point(m_cursor_location.x,
+-                                                      m_cursor_location.y);
+-        // Use get_monitor_geometry() instead of get_monitor_area().
+-        // get_monitor_area() excludes docks, but the lookup window should be
+-        // shown over them.
+-        Gdk.Rectangle monitor_area;
+-        screen.get_monitor_geometry(monitor_num, out monitor_area);
++        Gdk.Rectangle monitor_area = get_monitor_geometry();
+         int monitor_right = monitor_area.x + monitor_area.width;
+         int monitor_bottom = monitor_area.y + monitor_area.height;
+ 
+@@ -358,14 +371,7 @@ public class CandidatePanel : Gtk.Box{
+             m_cursor_location.y + allocation.height
+         };
+ 
+-        Gdk.Screen screen = Gdk.Screen.get_default();
+-        int monitor_num = screen.get_monitor_at_point(m_cursor_location.x,
+-                                                      m_cursor_location.y);
+-        // Use get_monitor_geometry() instead of get_monitor_area().
+-        // get_monitor_area() excludes docks, but the lookup window should be
+-        // shown over them.
+-        Gdk.Rectangle monitor_area;
+-        screen.get_monitor_geometry(monitor_num, out monitor_area);
++        Gdk.Rectangle monitor_area = get_monitor_geometry();
+         int monitor_right = monitor_area.x + monitor_area.width;
+         int monitor_bottom = monitor_area.y + monitor_area.height;
+ 
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 36ab4bab..9cd98140 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -575,7 +575,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+         if (lang == "en") {
+             bool has_variant = false;
+             foreach (unichar ch in EMOJI_VARIANT_LIST) {
+-                if (emoji.chr(-1, ch) != null) {
++                if (emoji.index_of_char(ch) >= 0) {
+                     has_variant = true;
+                     break;
+                 }
+@@ -782,15 +782,17 @@ class IBusEmojier : Gtk.ApplicationWindow {
+     private bool check_unicode_point() {
+         string annotation = m_entry.get_text();
+         m_unicode_point = null;
+-        var buff = new GLib.StringBuilder();
++        // Add "0x" because uint64.ascii_strtoull() is not accessible
++        // and need to use uint64.parse()
++        var buff = new GLib.StringBuilder("0x");
+         var retval = new GLib.StringBuilder();
+         for (int i = 0; i < annotation.char_count(); i++) {
+             unichar ch = annotation.get_char(i);
+             if (ch == 0)
+                 return false;
+             if (ch.isspace()) {
+-                unichar code = (unichar)buff.str.to_ulong(null, 16);
+-                buff.erase();
++                unichar code = (unichar)uint64.parse(buff.str);
++                buff.assign("0x");
+                 if (!code.validate())
+                     return false;
+                 retval.append(code.to_string());
+@@ -800,7 +802,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                 return false;
+             buff.append_unichar(ch);
+         }
+-        unichar code = (unichar)buff.str.to_ulong(null, 16);
++        unichar code = (unichar)uint64.parse(buff.str);
+         if (!code.validate())
+             return false;
+         retval.append(code.to_string());
+@@ -834,7 +836,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                         matched = true;
+                     break;
+                 case 2:
+-                    if (key.str(annotation) != null)
++                    if (key.index_of(annotation) >= 0)
+                         matched = true;
+                     break;
+                 default:
+@@ -1586,10 +1588,16 @@ class IBusEmojier : Gtk.ApplicationWindow {
+     public void present_centralize(Gdk.Event event) {
+         Gtk.Allocation allocation;
+         get_allocation(out allocation);
+-        Gdk.Screen screen = Gdk.Screen.get_default();
+-        int monitor_num = screen.get_monitor_at_window(get_window());
+         Gdk.Rectangle monitor_area;
++#if VALA_0_34
++        Gdk.Display display = Gdk.Display.get_default();
++        Gdk.Monitor monitor = display.get_monitor_at_window(this.get_window());
++        monitor_area = monitor.get_geometry();
++#else
++        Gdk.Screen screen = Gdk.Screen.get_default();
++        int monitor_num = screen.get_monitor_at_window(this.get_window());
+         screen.get_monitor_geometry(monitor_num, out monitor_area);
++#endif
+         int x = (monitor_area.x + monitor_area.width - allocation.width)/2;
+         int y = (monitor_area.y + monitor_area.height
+                  - allocation.height)/2;
+diff --git a/ui/gtk3/handle.vala b/ui/gtk3/handle.vala
+index bef5e8ba..fc9164a0 100644
+--- a/ui/gtk3/handle.vala
++++ b/ui/gtk3/handle.vala
+@@ -3,7 +3,7 @@
+  * ibus - The Input Bus
+  *
+  * Copyright(c) 2011-2016 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright(c) 2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright(c) 2016-2017 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
+@@ -59,7 +59,6 @@ class Handle : Gtk.EventBox {
+ 
+     public override void realize() {
+         base.realize();
+-        // get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.FLEUR));
+     }
+ 
+     public override bool button_press_event(Gdk.EventButton event) {
+@@ -138,7 +137,9 @@ class Handle : Gtk.EventBox {
+         m_move_begined = false;
+         m_press_pos.x = 0;
+         m_press_pos.y = 0;
+-        get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR));
++        get_window().set_cursor(new Gdk.Cursor.for_display(
++                   Gdk.Display.get_default(),
++                   Gdk.CursorType.FLEUR));
+         move_end();
+         return true;
+     }
+diff --git a/ui/gtk3/indicator.vala b/ui/gtk3/indicator.vala
+index dac72b49..4d111a64 100644
+--- a/ui/gtk3/indicator.vala
++++ b/ui/gtk3/indicator.vala
+@@ -2,7 +2,7 @@
+  *
+  * ibus - The Input Bus
+  *
+- * Copyright(c) 2015 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright(c) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
+  * Copyright(c) 2015 Red Hat, Inc.
+  *
+  * This library is free software; you can redistribute it and/or
+@@ -97,6 +97,7 @@ class Indicator : IBus.Service
+     private int m_context_menu_y;
+     private int m_activate_menu_x;
+     private int m_activate_menu_y;
++    private Gdk.Window m_indicator_window;
+ 
+     public Indicator(string id,
+                      GLib.DBusConnection connection,
+@@ -206,7 +207,8 @@ class Indicator : IBus.Service
+         GLib.Variant var_y = parameters.get_child_value(1);
+         m_context_menu_x = var_x.get_int32();
+         m_context_menu_y = var_y.get_int32();
+-        context_menu(2, 0);
++        Gdk.Window window = query_gdk_window();
++        context_menu(m_context_menu_x, m_context_menu_y, window, 2, 0);
+     }
+ 
+     private void _activate_menu_cb(GLib.DBusConnection       connection,
+@@ -216,7 +218,57 @@ class Indicator : IBus.Service
+         GLib.Variant var_y = parameters.get_child_value(1);
+         m_activate_menu_x = var_x.get_int32();
+         m_activate_menu_y = var_y.get_int32();
+-        activate();
++        Gdk.Window window = query_gdk_window();
++        activate(m_activate_menu_x, m_activate_menu_y, window);
++    }
 +
-+/**
-+ * ibus_fontset_set_family:
-+ * @fontset: #IBusFcFontSet
-+ * @family: base font family for #FcFontSet
-+ *
-+ * Set the base font family for #FcFontSet
-+ */
-+void            ibus_fontset_set_family         (IBusFontSet    *fontset,
-+                                                 const gchar    *family);
-+/**
-+ * ibus_fontset_get_size:
-+ * @fontset: #IBusFcFontSet
-+ *
-+ * Return the font size of #FcFontSet
-+ *
-+ * Returns: Font size of #FcFontSet
-+ */
-+guint           ibus_fontset_get_size           (IBusFontSet    *fontset);
++    private Gdk.Window? query_gdk_window() {
++        if (m_indicator_window != null)
++            return m_indicator_window;
++
++        Gdk.Display display = Gdk.Display.get_default();
++        unowned X.Display xdisplay =
++                (display as Gdk.X11.Display).get_xdisplay();
++        X.Window current = xdisplay.default_root_window();
++        X.Window parent = 0;
++        X.Window child = 0;
++        int root_x, root_y, win_x, win_y;
++        uint mask = 0;
++        root_x = root_y = win_x = win_y = 0;
++        bool retval;
++        // Need XSetErrorHandler for BadWindow?
++        while ((retval = xdisplay.query_pointer(current,
++                                      out parent, out child,
++                                      out root_x, out root_y,
++                                      out win_x, out win_y,
++                                      out mask))) {
++            if (child == 0)
++                break;
++            current = child;
++        }
++        if (!retval) {
++            string format =
++                    "XQueryPointer is failed: current: %x root: %x " +
++                    "child: %x (%d, %d), (%d, %d), %u";
++            string message = format.printf((uint)current,
++                                           (uint)xdisplay.default_root_window(),
++                                           (uint)child,
++                                           root_x, root_y, win_x, win_y,
++                                           mask);
++            warning("XQueryPointer is failed: %s", message);
++            return null;
++        }
++        if (current == xdisplay.default_root_window())
++            warning("The query window is root window");
++        m_indicator_window = Gdk.X11.Window.lookup_for_display(
++                display as Gdk.X11.Display,
++                current);
++        if (m_indicator_window != null)
++            return m_indicator_window;
++        m_indicator_window = new Gdk.X11.Window.foreign_for_display(
++                display as Gdk.X11.Display,
++                current);
++        return m_indicator_window;
+     }
+ 
+     private GLib.Variant? _get_id(GLib.DBusConnection connection) {
+@@ -479,7 +531,13 @@ class Indicator : IBus.Service
+         push_in = false;
+     }
+ 
+-    public signal void context_menu(uint button, uint activate_time);
+-    public signal void activate();
++    public signal void context_menu(int        x,
++                                    int        y,
++                                    Gdk.Window window,
++                                    uint       button,
++                                    uint       activate_time);
++    public signal void activate(int        x,
++                                int        y,
++                                Gdk.Window window);
+     public signal void registered_status_notifier_item();
+ }
+diff --git a/ui/gtk3/keybindingmanager.vala b/ui/gtk3/keybindingmanager.vala
+index 49013b8d..c8b1e7f6 100644
+--- a/ui/gtk3/keybindingmanager.vala
++++ b/ui/gtk3/keybindingmanager.vala
+@@ -18,7 +18,7 @@ public class KeybindingManager : GLib.Object {
+ 
+     private static KeybindingManager m_instance = null;
+ 
+-    public static const uint MODIFIER_FILTER =
++    public const uint MODIFIER_FILTER =
+         Gdk.ModifierType.MODIFIER_MASK & ~(
+         Gdk.ModifierType.LOCK_MASK |  // Caps Lock
+         // Gdk.ModifierType.MOD1_MASK |  // Alt
+diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
+index bf43cbf9..629dadce 100644
+--- a/ui/gtk3/panel.vala
++++ b/ui/gtk3/panel.vala
+@@ -267,6 +267,27 @@ class Panel : IBus.PanelService {
+         });
+     }
+ 
++    private void popup_menu_at_area_window(Gtk.Menu              menu,
++                                           Gdk.Rectangle         area,
++                                           Gdk.Window?           window,
++                                           Gtk.MenuPositionFunc? func) {
++#if VALA_0_34
++        Gdk.Gravity rect_anchor = Gdk.Gravity.SOUTH_WEST;
++        Gdk.Gravity menu_anchor = Gdk.Gravity.NORTH_WEST;
++
++        // Gtk.Menu.popup() is now deprecated but
++        // Gtk.Menu.popup_at_rect() requires a Gdk.Window and
++        // Gtk.Menu.popup_at_rect() outputs a warning of
++        // "no trigger event for menu popup"
++        // for the foreigner QT window which is generated by
++        // Gdk.X11.Window.foreign_for_display.
++        // https://git.gnome.org/browse/gtk+/tree/gtk/gtkmenu.c?h=gtk-3-22#n2251
++        menu.popup_at_rect(window, area, rect_anchor, menu_anchor, null);
++#else
++        menu.popup(null, null, func, 0, Gtk.get_current_event_time());
++#endif
++    }
 +
-+/**
-+ * ibus_fontset_set_size:
-+ * @fontset: #IBusFcFontSet
-+ * @size: font size for #FcFontSet
-+ *
-+ * Set the font size for #FcFontSet
-+ */
-+void            ibus_fontset_set_size           (IBusFontSet    *fontset,
-+                                                 guint           size);
-+/**
-+ * ibus_fontset_get_language:
-+ * @fontset: #IBusFcFontSet
-+ *
-+ * Return the font language of #FcFontSet
-+ *
-+ * Returns: Font language of #FcFontSet
-+ */
-+const gchar *   ibus_fontset_get_language       (IBusFontSet    *fontset);
+ #if INDICATOR
+     private bool is_kde() {
+         if (Environment.get_variable("XDG_CURRENT_DESKTOP") == "KDE")
+@@ -276,6 +297,19 @@ class Panel : IBus.PanelService {
+         return false;
+     }
+ 
++    private void popup_menu_at_pointer_window(Gtk.Menu              menu,
++                                              int                   x,
++                                              int                   y,
++                                              Gdk.Window?           window,
++                                              Gtk.MenuPositionFunc? func) {
++        int win_x = 0;
++        int win_y = 0;
++        window.get_origin(out win_x, out win_y);
++        Gdk.Rectangle area = { x - win_x, y - win_y, 1, 1 };
++        // window is a bottom wide panel instead of status icon
++        popup_menu_at_area_window(menu, area, window, func);
++    }
 +
-+/**
-+ * ibus_fontset_set_language:
-+ * @fontset: #IBusFcFontSet
-+ * @language: font langauge for #FcFontSet
-+ *
-+ * Set the font language for #FcFontSet
-+ */
-+void            ibus_fontset_set_language       (IBusFontSet    *fontset,
-+                                                 const gchar    *language);
+     private void init_indicator() {
+         m_icon_type = IconType.INDICATOR;
+         GLib.Bus.get.begin(GLib.BusType.SESSION, null, (obj, res) => {
+@@ -290,21 +324,17 @@ class Panel : IBus.PanelService {
+                     m_indicator.set_status(Indicator.Status.ACTIVE);
+                     state_changed();
+                 });
+-                m_indicator.context_menu.connect((b, t) => {
+-                    Gtk.Menu menu = create_context_menu();
+-                    menu.popup(null,
+-                               null,
+-                               m_indicator.position_context_menu,
+-                               0,
+-                               Gtk.get_current_event_time());
++                m_indicator.context_menu.connect((x, y, w, b, t) => {
++                    popup_menu_at_pointer_window(
++                            create_context_menu(),
++                            x, y, w,
++                            m_indicator.position_context_menu);
+                 });
+-                m_indicator.activate.connect(() => {
+-                    Gtk.Menu menu = create_activate_menu();
+-                    menu.popup(null,
+-                               null,
+-                               m_indicator.position_activate_menu,
+-                               0,
+-                               Gtk.get_current_event_time());
++                m_indicator.activate.connect((x, y, w) => {
++                    popup_menu_at_pointer_window(
++                            create_activate_menu(),
++                            x, y, w,
++                            m_indicator.position_activate_menu);
+                 });
+             } catch (GLib.IOError e) {
+                 warning("Failed to get the session bus: %s", e.message);
+@@ -317,21 +347,47 @@ class Panel : IBus.PanelService {
+         m_status_icon = new Gtk.StatusIcon();
+         m_status_icon.set_name("ibus-ui-gtk");
+         m_status_icon.set_title(_("IBus Panel"));
++
++        // Gdk.Window.get_width() is needed for the menu position
++        if (m_status_icon.get_size() > 0)
++            init_status_icon_menu();
++        else
++            m_status_icon.notify["size"].connect(init_status_icon_menu);
++    }
 +
-+/**
-+ * ibus_fontset_update_fcfontset:
-+ * @fontset: #IBusFcFontSet
-+ *
-+ * Update #FcFontSet from font family, size and langauge of @fontset.
-+ * Returns: %TRUE if #FcFontSet is updated. %FALSE otherwise.
-+ */
-+gboolean        ibus_fontset_update_fcfontset   (IBusFontSet    *fontset);
++    private void init_status_icon_menu() {
++        Gdk.Rectangle area = { 0, 0, 0, 0 };
++        Gdk.Window? window = null;
++        Gtk.MenuPositionFunc? func = null;
++#if VALA_0_34
++        window = Gdk.X11.Window.lookup_for_display(
++                Gdk.Display.get_default() as Gdk.X11.Display,
++                m_status_icon.get_x11_window_id()) as Gdk.Window;
++        if (window == null) {
++            warning("StatusIcon does not have GdkWindow");
++            return;
++        }
++        Gtk.Orientation orient;
++        m_status_icon.get_geometry(null, out area, out orient);
++        int win_x = 0;
++        int win_y = 0;
++        window.get_origin(out win_x, out win_y);
++        // The (x, y) is converted by gdk_window_get_root_coords()
++        // in gdk_window_impl_move_to_rect()
++        area.x -= win_x;
++        area.y -= win_y;
++#else
++        func = m_status_icon.position_menu;
++#endif
+         m_status_icon.popup_menu.connect((b, t) => {
+-                Gtk.Menu menu = create_context_menu();
+-                menu.popup(null,
+-                           null,
+-                           m_status_icon.position_menu,
+-                           0,
+-                           Gtk.get_current_event_time());
++                popup_menu_at_area_window(
++                        create_context_menu(),
++                        area, window, func);
+         });
+         m_status_icon.activate.connect(() => {
+-                Gtk.Menu menu = create_activate_menu();
+-                menu.popup(null,
+-                           null,
+-                           m_status_icon.position_menu,
+-                           0,
+-                           Gtk.get_current_event_time());
++                popup_menu_at_area_window(
++                        create_activate_menu(),
++                        area, window, func);
+         });
+         m_status_icon.set_from_icon_name("ibus-keyboard");
+     }
+diff --git a/ui/gtk3/propertypanel.vala b/ui/gtk3/propertypanel.vala
+index dd4342ec..857f8e20 100644
+--- a/ui/gtk3/propertypanel.vala
++++ b/ui/gtk3/propertypanel.vala
+@@ -4,7 +4,7 @@
+  *
+  * Copyright(c) 2013-2016 Red Hat, Inc.
+  * Copyright(c) 2013-2015 Peng Huang <shawn.p.huang@gmail.com>
+- * Copyright(c) 2013-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright(c) 2013-2017 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
+@@ -330,8 +330,16 @@ public class PropertyPanel : Gtk.Box {
+         Gtk.Allocation allocation;
+         m_toplevel.get_allocation(out allocation);
+ 
++        Gdk.Rectangle monitor_area;
++#if VALA_0_34
++        // gdk_screen_get_monitor_workarea() no longer return the correct
++        // area from "_NET_WORKAREA" atom in GTK 3.22
++        Gdk.Monitor monitor = Gdk.Display.get_default().get_monitor(0);
++        monitor_area = monitor.get_workarea();
++#else
+         Gdk.Screen screen = Gdk.Screen.get_default();
+-        Gdk.Rectangle monitor_area = screen.get_monitor_workarea(0);
++        monitor_area = screen.get_monitor_workarea(0);
++#endif
+         int monitor_right = monitor_area.x + monitor_area.width;
+         int monitor_bottom = monitor_area.y + monitor_area.height;
+         int x, y;
+@@ -472,8 +480,15 @@ public class PropMenu : Gtk.Menu, IPropToolItem {
+     public new void popup(uint       button,
+                           uint32     activate_time,
+                           Gtk.Widget widget) {
++#if VALA_0_34
++        base.popup_at_widget(widget,
++                             Gdk.Gravity.SOUTH_WEST,
++                             Gdk.Gravity.NORTH_WEST,
++                             null);
++#else
+         m_parent_button = widget;
+         base.popup(null, null, menu_position, button, activate_time);
++#endif
+     }
+ 
+     public override void destroy() {
+@@ -532,6 +547,7 @@ public class PropMenu : Gtk.Menu, IPropToolItem {
+         }
+     }
+ 
++#if !VALA_0_34
+     private void menu_position(Gtk.Menu menu,
+                                out int  x,
+                                out int  y,
+@@ -580,6 +596,7 @@ public class PropMenu : Gtk.Menu, IPropToolItem {
+ 
+         push_in = false;
+     }
++#endif
+ }
+ 
+ public class PropToolButton : Gtk.ToolButton, IPropToolItem {
+diff --git a/ui/gtk3/switcher.vala b/ui/gtk3/switcher.vala
+index 269a68d4..0ce742a1 100644
+--- a/ui/gtk3/switcher.vala
++++ b/ui/gtk3/switcher.vala
+@@ -157,6 +157,55 @@ class Switcher : Gtk.Window {
+         m_label.set_text(m_buttons[index].longname);
+         m_buttons[index].grab_focus();
+ 
++        // Avoid regressions.
++        if (m_popup_delay_time > 0) {
++            get_position(out m_root_x, out m_root_y);
++            // Pull the window from the screen so that the window gets
++            // the key press and release events but mouse does not select
++            // an IME unexpectedly.
++            move(-1000, -1000);
++        }
 +
-+/**
-+ * ibus_fontset_get_preferred_size_hb:
-+ * @fontset: #IBusFcFontSet
-+ * @text: a string to be calculate the preferred rectangle size.
-+ * @widest: (out): #cairo_rectangle_int_t is updated.
-+ *
-+ * Calculate @widest for @text.
-+ *
-+ * Returns: #IBusRequisitionEx which includes the glyphs and coordinates.
-+ */
-+IBusRequisitionEx *
-+                ibus_fontset_get_preferred_size_hb
-+                                                (IBusFontSet    *fontset,
-+                                                 const gchar    *text,
-+                                                 cairo_rectangle_int_t
-+                                                                *widest);
++        show_all();
 +
-+/**
-+ * ibus_fontset_draw_cairo_lines:
-+ * @fontset: #IBusFcFontSet
-+ * @cr: #cairo_t in #GtkWidget.draw().
-+ * @ex: #IBusRequisitionEx which includes glyph, x, y values, char width
-+ *      and height.
-+ *
-+ * Draw glyphs in @ex using cairo @cr.
-+ */
-+void            ibus_fontset_draw_cairo_with_requisition_ex
-+                                                (IBusFontSet    *fontset,
-+                                                 cairo_t        *cr,
-+                                                 IBusRequisitionEx
-+                                                                *ex);
++        if (m_popup_delay_time > 0) {
++            // Restore the window position after m_popup_delay_time
++            m_popup_delay_time_id = GLib.Timeout.add(m_popup_delay_time,
++                                                     () => {
++                restore_window_position("timeout");
++                return false;
++            });
++        }
 +
-+/**
-+ * ibus_fontset_unref:
-+ * @fontset: #IBusFcFontSet
-+ *
-+ * Call g_object_unref().
-+ * FIXME: Seems Vala needs this API.
-+ */
-+void            ibus_fontset_unref              (IBusFontSet    *fontset);
++        Gdk.Device pointer;
++#if VALA_0_34
++        Gdk.Seat seat = event.get_seat();
++        if (seat == null) {
++            var display = get_display();
++            seat = display.get_default_seat();
++        }
++        //keyboard = seat.get_keyboard();
++        pointer = seat.get_pointer();
++
++        Gdk.GrabStatus status;
++        // Grab all keyboard events
++        status = seat.grab(get_window(),
++                           Gdk.SeatCapabilities.KEYBOARD,
++                           true,
++                           null,
++                           event,
++                           null);
++        if (status != Gdk.GrabStatus.SUCCESS)
++            warning("Grab keyboard failed! status = %d", status);
++        status = seat.grab(get_window(),
++                           Gdk.SeatCapabilities.POINTER,
++                           true,
++                           null,
++                           event,
++                           null);
++        if (status != Gdk.GrabStatus.SUCCESS)
++            warning("Grab pointer failed! status = %d", status);
++#else
+         Gdk.Device device = event.get_device();
+         if (device == null) {
+             var display = get_display();
+@@ -174,7 +223,6 @@ class Switcher : Gtk.Window {
+         }
+ 
+         Gdk.Device keyboard;
+-        Gdk.Device pointer;
+         if (device.get_source() == Gdk.InputSource.KEYBOARD) {
+             keyboard = device;
+             pointer = device.get_associated_device();
+@@ -183,26 +231,6 @@ class Switcher : Gtk.Window {
+             keyboard = device.get_associated_device();
+         }
+ 
+-        // Avoid regressions.
+-        if (m_popup_delay_time > 0) {
+-            get_position(out m_root_x, out m_root_y);
+-            // Pull the window from the screen so that the window gets
+-            // the key press and release events but mouse does not select
+-            // an IME unexpectedly.
+-            move(-1000, -1000);
+-        }
+-
+-        show_all();
+-
+-        if (m_popup_delay_time > 0) {
+-            // Restore the window position after m_popup_delay_time
+-            m_popup_delay_time_id = GLib.Timeout.add(m_popup_delay_time,
+-                                                     () => {
+-                restore_window_position("timeout");
+-                return false;
+-            });
+-        }
+-
+         Gdk.GrabStatus status;
+         // Grab all keyboard events
+         status = keyboard.grab(get_window(),
+@@ -224,6 +252,8 @@ class Switcher : Gtk.Window {
+                               Gdk.CURRENT_TIME);
+         if (status != Gdk.GrabStatus.SUCCESS)
+             warning("Grab pointer failed! status = %d", status);
++#endif
 +
-+G_END_DECLS
+         // Probably we can delete m_popup_delay_time in 1.6
+         pointer.get_position_double(null,
+                                     out m_mouse_init_x,
+@@ -235,8 +265,12 @@ class Switcher : Gtk.Window {
+         m_loop.run();
+         m_loop = null;
+ 
++#if VALA_0_34
++        seat.ungrab();
++#else
+         keyboard.ungrab(Gdk.CURRENT_TIME);
+         pointer.ungrab(Gdk.CURRENT_TIME);
++#endif
+ 
+         hide();
+         // Make sure the switcher is hidden before returning from this function.
+@@ -319,13 +353,19 @@ class Switcher : Gtk.Window {
+         m_label.set_ellipsize(Pango.EllipsizeMode.END);
+ 
+         Gdk.Display display = Gdk.Display.get_default();
++        int screen_width = 0;
++#if VALA_0_34
++        Gdk.Monitor monitor = display.get_monitor_at_window(this.get_window());
++        Gdk.Rectangle area = monitor.get_geometry();
++        screen_width = area.width;
++#else
+         Gdk.Screen screen = (display != null) ?
+                 display.get_default_screen() : null;
+-        int screen_width = 0;
+ 
+         if (screen != null) {
+             screen_width = screen.get_width();
+         }
 +#endif
+ 
+         if (screen_width > 0 && max_label_width > (screen_width / 4)) {
+             max_label_width = screen_width / 4;
 -- 
-2.9.3
+2.13.4
+
+From a7e78022b95329ca5782512872398a365503c410 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 14 Sep 2017 18:07:42 +0900
+Subject: [PATCH 14/14] ui/gtk3: Fix to enable menu button on PropertyPanel
+
+---
+ ui/gtk3/propertypanel.vala | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/ui/gtk3/propertypanel.vala b/ui/gtk3/propertypanel.vala
+index 857f8e20..f5d9cff7 100644
+--- a/ui/gtk3/propertypanel.vala
++++ b/ui/gtk3/propertypanel.vala
+@@ -84,6 +84,23 @@ public class PropertyPanel : Gtk.Box {
+     public void set_properties(IBus.PropList props) {
+         debug("set_properties()\n");
+ 
++        // When click PropMenuToolButton, the focus is changed and
++        // set_properties() is called here while the menu button is active.
++        // Ignore that case here not to remove items.
++        bool has_active = false;
++        foreach (var item in m_items) {
++            Type type = item.get_type();
++            if (type == typeof(PropMenuToolButton) ||
++                type == typeof(PropToggleToolButton)) {
++                if ((item as Gtk.ToggleToolButton).get_active()) {
++                    has_active = true;
++                    break;
++                }
++            }
++        }
++        if (has_active)
++            return;
++
+         foreach (var item in m_items)
+             remove((item as Gtk.Widget));
+         m_items = {};
+-- 
+2.13.4
 

diff --git a/ibus-xx-emoji-harfbuzz.patch b/ibus-xx-emoji-harfbuzz.patch
new file mode 100644
index 0000000..fbfe8dc
--- /dev/null
+++ b/ibus-xx-emoji-harfbuzz.patch
@@ -0,0 +1,1647 @@
+From c5e3a76dc92ea967b138d43dc9ed7ecdb2e3fc7a Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 14 Sep 2017 15:55:21 +0900
+Subject: [PATCH] Integrate custom rendering to use HarfBuzz glyph info
+
+IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering
+on Cairo context.
+Current Pango changes fonts by emoji variants and draws the separated
+glyphs [1] but actually the emoji characters with variants can be drawn
+as one glyph so this class  manages Fontconfig fontsets to select a font,
+HarfBuzz to get glyphs for emoji variants, Cairo to draw glyphs.
+Need configure --enable-harfbuzz-for-emoji option to enable this feature.
+
+[1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669
+     https://bugzilla.gnome.org/show_bug.cgi?id=781123
+---
+ bindings/vala/IBusFontSet-1.0.metadata |   1 +
+ bindings/vala/Makefile.am              |  83 +++
+ bindings/vala/ibus-fontset-1.0.deps    |   1 +
+ configure.ac                           |  29 +
+ ui/gtk3/Makefile.am                    |  32 ++
+ ui/gtk3/emojier.vala                   | 100 +++-
+ ui/gtk3/ibusfontset.c                  | 932 +++++++++++++++++++++++++++++++++
+ ui/gtk3/ibusfontset.h                  | 302 +++++++++++
+ 8 files changed, 1478 insertions(+), 2 deletions(-)
+ create mode 100644 bindings/vala/IBusFontSet-1.0.metadata
+ create mode 100644 bindings/vala/ibus-fontset-1.0.deps
+ create mode 100644 ui/gtk3/ibusfontset.c
+ create mode 100644 ui/gtk3/ibusfontset.h
+
+diff --git a/bindings/vala/IBusFontSet-1.0.metadata b/bindings/vala/IBusFontSet-1.0.metadata
+new file mode 100644
+index 00000000..73037d7f
+--- /dev/null
++++ b/bindings/vala/IBusFontSet-1.0.metadata
+@@ -0,0 +1 @@
++IBusFontSet cheader_filename="ibusfontset.h"
+diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
+index fc8e2f01..f7b9e97a 100644
+--- a/bindings/vala/Makefile.am
++++ b/bindings/vala/Makefile.am
+@@ -83,8 +83,10 @@ EXTRA_DIST =                                    \
+     IBus-1.0.metadata                           \
+     IBus-1.0-custom.vala                        \
+     IBusEmojiDialog-1.0.metadata                \
++    IBusFontSet-1.0.metadata                    \
+     ibus-1.0.deps                               \
+     ibus-emoji-dialog-1.0.deps                  \
++    ibus-fontset-1.0.deps                       \
+     config.vapi                                 \
+     xi.vapi                                     \
+     $(NULL)
+@@ -131,6 +133,15 @@ libibus_emoji_dialog_1_0_la_LDFLAGS =           \
+ 	if test ! -f $@ ; then                                              \
+ 	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
+ 	fi;
++ibusfontset.c: $(ibus_vapi) ibusfontset.h
++	if test ! -f $@ ; then                                              \
++	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
++	fi;
++ibusfontset.h: $(ibus_vapi)
++	if test ! -f $@ ; then                                              \
++	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
++	fi;
++
+ 
+ MAINTAINERCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES)
+ DISTCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES)
+@@ -184,6 +195,78 @@ DISTCLEANFILES += $(ibus_emoji_dialog_vapi)
+ 
+ endif
+ #end of HAVE_INTROSPECTION
++
++
++if ENABLE_HARFBUZZ_FOR_EMOJI
++libibus_fontset = libibus-fontset-1.0.la
++noinst_LTLIBRARIES += $(libibus_fontset)
++
++libibus_fontset_1_0_la_SOURCES =                \
++    ibusfontset.c                               \
++    $(NULL)
++libibus_fontset_1_0_la_CFLAGS =                 \
++    $(AM_CFLAGS)                                \
++    @CAIRO_CFLAGS@                              \
++    @FONTCONFIG_CFLAGS@                         \
++    @GLIB2_CFLAGS@                              \
++    @HARFBUZZ_CFLAGS@                           \
++    @PANGO_CFLAGS@                              \
++    $(NULL)
++libibus_fontset_1_0_la_LIBADD =                 \
++    @CAIRO_LIBS@                                \
++    @FONTCONFIG_LIBS@                           \
++    @GLIB2_LIBS@                                \
++    @HARFBUZZ_LIBS@                             \
++    @PANGO_LIBS@                                \
++    $(NULL)
++libibus_fontset_1_0_la_LDFLAGS =                \
++    -no-undefined                               \
++    -export-symbols-regex "ibus_.*"             \
++    $(NULL)
++
++MAINTAINERCLEANFILES += ibusfontset.c ibusfontset.h
++DISTCLEANFILES += ibusfontset.c ibusfontset.h
++
++if HAVE_INTROSPECTION
++IBusFontSet-1.0.gir: $(libibus_fontset) Makefile
++IBusFontSet_1_0_gir_SCANNERFLAGS =              \
++    --pkg-export=ibus-1.0                       \
++    --pkg=cairo                                 \
++    --pkg=fontconfig                            \
++    --pkg=harfbuzz                              \
++    $(IBUS_GIR_SCANNERFLAGS)                    \
++    $(NULL)
++IBusFontSet_1_0_gir_LIBS = $(libibus_fontset) $(libibus)
++IBusFontSet_1_0_gir_INCLUDES = cairo-1.0 GLib-2.0 GObject-2.0
++IBusFontSet_1_0_gir_FILES =                     \
++    ibusfontset.h                               \
++    $(NULL)
++IBusFontSet_1_0_gir_CFLAGS =                    \
++    -I$(srcdir)                                 \
++    -I$(builddir)                               \
++    -I$(top_srcdir)/src                         \
++    $(NULL)
++ibus_fontset_gir = IBusFontSet-1.0.gir
++INTROSPECTION_GIRS += $(ibus_fontset_gir)
++noinst_DATA += $(ibus_fontset_gir)
++EXTRA_DIST += $(ibus_fontset_gir)
++MAINTAINERCLEANFILES += $(ibus_fontset_gir)
++DISTCLEANFILES += $(ibus_fontset_gir)
++
++ibus-fontset-1.0.vapi: $(ibus_fontset_gir) IBusFontSet-1.0.metadata
++ibus_fontset_vapi = ibus-fontset-1.0.vapi
++ibus_fontset_1_0_vapi_METADATADIRS = $(srcdir)
++ibus_fontset_1_0_vapi_FILES = IBusFontSet-1.0.gir
++VAPIGEN_VAPIS += $(ibus_fontset_vapi)
++noinst_DATA += $(ibus_fontset_vapi)
++EXTRA_DIST += $(ibus_fontset_vapi)
++MAINTAINERCLEANFILES += $(ibus_fontset_vapi)
++DISTCLEANFILES += $(ibus_fontset_vapi)
++
++endif
++# end of HAVE_INTROSPECTION
++endif
++# end of ENABLE_HARFBUZZ_FOR_EMOJI
+ endif
+ # end of ENABLE_EMOJI_DICT
+ 
+diff --git a/bindings/vala/ibus-fontset-1.0.deps b/bindings/vala/ibus-fontset-1.0.deps
+new file mode 100644
+index 00000000..129fe166
+--- /dev/null
++++ b/bindings/vala/ibus-fontset-1.0.deps
+@@ -0,0 +1 @@
++cairo
+diff --git a/configure.ac b/configure.ac
+index 14556a3a..6ff8f4a9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -653,6 +653,34 @@ https://github.com/fujiwarat/cldr-emoji-annotation)
+     enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
+ fi
+ 
++AC_ARG_ENABLE(harfbuzz-for-emoji,
++    AS_HELP_STRING([--enable-harfbuzz-for-emoji],
++                   [Enable HarBuzz to draw emoji characters.
++                    Current Pango has a problem to draw emoji variants and
++                    this way enables to use HarfBuzz directly in GtkLabel.]),
++    [enable_harfbuzz_for_emoji=$enableval],
++    [enable_harfbuzz_for_emoji=no]
++)
++AM_CONDITIONAL([ENABLE_HARFBUZZ_FOR_EMOJI],
++               [test x"$enable_harfbuzz_for_emoji" = x"yes"])
++
++if test x"$enable_harfbuzz_for_emoji" = x"yes"; then
++    PKG_CHECK_MODULES(CAIRO, [
++        cairo
++    ])
++    PKG_CHECK_MODULES(FONTCONFIG, [
++        fontconfig
++    ])
++    PKG_CHECK_MODULES(HARFBUZZ, [
++        harfbuzz
++    ])
++    PKG_CHECK_MODULES(PANGO, [
++        pango
++    ])
++else
++    enable_harfbuzz_for_emoji="no (disabled, use --enable-harfbuzz-for-emoji to enable)"
++fi
++
+ # Check iso-codes.
+ PKG_CHECK_MODULES(ISOCODES, [
+     iso-codes
+@@ -743,6 +771,7 @@ Build options:
+   Enable Emoji dict             $enable_emoji_dict
+   Unicode Emoji directory       $UNICODE_EMOJI_DIR
+   CLDR annotation directory     $EMOJI_ANNOTATION_DIR
++  Enable HarfBuzz for Emoji     $enable_harfbuzz_for_emoji
+   Run test cases                $enable_tests
+ ])
+ 
+diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
+index 786b80e6..cd1e9c2c 100644
+--- a/ui/gtk3/Makefile.am
++++ b/ui/gtk3/Makefile.am
+@@ -156,6 +156,8 @@ EXTRA_DIST =                            \
+     $(man_seven_in_files)               \
+     emojierapp.vala                     \
+     gtkpanel.xml.in                     \
++    ibusfontset.c                       \
++    ibusfontset.h                       \
+     notification-item.xml               \
+     notification-watcher.xml            \
+     $(NULL)
+@@ -198,6 +200,36 @@ emojierapp.o: $(srcdir)/emojierapp.c
+ 	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
+ 	$(NULL)
+ 
++if ENABLE_HARFBUZZ_FOR_EMOJI
++ibus_ui_gtk3_SOURCES +=                         \
++    ibusfontset.c                               \
++    $(NULL)
++
++ibus_ui_emojier_SOURCES +=                      \
++    ibusfontset.c                               \
++    $(NULL)
++
++AM_CFLAGS += \
++    @CAIRO_CFLAGS@                              \
++    @FONTCONFIG_CFLAGS@                         \
++    @HARFBUZZ_CFLAGS@                           \
++    $(NULL)
++
++AM_LDADD += \
++    @CAIRO_LIBS@                                \
++    @FONTCONFIG_LIBS@                           \
++    @HARFBUZZ_LIBS@                             \
++    $(NULL)
++
++AM_VALAFLAGS += \
++    -D ENABLE_HARFBUZZ_FOR_EMOJI                \
++    --pkg=cairo                                 \
++    --pkg=ibus-fontset-1.0                      \
++    $(NULL)
++
++endif
++# end of ENABLE_HARFBUZZ_FOR_EMOJI
++
+ man_seven_files = $(man_seven_in_files:.7.in=.7)
+ man_seven_DATA =$(man_seven_files:.7=.7.gz)
+ man_sevendir = $(mandir)/man7
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 36ab4bab..7d5116fe 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -80,6 +80,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
+         }
+     }
+     private class EWhiteLabel : Gtk.Label {
++#if ENABLE_HARFBUZZ_FOR_EMOJI
++        IBus.RequisitionEx m_requisition;
++#endif
+         public EWhiteLabel(string text) {
+             GLib.Object(
+                 name : "IBusEmojierWhiteLabel"
+@@ -87,8 +90,78 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             if (text != "")
+                 set_label(text);
+         }
++#if ENABLE_HARFBUZZ_FOR_EMOJI
++        private void get_preferred_size_with_hb(out int minimum_width,
++                                                out int natural_width,
++                                                out int minimum_height,
++                                                out int natural_height) {
++            minimum_width = 0;
++            natural_width = 0;
++            minimum_height = 0;
++            natural_height = 0;
++            var text = this.get_text();
++            if (text == null || text == "")
++                return;
++            var context = this.get_pango_context();
++            var language = context.get_language();
++            update_fontset(language);
++            Cairo.RectangleInt widest = Cairo.RectangleInt();
++            m_requisition = m_fontset.get_preferred_size_hb(text, out widest);
++            minimum_width = widest.width;
++            natural_width = widest.width;
++            minimum_height = widest.height;
++            natural_height = widest.height;
++        }
++        public override void get_preferred_width(out int minimum_width,
++                                                 out int natural_width) {
++            get_preferred_size_with_hb(out minimum_width,
++                                       out natural_width,
++                                       null, null);
++        }
++        public override void get_preferred_height(out int minimum_height,
++                                                  out int natural_height) {
++            get_preferred_size_with_hb(null, null,
++                                       out minimum_height,
++                                       out natural_height);
++        }
++        public override bool draw(Cairo.Context cr) {
++            if (m_fontset == null)
++                return true;
++            if (m_requisition == null)
++                return true;
++            if (m_requisition.cairo_lines == null)
++                return true;
++            var style_context = get_style_context();
++            Gtk.Allocation allocation;
++            get_allocation(out allocation);
++            style_context.render_background(cr,
++                                            0, 0,
++                                            allocation.width,
++                                            allocation.height);
++            Gdk.RGBA *normal_fg = null;
++            style_context.get(Gtk.StateFlags.NORMAL,
++                              "color",
++                              out normal_fg);
++            cr.set_operator(Cairo.Operator.OVER);
++            cr.set_source_rgba(normal_fg.red, normal_fg.green, normal_fg.blue,
++                               normal_fg.alpha);
++            cr.save();
++            double x = 0.0;
++            double y = 0.0;
++            if (allocation.width > m_requisition.width)
++                x = (allocation.width - m_requisition.width) / 2.0;
++            if (allocation.height > m_requisition.height)
++                y = (allocation.height - m_requisition.height) / 2.0;
++            cr.translate(x, y);
++            m_fontset.draw_cairo_with_requisition_ex(cr, m_requisition);
++            cr.restore();
++            normal_fg.free();
++            normal_fg = null;
++            return true;
++        }
++#endif
+     }
+-    private class ESelectedLabel : Gtk.Label {
++    private class ESelectedLabel : EWhiteLabel {
+         public ESelectedLabel(string text) {
+             GLib.Object(
+                 name : "IBusEmojierSelectedLabel"
+@@ -97,7 +170,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                 set_label(text);
+         }
+     }
+-    private class EGoldLabel : Gtk.Label {
++    private class EGoldLabel : EWhiteLabel {
+         public EGoldLabel(string text) {
+             GLib.Object(
+                 name : "IBusEmojierGoldLabel"
+@@ -212,6 +285,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             m_category_to_emojis_dict;
+     private static GLib.HashTable<string, GLib.SList<string>>?
+             m_emoji_to_emoji_variants_dict;
++#if ENABLE_HARFBUZZ_FOR_EMOJI
++    private static IBus.FontSet m_fontset;
++#endif
+ 
+     private ThemedRGBA m_rgba;
+     private Gtk.Box m_vbox;
+@@ -1601,6 +1677,22 @@ class IBusEmojier : Gtk.ApplicationWindow {
+     }
+ 
+ 
++#if ENABLE_HARFBUZZ_FOR_EMOJI
++    private static void update_fontset(Pango.Language language) {
++        if (m_fontset != null) {
++            m_fontset.set_family(m_emoji_font_family);
++            m_fontset.set_size(m_emoji_font_size);
++            m_fontset.set_language(language.to_string());
++            m_fontset.update_fcfontset();
++        } else {
++            m_fontset = new IBus.FontSet.with_font(
++                    m_emoji_font_family,
++                    m_emoji_font_size,
++                    language.to_string());
++        }
++    }
++#endif
++
+     public static bool has_loaded_emoji_dict() {
+         if (m_emoji_to_data_dict == null)
+             return false;
+@@ -1631,6 +1723,10 @@ class IBusEmojier : Gtk.ApplicationWindow {
+         int font_size = font_desc.get_size() / Pango.SCALE;
+         if (font_size != 0)
+             m_emoji_font_size = font_size;
++#if ENABLE_HARFBUZZ_FOR_EMOJI
++        var widget = new Gtk.Label("");
++        update_fontset(widget.get_pango_context().get_language());
++#endif
+     }
+ 
+ 
+diff --git a/ui/gtk3/ibusfontset.c b/ui/gtk3/ibusfontset.c
+new file mode 100644
+index 00000000..66090ecd
+--- /dev/null
++++ b/ui/gtk3/ibusfontset.c
+@@ -0,0 +1,932 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* ibus - The Input Bus
++ * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2017 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
++ * 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, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
++ * USA
++ */
++
++#include <cairo-ft.h>
++#include <fontconfig/fontconfig.h>
++#include <ft2build.h>
++#include FT_FREETYPE_H
++#include <glib.h>
++#include <hb-ot.h>
++#include <pango/pango.h>
++
++#include "ibusfontset.h"
++
++#define XPAD 2
++#define YPAD 2
++#define UNKNOWN_FONT_SIZE 7
++#define IBUS_FONTSET_GET_PRIVATE(o)  \
++   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_FONTSET, IBusFontSetPrivate))
++#define MONOSPACE "monospace"
++#define SERIF     "serif"
++#define SANS      "sans"
++
++
++static FT_Library  m_ftlibrary;
++static FcFontSet  *m_fcfontset;
++static gchar      *m_family;
++static guint       m_size;
++static gchar      *m_language;
++static GHashTable *m_scaled_font_table;
++static GHashTable *m_hb_font_table;
++
++enum {
++    PROP_0,
++    PROP_FAMILY,
++    PROP_SIZE,
++    PROP_LANGUAGE
++};
++
++typedef struct {
++    gunichar   ch;
++    FcPattern *fcfont;
++} FontPerChar;
++
++struct _IBusFontSetPrivate {
++    gchar     *family;
++    guint      size;
++    gchar     *language;
++};
++
++static GObject * ibus_fontset_constructor   (GType                  type,
++                                             guint                  n,
++                                             GObjectConstructParam *args);
++static void      ibus_fontset_destroy       (IBusFontSet           *fontset);
++static void      ibus_fontset_set_property  (IBusFontSet           *fontset,
++                                             guint                  prop_id,
++                                             const GValue          *value,
++                                             GParamSpec            *pspec);
++static void      ibus_fontset_get_property  (IBusFontSet           *fontset,
++                                             guint                  prop_id,
++                                             GValue                *value,
++                                             GParamSpec            *pspec);
++static cairo_scaled_font_t *
++                 ibus_fontset_cairo_scaled_font_new_with_font
++                                            (const gchar           *family,
++                                             guint                  size);
++
++G_DEFINE_BOXED_TYPE (IBusCairoLine,
++                     ibus_cairo_line,
++                     ibus_cairo_line_copy,
++                     ibus_cairo_line_free);
++G_DEFINE_BOXED_TYPE (IBusRequisitionEx,
++                     ibus_requisition_ex,
++                     ibus_requisition_ex_copy,
++                     ibus_requisition_ex_free);
++G_DEFINE_TYPE (IBusFontSet, ibus_fontset, IBUS_TYPE_OBJECT)
++
++static void
++ibus_fontset_class_init (IBusFontSetClass *class)
++{
++    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
++    IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
++    cairo_glyph_t dummy;
++    IBusGlyph dummy2;
++
++    gobject_class->constructor = ibus_fontset_constructor;
++    gobject_class->get_property =
++            (GObjectGetPropertyFunc) ibus_fontset_get_property;
++    gobject_class->set_property =
++            (GObjectSetPropertyFunc) ibus_fontset_set_property;
++    object_class->destroy = (IBusObjectDestroyFunc) ibus_fontset_destroy;
++
++    /* install properties */
++    /**
++     * IBusFontSet:family:
++     *
++     * Font family of this IBusFontSet.
++     */
++    g_object_class_install_property (gobject_class,
++                    PROP_FAMILY,
++                    g_param_spec_string ("family",
++                        "family",
++                        "family",
++                        "",
++                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
++
++    /**
++     * IBusFontSet:size:
++     *
++     * Font size of this IBusFontSet.
++     */
++    g_object_class_install_property (gobject_class,
++                    PROP_SIZE,
++                    g_param_spec_uint ("size",
++                        "size",
++                        "size",
++                        0, G_MAXUINT16, 0,
++                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
++
++    /**
++     * IBusFontSet:language:
++     *
++     * Font language of this IBusFontSet.
++     */
++    g_object_class_install_property (gobject_class,
++                    PROP_LANGUAGE,
++                    g_param_spec_string ("language",
++                        "language",
++                        "language",
++                        "",
++                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
++
++    g_type_class_add_private (class, sizeof (IBusFontSetPrivate));
++    FT_Init_FreeType (&m_ftlibrary);
++    m_scaled_font_table = g_hash_table_new_full (
++            g_str_hash, g_str_equal,
++            g_free,
++            (GDestroyNotify) cairo_scaled_font_destroy);
++    m_hb_font_table = g_hash_table_new_full (
++            g_str_hash, g_str_equal,
++            g_free,
++            (GDestroyNotify) hb_font_destroy);
++
++    /* hb_glyph_t is not available in Vala so override it with IBusGlyph. */
++    g_assert (sizeof (dummy) == sizeof (dummy2));
++    g_assert (sizeof (dummy.index) == sizeof (dummy2.index));
++    g_assert (sizeof (dummy.x) == sizeof (dummy2.x));
++    g_assert (sizeof (dummy.y) == sizeof (dummy2.y));
++}
++
++static void
++ibus_fontset_init (IBusFontSet *fontset)
++{
++    fontset->priv = IBUS_FONTSET_GET_PRIVATE (fontset);
++}
++
++
++static GObject *
++ibus_fontset_constructor (GType                   type,
++                          guint                   n,
++                          GObjectConstructParam  *args)
++{
++    GObject *object;
++    IBusFontSet *fontset;
++    const gchar *family;
++    guint size;
++
++    object = G_OBJECT_CLASS (ibus_fontset_parent_class)->constructor (
++            type, n ,args);
++    fontset = IBUS_FONTSET (object);
++    family = ibus_fontset_get_family (fontset);
++    size = ibus_fontset_get_size (fontset);
++    ibus_fontset_update_fcfontset (fontset);
++    if (family != NULL && size > 0) {
++        /* cache the font */
++        ibus_fontset_cairo_scaled_font_new_with_font (family,
++                                                      size);
++    }
++    return object;
++}
++
++static void
++ibus_fontset_destroy (IBusFontSet *fontset)
++{
++    g_clear_pointer (&fontset->priv->family, g_free);
++    g_clear_pointer (&fontset->priv->language, g_free);
++}
++
++static void
++ibus_fontset_set_property (IBusFontSet  *fontset,
++                           guint         prop_id,
++                           const GValue *value,
++                           GParamSpec   *pspec)
++{
++    switch (prop_id) {
++    case PROP_FAMILY:
++        ibus_fontset_set_family (fontset, g_value_get_string (value));
++        break;
++    case PROP_SIZE:
++        ibus_fontset_set_size (fontset, g_value_get_uint (value));
++        break;
++    case PROP_LANGUAGE:
++        ibus_fontset_set_language (fontset, g_value_get_string (value));
++        break;
++    default:
++        G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec);
++    }
++}
++
++static void
++ibus_fontset_get_property (IBusFontSet *fontset,
++                           guint          prop_id,
++                           GValue        *value,
++                           GParamSpec    *pspec)
++{
++    switch (prop_id) {
++    case PROP_FAMILY:
++        g_value_set_string (value, ibus_fontset_get_family (fontset));
++        break;
++    case PROP_SIZE:
++        g_value_set_uint (value, ibus_fontset_get_size (fontset));
++        break;
++    case PROP_LANGUAGE:
++        g_value_set_string (value, ibus_fontset_get_language (fontset));
++        break;
++    default:
++        G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec);
++    }
++}
++
++static cairo_scaled_font_t *
++ibus_fontset_cairo_scaled_font_new_with_font (const gchar *family,
++                                              guint        size)
++{
++    gchar *font_name;
++    cairo_scaled_font_t *scaled_font = NULL;
++    FcPattern *pattern, *resolved;
++    FcResult result;
++    cairo_font_options_t *font_options;
++    double pixel_size = 0.;
++    FcMatrix fc_matrix, *fc_matrix_val;
++    cairo_font_face_t *cairo_face = NULL;
++    cairo_matrix_t font_matrix;
++    cairo_matrix_t ctm;
++    int i;
++
++    g_return_val_if_fail (family != NULL, NULL);
++    g_return_val_if_fail (m_scaled_font_table != NULL, NULL);
++
++    font_name = g_strdup_printf ("%s %u", family, size);
++    scaled_font = g_hash_table_lookup (m_scaled_font_table, font_name);
++    if (scaled_font != NULL) {
++        g_free (font_name);
++        return scaled_font;
++    }
++    pattern = FcPatternCreate ();
++    FcPatternAddString (pattern, FC_FAMILY, (FcChar8*) family);
++    FcPatternAddDouble (pattern, FC_SIZE, (double) size);
++    FcPatternAddDouble (pattern, FC_DPI, 96);
++    FcConfigSubstitute(NULL, pattern, FcMatchPattern);
++    font_options = cairo_font_options_create ();
++    cairo_ft_font_options_substitute (font_options, pattern);
++    FcDefaultSubstitute (pattern);
++    resolved = FcFontMatch (NULL, pattern, &result);
++    FcPatternDestroy (pattern);
++    FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size);
++    if (pixel_size == 0.)
++        g_warning ("Failed to scaled the font: %s %u", family, size);
++    cairo_face = cairo_ft_font_face_create_for_pattern (resolved);
++    FcMatrixInit (&fc_matrix);
++    for (i = 0;
++         FcPatternGetMatrix (resolved, FC_MATRIX, i, &fc_matrix_val)
++                 == FcResultMatch;
++         i++) {
++        FcMatrixMultiply (&fc_matrix, &fc_matrix, fc_matrix_val);
++    }
++    FcPatternDestroy (resolved);
++    cairo_matrix_init (&font_matrix,
++                       fc_matrix.xx, -fc_matrix.yx,
++                       -fc_matrix.xy, fc_matrix.yy,
++                       0., 0.);
++    if (pixel_size != 0.)
++        cairo_matrix_scale (&font_matrix, pixel_size, pixel_size);
++    cairo_matrix_init_identity (&ctm);
++    scaled_font = cairo_scaled_font_create (cairo_face,
++                                            &font_matrix, &ctm,
++                                            font_options);
++    cairo_font_face_destroy (cairo_face);
++    if (font_name)
++        g_hash_table_insert(m_scaled_font_table, font_name, scaled_font);
++
++    return scaled_font;
++}
++
++static hb_font_t *
++ibus_fontset_hb_font_new_with_font_path (const gchar *font_path)
++{
++    hb_font_t *hb_font;
++    GError *error = NULL;
++    GMappedFile *mf;
++    char *font_data = NULL;
++    gsize len;
++    hb_blob_t *hb_blob;
++    hb_face_t *hb_face;
++
++    g_return_val_if_fail (font_path != NULL, NULL);
++    g_return_val_if_fail (m_hb_font_table != NULL, NULL);
++
++    hb_font = g_hash_table_lookup (m_hb_font_table, font_path);
++    if (hb_font != NULL)
++        return hb_font;
++
++    mf = g_mapped_file_new (font_path, FALSE, &error);
++    if (mf == NULL) {
++        g_warning ("Not found font %s", font_path);
++        return NULL;
++    }
++    font_data = g_mapped_file_get_contents (mf);
++    len = g_mapped_file_get_length (mf);
++    if (len == 0) {
++        g_warning ("zero size font %s", font_path);
++        g_mapped_file_unref (mf);
++        return NULL;
++    }
++    hb_blob = hb_blob_create (font_data, len,
++                              HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
++                              mf, (hb_destroy_func_t)g_mapped_file_unref);
++    hb_face = hb_face_create (hb_blob, 0);
++    hb_blob_destroy (hb_blob);
++    hb_font = hb_font_create (hb_face);
++    unsigned int upem = hb_face_get_upem (hb_face);
++    hb_font_set_scale (hb_font, upem, upem);
++    hb_face_destroy (hb_face);
++    hb_ot_font_set_funcs (hb_font);
++    g_hash_table_insert (m_hb_font_table, g_strdup (font_path), hb_font);
++
++    return hb_font;
++}
++
++static void
++get_font_extents_with_scaled_font (cairo_scaled_font_t *scaled_font,
++                                   PangoRectangle      *font_rect)
++{
++    cairo_font_extents_t font_extents;
++
++    g_assert (scaled_font != NULL && font_rect != NULL);
++
++    cairo_scaled_font_extents (scaled_font, &font_extents);
++    font_rect->x = 0;
++    font_rect->y = - pango_units_from_double (font_extents.ascent);
++    font_rect->width = 0;
++    font_rect->height = pango_units_from_double (
++            font_extents.ascent + font_extents.descent);
++}
++
++static void
++get_glyph_extents_with_scaled_hb_font (const gchar          *str,
++                                       cairo_scaled_font_t  *scaled_font,
++                                       hb_font_t            *hb_font,
++                                       PangoRectangle       *font_rect,
++                                       IBusCairoLine       **cairo_lines,
++                                       FcChar8              *fallback_family)
++{
++    gboolean has_unknown_glyph = FALSE;
++    hb_buffer_t *hb_buffer;
++    unsigned int len, n, i;
++    hb_glyph_info_t *info;
++    hb_glyph_position_t *pos;
++    double x;
++    cairo_glyph_t *glyph;
++    cairo_text_extents_t text_extents = { 0, };
++
++    g_return_if_fail (str != NULL);
++
++    hb_buffer = hb_buffer_create ();
++    hb_buffer_add_utf8 (hb_buffer, str, -1, 0, -1);
++    hb_buffer_guess_segment_properties (hb_buffer);
++    for (n = 0; *cairo_lines && (*cairo_lines)[n].scaled_font; n++);
++    if (n == 0)
++        *cairo_lines = g_new0 (IBusCairoLine, 2);
++    else
++        *cairo_lines = g_renew (IBusCairoLine, *cairo_lines, n + 2);
++    (*cairo_lines)[n + 1].scaled_font = NULL;
++    (*cairo_lines)[n + 1].num_glyphs = 0;
++    (*cairo_lines)[n + 1].glyphs = NULL;
++    hb_shape (hb_font, hb_buffer, NULL, 0);
++    len = hb_buffer_get_length (hb_buffer);
++    info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
++    pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);
++    (*cairo_lines)[n].scaled_font = scaled_font;
++    (*cairo_lines)[n].num_glyphs = len;
++    (*cairo_lines)[n].glyphs = (IBusGlyph*) cairo_glyph_allocate (len + 1);
++    x = 0.;
++    for (i = 0; i < len; i++) {
++        hb_codepoint_t c = info[i].codepoint;
++        if (c) {
++            (*cairo_lines)[n].glyphs[i].index = info[i].codepoint;
++            (*cairo_lines)[n].glyphs[i].x = x;
++            (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE;
++            glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]);
++            cairo_scaled_font_glyph_extents (scaled_font, glyph,
++                                             1, &text_extents);
++            x += text_extents.width;
++        } else {
++            has_unknown_glyph = TRUE;
++            c = g_utf8_get_char (str);
++            (*cairo_lines)[n].glyphs[i].index = PANGO_GET_UNKNOWN_GLYPH (c);
++            (*cairo_lines)[n].glyphs[i].x = x;
++            (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE;
++            glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]);
++            cairo_scaled_font_glyph_extents (scaled_font, glyph,
++                                             1, &text_extents);
++            x += 10;
++        }
++    }
++    (*cairo_lines)[n].glyphs[i].index = -1;
++    (*cairo_lines)[n].glyphs[i].x = 0;
++    (*cairo_lines)[n].glyphs[i].y = 0;
++    glyph = (cairo_glyph_t *) (*cairo_lines)[n].glyphs;
++    cairo_scaled_font_glyph_extents (scaled_font, glyph,
++                                     len, &text_extents);
++    if (text_extents.width) {
++        font_rect->width = pango_units_from_double (text_extents.width);
++    } else {
++        font_rect->width = font_rect->height;
++    }
++    if (has_unknown_glyph && fallback_family != NULL) {
++        cairo_scaled_font_t  *unknown_font;
++        unknown_font = ibus_fontset_cairo_scaled_font_new_with_font (
++            (const gchar *) fallback_family,
++            UNKNOWN_FONT_SIZE);
++        (*cairo_lines)[n].scaled_font = unknown_font;
++    }
++    hb_buffer_destroy (hb_buffer);
++}
++
++static void
++get_string_extents_with_font (const gchar            *str,
++                              FontPerChar            *buff,
++                              cairo_rectangle_int_t  *rect,
++                              IBusCairoLine         **cairo_lines)
++{
++    FcChar8 *family = NULL;
++    FcChar8 *font_path = NULL;
++    guint size = 0;
++    cairo_scaled_font_t *scaled_font = NULL;
++    PangoRectangle font_rect = { 0, };
++    hb_font_t *hb_font;
++
++    g_return_if_fail (str != NULL);
++    g_return_if_fail (buff != NULL && buff->fcfont != NULL);
++
++    FcPatternGetString (buff->fcfont, FC_FAMILY, 0, &family);
++    g_return_if_fail (family != NULL);
++    size = m_size;
++    if (size == 0) {
++        g_warning ("Font size is not right for font %s.", family);
++        size = 14;
++    }
++    scaled_font = ibus_fontset_cairo_scaled_font_new_with_font (
++            (const gchar *) family,
++            size);
++    g_return_if_fail (scaled_font != NULL);
++    get_font_extents_with_scaled_font (scaled_font, &font_rect);
++
++    FcPatternGetString (buff->fcfont, FC_FILE, 0, &font_path);
++    g_return_if_fail (font_path != NULL);
++    hb_font = ibus_fontset_hb_font_new_with_font_path (
++            (const gchar *) font_path);
++    if (hb_font == NULL)
++        return;
++    get_glyph_extents_with_scaled_hb_font (str,
++                                           scaled_font,
++                                           hb_font,
++                                           &font_rect,
++                                           cairo_lines,
++                                           family);
++    rect->width += font_rect.width / PANGO_SCALE;
++    rect->height += font_rect.height / PANGO_SCALE;
++}
++
++static FT_Face
++ibus_fontset_get_ftface_from_fcfont (IBusFontSet *fontset,
++                                     FcPattern   *fcfont)
++{
++    FcChar8 *font_file = NULL;
++    FT_Face ft_face;
++    guint size = ibus_fontset_get_size (fontset);
++
++    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
++    g_return_val_if_fail (fcfont != NULL, NULL);
++
++    size = ibus_fontset_get_size (fontset);
++    FcPatternGetString (fcfont, FC_FILE, 0, &font_file);
++    FT_New_Face (m_ftlibrary, (const gchar *) font_file, 0, &ft_face);
++    FT_Set_Pixel_Sizes (ft_face, size, size);
++    return ft_face;
++}
++
++void
++_cairo_show_unknown_glyphs (cairo_t             *cr,
++                            const cairo_glyph_t *glyphs,
++                            guint                num_glyphs,
++                            guint                width,
++                            guint                height)
++{
++    gunichar ch;
++    gboolean invalid_input;
++    int rows = 2;
++    int cols;
++    int row, col;
++    char buf[7];
++    double cx = 0.;
++    double cy;
++    const double box_descent = 3.;
++    double x0;
++    double y0;
++    const double digit_width = 5.;
++    const double digit_height= 6.;
++    char hexbox_string[2] = {0, 0};
++
++    g_assert (glyphs != NULL);
++    g_assert (num_glyphs > 0);
++
++    ch = glyphs[0].index & ~PANGO_GLYPH_UNKNOWN_FLAG;
++    invalid_input = G_UNLIKELY (glyphs[0].index == PANGO_GLYPH_INVALID_INPUT ||
++                                ch > 0x10FFFF);
++    if (G_UNLIKELY (invalid_input)) {
++      g_warning ("Unsupported U+%06X",  ch);
++      return;
++    }
++
++    cairo_save (cr);
++
++    cols = (ch > 0xffff ? 6 : 4) / rows;
++    g_snprintf (buf, sizeof(buf), (ch > 0xffff) ? "%06X" : "%04X", ch);
++    cy = (double) height;
++    x0 = cx + box_descent + XPAD / 2;
++    y0 = cy - box_descent - YPAD / 2;
++
++    for (row = 0; row < rows; row++) {
++        double y = y0 - (rows - 1 - row) * (digit_height + YPAD);
++        for (col = 0; col < cols; col++) {
++            double x = x0 + col * (digit_width + XPAD);
++            cairo_move_to (cr, x, y);
++            hexbox_string[0] = buf[row * cols + col];
++            cairo_show_text (cr, hexbox_string);
++        }
++    }
++    cairo_move_to (cr, XPAD, YPAD);
++    cairo_line_to (cr, width - XPAD, YPAD);
++    cairo_line_to (cr, width - XPAD,
++                       height - YPAD);
++    cairo_line_to (cr, XPAD, height - YPAD);
++    cairo_line_to (cr, XPAD, YPAD);
++    cairo_set_line_width (cr, 1.);
++    cairo_stroke (cr);
++
++    cairo_restore (cr);
++}
++
++IBusCairoLine *
++ibus_cairo_line_copy (IBusCairoLine *cairo_lines)
++{
++    IBusCairoLine *ret;
++    guint n, i, j, num_glyphs;
++    if (!cairo_lines)
++        return NULL;
++
++    for (n = 0; cairo_lines[n].scaled_font; n++);
++    ret = g_new0 (IBusCairoLine, n + 1);
++    for (i = 0; i < n; i++) {
++        ret[i].scaled_font = cairo_lines[i].scaled_font;
++        num_glyphs = cairo_lines[i].num_glyphs;
++        ret[i].num_glyphs = num_glyphs;
++        ret[i].glyphs = (IBusGlyph *) cairo_glyph_allocate (num_glyphs + 1);
++        for (j = 0; j < num_glyphs; j++) {
++            ret[i].glyphs[j] = cairo_lines[i].glyphs[j];
++        }
++        ret[i].glyphs[j].index = -1;
++        ret[i].glyphs[j].x = 0;
++        ret[i].glyphs[j].y = 0;
++    }
++    ret[i].scaled_font = NULL;
++    ret[i].num_glyphs = 0;
++    ret[i].glyphs = NULL;
++    return ret;
++}
++
++void
++ibus_cairo_line_free (IBusCairoLine *cairo_lines)
++{
++    guint i;
++    if (!cairo_lines)
++        return;
++    for (i = 0; cairo_lines[i].scaled_font; i++) {
++        g_free (cairo_lines[i].glyphs);
++    }
++    g_free (cairo_lines);
++}
++
++IBusRequisitionEx *
++ibus_requisition_ex_copy (IBusRequisitionEx *req)
++{
++    IBusRequisitionEx *ret;
++    if (!req)
++        return NULL;
++    ret = g_new0 (IBusRequisitionEx, 1);
++    ret->width = req->width;
++    ret->height = req->height;
++    ret->cairo_lines = ibus_cairo_line_copy (req->cairo_lines);
++    return ret;
++}
++
++void
++ibus_requisition_ex_free (IBusRequisitionEx *req)
++{
++    if (!req)
++        return;
++    g_clear_pointer (&req->cairo_lines, ibus_cairo_line_free);
++    g_free (req);
++}
++
++IBusFontSet *
++ibus_fontset_new (const gchar *first_property_name, ...)
++{
++    va_list var_args;
++    IBusFontSet *fontset;
++
++    g_assert (first_property_name);
++
++    va_start (var_args, first_property_name);
++    fontset = (IBusFontSet *)g_object_new_valist (IBUS_TYPE_FONTSET,
++                                                  first_property_name,
++                                                  var_args);
++    va_end (var_args);
++    g_assert (fontset->priv->family);
++    g_assert (fontset->priv->language);
++    return fontset;
++}
++
++IBusFontSet *
++ibus_fontset_new_with_font (const gchar *family,
++                            guint        size,
++                            const gchar *language)
++{
++    return ibus_fontset_new ("family", family,
++                             "size", size,
++                             "language", language,
++                             NULL);
++}
++
++void
++ibus_fontset_exit ()
++{
++    g_clear_pointer (&m_ftlibrary, FT_Done_FreeType);
++}
++
++const gchar *
++ibus_fontset_get_family (IBusFontSet *fontset)
++{
++    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
++    return fontset->priv->family;
++}
++
++void
++ibus_fontset_set_family (IBusFontSet *fontset,
++                         const gchar   *family)
++{
++    g_return_if_fail (IBUS_IS_FONTSET (fontset));
++    g_free (fontset->priv->family);
++    fontset->priv->family = g_strdup (family);
++}
++
++guint
++ibus_fontset_get_size (IBusFontSet *fontset)
++{
++    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), 0);
++    return fontset->priv->size;
++}
++
++void
++ibus_fontset_set_size (IBusFontSet *fontset,
++                       guint          size)
++{
++    g_return_if_fail (IBUS_IS_FONTSET (fontset));
++    fontset->priv->size = size;
++}
++
++const gchar *
++ibus_fontset_get_language (IBusFontSet *fontset)
++{
++    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
++    return fontset->priv->language;
++}
++
++void
++ibus_fontset_set_language (IBusFontSet *fontset,
++                           const gchar *language)
++{
++    g_return_if_fail (IBUS_IS_FONTSET (fontset));
++    g_free (fontset->priv->language);
++    fontset->priv->language = g_strdup (language);
++}
++
++gboolean
++ibus_fontset_update_fcfontset (IBusFontSet *fontset)
++{
++    FcPattern *pattern;
++    const gchar *family;
++    guint size;
++    const gchar *language;
++    gboolean update_fontset = FALSE;
++    FcResult result;
++
++    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), FALSE);
++
++    pattern = FcPatternCreate ();
++    family = fontset->priv->family;
++    size = fontset->priv->size;
++    language = fontset->priv->language;
++
++    if (g_strcmp0 (m_family, family)) {
++        g_free (m_family);
++        m_family = g_strdup (family);
++        update_fontset = TRUE;
++    }
++    if (m_size != size) {
++        m_size = size;
++        update_fontset = TRUE;
++    }
++    if (g_strcmp0 (m_language, language)) {
++        g_free (m_language);
++        m_language = g_strdup (language);
++        update_fontset = TRUE;
++    }
++    if (!update_fontset && m_fcfontset != NULL)
++        return FALSE;
++
++    if (m_fcfontset)
++        g_clear_pointer (&m_fcfontset, FcFontSetDestroy);
++
++    if (g_strcmp0 (family, ""))
++        FcPatternAddString (pattern, FC_FAMILY, (const FcChar8*) family);
++    if (size > 0)
++        FcPatternAddDouble (pattern, FC_SIZE, (double) size);
++    if (g_strcmp0 (language, ""))
++        FcPatternAddString (pattern, FC_LANG, (const FcChar8*) language);
++    FcPatternAddInteger (pattern, FC_WEIGHT, FC_WEIGHT_NORMAL);
++    FcPatternAddInteger (pattern, FC_WIDTH, FC_WIDTH_NORMAL);
++    FcPatternAddInteger (pattern, FC_DPI, 96);
++    if (FC_VERSION >= 21205 &&
++        (!g_ascii_strncasecmp (family, MONOSPACE, strlen (MONOSPACE)) ||
++         !g_ascii_strncasecmp (family, SERIF, strlen (SERIF)) ||
++         !g_ascii_strncasecmp (family, SANS, strlen (SANS)))) {
++        FcPatternAddBool (pattern, FC_COLOR, TRUE);
++    }
++    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
++    FcConfigSubstitute (NULL, pattern, FcMatchFont);
++    FcDefaultSubstitute (pattern);
++    m_fcfontset = FcFontSort (NULL, pattern, FcTrue, NULL, &result);
++    FcPatternDestroy (pattern);
++    if (result == FcResultNoMatch || m_fcfontset->nfont == 0) {
++        g_warning ("No FcFontSet for %s", family ? family : "(null)");
++        return FALSE;
++    }
++    return TRUE;
++}
++
++void
++ibus_fontset_unref (IBusFontSet *fontset)
++{
++    g_object_unref (fontset);
++}
++
++IBusRequisitionEx *
++ibus_fontset_get_preferred_size_hb (IBusFontSet          *fontset,
++                                    const gchar           *text,
++                                    cairo_rectangle_int_t *widest)
++{
++    gchar *copied_text;
++    gchar *p;
++    FontPerChar *buff;
++    IBusCairoLine *cairo_lines = NULL;
++    IBusRequisitionEx *req = NULL;
++    GString *str = NULL;
++    int text_length;
++    int i, n = 0;
++
++    g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL);
++    g_return_val_if_fail (m_fcfontset != NULL, NULL);
++
++    copied_text = g_strdup (text);
++    text_length =  g_utf8_strlen (text, -1);
++    buff = g_slice_alloc0 (sizeof (FontPerChar) * text_length);
++    str = g_string_new (NULL);
++
++    for (p = copied_text; *p != '\0'; p = g_utf8_next_char (p)) {
++        gunichar c = g_utf8_get_char (p);
++        gboolean has_glyphs = FALSE;
++        buff[n].ch = c;
++        if ((c == 0xfe0eu || c == 0xfe0fu) && n > 0) {
++            buff[n].fcfont = buff[n-1].fcfont;
++            ++n;
++            continue;
++        }
++        for (i = 0; i < m_fcfontset->nfont; i++) {
++            if (g_unichar_iscntrl (c) && !g_unichar_isspace (c))
++                break;
++            FT_Face ft_face = ibus_fontset_get_ftface_from_fcfont (
++                    fontset,
++                    m_fcfontset->fonts[i]);
++            if (FT_Get_Char_Index (ft_face, c) != 0) {
++                buff[n].fcfont = m_fcfontset->fonts[i];
++                if (n > 0 && buff[n - 1].fcfont != buff[n].fcfont) {
++                    get_string_extents_with_font (str->str,
++                                                  &buff[n - 1],
++                                                  widest,
++                                                  &cairo_lines);
++                    g_string_free (str, TRUE);
++                    str = g_string_new (NULL);
++                    g_string_append_unichar (str, c);
++                } else {
++                    g_string_append_unichar (str, c);
++                }
++                ++n;
++                has_glyphs = TRUE;
++                FT_Done_Face (ft_face);
++                break;
++            }
++            FT_Done_Face (ft_face);
++        }
++        if (!has_glyphs) {
++            if (n > 0) {
++                buff[n].fcfont = buff[n - 1].fcfont;
++            } else {
++                /* Search a font for non-glyph char to draw the code points
++                 * likes Pango.
++                 */
++                for (i = 0; i < m_fcfontset->nfont; i++) {
++                    FT_Face ft_face = ibus_fontset_get_ftface_from_fcfont (
++                            fontset,
++                            m_fcfontset->fonts[i]);
++                    /* Check alphabets instead of space or digits
++                     * because 'Noto Emoji Color' font's digits are
++                     * white color and cannot change the font color.
++                     * the font does not have alphabets.
++                     */
++                    if (FT_Get_Char_Index (ft_face, 'A') != 0) {
++                        buff[n].fcfont = m_fcfontset->fonts[i];
++                        FT_Done_Face (ft_face);
++                        has_glyphs = TRUE;
++                        break;
++                    }
++                    FT_Done_Face (ft_face);
++                }
++                if (!has_glyphs) {
++                    buff[n].fcfont = m_fcfontset->fonts[0];
++                    g_warning ("Not found fonts for unicode %04X at %d in %s",
++                               c, n, text);
++                }
++            }
++            n++;
++            g_string_append_unichar (str, c);
++        }
++    }
++    if (str->str) {
++        get_string_extents_with_font (str->str,
++                                      &buff[n - 1],
++                                      widest,
++                                      &cairo_lines);
++        g_string_free (str, TRUE);
++    }
++    g_slice_free1 (sizeof (FontPerChar) * text_length, buff);
++    g_free (copied_text);
++    widest->width += XPAD * 2;
++    widest->height += YPAD * 2;
++    req = g_new0 (IBusRequisitionEx, 1);
++    req->width = widest->width;
++    req->height = widest->height;
++    req->cairo_lines = cairo_lines;
++    return req;
++}
++
++void
++ibus_fontset_draw_cairo_with_requisition_ex (IBusFontSet       *fontset,
++                                             cairo_t           *cr,
++                                             IBusRequisitionEx *ex)
++{
++    IBusCairoLine *cairo_lines;
++    int i;
++
++    g_return_if_fail (IBUS_IS_FONTSET (fontset));
++    g_return_if_fail (cr != NULL);
++    g_return_if_fail (ex != NULL);
++
++    cairo_lines = ex->cairo_lines;
++    g_return_if_fail (cairo_lines != NULL);
++
++    for (i = 0; cairo_lines[i].scaled_font; i++) {
++        const cairo_glyph_t *glyphs = (cairo_glyph_t *) cairo_lines[i].glyphs;
++        guint num_glyphs = cairo_lines[i].num_glyphs;
++
++        cairo_ft_scaled_font_lock_face (cairo_lines[i].scaled_font);
++        cairo_set_scaled_font (cr, cairo_lines[i].scaled_font);
++        if (num_glyphs > 0 && glyphs[0].index & PANGO_GLYPH_UNKNOWN_FLAG) {
++            _cairo_show_unknown_glyphs (cr, glyphs, num_glyphs,
++                                        ex->width, ex->height);
++        } else {
++            cairo_show_glyphs (cr, glyphs, num_glyphs);
++        }
++        cairo_ft_scaled_font_unlock_face (cairo_lines[i].scaled_font);
++    }
++}
+diff --git a/ui/gtk3/ibusfontset.h b/ui/gtk3/ibusfontset.h
+new file mode 100644
+index 00000000..efcaa286
+--- /dev/null
++++ b/ui/gtk3/ibusfontset.h
+@@ -0,0 +1,302 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* ibus - The Input Bus
++ * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2017 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
++ * 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, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
++ * USA
++ */
++
++#ifndef __IBUS_HARFBUZZ_H_
++#define __IBUS_HARFBUZZ_H_
++
++/**
++ * SECTION: ibusfontset
++ * @short_description: Object for HarfBuzz and Fontconfig.
++ * @title: IBusFontSet
++ * @stability: Unstable
++ *
++ * IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering
++ * on Cairo context.
++ * Current Pango changes fonts by emoji variants and draws the separated
++ * glyphs [1] but actually the emoji characters with variants can be drawn
++ * as one glyph so this class  manages Fontconfig fontsets to select a font,
++ * HarfBuzz to get glyphs for emoji variants, Cairo to draw glyphs.
++ *
++ * [1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669
++ *      https://bugzilla.gnome.org/show_bug.cgi?id=781123
++ */
++
++#include <ibus.h>
++#include <cairo.h>
++
++#define IBUS_TYPE_CAIRO_LINE (ibus_cairo_line_get_type ())
++#define IBUS_TYPE_REQUISITION_EX (ibus_requisition_ex_get_type ())
++#define IBUS_TYPE_FONTSET (ibus_fontset_get_type ())
++#define IBUS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_CAST (\
++        (obj), \
++        IBUS_TYPE_FONTSET, \
++        IBusFontSet))
++#define IBUS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (\
++        (klass), \
++        IBUS_TYPE_FONTSET, \
++        IBusFontSetClass))
++#define IBUS_IS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (\
++        (obj), \
++        IBUS_TYPE_FONTSET))
++#define IBUS_IS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (\
++        (klass), \
++        IBUS_TYPE_FONTSET))
++#define IBUS_FONTSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (\
++        (obj), \
++        IBUS_TYPE_FONTSET, \
++        IBusFontSetClass))
++
++G_BEGIN_DECLS
++
++typedef struct _IBusGlyph IBusGlyph;
++typedef struct _IBusCairoLine IBusCairoLine;
++typedef struct _IBusRequisitionEx IBusRequisitionEx;
++typedef struct _IBusFontSet IBusFontSet;
++typedef struct _IBusFontSetPrivate IBusFontSetPrivate;
++typedef struct _IBusFontSetClass IBusFontSetClass;
++
++struct _IBusGlyph {
++    unsigned long index;
++    double               x;
++    double               y;
++};
++
++struct _IBusCairoLine {
++    IBusGlyph           *glyphs;
++    guint                num_glyphs;
++    cairo_scaled_font_t *scaled_font;
++    gpointer             pdummy[5];
++};
++
++struct _IBusRequisitionEx {
++    guint                width;
++    guint                height;
++    IBusCairoLine       *cairo_lines;
++    gpointer             pdummy[5];
++};
++
++struct _IBusFontSet {
++    IBusObject           parent_instance;
++    IBusFontSetPrivate  *priv;
++};
++
++struct _IBusFontSetClass {
++    IBusObjectClass parent_class;
++    /* signals */
++    /*< private >*/
++    /* padding */
++    gpointer        pdummy[10];
++};
++
++GType           ibus_cairo_line_get_type        (void) G_GNUC_CONST;
++
++/**
++ * ibus_cairo_line_copy:
++ * @cairo_lines: #IBusCairoLine
++ *
++ * Creates a copy of @cairo_liens, which should be freed with
++ * ibus_cairo_line_free(). Primarily used by language bindings,
++ * not that useful otherwise (since @req can just be copied
++ * by assignment in C).
++ *
++ * Returns: the newly allocated #IBusCairoLine, which should
++ *          be freed with ibus_cairo_line_free(), or %NULL
++ *          if @cairo_lines was %NULL.
++ **/
++IBusCairoLine * ibus_cairo_line_copy            (IBusCairoLine *cairo_lines);
++
++/**
++ * ibus_cairo_line_free:
++ * @cairo_lines: #IBusCairoLine
++ *
++ * Free an #IBusCairoLine.
++ */
++void            ibus_cairo_line_free            (IBusCairoLine *cairo_lines);
++
++
++GType           ibus_requisition_ex_get_type    (void) G_GNUC_CONST;
++
++/**
++ * ibus_requisition_ex_copy:
++ * @req: #IBusRequisitionEx
++ *
++ * Creates a copy of @req, which should be freed with
++ * ibus_requisition_ex_free(). Primarily used by language bindings,
++ * not that useful otherwise (since @req can just be copied
++ * by assignment in C).
++ *
++ * Returns: the newly allocated #IBusRequisitionEx, which should
++ *          be freed with ibus_requisition_ex_free(), or %NULL
++ *          if @req was %NULL.
++ **/
++IBusRequisitionEx *
++                ibus_requisition_ex_copy        (IBusRequisitionEx *req);
++
++/**
++ * ibus_requisition_ex_free:
++ * @req: #IBusRequisitionEx
++ *
++ * Free an #IBusRequisitionEx.
++ */
++void            ibus_requisition_ex_free        (IBusRequisitionEx *req);
++
++
++GType           ibus_fontset_get_type           (void);
++
++/**
++ * ibus_fontset_new:
++ * @first_property_name: 
++ *
++ * Creates a new #IBusFcFontSet.
++ *
++ * Returns: (transfer full): A newly allocated #IBusFontSet and includes
++ * #FcFontSet internally. E.g. ibus_fontset_new ("family",
++ * "Noto Emoji Color", "size", 16, "language", "ja-jp");
++ */
++IBusFontSet *   ibus_fontset_new                (const gchar
++                                                           *first_property_name,
++                                                                 ...);
++
++/**
++ * ibus_fontset_new_with_font:
++ * @family: font family
++ * @size: font size
++ * @language: font language
++ *
++ * Creates  a new #IBusFcFontSet.
++ *
++ * Returns: (transfer full): A newly allocated #IBusFcFontSet and includes
++ * #FcFontSet internally.
++ */
++IBusFontSet *   ibus_fontset_new_with_font      (const gchar    *family,
++                                                 guint           size,
++                                                 const gchar    *language);
++/**
++ * ibus_fontset_get_family:
++ * @fontset: #IBusFcFontSet
++ *
++ * Return the base font family of #FcFontSet
++ *
++ * Returns: Base font family of #FcFontSet
++ */
++const gchar *   ibus_fontset_get_family         (IBusFontSet    *fontset);
++
++/**
++ * ibus_fontset_set_family:
++ * @fontset: #IBusFcFontSet
++ * @family: base font family for #FcFontSet
++ *
++ * Set the base font family for #FcFontSet
++ */
++void            ibus_fontset_set_family         (IBusFontSet    *fontset,
++                                                 const gchar    *family);
++/**
++ * ibus_fontset_get_size:
++ * @fontset: #IBusFcFontSet
++ *
++ * Return the font size of #FcFontSet
++ *
++ * Returns: Font size of #FcFontSet
++ */
++guint           ibus_fontset_get_size           (IBusFontSet    *fontset);
++
++/**
++ * ibus_fontset_set_size:
++ * @fontset: #IBusFcFontSet
++ * @size: font size for #FcFontSet
++ *
++ * Set the font size for #FcFontSet
++ */
++void            ibus_fontset_set_size           (IBusFontSet    *fontset,
++                                                 guint           size);
++/**
++ * ibus_fontset_get_language:
++ * @fontset: #IBusFcFontSet
++ *
++ * Return the font language of #FcFontSet
++ *
++ * Returns: Font language of #FcFontSet
++ */
++const gchar *   ibus_fontset_get_language       (IBusFontSet    *fontset);
++
++/**
++ * ibus_fontset_set_language:
++ * @fontset: #IBusFcFontSet
++ * @language: font langauge for #FcFontSet
++ *
++ * Set the font language for #FcFontSet
++ */
++void            ibus_fontset_set_language       (IBusFontSet    *fontset,
++                                                 const gchar    *language);
++
++/**
++ * ibus_fontset_update_fcfontset:
++ * @fontset: #IBusFcFontSet
++ *
++ * Update #FcFontSet from font family, size and langauge of @fontset.
++ * Returns: %TRUE if #FcFontSet is updated. %FALSE otherwise.
++ */
++gboolean        ibus_fontset_update_fcfontset   (IBusFontSet    *fontset);
++
++/**
++ * ibus_fontset_get_preferred_size_hb:
++ * @fontset: #IBusFcFontSet
++ * @text: a string to be calculate the preferred rectangle size.
++ * @widest: (out): #cairo_rectangle_int_t is updated.
++ *
++ * Calculate @widest for @text.
++ *
++ * Returns: #IBusRequisitionEx which includes the glyphs and coordinates.
++ */
++IBusRequisitionEx *
++                ibus_fontset_get_preferred_size_hb
++                                                (IBusFontSet    *fontset,
++                                                 const gchar    *text,
++                                                 cairo_rectangle_int_t
++                                                                *widest);
++
++/**
++ * ibus_fontset_draw_cairo_lines:
++ * @fontset: #IBusFcFontSet
++ * @cr: #cairo_t in #GtkWidget.draw().
++ * @ex: #IBusRequisitionEx which includes glyph, x, y values, char width
++ *      and height.
++ *
++ * Draw glyphs in @ex using cairo @cr.
++ */
++void            ibus_fontset_draw_cairo_with_requisition_ex
++                                                (IBusFontSet    *fontset,
++                                                 cairo_t        *cr,
++                                                 IBusRequisitionEx
++                                                                *ex);
++
++/**
++ * ibus_fontset_unref:
++ * @fontset: #IBusFcFontSet
++ *
++ * Call g_object_unref().
++ * FIXME: Seems Vala needs this API.
++ */
++void            ibus_fontset_unref              (IBusFontSet    *fontset);
++
++G_END_DECLS
++#endif
+-- 
+2.13.4
+

diff --git a/ibus.spec b/ibus.spec
index 5dd1616..fc47167 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -30,7 +30,7 @@
 
 Name:           ibus
 Version:        1.5.16
-Release:        8%{?dist}
+Release:        9%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPLv2+
 Group:          System Environment/Libraries
@@ -42,8 +42,12 @@ Source2:        %{name}.conf.5
 # Upstreamed patches.
 # Patch0:         %%{name}-HEAD.patch
 Patch0:         %{name}-HEAD.patch
-# Under testing #1349148 #1385349 #1350291
+# Under testing #1349148 #1385349 #1350291 #1406699 #1432252
 Patch1:         %{name}-1385349-segv-bus-proxy.patch
+%if %with_emoji_harfbuzz
+# Under testing self rendering until Pango, Fontconfig, Cairo are stable
+Patch2:         %{name}-xx-emoji-harfbuzz.patch
+%endif
 
 BuildRequires:  gettext-devel
 BuildRequires:  libtool
@@ -238,7 +242,10 @@ The ibus-devel-docs package contains developer documentation for IBus
 # %%patch0 -p1
 # cp client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c ||
 %patch0 -p1
-%patch1 -p1
+%patch1 -p1 -z .segv
+%if %with_emoji_harfbuzz
+%patch2 -p1 -z .hb
+%endif
 
 %build
 #autoreconf -f -i -v
@@ -375,6 +382,7 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
 %{_datadir}/man/man7/ibus-emoji.7.gz
 %{_libexecdir}/ibus-engine-simple
 %{_libexecdir}/ibus-dconf
+%{_libexecdir}/ibus-portal
 %{_libexecdir}/ibus-ui-emojier
 %{_libexecdir}/ibus-ui-gtk3
 %{_libexecdir}/ibus-x11
@@ -433,6 +441,13 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
 %{_datadir}/gtk-doc/html/*
 
 %changelog
+* Thu Sep 14 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.16-9
+- Fix scaling factor, mouse events on switcher, c-s-u on im-ibus, 
+  propertypanel position and menu 
+- Add ibus-portal
+- Move ibus-emoji-dialog.vapi in the build
+- Fixed some SEGVs #1406699 #1432252
+
 * Wed Aug 02 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.5.16-8
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
 

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-05-31  2:06 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31  2:06 [rpms/ibus] autotool: Fixed several bugs Takao Fujiwara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox