From e6bab7ab78c69d238a70a64e60963dd5a6711ffe Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Fri, 19 May 2017 12:13:04 +0900 Subject: [PATCH] Fix a typo in configure.ac BUG=https://github.com/ibus/ibus/pull/1927 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/317640043 Patch from Felix Yan . --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 219b89d..2cc96d1 100644 --- a/configure.ac +++ b/configure.ac @@ -727,7 +727,7 @@ Build options: Enable surrounding-text $enable_surrounding_text Enable libnotify $enable_libnotify Enable Emoji dict $enable_emoji_dict - Uicode Emoji directory $UNICODE_EMOJI_DIR + Unicode Emoji directory $UNICODE_EMOJI_DIR CLDR annotation directory $EMOJI_ANNOTATION_DIR Run test cases $enable_tests ]) -- 2.9.3 From 4fe3050efa7335f82870fb1d5a1d170d20afc160 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Mon, 22 May 2017 12:04:28 +0900 Subject: [PATCH] configure: Change relative paths to absolute ones BUG=https://github.com/ibus/ibus/issues/1926 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/322990043 --- configure.ac | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/configure.ac b/configure.ac index 2cc96d1..cb48ad4 100644 --- a/configure.ac +++ b/configure.ac @@ -634,10 +634,21 @@ if test x"$enable_emoji_dict" = x"yes"; then if test ! -f $UNICODE_EMOJI_DIR/emoji-test.txt ; then AC_MSG_ERROR(Not found $UNICODE_EMOJI_DIR/emoji-test.txt. You can get \ the emoji files from http://www.unicode.org/Public/emoji/4.0/) + else + # POSIX SHELL has no ${FOO:0:1} + head=`echo "$UNICODE_EMOJI_DIR" | cut -c1`; + if test $head != "/" ; then + UNICODE_EMOJI_DIR=`realpath "$UNICODE_EMOJI_DIR"` + fi fi if test ! -f $EMOJI_ANNOTATION_DIR/en.xml ; then AC_MSG_ERROR(Not found $EMOJI_ANNOTATION_DIR/en.xml. You can get \ https://github.com/fujiwarat/cldr-emoji-annotation) + else + head=`echo "$EMOJI_ANNOTATION_DIR" | cut -c1`; + if test $head != "/" ; then + EMOJI_ANNOTATION_DIR=`realpath "$EMOJI_ANNOTATION_DIR"` + fi fi enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)" fi -- 2.9.3 From 44d053577a6ac115f3fd3b7beb7bdd65da81aa64 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 24 May 2017 11:52:19 +0900 Subject: [PATCH] engine: Add Malay and Mongolian keymaps R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/325790043 --- engine/simple.xml.in | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/engine/simple.xml.in b/engine/simple.xml.in index c08000f..f35d7a5 100644 --- a/engine/simple.xml.in +++ b/engine/simple.xml.in @@ -706,5 +706,27 @@ ibus-keyboard 1 + + xkb:my::msa + ms + GPL + Peng Huang <shawn.p.huang@gmail.com> + my + Malay (Jawi) + Malay (Jawi) + ibus-keyboard + 1 + + + xkb:mn::mon + mn + GPL + Peng Huang <shawn.p.huang@gmail.com> + mn + Mongolian + Mongolian + ibus-keyboard + 1 + -- 2.9.3 From 081d09f1a927f459dacda3bcc59a1678ca2f9a95 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Mon, 29 May 2017 11:54:31 +0900 Subject: [PATCH] ui/gtk3: Emojier supports Ctrl-c,v,x and Ctrl-Shift-c Ctrl-[c|v|x] copy, paste, or cut the emoji annotatons. Ctrl-Shift-c copies the selected emoji. Also Ctrl-Backspace is implemented to delete an annotation word. Also updated ibus-emoji.7.in man page. R=penghuang@google.com Review URL: https://codereview.appspot.com/316650043 --- ui/gtk3/emojier.vala | 58 +++++++++++++++++++++++++++++++++++++++++++++++-- ui/gtk3/ibus-emoji.7.in | 11 ++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala index d0d69ed..1d105fd 100644 --- a/ui/gtk3/emojier.vala +++ b/ui/gtk3/emojier.vala @@ -1392,7 +1392,26 @@ class IBusEmojier : Gtk.ApplicationWindow { return true; case Gdk.Key.BackSpace: if (m_entry.get_text().len() > 0) { - GLib.Signal.emit_by_name(m_entry, "backspace"); + if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) { + GLib.Signal.emit_by_name(m_entry, "delete-from-cursor", + Gtk.DeleteType.WORD_ENDS, -1); + } else { + GLib.Signal.emit_by_name(m_entry, "backspace"); + } + return true; + } + break; + case Gdk.Key.Delete: + case Gdk.Key.KP_Delete: + if (m_entry.get_text().len() > 0) { + if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) { + GLib.Signal.emit_by_name(m_entry, "delete-from-cursor", + Gtk.DeleteType.WORD_ENDS, 1); + } else { + GLib.Signal.emit_by_name(m_entry, "delete-from-cursor", + Gtk.DeleteType.CHARS, 1); + } + return true; } break; case Gdk.Key.space: @@ -1445,6 +1464,10 @@ class IBusEmojier : Gtk.ApplicationWindow { if (key_press_cursor_home_end(keyval, modifiers)) return true; break; + case Gdk.Key.Insert: + case Gdk.Key.KP_Insert: + GLib.Signal.emit_by_name(m_entry, "toggle-overwrite"); + return true; } if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) { @@ -1470,8 +1493,13 @@ class IBusEmojier : Gtk.ApplicationWindow { return true; break; case Gdk.Key.u: - if (key_press_escape()) + if (m_entry.get_text().len() > 0) { + GLib.Signal.emit_by_name(m_entry, + "delete-from-cursor", + Gtk.DeleteType.PARAGRAPH_ENDS, + -1); return true; + } break; case Gdk.Key.a: if (m_entry.get_text().len() > 0) { @@ -1479,6 +1507,32 @@ class IBusEmojier : Gtk.ApplicationWindow { return true; } break; + case Gdk.Key.x: + if (m_entry.get_text().len() > 0) { + GLib.Signal.emit_by_name(m_entry, "cut-clipboard"); + return true; + } + break; + case Gdk.Key.C: + case Gdk.Key.c: + if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) { + if (m_candidate_panel_is_visible) { + uint index = m_lookup_table.get_cursor_pos(); + var text = m_lookup_table.get_candidate(index).text; + Gtk.Clipboard clipboard = + Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD); + clipboard.set_text(text, -1); + clipboard.store(); + return true; + } + } else if (m_entry.get_text().len() > 0) { + GLib.Signal.emit_by_name(m_entry, "copy-clipboard"); + return true; + } + break; + case Gdk.Key.v: + GLib.Signal.emit_by_name(m_entry, "paste-clipboard"); + return true; } return false; } diff --git a/ui/gtk3/ibus-emoji.7.in b/ui/gtk3/ibus-emoji.7.in index a5045f6..4ee8636 100644 --- a/ui/gtk3/ibus-emoji.7.in +++ b/ui/gtk3/ibus-emoji.7.in @@ -83,6 +83,17 @@ Move to the next or previous page in the emoji list. \fBHead, End, Control-h or Control-e\fR Select the first or last emoji on the list if an annotation is not typed. Otherwise move the cursor to the head or end in the typed annotation. +.TP +\fBControl-u\fR +Erase the typed annotation. +.TP +\fBControl-x or Control-v or Control-c\fR +Cut the selected annotation to the clipboard with Control-x. Paste +the contents of the clipboard into the annotation entry with Control-v. +Copy the selected annotation to the clipboard with Control-c. +.TP +\fBControl-Shift-c\fR +Copy the selected emoji to the clipboard. .SH BUGS If you find a bug, please report it at https://github.com/ibus/ibus/issues -- 2.9.3 From ad80999f5a10faee1a665a2232e1cf60be901cc8 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Mon, 29 May 2017 12:03:41 +0900 Subject: [PATCH] Make all emoji dicts for fully qualified Currently only emoji-en.dict enables fully qualified since it imports emoji-test.txt and it causes to hardly compare emojis between emoji-en.dict and emoji-$lang.dict when m_show_emoji_variant is enabled. E.g. U+1F3CC-FE0F-200D-2642-FE0F Now emoji-$lang.dict also import emoji-test.txt and enables fully qualified. R=penghuang@google.com Review URL: https://codereview.appspot.com/323860043 --- src/Makefile.am | 1 + src/emoji-parser.c | 167 +++++++++++++++++++++++++++++++++++++++++++++------ src/ibusemoji.c | 2 +- ui/gtk3/emojier.vala | 34 +++++------ 4 files changed, 169 insertions(+), 35 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 27cd168..e7bc8be 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -263,6 +263,7 @@ dicts/emoji-en.dict: emoji-parser --out $@; \ else \ $(builddir)/emoji-parser \ + --unicode-emoji-dir $(UNICODE_EMOJI_DIR) \ --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \ $$xml_derived_option \ --out dicts/emoji-$$f.dict; \ diff --git a/src/emoji-parser.c b/src/emoji-parser.c index 5e6155b..fe3e4ef 100644 --- a/src/emoji-parser.c +++ b/src/emoji-parser.c @@ -31,12 +31,20 @@ * ASCII emoji annotations are saved in ../data/annotations/en_ascii.xml */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include #ifdef HAVE_JSON_GLIB1 #include #endif +#ifdef HAVE_LOCALE_H +#include +#endif + #include #include "ibusemoji.h" @@ -65,8 +73,73 @@ struct _EmojiData { EmojiDataSearchType search_type; }; +typedef struct _NoTransData NoTransData; +struct _NoTransData { + const gchar *xml_file; + const gchar *xml_derived_file; + GSList *emoji_list; +}; + static gchar *unicode_emoji_version; + +static void +init_annotations (IBusEmojiData *emoji, + gpointer user_data) +{ + g_return_if_fail (IBUS_IS_EMOJI_DATA (emoji)); + ibus_emoji_data_set_annotations (emoji, NULL); + ibus_emoji_data_set_description (emoji, ""); +} + +static void +check_no_trans (IBusEmojiData *emoji, + NoTransData *no_trans_data) +{ + const gchar *str = NULL; + g_return_if_fail (IBUS_IS_EMOJI_DATA (emoji)); + if (ibus_emoji_data_get_annotations (emoji) != NULL) + return; + str = ibus_emoji_data_get_emoji (emoji); + if (g_getenv ("IBUS_EMOJI_PARSER_DEBUG") != NULL) { + gchar *basename = NULL; + if (no_trans_data->xml_file) + basename = g_path_get_basename (no_trans_data->xml_file); + else if (no_trans_data->xml_derived_file) + basename = g_path_get_basename (no_trans_data->xml_derived_file); + else + basename = g_strdup ("WRONG FILE"); + g_warning ("Not found emoji %s in the file %s", str, basename); + g_free (basename); + } + no_trans_data->emoji_list = + g_slist_append (no_trans_data->emoji_list, g_strdup (str)); +} + +int +strcmp_ibus_emoji_data_str (IBusEmojiData *emoji, + const gchar *str) +{ + g_return_val_if_fail (IBUS_IS_EMOJI_DATA (emoji), -1); + return g_strcmp0 (ibus_emoji_data_get_emoji (emoji), str); +} + +static void +delete_emoji_from_list (const gchar *str, + GSList **list) +{ + IBusEmojiData *emoji; + + g_return_if_fail (list != NULL); + GSList *p = g_slist_find_custom (*list, + str, + (GCompareFunc)strcmp_ibus_emoji_data_str); + g_return_if_fail (p != NULL); + emoji = p->data; + *list = g_slist_remove (*list, emoji); + g_object_unref (emoji); +} + static void reset_emoji_element (EmojiData *data) { @@ -79,6 +152,13 @@ reset_emoji_element (EmojiData *data) g_clear_pointer (&data->description, g_free); } +/** + * strcmp_novariant: + * + * Return 0 between non-fully-qualified and fully-qualified emojis. + * E.g. U+1F3CC-200D-2642 and U+1F3CC-FE0F-200D-2642-FE0F + * in case @a_variant or @b_variant == U+FE0F + */ gint strcmp_novariant (const gchar *a, const gchar *b, @@ -86,40 +166,54 @@ strcmp_novariant (const gchar *a, gunichar b_variant) { gint retval; - gchar *p = NULL; GString *buff = NULL;; + gchar *head = NULL; + gchar *p; + gchar *variant = NULL; gchar *substr = NULL; if (a_variant > 0) { - if ((p = g_utf8_strchr (a, -1, a_variant)) != NULL) { + if (g_utf8_strchr (a, -1, a_variant) != NULL) { buff = g_string_new (NULL); - if (a != p) { - substr = g_strndup (a, p - a); - g_string_append (buff, substr); - g_free (substr); + p = head = g_strdup (a); + while (*p != '\0') { + if ((variant = g_utf8_strchr (p, -1, a_variant)) == NULL) { + g_string_append (buff, p); + break; + } + if (p != variant) { + substr = g_strndup (p, variant - p); + g_string_append (buff, substr); + g_free (substr); + } + p = g_utf8_next_char (variant); } - p = g_utf8_next_char (p); - if (*p != '\0') - g_string_append (buff, p); retval = g_strcmp0 (buff->str, b); g_string_free (buff, TRUE); + g_free (head); return retval; } else { return -1; } } else if (b_variant > 0) { - if ((p = g_utf8_strchr (b, -1, b_variant)) != NULL) { + if (g_utf8_strchr (b, -1, b_variant) != NULL) { buff = g_string_new (NULL); - if (b != p) { - substr = g_strndup (b, p - b); - g_string_append (buff, substr); - g_free (substr); + p = head = g_strdup (b); + while (*p != '\0') { + if ((variant = g_utf8_strchr (p, -1, b_variant)) == NULL) { + g_string_append (buff, p); + break; + } + if (p != variant) { + substr = g_strndup (p, variant - p); + g_string_append (buff, substr); + g_free (substr); + } + p = g_utf8_next_char (variant); } - p = g_utf8_next_char (p); - if (*p != '\0') - g_string_append (buff, p); retval = g_strcmp0 (a, buff->str); g_string_free (buff, TRUE); + g_free (head); return retval; } else { return -1; @@ -1117,6 +1211,12 @@ main (int argc, char *argv[]) GOptionContext *context; GError *error = NULL; GSList *list = NULL; + gboolean is_en = TRUE; + +#ifdef HAVE_LOCALE_H + /* To output emoji warnings. */ + setlocale (LC_ALL, ""); +#endif prgname = g_path_get_basename (argv[0]); g_set_prgname (prgname); @@ -1144,12 +1244,45 @@ main (int argc, char *argv[]) #endif if (emoji_dir) unicode_emoji_parse_dir (emoji_dir, &list); + if (list) { +#define CHECK_IS_EN(file) if ((file)) { \ + gchar *basename = g_path_get_basename ((file)); \ + is_en = (g_ascii_strncasecmp (basename, "en.", 3) == 0) ? \ + TRUE : FALSE; \ + g_free (basename); \ +} + + CHECK_IS_EN(xml_derived_file); + CHECK_IS_EN(xml_file); +#undef CHECK_IS_EN + + /* Use English emoji-test.txt to get fully-qualified. */ + if (!is_en) + g_slist_foreach (list, (GFunc)init_annotations, NULL); + } if (xml_file) unicode_annotations_parse_xml_file (xml_file, &list, FALSE); if (xml_derived_file) unicode_annotations_parse_xml_file (xml_derived_file, &list, TRUE); if (xml_ascii_file) unicode_annotations_parse_xml_file (xml_ascii_file, &list, FALSE); + if (list != NULL && !is_en) { + /* If emoji-test.txt has an emoji but $lang.xml does not, clear it + * since the language dicts do not want English annotations. + */ + NoTransData no_trans_data = { + xml_file, + xml_derived_file, + NULL + }; + g_slist_foreach (list, (GFunc)check_no_trans, &no_trans_data); + if (no_trans_data.emoji_list) { + g_slist_foreach (no_trans_data.emoji_list, + (GFunc)delete_emoji_from_list, + &list); + g_slist_free_full (no_trans_data.emoji_list, g_free); + } + } if (list != NULL && output) ibus_emoji_data_save (output, list); if (list != NULL && output_category) diff --git a/src/ibusemoji.c b/src/ibusemoji.c index d2e16c5..3d38c2a 100644 --- a/src/ibusemoji.c +++ b/src/ibusemoji.c @@ -29,7 +29,7 @@ #include "ibusinternal.h" #define IBUS_EMOJI_DATA_MAGIC "IBusEmojiData" -#define IBUS_EMOJI_DATA_VERSION (4) +#define IBUS_EMOJI_DATA_VERSION (5) enum { PROP_0 = 0, diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala index 1d105fd..95912bf 100644 --- a/ui/gtk3/emojier.vala +++ b/ui/gtk3/emojier.vala @@ -190,9 +190,6 @@ class IBusEmojier : Gtk.ApplicationWindow { private const string EMOJI_CATEGORY_OTHERS = N_("Others"); private const unichar[] EMOJI_VARIANT_LIST = { 0x1f3fb, 0x1f3fc, 0x1f3fd, 0x1f3fe, 0x1f3ff, 0x200d }; - private const GLib.ActionEntry[] m_action_entries = { - { "variant", check_action_variant_cb, null, "false", null } - }; // Set the actual default values in the constructor // because these fields are used for class_init() and static functions, @@ -253,7 +250,13 @@ class IBusEmojier : Gtk.ApplicationWindow { focus_visible : true ); - add_action_entries(m_action_entries, this); + // GLib.ActionEntry accepts const variables only. + var action = new GLib.SimpleAction.stateful( + "variant", + null, + new GLib.Variant.boolean(m_show_emoji_variant)); + action.activate.connect(check_action_variant_cb); + add_action(action); if (m_current_lang_id == null) m_current_lang_id = "en"; if (m_emoji_font_family == null) @@ -521,18 +524,7 @@ class IBusEmojier : Gtk.ApplicationWindow { m_emoji_to_data_dict.replace(emoji, data); } else { unowned IBus.EmojiData? en_data = null; - // If emoji presentation (+= 0xfe0f) is already saved in dict, - // update it instead of no presentation. - // emoji-test.txt has all emoji presentations but $lang.xml has - // some no emoji presentations. - if (emoji.chr(-1, 0xfe0f) == null) { - var buff = new GLib.StringBuilder(); - buff.append(emoji); - buff.append_unichar(0xfe0f); - en_data = m_emoji_to_data_dict.lookup(buff.str); - } - if (en_data == null) - en_data = m_emoji_to_data_dict.lookup(emoji); + en_data = m_emoji_to_data_dict.lookup(emoji); if (en_data == null) { m_emoji_to_data_dict.insert(emoji, data); return; @@ -923,7 +915,12 @@ class IBusEmojier : Gtk.ApplicationWindow { m_vbox.add(button); button.show_all(); button.button_press_event.connect((w, e) => { - hide_candidate_panel(); + // Bring back to emoji candidate panel in case + // m_show_emoji_variant is enabled and shows variants. + if (m_backward_index >= 0 && m_backward != null) + show_emoji_for_category(m_backward); + else + hide_candidate_panel(); return true; }); } @@ -1269,6 +1266,9 @@ class IBusEmojier : Gtk.ApplicationWindow { GLib.Variant? parameter) { m_show_emoji_variant = !action.get_state().get_boolean(); action.set_state(new GLib.Variant.boolean(m_show_emoji_variant)); + // Redraw emoji candidate panel for m_show_emoji_variant + if (m_candidate_panel_is_visible) + show_candidate_panel(); } -- 2.9.3 From bc0f91342c6f3a6e554493af9430d634d906ee19 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Tue, 25 Jul 2017 12:00:14 +0900 Subject: [PATCH] ui/gtk3: Fix SEGV of IBusEmojier on de_DE.UTF-8 de's decimal_point is ',' instead of '.' and failed to load the CSS data in Gtk.CssProvider.load_from_data(), launched null window of emojis and finally caused a SEGV due to the null window. This also fixes some memory leaks. BUG=rhbz#1471079 Review URL: https://codereview.appspot.com/323310043 --- src/ibusemoji.c | 1 + ui/gtk3/emojier.vala | 33 ++++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/ibusemoji.c b/src/ibusemoji.c index 3d38c2a..d56c48a 100644 --- a/src/ibusemoji.c +++ b/src/ibusemoji.c @@ -591,6 +591,7 @@ out_load_cache: g_variant_unref (variant); if (variant_table) g_variant_unref (variant_table); + g_free (contents); return retval; } diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala index 95912bf..9df59ac 100644 --- a/ui/gtk3/emojier.vala +++ b/ui/gtk3/emojier.vala @@ -276,6 +276,17 @@ class IBusEmojier : Gtk.ApplicationWindow { warning("Could not open display."); return; } + // Set en locale because de_DE's decimal_point is ',' instead of '.' + string? backup_locale = + Intl.setlocale(LocaleCategory.NUMERIC, null).dup(); + if (Intl.setlocale(LocaleCategory.NUMERIC, "en_US.UTF-8") == null) { + if (Intl.setlocale(LocaleCategory.NUMERIC, "C.UTF-8") == null) { + if (Intl.setlocale(LocaleCategory.NUMERIC, "C") == null) { + warning("You don't install either en_US.UTF-8 or C.UTF-8 " + + "or C locale"); + } + } + } m_rgba = new ThemedRGBA(this); uint bg_red = (uint)(m_rgba.normal_bg.red * 255); uint bg_green = (uint)(m_rgba.normal_bg.green * 255); @@ -321,6 +332,10 @@ class IBusEmojier : Gtk.ApplicationWindow { warning("Failed css_provider_from_data: %s", e.message); return; } + if (backup_locale != null) + Intl.setlocale(LocaleCategory.NUMERIC, backup_locale); + else + Intl.setlocale(LocaleCategory.NUMERIC, ""); Gtk.StyleContext.add_provider_for_screen( screen, @@ -424,8 +439,9 @@ class IBusEmojier : Gtk.ApplicationWindow { unowned GLib.SList annotations = data.get_annotations(); foreach (string annotation in annotations) { bool has_emoji = false; - unowned GLib.SList hits = - m_annotation_to_emojis_dict.lookup(annotation); + GLib.SList hits = + m_annotation_to_emojis_dict.lookup(annotation).copy_deep( + GLib.strdup); foreach (string hit_emoji in hits) { if (hit_emoji == emoji) { has_emoji = true; @@ -485,7 +501,8 @@ class IBusEmojier : Gtk.ApplicationWindow { private static void update_annotations_with_description (IBus.EmojiData data, string description) { - unowned GLib.SList annotations = data.get_annotations(); + GLib.SList annotations = + data.get_annotations().copy_deep(GLib.strdup); bool update_annotations = false; string former = null; string later = null; @@ -574,8 +591,9 @@ class IBusEmojier : Gtk.ApplicationWindow { buff.append_unichar(0xfe0f); if (m_emoji_to_data_dict.lookup(buff.str) != null) base_emoji = buff.str; - unowned GLib.SList? variants = - m_emoji_to_emoji_variants_dict.lookup(base_emoji); + GLib.SList? variants = + m_emoji_to_emoji_variants_dict.lookup( + base_emoji).copy_deep(GLib.strdup); if (variants.find_custom(emoji, GLib.strcmp) == null) { if (variants == null) variants.append(base_emoji); @@ -587,8 +605,9 @@ class IBusEmojier : Gtk.ApplicationWindow { return; } bool has_emoji = false; - unowned GLib.SList hits = - m_category_to_emojis_dict.lookup(category); + GLib.SList hits = + m_category_to_emojis_dict.lookup(category).copy_deep( + GLib.strdup); foreach (string hit_emoji in hits) { if (hit_emoji == emoji) { has_emoji = true; -- 2.9.3 From 4134113a1ae30534367a9ca729c7f36a0a8e3662 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Thu, 27 Jul 2017 14:07:00 +0900 Subject: [PATCH] ui/gtk3: Fix SEGV of XKeysymToKeycode() on Wayland BUG=rhbz#1368593 --- ui/gtk3/application.vala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/gtk3/application.vala b/ui/gtk3/application.vala index 5ae6e83..fa80272 100644 --- a/ui/gtk3/application.vala +++ b/ui/gtk3/application.vala @@ -3,6 +3,7 @@ * ibus - The Input Bus * * Copyright(c) 2011 Peng Huang + * Copyright(c) 2017 Takao Fujiwara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -99,6 +100,9 @@ class Application { } public static void main(string[] argv) { + // for Gdk.X11.get_default_xdisplay() + Gdk.set_allowed_backends("x11"); + Application app = new Application(argv); app.run(); } -- 2.9.3 From 6a3301db85e77e0652f7e00894cce493b6a942f6 Mon Sep 17 00:00:00 2001 From: Xiang Fan Date: Thu, 10 Aug 2017 11:24:39 +0900 Subject: [PATCH 01/14] client/gtk2: include the scaling factor Scaling factor, which exists for HiDPI displays, needs to be included in the calculation of cursor location. This does not affect devices without a HiDPI display. Candidate windows would be misplaced to smaller coordinates without this patch. BUG=https://github.com/ibus/ibus/issues/1806 Review URL: https://codereview.appspot.com/328250043 Patch from Xiang Fan . --- client/gtk2/ibusimcontext.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index 0df00620..41c7a3af 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -999,6 +999,24 @@ ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client) gtk_im_context_set_client_window (ibusimcontext->slave, client); } +static void +_set_rect_scale_factor_with_window (GdkRectangle *area, + GdkWindow *window) +{ +#if GTK_CHECK_VERSION (3, 10, 0) + int scale_factor; + + g_assert (area); + g_assert (GDK_IS_WINDOW (window)); + + scale_factor = gdk_window_get_scale_factor (window); + area->x *= scale_factor; + area->y *= scale_factor; + area->width *= scale_factor; + area->height *= scale_factor; +#endif +} + static gboolean _set_cursor_location_internal (IBusIMContext *ibusimcontext) { @@ -1024,6 +1042,8 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext) window = parent; } + _set_rect_scale_factor_with_window (&area, + ibusimcontext->client_window); ibus_input_context_set_cursor_location_relative ( ibusimcontext->ibuscontext, area.x, @@ -1049,6 +1069,7 @@ _set_cursor_location_internal (IBusIMContext *ibusimcontext) gdk_window_get_root_coords (ibusimcontext->client_window, area.x, area.y, &area.x, &area.y); + _set_rect_scale_factor_with_window (&area, ibusimcontext->client_window); ibus_input_context_set_cursor_location (ibusimcontext->ibuscontext, area.x, area.y, -- 2.13.4 From c1b93f933f5cbd74f3e06575d26ed7432a5420fd Mon Sep 17 00:00:00 2001 From: Mario Bodemann Date: Tue, 15 Aug 2017 12:10:56 +0900 Subject: [PATCH 02/14] Typo fix BUG=https://github.com/ibus/ibus/pull/1939 Review URL: https://codereview.appspot.com/327080043 Patch from Mario Bodemann . --- ui/gtk3/ibus-emoji.7.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/gtk3/ibus-emoji.7.in b/ui/gtk3/ibus-emoji.7.in index 4ee86364..d5eae310 100644 --- a/ui/gtk3/ibus-emoji.7.in +++ b/ui/gtk3/ibus-emoji.7.in @@ -15,7 +15,7 @@ .SH "DESCRIPTION" .PP -IBus Emojier provides a GUI to select an emoji by typing an emoji annotaion +IBus Emojier provides a GUI to select an emoji by typing an emoji annotation or choosing a character with mouse click and it's designed to work as an extended IBus lookup window using Space, Enter, and Arrow keys. The text entry accepts an emoji annotation or Unicode points. -- 2.13.4 From 203a3df5a239d644cf42b7bac03a268eb5babfc7 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 30 Aug 2017 11:38:09 +0900 Subject: [PATCH 03/14] Initial version of ibus portal This adds a dbus service called org.freedesktop.portal.IBus on the session bus. It is a very limited service that only implements CreateInputContext and the InputContext interface (and Service.Destroy for lifetime access). It uses gdbus code generation for demarshalling the method calls which means it will verify that all arguments have the right type. Additionally all method calls to the input context object have to be from the client that created it, so each client is isolated. BUG=https://github.com/flatpak/flatpak/issues/675 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/326350043 Patch from Alexander Larsson . --- Makefile.am | 1 + configure.ac | 5 +- portal/Makefile.am | 95 ++++ portal/org.freedesktop.IBus.Portal.xml | 132 +++++ portal/org.freedesktop.portal.IBus.service.in | 3 + portal/portal.c | 698 ++++++++++++++++++++++++++ src/ibusshare.h | 14 + 7 files changed, 947 insertions(+), 1 deletion(-) create mode 100644 portal/Makefile.am create mode 100644 portal/org.freedesktop.IBus.Portal.xml create mode 100644 portal/org.freedesktop.portal.IBus.service.in create mode 100644 portal/portal.c diff --git a/Makefile.am b/Makefile.am index f703d4c6..c8e802da 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,7 @@ SUBDIRS = \ util \ conf \ client \ + portal \ data \ m4 \ po \ diff --git a/configure.ac b/configure.ac index cb48ad4c..14556a3a 100644 --- a/configure.ac +++ b/configure.ac @@ -153,7 +153,7 @@ PKG_CHECK_MODULES(GOBJECT2, [ gobject-2.0 >= glib_required_version ]) PKG_CHECK_MODULES(GIO2, [ - gio-2.0 >= glib_required_version + gio-2.0 gio-unix-2.0 >= glib_required_version ]) PKG_CHECK_MODULES(GTHREAD2, [ gthread-2.0 >= glib_required_version @@ -660,6 +660,8 @@ PKG_CHECK_MODULES(ISOCODES, [ ISOCODES_PREFIX=`$PKG_CONFIG iso-codes --variable=prefix` AC_SUBST(ISOCODES_PREFIX) +AC_SUBST([GDBUS_CODEGEN], [`$PKG_CONFIG --variable gdbus_codegen gio-2.0`]) + # OUTPUT files AC_CONFIG_FILES([ po/Makefile.in Makefile @@ -674,6 +676,7 @@ src/Makefile src/ibusversion.h src/tests/Makefile bus/Makefile +portal/Makefile engine/Makefile util/Makefile util/IMdkit/Makefile diff --git a/portal/Makefile.am b/portal/Makefile.am new file mode 100644 index 00000000..954fc591 --- /dev/null +++ b/portal/Makefile.am @@ -0,0 +1,95 @@ +# vim:set noet ts=4: +# +# ibus - The Input Bus +# +# Copyright (c) 2007-2013 Peng Huang +# Copyright (c) 2007-2013 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# USA + +NULL = + +libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src \ + $(NULL) + +AM_CFLAGS = \ + @GLIB2_CFLAGS@ \ + @GIO2_CFLAGS@ \ + @GTHREAD2_CFLAGS@ \ + -DG_LOG_DOMAIN=\"IBUS\" \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DBINDIR=\"@bindir@\" \ + -DIBUS_DISABLE_DEPRECATED \ + $(NULL) +AM_LDADD = \ + @GOBJECT2_LIBS@ \ + @GLIB2_LIBS@ \ + @GIO2_LIBS@ \ + @GTHREAD2_LIBS@ \ + $(libibus) \ + $(NULL) + +ibus_dbus_built_sources = ibus-portal-dbus.c ibus-portal-dbus.h +BUILT_SOURCES = $(ibus_dbus_built_sources) + +libexec_PROGRAMS = ibus-portal +ibus_portal_DEPENDENCIES = \ + $(libibus) \ + $(NULL) +ibus_portal_SOURCES = \ + portal.c \ + $(ibus_dbus_built_sources) \ + $(NULL) +ibus_portal_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +ibus_portal_LDADD = \ + $(AM_LDADD) \ + $(NULL) + +EXTRA_DIST = \ + $(NULL) + +CLEANFILES = \ + $(NULL) + +$(libibus): + $(MAKE) -C $(top_builddir)/src + +dbusservice_in_files = org.freedesktop.portal.IBus.service.in +dbusservice_DATA = $(dbusservice_in_files:.service.in=.service) +dbusservicedir=${datadir}/dbus-1/services + +org.freedesktop.portal.IBus.service: org.freedesktop.portal.IBus.service.in + $(AM_V_GEN) sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@.tmp && mv $@.tmp $@ + +$(ibus_dbus_built_sources) : org.freedesktop.IBus.Portal.xml + $(AM_V_GEN) $(GDBUS_CODEGEN) \ + --interface-prefix org.freedesktop.IBus. \ + --c-namespace IBusDbus \ + --generate-c-code $(builddir)/ibus-portal-dbus \ + $^ \ + $(NULL) + +EXTRA_DIST += $(dbusservice_in_files) +CLEANFILES += $(dbusservice_DATA) + +-include $(top_srcdir)/git.mk diff --git a/portal/org.freedesktop.IBus.Portal.xml b/portal/org.freedesktop.IBus.Portal.xml new file mode 100644 index 00000000..afce4daa --- /dev/null +++ b/portal/org.freedesktop.IBus.Portal.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/portal/org.freedesktop.portal.IBus.service.in b/portal/org.freedesktop.portal.IBus.service.in new file mode 100644 index 00000000..47ae9ffc --- /dev/null +++ b/portal/org.freedesktop.portal.IBus.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.portal.IBus +Exec=@libexecdir@/ibus-portal diff --git a/portal/portal.c b/portal/portal.c new file mode 100644 index 00000000..0415f996 --- /dev/null +++ b/portal/portal.c @@ -0,0 +1,698 @@ +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* vim:set et sts=4: */ +/* ibus - The Input Bus + * Copyright (C) 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ibus-portal-dbus.h" + +typedef struct _IBusPortal IBusPortal; +typedef struct _IBusPortalClass IBusPortalClass; +typedef struct _IBusPortalContext IBusPortalContext; +typedef struct _IBusPortalContextClass IBusPortalContextClass; + +struct _IBusPortalContext +{ + IBusDbusInputContextSkeleton parent_instance; + IBusInputContext *context; + guint id; + char *owner; + char *object_path; + IBusDbusService *service; +}; + +struct _IBusPortalContextClass +{ + IBusDbusInputContextSkeletonClass parent_class; +}; + +struct _IBusPortal +{ + IBusDbusPortalSkeleton parent_instance; + +}; + +struct _IBusPortalClass +{ + IBusDbusPortalSkeletonClass parent_class; +}; + +enum +{ + PROP_CONTENT_TYPE = 1, + N_PROPERTIES +}; + +static GMainLoop *loop = NULL; +static IBusBus *ibus_bus; +static IBusPortal *ibus_portal = NULL; +static gboolean opt_verbose; +static gboolean opt_replace; + +static GList *all_contexts = NULL; + +static guint next_context_id; + +GType ibus_portal_context_get_type (void) G_GNUC_CONST; +static void ibus_portal_context_iface_init (IBusDbusInputContextIface *iface); + +static void portal_context_g_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + IBusPortalContext *portal_context); + +G_DEFINE_TYPE_WITH_CODE (IBusPortalContext, + ibus_portal_context, + IBUS_DBUS_TYPE_INPUT_CONTEXT_SKELETON, + G_IMPLEMENT_INTERFACE (IBUS_DBUS_TYPE_INPUT_CONTEXT, + ibus_portal_context_iface_init)); + +static void +_forward_method_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GDBusMethodInvocation *invocation = user_data; + IBusPortalContext *portal_context = + (IBusPortalContext *) g_dbus_method_invocation_get_user_data ( + invocation); + IBusEngineDesc *desc; + GError *error = NULL; + + GVariant *variant = g_dbus_proxy_call_finish ((GDBusProxy *) source_object, + res, &error); + if (variant == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + return; + } + + g_dbus_method_invocation_return_value (invocation, variant); +} + +static gboolean +_forward_method (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation) +{ + IBusPortalContext *portal_context = (IBusPortalContext *)object; + GDBusMessage *message = g_dbus_method_invocation_get_message (invocation); + + g_dbus_proxy_call (G_DBUS_PROXY (portal_context->context), + g_dbus_method_invocation_get_method_name (invocation), + g_dbus_message_get_body (message), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + _forward_method_cb, invocation); + return TRUE; +} + +static gboolean +ibus_dbus_context_cancel_hand_writing (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + guint arg_n_strokes) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_focus_in (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_focus_out (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_get_engine (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_process_hand_writing_event (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + GVariant + *arg_coordinates) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_process_key_event (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + guint arg_keyval, + guint arg_keycode, + guint arg_state) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_property_activate (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name, + guint arg_state) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_reset (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_set_capabilities (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + guint arg_caps) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_set_cursor_location (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + gint arg_x, + gint arg_y, + gint arg_w, + gint arg_h) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_set_cursor_location_relative (IBusDbusInputContext *object, + GDBusMethodInvocation + *invocation, + gint arg_x, + gint arg_y, + gint arg_w, + gint arg_h) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_set_engine (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + return _forward_method (object, invocation); +} + +static gboolean +ibus_dbus_context_set_surrounding_text (IBusDbusInputContext *object, + GDBusMethodInvocation *invocation, + GVariant *arg_text, + guint arg_cursor_pos, + guint arg_anchor_pos) +{ + return _forward_method (object, invocation); +} + +static void +ibus_portal_context_iface_init (IBusDbusInputContextIface *iface) +{ + iface->handle_cancel_hand_writing = ibus_dbus_context_cancel_hand_writing; + iface->handle_focus_in = ibus_dbus_context_focus_in; + iface->handle_focus_out = ibus_dbus_context_focus_out; + iface->handle_get_engine = ibus_dbus_context_get_engine; + iface->handle_process_hand_writing_event = + ibus_dbus_context_process_hand_writing_event; + iface->handle_process_key_event = ibus_dbus_context_process_key_event; + iface->handle_property_activate = ibus_dbus_context_property_activate; + iface->handle_reset = ibus_dbus_context_reset; + iface->handle_set_capabilities = ibus_dbus_context_set_capabilities; + iface->handle_set_cursor_location = ibus_dbus_context_set_cursor_location; + iface->handle_set_cursor_location_relative = + ibus_dbus_context_set_cursor_location_relative; + iface->handle_set_engine = ibus_dbus_context_set_engine; + iface->handle_set_surrounding_text = ibus_dbus_context_set_surrounding_text; +} + +static void +ibus_portal_context_init (IBusPortalContext *portal_context) +{ +} + +static void +ibus_portal_context_finalize (GObject *object) +{ + IBusPortalContext *portal_context = (IBusPortalContext *)object; + + all_contexts = g_list_remove (all_contexts, portal_context); + + g_dbus_interface_skeleton_unexport ( + G_DBUS_INTERFACE_SKELETON (portal_context->service)); + g_dbus_interface_skeleton_unexport ( + G_DBUS_INTERFACE_SKELETON (portal_context)); + + g_free (portal_context->owner); + g_free (portal_context->object_path); + g_object_unref (portal_context->service); + + g_signal_handlers_disconnect_by_func ( + portal_context->context, + G_CALLBACK(portal_context_g_signal), + portal_context); + g_object_unref (portal_context->context); + + G_OBJECT_CLASS (ibus_portal_context_parent_class)->finalize (object); +} + +static void +ibus_portal_context_set_property (IBusPortalContext *portal_context, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONTENT_TYPE: + g_dbus_proxy_call (G_DBUS_PROXY (portal_context->context), + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + IBUS_INTERFACE_INPUT_CONTEXT, + "ContentType", + g_value_get_variant (value)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + NULL, /* callback */ + NULL /* user_data */ + ); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (portal_context, prop_id, pspec); + } +} + +static void +ibus_portal_context_get_property (IBusPortalContext *portal_context, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_CONTENT_TYPE: + g_warning ("No support for setting content type"); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (portal_context, prop_id, pspec); + } +} + +static gboolean +ibus_portal_context_g_authorize_method (GDBusInterfaceSkeleton *interface, + GDBusMethodInvocation *invocation) +{ + IBusPortalContext *portal_context = (IBusPortalContext *)interface; + + if (g_strcmp0 (g_dbus_method_invocation_get_sender (invocation), + portal_context->owner) == 0) { + return TRUE; + } + + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Access denied"); + return FALSE; +} + + +static void +ibus_portal_context_class_init (IBusPortalContextClass *klass) +{ + GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *skeleton_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = ibus_portal_context_finalize; + gobject_class->set_property = + (GObjectSetPropertyFunc) ibus_portal_context_set_property; + gobject_class->get_property = + (GObjectGetPropertyFunc) ibus_portal_context_get_property; + + skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS(klass); + skeleton_class->g_authorize_method = ibus_portal_context_g_authorize_method; + + ibus_dbus_input_context_override_properties (gobject_class, + PROP_CONTENT_TYPE); +} + +static void +portal_context_g_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + IBusPortalContext *portal_context) +{ + GError *error = NULL; + GDBusConnection *connection; + + if (g_strcmp0 (sender_name, IBUS_SERVICE_IBUS) != 0) + return; + + connection = g_dbus_interface_skeleton_get_connection ( + G_DBUS_INTERFACE_SKELETON (portal_context)); + if (!g_dbus_connection_emit_signal (connection, + portal_context->owner, + portal_context->object_path, + IBUS_INTERFACE_INPUT_CONTEXT, + signal_name, + parameters, + &error)) { + g_warning ("Unable to emit signal %s: %s", signal_name, error->message); + g_error_free (error); + } + + g_signal_stop_emission_by_name (proxy, "g-signal"); +} + +static gboolean +ibus_portal_context_handle_destroy (IBusDbusService *object, + GDBusMethodInvocation *invocation, + IBusPortalContext *portal_context) +{ + g_object_unref (portal_context); +} + +static IBusPortalContext * +ibus_portal_context_new (IBusInputContext *context, + GDBusConnection *connection, + const char *owner, + GError **error) +{ + IBusPortalContext *portal_context = + g_object_new (ibus_portal_context_get_type (), NULL); + + g_signal_connect (context, + "g-signal", + G_CALLBACK(portal_context_g_signal), + portal_context); + + portal_context->id = ++next_context_id; + portal_context->context = g_object_ref (context); + portal_context->owner = g_strdup (owner); + portal_context->object_path = + g_strdup_printf (IBUS_PATH_INPUT_CONTEXT, portal_context->id); + portal_context->service = ibus_dbus_service_skeleton_new (); + + g_signal_connect (portal_context->service, + "handle-destroy", + G_CALLBACK (ibus_portal_context_handle_destroy), + portal_context); + + if (!g_dbus_interface_skeleton_export ( + G_DBUS_INTERFACE_SKELETON (portal_context->service), + connection, portal_context->object_path, + error) || + !g_dbus_interface_skeleton_export ( + G_DBUS_INTERFACE_SKELETON (portal_context), + connection, portal_context->object_path, + error)) { + g_object_unref (portal_context); + return NULL; + } + + all_contexts = g_list_prepend (all_contexts, portal_context); + + return portal_context; +} + +GType ibus_portal_get_type (void) G_GNUC_CONST; +static void ibus_portal_iface_init (IBusDbusPortalIface *iface); + +G_DEFINE_TYPE_WITH_CODE (IBusPortal, ibus_portal, + IBUS_DBUS_TYPE_PORTAL_SKELETON, + G_IMPLEMENT_INTERFACE (IBUS_DBUS_TYPE_PORTAL, + ibus_portal_iface_init)); + + +static void +create_input_context_done (IBusBus *bus, + GAsyncResult *res, + GDBusMethodInvocation *invocation) +{ + GError *error = NULL; + IBusInputContext *context; + IBusPortalContext *portal_context; + char *object_path; + + context = ibus_bus_create_input_context_async_finish (ibus_bus, + res, + &error); + if (context == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + return; + } + + portal_context = ibus_portal_context_new ( + context, + g_dbus_method_invocation_get_connection (invocation), + g_dbus_method_invocation_get_sender (invocation), + &error); + g_object_unref (context); + + if (portal_context == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + g_object_unref (portal_context); + return; + } + + ibus_dbus_portal_complete_create_input_context ( + IBUS_DBUS_PORTAL(ibus_portal), + invocation, portal_context->object_path); +} + +static gboolean +ibus_portal_handle_create_input_context (IBusDbusPortal *object, + GDBusMethodInvocation *invocation, + const gchar *arg_client_name) +{ + ibus_bus_create_input_context_async ( + ibus_bus, + arg_client_name, -1, + NULL, + (GAsyncReadyCallback)create_input_context_done, + invocation); + return TRUE; +} + +static void +ibus_portal_iface_init (IBusDbusPortalIface *iface) +{ + iface->handle_create_input_context = + ibus_portal_handle_create_input_context; +} + +static void +ibus_portal_init (IBusPortal *portal) +{ +} + +static void +ibus_portal_class_init (IBusPortalClass *klass) +{ +} + + +static void +show_version_and_quit (void) +{ + g_print ("%s - Version %s\n", g_get_application_name (), VERSION); + exit (EXIT_SUCCESS); +} + +static const GOptionEntry entries[] = +{ + { "version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, + show_version_and_quit, "Show the application's version.", NULL }, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, + &opt_verbose, "verbose.", NULL }, + { "replace", 'r', 0, G_OPTION_ARG_NONE, + &opt_replace, "Replace.", NULL }, + { NULL }, +}; + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GError *error = NULL; + + ibus_portal = g_object_new (ibus_portal_get_type (), NULL); + + if (!g_dbus_interface_skeleton_export ( + G_DBUS_INTERFACE_SKELETON (ibus_portal), + connection, + IBUS_PATH_IBUS, + &error)) { + g_warning ("Error exporting portal: %s", error->message); + g_error_free (error); + return; + } +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_main_loop_quit (loop); +} + +static void +name_owner_changed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + const char *name, *from, *to; + + g_variant_get (parameters, "(sss)", &name, &from, &to); + + if (name[0] == ':' && + g_strcmp0 (name, from) == 0 && + g_strcmp0 (to, "") == 0) + { + GList *l, *next; + /* Client disconnected, free any input contexts it may have */ + for (l = all_contexts; l != NULL; l = next) { + IBusPortalContext *portal_context = l->data; + next = l->next; + + if (g_strcmp0 (portal_context->owner, name) == 0) { + g_object_unref (portal_context); + } + } + } +} + +static void +_bus_disconnected_cb (IBusBus *ibusbus) +{ + g_main_loop_quit (loop); +} + +gint +main (gint argc, gchar **argv) +{ + GDBusConnection *session_bus = NULL; + guint owner_id; + + setlocale (LC_ALL, ""); + + GOptionContext *context = g_option_context_new ("- ibus daemon"); + g_option_context_add_main_entries (context, entries, "ibus-daemon"); + + GError *error = NULL; + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("Option parsing failed: %s\n", error->message); + g_error_free (error); + exit (-1); + } + + /* Avoid even loading gvfs to avoid accidental confusion */ + g_setenv ("GIO_USE_VFS", "local", TRUE); + + ibus_init (); + + ibus_set_log_handler (opt_verbose); + + ibus_bus = ibus_bus_new (); + if (!ibus_bus_is_connected (ibus_bus)) { + g_printerr ("Not connected to the ibus bus\n"); + exit (1); + } + + g_signal_connect (ibus_bus, "disconnected", + G_CALLBACK (_bus_disconnected_cb), NULL); + + loop = g_main_loop_new (NULL, FALSE); + + session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + if (session_bus == NULL) { + g_printerr ("No session bus: %s", error->message); + exit (-1); + } + + g_dbus_connection_signal_subscribe (session_bus, + "org.freedesktop.DBus", + "org.freedesktop.DBus", + "NameOwnerChanged", + "/org/freedesktop/DBus", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + name_owner_changed, + NULL, NULL); + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + IBUS_SERVICE_PORTAL, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE + : 0), + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + g_main_loop_run (loop); + + g_bus_unown_name (owner_id); + g_main_loop_unref (loop); + + return 0; +} diff --git a/src/ibusshare.h b/src/ibusshare.h index bca477c0..f3e2011e 100644 --- a/src/ibusshare.h +++ b/src/ibusshare.h @@ -52,6 +52,13 @@ #define IBUS_SERVICE_IBUS "org.freedesktop.IBus" /** + * IBUS_SERVICE_PORTAL: + * + * Address of IBus portalservice. + */ +#define IBUS_SERVICE_PORTAL "org.freedesktop.portal.IBus" + +/** * IBUS_SERVICE_PANEL: * * Address of IBus panel service. @@ -122,6 +129,13 @@ #define IBUS_INTERFACE_IBUS "org.freedesktop.IBus" /** + * IBUS_INTERFACE_PORTAL: + * + * D-Bus interface for IBus portal. + */ +#define IBUS_INTERFACE_PORTAL "org.freedesktop.IBus.Portal" + +/** * IBUS_INTERFACE_INPUT_CONTEXT: * * D-Bus interface for IBus input context. -- 2.13.4 From 35ce62474fa97a5460d72c360943700a413a07ae Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 31 Aug 2017 12:03:37 +0900 Subject: [PATCH 04/14] Support the portal in the gtk im modules This adds a new way to create an IbusBus, ibus_bus_new_async_client(). This returns an object that is not guarantee to handle any calls that are not needed by a client, meaning CreateInputContext and handling the input context. If you are running in a flatpak, or if IBUS_USE_PORTAL is set, then instead of talking to the regular ibus bus we connect to org.freedesktop.portal.IBus on the session bus and use the limited org.freedesktop.IBus.Portal interface instead of the org.freedesktop.IBus interface. This allows flatpaks (or other sandbox systems) to safely use dbus clients (apps). BUG=https://github.com/flatpak/flatpak/issues/675 Review URL: https://codereview.appspot.com/328410043 Patch from Alexander Larsson . --- client/gtk2/ibusimcontext.c | 4 +- src/ibusbus.c | 248 +++++++++++++++++++++++++++++++++++++++----- src/ibusbus.h | 23 ++++ src/ibusinputcontext.c | 12 ++- 4 files changed, 256 insertions(+), 31 deletions(-) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index 41c7a3af..b4ca8828 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -583,7 +583,7 @@ ibus_im_context_class_init (IBusIMContextClass *class) /* init bus object */ if (_bus == NULL) { - _bus = ibus_bus_new_async (); + _bus = ibus_bus_new_async_client (); /* init the global fake context */ if (ibus_bus_is_connected (_bus)) { @@ -603,7 +603,7 @@ ibus_im_context_class_init (IBusIMContextClass *class) } _daemon_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, - IBUS_SERVICE_IBUS, + ibus_bus_get_service_name (_bus), G_BUS_NAME_WATCHER_FLAGS_NONE, daemon_name_appeared, daemon_name_vanished, diff --git a/src/ibusbus.c b/src/ibusbus.c index 75406a37..fc0c9033 100644 --- a/src/ibusbus.c +++ b/src/ibusbus.c @@ -48,12 +48,14 @@ enum { enum { PROP_0 = 0, PROP_CONNECT_ASYNC, + PROP_CLIENT_ONLY, }; /* IBusBusPriv */ struct _IBusBusPrivate { GFileMonitor *monitor; GDBusConnection *connection; + gboolean connected; gboolean watch_dbus_signal; guint watch_dbus_signal_id; gboolean watch_ibus_signal; @@ -62,7 +64,10 @@ struct _IBusBusPrivate { gchar *unique_name; gboolean connect_async; gchar *bus_address; + gboolean use_portal; + gboolean client_only; GCancellable *cancellable; + guint portal_name_watch_id; }; static guint bus_signals[LAST_SIGNAL] = { 0 }; @@ -74,6 +79,7 @@ static GObject *ibus_bus_constructor (GType type, guint n_params, GObjectConstructParam *params); static void ibus_bus_destroy (IBusObject *object); +static void ibus_bus_connect_async (IBusBus *bus); static void ibus_bus_watch_dbus_signal (IBusBus *bus); static void ibus_bus_unwatch_dbus_signal (IBusBus *bus); static void ibus_bus_watch_ibus_signal (IBusBus *bus); @@ -117,8 +123,10 @@ ibus_bus_class_init (IBusBusClass *class) IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class); gobject_class->constructor = ibus_bus_constructor; - gobject_class->set_property = (GObjectSetPropertyFunc) ibus_bus_set_property; - gobject_class->get_property = (GObjectGetPropertyFunc) ibus_bus_get_property; + gobject_class->set_property = + (GObjectSetPropertyFunc) ibus_bus_set_property; + gobject_class->get_property = + (GObjectGetPropertyFunc) ibus_bus_get_property; ibus_object_class->destroy = ibus_bus_destroy; /* install properties */ @@ -128,13 +136,28 @@ ibus_bus_class_init (IBusBusClass *class) * Whether the #IBusBus object should connect asynchronously to the bus. * */ - g_object_class_install_property (gobject_class, - PROP_CONNECT_ASYNC, - g_param_spec_boolean ("connect-async", - "Connect Async", - "Connect asynchronously to the bus", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property ( + gobject_class, + PROP_CONNECT_ASYNC, + g_param_spec_boolean ("connect-async", + "Connect Async", + "Connect asynchronously to the bus", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /** + * IBusBus:client-only: + * + * Whether the #IBusBus object is for client use only. + * + */ + g_object_class_install_property ( + gobject_class, + PROP_CLIENT_ONLY, + g_param_spec_boolean ("client-only", + "ClientOnly", + "Client use only", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /* install signals */ /** @@ -294,6 +317,8 @@ ibus_bus_close_connection (IBusBus *bus) g_cancellable_cancel (bus->priv->cancellable); g_cancellable_reset (bus->priv->cancellable); + bus->priv->connected = FALSE; + /* unref the old connection at first */ if (bus->priv->connection != NULL) { g_signal_handlers_disconnect_by_func (bus->priv->connection, @@ -311,6 +336,8 @@ static void ibus_bus_connect_completed (IBusBus *bus) { g_assert (bus->priv->connection); + + bus->priv->connected = TRUE; /* FIXME */ ibus_bus_hello (bus); @@ -329,9 +356,38 @@ ibus_bus_connect_completed (IBusBus *bus) } static void +_bus_connect_start_portal_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + IBusBus *bus = IBUS_BUS (user_data); + GVariant *result; + GError *error = NULL; + + result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (result != NULL) { + ibus_bus_connect_completed (bus); + g_variant_unref (result); + } else { + g_error_free (error); + + g_dbus_connection_close (bus->priv->connection, NULL, NULL, NULL); + g_object_unref (bus->priv->connection); + bus->priv->connection = NULL; + + g_free (bus->priv->bus_address); + bus->priv->bus_address = NULL; + } + + g_object_unref (bus); +} + +static void _bus_connect_async_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) + GAsyncResult *res, + gpointer user_data) { g_return_if_fail (user_data != NULL); g_return_if_fail (IBUS_IS_BUS (user_data)); @@ -349,7 +405,26 @@ _bus_connect_async_cb (GObject *source_object, } if (bus->priv->connection != NULL) { - ibus_bus_connect_completed (bus); + if (bus->priv->use_portal) { + g_object_set_data (G_OBJECT (bus->priv->connection), + "ibus-portal-connection", + GINT_TO_POINTER (TRUE)); + g_dbus_connection_call ( + bus->priv->connection, + IBUS_SERVICE_PORTAL, + IBUS_PATH_IBUS, + "org.freedesktop.DBus.Peer", + "Ping", + g_variant_new ("()"), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + bus->priv->cancellable, + (GAsyncReadyCallback) _bus_connect_start_portal_cb, + g_object_ref (bus)); + } else { + ibus_bus_connect_completed (bus); + } } else { g_free (bus->priv->bus_address); @@ -360,21 +435,32 @@ _bus_connect_async_cb (GObject *source_object, g_object_unref (bus); } +static gchar * +ibus_bus_get_bus_address (IBusBus *bus) +{ + if (_bus->priv->use_portal) + return g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, NULL); + else + return g_strdup (ibus_get_address ()); +} + static void ibus_bus_connect_async (IBusBus *bus) { - const gchar *bus_address = ibus_get_address (); + gchar *bus_address = ibus_bus_get_bus_address (bus); if (bus_address == NULL) return; - if (g_strcmp0 (bus->priv->bus_address, bus_address) == 0) + if (g_strcmp0 (bus->priv->bus_address, bus_address) == 0) { + g_free (bus_address); return; + } /* Close current connection and cancel ongoing connect request. */ ibus_bus_close_connection (bus); - bus->priv->bus_address = g_strdup (bus_address); + bus->priv->bus_address = bus_address; g_object_ref (bus); g_dbus_connection_new_for_address ( bus_address, @@ -385,6 +471,28 @@ ibus_bus_connect_async (IBusBus *bus) _bus_connect_async_cb, bus); } +static gboolean +is_in_flatpak (void) +{ + static gboolean flatpak_info_read; + static gboolean in_flatpak; + + if (flatpak_info_read) + return in_flatpak; + + flatpak_info_read = TRUE; + if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS)) + in_flatpak = TRUE; + return in_flatpak; +} + +static gboolean +ibus_bus_should_connect_portal (IBusBus *bus) +{ + return bus->priv->client_only && + (is_in_flatpak () || g_getenv ("IBUS_USE_PORTAL") != NULL); +} + static void ibus_bus_connect (IBusBus *bus) { @@ -431,7 +539,6 @@ ibus_bus_init (IBusBus *bus) { struct stat buf; gchar *path; - GFile *file; bus->priv = IBUS_BUS_GET_PRIVATE (bus); @@ -443,6 +550,7 @@ ibus_bus_init (IBusBus *bus) bus->priv->watch_ibus_signal_id = 0; bus->priv->unique_name = NULL; bus->priv->connect_async = FALSE; + bus->priv->client_only = FALSE; bus->priv->bus_address = NULL; bus->priv->cancellable = g_cancellable_new (); @@ -453,17 +561,12 @@ ibus_bus_init (IBusBus *bus) if (stat (path, &buf) == 0) { if (buf.st_uid != getuid ()) { - g_warning ("The owner of %s is not %s!", path, ibus_get_user_name ()); + g_warning ("The owner of %s is not %s!", + path, ibus_get_user_name ()); return; } } - file = g_file_new_for_path (ibus_get_socket_path ()); - bus->priv->monitor = g_file_monitor_file (file, 0, NULL, NULL); - - g_signal_connect (bus->priv->monitor, "changed", (GCallback) _changed_cb, bus); - - g_object_unref (file); g_free (path); } @@ -477,6 +580,9 @@ ibus_bus_set_property (IBusBus *bus, case PROP_CONNECT_ASYNC: bus->priv->connect_async = g_value_get_boolean (value); break; + case PROP_CLIENT_ONLY: + bus->priv->client_only = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (bus, prop_id, pspec); } @@ -492,25 +598,73 @@ ibus_bus_get_property (IBusBus *bus, case PROP_CONNECT_ASYNC: g_value_set_boolean (value, bus->priv->connect_async); break; + case PROP_CLIENT_ONLY: + g_value_set_boolean (value, bus->priv->client_only); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (bus, prop_id, pspec); } } +static void +portal_name_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *owner, + gpointer user_data) +{ + IBusBus *bus = IBUS_BUS (user_data); + + if (bus->priv->connection == NULL) + ibus_bus_connect_async (bus); +} + +static void +portal_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + IBusBus *bus = IBUS_BUS (user_data); + + if (bus->priv->connection) + g_dbus_connection_close (bus->priv->connection, NULL, NULL, NULL); +} + + static GObject* ibus_bus_constructor (GType type, guint n_params, GObjectConstructParam *params) { GObject *object; + GFile *file; /* share one IBusBus instance in whole application */ if (_bus == NULL) { - object = G_OBJECT_CLASS (ibus_bus_parent_class)->constructor (type, n_params, params); + object = G_OBJECT_CLASS (ibus_bus_parent_class)->constructor ( + type, n_params, params); /* make bus object sink */ g_object_ref_sink (object); _bus = IBUS_BUS (object); + _bus->priv->use_portal = ibus_bus_should_connect_portal (_bus); + + if (!_bus->priv->use_portal) { + file = g_file_new_for_path (ibus_get_socket_path ()); + _bus->priv->monitor = g_file_monitor_file (file, 0, NULL, NULL); + g_signal_connect (_bus->priv->monitor, "changed", + (GCallback) _changed_cb, _bus); + g_object_unref (file); + } else { + _bus->priv->portal_name_watch_id = + g_bus_watch_name (G_BUS_TYPE_SESSION, + IBUS_SERVICE_PORTAL, + G_BUS_NAME_WATCHER_FLAGS_NONE, + portal_name_appeared, + portal_name_vanished, + _bus, NULL); + } + + if (_bus->priv->connect_async) ibus_bus_connect_async (_bus); else @@ -561,6 +715,11 @@ ibus_bus_destroy (IBusObject *object) g_object_unref (bus->priv->cancellable); bus->priv->cancellable = NULL; + if (bus->priv->portal_name_watch_id) { + g_bus_unwatch_name (bus->priv->portal_name_watch_id); + bus->priv->portal_name_watch_id = 0; + } + IBUS_OBJECT_CLASS (ibus_bus_parent_class)->destroy (object); } @@ -656,6 +815,7 @@ ibus_bus_new (void) { IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS, "connect-async", FALSE, + "client-only", FALSE, NULL)); return bus; @@ -666,6 +826,18 @@ ibus_bus_new_async (void) { IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS, "connect-async", TRUE, + "client-only", FALSE, + NULL)); + + return bus; +} + +IBusBus * +ibus_bus_new_async_client (void) +{ + IBusBus *bus = IBUS_BUS (g_object_new (IBUS_TYPE_BUS, + "connect-async", TRUE, + "client-only", TRUE, NULL)); return bus; @@ -679,7 +851,7 @@ ibus_bus_is_connected (IBusBus *bus) if (bus->priv->connection == NULL || g_dbus_connection_is_closed (bus->priv->connection)) return FALSE; - return TRUE; + return bus->priv->connected; } IBusInputContext * @@ -795,9 +967,9 @@ ibus_bus_create_input_context_async (IBusBus *bus, * 2. New local IBusInputContext proxy of the remote IC */ g_dbus_connection_call (bus->priv->connection, - IBUS_SERVICE_IBUS, + ibus_bus_get_service_name (bus), IBUS_PATH_IBUS, - IBUS_INTERFACE_IBUS, + bus->priv->use_portal ? IBUS_INTERFACE_PORTAL : IBUS_INTERFACE_IBUS, "CreateInputContext", g_variant_new ("(s)", client_name), G_VARIANT_TYPE("(o)"), @@ -1454,6 +1626,14 @@ ibus_bus_get_connection (IBusBus *bus) return bus->priv->connection; } +const gchar * +ibus_bus_get_service_name (IBusBus *bus) +{ + if (bus->priv->use_portal) + return IBUS_SERVICE_PORTAL; + return IBUS_SERVICE_IBUS; +} + gboolean ibus_bus_exit (IBusBus *bus, gboolean restart) @@ -2369,6 +2549,13 @@ ibus_bus_call_sync (IBusBus *bus, g_assert (member != NULL); g_return_val_if_fail (ibus_bus_is_connected (bus), NULL); + if (bus->priv->use_portal && + g_strcmp0 (bus_name, IBUS_SERVICE_IBUS) == 0) { + bus_name = IBUS_SERVICE_PORTAL; + if (g_strcmp0 (interface, IBUS_INTERFACE_IBUS) == 0) + interface = IBUS_INTERFACE_PORTAL; + } + GError *error = NULL; GVariant *result; result = g_dbus_connection_call_sync (bus->priv->connection, @@ -2436,6 +2623,13 @@ ibus_bus_call_async (IBusBus *bus, task = g_task_new (bus, cancellable, callback, user_data); g_task_set_source_tag (task, source_tag); + if (bus->priv->use_portal && + g_strcmp0 (bus_name, IBUS_SERVICE_IBUS) == 0) { + bus_name = IBUS_SERVICE_PORTAL; + if (g_strcmp0 (interface, IBUS_INTERFACE_IBUS) == 0) + interface = IBUS_INTERFACE_PORTAL; + } + g_dbus_connection_call (bus->priv->connection, bus_name, path, diff --git a/src/ibusbus.h b/src/ibusbus.h index 9f65d36a..dff3dfb7 100644 --- a/src/ibusbus.h +++ b/src/ibusbus.h @@ -105,6 +105,19 @@ IBusBus *ibus_bus_new (void); */ IBusBus *ibus_bus_new_async (void); +/** + * ibus_bus_new_async_client: + * + * Creates a new #IBusBus instance for client use only. It will possibly + * be limited in what it can do. + * + * The instance will asynchronously connect to the IBus daemon. + * + * Returns: A newly allocated #IBusBus instance, and the instance is not + * floating. + */ +IBusBus *ibus_bus_new_async_client (void); + /** * ibus_bus_is_connected: @@ -128,6 +141,16 @@ GDBusConnection * ibus_bus_get_connection (IBusBus *bus); /** + * ibus_bus_get_service_name: + * @bus: An #IBusBus. + * + * Return the main service name to use for calls on the ibus connection. + * + * Returns: at dbus name. + */ +const gchar * ibus_bus_get_service_name (IBusBus *bus); + +/** * ibus_bus_hello: * @bus: An #IBusBus. * diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c index 9a50acc0..ae7048ad 100644 --- a/src/ibusinputcontext.c +++ b/src/ibusinputcontext.c @@ -684,16 +684,20 @@ ibus_input_context_new (const gchar *path, { g_assert (path != NULL); g_assert (G_IS_DBUS_CONNECTION (connection)); + const gchar *service_name = IBUS_SERVICE_IBUS; GInitable *initable; GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START; + if (g_object_get_data (G_OBJECT (connection), "ibus-portal-connection")) + service_name = IBUS_SERVICE_PORTAL; + initable = g_initable_new (IBUS_TYPE_INPUT_CONTEXT, cancellable, error, "g-connection", connection, - "g-name", IBUS_SERVICE_IBUS, + "g-name", service_name, "g-flags", flags, "g-interface-name", IBUS_INTERFACE_INPUT_CONTEXT, "g-object-path", path, @@ -714,16 +718,20 @@ ibus_input_context_new_async (const gchar *path, g_assert (path != NULL); g_assert (G_IS_DBUS_CONNECTION (connection)); g_assert (callback != NULL); + const gchar *service_name = IBUS_SERVICE_IBUS; GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START; + if (g_object_get_data (G_OBJECT (connection), "ibus-portal-connection")) + service_name = IBUS_SERVICE_PORTAL; + g_async_initable_new_async (IBUS_TYPE_INPUT_CONTEXT, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-connection", connection, - "g-name", IBUS_SERVICE_IBUS, + "g-name", service_name, "g-flags", flags, "g-interface-name", IBUS_INTERFACE_INPUT_CONTEXT, "g-object-path", path, -- 2.13.4 From 9772e800f3e6937510f2609c5ce9a6860c59cb81 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 4 Sep 2017 12:02:17 +0900 Subject: [PATCH 05/14] test: Testing in flatpak Test with: flatpak-builder --force-clean app org.test.IBus.json flatpak-builder --run --nofilesystem=host app org.test.IBus.json zenity --entry BUG=https://github.com/flatpak/flatpak/issues/675 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/329090043 Patch from Alexander Larsson . --- test/org.test.IBus.json | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/org.test.IBus.json diff --git a/test/org.test.IBus.json b/test/org.test.IBus.json new file mode 100644 index 00000000..dc3caa62 --- /dev/null +++ b/test/org.test.IBus.json @@ -0,0 +1,43 @@ +{ + "app-id": "org.test.IBus", + "runtime": "org.gnome.Platform", + "runtime-version": "3.24", + "sdk": "org.gnome.Sdk", + "command": "/usr/bin/zenity", + "finish-args": [ + /* X11 + XShm access */ + "--share=ipc", "--socket=x11", + /* Wayland access */ + "--socket=wayland", + /* Needed for dconf to work */ + "--filesystem=xdg-run/dconf", "--filesystem=~/.config/dconf:ro", + "--talk-name=ca.desrt.dconf", "--env=DCONF_USER_CONFIG_DIR=.config/dconf", + "--env=GTK_IM_MODULE_FILE=/app/lib/gtk-3.0/3.0.0/immodules.cache" + ], + "build-options" : { + "cflags": "-O2 -g", + "cxxflags": "-O2 -g", + "env": { + "V": "1" + } + }, + "cleanup": ["/include", "/lib/pkgconfig", + "/share/pkgconfig", "/share/aclocal", + "/man", "/share/man", "/share/gtk-doc", + "/share/vala", + "*.la", "*.a"], + "modules": [ + { + "name": "ibus", + "sources": [ + { + "type": "git", + "url": "https://github.com/alexlarsson/ibus", + "branch": "ibus-portal" + } + ], + "config-opts": ["--disable-emoji-dict", "--disable-dconf"], + "post-install": ["gtk-query-immodules-3.0 /app/lib/gtk-3.0/3.0.0/immodules/im-ibus.so > /app/lib/gtk-3.0/3.0.0/immodules.cache"] + } + ] +} -- 2.13.4 From 9937a0e4501ccf0cfd238ce7c97733c3099db3f7 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Mon, 4 Sep 2017 12:19:07 +0900 Subject: [PATCH 06/14] bus: ibus-daemon activates ibus-portal When ibus-daemon restarts, ibus-portal exits with on_name_lost() and the clients wait for portal_name_appeared() until ibus-poral restarts. Now the clients can connect to ibus-daemon with this way and also they don't have to activate ibus-portal. BUG=https://github.com/flatpak/flatpak/issues/675 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/321530043 --- bus/server.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- portal/portal.c | 6 +----- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/bus/server.c b/bus/server.c index ff3ea093..e2898274 100644 --- a/bus/server.c +++ b/bus/server.c @@ -93,6 +93,45 @@ bus_new_connection_cb (GDBusServer *server, return TRUE; } +static void +_server_connect_start_portal_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GVariant *result; + GError *error = NULL; + + result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (result != NULL) { + g_variant_unref (result); + } else { + g_print ("portal is not running: %s\n", error->message); + g_error_free (error); + } +} + +static void +bus_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_dbus_connection_call (connection, + IBUS_SERVICE_PORTAL, + IBUS_PATH_IBUS, + "org.freedesktop.DBus.Peer", + "Ping", + g_variant_new ("()"), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL /* cancellable */, + (GAsyncReadyCallback) + _server_connect_start_portal_cb, + NULL); +} + void bus_server_init (void) { @@ -134,8 +173,10 @@ bus_server_init (void) ibus_write_address (address); /* own a session bus name so that third parties can easily track our life-cycle */ - g_bus_own_name (G_BUS_TYPE_SESSION, IBUS_SERVICE_IBUS, G_BUS_NAME_OWNER_FLAGS_NONE, - NULL, NULL, NULL, NULL, NULL); + g_bus_own_name (G_BUS_TYPE_SESSION, IBUS_SERVICE_IBUS, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + NULL, NULL, NULL, NULL); } const gchar * diff --git a/portal/portal.c b/portal/portal.c index 0415f996..cb24d257 100644 --- a/portal/portal.c +++ b/portal/portal.c @@ -101,10 +101,6 @@ _forward_method_cb (GObject *source_object, gpointer user_data) { GDBusMethodInvocation *invocation = user_data; - IBusPortalContext *portal_context = - (IBusPortalContext *) g_dbus_method_invocation_get_user_data ( - invocation); - IBusEngineDesc *desc; GError *error = NULL; GVariant *variant = g_dbus_proxy_call_finish ((GDBusProxy *) source_object, @@ -413,6 +409,7 @@ ibus_portal_context_handle_destroy (IBusDbusService *object, IBusPortalContext *portal_context) { g_object_unref (portal_context); + return FALSE; } static IBusPortalContext * @@ -475,7 +472,6 @@ create_input_context_done (IBusBus *bus, GError *error = NULL; IBusInputContext *context; IBusPortalContext *portal_context; - char *object_path; context = ibus_bus_create_input_context_async_finish (ibus_bus, res, -- 2.13.4 From 3e01bab972ad12b92c55a9dde554a0359c217290 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 6 Sep 2017 12:04:52 +0900 Subject: [PATCH 07/14] portal: Add org.freedesktop.IBus.Portal.xml in EXTRA_DIST Review URL: https://codereview.appspot.com/325370043 --- portal/Makefile.am | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/portal/Makefile.am b/portal/Makefile.am index 954fc591..d1e2051a 100644 --- a/portal/Makefile.am +++ b/portal/Makefile.am @@ -65,12 +65,6 @@ ibus_portal_LDADD = \ $(AM_LDADD) \ $(NULL) -EXTRA_DIST = \ - $(NULL) - -CLEANFILES = \ - $(NULL) - $(libibus): $(MAKE) -C $(top_builddir)/src @@ -89,7 +83,14 @@ $(ibus_dbus_built_sources) : org.freedesktop.IBus.Portal.xml $^ \ $(NULL) -EXTRA_DIST += $(dbusservice_in_files) -CLEANFILES += $(dbusservice_DATA) +EXTRA_DIST = \ + $(dbusservice_in_files) \ + org.freedesktop.IBus.Portal.xml \ + $(NULL) + +CLEANFILES = \ + $(dbusservice_DATA) \ + $(NULL) + -include $(top_srcdir)/git.mk -- 2.13.4 From 21bac4733684ca6a74ddb02f457c0fe19eb9180d Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 6 Sep 2017 12:11:01 +0900 Subject: [PATCH 08/14] ui/gtk3: Move ibus-emoji-dialog.vapi from ui/gtk3 to bindings/vala R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/322590043 --- .../vala}/IBusEmojiDialog-1.0.metadata | 0 bindings/vala/Makefile.am | 161 +++++++++++++++++++-- .../vala}/ibus-emoji-dialog-1.0.deps | 0 po/POTFILES.skip | 5 + ui/gtk3/Makefile.am | 113 ++++----------- ui/gtk3/emojier.vala | 19 +-- ui/gtk3/ibusemojidialog.h | 26 ++++ 7 files changed, 213 insertions(+), 111 deletions(-) rename {ui/gtk3 => bindings/vala}/IBusEmojiDialog-1.0.metadata (100%) rename {ui/gtk3 => bindings/vala}/ibus-emoji-dialog-1.0.deps (100%) diff --git a/ui/gtk3/IBusEmojiDialog-1.0.metadata b/bindings/vala/IBusEmojiDialog-1.0.metadata similarity index 100% rename from ui/gtk3/IBusEmojiDialog-1.0.metadata rename to bindings/vala/IBusEmojiDialog-1.0.metadata diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am index 4e34afc7..fc8e2f01 100644 --- a/bindings/vala/Makefile.am +++ b/bindings/vala/Makefile.am @@ -3,7 +3,8 @@ # ibus - The Input Bus # # Copyright (c) 2007-2016 Peng Huang -# Copyright (c) 2007-2016 Red Hat, Inc. +# Copyright (c) 2017 Takao Fujiwara +# Copyright (c) 2007-2017 Red Hat, Inc. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,15 +23,47 @@ -include $(VAPIGEN_MAKEFILE) +libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la + +noinst_LTLIBRARIES = +noinst_DATA = +INTROSPECTION_GIRS = +girdir = $(datadir)/gir-1.0 + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src \ + -include $(CONFIG_HEADER) \ + $(NULL) +AM_CFLAGS = \ + -DG_LOG_DOMAIN=\"IBUS\" \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + -DIBUS_DISABLE_DEPRECATED \ + -Wno-unused-variable \ + -Wno-unused-but-set-variable \ + -Wno-unused-function \ + $(NULL) +AM_VALAFLAGS = \ + --vapidir=$(builddir) \ + --vapidir=$(srcdir) \ + --pkg=posix \ + --pkg=gtk+-3.0 \ + --pkg=gdk-x11-3.0 \ + --pkg=ibus-1.0 \ + --pkg=config \ + --pkg=xi \ + --target-glib="$(VALA_TARGET_GLIB_VERSION)" \ + $(NULL) + vapi_deps = \ IBus-1.0.metadata \ - IBus-1.0-custom.vala \ $(top_builddir)/src/IBus-1.0.gir \ $(NULL) ibus-1.0.vapi: $(vapi_deps) -VAPIGEN_VAPIS = ibus-1.0.vapi +ibus_vapi = ibus-1.0.vapi +VAPIGEN_VAPIS = $(ibus_vapi) ibus_1_0_vapi_DEPS = gio-2.0 ibus_1_0_vapi_METADATADIRS = $(srcdir) @@ -40,18 +73,118 @@ ibus_1_0_vapi_FILES = \ $(NULL) vapidir = $(datadir)/vala/vapi -vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps) +vapi_DATA = $(ibus_vapi) $(ibus_vapi:.vapi=.deps) -MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS) -DISTCLEANFILES = $(VAPIGEN_VAPIS) +MAINTAINERCLEANFILES = $(ibus_vapi) +DISTCLEANFILES = $(ibus_vapi) -EXTRA_DIST = \ - $(VAPIGEN_VAPIS) \ - IBus-1.0.metadata \ - IBus-1.0-custom.vala \ - ibus-1.0.deps \ - config.vapi \ - xi.vapi \ - $(NULL) +EXTRA_DIST = \ + $(ibus_vapi) \ + IBus-1.0.metadata \ + IBus-1.0-custom.vala \ + IBusEmojiDialog-1.0.metadata \ + ibus-1.0.deps \ + ibus-emoji-dialog-1.0.deps \ + config.vapi \ + xi.vapi \ + $(NULL) + +if ENABLE_EMOJI_DICT +AM_VALAFLAGS += --define=EMOJI_DICT + +libibus_emoji_dialog = libibus-emoji-dialog-1.0.la +noinst_LTLIBRARIES += $(libibus_emoji_dialog) + +libibus_emoji_dialog_1_0_la_SOURCES = \ + candidatearea.vala \ + emojier.vala \ + iconwidget.vala \ + pango.vala \ + separator.vala \ + $(NULL) +libibus_emoji_dialog_1_0_la_CFLAGS = \ + $(AM_CFLAGS) \ + @GLIB2_CFLAGS@ \ + @GIO2_CFLAGS@ \ + @GTHREAD2_CFLAGS@ \ + @GTK3_CFLAGS@ \ + @X11_CFLAGS@ \ + -DBINDIR=\"$(bindir)\" \ + $(NULL) +libibus_emoji_dialog_1_0_la_LIBADD = \ + @GLIB2_LIBS@ \ + @GIO2_LIBS@ \ + @GTHREAD2_LIBS@ \ + @GTK3_LIBS@ \ + @X11_LIBS@ \ + -lXi \ + $(libibus) \ + $(NULL) +libibus_emoji_dialog_1_0_la_LDFLAGS = \ + -no-undefined \ + -export-symbols-regex "ibus_.*" \ + $(NULL) + +# per file setting is needed to avoid conflicting LN_S by calling +# duplicated times in parallel make +%.vala: $(ibus_vapi) + if test ! -f $@ ; then \ + $(LN_S) $(top_srcdir)/ui/gtk3/$@ .; \ + fi; + +MAINTAINERCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES) +DISTCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES) + +if HAVE_INTROSPECTION +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_SCANNER_ARGS = +INTROSPECTION_COMPILER_ARGS = \ + --includedir=$(srcdir) \ + --includedir=. \ + --includedir=$(top_srcdir)/src \ + $(NULL) + + +emoji_headers = \ + $(top_srcdir)/ui/gtk3/ibusemojidialog.h \ + $(NULL) + +IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile +IBusEmojiDialog_1_0_gir_SCANNERFLAGS = \ + --pkg-export=ibus-1.0 \ + --pkg=gtk+-3.0 \ + $(IBUS_GIR_SCANNERFLAGS) \ + $(NULL) +IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0 +IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus) +IBusEmojiDialog_1_0_gir_FILES = $(emoji_headers) +IBusEmojiDialog_1_0_gir_CFLAGS = \ + -I$(srcdir) \ + -I$(builddir) \ + -I$(top_srcdir)/src \ + $(NULL) + +ibus_emoji_dialog_gir = IBusEmojiDialog-1.0.gir +INTROSPECTION_GIRS += $(ibus_emoji_dialog_gir) +noinst_DATA += $(ibus_emoji_dialog_gir) +EXTRA_DIST += $(ibus_emoji_dialog_gir) +MAINTAINERCLEANFILES += $(ibus_emoji_dialog_gir) +DISTCLEANFILES += $(ibus_emoji_dialog_gir) + +ibus-emoji-dialog-1.0.vapi: $(ibus_emoji_dialog_gir) IBusEmojiDialog-1.0.metadata +ibus_emoji_dialog_vapi = ibus-emoji-dialog-1.0.vapi +ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0 +ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir) +ibus_emoji_dialog_1_0_vapi_FILES = IBusEmojiDialog-1.0.gir +VAPIGEN_VAPIS += $(ibus_emoji_dialog_vapi) +noinst_DATA += $(ibus_emoji_dialog_vapi) +EXTRA_DIST += $(ibus_emoji_dialog_vapi) +MAINTAINERCLEANFILES += $(ibus_emoji_dialog_vapi) +DISTCLEANFILES += $(ibus_emoji_dialog_vapi) + +endif +#end of HAVE_INTROSPECTION +endif +# end of ENABLE_EMOJI_DICT -include $(top_srcdir)/git.mk diff --git a/ui/gtk3/ibus-emoji-dialog-1.0.deps b/bindings/vala/ibus-emoji-dialog-1.0.deps similarity index 100% rename from ui/gtk3/ibus-emoji-dialog-1.0.deps rename to bindings/vala/ibus-emoji-dialog-1.0.deps diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 7190221d..10b88298 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -2,6 +2,11 @@ # Please keep this file in alphabetical order. # Files under ui/gtk2/ are not shipped in the distribution, but kept # in the git repository for reference. +bindings/vala/candidatearea.c +bindings/vala/emojier.c +bindings/vala/iconwidget.c +bindings/vala/pango.c +bindings/vala/separator.c ibus/_config.py tools/main.c ui/gtk2/candidatepanel.py diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am index c79641a5..786b80e6 100644 --- a/ui/gtk3/Makefile.am +++ b/ui/gtk3/Makefile.am @@ -81,10 +81,6 @@ AM_VALAFLAGS = \ --target-glib="$(VALA_TARGET_GLIB_VERSION)" \ $(NULL) -MAINTAINERCLEANFILES = -DISTCLEANFILES = -noinst_DATA = - if ENABLE_LIBNOTIFY AM_CFLAGS += \ @LIBNOTIFY_CFLAGS@ \ @@ -158,9 +154,8 @@ man_seven_in_files = ibus-emoji.7.in EXTRA_DIST = \ $(emoji_headers) \ $(man_seven_in_files) \ - IBusEmojiDialog-1.0.metadata \ + emojierapp.vala \ gtkpanel.xml.in \ - ibus-emoji-dialog-1.0.deps \ notification-item.xml \ notification-watcher.xml \ $(NULL) @@ -168,98 +163,40 @@ EXTRA_DIST = \ if ENABLE_EMOJI_DICT AM_VALAFLAGS += --define=EMOJI_DICT -libibus_emoji_dialog = libibus-emoji-dialog-1.0.la - -noinst_LTLIBRARIES = $(libibus_emoji_dialog) - -libibus_emoji_dialog_1_0_la_CFLAGS = $(AM_CFLAGS) -libibus_emoji_dialog_1_0_la_LDFLAGS = \ - -no-undefined \ - -export-symbols-regex "ibus_.*" \ - -version-info @LT_VERSION_INFO@ \ - $(NULL) -libibus_emoji_dialog_1_0_la_SOURCES = \ - candidatearea.vala \ - emojier.vala \ - iconwidget.vala \ - pango.vala \ - separator.vala \ - $(NULL) - libexec_PROGRAMS += ibus-ui-emojier -ibus_ui_emojier_SOURCES = \ - $(libibus_emoji_dialog_1_0_la_SOURCES) \ +ibus_ui_emojier_VALASOURCES = \ emojierapp.vala \ + candidatearea.vala \ + emojier.vala \ + iconwidget.vala \ + pango.vala \ + separator.vala \ + $(NULL) +ibus_ui_emojier_SOURCES = \ + $(ibus_ui_emojier_VALASOURCES:.vala=.c) \ $(NULL) ibus_ui_emojier_LDADD = \ $(AM_LDADD) \ $(NULL) --include $(INTROSPECTION_MAKEFILE) -INTROSPECTION_SCANNER_ARGS = -INTROSPECTION_COMPILER_ARGS = \ - --includedir=$(srcdir) \ - --includedir=. \ - --includedir=$(top_srcdir)/src \ - $(NULL) - -if HAVE_INTROSPECTION -introspection_sources = \ - $(emoji_headers) \ - $(NULL) -IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile -IBusEmojiDialog_1_0_gir_SCANNERFLAGS = \ - --pkg-export=ibus-1.0 \ - --pkg=gtk+-3.0 \ - $(IBUS_GIR_SCANNERFLAGS) \ +ibus_ui_emojier_VALAFLAGS = \ + $(AM_VALAFLAGS) \ $(NULL) -IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile -IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0 -IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus) -IBusEmojiDialog_1_0_gir_FILES = \ - $(addprefix $(srcdir)/,$(introspection_sources)) \ - $(NULL) -IBusEmojiDialog_1_0_gir_CFLAGS = \ - -DIBUS_COMPILATION \ - -I$(srcdir) \ - -I$(builddir) \ - -I$(top_srcdir)/src \ - $(NULL) -INTROSPECTION_GIRS = IBusEmojiDialog-1.0.gir - -girdir = $(datadir)/gir-1.0 -noinst_DATA += $(INTROSPECTION_GIRS) -CLEANFILES += $(INTROSPECTION_GIRS) -typelibsdir = $(libdir)/girepository-1.0 -noinst_DATA += $(INTROSPECTION_GIRS:.gir=.typelib) -CLEANFILES += $(INTROSPECTION_GIRS:.gir=.typelib) - - -if ENABLE_VAPIGEN --include $(VAPIGEN_MAKEFILE) - -ibus-emoji-dialog-1.0.vapi: $(INTROSPECTION_GIRS) IBusEmojiDialog-1.0.metadata - -VAPIGEN_VAPIS = ibus-emoji-dialog-1.0.vapi - -ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0 -ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir) -ibus_emoji_dialog_1_0_vapi_FILES = $(INTROSPECTION_GIRS) - -vapidir = $(datadir)/vala/vapi -noinst_DATA += $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps) - -MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS) -DISTCLEANFILES += $(VAPIGEN_VAPIS) -EXTRA_DIST += $(VAPIGEN_VAPIS) - -# end of ENABLE_VAPIGEN -endif -# end of HAVE_INTROSPECTION -endif +# This line and foo_VALASOURCES line can delete the duplicated entries +# of emojier.c: emojier.vala +emojierapp.c: $(ibus_ui_emojier_VALASOURCES) + $(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \ +$(VALAFLAGS) -C $(ibus_ui_emojier_VALASOURCES) + $(NULL) +# make dist creates .c files in a different srcdir +emojierapp.o: $(srcdir)/emojierapp.c + $(AM_V_CC)source='$<' object='$@' libtool=no \ + DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ + $(AM_V_CC_no)$(COMPILE) -c -o $@ $< + $(NULL) man_seven_files = $(man_seven_in_files:.7.in=.7) man_seven_DATA =$(man_seven_files:.7=.7.gz) @@ -276,7 +213,7 @@ CLEANFILES += \ $(man_seven_files) \ $(NULL) -# end of ENABLE_EMOJI_DICT endif +# end of ENABLE_EMOJI_DICT -include $(top_srcdir)/git.mk diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala index 9df59ac4..36ab4bab 100644 --- a/ui/gtk3/emojier.vala +++ b/ui/gtk3/emojier.vala @@ -1139,6 +1139,7 @@ class IBusEmojier : Gtk.ApplicationWindow { m_category_active_index = (int)list.length(); } Gtk.Adjustment adjustment = m_list_box.get_adjustment(); + m_scrolled_window.set_hadjustment(new Gtk.Adjustment(0, 0, 0, 0, 0, 0)); m_scrolled_window.set_vadjustment(adjustment); show_category_list(); } @@ -1156,7 +1157,7 @@ class IBusEmojier : Gtk.ApplicationWindow { else if (keyval == Gdk.Key.Right) m_lookup_table.cursor_down(); show_candidate_panel(); - } else if (m_entry.get_text().len() > 0) { + } else if (m_entry.get_text().length > 0) { int step = 0; if (keyval == Gdk.Key.Left) step = -1; @@ -1211,7 +1212,7 @@ class IBusEmojier : Gtk.ApplicationWindow { show_candidate_panel(); return true; } - if (m_entry.get_text().len() > 0) { + if (m_entry.get_text().length > 0) { int step = 0; if (keyval == Gdk.Key.Home) step = -1; @@ -1410,7 +1411,7 @@ class IBusEmojier : Gtk.ApplicationWindow { key_press_enter(); return true; case Gdk.Key.BackSpace: - if (m_entry.get_text().len() > 0) { + if (m_entry.get_text().length > 0) { if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) { GLib.Signal.emit_by_name(m_entry, "delete-from-cursor", Gtk.DeleteType.WORD_ENDS, -1); @@ -1422,7 +1423,7 @@ class IBusEmojier : Gtk.ApplicationWindow { break; case Gdk.Key.Delete: case Gdk.Key.KP_Delete: - if (m_entry.get_text().len() > 0) { + if (m_entry.get_text().length > 0) { if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) { GLib.Signal.emit_by_name(m_entry, "delete-from-cursor", Gtk.DeleteType.WORD_ENDS, 1); @@ -1436,7 +1437,7 @@ class IBusEmojier : Gtk.ApplicationWindow { case Gdk.Key.space: case Gdk.Key.KP_Space: if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) { - if (m_entry.get_text().len() > 0) + if (m_entry.get_text().length > 0) entry_enter_keyval(keyval); } else if (m_candidate_panel_is_visible) { enter_notify_disable_with_timer(); @@ -1512,7 +1513,7 @@ class IBusEmojier : Gtk.ApplicationWindow { return true; break; case Gdk.Key.u: - if (m_entry.get_text().len() > 0) { + if (m_entry.get_text().length > 0) { GLib.Signal.emit_by_name(m_entry, "delete-from-cursor", Gtk.DeleteType.PARAGRAPH_ENDS, @@ -1521,13 +1522,13 @@ class IBusEmojier : Gtk.ApplicationWindow { } break; case Gdk.Key.a: - if (m_entry.get_text().len() > 0) { + if (m_entry.get_text().length > 0) { m_entry.select_region(0, -1); return true; } break; case Gdk.Key.x: - if (m_entry.get_text().len() > 0) { + if (m_entry.get_text().length > 0) { GLib.Signal.emit_by_name(m_entry, "cut-clipboard"); return true; } @@ -1544,7 +1545,7 @@ class IBusEmojier : Gtk.ApplicationWindow { clipboard.store(); return true; } - } else if (m_entry.get_text().len() > 0) { + } else if (m_entry.get_text().length > 0) { GLib.Signal.emit_by_name(m_entry, "copy-clipboard"); return true; } diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h index 24d195c8..ed8886a8 100644 --- a/ui/gtk3/ibusemojidialog.h +++ b/ui/gtk3/ibusemojidialog.h @@ -170,5 +170,31 @@ void ibus_emojier_set_favorites (gchar** favorites, favorite_annotations, int favorite_annotations_length); + +/** + * ibus_emojier_set_partial_match: + * @has_partial_match: Enable the partial match if %TRUE. Otherwise if %FALSE. + * + * Set partial match for emoji annotations. + */ +void ibus_emojier_set_partial_match (gboolean has_partial_match); + +/** + * ibus_emojier_set_partial_match_length: + * @length: minimum lenght to match partially. + * + * Set the minimum lenght to match partially. + */ +void ibus_emojier_set_partial_match_length + (gint length); + +/** + * ibus_emojier_set_partial_match_condition: + * @condition: condition id between 0 and 2. + * + * Set the partial match condition with the integer. + */ +void ibus_emojier_set_partial_match_condition + (gint condition); G_END_DECLS #endif -- 2.13.4 From d788918b635275d0247e68f26f9c840100bca366 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 6 Sep 2017 12:17:30 +0900 Subject: [PATCH 09/14] ui/gtk3: Switcher should ignore mouse until it moves BUG=https://github.com/ibus/ibus/issues/1929 Review URL: https://codereview.appspot.com/329100043 --- ui/gtk3/switcher.vala | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/ui/gtk3/switcher.vala b/ui/gtk3/switcher.vala index cf187555..269a68d4 100644 --- a/ui/gtk3/switcher.vala +++ b/ui/gtk3/switcher.vala @@ -91,6 +91,9 @@ class Switcher : Gtk.Window { private uint m_popup_delay_time_id = 0; private int m_root_x; private int m_root_y; + private double m_mouse_init_x; + private double m_mouse_init_y; + private bool m_mouse_moved; private GLib.HashTable m_xkb_languages = new GLib.HashTable(GLib.str_hash, GLib.str_equal); @@ -221,6 +224,11 @@ class Switcher : Gtk.Window { Gdk.CURRENT_TIME); if (status != Gdk.GrabStatus.SUCCESS) warning("Grab pointer failed! status = %d", status); + // Probably we can delete m_popup_delay_time in 1.6 + pointer.get_position_double(null, + out m_mouse_init_x, + out m_mouse_init_y); + m_mouse_moved = false; m_loop = new GLib.MainLoop(); @@ -263,12 +271,30 @@ class Switcher : Gtk.Window { var button = new IBusEngineButton(engine, this); var longname = engine.get_longname(); button.set_relief(Gtk.ReliefStyle.NONE); + button.add_events(Gdk.EventMask.POINTER_MOTION_MASK); button.show(); button.enter_notify_event.connect((e) => { + // avoid gtk_button_update_state() + return true; + }); + button.motion_notify_event.connect((e) => { +#if VALA_0_24 + Gdk.EventMotion pe = e; +#else + Gdk.EventMotion *pe = &e; +#endif + if (m_selected_engine == index) + return false; + if (!m_mouse_moved && + m_mouse_init_x == pe.x_root && + m_mouse_init_y == pe.y_root) { + return false; + } + m_mouse_moved = true; button.grab_focus(); m_selected_engine = index; - return true; + return false; }); button.button_press_event.connect((e) => { -- 2.13.4 From bbfb3d738b9d61d1eb0658a9ce56e3cd8c111ac4 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 6 Sep 2017 14:08:40 +0900 Subject: [PATCH 10/14] client/gtk2: Do not send key events to GtkIMContextSimple GtkIMContextSimple binds Ctrl-Shift-u but IBus clients do not now. BUG=https://github.com/ibus/ibus/issues/1889 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/327290043 --- client/gtk2/ibusimcontext.c | 41 +++++++++++++++++++++++++++++++++++++++-- src/ibusenginesimple.c | 23 ++--------------------- src/ibusenginesimple.h | 24 ++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index b4ca8828..3ea46951 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -2,7 +2,8 @@ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2013 Peng Huang - * Copyright (C) 2008-2013 Red Hat, Inc. + * Copyright (C) 2015-2017 Takao Fujiwara + * Copyright (C) 2008-2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -247,6 +248,39 @@ _focus_out_cb (GtkWidget *widget, return FALSE; } +static gboolean +ibus_im_context_commit_event (IBusIMContext *ibusimcontext, + GdkEventKey *event) +{ + int i; + GdkModifierType no_text_input_mask; + gunichar ch; + + if (event->type == GDK_KEY_RELEASE) + return FALSE; + /* Ignore modifier key presses */ + for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++) + if (event->keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i]) + return FALSE; + no_text_input_mask = gdk_keymap_get_modifier_mask ( + gdk_keymap_get_for_display (gdk_display_get_default ()), + GDK_MODIFIER_INTENT_NO_TEXT_INPUT); + if (event->state & no_text_input_mask || + event->keyval == GDK_KEY_Return || + event->keyval == GDK_KEY_ISO_Enter || + event->keyval == GDK_KEY_KP_Enter) { + return FALSE; + } + ch = ibus_keyval_to_unicode (event->keyval); + if (ch != 0 && !g_unichar_iscntrl (ch)) { + IBusText *text = ibus_text_new_from_unichar (ch); + g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text); + g_object_unref (text); + return TRUE; + } + return FALSE; +} + static void _process_key_event_done (GObject *object, GAsyncResult *res, @@ -797,8 +831,11 @@ ibus_im_context_filter_keypress (GtkIMContext *context, if (event->state & IBUS_HANDLED_MASK) return TRUE; + /* Do not call gtk_im_context_filter_keypress() because + * gtk_im_context_simple_filter_keypress() binds Ctrl-Shift-u + */ if (event->state & IBUS_IGNORED_MASK) - return gtk_im_context_filter_keypress (ibusimcontext->slave, event); + return ibus_im_context_commit_event (ibusimcontext, event); /* XXX it is a workaround for some applications do not set client * window. */ diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c index cddd932c..63785223 100644 --- a/src/ibusenginesimple.c +++ b/src/ibusenginesimple.c @@ -81,25 +81,6 @@ const IBusComposeTableCompact ibus_compose_table_compact = { static GSList *global_tables; -static const guint16 ibus_compose_ignore[] = { - IBUS_KEY_Shift_L, - IBUS_KEY_Shift_R, - IBUS_KEY_Control_L, - IBUS_KEY_Control_R, - IBUS_KEY_Caps_Lock, - IBUS_KEY_Shift_Lock, - IBUS_KEY_Meta_L, - IBUS_KEY_Meta_R, - IBUS_KEY_Alt_L, - IBUS_KEY_Alt_R, - IBUS_KEY_Super_L, - IBUS_KEY_Super_R, - IBUS_KEY_Hyper_L, - IBUS_KEY_Hyper_R, - IBUS_KEY_Mode_switch, - IBUS_KEY_ISO_Level3_Shift -}; - /* functions prototype */ static void ibus_engine_simple_destroy (IBusEngineSimple *simple); static void ibus_engine_simple_reset (IBusEngine *engine); @@ -1045,8 +1026,8 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, } /* Ignore modifier key presses */ - for (i = 0; i < G_N_ELEMENTS (ibus_compose_ignore); i++) - if (keyval == ibus_compose_ignore[i]) + for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++) + if (keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i]) return FALSE; if ((priv->in_hex_sequence || priv->in_emoji_sequence) diff --git a/src/ibusenginesimple.h b/src/ibusenginesimple.h index 8712659c..a5ef34fb 100644 --- a/src/ibusenginesimple.h +++ b/src/ibusenginesimple.h @@ -2,8 +2,8 @@ /* vim:set et sts=4: */ /* ibus - The Input Bus * Copyright (C) 2008-2015 Peng Huang - * Copyright (C) 2015-2016 Takao Fujiwara - * Copyright (C) 2008-2016 Red Hat, Inc. + * Copyright (C) 2015-2017 Takao Fujiwara + * Copyright (C) 2008-2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,6 +40,7 @@ */ #include "ibusengine.h" +#include "ibuskeysyms.h" G_BEGIN_DECLS @@ -94,6 +95,25 @@ struct _IBusEngineSimpleClass { gpointer pdummy[8]; }; +static const guint16 IBUS_COMPOSE_IGNORE_KEYLIST[] = { + IBUS_KEY_Shift_L, + IBUS_KEY_Shift_R, + IBUS_KEY_Control_L, + IBUS_KEY_Control_R, + IBUS_KEY_Caps_Lock, + IBUS_KEY_Shift_Lock, + IBUS_KEY_Meta_L, + IBUS_KEY_Meta_R, + IBUS_KEY_Alt_L, + IBUS_KEY_Alt_R, + IBUS_KEY_Super_L, + IBUS_KEY_Super_R, + IBUS_KEY_Hyper_L, + IBUS_KEY_Hyper_R, + IBUS_KEY_Mode_switch, + IBUS_KEY_ISO_Level3_Shift +}; + GType ibus_engine_simple_get_type (void); /** -- 2.13.4 From d784e04c38eeb069f9a8da8b30743f4463fa34c3 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Thu, 7 Sep 2017 10:57:14 +0900 Subject: [PATCH 11/14] client/gtk2: Fix a build failure with GDK_MODIFIER_INTENT_NO_TEXT_INPUT BUG=https://github.com/ibus/ibus/issues/1942 Review URL: https://codereview.appspot.com/327300043 --- client/gtk2/ibusimcontext.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index 3ea46951..a806382d 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -262,9 +262,21 @@ ibus_im_context_commit_event (IBusIMContext *ibusimcontext, for (i = 0; i < G_N_ELEMENTS (IBUS_COMPOSE_IGNORE_KEYLIST); i++) if (event->keyval == IBUS_COMPOSE_IGNORE_KEYLIST[i]) return FALSE; +#if GTK_CHECK_VERSION (3, 4, 0) no_text_input_mask = gdk_keymap_get_modifier_mask ( gdk_keymap_get_for_display (gdk_display_get_default ()), GDK_MODIFIER_INTENT_NO_TEXT_INPUT); +#else +# ifndef GDK_WINDOWING_QUARTZ +# define _IBUS_NO_TEXT_INPUT_MOD_MASK (GDK_MOD1_MASK | GDK_CONTROL_MASK) +# else +# define _IBUS_NO_TEXT_INPUT_MOD_MASK (GDK_MOD2_MASK | GDK_CONTROL_MASK) +# endif + + no_text_input_mask = _IBUS_NO_TEXT_INPUT_MOD_MASK; + +# undef _IBUS_NO_TEXT_INPUT_MOD_MASK +#endif if (event->state & no_text_input_mask || event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || -- 2.13.4 From 0632cbbbb573749bbca96a416fde1490810e52d2 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 13 Sep 2017 18:15:06 +0900 Subject: [PATCH 13/14] ui/gtk3: Fix PropertyPanel position in workarea gdk_screen_get_monitor_workarea() no longer return the correct area from "_NET_WORKAREA" atom in GTK 3.22 and now use gdk_monitor_get_workarea() instead. Use gdk_seat_grab() instead of deprecated gdk_device_grab(). Use gtk_menu_popup_at_rect() instead of deprecated gtk_menu_popup() and generate a new foreign GdkWindow with mouse cursor for the Qt Window. Also fixed some deprecated APIs. R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/330190043 --- ui/gtk3/candidatepanel.vala | 40 ++++++++------- ui/gtk3/emojier.vala | 24 ++++++--- ui/gtk3/handle.vala | 7 +-- ui/gtk3/indicator.vala | 68 ++++++++++++++++++++++++-- ui/gtk3/keybindingmanager.vala | 2 +- ui/gtk3/panel.vala | 108 +++++++++++++++++++++++++++++++---------- ui/gtk3/propertypanel.vala | 21 +++++++- ui/gtk3/switcher.vala | 84 +++++++++++++++++++++++--------- 8 files changed, 270 insertions(+), 84 deletions(-) diff --git a/ui/gtk3/candidatepanel.vala b/ui/gtk3/candidatepanel.vala index 0e5e3bc2..ec2d3db4 100644 --- a/ui/gtk3/candidatepanel.vala +++ b/ui/gtk3/candidatepanel.vala @@ -3,7 +3,7 @@ * ibus - The Input Bus * * Copyright(c) 2011-2015 Peng Huang - * Copyright(c) 2015-2016 Takao Fujiwara + * Copyright(c) 2015-2017 Takao Fujiwara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -292,6 +292,26 @@ public class CandidatePanel : Gtk.Box{ adjust_window_position_vertical(); } + private Gdk.Rectangle get_monitor_geometry() { + Gdk.Rectangle monitor_area = { 0, }; + + // Use get_monitor_geometry() instead of get_monitor_area(). + // get_monitor_area() excludes docks, but the lookup window should be + // shown over them. +#if VALA_0_34 + Gdk.Monitor monitor = Gdk.Display.get_default().get_monitor_at_point( + m_cursor_location.x, + m_cursor_location.y); + monitor_area = monitor.get_geometry(); +#else + Gdk.Screen screen = Gdk.Screen.get_default(); + int monitor_num = screen.get_monitor_at_point(m_cursor_location.x, + m_cursor_location.y); + screen.get_monitor_geometry(monitor_num, out monitor_area); +#endif + return monitor_area; + } + private void adjust_window_position_horizontal() { Gdk.Point cursor_right_bottom = { m_cursor_location.x + m_cursor_location.width, @@ -305,14 +325,7 @@ public class CandidatePanel : Gtk.Box{ cursor_right_bottom.y + allocation.height }; - Gdk.Screen screen = Gdk.Screen.get_default(); - int monitor_num = screen.get_monitor_at_point(m_cursor_location.x, - m_cursor_location.y); - // Use get_monitor_geometry() instead of get_monitor_area(). - // get_monitor_area() excludes docks, but the lookup window should be - // shown over them. - Gdk.Rectangle monitor_area; - screen.get_monitor_geometry(monitor_num, out monitor_area); + Gdk.Rectangle monitor_area = get_monitor_geometry(); int monitor_right = monitor_area.x + monitor_area.width; int monitor_bottom = monitor_area.y + monitor_area.height; @@ -358,14 +371,7 @@ public class CandidatePanel : Gtk.Box{ m_cursor_location.y + allocation.height }; - Gdk.Screen screen = Gdk.Screen.get_default(); - int monitor_num = screen.get_monitor_at_point(m_cursor_location.x, - m_cursor_location.y); - // Use get_monitor_geometry() instead of get_monitor_area(). - // get_monitor_area() excludes docks, but the lookup window should be - // shown over them. - Gdk.Rectangle monitor_area; - screen.get_monitor_geometry(monitor_num, out monitor_area); + Gdk.Rectangle monitor_area = get_monitor_geometry(); int monitor_right = monitor_area.x + monitor_area.width; int monitor_bottom = monitor_area.y + monitor_area.height; diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala index 36ab4bab..9cd98140 100644 --- a/ui/gtk3/emojier.vala +++ b/ui/gtk3/emojier.vala @@ -575,7 +575,7 @@ class IBusEmojier : Gtk.ApplicationWindow { if (lang == "en") { bool has_variant = false; foreach (unichar ch in EMOJI_VARIANT_LIST) { - if (emoji.chr(-1, ch) != null) { + if (emoji.index_of_char(ch) >= 0) { has_variant = true; break; } @@ -782,15 +782,17 @@ class IBusEmojier : Gtk.ApplicationWindow { private bool check_unicode_point() { string annotation = m_entry.get_text(); m_unicode_point = null; - var buff = new GLib.StringBuilder(); + // Add "0x" because uint64.ascii_strtoull() is not accessible + // and need to use uint64.parse() + var buff = new GLib.StringBuilder("0x"); var retval = new GLib.StringBuilder(); for (int i = 0; i < annotation.char_count(); i++) { unichar ch = annotation.get_char(i); if (ch == 0) return false; if (ch.isspace()) { - unichar code = (unichar)buff.str.to_ulong(null, 16); - buff.erase(); + unichar code = (unichar)uint64.parse(buff.str); + buff.assign("0x"); if (!code.validate()) return false; retval.append(code.to_string()); @@ -800,7 +802,7 @@ class IBusEmojier : Gtk.ApplicationWindow { return false; buff.append_unichar(ch); } - unichar code = (unichar)buff.str.to_ulong(null, 16); + unichar code = (unichar)uint64.parse(buff.str); if (!code.validate()) return false; retval.append(code.to_string()); @@ -834,7 +836,7 @@ class IBusEmojier : Gtk.ApplicationWindow { matched = true; break; case 2: - if (key.str(annotation) != null) + if (key.index_of(annotation) >= 0) matched = true; break; default: @@ -1586,10 +1588,16 @@ class IBusEmojier : Gtk.ApplicationWindow { public void present_centralize(Gdk.Event event) { Gtk.Allocation allocation; get_allocation(out allocation); - Gdk.Screen screen = Gdk.Screen.get_default(); - int monitor_num = screen.get_monitor_at_window(get_window()); Gdk.Rectangle monitor_area; +#if VALA_0_34 + Gdk.Display display = Gdk.Display.get_default(); + Gdk.Monitor monitor = display.get_monitor_at_window(this.get_window()); + monitor_area = monitor.get_geometry(); +#else + Gdk.Screen screen = Gdk.Screen.get_default(); + int monitor_num = screen.get_monitor_at_window(this.get_window()); screen.get_monitor_geometry(monitor_num, out monitor_area); +#endif int x = (monitor_area.x + monitor_area.width - allocation.width)/2; int y = (monitor_area.y + monitor_area.height - allocation.height)/2; diff --git a/ui/gtk3/handle.vala b/ui/gtk3/handle.vala index bef5e8ba..fc9164a0 100644 --- a/ui/gtk3/handle.vala +++ b/ui/gtk3/handle.vala @@ -3,7 +3,7 @@ * ibus - The Input Bus * * Copyright(c) 2011-2016 Peng Huang - * Copyright(c) 2016 Takao Fujiwara + * Copyright(c) 2016-2017 Takao Fujiwara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -59,7 +59,6 @@ class Handle : Gtk.EventBox { public override void realize() { base.realize(); - // get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.FLEUR)); } public override bool button_press_event(Gdk.EventButton event) { @@ -138,7 +137,9 @@ class Handle : Gtk.EventBox { m_move_begined = false; m_press_pos.x = 0; m_press_pos.y = 0; - get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR)); + get_window().set_cursor(new Gdk.Cursor.for_display( + Gdk.Display.get_default(), + Gdk.CursorType.FLEUR)); move_end(); return true; } diff --git a/ui/gtk3/indicator.vala b/ui/gtk3/indicator.vala index dac72b49..4d111a64 100644 --- a/ui/gtk3/indicator.vala +++ b/ui/gtk3/indicator.vala @@ -2,7 +2,7 @@ * * ibus - The Input Bus * - * Copyright(c) 2015 Takao Fujiwara + * Copyright(c) 2015-2017 Takao Fujiwara * Copyright(c) 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -97,6 +97,7 @@ class Indicator : IBus.Service private int m_context_menu_y; private int m_activate_menu_x; private int m_activate_menu_y; + private Gdk.Window m_indicator_window; public Indicator(string id, GLib.DBusConnection connection, @@ -206,7 +207,8 @@ class Indicator : IBus.Service GLib.Variant var_y = parameters.get_child_value(1); m_context_menu_x = var_x.get_int32(); m_context_menu_y = var_y.get_int32(); - context_menu(2, 0); + Gdk.Window window = query_gdk_window(); + context_menu(m_context_menu_x, m_context_menu_y, window, 2, 0); } private void _activate_menu_cb(GLib.DBusConnection connection, @@ -216,7 +218,57 @@ class Indicator : IBus.Service GLib.Variant var_y = parameters.get_child_value(1); m_activate_menu_x = var_x.get_int32(); m_activate_menu_y = var_y.get_int32(); - activate(); + Gdk.Window window = query_gdk_window(); + activate(m_activate_menu_x, m_activate_menu_y, window); + } + + private Gdk.Window? query_gdk_window() { + if (m_indicator_window != null) + return m_indicator_window; + + Gdk.Display display = Gdk.Display.get_default(); + unowned X.Display xdisplay = + (display as Gdk.X11.Display).get_xdisplay(); + X.Window current = xdisplay.default_root_window(); + X.Window parent = 0; + X.Window child = 0; + int root_x, root_y, win_x, win_y; + uint mask = 0; + root_x = root_y = win_x = win_y = 0; + bool retval; + // Need XSetErrorHandler for BadWindow? + while ((retval = xdisplay.query_pointer(current, + out parent, out child, + out root_x, out root_y, + out win_x, out win_y, + out mask))) { + if (child == 0) + break; + current = child; + } + if (!retval) { + string format = + "XQueryPointer is failed: current: %x root: %x " + + "child: %x (%d, %d), (%d, %d), %u"; + string message = format.printf((uint)current, + (uint)xdisplay.default_root_window(), + (uint)child, + root_x, root_y, win_x, win_y, + mask); + warning("XQueryPointer is failed: %s", message); + return null; + } + if (current == xdisplay.default_root_window()) + warning("The query window is root window"); + m_indicator_window = Gdk.X11.Window.lookup_for_display( + display as Gdk.X11.Display, + current); + if (m_indicator_window != null) + return m_indicator_window; + m_indicator_window = new Gdk.X11.Window.foreign_for_display( + display as Gdk.X11.Display, + current); + return m_indicator_window; } private GLib.Variant? _get_id(GLib.DBusConnection connection) { @@ -479,7 +531,13 @@ class Indicator : IBus.Service push_in = false; } - public signal void context_menu(uint button, uint activate_time); - public signal void activate(); + public signal void context_menu(int x, + int y, + Gdk.Window window, + uint button, + uint activate_time); + public signal void activate(int x, + int y, + Gdk.Window window); public signal void registered_status_notifier_item(); } diff --git a/ui/gtk3/keybindingmanager.vala b/ui/gtk3/keybindingmanager.vala index 49013b8d..c8b1e7f6 100644 --- a/ui/gtk3/keybindingmanager.vala +++ b/ui/gtk3/keybindingmanager.vala @@ -18,7 +18,7 @@ public class KeybindingManager : GLib.Object { private static KeybindingManager m_instance = null; - public static const uint MODIFIER_FILTER = + public const uint MODIFIER_FILTER = Gdk.ModifierType.MODIFIER_MASK & ~( Gdk.ModifierType.LOCK_MASK | // Caps Lock // Gdk.ModifierType.MOD1_MASK | // Alt diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala index bf43cbf9..629dadce 100644 --- a/ui/gtk3/panel.vala +++ b/ui/gtk3/panel.vala @@ -267,6 +267,27 @@ class Panel : IBus.PanelService { }); } + private void popup_menu_at_area_window(Gtk.Menu menu, + Gdk.Rectangle area, + Gdk.Window? window, + Gtk.MenuPositionFunc? func) { +#if VALA_0_34 + Gdk.Gravity rect_anchor = Gdk.Gravity.SOUTH_WEST; + Gdk.Gravity menu_anchor = Gdk.Gravity.NORTH_WEST; + + // Gtk.Menu.popup() is now deprecated but + // Gtk.Menu.popup_at_rect() requires a Gdk.Window and + // Gtk.Menu.popup_at_rect() outputs a warning of + // "no trigger event for menu popup" + // for the foreigner QT window which is generated by + // Gdk.X11.Window.foreign_for_display. + // https://git.gnome.org/browse/gtk+/tree/gtk/gtkmenu.c?h=gtk-3-22#n2251 + menu.popup_at_rect(window, area, rect_anchor, menu_anchor, null); +#else + menu.popup(null, null, func, 0, Gtk.get_current_event_time()); +#endif + } + #if INDICATOR private bool is_kde() { if (Environment.get_variable("XDG_CURRENT_DESKTOP") == "KDE") @@ -276,6 +297,19 @@ class Panel : IBus.PanelService { return false; } + private void popup_menu_at_pointer_window(Gtk.Menu menu, + int x, + int y, + Gdk.Window? window, + Gtk.MenuPositionFunc? func) { + int win_x = 0; + int win_y = 0; + window.get_origin(out win_x, out win_y); + Gdk.Rectangle area = { x - win_x, y - win_y, 1, 1 }; + // window is a bottom wide panel instead of status icon + popup_menu_at_area_window(menu, area, window, func); + } + private void init_indicator() { m_icon_type = IconType.INDICATOR; GLib.Bus.get.begin(GLib.BusType.SESSION, null, (obj, res) => { @@ -290,21 +324,17 @@ class Panel : IBus.PanelService { m_indicator.set_status(Indicator.Status.ACTIVE); state_changed(); }); - m_indicator.context_menu.connect((b, t) => { - Gtk.Menu menu = create_context_menu(); - menu.popup(null, - null, - m_indicator.position_context_menu, - 0, - Gtk.get_current_event_time()); + m_indicator.context_menu.connect((x, y, w, b, t) => { + popup_menu_at_pointer_window( + create_context_menu(), + x, y, w, + m_indicator.position_context_menu); }); - m_indicator.activate.connect(() => { - Gtk.Menu menu = create_activate_menu(); - menu.popup(null, - null, - m_indicator.position_activate_menu, - 0, - Gtk.get_current_event_time()); + m_indicator.activate.connect((x, y, w) => { + popup_menu_at_pointer_window( + create_activate_menu(), + x, y, w, + m_indicator.position_activate_menu); }); } catch (GLib.IOError e) { warning("Failed to get the session bus: %s", e.message); @@ -317,21 +347,47 @@ class Panel : IBus.PanelService { m_status_icon = new Gtk.StatusIcon(); m_status_icon.set_name("ibus-ui-gtk"); m_status_icon.set_title(_("IBus Panel")); + + // Gdk.Window.get_width() is needed for the menu position + if (m_status_icon.get_size() > 0) + init_status_icon_menu(); + else + m_status_icon.notify["size"].connect(init_status_icon_menu); + } + + private void init_status_icon_menu() { + Gdk.Rectangle area = { 0, 0, 0, 0 }; + Gdk.Window? window = null; + Gtk.MenuPositionFunc? func = null; +#if VALA_0_34 + window = Gdk.X11.Window.lookup_for_display( + Gdk.Display.get_default() as Gdk.X11.Display, + m_status_icon.get_x11_window_id()) as Gdk.Window; + if (window == null) { + warning("StatusIcon does not have GdkWindow"); + return; + } + Gtk.Orientation orient; + m_status_icon.get_geometry(null, out area, out orient); + int win_x = 0; + int win_y = 0; + window.get_origin(out win_x, out win_y); + // The (x, y) is converted by gdk_window_get_root_coords() + // in gdk_window_impl_move_to_rect() + area.x -= win_x; + area.y -= win_y; +#else + func = m_status_icon.position_menu; +#endif m_status_icon.popup_menu.connect((b, t) => { - Gtk.Menu menu = create_context_menu(); - menu.popup(null, - null, - m_status_icon.position_menu, - 0, - Gtk.get_current_event_time()); + popup_menu_at_area_window( + create_context_menu(), + area, window, func); }); m_status_icon.activate.connect(() => { - Gtk.Menu menu = create_activate_menu(); - menu.popup(null, - null, - m_status_icon.position_menu, - 0, - Gtk.get_current_event_time()); + popup_menu_at_area_window( + create_activate_menu(), + area, window, func); }); m_status_icon.set_from_icon_name("ibus-keyboard"); } diff --git a/ui/gtk3/propertypanel.vala b/ui/gtk3/propertypanel.vala index dd4342ec..857f8e20 100644 --- a/ui/gtk3/propertypanel.vala +++ b/ui/gtk3/propertypanel.vala @@ -4,7 +4,7 @@ * * Copyright(c) 2013-2016 Red Hat, Inc. * Copyright(c) 2013-2015 Peng Huang - * Copyright(c) 2013-2016 Takao Fujiwara + * Copyright(c) 2013-2017 Takao Fujiwara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -330,8 +330,16 @@ public class PropertyPanel : Gtk.Box { Gtk.Allocation allocation; m_toplevel.get_allocation(out allocation); + Gdk.Rectangle monitor_area; +#if VALA_0_34 + // gdk_screen_get_monitor_workarea() no longer return the correct + // area from "_NET_WORKAREA" atom in GTK 3.22 + Gdk.Monitor monitor = Gdk.Display.get_default().get_monitor(0); + monitor_area = monitor.get_workarea(); +#else Gdk.Screen screen = Gdk.Screen.get_default(); - Gdk.Rectangle monitor_area = screen.get_monitor_workarea(0); + monitor_area = screen.get_monitor_workarea(0); +#endif int monitor_right = monitor_area.x + monitor_area.width; int monitor_bottom = monitor_area.y + monitor_area.height; int x, y; @@ -472,8 +480,15 @@ public class PropMenu : Gtk.Menu, IPropToolItem { public new void popup(uint button, uint32 activate_time, Gtk.Widget widget) { +#if VALA_0_34 + base.popup_at_widget(widget, + Gdk.Gravity.SOUTH_WEST, + Gdk.Gravity.NORTH_WEST, + null); +#else m_parent_button = widget; base.popup(null, null, menu_position, button, activate_time); +#endif } public override void destroy() { @@ -532,6 +547,7 @@ public class PropMenu : Gtk.Menu, IPropToolItem { } } +#if !VALA_0_34 private void menu_position(Gtk.Menu menu, out int x, out int y, @@ -580,6 +596,7 @@ public class PropMenu : Gtk.Menu, IPropToolItem { push_in = false; } +#endif } public class PropToolButton : Gtk.ToolButton, IPropToolItem { diff --git a/ui/gtk3/switcher.vala b/ui/gtk3/switcher.vala index 269a68d4..0ce742a1 100644 --- a/ui/gtk3/switcher.vala +++ b/ui/gtk3/switcher.vala @@ -157,6 +157,55 @@ class Switcher : Gtk.Window { m_label.set_text(m_buttons[index].longname); m_buttons[index].grab_focus(); + // Avoid regressions. + if (m_popup_delay_time > 0) { + get_position(out m_root_x, out m_root_y); + // Pull the window from the screen so that the window gets + // the key press and release events but mouse does not select + // an IME unexpectedly. + move(-1000, -1000); + } + + show_all(); + + if (m_popup_delay_time > 0) { + // Restore the window position after m_popup_delay_time + m_popup_delay_time_id = GLib.Timeout.add(m_popup_delay_time, + () => { + restore_window_position("timeout"); + return false; + }); + } + + Gdk.Device pointer; +#if VALA_0_34 + Gdk.Seat seat = event.get_seat(); + if (seat == null) { + var display = get_display(); + seat = display.get_default_seat(); + } + //keyboard = seat.get_keyboard(); + pointer = seat.get_pointer(); + + Gdk.GrabStatus status; + // Grab all keyboard events + status = seat.grab(get_window(), + Gdk.SeatCapabilities.KEYBOARD, + true, + null, + event, + null); + if (status != Gdk.GrabStatus.SUCCESS) + warning("Grab keyboard failed! status = %d", status); + status = seat.grab(get_window(), + Gdk.SeatCapabilities.POINTER, + true, + null, + event, + null); + if (status != Gdk.GrabStatus.SUCCESS) + warning("Grab pointer failed! status = %d", status); +#else Gdk.Device device = event.get_device(); if (device == null) { var display = get_display(); @@ -174,7 +223,6 @@ class Switcher : Gtk.Window { } Gdk.Device keyboard; - Gdk.Device pointer; if (device.get_source() == Gdk.InputSource.KEYBOARD) { keyboard = device; pointer = device.get_associated_device(); @@ -183,26 +231,6 @@ class Switcher : Gtk.Window { keyboard = device.get_associated_device(); } - // Avoid regressions. - if (m_popup_delay_time > 0) { - get_position(out m_root_x, out m_root_y); - // Pull the window from the screen so that the window gets - // the key press and release events but mouse does not select - // an IME unexpectedly. - move(-1000, -1000); - } - - show_all(); - - if (m_popup_delay_time > 0) { - // Restore the window position after m_popup_delay_time - m_popup_delay_time_id = GLib.Timeout.add(m_popup_delay_time, - () => { - restore_window_position("timeout"); - return false; - }); - } - Gdk.GrabStatus status; // Grab all keyboard events status = keyboard.grab(get_window(), @@ -224,6 +252,8 @@ class Switcher : Gtk.Window { Gdk.CURRENT_TIME); if (status != Gdk.GrabStatus.SUCCESS) warning("Grab pointer failed! status = %d", status); +#endif + // Probably we can delete m_popup_delay_time in 1.6 pointer.get_position_double(null, out m_mouse_init_x, @@ -235,8 +265,12 @@ class Switcher : Gtk.Window { m_loop.run(); m_loop = null; +#if VALA_0_34 + seat.ungrab(); +#else keyboard.ungrab(Gdk.CURRENT_TIME); pointer.ungrab(Gdk.CURRENT_TIME); +#endif hide(); // Make sure the switcher is hidden before returning from this function. @@ -319,13 +353,19 @@ class Switcher : Gtk.Window { m_label.set_ellipsize(Pango.EllipsizeMode.END); Gdk.Display display = Gdk.Display.get_default(); + int screen_width = 0; +#if VALA_0_34 + Gdk.Monitor monitor = display.get_monitor_at_window(this.get_window()); + Gdk.Rectangle area = monitor.get_geometry(); + screen_width = area.width; +#else Gdk.Screen screen = (display != null) ? display.get_default_screen() : null; - int screen_width = 0; if (screen != null) { screen_width = screen.get_width(); } +#endif if (screen_width > 0 && max_label_width > (screen_width / 4)) { max_label_width = screen_width / 4; -- 2.13.4 From a7e78022b95329ca5782512872398a365503c410 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Thu, 14 Sep 2017 18:07:42 +0900 Subject: [PATCH 14/14] ui/gtk3: Fix to enable menu button on PropertyPanel --- ui/gtk3/propertypanel.vala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ui/gtk3/propertypanel.vala b/ui/gtk3/propertypanel.vala index 857f8e20..f5d9cff7 100644 --- a/ui/gtk3/propertypanel.vala +++ b/ui/gtk3/propertypanel.vala @@ -84,6 +84,23 @@ public class PropertyPanel : Gtk.Box { public void set_properties(IBus.PropList props) { debug("set_properties()\n"); + // When click PropMenuToolButton, the focus is changed and + // set_properties() is called here while the menu button is active. + // Ignore that case here not to remove items. + bool has_active = false; + foreach (var item in m_items) { + Type type = item.get_type(); + if (type == typeof(PropMenuToolButton) || + type == typeof(PropToggleToolButton)) { + if ((item as Gtk.ToggleToolButton).get_active()) { + has_active = true; + break; + } + } + } + if (has_active) + return; + foreach (var item in m_items) remove((item as Gtk.Widget)); m_items = {}; -- 2.13.4