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