From c57b7c34d75871db172e023b0094b979000f62fc Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Tue, 27 Feb 2018 14:10:29 +0900 Subject: [PATCH] Enable emoji keybinding in Wayland XI2 keybinding does not work for the root window in Wayland because of a security issue maybe. Now I think to move the keybinding in ibus-extension-gtk3 to each IBusEngine. FIXME: Unfortunatelly gtk_get_current_event_time() cannot get time for the delayed DBus events and gtk_window_move() does not work for GtkDialog without a parent window in Wayland. R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/333700043 --- bus/engineproxy.c | 19 +- bus/ibusimpl.c | 47 +++- bus/inputcontext.c | 29 ++- src/Makefile.am | 2 + src/ibus.h | 1 + src/ibusaccelgroup.c | 529 ++++++++++++++++++++++++++++++++++++++ src/ibusaccelgroup.h | 51 ++++ src/ibusengine.c | 179 ++++++++++++- ui/gtk3/emojier.vala | 1 + ui/gtk3/extension.vala | 5 +- ui/gtk3/panelbinding.vala | 65 ++--- 11 files changed, 863 insertions(+), 65 deletions(-) create mode 100644 src/ibusaccelgroup.c create mode 100644 src/ibusaccelgroup.h diff --git a/bus/engineproxy.c b/bus/engineproxy.c index cd4d54fd1..175aec569 100644 --- a/bus/engineproxy.c +++ b/bus/engineproxy.c @@ -2,7 +2,7 @@ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2013 Peng Huang - * Copyright (C) 2015-2016 Takao Fujiwara + * Copyright (C) 2015-2018 Takao Fujiwara * Copyright (C) 2008-2016 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -90,6 +90,7 @@ enum { CURSOR_DOWN_LOOKUP_TABLE, REGISTER_PROPERTIES, UPDATE_PROPERTY, + PANEL_EXTENSION, LAST_SIGNAL, }; @@ -370,6 +371,17 @@ bus_engine_proxy_class_init (BusEngineProxyClass *class) 1, IBUS_TYPE_PROPERTY); + engine_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); + text_empty = ibus_text_new_from_static_string (""); g_object_ref_sink (text_empty); @@ -631,6 +643,11 @@ bus_engine_proxy_g_signal (GDBusProxy *proxy, return; } + if (g_strcmp0 (signal_name, "PanelExtension") == 0) { + g_signal_emit (engine, engine_signals[PANEL_EXTENSION], 0, parameters); + return; + } + g_return_if_reached (); } diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c index 58d205cf8..a4ce3d9d8 100644 --- a/bus/ibusimpl.c +++ b/bus/ibusimpl.c @@ -302,9 +302,8 @@ _panel_destroy_cb (BusPanelProxy *panel, } static void -_panel_panel_extension_cb (BusPanelProxy *panel, - GVariant *parameters, - BusIBusImpl *ibus) +bus_ibus_impl_panel_extension_received (BusIBusImpl *ibus, + GVariant *parameters) { if (!ibus->extension) { g_warning ("Panel extension is not running."); @@ -323,6 +322,14 @@ _panel_panel_extension_cb (BusPanelProxy *panel, -1, NULL, NULL, NULL); } +static void +_panel_panel_extension_cb (BusPanelProxy *panel, + GVariant *parameters, + BusIBusImpl *ibus) +{ + bus_ibus_impl_panel_extension_received (ibus, parameters); +} + static void _registry_changed_cb (IBusRegistry *registry, BusIBusImpl *ibus) @@ -642,6 +649,21 @@ bus_ibus_impl_set_context_engine_from_desc (BusIBusImpl *ibus, NULL); } +static void +_context_panel_extension_cb (BusInputContext *context, + GVariant *parameters, + BusIBusImpl *ibus) +{ + bus_ibus_impl_panel_extension_received (ibus, parameters); +} + +const static struct { + const gchar *name; + GCallback callback; +} context_signals [] = { + { "panel-extension", G_CALLBACK (_context_panel_extension_cb) } +}; + /** * bus_ibus_impl_set_focused_context: * @@ -651,6 +673,11 @@ static void bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, BusInputContext *context) { + gint i; + BusEngineProxy *engine = NULL; + guint purpose = 0; + guint hints = 0; + g_assert (BUS_IS_IBUS_IMPL (ibus)); g_assert (context == NULL || BUS_IS_INPUT_CONTEXT (context)); g_assert (context == NULL || bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS); @@ -660,10 +687,6 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, return; } - BusEngineProxy *engine = NULL; - guint purpose = 0; - guint hints = 0; - if (ibus->focused_context) { if (ibus->use_global_engine) { /* dettach engine from the focused context */ @@ -681,6 +704,10 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, bus_input_context_get_content_type (ibus->focused_context, &purpose, &hints); + for (i = 0; i < G_N_ELEMENTS(context_signals); i++) { + g_signal_handlers_disconnect_by_func (ibus->focused_context, + context_signals[i].callback, ibus); + } g_object_unref (ibus->focused_context); ibus->focused_context = NULL; } @@ -698,6 +725,12 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, bus_input_context_set_engine (context, engine); bus_input_context_enable (context); } + for (i = 0; i < G_N_ELEMENTS(context_signals); i++) { + g_signal_connect (ibus->focused_context, + context_signals[i].name, + context_signals[i].callback, + ibus); + } if (ibus->panel != NULL) bus_panel_proxy_focus_in (ibus->panel, context); diff --git a/bus/inputcontext.c b/bus/inputcontext.c index 4f2ecafc1..a957d1077 100644 --- a/bus/inputcontext.c +++ b/bus/inputcontext.c @@ -127,6 +127,7 @@ enum { ENGINE_CHANGED, REQUEST_ENGINE, SET_CONTENT_TYPE, + PANEL_EXTENSION, LAST_SIGNAL, }; @@ -598,6 +599,17 @@ bus_input_context_class_init (BusInputContextClass *class) G_TYPE_UINT, G_TYPE_UINT); + context_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); + text_empty = ibus_text_new_from_string (""); g_object_ref_sink (text_empty); lookup_table_empty = ibus_lookup_table_new (9 /* page size */, 0, FALSE, FALSE); @@ -2102,6 +2114,20 @@ _engine_update_property_cb (BusEngineProxy *engine, bus_input_context_update_property (context, prop); } +/** + * _engine_panel_extension_cb: + * + * A function to be called when "panel-extension" glib signal is sent + * from the engine object. + */ +static void +_engine_panel_extension_cb (BusEngineProxy *engine, + GVariant *parameters, + BusInputContext *context) +{ + g_signal_emit (context, context_signals[PANEL_EXTENSION], 0, parameters); +} + #define DEFINE_FUNCTION(name) \ static void \ _engine_##name##_cb (BusEngineProxy *engine, \ @@ -2244,7 +2270,8 @@ const static struct { { "cursor-down-lookup-table", G_CALLBACK (_engine_cursor_down_lookup_table_cb) }, { "register-properties", G_CALLBACK (_engine_register_properties_cb) }, { "update-property", G_CALLBACK (_engine_update_property_cb) }, - { "destroy", G_CALLBACK (_engine_destroy_cb) }, + { "panel-extension", G_CALLBACK (_engine_panel_extension_cb) }, + { "destroy", G_CALLBACK (_engine_destroy_cb) } }; static void diff --git a/src/Makefile.am b/src/Makefile.am index 72ec05abb..6a62e0f01 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,6 +72,7 @@ libibus_1_0_la_LDFLAGS = \ $(NULL) ibus_sources = \ + ibusaccelgroup.c \ ibusattribute.c \ ibusattrlist.c \ ibusbus.c \ @@ -122,6 +123,7 @@ ibus_enumtypes_sources = \ $(NULL) ibus_headers = \ ibus.h \ + ibusaccelgroup.h \ ibusattribute.h \ ibusattrlist.h \ ibusbus.h \ diff --git a/src/ibus.h b/src/ibus.h index b15dded90..256d57bac 100644 --- a/src/ibus.h +++ b/src/ibus.h @@ -60,6 +60,7 @@ #include #include #include +#include #ifndef IBUS_DISABLE_DEPRECATED #include diff --git a/src/ibusaccelgroup.c b/src/ibusaccelgroup.c new file mode 100644 index 000000000..8a81597ec --- /dev/null +++ b/src/ibusaccelgroup.c @@ -0,0 +1,529 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1998, 2001 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include +#include + +#include "ibusaccelgroup.h" +#include "ibuskeys.h" +#include "ibuskeysyms.h" + + +/* for _gtk_get_primary_accel_mod() */ +#define _IBUS_GET_PRIMARY_ACCEL_MOD IBUS_CONTROL_MASK + +/** + * SECTION: ibusaccelgroup + * @short_description: Groups of global keyboard accelerators for an + * entire GtkWindow + * @title: Accelerator Groups + * @stability: Unstable + * + * Provides ibus_accelerator_parse() + */ + + +/** + * ibus_accelerator_valid: + * @keyval: a GDK keyval + * @modifiers: modifier mask + * + * Determines whether a given keyval and modifier mask constitute + * a valid keyboard accelerator. For example, the #IBUS_KEY_a keyval + * plus #IBUS_CONTROL_MASK is valid - this is a “Ctrl+a” accelerator. + * But, you can't, for instance, use the #IBUS_KEY_Control_L keyval + * as an accelerator. + * + * Returns: %TRUE if the accelerator is valid + */ +gboolean +ibus_accelerator_valid (guint keyval, + IBusModifierType modifiers) +{ + static const guint invalid_accelerator_vals[] = { + IBUS_KEY_Shift_L, IBUS_KEY_Shift_R, IBUS_KEY_Shift_Lock, + IBUS_KEY_Caps_Lock, IBUS_KEY_ISO_Lock, IBUS_KEY_Control_L, + IBUS_KEY_Control_R, IBUS_KEY_Meta_L, IBUS_KEY_Meta_R, + IBUS_KEY_Alt_L, IBUS_KEY_Alt_R, IBUS_KEY_Super_L, IBUS_KEY_Super_R, + IBUS_KEY_Hyper_L, IBUS_KEY_Hyper_R, IBUS_KEY_ISO_Level3_Shift, + IBUS_KEY_ISO_Next_Group, IBUS_KEY_ISO_Prev_Group, + IBUS_KEY_ISO_First_Group, IBUS_KEY_ISO_Last_Group, + IBUS_KEY_Mode_switch, IBUS_KEY_Num_Lock, IBUS_KEY_Multi_key, + IBUS_KEY_Scroll_Lock, IBUS_KEY_Sys_Req, + IBUS_KEY_Tab, IBUS_KEY_ISO_Left_Tab, IBUS_KEY_KP_Tab, + IBUS_KEY_First_Virtual_Screen, IBUS_KEY_Prev_Virtual_Screen, + IBUS_KEY_Next_Virtual_Screen, IBUS_KEY_Last_Virtual_Screen, + IBUS_KEY_Terminate_Server, IBUS_KEY_AudibleBell_Enable, + 0 + }; + static const guint invalid_unmodified_vals[] = { + IBUS_KEY_Up, IBUS_KEY_Down, IBUS_KEY_Left, IBUS_KEY_Right, + IBUS_KEY_KP_Up, IBUS_KEY_KP_Down, IBUS_KEY_KP_Left, IBUS_KEY_KP_Right, + 0 + }; + const guint *ac_val; + + modifiers &= IBUS_MODIFIER_MASK; + + if (keyval <= 0xFF) + return keyval >= 0x20; + + ac_val = invalid_accelerator_vals; + while (*ac_val) { + if (keyval == *ac_val++) + return FALSE; + } + + if (!modifiers) { + ac_val = invalid_unmodified_vals; + while (*ac_val) { + if (keyval == *ac_val++) + return FALSE; + } + } + + return TRUE; +} + +static inline gboolean +is_alt (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'a' || string[1] == 'A') && + (string[2] == 'l' || string[2] == 'L') && + (string[3] == 't' || string[3] == 'T') && + (string[4] == '>')); +} + +static inline gboolean +is_ctl (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 't' || string[2] == 'T') && + (string[3] == 'l' || string[3] == 'L') && + (string[4] == '>')); +} + +static inline gboolean +is_modx (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'm' || string[1] == 'M') && + (string[2] == 'o' || string[2] == 'O') && + (string[3] == 'd' || string[3] == 'D') && + (string[4] >= '1' && string[4] <= '5') && + (string[5] == '>')); +} + +static inline gboolean +is_ctrl (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 't' || string[2] == 'T') && + (string[3] == 'r' || string[3] == 'R') && + (string[4] == 'l' || string[4] == 'L') && + (string[5] == '>')); +} + +static inline gboolean +is_shft (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'h' || string[2] == 'H') && + (string[3] == 'f' || string[3] == 'F') && + (string[4] == 't' || string[4] == 'T') && + (string[5] == '>')); +} + +static inline gboolean +is_shift (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'h' || string[2] == 'H') && + (string[3] == 'i' || string[3] == 'I') && + (string[4] == 'f' || string[4] == 'F') && + (string[5] == 't' || string[5] == 'T') && + (string[6] == '>')); +} + +static inline gboolean +is_control (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 'o' || string[2] == 'O') && + (string[3] == 'n' || string[3] == 'N') && + (string[4] == 't' || string[4] == 'T') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == 'o' || string[6] == 'O') && + (string[7] == 'l' || string[7] == 'L') && + (string[8] == '>')); +} + +static inline gboolean +is_release (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'r' || string[1] == 'R') && + (string[2] == 'e' || string[2] == 'E') && + (string[3] == 'l' || string[3] == 'L') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'a' || string[5] == 'A') && + (string[6] == 's' || string[6] == 'S') && + (string[7] == 'e' || string[7] == 'E') && + (string[8] == '>')); +} + +static inline gboolean +is_meta (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'm' || string[1] == 'M') && + (string[2] == 'e' || string[2] == 'E') && + (string[3] == 't' || string[3] == 'T') && + (string[4] == 'a' || string[4] == 'A') && + (string[5] == '>')); +} + +static inline gboolean +is_super (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'u' || string[2] == 'U') && + (string[3] == 'p' || string[3] == 'P') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == '>')); +} + +static inline gboolean +is_hyper (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'h' || string[1] == 'H') && + (string[2] == 'y' || string[2] == 'Y') && + (string[3] == 'p' || string[3] == 'P') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == '>')); +} + +static inline gboolean +is_primary (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'p' || string[1] == 'P') && + (string[2] == 'r' || string[2] == 'R') && + (string[3] == 'i' || string[3] == 'I') && + (string[4] == 'm' || string[4] == 'M') && + (string[5] == 'a' || string[5] == 'A') && + (string[6] == 'r' || string[6] == 'R') && + (string[7] == 'y' || string[7] == 'Y') && + (string[8] == '>')); +} + +static inline gboolean +is_keycode (const gchar *string) +{ + return (string[0] == '0' && + string[1] == 'x' && + g_ascii_isxdigit (string[2]) && + g_ascii_isxdigit (string[3])); +} + +/** + * ibus_accelerator_parse: + * @accelerator: string representing an accelerator + * @accelerator_key: (out) (allow-none): return location for accelerator + * keyval, or %NULL + * @accelerator_mods: (out) (allow-none): return location for accelerator + * modifier mask, %NULL + * + * Parses a string representing an accelerator. The format looks like + * “a” or “F1” or “z” (the last one is + * for key release). + * + * The parser is fairly liberal and allows lower or upper case, and also + * abbreviations such as “” and “”. Key names are parsed using + * gdk_keyval_from_name(). For character keys the name is not the symbol, + * but the lowercase name, e.g. one would use “minus” instead of + * “-”. + * + * If the parse fails, @accelerator_key and @accelerator_mods will + * be set to 0 (zero). + * + * Since: 1.5.18 + */ +void +ibus_accelerator_parse (const gchar *accelerator, + guint *accelerator_key, + IBusModifierType *accelerator_mods) +{ + guint keyval; + IBusModifierType mods; + gint len; + gboolean error; + + if (accelerator_key) + *accelerator_key = 0; + if (accelerator_mods) + *accelerator_mods = 0; + g_return_if_fail (accelerator != NULL); + + error = FALSE; + keyval = 0; + mods = 0; + len = strlen (accelerator); + while (len) { + if (*accelerator == '<') { + if (len >= 9 && is_release (accelerator)) { + accelerator += 9; + len -= 9; + mods |= IBUS_RELEASE_MASK; + } else if (len >= 9 && is_primary (accelerator)) { + accelerator += 9; + len -= 9; + mods |= _IBUS_GET_PRIMARY_ACCEL_MOD; + } else if (len >= 9 && is_control (accelerator)) { + accelerator += 9; + len -= 9; + mods |= IBUS_CONTROL_MASK; + } else if (len >= 7 && is_shift (accelerator)) { + accelerator += 7; + len -= 7; + mods |= IBUS_SHIFT_MASK; + } else if (len >= 6 && is_shft (accelerator)) { + accelerator += 6; + len -= 6; + mods |= IBUS_SHIFT_MASK; + } else if (len >= 6 && is_ctrl (accelerator)) { + accelerator += 6; + len -= 6; + mods |= IBUS_CONTROL_MASK; + } else if (len >= 6 && is_modx (accelerator)) { + static const guint mod_vals[] = { + IBUS_MOD1_MASK, IBUS_MOD2_MASK, IBUS_MOD3_MASK, + IBUS_MOD4_MASK, IBUS_MOD5_MASK + }; + + len -= 6; + accelerator += 4; + mods |= mod_vals[*accelerator - '1']; + accelerator += 2; + } else if (len >= 5 && is_ctl (accelerator)) { + accelerator += 5; + len -= 5; + mods |= IBUS_CONTROL_MASK; + } else if (len >= 5 && is_alt (accelerator)) { + accelerator += 5; + len -= 5; + mods |= IBUS_MOD1_MASK; + } else if (len >= 6 && is_meta (accelerator)) { + accelerator += 6; + len -= 6; + mods |= IBUS_META_MASK; + } else if (len >= 7 && is_hyper (accelerator)) { + accelerator += 7; + len -= 7; + mods |= IBUS_HYPER_MASK; + } else if (len >= 7 && is_super (accelerator)) { + accelerator += 7; + len -= 7; + mods |= IBUS_SUPER_MASK; + } else { + gchar last_ch; + + last_ch = *accelerator; + while (last_ch && last_ch != '>') { + last_ch = *accelerator; + accelerator += 1; + len -= 1; + } + } + } else { + if (len >= 4 && is_keycode (accelerator)) { + /* There was a keycode in the string, but + * we cannot store it, so we have an error */ + error = TRUE; + goto out; + } else { + keyval = ibus_keyval_from_name (accelerator); + if (keyval == IBUS_KEY_VoidSymbol) { + error = TRUE; + goto out; + } + } + + accelerator += len; + len -= len; + } + } + +out: + if (error) + keyval = mods = 0; + + if (accelerator_key) + *accelerator_key = ibus_keyval_to_lower (keyval); + if (accelerator_mods) + *accelerator_mods = mods; +} + +/** + * ibus_accelerator_name: + * @accelerator_key: accelerator keyval + * @accelerator_mods: accelerator modifier mask + * + * Converts an accelerator keyval and modifier mask into a string + * parseable by gtk_accelerator_parse(). For example, if you pass in + * #IBUS_KEY_q and #IBUS_CONTROL_MASK, this function returns “q”. + * + * If you need to display accelerators in the user interface, + * see gtk_accelerator_get_label(). + * + * Returns: a newly-allocated accelerator name + */ +gchar* +ibus_accelerator_name (guint accelerator_key, + IBusModifierType accelerator_mods) +{ + static const gchar text_release[] = ""; + static const gchar text_primary[] = ""; + static const gchar text_shift[] = ""; + static const gchar text_control[] = ""; + static const gchar text_mod1[] = ""; + static const gchar text_mod2[] = ""; + static const gchar text_mod3[] = ""; + static const gchar text_mod4[] = ""; + static const gchar text_mod5[] = ""; + static const gchar text_meta[] = ""; + static const gchar text_super[] = ""; + static const gchar text_hyper[] = ""; + IBusModifierType saved_mods; + guint l; + const gchar *keyval_name; + gchar *accelerator; + + accelerator_mods &= IBUS_MODIFIER_MASK; + + keyval_name = ibus_keyval_name (ibus_keyval_to_lower (accelerator_key)); + if (!keyval_name) + keyval_name = ""; + + saved_mods = accelerator_mods; + l = 0; + if (accelerator_mods & IBUS_RELEASE_MASK) + l += sizeof (text_release) - 1; + if (accelerator_mods & _IBUS_GET_PRIMARY_ACCEL_MOD) { + l += sizeof (text_primary) - 1; + /* consume the default accel */ + accelerator_mods &= ~_IBUS_GET_PRIMARY_ACCEL_MOD; + } + if (accelerator_mods & IBUS_SHIFT_MASK) + l += sizeof (text_shift) - 1; + if (accelerator_mods & IBUS_CONTROL_MASK) + l += sizeof (text_control) - 1; + if (accelerator_mods & IBUS_MOD1_MASK) + l += sizeof (text_mod1) - 1; + if (accelerator_mods & IBUS_MOD2_MASK) + l += sizeof (text_mod2) - 1; + if (accelerator_mods & IBUS_MOD3_MASK) + l += sizeof (text_mod3) - 1; + if (accelerator_mods & IBUS_MOD4_MASK) + l += sizeof (text_mod4) - 1; + if (accelerator_mods & IBUS_MOD5_MASK) + l += sizeof (text_mod5) - 1; + l += strlen (keyval_name); + if (accelerator_mods & IBUS_META_MASK) + l += sizeof (text_meta) - 1; + if (accelerator_mods & IBUS_HYPER_MASK) + l += sizeof (text_hyper) - 1; + if (accelerator_mods & IBUS_SUPER_MASK) + l += sizeof (text_super) - 1; + + accelerator = g_new (gchar, l + 1); + + accelerator_mods = saved_mods; + l = 0; + accelerator[l] = 0; + if (accelerator_mods & IBUS_RELEASE_MASK) { + strcpy (accelerator + l, text_release); + l += sizeof (text_release) - 1; + } + if (accelerator_mods & _IBUS_GET_PRIMARY_ACCEL_MOD) { + strcpy (accelerator + l, text_primary); + l += sizeof (text_primary) - 1; + /* consume the default accel */ + accelerator_mods &= ~_IBUS_GET_PRIMARY_ACCEL_MOD; + } + if (accelerator_mods & IBUS_SHIFT_MASK) { + strcpy (accelerator + l, text_shift); + l += sizeof (text_shift) - 1; + } + if (accelerator_mods & IBUS_CONTROL_MASK) { + strcpy (accelerator + l, text_control); + l += sizeof (text_control) - 1; + } + if (accelerator_mods & IBUS_MOD1_MASK) { + strcpy (accelerator + l, text_mod1); + l += sizeof (text_mod1) - 1; + } + if (accelerator_mods & IBUS_MOD2_MASK) { + strcpy (accelerator + l, text_mod2); + l += sizeof (text_mod2) - 1; + } + if (accelerator_mods & IBUS_MOD3_MASK) { + strcpy (accelerator + l, text_mod3); + l += sizeof (text_mod3) - 1; + } + if (accelerator_mods & IBUS_MOD4_MASK) { + strcpy (accelerator + l, text_mod4); + l += sizeof (text_mod4) - 1; + } + if (accelerator_mods & IBUS_MOD5_MASK) { + strcpy (accelerator + l, text_mod5); + l += sizeof (text_mod5) - 1; + } + if (accelerator_mods & IBUS_META_MASK) { + strcpy (accelerator + l, text_meta); + l += sizeof (text_meta) - 1; + } + if (accelerator_mods & IBUS_HYPER_MASK) { + strcpy (accelerator + l, text_hyper); + l += sizeof (text_hyper) - 1; + } + if (accelerator_mods & IBUS_SUPER_MASK) { + strcpy (accelerator + l, text_super); + l += sizeof (text_super) - 1; + } + strcpy (accelerator + l, keyval_name); + + return accelerator; +} diff --git a/src/ibusaccelgroup.h b/src/ibusaccelgroup.h new file mode 100644 index 000000000..cb38bee40 --- /dev/null +++ b/src/ibusaccelgroup.h @@ -0,0 +1,51 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1998, 2001 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __IBUS_ACCEL_GROUP_H_ +#define __IBUS_ACCEL_GROUP_H_ + + +#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION) +#error "Only can be included directly" +#endif + +#include +#include + +G_BEGIN_DECLS + + +/* --- Accelerators--- */ +gboolean ibus_accelerator_valid (guint keyval, + IBusModifierType modifiers) + G_GNUC_CONST; +void ibus_accelerator_parse (const gchar *accelerator, + guint *accelerator_key, + IBusModifierType *accelerator_mods); +gchar* ibus_accelerator_name (guint accelerator_key, + IBusModifierType accelerator_mods); + +G_END_DECLS + +#endif /* __IBUS_ACCEL_GROUP_H_ */ diff --git a/src/ibusengine.c b/src/ibusengine.c index da648d11f..9a0b1a8ad 100644 --- a/src/ibusengine.c +++ b/src/ibusengine.c @@ -20,12 +20,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ -#include "ibusengine.h" #include #include + +#include "ibusaccelgroup.h" +#include "ibusengine.h" +#include "ibuskeysyms.h" #include "ibusmarshalers.h" #include "ibusinternal.h" #include "ibusshare.h" +#include "ibusxevent.h" #define IBUS_ENGINE_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_ENGINE, IBusEnginePrivate)) @@ -60,6 +64,8 @@ enum { }; +typedef struct _IBusEngineKeybinding IBusEngineKeybinding; + /* IBusEnginePriv */ struct _IBusEnginePrivate { gchar *engine_name; @@ -74,11 +80,19 @@ struct _IBusEnginePrivate { /* cached content-type */ guint content_purpose; guint content_hints; + + GSettings *settings_emoji; + IBusEngineKeybinding **emoji_keybindings; +}; + +struct _IBusEngineKeybinding { + guint keyval; + IBusModifierType modifiers; }; static guint engine_signals[LAST_SIGNAL] = { 0 }; -static IBusText *text_empty = NULL; +static IBusText *text_empty; /* functions prototype */ static void ibus_engine_destroy (IBusEngine *engine); @@ -176,6 +190,11 @@ static void ibus_engine_dbus_property_changed (IBusEngine *engine, const gchar *property_name, GVariant *value); +static void ibus_engine_keybinding_free (IBusEngine *engine); +static void settings_emoji_hotkey_changed_cb + (GSettings *settings, + const gchar *key, + gpointer data); G_DEFINE_TYPE (IBusEngine, ibus_engine, IBUS_TYPE_SERVICE) @@ -263,11 +282,27 @@ static const gchar introspection_xml[] = " " " " " " + " " + " " + " " /* FIXME properties */ " " " " ""; +static const guint IBUS_MODIFIER_FILTER = + IBUS_MODIFIER_MASK & ~( + IBUS_LOCK_MASK | /* Caps Lock */ + IBUS_MOD2_MASK | /* Num Lock */ + IBUS_BUTTON1_MASK | + IBUS_BUTTON2_MASK | + IBUS_BUTTON3_MASK | + IBUS_BUTTON4_MASK | + IBUS_BUTTON5_MASK | + IBUS_SUPER_MASK | + IBUS_HYPER_MASK | + IBUS_META_MASK); + static void ibus_engine_class_init (IBusEngineClass *class) { @@ -802,9 +837,15 @@ ibus_engine_class_init (IBusEngineClass *class) static void ibus_engine_init (IBusEngine *engine) { - engine->priv = IBUS_ENGINE_GET_PRIVATE (engine); - - engine->priv->surrounding_text = g_object_ref_sink (text_empty); + IBusEnginePrivate *priv; + engine->priv = priv = IBUS_ENGINE_GET_PRIVATE (engine); + + priv->surrounding_text = g_object_ref_sink (text_empty); + priv->settings_emoji = + g_settings_new ("org.freedesktop.ibus.panel.emoji"); + settings_emoji_hotkey_changed_cb (priv->settings_emoji, "hotkey", engine); + g_signal_connect (priv->settings_emoji, "changed::hotkey", + G_CALLBACK (settings_emoji_hotkey_changed_cb), engine); } static void @@ -817,6 +858,7 @@ ibus_engine_destroy (IBusEngine *engine) g_object_unref (engine->priv->surrounding_text); engine->priv->surrounding_text = NULL; } + ibus_engine_keybinding_free (engine); IBUS_OBJECT_CLASS(ibus_engine_parent_class)->destroy (IBUS_OBJECT (engine)); } @@ -852,6 +894,53 @@ ibus_engine_get_property (IBusEngine *engine, } } +static void +ibus_engine_panel_extension (IBusEngine *engine) +{ + IBusXEvent *xevent = ibus_x_event_new ( + "event-type", IBUS_X_EVENT_KEY_PRESS, + "purpose", "emoji", + NULL); + GVariant *data = ibus_serializable_serialize_object ( + IBUS_SERIALIZABLE (xevent)); + + g_assert (data != NULL); + ibus_engine_emit_signal (engine, + "PanelExtension", + g_variant_new ("(v)", data)); +} + +static gboolean +ibus_engine_filter_key_event (IBusEngine *engine, + guint keyval, + guint keycode, + guint state) +{ + IBusEnginePrivate *priv; + int i; + guint modifiers; + + if ((state & IBUS_RELEASE_MASK) != 0) + return FALSE; + g_return_val_if_fail (IBUS_IS_ENGINE (engine), FALSE); + + priv = engine->priv; + modifiers = state & IBUS_MODIFIER_FILTER; + if (keyval >= IBUS_KEY_A && keyval <= IBUS_KEY_Z && + (modifiers & IBUS_SHIFT_MASK) != 0) { + keyval = keyval - IBUS_KEY_A + IBUS_KEY_a; + } + for (i = 0; priv->emoji_keybindings[i]; i++) { + IBusEngineKeybinding *binding = priv->emoji_keybindings[i]; + if (binding->keyval == keyval && + binding->modifiers == modifiers) { + ibus_engine_panel_extension (engine); + return TRUE; + } + } + return FALSE; +} + static gboolean ibus_engine_service_authorized_method (IBusService *service, GDBusConnection *connection) @@ -892,6 +981,7 @@ ibus_engine_service_method_call (IBusService *service, if (g_strcmp0 (method_name, "ProcessKeyEvent") == 0) { guint keyval, keycode, state; gboolean retval = FALSE; + g_variant_get (parameters, "(uuu)", &keyval, &keycode, &state); g_signal_emit (engine, engine_signals[PROCESS_KEY_EVENT], @@ -900,6 +990,12 @@ ibus_engine_service_method_call (IBusService *service, keycode, state, &retval); + if (!retval) { + retval = ibus_engine_filter_key_event (engine, + keyval, + keycode, + state); + } g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", retval)); return; } @@ -1338,6 +1434,79 @@ ibus_engine_dbus_property_changed (IBusEngine *engine, g_object_unref (message); } +static void +ibus_engine_keybinding_free (IBusEngine *engine) +{ + IBusEnginePrivate *priv; + int i; + + g_return_if_fail (IBUS_IS_ENGINE (engine)); + + priv = engine->priv; + if (priv->emoji_keybindings) { + for (i = 0; priv->emoji_keybindings[i]; i++) + g_slice_free (IBusEngineKeybinding, priv->emoji_keybindings[i]); + g_clear_pointer (&priv->emoji_keybindings, g_free); + } +} + +static IBusEngineKeybinding * +ibus_engine_keybinding_new (IBusEngine *engine, + const gchar *accelerator) +{ + guint keyval = 0U; + IBusModifierType modifiers = 0; + IBusEngineKeybinding *binding = NULL; + + ibus_accelerator_parse (accelerator, &keyval, &modifiers); + if (keyval == 0U && modifiers == 0) { + g_warning ("Failed to parse shortcut key '%s'", accelerator); + return NULL; + } + if (modifiers & IBUS_SUPER_MASK) { + modifiers^=IBUS_SUPER_MASK; + modifiers|=IBUS_MOD4_MASK; + } + + binding = g_slice_new0 (IBusEngineKeybinding); + binding->keyval = keyval; + binding->modifiers = modifiers; + return binding; +} + +static void +settings_emoji_hotkey_changed_cb (GSettings *settings, + const gchar *key, + gpointer data) +{ + IBusEngine *engine; + IBusEnginePrivate *priv; + gchar **accelerators; + int i, j, length; + g_return_if_fail (IBUS_IS_ENGINE (data)); + engine = IBUS_ENGINE (data); + priv = engine->priv; + + if (g_strcmp0 (key, "hotkey") != 0) + return; + accelerators = g_settings_get_strv (settings, key); + length = g_strv_length (accelerators); + ibus_engine_keybinding_free (engine); + if (length == 0) { + g_strfreev (accelerators); + return; + } + priv->emoji_keybindings = g_new0 (IBusEngineKeybinding*, length + 1); + for (i = 0, j = 0; i < length; i++) { + IBusEngineKeybinding *binding = + ibus_engine_keybinding_new (engine, accelerators[i]); + if (!binding) + continue; + priv->emoji_keybindings[j++] = binding; + } + g_strfreev (accelerators); +} + IBusEngine * ibus_engine_new (const gchar *engine_name, const gchar *object_path, diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala index c85dfa863..8217a000b 100644 --- a/ui/gtk3/emojier.vala +++ b/ui/gtk3/emojier.vala @@ -2062,6 +2062,7 @@ public class IBusEmojier : Gtk.ApplicationWindow { // Do not hide a bottom panel in XFCE4 if (work_area.y < y) y = work_area.y; + // FIXME: move() does not work in Wayland move(x, y); uint32 timestamp = event.get_time(); diff --git a/ui/gtk3/extension.vala b/ui/gtk3/extension.vala index a170280bb..03026d001 100644 --- a/ui/gtk3/extension.vala +++ b/ui/gtk3/extension.vala @@ -115,8 +115,9 @@ class ExtensionGtk { // 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"); + // Gdk.set_allowed_backends("x11") let present_with_time() failed on + // launching the dialog secondly in Wayland. + //Gdk.set_allowed_backends("x11"); ExtensionGtk extension = new ExtensionGtk(argv); extension.run(); diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala index 1fbf6cfc9..e3f39e12d 100644 --- a/ui/gtk3/panelbinding.vala +++ b/ui/gtk3/panelbinding.vala @@ -33,8 +33,6 @@ class PanelBinding : IBus.PanelService { 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(); private bool m_load_emoji_at_startup; private bool m_loaded_emoji = false; private bool m_load_unicode_at_startup; @@ -49,15 +47,6 @@ class PanelBinding : IBus.PanelService { m_bus = bus; init_settings(); - - bind_emoji_shortcut(); - } - - - ~PanelBinding() { - BindingCommon.unbind_switch_shortcut( - BindingCommon.KeyEventFuncType.ANY, - m_keybindings); } @@ -77,13 +66,6 @@ class PanelBinding : IBus.PanelService { 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, @@ -124,25 +106,6 @@ class PanelBinding : IBus.PanelService { } - 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( @@ -195,9 +158,6 @@ class PanelBinding : IBus.PanelService { set_load_emoji_at_startup(); set_load_unicode_at_startup(); - 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); @@ -354,19 +314,26 @@ class PanelBinding : IBus.PanelService { return; } Gdk.Event event = new Gdk.Event(event_type); - event.key.time = xevent.get_time(); - Gdk.Display? display = Gdk.Display.get_default(); + uint32 time = xevent.get_time(); + if (time == 0) + time = Gtk.get_current_event_time(); + event.key.time = time; 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 { + Gdk.Display? display = Gdk.Display.get_default(); + Gdk.Window? window = null; + if (window == null && xid != 0) { + window = Gdk.X11.Window.lookup_for_display( + display as Gdk.X11.Display, xid); + } + if (window == null && xid != 0) { window = new Gdk.X11.Window.foreign_for_display( display as Gdk.X11.Display, xid); - event.key.window = window; } + if (window == null) { + window = Gdk.get_default_root_window(); + window.ref(); + } + event.key.window = window; handle_emoji_typing(event); } }