From 3818b86c29bd09b4beecebbaf0e26ef8ae9ff36b Mon Sep 17 00:00:00 2001 From: Takao Fujiwara Date: Sep 14 2017 10:10:26 +0000 Subject: 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 -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 -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 +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 . --- - .../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 --# Copyright (c) 2007-2016 Red Hat, Inc. -+# Copyright (c) 2017 Takao Fujiwara -+# 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 +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 . +--- + 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 +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 . +--- + 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 ++# 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>? - 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 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +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 + * 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 -+#include -+#include -+#include FT_FREETYPE_H ++#include ++#include +#include -+#include -+#include -+ -+#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 +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 . +--- + 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 -+ * 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 -+#include -+ -+#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 +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 . +--- + 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 +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 +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 +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 +-# Copyright (c) 2007-2016 Red Hat, Inc. ++# Copyright (c) 2017 Takao Fujiwara ++# 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 +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 m_xkb_languages = + new GLib.HashTable(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 +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 +- * Copyright (C) 2008-2013 Red Hat, Inc. ++ * Copyright (C) 2015-2017 Takao Fujiwara ++ * 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 +- * Copyright (C) 2015-2016 Takao Fujiwara +- * Copyright (C) 2008-2016 Red Hat, Inc. ++ * Copyright (C) 2015-2017 Takao Fujiwara ++ * 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 +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 +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 +- * Copyright(c) 2015-2016 Takao Fujiwara ++ * Copyright(c) 2015-2017 Takao Fujiwara + * + * 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 +- * Copyright(c) 2016 Takao Fujiwara ++ * Copyright(c) 2016-2017 Takao Fujiwara + * + * 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 ++ * Copyright(c) 2015-2017 Takao Fujiwara + * 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 +- * Copyright(c) 2013-2016 Takao Fujiwara ++ * Copyright(c) 2013-2017 Takao Fujiwara + * + * 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 +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 +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>? + 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 ++ * 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 ++#include ++#include ++#include FT_FREETYPE_H ++#include ++#include ++#include ++ ++#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 ++ * 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 ++#include ++ ++#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 - 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 - 1.5.16-8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild