diff --git a/.gitignore b/.gitignore index cb853da..627f1d9 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ /ibus-po-1.5.9-20141001.tar.gz /ibus-po-1.5.10-20150402.tar.gz /ibus-po-1.5.14-20160909.tar.gz +/ibus-po-1.5.17-20180221.tar.gz ibus-1.3.6.tar.gz /ibus-1.3.7.tar.gz /ibus-1.3.8.tar.gz diff --git a/ibus-1385349-segv-bus-proxy.patch b/ibus-1385349-segv-bus-proxy.patch index a9c1a73..02912a5 100644 --- a/ibus-1385349-segv-bus-proxy.patch +++ b/ibus-1385349-segv-bus-proxy.patch @@ -1,6 +1,6 @@ -From 8ea0d3f25078c612b4b16c955c1c0c17e764d8c5 Mon Sep 17 00:00:00 2001 +From 4ad2f160e2af0b71148b3f7726e71f26a107ff1c Mon Sep 17 00:00:00 2001 From: fujiwarat -Date: Thu, 27 Jul 2017 18:56:01 +0900 +Date: Wed, 21 Feb 2018 15:05:18 +0900 Subject: [PATCH] bus: Fix SEGV in bus_panel_proxy_focus_in() BUG=rhbz#1349148 @@ -10,11 +10,11 @@ BUG=rhbz#1406699 BUG=rhbz#1432252 --- bus/dbusimpl.c | 38 ++++++++++++++++++++++++++++++++------ - bus/ibusimpl.c | 22 +++++++++++++++++++--- - 2 files changed, 51 insertions(+), 9 deletions(-) + bus/ibusimpl.c | 21 ++++++++++++++++++--- + 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c -index b54ef81..e4dd868 100644 +index b54ef817..e4dd8683 100644 --- a/bus/dbusimpl.c +++ b/bus/dbusimpl.c @@ -2,7 +2,8 @@ @@ -124,41 +124,37 @@ index b54ef81..e4dd868 100644 if (incoming) { /* is incoming message */ diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c -index f99307a..2d43ff6 100644 +index 58d205cf..34f6c909 100644 --- a/bus/ibusimpl.c +++ b/bus/ibusimpl.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 -@@ -323,11 +324,14 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, - g_assert (new_name != NULL); - g_assert (BUS_IS_IBUS_IMPL (ibus)); +@@ -357,13 +357,16 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, + else if (!g_strcmp0 (name, IBUS_SERVICE_PANEL_EXTENSION)) + panel_type = PANEL_TYPE_EXTENSION; -- if (g_strcmp0 (name, IBUS_SERVICE_PANEL) == 0) { +- if (panel_type != PANEL_TYPE_NONE) { + do { -+ if (g_strcmp0 (name, IBUS_SERVICE_PANEL) != 0) ++ if (panel_type == PANEL_TYPE_NONE) + break; if (g_strcmp0 (new_name, "") != 0) { /* a Panel process is started. */ BusConnection *connection; BusInputContext *context = NULL; + BusPanelProxy **panel = (panel_type == PANEL_TYPE_PANEL) ? + &ibus->panel : &ibus->extension; + GDBusConnection *dbus_connection = NULL; - if (ibus->panel != NULL) { - ibus_proxy_destroy ((IBusProxy *) ibus->panel); -@@ -338,6 +342,18 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, - connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name); + if (*panel != NULL) { + ibus_proxy_destroy ((IBusProxy *)(*panel)); +@@ -372,9 +375,21 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, + g_assert (*panel == NULL); + } + +- connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name); ++ connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, ++ new_name); g_return_if_fail (connection != NULL); + dbus_connection = bus_connection_get_dbus_connection (connection); -+ + /* rhbz#1349148 rhbz#1385349 + * Avoid SEGV of BUS_IS_PANEL_PROXY (ibus->panel) + * This function is called during destroying the connection @@ -169,10 +165,10 @@ index f99307a..2d43ff6 100644 + break; + } + - ibus->panel = bus_panel_proxy_new (connection); + *panel = bus_panel_proxy_new (connection, panel_type); - g_signal_connect (ibus->panel, -@@ -366,7 +382,7 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, + g_signal_connect (*panel, +@@ -406,7 +421,7 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, } } } @@ -182,5 +178,5 @@ index f99307a..2d43ff6 100644 bus_ibus_impl_component_name_owner_changed (ibus, name, old_name, new_name); } -- -2.9.3 +2.14.3 diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch index 6b21ef5..8d1fa9f 100644 --- a/ibus-HEAD.patch +++ b/ibus-HEAD.patch @@ -4442,3 +4442,4184 @@ index 555ea68f..0bf34da8 100644 -- 2.14.3 +From fb07f64764f18f702221ff5574b2fd2193f051f0 Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Tue, 20 Feb 2018 17:25:07 +0900 +Subject: [PATCH] Implement ibus-extension-gtk3 for the global keybinding + +Currently IBus panel (ibus-ui-gtk3) is not available in GNOME and Plasma +so the emoji and unicode point typings are not available in GNOME and Plasma. +The workaround `ibus emoji` command is available but it put the selected +character into the copy buffer and users have to paste the character. + +Originaly the emoji feature was implemented in IBus GtkIMModule but +it had several problems; the first is the keybinding is hard-coded +and IBus GtkIMModule does not use GSettings for the customized settings. +The second is the feature was available for GTK applications. +The third is that XKB input sources uses gtk-im-context-simple +but not ibus in GNOME desktop so users have to add an IM input sources +to enable IBus for the XKB input sources. The fourth is the feature +was available for IBusEngineSimple only and other IBus IMEs need to +inherit that class to get the emoji feature. The fifth is that +emoji typing is available for English only since IBusEngineSimple +had the feature. The sixth is that the default one dimension lookup +window was not useful to choose an emoji and needed two dimensions +lookup window. + +And the implementation was moved from IBus GtkIMModule to IBus panel +to fix above problems. +But users have to use `ibus emoji` at present if ibus-ui-gtk3 +is not available. + +Now I think to move the emoji feature from ibus-ui-gtk3 to another +IBus component; ibus-extension-gtk3 which manages the Ctrl-Shift-e. +GNOME and Plasma desktops still do not show the GUI menu but +the shortcut key is available in this implementation. + +BUG=RHBZ#1430501 +R=Shawn.P.Huang@gmail.com + +Review URL: https://codereview.appspot.com/339300043 +--- + bindings/vala/IBus-1.0-custom.vala | 4 + + bus/Makefile.am | 7 +- + bus/ibusimpl.c | 82 ++- + bus/main.c | 25 +- + bus/marshalers.list | 1 + + bus/panelproxy.c | 57 +- + bus/panelproxy.h | 20 +- + src/Makefile.am | 4 +- + src/ibus.h | 1 + + src/ibusmarshalers.list | 1 + + src/ibuspanelservice.c | 81 ++- + src/ibuspanelservice.h | 15 +- + src/ibusserializable.c | 7 +- + src/ibusserializable.h | 18 +- + src/ibusshare.c | 4 +- + src/ibusshare.h | 17 +- + src/ibusxevent.c | 1004 ++++++++++++++++++++++++++++++++++++ + src/ibusxevent.h | 294 +++++++++++ + src/tests/runtest | 1 + + ui/gtk3/Makefile.am | 59 ++- + ui/gtk3/bindingcommon.vala | 215 ++++++++ + ui/gtk3/candidatearea.vala | 102 ---- + ui/gtk3/extension.vala | 124 +++++ + ui/gtk3/gtkextension.xml.in | 12 + + ui/gtk3/iconwidget.vala | 103 ++++ + ui/gtk3/panel.vala | 408 +++------------ + ui/gtk3/panelbinding.vala | 335 ++++++++++++ + 27 files changed, 2506 insertions(+), 495 deletions(-) + create mode 100644 src/ibusxevent.c + create mode 100644 src/ibusxevent.h + create mode 100644 ui/gtk3/bindingcommon.vala + create mode 100644 ui/gtk3/extension.vala + create mode 100644 ui/gtk3/gtkextension.xml.in + create mode 100644 ui/gtk3/panelbinding.vala + +diff --git a/bindings/vala/IBus-1.0-custom.vala b/bindings/vala/IBus-1.0-custom.vala +index 144d75e2..cf1fc3fa 100644 +--- a/bindings/vala/IBus-1.0-custom.vala ++++ b/bindings/vala/IBus-1.0-custom.vala +@@ -6,4 +6,8 @@ namespace IBus { + [CCode (cname = "ibus_text_new_from_static_string", has_construct_function = false)] + public Text.from_static_string (string str); + } ++ public class XEvent : IBus.Serializable { ++ [CCode (cname = "ibus_x_event_new", has_construct_function = true)] ++ public XEvent (string first_property_name, ...); ++ } + } +diff --git a/bus/Makefile.am b/bus/Makefile.am +index 864ba923..8bcc8e16 100644 +--- a/bus/Makefile.am ++++ b/bus/Makefile.am +@@ -3,7 +3,8 @@ + # ibus - The Input Bus + # + # Copyright (c) 2007-2013 Peng Huang +-# Copyright (c) 2007-2013 Red Hat, Inc. ++# Copyright (c) 2013-2018 Takao Fujiwara ++# Copyright (c) 2007-2018 Red Hat, Inc. + # + # This library is free software; you can redistribute it and/or + # modify it under the terms of the GNU Lesser General Public +@@ -105,6 +106,10 @@ marshalers.c: marshalers.h marshalers.list + $(GLIB_GENMARSHAL) --prefix=bus_marshal $(srcdir)/marshalers.list --body --internal) > $@.tmp && \ + mv $@.tmp $@ + ++if ENABLE_EMOJI_DICT ++AM_CFLAGS += -DEMOJI_DICT ++endif ++ + + if ENABLE_TESTS + TESTS = \ +diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c +index f99307ad..58d205cf 100644 +--- a/bus/ibusimpl.c ++++ b/bus/ibusimpl.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) 2011-2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -73,6 +74,7 @@ struct _BusIBusImpl { + + BusInputContext *focused_context; + BusPanelProxy *panel; ++ BusPanelProxy *extension; + + /* a default keymap of ibus-daemon (usually "us") which is used only + * when use_sys_layout is FALSE. */ +@@ -290,12 +292,37 @@ _panel_destroy_cb (BusPanelProxy *panel, + g_assert (BUS_IS_PANEL_PROXY (panel)); + g_assert (BUS_IS_IBUS_IMPL (ibus)); + +- g_return_if_fail (ibus->panel == panel); +- +- ibus->panel = NULL; ++ if (ibus->panel == panel) ++ ibus->panel = NULL; ++ else if (ibus->extension == panel) ++ ibus->extension = NULL; ++ else ++ g_return_if_reached (); + g_object_unref (panel); + } + ++static void ++_panel_panel_extension_cb (BusPanelProxy *panel, ++ GVariant *parameters, ++ BusIBusImpl *ibus) ++{ ++ if (!ibus->extension) { ++ g_warning ("Panel extension is not running."); ++ return; ++ } ++ ++ g_return_if_fail (BUS_IS_IBUS_IMPL (ibus)); ++ g_return_if_fail (BUS_IS_PANEL_PROXY (ibus->extension)); ++ ++ /* Use the DBus method because it seems any DBus signal, ++ * g_dbus_message_new_signal(), cannot be reached to the server. */ ++ g_dbus_proxy_call (G_DBUS_PROXY (ibus->extension), ++ "PanelExtensionReceived", ++ parameters, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, NULL, NULL, NULL); ++} ++ + static void + _registry_changed_cb (IBusRegistry *registry, + BusIBusImpl *ibus) +@@ -317,33 +344,47 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, + const gchar *new_name, + BusIBusImpl *ibus) + { ++ PanelType panel_type = PANEL_TYPE_NONE; ++ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (name != NULL); + g_assert (old_name != NULL); + g_assert (new_name != NULL); + g_assert (BUS_IS_IBUS_IMPL (ibus)); + +- if (g_strcmp0 (name, IBUS_SERVICE_PANEL) == 0) { ++ if (!g_strcmp0 (name, IBUS_SERVICE_PANEL)) ++ panel_type = PANEL_TYPE_PANEL; ++ else if (!g_strcmp0 (name, IBUS_SERVICE_PANEL_EXTENSION)) ++ panel_type = PANEL_TYPE_EXTENSION; ++ ++ if (panel_type != PANEL_TYPE_NONE) { + if (g_strcmp0 (new_name, "") != 0) { + /* a Panel process is started. */ + BusConnection *connection; + BusInputContext *context = NULL; +- +- if (ibus->panel != NULL) { +- ibus_proxy_destroy ((IBusProxy *) ibus->panel); +- /* panel should be NULL after destroy. See _panel_destroy_cb for details. */ +- g_assert (ibus->panel == NULL); ++ BusPanelProxy **panel = (panel_type == PANEL_TYPE_PANEL) ? ++ &ibus->panel : &ibus->extension; ++ ++ if (*panel != NULL) { ++ ibus_proxy_destroy ((IBusProxy *)(*panel)); ++ /* panel should be NULL after destroy. See _panel_destroy_cb ++ * for details. */ ++ g_assert (*panel == NULL); + } + + connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name); + g_return_if_fail (connection != NULL); + +- ibus->panel = bus_panel_proxy_new (connection); ++ *panel = bus_panel_proxy_new (connection, panel_type); + +- g_signal_connect (ibus->panel, ++ g_signal_connect (*panel, + "destroy", + G_CALLBACK (_panel_destroy_cb), + ibus); ++ g_signal_connect (*panel, ++ "panel-extension", ++ G_CALLBACK (_panel_panel_extension_cb), ++ ibus); + + if (ibus->focused_context != NULL) { + context = ibus->focused_context; +@@ -355,14 +396,13 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus, + if (context != NULL) { + BusEngineProxy *engine; + +- bus_panel_proxy_focus_in (ibus->panel, context); ++ bus_panel_proxy_focus_in (*panel, context); + + engine = bus_input_context_get_engine (context); + if (engine != NULL) { + IBusPropList *prop_list = + bus_engine_proxy_get_properties (engine); +- bus_panel_proxy_register_properties (ibus->panel, +- prop_list); ++ bus_panel_proxy_register_properties (*panel, prop_list); + } + } + } +@@ -403,6 +443,7 @@ bus_ibus_impl_init (BusIBusImpl *ibus) + ibus->contexts = NULL; + ibus->focused_context = NULL; + ibus->panel = NULL; ++ ibus->extension = NULL; + + ibus->keymap = ibus_keymap_get ("us"); + +@@ -635,6 +676,8 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, + + if (ibus->panel != NULL) + bus_panel_proxy_focus_out (ibus->panel, ibus->focused_context); ++ if (ibus->extension != NULL) ++ bus_panel_proxy_focus_out (ibus->extension, ibus->focused_context); + + bus_input_context_get_content_type (ibus->focused_context, + &purpose, &hints); +@@ -658,6 +701,8 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, + + if (ibus->panel != NULL) + bus_panel_proxy_focus_in (ibus->panel, context); ++ if (ibus->extension != NULL) ++ bus_panel_proxy_focus_in (ibus->extension, context); + } + + if (engine != NULL) +@@ -846,8 +891,13 @@ _context_destroy_cb (BusInputContext *context, + bus_ibus_impl_set_focused_context (ibus, NULL); + + if (ibus->panel && +- bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) ++ bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) { + bus_panel_proxy_destroy_context (ibus->panel, context); ++ } ++ if (ibus->extension && ++ bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) { ++ bus_panel_proxy_destroy_context (ibus->extension, context); ++ } + + ibus->contexts = g_list_remove (ibus->contexts, context); + g_object_unref (context); +diff --git a/bus/main.c b/bus/main.c +index 6ad60179..5b2589b1 100644 +--- a/bus/main.c ++++ b/bus/main.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) 2013-2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -42,6 +43,7 @@ static gboolean xim = FALSE; + static gboolean replace = FALSE; + static gboolean restart = FALSE; + static gchar *panel = "default"; ++static gchar *panel_extension = "default"; + static gchar *config = "default"; + static gchar *desktop = "gnome"; + +@@ -60,6 +62,7 @@ static const GOptionEntry entries[] = + { "xim", 'x', 0, G_OPTION_ARG_NONE, &xim, "execute ibus XIM server.", NULL }, + { "desktop", 'n', 0, G_OPTION_ARG_STRING, &desktop, "specify the name of desktop session. [default=gnome]", "name" }, + { "panel", 'p', 0, G_OPTION_ARG_STRING, &panel, "specify the cmdline of panel program. pass 'disable' not to start a panel program.", "cmdline" }, ++ { "panel-extension", 'E', 0, G_OPTION_ARG_STRING, &panel_extension, "specify the cmdline of panel extension program. pass 'disable' not to start an extension program.", "cmdline" }, + { "config", 'c', 0, G_OPTION_ARG_STRING, &config, "specify the cmdline of config program. pass 'disable' not to start a config program.", "cmdline" }, + { "address", 'a', 0, G_OPTION_ARG_STRING, &g_address, "specify the address of ibus daemon.", "address" }, + { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "if there is an old ibus-daemon is running, it will be replaced.", NULL }, +@@ -268,7 +271,27 @@ main (gint argc, gchar **argv) + if (!execute_cmdline (panel)) + exit (-1); + } ++ ++#ifdef EMOJI_DICT ++ if (g_strcmp0 (panel_extension, "default") == 0) { ++ BusComponent *component; ++ component = bus_ibus_impl_lookup_component_by_name ( ++ BUS_DEFAULT_IBUS, IBUS_SERVICE_PANEL_EXTENSION); ++ if (component) { ++ bus_component_set_restart (component, restart); ++ } ++ if (component == NULL || ++ !bus_component_start (component, g_verbose)) { ++ g_printerr ("Can not execute default panel program\n"); ++ exit (-1); ++ } ++ } else if (g_strcmp0 (panel_extension, "disable") != 0 && ++ g_strcmp0 (panel_extension, "") != 0) { ++ if (!execute_cmdline (panel_extension)) ++ exit (-1); ++ } + } ++#endif + + /* execute ibus xim server */ + if (xim) { +diff --git a/bus/marshalers.list b/bus/marshalers.list +index c032cdaa..437c6fee 100644 +--- a/bus/marshalers.list ++++ b/bus/marshalers.list +@@ -12,4 +12,5 @@ VOID:STRING + VOID:STRING,INT + VOID:UINT,UINT + VOID:UINT,UINT,UINT ++VOID:VARIANT + VOID:VOID +diff --git a/bus/panelproxy.c b/bus/panelproxy.c +index 8381d7dc..c3908fcf 100644 +--- a/bus/panelproxy.c ++++ b/bus/panelproxy.c +@@ -2,8 +2,8 @@ + /* vim:set et sts=4: */ + /* ibus - The Input Bus + * Copyright (C) 2008-2014 Peng Huang +- * Copyright (C) 2017 Takao Fujiwara +- * Copyright (C) 2008-2014 Red Hat, Inc. ++ * Copyright (C) 2017-2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -51,6 +51,7 @@ enum { + PROPERTY_SHOW, + PROPERTY_HIDE, + COMMIT_TEXT, ++ PANEL_EXTENSION, + LAST_SIGNAL, + }; + +@@ -59,6 +60,7 @@ struct _BusPanelProxy { + + /* instance members */ + BusInputContext *focused_context; ++ PanelType panel_type; + }; + + struct _BusPanelProxyClass { +@@ -110,22 +112,39 @@ static void bus_panel_proxy_commit_text + G_DEFINE_TYPE(BusPanelProxy, bus_panel_proxy, IBUS_TYPE_PROXY) + + BusPanelProxy * +-bus_panel_proxy_new (BusConnection *connection) ++bus_panel_proxy_new (BusConnection *connection, ++ PanelType panel_type) + { ++ const gchar *path = NULL; ++ GObject *obj; ++ BusPanelProxy *panel; ++ + g_assert (BUS_IS_CONNECTION (connection)); + +- GObject *obj; ++ switch (panel_type) { ++ case PANEL_TYPE_PANEL: ++ path = IBUS_PATH_PANEL; ++ break; ++ case PANEL_TYPE_EXTENSION: ++ path = IBUS_PATH_PANEL_EXTENSION; ++ break; ++ default: ++ g_return_val_if_reached (NULL); ++ } ++ + obj = g_initable_new (BUS_TYPE_PANEL_PROXY, + NULL, + NULL, +- "g-object-path", IBUS_PATH_PANEL, ++ "g-object-path", path, + "g-interface-name", IBUS_INTERFACE_PANEL, + "g-connection", bus_connection_get_dbus_connection (connection), + "g-default-timeout", g_gdbus_timeout, + "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL); + +- return BUS_PANEL_PROXY (obj); ++ panel = BUS_PANEL_PROXY (obj); ++ panel->panel_type = panel_type; ++ return panel; + } + + static void +@@ -231,6 +250,16 @@ bus_panel_proxy_class_init (BusPanelProxyClass *class) + bus_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + IBUS_TYPE_TEXT); ++ ++ panel_signals[PANEL_EXTENSION] = ++ g_signal_new (I_("panel-extension"), ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, ++ bus_marshal_VOID__VARIANT, ++ G_TYPE_NONE, 1, ++ G_TYPE_VARIANT); + } + + static void +@@ -337,6 +366,15 @@ bus_panel_proxy_g_signal (GDBusProxy *proxy, + return; + } + ++ if (g_strcmp0 ("PanelExtension", signal_name) == 0) { ++ if (panel->panel_type != PANEL_TYPE_PANEL) { ++ g_warning ("Wrong signal"); ++ return; ++ } ++ g_signal_emit (panel, panel_signals[PANEL_EXTENSION], 0, parameters); ++ return; ++ } ++ + /* shound not be reached */ + g_return_if_reached (); + } +@@ -832,3 +870,10 @@ bus_panel_proxy_destroy_context (BusPanelProxy *panel, + + g_object_unref (context); + } ++ ++PanelType ++bus_panel_proxy_get_panel_type (BusPanelProxy *panel) ++{ ++ g_assert (BUS_IS_PANEL_PROXY (panel)); ++ return panel->panel_type; ++} +diff --git a/bus/panelproxy.h b/bus/panelproxy.h +index 5002f86d..b5a7af17 100644 +--- a/bus/panelproxy.h ++++ b/bus/panelproxy.h +@@ -2,7 +2,8 @@ + /* vim:set et sts=4: */ + /* ibus - The Input Bus + * Copyright (C) 2008-2014 Peng Huang +- * Copyright (C) 2008-2014 Red Hat, Inc. ++ * Copyright (C) 2017-2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -50,12 +51,25 @@ + + G_BEGIN_DECLS + ++typedef enum ++{ ++ PANEL_TYPE_NONE, ++ PANEL_TYPE_PANEL, ++ PANEL_TYPE_EXTENSION ++} PanelType; ++ + typedef struct _BusPanelProxy BusPanelProxy; + typedef struct _BusPanelProxyClass BusPanelProxyClass; + + GType bus_panel_proxy_get_type (void); +-BusPanelProxy *bus_panel_proxy_new (BusConnection *connection); ++BusPanelProxy *bus_panel_proxy_new (BusConnection *connection, ++ PanelType panel_type); + ++gboolean bus_panel_proxy_send_signal (BusPanelProxy *panel, ++ const gchar *interface_name, ++ const gchar *signal_name, ++ GVariant *parameters, ++ GError **error); + /* functions that invoke D-Bus methods of the panel component. */ + void bus_panel_proxy_focus_in (BusPanelProxy *panel, + BusInputContext *context); +@@ -119,6 +133,8 @@ void bus_panel_proxy_set_content_type + (BusPanelProxy *panel, + guint purpose, + guint hints); ++PanelType bus_panel_proxy_get_panel_type ++ (BusPanelProxy *panel); + G_END_DECLS + #endif + +diff --git a/src/Makefile.am b/src/Makefile.am +index 1ba418d8..72ec05ab 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -3,7 +3,7 @@ + # ibus - The Input Bus + # + # Copyright (c) 2007-2015 Peng Huang +-# Copyright (c) 2015-2017 Takao Fujiwara ++# Copyright (c) 2015-2018 Takao Fujiwara + # Copyright (c) 2007-2017 Red Hat, Inc. + # + # This library is free software; you can redistribute it and/or +@@ -103,6 +103,7 @@ ibus_sources = \ + ibustext.c \ + ibusunicode.c \ + ibusutil.c \ ++ ibusxevent.c \ + ibusxml.c \ + $(NULL) + libibus_1_0_la_SOURCES = \ +@@ -155,6 +156,7 @@ ibus_headers = \ + ibustypes.h \ + ibusunicode.h \ + ibusutil.h \ ++ ibusxevent.h \ + ibusxml.h \ + $(NULL) + ibusincludedir = $(includedir)/ibus-@IBUS_API_VERSION@ +diff --git a/src/ibus.h b/src/ibus.h +index 8011729f..b15dded9 100644 +--- a/src/ibus.h ++++ b/src/ibus.h +@@ -59,6 +59,7 @@ + #include + #include + #include ++#include + + #ifndef IBUS_DISABLE_DEPRECATED + #include +diff --git a/src/ibusmarshalers.list b/src/ibusmarshalers.list +index 918bc7f7..8d91937e 100644 +--- a/src/ibusmarshalers.list ++++ b/src/ibusmarshalers.list +@@ -24,4 +24,5 @@ VOID:STRING,STRING,STRING + VOID:UINT + VOID:UINT,POINTER + VOID:POINTER,UINT ++VOID:VARIANT + OBJECT:STRING +diff --git a/src/ibuspanelservice.c b/src/ibuspanelservice.c +index 33949fa1..f37b91c3 100644 +--- a/src/ibuspanelservice.c ++++ b/src/ibuspanelservice.c +@@ -25,6 +25,10 @@ + #include "ibusmarshalers.h" + #include "ibusinternal.h" + ++#define IBUS_PANEL_SERVICE_GET_PRIVATE(o) \ ++ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_PANEL_SERVICE, \ ++ IBusPanelServicePrivate)) ++ + enum { + UPDATE_PREEDIT_TEXT, + UPDATE_AUXILIARY_TEXT, +@@ -52,6 +56,7 @@ enum { + STATE_CHANGED, + DESTROY_CONTEXT, + SET_CONTENT_TYPE, ++ PANEL_EXTENSION_RECEIVED, + LAST_SIGNAL, + }; + +@@ -146,6 +151,9 @@ static void ibus_panel_service_set_content_type + (IBusPanelService *panel, + guint purpose, + guint hints); ++static void ibus_panel_service_panel_extension_received ++ (IBusPanelService *panel, ++ GVariant *data); + + G_DEFINE_TYPE (IBusPanelService, ibus_panel_service, IBUS_TYPE_SERVICE) + +@@ -212,6 +220,9 @@ static const gchar introspection_xml[] = + " " + " " + " " ++ " " ++ " " ++ " " + /* Signals */ + " " + " " +@@ -235,6 +246,9 @@ static const gchar introspection_xml[] = + " " + " " + " " ++ " " ++ " " ++ " " + " " + ""; + +@@ -274,6 +288,8 @@ ibus_panel_service_class_init (IBusPanelServiceClass *class) + class->update_preedit_text = ibus_panel_service_update_preedit_text; + class->update_property = ibus_panel_service_update_property; + class->set_content_type = ibus_panel_service_set_content_type; ++ class->panel_extension_received = ++ ibus_panel_service_panel_extension_received; + + class->cursor_down_lookup_table = ibus_panel_service_not_implemented; + class->cursor_up_lookup_table = ibus_panel_service_not_implemented; +@@ -891,6 +907,30 @@ ibus_panel_service_class_init (IBusPanelServiceClass *class) + 2, + G_TYPE_UINT, + G_TYPE_UINT); ++ ++ /** ++ * IBusPanelService::panel-extension-received: ++ * @panel: An #IBusPanelService ++ * @data: A #GVariant ++ * ++ * Emitted when the client application get the ::panel-extension-received. ++ * Implement the member function ++ * IBusPanelServiceClass::panel_extension_received in extended class to ++ * receive this signal. ++ * ++ * Argument @user_data is ignored in this function. ++ * ++ */ ++ panel_signals[PANEL_EXTENSION_RECEIVED] = ++ g_signal_new (I_("panel-extension-received"), ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (IBusPanelServiceClass, panel_extension_received), ++ NULL, NULL, ++ _ibus_marshal_VOID__VARIANT, ++ G_TYPE_NONE, ++ 1, ++ G_TYPE_VARIANT); + } + + static void +@@ -1088,6 +1128,24 @@ ibus_panel_service_service_method_call (IBusService *service, + return; + } + ++ if (g_strcmp0 (method_name, "PanelExtensionReceived") == 0) { ++ GVariant *variant = NULL; ++ g_variant_get (parameters, "(v)", &variant); ++ if (variant == NULL) { ++ g_dbus_method_invocation_return_error ( ++ invocation, ++ G_DBUS_ERROR, ++ G_DBUS_ERROR_FAILED, ++ "PanelExtensionReceived method gives NULL"); ++ return; ++ } ++ g_signal_emit (panel, panel_signals[PANEL_EXTENSION_RECEIVED], 0, ++ variant); ++ g_variant_unref (variant); ++ g_dbus_method_invocation_return_value (invocation, NULL); ++ return; ++ } ++ + const static struct { + const gchar *name; + const gint signal_id; +@@ -1259,6 +1317,13 @@ ibus_panel_service_set_content_type (IBusPanelService *panel, + ibus_panel_service_not_implemented(panel); + } + ++static void ++ibus_panel_service_panel_extension_received (IBusPanelService *panel, ++ GVariant *data) ++{ ++ ibus_panel_service_not_implemented(panel); ++} ++ + IBusPanelService * + ibus_panel_service_new (GDBusConnection *connection) + { +@@ -1347,6 +1412,21 @@ ibus_panel_service_commit_text (IBusPanelService *panel, + } + } + ++void ++ibus_panel_service_panel_extension (IBusPanelService *panel, ++ GVariant *variant) ++{ ++ g_return_if_fail (IBUS_IS_PANEL_SERVICE (panel)); ++ g_return_if_fail (variant); ++ ++ ibus_service_emit_signal ((IBusService *) panel, ++ NULL, ++ IBUS_INTERFACE_PANEL, ++ "PanelExtension", ++ g_variant_new ("(v)", variant), ++ NULL); ++} ++ + #define DEFINE_FUNC(name, Name) \ + void \ + ibus_panel_service_##name (IBusPanelService *panel) \ +@@ -1364,4 +1444,3 @@ DEFINE_FUNC (cursor_up, CursorUp) + DEFINE_FUNC (page_down, PageDown) + DEFINE_FUNC (page_up, PageUp) + #undef DEFINE_FUNC +- +diff --git a/src/ibuspanelservice.h b/src/ibuspanelservice.h +index a5b13c73..60ef842b 100644 +--- a/src/ibuspanelservice.h ++++ b/src/ibuspanelservice.h +@@ -2,7 +2,7 @@ + /* vim:set et sts=4: */ + /* ibus - The Input Bus + * Copyright (c) 2009-2014 Google Inc. All rights reserved. +- * Copyright (c) 2017 Takao Fujiwara ++ * Copyright (c) 2017-2018 Takao Fujiwara + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -128,6 +128,9 @@ struct _IBusPanelServiceClass { + gint y, + gint w, + gint h); ++ void (* panel_extension_received) ++ (IBusPanelService *panel, ++ GVariant *data); + + /*< private >*/ + /* padding */ +@@ -242,5 +245,15 @@ void ibus_panel_service_property_hide (IBusPanelService *panel, + void ibus_panel_service_commit_text (IBusPanelService *panel, + IBusText *text); + ++/** ++ * ibus_panel_service_panel_extension: ++ * @panel: An #IBusPanelService ++ * @data: (transfer full): A #GVariant data which is sent to a panel extension. ++ * ++ * Notify that a data is sent ++ * by sending a "PanelExtension" message to IBus panel extension service. ++ */ ++void ibus_panel_service_panel_extension (IBusPanelService *panel, ++ GVariant *data); + G_END_DECLS + #endif +diff --git a/src/ibusserializable.c b/src/ibusserializable.c +index d7f867f4..a377b613 100644 +--- a/src/ibusserializable.c ++++ b/src/ibusserializable.c +@@ -2,7 +2,8 @@ + /* vim:set et sts=4: */ + /* ibus - The Input Bus + * Copyright (C) 2008-2010 Peng Huang +- * Copyright (C) 2008-2010 Red Hat, Inc. ++ * Copyright (C) 2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -251,7 +252,7 @@ ibus_serializable_copy (IBusSerializable *object) + } + + GVariant * +-ibus_serializable_serialize (IBusSerializable *object) ++ibus_serializable_serialize_object (IBusSerializable *object) + { + g_return_val_if_fail (IBUS_IS_SERIALIZABLE (object), FALSE); + gboolean retval; +@@ -267,7 +268,7 @@ ibus_serializable_serialize (IBusSerializable *object) + } + + IBusSerializable * +-ibus_serializable_deserialize (GVariant *variant) ++ibus_serializable_deserialize_object (GVariant *variant) + { + g_return_val_if_fail (variant != NULL, NULL); + +diff --git a/src/ibusserializable.h b/src/ibusserializable.h +index 4327eaee..102de1bd 100644 +--- a/src/ibusserializable.h ++++ b/src/ibusserializable.h +@@ -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) 2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -248,10 +249,10 @@ void ibus_serializable_remove_qattachment + * + * See also: IBusSerializableCopyFunc(). + */ +-IBusSerializable *ibus_serializable_copy (IBusSerializable *serializable); ++IBusSerializable *ibus_serializable_copy (IBusSerializable *serializable); + + /** +- * ibus_serializable_serialize: ++ * ibus_serializable_serialize_object: + * @serializable: An #IBusSerializable. + * + * Serialize an #IBusSerializable to a #GVariant. +@@ -261,10 +262,11 @@ IBusSerializable *ibus_serializable_copy (IBusSerializable *ser + * + * See also: IBusSerializableCopyFunc(). + */ +-GVariant *ibus_serializable_serialize (IBusSerializable *serializable); ++GVariant *ibus_serializable_serialize_object ++ (IBusSerializable *serializable); + + /** +- * ibus_serializable_deserialize: ++ * ibus_serializable_deserialize_object: + * @variant: A #GVariant. + * + * Deserialize a #GVariant to an #IBusSerializable/ +@@ -274,7 +276,11 @@ GVariant *ibus_serializable_serialize (IBusSerializable *ser + * + * See also: IBusSerializableCopyFunc(). + */ +-IBusSerializable *ibus_serializable_deserialize (GVariant *variant); ++IBusSerializable *ibus_serializable_deserialize_object ++ (GVariant *variant); ++ ++#define ibus_serializable_serialize ibus_serializable_serialize_object ++#define ibus_serializable_deserialize ibus_serializable_deserialize_object + + G_END_DECLS + #endif +diff --git a/src/ibusshare.c b/src/ibusshare.c +index b793a962..d7724a6b 100644 +--- a/src/ibusshare.c ++++ b/src/ibusshare.c +@@ -2,7 +2,8 @@ + /* vim:set et sts=4: */ + /* ibus - The Input Bus + * Copyright (C) 2008-2010 Peng Huang +- * Copyright (C) 2008-2010 Red Hat, Inc. ++ * Copyright (C) 2015-2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -287,6 +288,7 @@ ibus_init (void) + IBUS_TYPE_ENGINE_DESC; + IBUS_TYPE_OBSERVED_PATH; + IBUS_TYPE_REGISTRY; ++ IBUS_TYPE_X_EVENT; + } + + static GMainLoop *main_loop = NULL; +diff --git a/src/ibusshare.h b/src/ibusshare.h +index f3e2011e..757d915b 100644 +--- a/src/ibusshare.h ++++ b/src/ibusshare.h +@@ -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-2018 Takao Fujiwara ++ * Copyright (C) 2008-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -65,6 +66,13 @@ + */ + #define IBUS_SERVICE_PANEL "org.freedesktop.IBus.Panel" + ++/** ++ * IBUS_SERVICE_PANEL_EXTENSION: ++ * ++ * Address of IBus panel extension service. ++ */ ++#define IBUS_SERVICE_PANEL_EXTENSION "org.freedesktop.IBus.Panel.Extension" ++ + /** + * IBUS_SERVICE_CONFIG: + * +@@ -100,6 +108,13 @@ + */ + #define IBUS_PATH_PANEL "/org/freedesktop/IBus/Panel" + ++/** ++ * IBUS_PATH_PANEL_EXTENSION: ++ * ++ * D-Bus path for IBus panel. ++ */ ++#define IBUS_PATH_PANEL_EXTENSION "/org/freedesktop/IBus/Panel/Extension" ++ + /** + * IBUS_PATH_CONFIG: + * +diff --git a/src/ibusxevent.c b/src/ibusxevent.c +new file mode 100644 +index 00000000..dea80272 +--- /dev/null ++++ b/src/ibusxevent.c +@@ -0,0 +1,1004 @@ ++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ ++/* vim:set et sts=4: */ ++/* ibus - The Input Bus ++ * Copyright (C) 2018 Takao Fujiwara ++ * Copyright (C) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * 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 "ibusinternal.h" ++#include "ibusxevent.h" ++ ++#define IBUS_X_EVENT_VERSION 1 ++#define IBUS_X_EVENT_GET_PRIVATE(o) \ ++ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_X_EVENT, IBusXEventPrivate)) ++ ++enum { ++ PROP_0, ++ PROP_VERSION, ++ PROP_EVENT_TYPE, ++ PROP_WINDOW, ++ PROP_SEND_EVENT, ++ PROP_SERIAL, ++ PROP_TIME, ++ PROP_STATE, ++ PROP_KEYVAL, ++ PROP_LENGTH, ++ PROP_STRING, ++ PROP_HARDWARE_KEYCODE, ++ PROP_GROUP, ++ PROP_IS_MODIFIER, ++ PROP_ROOT, ++ PROP_SUBWINDOW, ++ PROP_X, ++ PROP_Y, ++ PROP_X_ROOT, ++ PROP_Y_ROOT, ++ PROP_SAME_SCREEN, ++ PROP_PURPOSE ++}; ++ ++ ++struct _IBusXEventPrivate { ++ guint version; ++ guint32 time; ++ guint state; ++ guint keyval; ++ gint length; ++ gchar *string; ++ guint16 hardware_keycode; ++ guint8 group; ++ gboolean is_modifier; ++ guint root; ++ guint subwindow; ++ gint x; ++ gint y; ++ gint x_root; ++ gint y_root; ++ gboolean same_screen; ++ gchar *purpose; ++}; ++ ++/* functions prototype */ ++static void ibus_x_event_destroy (IBusXEvent *event); ++static void ibus_x_event_set_property (IBusXEvent *event, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec); ++static void ibus_x_event_get_property (IBusXEvent *event, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec); ++static gboolean ibus_x_event_serialize (IBusXEvent *event, ++ GVariantBuilder *builder); ++static gint ibus_x_event_deserialize (IBusXEvent *event, ++ GVariant *variant); ++static gboolean ibus_x_event_copy (IBusXEvent *dest, ++ const IBusXEvent *src); ++ ++G_DEFINE_TYPE (IBusXEvent, ibus_x_event, IBUS_TYPE_SERIALIZABLE) ++ ++static void ++ibus_x_event_class_init (IBusXEventClass *class) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (class); ++ IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class); ++ IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class); ++ ++ gobject_class->set_property = ++ (GObjectSetPropertyFunc) ibus_x_event_set_property; ++ gobject_class->get_property = ++ (GObjectGetPropertyFunc) ibus_x_event_get_property; ++ ++ object_class->destroy = (IBusObjectDestroyFunc) ibus_x_event_destroy; ++ ++ serializable_class->serialize = ++ (IBusSerializableSerializeFunc) ibus_x_event_serialize; ++ serializable_class->deserialize = ++ (IBusSerializableDeserializeFunc) ibus_x_event_deserialize; ++ serializable_class->copy = ++ (IBusSerializableCopyFunc) ibus_x_event_copy; ++ ++ /* install properties */ ++ /** ++ * IBusXEvent:version: ++ * ++ * Version of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_VERSION, ++ g_param_spec_uint ("version", ++ "version", ++ "version", ++ 0, ++ G_MAXUINT32, ++ IBUS_X_EVENT_VERSION, ++ G_PARAM_READABLE)); ++ ++ /** ++ * IBusXEvent:event-type: ++ * ++ * IBusXEventType of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_EVENT_TYPE, ++ g_param_spec_int ("event-type", ++ "event type", ++ "event type", ++ -1, ++ G_MAXINT32, ++ -1, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:window: ++ * ++ * window of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_WINDOW, ++ g_param_spec_uint ("window", ++ "window", ++ "window", ++ 0, ++ G_MAXUINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:send-event: ++ * ++ * send_event of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_SEND_EVENT, ++ g_param_spec_int ("send-event", ++ "send event", ++ "send event", ++ 0, ++ G_MAXINT8, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:serial: ++ * ++ * serial of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_SERIAL, ++ g_param_spec_ulong ("serial", ++ "serial", ++ "serial", ++ 0, ++ G_MAXUINT64, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:time: ++ * ++ * time of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_TIME, ++ g_param_spec_uint ("time", ++ "time", ++ "time", ++ 0, ++ G_MAXUINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:state: ++ * ++ * state of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_STATE, ++ g_param_spec_uint ("state", ++ "state", ++ "state", ++ 0, ++ G_MAXUINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:keyval: ++ * ++ * keyval of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_KEYVAL, ++ g_param_spec_uint ("keyval", ++ "keyval", ++ "keyval", ++ 0, ++ G_MAXUINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:length: ++ * ++ * keyval of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_LENGTH, ++ g_param_spec_int ("length", ++ "length", ++ "length", ++ -1, ++ G_MAXINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:string: ++ * ++ * string of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_STRING, ++ g_param_spec_string ("string", ++ "string", ++ "string", ++ "", ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:hardware-keycode: ++ * ++ * hardware keycode of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_HARDWARE_KEYCODE, ++ g_param_spec_uint ("hardware-keycode", ++ "hardware keycode", ++ "hardware keycode", ++ 0, ++ G_MAXUINT16, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:group: ++ * ++ * group of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_GROUP, ++ g_param_spec_uint ("group", ++ "group", ++ "group", ++ 0, ++ G_MAXUINT8, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:is-modifier: ++ * ++ * is_modifier of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_IS_MODIFIER, ++ g_param_spec_boolean ("is-modifier", ++ "is modifier", ++ "is modifier", ++ FALSE, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:root: ++ * ++ * root window of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_ROOT, ++ g_param_spec_uint ("root", ++ "root", ++ "root", ++ 0, ++ G_MAXUINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:subwindow: ++ * ++ * subwindow of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_SUBWINDOW, ++ g_param_spec_uint ("subwindow", ++ "subwindow", ++ "subwindow", ++ 0, ++ G_MAXUINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:x: ++ * ++ * x of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_X, ++ g_param_spec_int ("x", ++ "x", ++ "x", ++ G_MININT32, ++ G_MAXINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:y: ++ * ++ * x of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_Y, ++ g_param_spec_int ("y", ++ "y", ++ "y", ++ G_MININT32, ++ G_MAXINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:x-root: ++ * ++ * root-x of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_X_ROOT, ++ g_param_spec_int ("x-root", ++ "x root", ++ "x root", ++ G_MININT32, ++ G_MAXINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:y-root: ++ * ++ * root-y of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_Y_ROOT, ++ g_param_spec_int ("y-root", ++ "y root", ++ "y root", ++ G_MININT32, ++ G_MAXINT32, ++ 0, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:same-screen: ++ * ++ * same_screen of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_SAME_SCREEN, ++ g_param_spec_boolean ("same-screen", ++ "same screen", ++ "same screen", ++ TRUE, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ /** ++ * IBusXEvent:purpose: ++ * ++ * purpose of this IBusXEvent. ++ */ ++ g_object_class_install_property (gobject_class, ++ PROP_PURPOSE, ++ g_param_spec_string ("purpose", ++ "purpose", ++ "purpose", ++ "", ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY)); ++ ++ g_type_class_add_private (class, sizeof (IBusXEventPrivate)); ++} ++ ++static void ++ibus_x_event_init (IBusXEvent *event) ++{ ++ event->priv = IBUS_X_EVENT_GET_PRIVATE (event); ++ event->priv->version = IBUS_X_EVENT_VERSION; ++} ++ ++static void ++ibus_x_event_destroy (IBusXEvent *event) ++{ ++ g_clear_pointer (&event->priv->string, g_free); ++ ++ IBUS_OBJECT_CLASS(ibus_x_event_parent_class)->destroy (IBUS_OBJECT (event)); ++} ++ ++static void ++ibus_x_event_set_property (IBusXEvent *event, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ IBusXEventPrivate *priv = event->priv; ++ ++ switch (prop_id) { ++ case PROP_EVENT_TYPE: ++ event->event_type = g_value_get_int (value); ++ break; ++ case PROP_WINDOW: ++ event->window = g_value_get_uint (value); ++ break; ++ case PROP_SEND_EVENT: ++ event->send_event = g_value_get_int (value); ++ break; ++ case PROP_SERIAL: ++ event->serial = g_value_get_ulong (value); ++ break; ++ case PROP_TIME: ++ priv->time = g_value_get_uint (value); ++ break; ++ case PROP_STATE: ++ priv->state = g_value_get_uint (value); ++ break; ++ case PROP_KEYVAL: ++ priv->keyval = g_value_get_uint (value); ++ break; ++ case PROP_LENGTH: ++ priv->length = g_value_get_int (value); ++ break; ++ case PROP_STRING: ++ g_free (priv->string); ++ priv->string = g_value_dup_string (value); ++ break; ++ case PROP_HARDWARE_KEYCODE: ++ priv->hardware_keycode = g_value_get_uint (value); ++ break; ++ case PROP_GROUP: ++ priv->group = g_value_get_uint (value); ++ break; ++ case PROP_IS_MODIFIER: ++ priv->is_modifier = g_value_get_boolean (value); ++ break; ++ case PROP_ROOT: ++ priv->root = g_value_get_uint (value); ++ break; ++ case PROP_SUBWINDOW: ++ priv->subwindow = g_value_get_uint (value); ++ break; ++ case PROP_X: ++ priv->x = g_value_get_int (value); ++ break; ++ case PROP_Y: ++ priv->y = g_value_get_int (value); ++ break; ++ case PROP_X_ROOT: ++ priv->x_root = g_value_get_int (value); ++ break; ++ case PROP_Y_ROOT: ++ priv->y_root = g_value_get_int (value); ++ break; ++ case PROP_SAME_SCREEN: ++ priv->same_screen = g_value_get_boolean (value); ++ break; ++ case PROP_PURPOSE: ++ g_free (priv->purpose); ++ priv->purpose = g_value_dup_string (value); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (event, prop_id, pspec); ++ } ++} ++ ++static void ++ibus_x_event_get_property (IBusXEvent *event, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ IBusXEventPrivate *priv = event->priv; ++ switch (prop_id) { ++ case PROP_VERSION: ++ g_value_set_uint (value, priv->version); ++ break; ++ case PROP_EVENT_TYPE: ++ g_value_set_int (value, event->event_type); ++ break; ++ case PROP_WINDOW: ++ g_value_set_uint (value, event->window); ++ break; ++ case PROP_SEND_EVENT: ++ g_value_set_int (value, event->send_event); ++ break; ++ case PROP_SERIAL: ++ g_value_set_ulong (value, event->serial); ++ break; ++ case PROP_TIME: ++ g_value_set_uint (value, priv->time); ++ break; ++ case PROP_STATE: ++ g_value_set_uint (value, priv->state); ++ break; ++ case PROP_KEYVAL: ++ g_value_set_uint (value, priv->keyval); ++ break; ++ case PROP_LENGTH: ++ g_value_set_int (value, priv->length); ++ break; ++ case PROP_STRING: ++ g_value_set_string (value, priv->string); ++ break; ++ case PROP_HARDWARE_KEYCODE: ++ g_value_set_uint (value, priv->hardware_keycode); ++ break; ++ case PROP_GROUP: ++ g_value_set_uint (value, priv->group); ++ break; ++ case PROP_IS_MODIFIER: ++ g_value_set_boolean (value, priv->is_modifier); ++ break; ++ case PROP_ROOT: ++ g_value_set_uint (value, priv->root); ++ break; ++ case PROP_SUBWINDOW: ++ g_value_set_uint (value, priv->subwindow); ++ break; ++ case PROP_X: ++ g_value_set_int (value, priv->x); ++ break; ++ case PROP_Y: ++ g_value_set_int (value, priv->y); ++ break; ++ case PROP_X_ROOT: ++ g_value_set_int (value, priv->x_root); ++ break; ++ case PROP_Y_ROOT: ++ g_value_set_int (value, priv->y_root); ++ break; ++ case PROP_SAME_SCREEN: ++ g_value_set_boolean (value, priv->same_screen); ++ break; ++ case PROP_PURPOSE: ++ g_value_set_string (value, priv->purpose); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (event, prop_id, pspec); ++ } ++} ++ ++static gboolean ++ibus_x_event_serialize (IBusXEvent *event, ++ GVariantBuilder *builder) ++{ ++ gboolean retval; ++ IBusXEventPrivate *priv; ++ ++ retval = IBUS_SERIALIZABLE_CLASS (ibus_x_event_parent_class)-> ++ serialize ((IBusSerializable *)event, builder); ++ g_return_val_if_fail (retval, FALSE); ++ /* End dict iter */ ++ ++ priv = event->priv; ++#define NOTNULL(s) ((s) != NULL ? (s) : "") ++ /* If you will add a new property, you can append it at the end and ++ * you should not change the serialized order of name, longname, ++ * description, ... because the order is also used in other applications ++ * likes ibus-qt. */ ++ g_variant_builder_add (builder, "u", priv->version); ++ g_variant_builder_add (builder, "u", event->event_type); ++ g_variant_builder_add (builder, "u", event->window); ++ g_variant_builder_add (builder, "i", event->send_event); ++ g_variant_builder_add (builder, "t", event->serial); ++ g_variant_builder_add (builder, "u", priv->time); ++ g_variant_builder_add (builder, "u", priv->state); ++ g_variant_builder_add (builder, "u", priv->keyval); ++ g_variant_builder_add (builder, "i", priv->length); ++ g_variant_builder_add (builder, "s", NOTNULL (priv->string)); ++ g_variant_builder_add (builder, "u", priv->hardware_keycode); ++ g_variant_builder_add (builder, "u", priv->group); ++ g_variant_builder_add (builder, "b", priv->is_modifier); ++ g_variant_builder_add (builder, "u", priv->root); ++ g_variant_builder_add (builder, "u", priv->subwindow); ++ g_variant_builder_add (builder, "i", priv->x); ++ g_variant_builder_add (builder, "i", priv->y); ++ g_variant_builder_add (builder, "i", priv->x_root); ++ g_variant_builder_add (builder, "i", priv->y_root); ++ g_variant_builder_add (builder, "b", priv->same_screen); ++ g_variant_builder_add (builder, "s", NOTNULL (priv->purpose)); ++#undef NOTNULL ++ ++ return TRUE; ++} ++ ++static gint ++ibus_x_event_deserialize (IBusXEvent *event, ++ GVariant *variant) ++{ ++ gint retval; ++ IBusXEventPrivate *priv; ++ ++ retval = IBUS_SERIALIZABLE_CLASS (ibus_x_event_parent_class)-> ++ deserialize ((IBusSerializable *)event, variant); ++ g_return_val_if_fail (retval, 0); ++ ++ priv = event->priv; ++ /* If you will add a new property, you can append it at the end and ++ * you should not change the serialized order of name, longname, ++ * description, ... because the order is also used in other applications ++ * likes ibus-qt. */ ++ g_variant_get_child (variant, retval++, "u", &priv->version); ++ g_variant_get_child (variant, retval++, "u", &event->event_type); ++ g_variant_get_child (variant, retval++, "u", &event->window); ++ g_variant_get_child (variant, retval++, "i", &event->send_event); ++ g_variant_get_child (variant, retval++, "t", &event->serial); ++ g_variant_get_child (variant, retval++, "u", &priv->time); ++ g_variant_get_child (variant, retval++, "u", &priv->state); ++ g_variant_get_child (variant, retval++, "u", &priv->keyval); ++ g_variant_get_child (variant, retval++, "i", &priv->length); ++ ibus_g_variant_get_child_string (variant, retval++, ++ &priv->string); ++ g_variant_get_child (variant, retval++, "u", &priv->hardware_keycode); ++ g_variant_get_child (variant, retval++, "u", &priv->group); ++ g_variant_get_child (variant, retval++, "b", &priv->is_modifier); ++ g_variant_get_child (variant, retval++, "u", &priv->root); ++ g_variant_get_child (variant, retval++, "u", &priv->subwindow); ++ g_variant_get_child (variant, retval++, "i", &priv->x); ++ g_variant_get_child (variant, retval++, "i", &priv->y); ++ g_variant_get_child (variant, retval++, "i", &priv->x_root); ++ g_variant_get_child (variant, retval++, "i", &priv->y_root); ++ g_variant_get_child (variant, retval++, "b", &priv->same_screen); ++ ibus_g_variant_get_child_string (variant, retval++, ++ &priv->purpose); ++ ++ return retval; ++} ++ ++static gboolean ++ibus_x_event_copy (IBusXEvent *dest, ++ const IBusXEvent *src) ++{ ++ gboolean retval; ++ IBusXEventPrivate *dest_priv = dest->priv; ++ IBusXEventPrivate *src_priv = src->priv; ++ ++ retval = IBUS_SERIALIZABLE_CLASS (ibus_x_event_parent_class)-> ++ copy ((IBusSerializable *)dest, (IBusSerializable *)src); ++ g_return_val_if_fail (retval, FALSE); ++ ++ dest_priv->version = src_priv->version; ++ dest->event_type = src->event_type; ++ dest->window = src->window; ++ dest->send_event = src->send_event; ++ dest->serial = src->serial; ++ dest_priv->time = src_priv->time; ++ dest_priv->state = src_priv->state; ++ dest_priv->keyval = src_priv->keyval; ++ dest_priv->length = src_priv->length; ++ dest_priv->string = g_strdup (src_priv->string); ++ dest_priv->hardware_keycode = src_priv->hardware_keycode; ++ dest_priv->group = src_priv->group; ++ dest_priv->is_modifier = src_priv->is_modifier; ++ dest_priv->root = src_priv->root; ++ dest_priv->subwindow = src_priv->subwindow; ++ dest_priv->x = src_priv->x; ++ dest_priv->y = src_priv->y; ++ dest_priv->x_root = src_priv->x_root; ++ dest_priv->y_root = src_priv->y_root; ++ dest_priv->same_screen = src_priv->same_screen; ++ dest_priv->purpose = g_strdup (src_priv->purpose); ++ ++ return TRUE; ++} ++ ++IBusXEvent * ++ibus_x_event_new (const gchar *first_property_name, ++ ...) ++{ ++ va_list var_args; ++ IBusXEvent *event; ++ ++ va_start (var_args, first_property_name); ++ event = (IBusXEvent *) g_object_new_valist (IBUS_TYPE_X_EVENT, ++ first_property_name, ++ var_args); ++ va_end (var_args); ++ g_assert (event->priv->version != 0); ++ g_assert (event->event_type != IBUS_X_EVENT_NOTHING); ++ return event; ++} ++ ++guint ++ibus_x_event_get_version (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ return event->priv->version; ++} ++ ++IBusXEventType ++ibus_x_event_get_event_type (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ return event->event_type; ++} ++ ++guint32 ++ibus_x_event_get_window (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ return event->window; ++} ++ ++gint8 ++ibus_x_event_get_send_event (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), -1); ++ return event->send_event; ++} ++ ++gulong ++ibus_x_event_get_serial (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ return event->serial; ++} ++ ++guint32 ++ibus_x_event_get_time (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->time; ++} ++ ++guint ++ibus_x_event_get_state (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->state; ++} ++ ++guint ++ibus_x_event_get_keyval (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->keyval; ++} ++ ++gint ++ibus_x_event_get_length (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), -1); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (-1); ++ } ++ return event->priv->length; ++} ++ ++const gchar * ++ibus_x_event_get_string (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), ""); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (""); ++ } ++ return event->priv->string; ++} ++ ++guint16 ++ibus_x_event_get_hardware_keycode (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->hardware_keycode; ++} ++ ++guint8 ++ibus_x_event_get_group (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->group; ++} ++ ++gboolean ++ibus_x_event_get_is_modifier (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->is_modifier; ++} ++ ++guint32 ++ibus_x_event_get_root (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->root; ++} ++ ++guint32 ++ibus_x_event_get_subwindow (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->subwindow; ++} ++ ++gint ++ibus_x_event_get_x (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->x; ++} ++ ++gint ++ibus_x_event_get_y (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->y; ++} ++ ++gint ++ibus_x_event_get_x_root (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->x_root; ++} ++ ++gint ++ibus_x_event_get_y_root (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (0); ++ } ++ return event->priv->y_root; ++} ++ ++gboolean ++ibus_x_event_get_same_screen (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), TRUE); ++ switch (event->event_type) { ++ case IBUS_X_EVENT_KEY_PRESS: ++ case IBUS_X_EVENT_KEY_RELEASE: ++ break; ++ default: ++ g_return_val_if_reached (TRUE); ++ } ++ return event->priv->same_screen; ++} ++ ++const gchar * ++ibus_x_event_get_purpose (IBusXEvent *event) ++{ ++ g_return_val_if_fail (IBUS_IS_X_EVENT (event), ""); ++ return event->priv->purpose; ++} +diff --git a/src/ibusxevent.h b/src/ibusxevent.h +new file mode 100644 +index 00000000..f35f14e4 +--- /dev/null ++++ b/src/ibusxevent.h +@@ -0,0 +1,294 @@ ++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ ++/* vim:set et sts=4: */ ++/* ibus - The Input Bus ++ * Copyright (C) 2018 Takao Fujiwara ++ * Copyright (C) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * 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 ++ */ ++ ++#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION) ++#error "Only can be included directly" ++#endif ++ ++#ifndef __IBUS_X_EVENT_H_ ++#define __IBUS_X_EVENT_H_ ++ ++/** ++ * SECTION: ibusxevent ++ * @short_description: XEvent wrapper object ++ * @title: IBusXEvent ++ * @stability: Unstable ++ * ++ * An IBusXEvent provides a wrapper of XEvent. ++ * ++ * see_also: #IBusComponent, #IBusEngineDesc ++ */ ++ ++#include "ibusserializable.h" ++ ++/* ++ * Type macros. ++ */ ++ ++/* define GOBJECT macros */ ++#define IBUS_TYPE_X_EVENT \ ++ (ibus_x_event_get_type ()) ++#define IBUS_X_EVENT(obj) \ ++ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_X_EVENT, IBusXEvent)) ++#define IBUS_X_EVENT_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_X_EVENT, IBusXEventClass)) ++#define IBUS_IS_X_EVENT(obj) \ ++ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_X_EVENT)) ++#define IBUS_IS_X_EVENT_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_X_EVENT)) ++#define IBUS_X_EVENT_GET_CLASS(obj) \ ++ (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_X_EVENT, IBusXEventClass)) ++ ++G_BEGIN_DECLS ++ ++typedef struct _IBusXEvent IBusXEvent; ++typedef struct _IBusXEventClass IBusXEventClass; ++typedef struct _IBusXEventPrivate IBusXEventPrivate; ++ ++typedef enum { ++ IBUS_X_EVENT_NOTHING = -1, ++ IBUS_X_EVENT_KEY_PRESS = 0, ++ IBUS_X_EVENT_KEY_RELEASE = 1, ++ IBUS_X_EVENT_OTHER = 2, ++ IBUS_X_EVENT_EVENT_LAST /* helper variable for decls */ ++} IBusXEventType; ++ ++/** ++ * IBusXEvent: ++ * @type: event type ++ * ++ * IBusEngine properties. ++ */ ++struct _IBusXEvent { ++ /*< private >*/ ++ IBusSerializable parent; ++ IBusXEventPrivate *priv; ++ ++ /* instance members */ ++ /*< public >*/ ++ IBusXEventType event_type; ++ guint window; ++ gint8 send_event; ++ gulong serial; ++}; ++ ++struct _IBusXEventClass { ++ /*< private >*/ ++ IBusSerializableClass parent; ++ ++ /* class members */ ++ /*< public >*/ ++ ++ /*< private >*/ ++ /* padding */ ++ gpointer pdummy[10]; ++}; ++ ++GType ibus_x_event_get_type (void); ++ ++/** ++ * ibus_x_event_new: ++ * @first_property_name: Name of the first property. ++ * @...: the NULL-terminated arguments of the properties and values. ++ * ++ * Create a new #IBusXEvent. ++ * ++ * Returns: A newly allocated #IBusXEvent. E.g. ++ * ibus_x_event_new ("event-type", IBUS_X_EVENT_KEY_PRESS, NULL); ++ */ ++IBusXEvent * ibus_x_event_new (const gchar ++ *first_property_name, ++ ...); ++ ++/** ++ * ibus_x_event_get_version: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: Version of #IBusXEvent ++ */ ++guint ibus_x_event_get_version (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_event_type: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: IBusXEventType of #IBusXEvent ++ */ ++IBusXEventType ibus_x_event_get_event_type (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_window: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: XID of #IBusXEvent ++ */ ++guint32 ibus_x_event_get_window (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_send_event: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: send_event of #IBusXEvent ++ */ ++gint8 ibus_x_event_get_send_event (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_serial: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: serial of #IBusXEvent ++ */ ++gulong ibus_x_event_get_serial (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_time: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: time of #IBusXEvent ++ */ ++guint32 ibus_x_event_get_time (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_state: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: state of #IBusXEvent ++ */ ++guint ibus_x_event_get_state (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_keyval: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: keyval of #IBusXEvent ++ */ ++guint ibus_x_event_get_keyval (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_length: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: length of #IBusXEvent ++ */ ++gint ibus_x_event_get_length (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_string: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: string of #IBusXEvent ++ */ ++const gchar * ibus_x_event_get_string (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_hardware_keycode: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: hardware keycode of #IBusXEvent ++ */ ++guint16 ibus_x_event_get_hardware_keycode ++ (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_group: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: group of #IBusXEvent ++ */ ++guint8 ibus_x_event_get_group (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_is_modifier: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: is_modifier of #IBusXEvent ++ */ ++gboolean ibus_x_event_get_is_modifier ++ (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_subwindow: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: subwindow of #IBusXEvent ++ */ ++guint32 ibus_x_event_get_subwindow (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_root: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: root window of #IBusXEvent ++ */ ++guint32 ibus_x_event_get_root (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_x: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: x of #IBusXEvent ++ */ ++gint ibus_x_event_get_x (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_y: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: y of #IBusXEvent ++ */ ++gint ibus_x_event_get_y (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_x_root: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: x-root of #IBusXEvent ++ */ ++gint ibus_x_event_get_x_root (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_y_root: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: y-root of #IBusXEvent ++ */ ++gint ibus_x_event_get_y_root (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_same_screen: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: same_screen of #IBusXEvent ++ */ ++gboolean ibus_x_event_get_same_screen ++ (IBusXEvent *event); ++ ++/** ++ * ibus_x_event_get_purpose: ++ * @event: An #IBusXEvent. ++ * ++ * Returns: purpose of #IBusXEvent ++ */ ++const gchar * ibus_x_event_get_purpose (IBusXEvent *event); ++ ++G_END_DECLS ++#endif +diff --git a/src/tests/runtest b/src/tests/runtest +index 91c4e95f..0e43fee5 100755 +--- a/src/tests/runtest ++++ b/src/tests/runtest +@@ -106,6 +106,7 @@ test -d $tstdir || mkdir $tstdir + --daemonize \ + --cache=none \ + --panel=disable \ ++ --panel-extension=disable \ + --config=default \ + --verbose; + +diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am +index 786b80e6..0a8f4200 100644 +--- a/ui/gtk3/Makefile.am ++++ b/ui/gtk3/Makefile.am +@@ -3,8 +3,8 @@ + # ibus - The Input Bus + # + # Copyright (c) 2007-2015 Peng Huang +-# Copyright (c) 2015-2017 Takao Fujwiara +-# Copyright (c) 2007-2017 Red Hat, Inc. ++# Copyright (c) 2015-2018 Takao Fujwiara ++# Copyright (c) 2007-2018 Red Hat, Inc. + # + # This library is free software; you can redistribute it and/or + # modify it under the terms of the GNU Lesser General Public +@@ -30,7 +30,7 @@ component_DATA = \ + $(NULL) + componentdir = $(pkgdatadir)/component + +-gtkpanel.xml: gtkpanel.xml.in ++%.xml: %.xml.in + $(AM_V_GEN) sed \ + -e 's|@VERSION[@]|$(VERSION)|g' \ + -e 's|@libexecdir[@]|$(libexecdir)|g' $< > $@.tmp && \ +@@ -108,6 +108,7 @@ libexec_PROGRAMS = ibus-ui-gtk3 + + ibus_ui_gtk3_SOURCES = \ + application.vala \ ++ bindingcommon.vala \ + candidatearea.vala \ + candidatepanel.vala \ + emojier.vala \ +@@ -155,9 +156,12 @@ EXTRA_DIST = \ + $(emoji_headers) \ + $(man_seven_in_files) \ + emojierapp.vala \ ++ extension.vala \ ++ gtkextension.xml.in \ + gtkpanel.xml.in \ + notification-item.xml \ + notification-watcher.xml \ ++ panelbinding.vala \ + $(NULL) + + if ENABLE_EMOJI_DICT +@@ -167,10 +171,8 @@ libexec_PROGRAMS += ibus-ui-emojier + + ibus_ui_emojier_VALASOURCES = \ + emojierapp.vala \ +- candidatearea.vala \ + emojier.vala \ + iconwidget.vala \ +- pango.vala \ + separator.vala \ + $(NULL) + ibus_ui_emojier_SOURCES = \ +@@ -198,6 +200,53 @@ emojierapp.o: $(srcdir)/emojierapp.c + $(AM_V_CC_no)$(COMPILE) -c -o $@ $< + $(NULL) + ++component_DATA += gtkextension.xml ++CLEANFILES += gtkextension.xml ++libexec_PROGRAMS += ibus-extension-gtk3 ++ ++ibus_extension_gtk3_VALASOURCES = \ ++ bindingcommon.vala \ ++ emojier.vala \ ++ extension.vala \ ++ iconwidget.vala \ ++ keybindingmanager.vala \ ++ panelbinding.vala \ ++ $(NULL) ++ibus_extension_gtk3_SOURCES = \ ++ $(ibus_extension_gtk3_VALASOURCES:.vala=.c) \ ++ $(NULL) ++ ++ibus_extension_gtk3_LDADD = \ ++ $(AM_LDADD) \ ++ $(NULL) ++ibus_extension_gtk3_VALAFLAGS = \ ++ $(AM_VALAFLAGS) \ ++ $(NULL) ++ ++# This line and foo_VALASOURCES line can delete the duplicated entries ++# of emojier.c: emojier.vala ++extension.c: $(ibus_extension_gtk3_VALASOURCES) ++ $(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \ ++$(VALAFLAGS) -C $(ibus_extension_gtk3_VALASOURCES) ++ $(NULL) ++# make dist creates .c files in a different srcdir ++extension.o: $(srcdir)/extension.c ++ $(AM_V_CC)source='$<' object='$@' libtool=no \ ++ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ ++ $(AM_V_CC_no)$(COMPILE) -c -o $@ $< ++ $(NULL) ++# of emojier.c: emojier.vala ++panelbinding.c: $(ibus_extension_gtk3_VALASOURCES) ++ $(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \ ++$(VALAFLAGS) -C $(ibus_extension_gtk3_VALASOURCES) ++ $(NULL) ++# make dist creates .c files in a different srcdir ++panelbinding.o: $(srcdir)/panelbinding.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) + man_sevendir = $(mandir)/man7 +diff --git a/ui/gtk3/bindingcommon.vala b/ui/gtk3/bindingcommon.vala +new file mode 100644 +index 00000000..4171f29d +--- /dev/null ++++ b/ui/gtk3/bindingcommon.vala +@@ -0,0 +1,215 @@ ++/* vim:set et sts=4 sw=4: ++ * ++ * ibus - The Input Bus ++ * ++ * Copyright(c) 2018 Peng Huang ++ * Copyright(c) 2018 Takao Fujwiara ++ * ++ * 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 ++ */ ++ ++/* This file depends on keybindingmanager.vala */ ++ ++class BindingCommon { ++ public enum KeyEventFuncType { ++ ANY, ++ IME_SWITCHER, ++ EMOJI_TYPING, ++ } ++ ++ public class Keybinding : GLib.Object { ++ public Keybinding(uint keysym, ++ Gdk.ModifierType modifiers, ++ bool reverse, ++ KeyEventFuncType ftype) { ++ this.keysym = keysym; ++ this.modifiers = modifiers; ++ this.reverse = reverse; ++ this.ftype = ftype; ++ } ++ ++ public uint keysym { get; set; } ++ public Gdk.ModifierType modifiers { get; set; } ++ public bool reverse { get; set; } ++ public KeyEventFuncType ftype { get; set; } ++ } ++ ++ public delegate void KeybindingFuncHandlerFunc(Gdk.Event event); ++ ++ public static void ++ keybinding_manager_bind(KeybindingManager keybinding_manager, ++ ref GLib.List keybindings, ++ string? accelerator, ++ KeyEventFuncType ftype, ++ KeybindingManager.KeybindingHandlerFunc ++ handler_normal, ++ KeybindingManager.KeybindingHandlerFunc? ++ handler_reverse) { ++ uint switch_keysym = 0; ++ Gdk.ModifierType switch_modifiers = 0; ++ Gdk.ModifierType reverse_modifier = Gdk.ModifierType.SHIFT_MASK; ++ Keybinding keybinding; ++ ++ Gtk.accelerator_parse(accelerator, ++ out switch_keysym, out switch_modifiers); ++ ++ // Map virtual modifiers to (i.e. Mod2, Mod3, ...) ++ const Gdk.ModifierType VIRTUAL_MODIFIERS = ( ++ Gdk.ModifierType.SUPER_MASK | ++ Gdk.ModifierType.HYPER_MASK | ++ Gdk.ModifierType.META_MASK); ++ if ((switch_modifiers & VIRTUAL_MODIFIERS) != 0) { ++ // workaround a bug in gdk vapi vala > 0.18 ++ // https://bugzilla.gnome.org/show_bug.cgi?id=677559 ++#if VALA_0_18 ++ Gdk.Keymap.get_default().map_virtual_modifiers( ++ ref switch_modifiers); ++#else ++ if ((switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0) ++ switch_modifiers |= Gdk.ModifierType.MOD4_MASK; ++ if ((switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0) ++ switch_modifiers |= Gdk.ModifierType.MOD4_MASK; ++#endif ++ switch_modifiers &= ~VIRTUAL_MODIFIERS; ++ } ++ ++ if (switch_keysym == 0 && switch_modifiers == 0) { ++ warning("Parse accelerator '%s' failed!", accelerator); ++ return; ++ } ++ ++ keybinding = new Keybinding(switch_keysym, ++ switch_modifiers, ++ false, ++ ftype); ++ keybindings.append(keybinding); ++ ++ keybinding_manager.bind(switch_keysym, switch_modifiers, ++ handler_normal); ++ if (ftype == KeyEventFuncType.EMOJI_TYPING) { ++ return; ++ } ++ ++ // accelerator already has Shift mask ++ if ((switch_modifiers & reverse_modifier) != 0) { ++ return; ++ } ++ ++ switch_modifiers |= reverse_modifier; ++ ++ keybinding = new Keybinding(switch_keysym, ++ switch_modifiers, ++ true, ++ ftype); ++ keybindings.append(keybinding); ++ ++ if (ftype == KeyEventFuncType.IME_SWITCHER) { ++ keybinding_manager.bind(switch_keysym, switch_modifiers, ++ handler_reverse); ++ } ++ return; ++ } ++ ++ public static void ++ unbind_switch_shortcut(KeyEventFuncType ftype, ++ GLib.List keybindings) { ++ var keybinding_manager = KeybindingManager.get_instance(); ++ ++ while (keybindings != null) { ++ Keybinding keybinding = keybindings.data; ++ ++ if (ftype == KeyEventFuncType.ANY || ++ ftype == keybinding.ftype) { ++ keybinding_manager.unbind(keybinding.keysym, ++ keybinding.modifiers); ++ } ++ keybindings = keybindings.next; ++ } ++ } ++ ++ public static void ++ set_custom_font(GLib.Settings? settings_panel, ++ GLib.Settings? settings_emoji, ++ ref Gtk.CssProvider? css_provider) { ++ Gdk.Display display = Gdk.Display.get_default(); ++ Gdk.Screen screen = (display != null) ? ++ display.get_default_screen() : null; ++ ++ if (screen == null) { ++ warning("Could not open display."); ++ return; ++ } ++ ++ if (settings_emoji != null) { ++ string emoji_font = settings_emoji.get_string("font"); ++ if (emoji_font == null) { ++ warning("No config emoji:font."); ++ return; ++ } ++ IBusEmojier.set_emoji_font(emoji_font); ++ } ++ ++ if (settings_panel == null) ++ return; ++ ++ bool use_custom_font = settings_panel.get_boolean("use-custom-font"); ++ ++ if (css_provider != null) { ++ Gtk.StyleContext.remove_provider_for_screen(screen, ++ css_provider); ++ css_provider = null; ++ } ++ ++ if (use_custom_font == false) { ++ return; ++ } ++ ++ string custom_font = settings_panel.get_string("custom-font"); ++ if (custom_font == null) { ++ warning("No config panel:custom-font."); ++ return; ++ } ++ ++ Pango.FontDescription font_desc = ++ Pango.FontDescription.from_string(custom_font); ++ string font_family = font_desc.get_family(); ++ int font_size = font_desc.get_size() / Pango.SCALE; ++ string data; ++ ++ if (Gtk.MAJOR_VERSION < 3 || ++ (Gtk.MAJOR_VERSION == 3 && Gtk.MINOR_VERSION < 20)) { ++ data = "GtkLabel { font: %s; }".printf(custom_font); ++ } else { ++ data = "label { font-family: %s; font-size: %dpt; }" ++ .printf(font_family, font_size); ++ } ++ ++ css_provider = new Gtk.CssProvider(); ++ ++ try { ++ css_provider.load_from_data(data, -1); ++ } catch (GLib.Error e) { ++ warning("Failed css_provider_from_data: %s: %s", custom_font, ++ e.message); ++ return; ++ } ++ ++ Gtk.StyleContext.add_provider_for_screen( ++ screen, ++ css_provider, ++ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++ } ++} +diff --git a/ui/gtk3/candidatearea.vala b/ui/gtk3/candidatearea.vala +index e162a960..f590cf3a 100644 +--- a/ui/gtk3/candidatearea.vala ++++ b/ui/gtk3/candidatearea.vala +@@ -21,108 +21,6 @@ + * USA + */ + +-class ThemedRGBA { +- public Gdk.RGBA *normal_fg { get; set; } +- public Gdk.RGBA *normal_bg { get; set; } +- public Gdk.RGBA *selected_fg { get; set; } +- public Gdk.RGBA *selected_bg { get; set; } +- +- private Gtk.StyleContext m_style_context; +- +- public ThemedRGBA(Gtk.Widget widget) { +- this.normal_fg = null; +- this.normal_bg = null; +- this.selected_fg = null; +- this.selected_bg = null; +- +- /* Use the color of Gtk.TextView instead of Gtk.Label +- * because the selected label "color" is not configured +- * in "Adwaita" theme and the selected label "background-color" +- * is not configured in "Maia" theme. +- * https://github.com/ibus/ibus/issues/1871 +- */ +- Gtk.WidgetPath widget_path = new Gtk.WidgetPath(); +- widget_path.append_type(typeof(Gtk.TextView)); +- m_style_context = new Gtk.StyleContext(); +- m_style_context.set_path(widget_path); +- m_style_context.add_class(Gtk.STYLE_CLASS_VIEW); +- +- /* "-gtk-secondary-caret-color" value is different +- * if the parent widget is set in "Menta" theme. +- */ +- m_style_context.set_parent(widget.get_style_context()); +- +- get_rgba(); +- +- m_style_context.changed.connect(() => { get_rgba(); }); +- } +- +- ~ThemedRGBA() { +- reset_rgba(); +- } +- +- private void reset_rgba() { +- if (this.normal_fg != null) { +- this.normal_fg.free(); +- this.normal_fg = null; +- } +- if (this.normal_bg != null) { +- this.normal_bg.free(); +- this.normal_bg = null; +- } +- if (this.selected_fg != null) { +- this.selected_fg.free(); +- this.selected_fg = null; +- } +- if (this.selected_bg != null) { +- this.selected_bg.free(); +- this.selected_bg = null; +- } +- } +- +- private void get_rgba() { +- reset_rgba(); +- Gdk.RGBA *normal_fg = null; +- Gdk.RGBA *normal_bg = null; +- Gdk.RGBA *selected_fg = null; +- Gdk.RGBA *selected_bg = null; +- m_style_context.get(Gtk.StateFlags.NORMAL, +- "color", +- out normal_fg); +- m_style_context.get(Gtk.StateFlags.SELECTED, +- "color", +- out selected_fg); +- +- string bg_prop = "background-color"; +- m_style_context.get(Gtk.StateFlags.NORMAL, +- bg_prop, +- out normal_bg); +- m_style_context.get(Gtk.StateFlags.SELECTED, +- bg_prop, +- out selected_bg); +- if (normal_bg.red == selected_bg.red && +- normal_bg.green == selected_bg.green && +- normal_bg.blue == selected_bg.blue && +- normal_bg.alpha == selected_bg.alpha) { +- normal_bg.free(); +- normal_bg = null; +- normal_bg.free(); +- normal_bg = null; +- bg_prop = "-gtk-secondary-caret-color"; +- m_style_context.get(Gtk.StateFlags.NORMAL, +- bg_prop, +- out normal_bg); +- m_style_context.get(Gtk.StateFlags.SELECTED, +- bg_prop, +- out selected_bg); +- } +- this.normal_fg = normal_fg; +- this.normal_bg = normal_bg; +- this.selected_fg = selected_fg; +- this.selected_bg = selected_bg; +- } +-} +- + class CandidateArea : Gtk.Box { + private bool m_vertical; + private Gtk.Label[] m_labels; +diff --git a/ui/gtk3/extension.vala b/ui/gtk3/extension.vala +new file mode 100644 +index 00000000..a170280b +--- /dev/null ++++ b/ui/gtk3/extension.vala +@@ -0,0 +1,124 @@ ++/* vim:set et sts=4 sw=4: ++ * ++ * ibus - The Input Bus ++ * ++ * Copyright(c) 2018 Peng Huang ++ * Copyright(c) 2018 Takao Fujiwara ++ * ++ * 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 ++ */ ++ ++class ExtensionGtk { ++ private IBus.Bus m_bus; ++ private PanelBinding m_panel; ++ ++ public ExtensionGtk(string[] argv) { ++ GLib.Intl.bindtextdomain(Config.GETTEXT_PACKAGE, ++ Config.GLIB_LOCALE_DIR); ++ GLib.Intl.bind_textdomain_codeset(Config.GETTEXT_PACKAGE, "UTF-8"); ++ IBus.init(); ++ Gtk.init(ref argv); ++ ++ m_bus = new IBus.Bus(); ++ ++ m_bus.connected.connect(bus_connected); ++ m_bus.disconnected.connect(bus_disconnected); ++ ++ if (m_bus.is_connected()) { ++ init(); ++ } ++ } ++ ++ private void init() { ++ DBusConnection connection = m_bus.get_connection(); ++ connection.signal_subscribe("org.freedesktop.DBus", ++ "org.freedesktop.DBus", ++ "NameAcquired", ++ "/org/freedesktop/DBus", ++ IBus.SERVICE_PANEL_EXTENSION, ++ DBusSignalFlags.NONE, ++ bus_name_acquired_cb); ++ connection.signal_subscribe("org.freedesktop.DBus", ++ "org.freedesktop.DBus", ++ "NameLost", ++ "/org/freedesktop/DBus", ++ IBus.SERVICE_PANEL_EXTENSION, ++ DBusSignalFlags.NONE, ++ bus_name_lost_cb); ++ var flags = ++ IBus.BusNameFlag.ALLOW_REPLACEMENT | ++ IBus.BusNameFlag.REPLACE_EXISTING; ++ m_bus.request_name(IBus.SERVICE_PANEL_EXTENSION, flags); ++ } ++ ++ public int run() { ++ Gtk.main(); ++ return 0; ++ } ++ ++ private void bus_name_acquired_cb(DBusConnection connection, ++ string sender_name, ++ string object_path, ++ string interface_name, ++ string signal_name, ++ Variant parameters) { ++ debug("signal_name = %s", signal_name); ++ m_panel = new PanelBinding(m_bus); ++ m_panel.load_settings(); ++ } ++ ++ private void bus_name_lost_cb(DBusConnection connection, ++ string sender_name, ++ string object_path, ++ string interface_name, ++ string signal_name, ++ Variant parameters) { ++ // "Destroy" dbus method was called before this callback is called. ++ // "Destroy" dbus method -> ibus_service_destroy() ++ // -> g_dbus_connection_unregister_object() ++ // -> g_object_unref(m_panel) will be called later with an idle method, ++ // which was assigned in the arguments of ++ // g_dbus_connection_register_object() ++ debug("signal_name = %s", signal_name); ++ ++ // unref m_panel ++ m_panel.disconnect_signals(); ++ m_panel = null; ++ } ++ ++ private void bus_disconnected(IBus.Bus bus) { ++ debug("connection is lost."); ++ Gtk.main_quit(); ++ } ++ ++ private void bus_connected(IBus.Bus bus) { ++ init(); ++ } ++ ++ public static void main(string[] argv) { ++ // https://bugzilla.redhat.com/show_bug.cgi?id=1226465#c20 ++ // In /etc/xdg/plasma-workspace/env/gtk3_scrolling.sh ++ // Plasma deskop sets this variable and prevents Super-space, ++ // and Ctrl-Shift-e when ibus-ui-gtk3 runs after the ++ // desktop is launched. ++ GLib.Environment.unset_variable("GDK_CORE_DEVICE_EVENTS"); ++ // for Gdk.X11.get_default_xdisplay() ++ Gdk.set_allowed_backends("x11"); ++ ++ ExtensionGtk extension = new ExtensionGtk(argv); ++ extension.run(); ++ } ++} +diff --git a/ui/gtk3/gtkextension.xml.in b/ui/gtk3/gtkextension.xml.in +new file mode 100644 +index 00000000..b8157c97 +--- /dev/null ++++ b/ui/gtk3/gtkextension.xml.in +@@ -0,0 +1,12 @@ ++ ++ ++ ++ org.freedesktop.IBus.Panel.Extension ++ Gtk Panel Extension Component ++ @libexecdir@/ibus-extension-gtk3 ++ @VERSION@ ++ Takao Fujiwara <takao.fujiwara1@gmail.com> ++ GPL ++ https://github.com/ibus/ibus/wiki ++ ibus ++ +diff --git a/ui/gtk3/iconwidget.vala b/ui/gtk3/iconwidget.vala +index d322650c..36643c74 100644 +--- a/ui/gtk3/iconwidget.vala ++++ b/ui/gtk3/iconwidget.vala +@@ -3,6 +3,7 @@ + * ibus - The Input Bus + * + * Copyright(c) 2011-2014 Peng Huang ++ * Copyright(c) 2018 Takao Fujiwara + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -20,6 +21,108 @@ + * USA + */ + ++class ThemedRGBA { ++ public Gdk.RGBA *normal_fg { get; set; } ++ public Gdk.RGBA *normal_bg { get; set; } ++ public Gdk.RGBA *selected_fg { get; set; } ++ public Gdk.RGBA *selected_bg { get; set; } ++ ++ private Gtk.StyleContext m_style_context; ++ ++ public ThemedRGBA(Gtk.Widget widget) { ++ this.normal_fg = null; ++ this.normal_bg = null; ++ this.selected_fg = null; ++ this.selected_bg = null; ++ ++ /* Use the color of Gtk.TextView instead of Gtk.Label ++ * because the selected label "color" is not configured ++ * in "Adwaita" theme and the selected label "background-color" ++ * is not configured in "Maia" theme. ++ * https://github.com/ibus/ibus/issues/1871 ++ */ ++ Gtk.WidgetPath widget_path = new Gtk.WidgetPath(); ++ widget_path.append_type(typeof(Gtk.TextView)); ++ m_style_context = new Gtk.StyleContext(); ++ m_style_context.set_path(widget_path); ++ m_style_context.add_class(Gtk.STYLE_CLASS_VIEW); ++ ++ /* "-gtk-secondary-caret-color" value is different ++ * if the parent widget is set in "Menta" theme. ++ */ ++ m_style_context.set_parent(widget.get_style_context()); ++ ++ get_rgba(); ++ ++ m_style_context.changed.connect(() => { get_rgba(); }); ++ } ++ ++ ~ThemedRGBA() { ++ reset_rgba(); ++ } ++ ++ private void reset_rgba() { ++ if (this.normal_fg != null) { ++ this.normal_fg.free(); ++ this.normal_fg = null; ++ } ++ if (this.normal_bg != null) { ++ this.normal_bg.free(); ++ this.normal_bg = null; ++ } ++ if (this.selected_fg != null) { ++ this.selected_fg.free(); ++ this.selected_fg = null; ++ } ++ if (this.selected_bg != null) { ++ this.selected_bg.free(); ++ this.selected_bg = null; ++ } ++ } ++ ++ private void get_rgba() { ++ reset_rgba(); ++ Gdk.RGBA *normal_fg = null; ++ Gdk.RGBA *normal_bg = null; ++ Gdk.RGBA *selected_fg = null; ++ Gdk.RGBA *selected_bg = null; ++ m_style_context.get(Gtk.StateFlags.NORMAL, ++ "color", ++ out normal_fg); ++ m_style_context.get(Gtk.StateFlags.SELECTED, ++ "color", ++ out selected_fg); ++ ++ string bg_prop = "background-color"; ++ m_style_context.get(Gtk.StateFlags.NORMAL, ++ bg_prop, ++ out normal_bg); ++ m_style_context.get(Gtk.StateFlags.SELECTED, ++ bg_prop, ++ out selected_bg); ++ if (normal_bg.red == selected_bg.red && ++ normal_bg.green == selected_bg.green && ++ normal_bg.blue == selected_bg.blue && ++ normal_bg.alpha == selected_bg.alpha) { ++ normal_bg.free(); ++ normal_bg = null; ++ normal_bg.free(); ++ normal_bg = null; ++ bg_prop = "-gtk-secondary-caret-color"; ++ m_style_context.get(Gtk.StateFlags.NORMAL, ++ bg_prop, ++ out normal_bg); ++ m_style_context.get(Gtk.StateFlags.SELECTED, ++ bg_prop, ++ out selected_bg); ++ } ++ this.normal_fg = normal_fg; ++ this.normal_bg = normal_bg; ++ this.selected_fg = selected_fg; ++ this.selected_bg = selected_bg; ++ } ++} ++ + class IconWidget: Gtk.Image { + /** + * IconWidget: +diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala +index bcb3ed75..d9238c89 100644 +--- a/ui/gtk3/panel.vala ++++ b/ui/gtk3/panel.vala +@@ -22,39 +22,16 @@ + */ + + class Panel : IBus.PanelService { +- private class Keybinding { +- public Keybinding(uint keysym, +- Gdk.ModifierType modifiers, +- bool reverse, +- KeyEventFuncType ftype) { +- this.keysym = keysym; +- this.modifiers = modifiers; +- this.reverse = reverse; +- this.ftype = ftype; +- } +- +- public uint keysym { get; set; } +- public Gdk.ModifierType modifiers { get; set; } +- public bool reverse { get; set; } +- public KeyEventFuncType ftype { get; set; } +- } + + private enum IconType { + STATUS_ICON, + INDICATOR, + } + +- private enum KeyEventFuncType { +- ANY, +- IME_SWITCHER, +- EMOJI_TYPING, +- } +- + private IBus.Bus m_bus; + private GLib.Settings m_settings_general = null; + private GLib.Settings m_settings_hotkey = null; + private GLib.Settings m_settings_panel = null; +- private GLib.Settings m_settings_emoji = null; + private IconType m_icon_type = IconType.STATUS_ICON; + private Indicator m_indicator; + #if INDICATOR +@@ -73,10 +50,6 @@ class Panel : IBus.PanelService { + private CandidatePanel m_candidate_panel; + private Switcher m_switcher; + private uint m_switcher_focus_set_engine_id; +- private IBusEmojier? m_emojier; +- private uint m_emojier_set_emoji_lang_id; +- private uint m_emojier_focus_commit_text_id; +- private string[] m_emojier_favorites = {}; + private PropertyManager m_property_manager; + private PropertyPanel m_property_panel; + private GLib.Pid m_setup_pid = 0; +@@ -108,7 +81,8 @@ class Panel : IBus.PanelService { + private ulong m_activate_id; + private ulong m_registered_status_notifier_item_id; + +- private GLib.List m_keybindings = new GLib.List(); ++ private GLib.List m_keybindings = ++ new GLib.List(); + + public Panel(IBus.Bus bus) { + GLib.assert(bus.is_connected()); +@@ -147,8 +121,6 @@ class Panel : IBus.PanelService { + m_switcher.set_popup_delay_time((uint) m_switcher_delay_time); + } + +- bind_emoji_shortcut(); +- + m_property_manager = new PropertyManager(); + m_property_manager.property_activate.connect((w, k, s) => { + property_activate(k, s); +@@ -168,7 +140,9 @@ class Panel : IBus.PanelService { + if (m_indicator != null) + m_indicator.unregister_connection(); + #endif +- unbind_switch_shortcut(KeyEventFuncType.ANY); ++ BindingCommon.unbind_switch_shortcut( ++ BindingCommon.KeyEventFuncType.ANY, m_keybindings); ++ m_keybindings = null; + } + + private void init_settings() { +@@ -176,7 +150,6 @@ class Panel : IBus.PanelService { + m_settings_hotkey = + new GLib.Settings("org.freedesktop.ibus.general.hotkey"); + m_settings_panel = new GLib.Settings("org.freedesktop.ibus.panel"); +- m_settings_emoji = new GLib.Settings("org.freedesktop.ibus.panel.emoji"); + + m_settings_general.changed["preload-engines"].connect((key) => { + update_engines(m_settings_general.get_strv(key), +@@ -205,16 +178,23 @@ class Panel : IBus.PanelService { + }); + + m_settings_hotkey.changed["triggers"].connect((key) => { +- unbind_switch_shortcut(KeyEventFuncType.IME_SWITCHER); ++ BindingCommon.unbind_switch_shortcut( ++ BindingCommon.KeyEventFuncType.IME_SWITCHER, ++ m_keybindings); ++ m_keybindings = null; + bind_switch_shortcut(); + }); + + m_settings_panel.changed["custom-font"].connect((key) => { +- set_custom_font(); ++ BindingCommon.set_custom_font(m_settings_panel, ++ null, ++ ref m_css_provider); + }); + + m_settings_panel.changed["use-custom-font"].connect((key) => { +- set_custom_font(); ++ BindingCommon.set_custom_font(m_settings_panel, ++ null, ++ ref m_css_provider); + }); + + m_settings_panel.changed["show-icon-on-systray"].connect((key) => { +@@ -245,39 +225,6 @@ class Panel : IBus.PanelService { + m_settings_panel.changed["property-icon-delay-time"].connect((key) => { + set_property_icon_delay_time(); + }); +- +- m_settings_emoji.changed["hotkey"].connect((key) => { +- unbind_switch_shortcut(KeyEventFuncType.EMOJI_TYPING); +- bind_emoji_shortcut(); +- }); +- +- m_settings_emoji.changed["font"].connect((key) => { +- set_custom_font(); +- }); +- +- m_settings_emoji.changed["favorites"].connect((key) => { +- set_emoji_favorites(); +- }); +- +- m_settings_emoji.changed["favorite-annotations"].connect((key) => { +- set_emoji_favorites(); +- }); +- +- m_settings_emoji.changed["lang"].connect((key) => { +- set_emoji_lang(); +- }); +- +- m_settings_emoji.changed["has-partial-match"].connect((key) => { +- set_emoji_partial_match(); +- }); +- +- m_settings_emoji.changed["partial-match-length"].connect((key) => { +- set_emoji_partial_match(); +- }); +- +- m_settings_emoji.changed["partial-match-condition"].connect((key) => { +- set_emoji_partial_match(); +- }); + } + + private void popup_menu_at_area_window(Gtk.Menu menu, +@@ -409,120 +356,40 @@ class Panel : IBus.PanelService { + m_status_icon.set_from_icon_name("ibus-keyboard"); + } + +- private void keybinding_manager_bind(KeybindingManager keybinding_manager, +- string? accelerator, +- KeyEventFuncType ftype) { +- uint switch_keysym = 0; +- Gdk.ModifierType switch_modifiers = 0; +- Gdk.ModifierType reverse_modifier = Gdk.ModifierType.SHIFT_MASK; +- Keybinding keybinding; +- +- Gtk.accelerator_parse(accelerator, +- out switch_keysym, out switch_modifiers); +- +- // Map virtual modifiers to (i.e. Mod2, Mod3, ...) +- const Gdk.ModifierType VIRTUAL_MODIFIERS = ( +- Gdk.ModifierType.SUPER_MASK | +- Gdk.ModifierType.HYPER_MASK | +- Gdk.ModifierType.META_MASK); +- if ((switch_modifiers & VIRTUAL_MODIFIERS) != 0) { +- // workaround a bug in gdk vapi vala > 0.18 +- // https://bugzilla.gnome.org/show_bug.cgi?id=677559 +-#if VALA_0_18 +- Gdk.Keymap.get_default().map_virtual_modifiers( +- ref switch_modifiers); +-#else +- if ((switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0) +- switch_modifiers |= Gdk.ModifierType.MOD4_MASK; +- if ((switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0) +- switch_modifiers |= Gdk.ModifierType.MOD4_MASK; +-#endif +- switch_modifiers &= ~VIRTUAL_MODIFIERS; +- } +- +- if (switch_keysym == 0 && switch_modifiers == 0) { +- warning("Parse accelerator '%s' failed!", accelerator); +- return; +- } +- +- keybinding = new Keybinding(switch_keysym, +- switch_modifiers, +- false, +- ftype); +- m_keybindings.append(keybinding); +- +- /* Workaround not to free the pointer of handle_engine_switch() */ +- if (ftype == KeyEventFuncType.IME_SWITCHER) { +- keybinding_manager.bind(switch_keysym, switch_modifiers, +- (e) => handle_engine_switch(e, false)); +- } else if (ftype == KeyEventFuncType.EMOJI_TYPING) { +- keybinding_manager.bind(switch_keysym, switch_modifiers, +- (e) => handle_emoji_typing(e)); +- return; +- } +- +- // accelerator already has Shift mask +- if ((switch_modifiers & reverse_modifier) != 0) { +- return; +- } +- +- switch_modifiers |= reverse_modifier; +- +- keybinding = new Keybinding(switch_keysym, +- switch_modifiers, +- true, +- ftype); +- m_keybindings.append(keybinding); +- +- if (ftype == KeyEventFuncType.IME_SWITCHER) { +- keybinding_manager.bind(switch_keysym, switch_modifiers, +- (e) => handle_engine_switch(e, true)); +- } +- } +- + private void bind_switch_shortcut() { + string[] accelerators = m_settings_hotkey.get_strv("triggers"); + + var keybinding_manager = KeybindingManager.get_instance(); + + foreach (var accelerator in accelerators) { +- keybinding_manager_bind(keybinding_manager, +- accelerator, +- KeyEventFuncType.IME_SWITCHER); +- } +- } +- +- private void bind_emoji_shortcut() { +-#if EMOJI_DICT +- string[] accelerators = m_settings_emoji.get_strv("hotkey"); +- +- var keybinding_manager = KeybindingManager.get_instance(); +- +- foreach (var accelerator in accelerators) { +- keybinding_manager_bind(keybinding_manager, +- accelerator, +- KeyEventFuncType.EMOJI_TYPING); ++ BindingCommon.keybinding_manager_bind( ++ keybinding_manager, ++ ref m_keybindings, ++ accelerator, ++ BindingCommon.KeyEventFuncType.IME_SWITCHER, ++ handle_engine_switch_normal, ++ handle_engine_switch_reverse); + } +-#endif + } + +- private void unbind_switch_shortcut(KeyEventFuncType ftype) { ++/* ++ public static void ++ unbind_switch_shortcut(KeyEventFuncType ftype, ++ GLib.List keybindings) { + var keybinding_manager = KeybindingManager.get_instance(); + +- unowned GLib.List keybindings = m_keybindings; +- + while (keybindings != null) { + Keybinding keybinding = keybindings.data; + +- if (ftype == KeyEventFuncType.ANY || ftype == keybinding.ftype) { ++ if (ftype == KeyEventFuncType.ANY || ++ ftype == keybinding.ftype) { + keybinding_manager.unbind(keybinding.keysym, + keybinding.modifiers); + } + keybindings = keybindings.next; + } +- +- m_keybindings = null; + } ++*/ + + /** + * panel_get_engines_from_xkb: +@@ -670,69 +537,6 @@ class Panel : IBus.PanelService { + m_settings_general.set_strv("preload-engines", names); + } + +- private void set_custom_font() { +- Gdk.Display display = Gdk.Display.get_default(); +- Gdk.Screen screen = (display != null) ? +- display.get_default_screen() : null; +- +- if (screen == null) { +- warning("Could not open display."); +- return; +- } +- +- string emoji_font = m_settings_emoji.get_string("font"); +- if (emoji_font == null) { +- warning("No config emoji:font."); +- return; +- } +- IBusEmojier.set_emoji_font(emoji_font); +- +- bool use_custom_font = m_settings_panel.get_boolean("use-custom-font"); +- +- if (m_css_provider != null) { +- Gtk.StyleContext.remove_provider_for_screen(screen, +- m_css_provider); +- m_css_provider = null; +- } +- +- if (use_custom_font == false) { +- return; +- } +- +- string custom_font = m_settings_panel.get_string("custom-font"); +- if (custom_font == null) { +- warning("No config panel:custom-font."); +- return; +- } +- +- Pango.FontDescription font_desc = +- Pango.FontDescription.from_string(custom_font); +- string font_family = font_desc.get_family(); +- int font_size = font_desc.get_size() / Pango.SCALE; +- string data; +- +- if (Gtk.MAJOR_VERSION < 3 || +- (Gtk.MAJOR_VERSION == 3 && Gtk.MINOR_VERSION < 20)) { +- data = "GtkLabel { font: %s; }".printf(custom_font); +- } else { +- data = "label { font-family: %s; font-size: %dpt; }" +- .printf(font_family, font_size); +- } +- +- m_css_provider = new Gtk.CssProvider(); +- +- try { +- m_css_provider.load_from_data(data, -1); +- } catch (GLib.Error e) { +- warning("Failed css_provider_from_data: %s: %s", custom_font, +- e.message); +- return; +- } +- +- Gtk.StyleContext.add_provider_for_screen(screen, +- m_css_provider, +- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); +- } + + private void set_switcher_delay_time() { + m_switcher_delay_time = +@@ -855,35 +659,6 @@ class Panel : IBus.PanelService { + m_settings_panel.get_int("property-icon-delay-time"); + } + +- private void set_emoji_favorites() { +- m_emojier_favorites = m_settings_emoji.get_strv("favorites"); +- IBusEmojier.set_favorites( +- m_emojier_favorites, +- m_settings_emoji.get_strv("favorite-annotations")); +- } +- +- private void set_emoji_lang() { +- if (m_emojier_set_emoji_lang_id > 0) { +- GLib.Source.remove(m_emojier_set_emoji_lang_id); +- m_emojier_set_emoji_lang_id = 0; +- } +- m_emojier_set_emoji_lang_id = GLib.Idle.add(() => { +- IBusEmojier.set_annotation_lang( +- m_settings_emoji.get_string("lang")); +- m_emojier_set_emoji_lang_id = 0; +- IBusEmojier.load_unicode_dict(); +- return false; +- }); +- } +- +- private void set_emoji_partial_match() { +- IBusEmojier.set_partial_match( +- m_settings_emoji.get_boolean("has-partial-match")); +- IBusEmojier.set_partial_match_length( +- m_settings_emoji.get_int("partial-match-length")); +- IBusEmojier.set_partial_match_condition( +- m_settings_emoji.get_int("partial-match-condition")); +- } + + private int compare_versions(string version1, string version2) { + string[] version1_list = version1.split("."); +@@ -985,12 +760,16 @@ class Panel : IBus.PanelService { + set_use_xmodmap(); + update_engines(m_settings_general.get_strv("preload-engines"), + m_settings_general.get_strv("engines-order")); +- unbind_switch_shortcut(KeyEventFuncType.ANY); ++ BindingCommon.unbind_switch_shortcut( ++ BindingCommon.KeyEventFuncType.ANY, ++ m_keybindings); ++ m_keybindings = null; + bind_switch_shortcut(); +- bind_emoji_shortcut(); + set_switcher_delay_time(); + set_embed_preedit_text(); +- set_custom_font(); ++ BindingCommon.set_custom_font(m_settings_panel, ++ null, ++ ref m_css_provider); + set_show_icon_on_systray(); + set_lookup_table_orientation(); + set_show_property_panel(); +@@ -998,9 +777,6 @@ class Panel : IBus.PanelService { + set_follow_input_cursor_when_always_shown_property_panel(); + set_xkb_icon_rgba(); + set_property_icon_delay_time(); +- set_emoji_favorites(); +- set_emoji_lang(); +- set_emoji_partial_match(); + } + + /** +@@ -1037,10 +813,6 @@ class Panel : IBus.PanelService { + GLib.Source.remove(m_preload_engines_id); + m_preload_engines_id = 0; + } +- if (m_emojier_set_emoji_lang_id > 0) { +- GLib.Source.remove(m_emojier_set_emoji_lang_id); +- m_emojier_set_emoji_lang_id = 0; +- } + } + + private void engine_contexts_insert(IBus.EngineDesc engine) { +@@ -1091,7 +863,15 @@ class Panel : IBus.PanelService { + set_engine(engine); + } + +- private void handle_engine_switch(Gdk.Event event, bool revert) { ++ private void handle_engine_switch_normal(Gdk.Event event) { ++ handle_engine_switch(event, false); ++ } ++ ++ private void handle_engine_switch_reverse(Gdk.Event event) { ++ handle_engine_switch(event, true); ++ } ++ ++ private void handle_engine_switch(Gdk.Event event, bool reverse) { + // Do not need switch IME + if (m_engines.length <= 1) + return; +@@ -1105,12 +885,12 @@ class Panel : IBus.PanelService { + bool pressed = KeybindingManager.primary_modifier_still_pressed( + event, primary_modifiers); + +- if (revert) { ++ if (reverse) { + modifiers &= ~Gdk.ModifierType.SHIFT_MASK; + } + + if (pressed && m_switcher_delay_time >= 0) { +- int i = revert ? m_engines.length - 1 : 1; ++ int i = reverse ? m_engines.length - 1 : 1; + + /* The flag of m_switcher.is_running avoids the following problem: + * +@@ -1132,28 +912,11 @@ class Panel : IBus.PanelService { + this.switcher_focus_set_engine(); + } + } else { +- int i = revert ? m_engines.length - 1 : 1; ++ int i = reverse ? m_engines.length - 1 : 1; + switch_engine(i); + } + } + +- private void show_emojier(Gdk.Event event) { +- m_emojier = new IBusEmojier(); +- string emoji = m_emojier.run(m_real_current_context_path, event); +- if (emoji == null) { +- m_emojier = null; +- return; +- } +- this.emojier_focus_commit(); +- } +- +- private void handle_emoji_typing(Gdk.Event event) { +- if (m_emojier != null && m_emojier.is_running()) { +- m_emojier.present_centralize(event); +- return; +- } +- show_emojier(event); +- } + + private void run_preload_engines(IBus.EngineDesc[] engines, int index) { + string[] names = {}; +@@ -1393,7 +1156,18 @@ class Panel : IBus.PanelService { + event.key.window = Gdk.get_default_root_window(); + event.key.window.ref(); + } +- handle_emoji_typing(event); ++ IBus.XEvent xevent = new IBus.XEvent( ++ "event-type", IBus.XEventType.KEY_PRESS, ++ "window", ++ (event.key.window as Gdk.X11.Window).get_xid(), ++ "time", event.key.time, ++ "purpose", "emoji"); ++ /* new GLib.Variant("(sv)", "emoji", xevent.serialize_object()) ++ * will call g_variant_unref() for the child variant by vala. ++ * I have no idea not to unref the object so integrated ++ * the purpose to IBus.XEvent above. ++ */ ++ panel_extension(xevent.serialize_object()); + }); + m_sys_menu.append(item); + #endif +@@ -1557,67 +1331,6 @@ class Panel : IBus.PanelService { + } + } + +- private bool emojier_focus_commit_real() { +- if (m_emojier == null) +- return true; +- string selected_string = m_emojier.get_selected_string(); +- string prev_context_path = m_emojier.get_input_context_path(); +- if (selected_string != null && +- prev_context_path != "" && +- prev_context_path == m_current_context_path) { +- IBus.Text text = new IBus.Text.from_string(selected_string); +- commit_text(text); +- m_emojier = null; +- bool has_favorite = false; +- foreach (unowned string favorite in m_emojier_favorites) { +- if (favorite == selected_string) { +- has_favorite = true; +- break; +- } +- } +- if (!has_favorite) { +- m_emojier_favorites += selected_string; +- m_settings_emoji.set_strv("favorites", m_emojier_favorites); +- } +- return true; +- } +- +- return false; +- } +- +- private void emojier_focus_commit() { +- if (m_emojier == null) +- return; +- string selected_string = m_emojier.get_selected_string(); +- string prev_context_path = m_emojier.get_input_context_path(); +- if (selected_string == null && +- prev_context_path != "" && +- m_emojier.is_running()) { +- var context = GLib.MainContext.default(); +- if (m_emojier_focus_commit_text_id > 0 && +- context.find_source_by_id(m_emojier_focus_commit_text_id) +- != null) { +- GLib.Source.remove(m_emojier_focus_commit_text_id); +- } +- m_emojier_focus_commit_text_id = GLib.Timeout.add(100, () => { +- // focus_in is comming before switcher returns +- emojier_focus_commit_real(); +- m_emojier_focus_commit_text_id = -1; +- return false; +- }); +- } else { +- if (emojier_focus_commit_real()) { +- var context = GLib.MainContext.default(); +- if (m_emojier_focus_commit_text_id > 0 && +- context.find_source_by_id(m_emojier_focus_commit_text_id) +- != null) { +- GLib.Source.remove(m_emojier_focus_commit_text_id); +- } +- m_emojier_focus_commit_text_id = -1; +- } +- } +- } +- + public override void focus_in(string input_context_path) { + m_current_context_path = input_context_path; + +@@ -1632,7 +1345,6 @@ class Panel : IBus.PanelService { + m_real_current_context_path = m_current_context_path; + m_property_panel.focus_in(); + this.switcher_focus_set_engine(); +- this.emojier_focus_commit(); + } + + if (m_use_global_engine) +diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala +new file mode 100644 +index 00000000..50700121 +--- /dev/null ++++ b/ui/gtk3/panelbinding.vala +@@ -0,0 +1,335 @@ ++/* vim:set et sts=4 sw=4: ++ * ++ * ibus - The Input Bus ++ * ++ * Copyright(c) 2018 Peng Huang ++ * Copyright(c) 2018 Takao Fujwiara ++ * ++ * 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 ++ */ ++ ++class PanelBinding : IBus.PanelService { ++ private IBus.Bus m_bus; ++ private GLib.Settings m_settings_panel = null; ++ private GLib.Settings m_settings_emoji = null; ++ private string m_current_context_path = ""; ++ private string m_real_current_context_path = ""; ++ private IBusEmojier? m_emojier; ++ private uint m_emojier_set_emoji_lang_id; ++ private uint m_emojier_focus_commit_text_id; ++ private string[] m_emojier_favorites = {}; ++ private Gtk.CssProvider m_css_provider; ++ private const uint PRELOAD_ENGINES_DELAY_TIME = 30000; ++ private GLib.List m_keybindings = ++ new GLib.List(); ++ ++ public PanelBinding(IBus.Bus bus) { ++ GLib.assert(bus.is_connected()); ++ // Chain up base class constructor ++ GLib.Object(connection : bus.get_connection(), ++ object_path : IBus.PATH_PANEL_EXTENSION); ++ ++ m_bus = bus; ++ ++ init_settings(); ++ ++ bind_emoji_shortcut(); ++ } ++ ++ ++ ~PanelBinding() { ++ BindingCommon.unbind_switch_shortcut( ++ BindingCommon.KeyEventFuncType.ANY, ++ m_keybindings); ++ } ++ ++ ++ private void init_settings() { ++ m_settings_panel = new GLib.Settings("org.freedesktop.ibus.panel"); ++ m_settings_emoji = new GLib.Settings("org.freedesktop.ibus.panel.emoji"); ++ ++ m_settings_panel.changed["custom-font"].connect((key) => { ++ BindingCommon.set_custom_font(m_settings_panel, ++ m_settings_emoji, ++ ref m_css_provider); ++ }); ++ ++ m_settings_panel.changed["use-custom-font"].connect((key) => { ++ BindingCommon.set_custom_font(m_settings_panel, ++ m_settings_emoji, ++ ref m_css_provider); ++ }); ++ ++ m_settings_emoji.changed["hotkey"].connect((key) => { ++ BindingCommon.unbind_switch_shortcut( ++ BindingCommon.KeyEventFuncType.EMOJI_TYPING, ++ m_keybindings); ++ bind_emoji_shortcut(); ++ }); ++ ++ m_settings_emoji.changed["font"].connect((key) => { ++ BindingCommon.set_custom_font(m_settings_panel, ++ m_settings_emoji, ++ ref m_css_provider); ++ }); ++ ++ m_settings_emoji.changed["favorites"].connect((key) => { ++ set_emoji_favorites(); ++ }); ++ ++ m_settings_emoji.changed["favorite-annotations"].connect((key) => { ++ set_emoji_favorites(); ++ }); ++ ++ m_settings_emoji.changed["lang"].connect((key) => { ++ set_emoji_lang(); ++ }); ++ ++ m_settings_emoji.changed["has-partial-match"].connect((key) => { ++ set_emoji_partial_match(); ++ }); ++ ++ m_settings_emoji.changed["partial-match-length"].connect((key) => { ++ set_emoji_partial_match(); ++ }); ++ ++ m_settings_emoji.changed["partial-match-condition"].connect((key) => { ++ set_emoji_partial_match(); ++ }); ++ } ++ ++ ++ private void bind_emoji_shortcut() { ++#if EMOJI_DICT ++ string[] accelerators = m_settings_emoji.get_strv("hotkey"); ++ ++ var keybinding_manager = KeybindingManager.get_instance(); ++ ++ foreach (var accelerator in accelerators) { ++ BindingCommon.keybinding_manager_bind( ++ keybinding_manager, ++ ref m_keybindings, ++ accelerator, ++ BindingCommon.KeyEventFuncType.EMOJI_TYPING, ++ handle_emoji_typing, ++ null); ++ } ++#endif ++ } ++ ++ ++ private void set_emoji_favorites() { ++ m_emojier_favorites = m_settings_emoji.get_strv("favorites"); ++ IBusEmojier.set_favorites( ++ m_emojier_favorites, ++ m_settings_emoji.get_strv("favorite-annotations")); ++ } ++ ++ ++ private void set_emoji_lang() { ++ if (m_emojier_set_emoji_lang_id > 0) { ++ GLib.Source.remove(m_emojier_set_emoji_lang_id); ++ m_emojier_set_emoji_lang_id = 0; ++ } ++ m_emojier_set_emoji_lang_id = GLib.Idle.add(() => { ++ IBusEmojier.set_annotation_lang( ++ m_settings_emoji.get_string("lang")); ++ m_emojier_set_emoji_lang_id = 0; ++ IBusEmojier.load_unicode_dict(); ++ return false; ++ }); ++ } ++ ++ ++ private void set_emoji_partial_match() { ++ IBusEmojier.set_partial_match( ++ m_settings_emoji.get_boolean("has-partial-match")); ++ IBusEmojier.set_partial_match_length( ++ m_settings_emoji.get_int("partial-match-length")); ++ IBusEmojier.set_partial_match_condition( ++ m_settings_emoji.get_int("partial-match-condition")); ++ } ++ ++ ++ public void load_settings() { ++ BindingCommon.unbind_switch_shortcut(BindingCommon.KeyEventFuncType.ANY, ++ m_keybindings); ++ bind_emoji_shortcut(); ++ BindingCommon.set_custom_font(m_settings_panel, ++ m_settings_emoji, ++ ref m_css_provider); ++ set_emoji_favorites(); ++ set_emoji_lang(); ++ set_emoji_partial_match(); ++ } ++ ++ ++ /** ++ * disconnect_signals: ++ * ++ * Call this API before m_panel = null so that the ref_count becomes 0 ++ */ ++ public void disconnect_signals() { ++ if (m_emojier_set_emoji_lang_id > 0) { ++ GLib.Source.remove(m_emojier_set_emoji_lang_id); ++ m_emojier_set_emoji_lang_id = 0; ++ } ++ } ++ ++ ++ private void show_emojier(Gdk.Event event) { ++ m_emojier = new IBusEmojier(); ++ string emoji = m_emojier.run(m_real_current_context_path, event); ++ if (emoji == null) { ++ m_emojier = null; ++ return; ++ } ++ this.emojier_focus_commit(); ++ } ++ ++ ++ private void handle_emoji_typing(Gdk.Event event) { ++ if (m_emojier != null && m_emojier.is_running()) { ++ m_emojier.present_centralize(event); ++ return; ++ } ++ show_emojier(event); ++ } ++ ++ ++ private bool emojier_focus_commit_real() { ++ if (m_emojier == null) ++ return true; ++ string selected_string = m_emojier.get_selected_string(); ++ string prev_context_path = m_emojier.get_input_context_path(); ++ if (selected_string != null && ++ prev_context_path != "" && ++ prev_context_path == m_current_context_path) { ++ IBus.Text text = new IBus.Text.from_string(selected_string); ++ commit_text(text); ++ m_emojier = null; ++ bool has_favorite = false; ++ foreach (unowned string favorite in m_emojier_favorites) { ++ if (favorite == selected_string) { ++ has_favorite = true; ++ break; ++ } ++ } ++ if (!has_favorite) { ++ m_emojier_favorites += selected_string; ++ m_settings_emoji.set_strv("favorites", m_emojier_favorites); ++ } ++ return true; ++ } ++ ++ return false; ++ } ++ ++ ++ private void emojier_focus_commit() { ++ if (m_emojier == null) ++ return; ++ string selected_string = m_emojier.get_selected_string(); ++ string prev_context_path = m_emojier.get_input_context_path(); ++ if (selected_string == null && ++ prev_context_path != "" && ++ m_emojier.is_running()) { ++ var context = GLib.MainContext.default(); ++ if (m_emojier_focus_commit_text_id > 0 && ++ context.find_source_by_id(m_emojier_focus_commit_text_id) ++ != null) { ++ GLib.Source.remove(m_emojier_focus_commit_text_id); ++ } ++ m_emojier_focus_commit_text_id = GLib.Timeout.add(100, () => { ++ // focus_in is comming before switcher returns ++ emojier_focus_commit_real(); ++ m_emojier_focus_commit_text_id = -1; ++ return false; ++ }); ++ } else { ++ if (emojier_focus_commit_real()) { ++ var context = GLib.MainContext.default(); ++ if (m_emojier_focus_commit_text_id > 0 && ++ context.find_source_by_id(m_emojier_focus_commit_text_id) ++ != null) { ++ GLib.Source.remove(m_emojier_focus_commit_text_id); ++ } ++ m_emojier_focus_commit_text_id = -1; ++ } ++ } ++ } ++ ++ ++ public override void focus_in(string input_context_path) { ++ m_current_context_path = input_context_path; ++ ++ /* 'fake' input context is named as ++ * '/org/freedesktop/IBus/InputContext_1' and always send in ++ * focus-out events by ibus-daemon for the global engine mode. ++ * Now ibus-daemon assumes to always use the global engine. ++ * But this event should not be used for modal dialogs ++ * such as Switcher. ++ */ ++ if (!input_context_path.has_suffix("InputContext_1")) { ++ m_real_current_context_path = m_current_context_path; ++ this.emojier_focus_commit(); ++ } ++ } ++ ++ ++ public override void focus_out(string input_context_path) { ++ m_current_context_path = ""; ++ } ++ ++ ++ public override void panel_extension_received(GLib.Variant data) { ++ IBus.XEvent? xevent = IBus.Serializable.deserialize_object(data) ++ as IBus.XEvent; ++ if (xevent == null) { ++ warning ("Failed to deserialize IBusXEvent"); ++ return; ++ } ++ if (xevent.get_purpose() != "emoji") { ++ string format = "The purpose %s is not implemented in PanelExtension"; ++ warning (format.printf(xevent.get_purpose())); ++ return; ++ } ++ Gdk.EventType event_type; ++ if (xevent.get_event_type() == IBus.XEventType.KEY_PRESS) { ++ event_type = Gdk.EventType.KEY_PRESS; ++ } else if (xevent.get_event_type() == IBus.XEventType.KEY_RELEASE) { ++ event_type = Gdk.EventType.KEY_RELEASE; ++ } else { ++ warning ("Not supported type %d".printf(xevent.get_event_type())); ++ return; ++ } ++ Gdk.Event event = new Gdk.Event(event_type); ++ event.key.time = xevent.get_time(); ++ Gdk.Display? display = Gdk.Display.get_default(); ++ X.Window xid = xevent.get_window(); ++ Gdk.X11.Window window; ++ window = Gdk.X11.Window.lookup_for_display( ++ display as Gdk.X11.Display, xid); ++ if (window != null) { ++ event.key.window = window; ++ } else { ++ window = new Gdk.X11.Window.foreign_for_display( ++ display as Gdk.X11.Display, xid); ++ event.key.window = window; ++ } ++ handle_emoji_typing(event); ++ } ++} +-- +2.14.3 + +From 366963d57d1468914611c71929cc64c83be9affd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Piotr=20Dr=C4=85g?= +Date: Tue, 20 Feb 2018 18:57:32 +0900 +Subject: [PATCH] Fix typos in translatable strings +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BUG=https://github.com/ibus/ibus/pull/1983 +R=Shawn.P.Huang@gmail.com + +Review URL: https://codereview.appspot.com/333670043 + +Patch from Piotr Drąg . +--- + data/ibus.schemas.in | 6 +++--- + ui/gtk3/emojierapp.vala | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in +index 278362d7..4523ccc4 100644 +--- a/data/ibus.schemas.in ++++ b/data/ibus.schemas.in +@@ -64,7 +64,7 @@ + [ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua] + + Latin layouts which have no ASCII +- US layout is appended to the latin layouts. variant can be ++ US layout is appended to the Latin layouts. variant can be + omitted. + + +@@ -299,7 +299,7 @@ + and blue, 3. a RGB color in form 'rgb(r,g,b)' or + 4. a RGBA color in form 'rgba(r,g,b,a)' where 'r', + 'g', and 'b' are either integers in the range 0 to 255 +- or precentage values in the range 0% to 100%, and ++ or percentage values in the range 0% to 100%, and + 'a' is a floating point value in the range 0 to 1 + of the alpha. + +@@ -373,7 +373,7 @@ + Monospace 16 + + Custom font +- Custom font name for emoji chracters on emoji dialog ++ Custom font name for emoji characters on emoji dialog + + + +diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala +index d816352e..efedf344 100644 +--- a/ui/gtk3/emojierapp.vala ++++ b/ui/gtk3/emojierapp.vala +@@ -94,7 +94,7 @@ public class EmojiApplication : Application { + /* TRANSLATORS: "FONT" should be capital and translatable. + * It's used for an argument command --font=FONT + */ +- N_("\"FONT\" for emoji chracters on emoji dialog"), ++ N_("\"FONT\" for emoji characters on emoji dialog"), + N_("FONT") }, + { "lang", 0, 0, OptionArg.STRING, out annotation_lang, + /* TRANSLATORS: "LANG" should be capital and translatable. +-- +2.14.3 + +From d1ebb3d77ebfe8f58188261c383d8122e2125fe6 Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Wed, 21 Feb 2018 12:12:11 +0900 +Subject: [PATCH] ui/gtk3: Show code points on Unicode name list dialog + +The code points are useful since the list has many names. + +R=Shawn.P.Huang@gmail.com + +Review URL: https://codereview.appspot.com/340770043 +--- + ui/gtk3/emojier.vala | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala +index 0bf34da8..c85dfa86 100644 +--- a/ui/gtk3/emojier.vala ++++ b/ui/gtk3/emojier.vala +@@ -198,7 +198,8 @@ public class IBusEmojier : Gtk.ApplicationWindow { + private class EPaddedLabelBox : Gtk.Box { + public EPaddedLabelBox(string text, + Gtk.Align align, +- TravelDirection direction=TravelDirection.NONE) { ++ TravelDirection direction=TravelDirection.NONE, ++ string? caption=null) { + GLib.Object( + name : "IBusEmojierPaddedLabelBox", + orientation : Gtk.Orientation.HORIZONTAL, +@@ -218,6 +219,11 @@ public class IBusEmojier : Gtk.ApplicationWindow { + } + EPaddedLabel label = new EPaddedLabel(text, align); + pack_start(label, true, true, 0); ++ if (caption != null) { ++ EPaddedLabel label_r = new EPaddedLabel(caption, ++ Gtk.Align.END); ++ pack_end(label_r, true, true, 0); ++ } + } + } + private class ETitleLabelBox : Gtk.HeaderBar { +@@ -979,9 +985,13 @@ public class IBusEmojier : Gtk.ApplicationWindow { + uint n = 0; + foreach (unowned IBus.UnicodeBlock block in m_unicode_block_list) { + string name = block.get_name(); ++ string caption = "U+%08X".printf(block.get_start()); + EBoxRow row = new EBoxRow(name); + EPaddedLabelBox widget = +- new EPaddedLabelBox(_(name), Gtk.Align.CENTER); ++ new EPaddedLabelBox(_(name), ++ Gtk.Align.CENTER, ++ TravelDirection.NONE, ++ caption); + row.add(widget); + m_list_box.add(row); + if (n++ == m_category_active_index) { +-- +2.14.3 + +From fc54b0c051c2eb4a2c1f836b1415def00314cfc1 Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Wed, 21 Feb 2018 12:17:31 +0900 +Subject: [PATCH] ui/gtk3: Load Unicode data when open the dialog by + default + +The emoji data requires about 10MB and the Unicode data requires about 15MB. +Now the emoji data is loaded at the time of startup and the Unicode data +is loaded if users open the dialog at the beginning. +The settings can be customized with gsettings command and the keys +of 'load-emoji-at-startup' and 'load-unicode-at-startup' in +'org.freedesktop.ibus.panel.emoji' schema. + +Review URL: https://codereview.appspot.com/340780043 +--- + data/ibus.schemas.in | 32 ++++++++++++++++++++++++++++++++ + ui/gtk3/panelbinding.vala | 41 +++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 71 insertions(+), 2 deletions(-) + +diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in +index 4523ccc4..3c6b6f69 100644 +--- a/data/ibus.schemas.in ++++ b/data/ibus.schemas.in +@@ -455,6 +455,38 @@ + + + ++ ++ /schemas/desktop/ibus/panel/emoji/load-emoji-at-startup ++ /desktop/ibus/panel/emoji/load-emoji-at-startup ++ ibus ++ bool ++ true ++ ++ Load the emoji data at the time of startup ++ Load the emoji data at the time of startup if true. ++ About 10MB memory is needed to load the data. ++ Load the emoji data when open the emoji dialog at the ++ beginning if false. ++ ++ ++ ++ ++ /schemas/desktop/ibus/panel/emoji/load-unicode-at-startup ++ /desktop/ibus/panel/emoji/load-unicode-at-startup ++ ibus ++ bool ++ false ++ ++ Load the Unicode data at the time of startup ++ Load the Unicode data at the time of startup if true. ++ About 15MB memory is needed to load the data. ++ Load the Unicode data when open the emoji dialog at the ++ beginning if false. ++ The Unicode data is always loaded after the emoji data ++ is loaded even if true. ++ ++ ++ + + /schemas/desktop/ibus/general/embed_preedit_text + /desktop/ibus/general/embed_preedit_text +diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala +index 50700121..1fbf6cfc 100644 +--- a/ui/gtk3/panelbinding.vala ++++ b/ui/gtk3/panelbinding.vala +@@ -35,6 +35,10 @@ class PanelBinding : IBus.PanelService { + private const uint PRELOAD_ENGINES_DELAY_TIME = 30000; + private GLib.List m_keybindings = + new GLib.List(); ++ private bool m_load_emoji_at_startup; ++ private bool m_loaded_emoji = false; ++ private bool m_load_unicode_at_startup; ++ private bool m_loaded_unicode = false; + + public PanelBinding(IBus.Bus bus) { + GLib.assert(bus.is_connected()); +@@ -109,6 +113,14 @@ class PanelBinding : IBus.PanelService { + m_settings_emoji.changed["partial-match-condition"].connect((key) => { + set_emoji_partial_match(); + }); ++ ++ m_settings_emoji.changed["load-emoji-at-startup"].connect((key) => { ++ set_load_emoji_at_startup(); ++ }); ++ ++ m_settings_emoji.changed["load-unicode-at-startup"].connect((key) => { ++ set_load_unicode_at_startup(); ++ }); + } + + +@@ -148,7 +160,11 @@ class PanelBinding : IBus.PanelService { + IBusEmojier.set_annotation_lang( + m_settings_emoji.get_string("lang")); + m_emojier_set_emoji_lang_id = 0; +- IBusEmojier.load_unicode_dict(); ++ m_loaded_emoji = true; ++ if (m_load_unicode_at_startup && !m_loaded_unicode) { ++ IBusEmojier.load_unicode_dict(); ++ m_loaded_unicode = true; ++ } + return false; + }); + } +@@ -164,7 +180,21 @@ class PanelBinding : IBus.PanelService { + } + + ++ private void set_load_emoji_at_startup() { ++ m_load_emoji_at_startup = ++ m_settings_emoji.get_boolean("load-emoji-at-startup"); ++ } ++ ++ ++ private void set_load_unicode_at_startup() { ++ m_load_unicode_at_startup = ++ m_settings_emoji.get_boolean("load-unicode-at-startup"); ++ } ++ + public void load_settings() { ++ ++ set_load_emoji_at_startup(); ++ set_load_unicode_at_startup(); + BindingCommon.unbind_switch_shortcut(BindingCommon.KeyEventFuncType.ANY, + m_keybindings); + bind_emoji_shortcut(); +@@ -172,7 +202,8 @@ class PanelBinding : IBus.PanelService { + m_settings_emoji, + ref m_css_provider); + set_emoji_favorites(); +- set_emoji_lang(); ++ if (m_load_emoji_at_startup && !m_loaded_emoji) ++ set_emoji_lang(); + set_emoji_partial_match(); + } + +@@ -191,6 +222,12 @@ class PanelBinding : IBus.PanelService { + + + private void show_emojier(Gdk.Event event) { ++ if (!m_loaded_emoji) ++ set_emoji_lang(); ++ if (!m_loaded_unicode && m_loaded_emoji) { ++ IBusEmojier.load_unicode_dict(); ++ m_loaded_unicode = true; ++ } + m_emojier = new IBusEmojier(); + string emoji = m_emojier.run(m_real_current_context_path, event); + if (emoji == null) { +-- +2.14.3 + diff --git a/ibus-xx-emoji-harfbuzz.patch b/ibus-xx-emoji-harfbuzz.patch index 89a37b1..e948a6f 100644 --- a/ibus-xx-emoji-harfbuzz.patch +++ b/ibus-xx-emoji-harfbuzz.patch @@ -1,6 +1,6 @@ -From c6c1e8ea01c8466dc97d7549e77538e2d7ec872a Mon Sep 17 00:00:00 2001 +From 158e06a10726a10393f1f6dd7237457b0b601f84 Mon Sep 17 00:00:00 2001 From: fujiwarat -Date: Mon, 29 Jan 2018 18:27:09 +0900 +Date: Wed, 21 Feb 2018 15:39:49 +0900 Subject: [PATCH] Integrate custom rendering to use HarfBuzz glyph info IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering @@ -20,11 +20,11 @@ Need configure --enable-harfbuzz-for-emoji option to enable this feature. bindings/vala/Makefile.am | 83 +++ bindings/vala/ibus-fontset-1.0.deps | 1 + configure.ac | 29 + - ui/gtk3/Makefile.am | 32 + + ui/gtk3/Makefile.am | 36 ++ ui/gtk3/emojier.vala | 111 ++++ ui/gtk3/ibusfontset.c | 1030 ++++++++++++++++++++++++++++++++ ui/gtk3/ibusfontset.h | 302 ++++++++++ - 8 files changed, 1589 insertions(+) + 8 files changed, 1593 insertions(+) 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 @@ -202,19 +202,19 @@ index bd41069b..243396ff 100644 ]) diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am -index 786b80e6..cd1e9c2c 100644 +index 0a8f4200..8bb2345d 100644 --- a/ui/gtk3/Makefile.am +++ b/ui/gtk3/Makefile.am -@@ -156,6 +156,8 @@ EXTRA_DIST = \ - $(man_seven_in_files) \ - emojierapp.vala \ +@@ -159,6 +159,8 @@ EXTRA_DIST = \ + extension.vala \ + gtkextension.xml.in \ gtkpanel.xml.in \ + ibusfontset.c \ + ibusfontset.h \ notification-item.xml \ notification-watcher.xml \ - $(NULL) -@@ -198,6 +200,36 @@ emojierapp.o: $(srcdir)/emojierapp.c + panelbinding.vala \ +@@ -247,6 +249,40 @@ panelbinding.o: $(srcdir)/panelbinding.c $(AM_V_CC_no)$(COMPILE) -c -o $@ $< $(NULL) @@ -227,6 +227,10 @@ index 786b80e6..cd1e9c2c 100644 + ibusfontset.c \ + $(NULL) + ++ibus_extension_gtk3_SOURCES += \ ++ ibusfontset.c \ ++ $(NULL) ++ +AM_CFLAGS += \ + @CAIRO_CFLAGS@ \ + @FONTCONFIG_CFLAGS@ \ @@ -252,7 +256,7 @@ index 786b80e6..cd1e9c2c 100644 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 555ea68f..0a703383 100644 +index c85dfa86..86482543 100644 --- a/ui/gtk3/emojier.vala +++ b/ui/gtk3/emojier.vala @@ -99,16 +99,103 @@ public class IBusEmojier : Gtk.ApplicationWindow { @@ -367,7 +371,7 @@ index 555ea68f..0a703383 100644 } private class ESelectedLabel : EWhiteLabel { public ESelectedLabel(string text) { -@@ -307,6 +395,9 @@ public class IBusEmojier : Gtk.ApplicationWindow { +@@ -313,6 +401,9 @@ public class IBusEmojier : Gtk.ApplicationWindow { private static bool m_show_unicode = false; private static LoadProgressObject m_unicode_progress_object; private static bool m_loaded_unicode = false; @@ -377,7 +381,7 @@ index 555ea68f..0a703383 100644 private ThemedRGBA m_rgba; private Gtk.Box m_vbox; -@@ -2064,6 +2155,22 @@ public class IBusEmojier : Gtk.ApplicationWindow { +@@ -2070,6 +2161,22 @@ public class IBusEmojier : Gtk.ApplicationWindow { } @@ -400,7 +404,7 @@ index 555ea68f..0a703383 100644 public static bool has_loaded_emoji_dict() { if (m_emoji_to_data_dict == null) return false; -@@ -2094,6 +2201,10 @@ public class IBusEmojier : Gtk.ApplicationWindow { +@@ -2100,6 +2207,10 @@ public class IBusEmojier : Gtk.ApplicationWindow { int font_size = font_desc.get_size() / Pango.SCALE; if (font_size != 0) m_emoji_font_size = font_size; diff --git a/ibus.spec b/ibus.spec index 0b17381..455a95d 100644 --- a/ibus.spec +++ b/ibus.spec @@ -30,7 +30,7 @@ Name: ibus Version: 1.5.17 -Release: 8%{?dist} +Release: 9%{?dist} Summary: Intelligent Input Bus for Linux OS License: LGPLv2+ Group: System Environment/Libraries @@ -38,16 +38,17 @@ URL: https://github.com/ibus/%name/wiki Source0: https://github.com/ibus/%name/releases/download/%{version}/%{name}-%{version}.tar.gz Source1: %{name}-xinput Source2: %{name}.conf.5 +Source3: https://fujiwara.fedorapeople.org/ibus/po/%{name}-po-1.5.17-20180221.tar.gz # Will remove the annotation tarball once the rpm is available on Fedora # Upstreamed patches. # Patch0: %%{name}-HEAD.patch Patch0: %{name}-HEAD.patch -# 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 +Patch1: %{name}-xx-emoji-harfbuzz.patch %endif +# Under testing #1349148 #1385349 #1350291 #1406699 #1432252 +Patch2: %{name}-1385349-segv-bus-proxy.patch BuildRequires: gettext-devel BuildRequires: libtool @@ -243,10 +244,12 @@ The ibus-devel-docs package contains developer documentation for IBus # %%patch0 -p1 %patch0 -p1 # cp client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c || -%patch1 -p1 -z .segv %if %with_emoji_harfbuzz -%patch2 -p1 -z .hb +%patch1 -p1 -z .hb %endif +%patch2 -p1 -z .segv + +zcat %SOURCE3 | tar xfvp - # prep test diff client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c @@ -360,6 +363,7 @@ dconf update || : %{_libexecdir}/ibus-engine-simple %{_libexecdir}/ibus-dconf %{_libexecdir}/ibus-portal +%{_libexecdir}/ibus-extension-gtk3 %{_libexecdir}/ibus-ui-emojier %{_libexecdir}/ibus-ui-gtk3 %{_libexecdir}/ibus-x11 @@ -420,6 +424,10 @@ dconf update || : %{_datadir}/gtk-doc/html/* %changelog +* Wed Feb 21 2018 Takao Fujiwara - 1.5.17-9 +- Added panel extension for emoji keybinding not to depen on desktops +- Showed Unicode code points on Unicode name list + * Tue Feb 13 2018 Igor Gnatenko - 1.5.17-8 - Remove useless requires diff --git a/sources b/sources index b11174b..5f3638d 100644 --- a/sources +++ b/sources @@ -1 +1,2 @@ -SHA512 (ibus-1.5.17.tar.gz) = 8a7e4fabbcb2096e647b1fb7487c92882bd320a4d777f2765817378abec2e60cafd63364c881fefc2805ff2baa6b28b15ee0710587662a3e65eeb60ead19496c +8bb26453d0d1fa58e56c22668aaa8786 ibus-1.5.17.tar.gz +c485c179e612ffd07cf6a6c567a928f1 ibus-po-1.5.17-20180221.tar.gz