From d5277646dbe9a88d32c42187c9c3701a7c57a469 Mon Sep 17 00:00:00 2001 From: Mike Gabriel Date: Thu, 26 Oct 2017 19:17:41 +0200 Subject: Fork from Ubuntu's indicator-keyboard. --- .bzrignore | 47 - AUTHORS | 1 + AUTHORS.Canonical | 1 + MERGE-REVIEW | 17 - Makefile.am | 2 +- README | 10 +- autogen.sh | 1 + configure.ac | 5 +- data/Makefile.am | 8 +- data/ayatana-indicator-keyboard.conf.in | 9 + data/ayatana-indicator-keyboard.desktop.in | 8 + data/icon-generator.vala | 241 ++++ data/indicator-keyboard.conf.in | 9 - data/indicator-keyboard.desktop.in | 9 - data/main.vala | 241 ---- data/upstart/Makefile.am | 4 +- data/upstart/ayatana-indicator-keyboard.desktop.in | 9 + data/upstart/indicator-keyboard.desktop.in | 9 - debian/changelog | 5 +- debian/control | 26 +- debian/copyright | 10 +- debian/rules | 8 +- lib/Makefile.am | 55 - lib/common.vala | 37 - lib/ibus-menu.vala | 313 ----- lib/ibus-panel.vala | 26 - lib/indicator-menu.vala | 151 --- lib/keyboard-plugin.vala | 23 - lib/main.vala | 1265 -------------------- lib/source.vala | 487 -------- lib/unity-greeter.vala | 26 - lib/unity-session.vala | 24 - lib/window-stack.vala | 37 - src/Makefile.am | 55 + src/common.vala | 37 + src/ibus-menu.vala | 313 +++++ src/ibus-panel.vala | 26 + src/indicator-menu.vala | 151 +++ src/keyboard-plugin.vala | 23 + src/main.vala | 1265 ++++++++++++++++++++ src/source.vala | 487 ++++++++ src/unity-greeter.vala | 26 + src/unity-session.vala | 24 + src/window-stack.vala | 37 + 44 files changed, 2757 insertions(+), 2811 deletions(-) delete mode 100644 .bzrignore create mode 100644 AUTHORS.Canonical delete mode 100644 MERGE-REVIEW create mode 100644 data/ayatana-indicator-keyboard.conf.in create mode 100644 data/ayatana-indicator-keyboard.desktop.in create mode 100644 data/icon-generator.vala delete mode 100644 data/indicator-keyboard.conf.in delete mode 100644 data/indicator-keyboard.desktop.in delete mode 100644 data/main.vala create mode 100644 data/upstart/ayatana-indicator-keyboard.desktop.in delete mode 100644 data/upstart/indicator-keyboard.desktop.in delete mode 100644 lib/Makefile.am delete mode 100644 lib/common.vala delete mode 100644 lib/ibus-menu.vala delete mode 100644 lib/ibus-panel.vala delete mode 100644 lib/indicator-menu.vala delete mode 100644 lib/keyboard-plugin.vala delete mode 100644 lib/main.vala delete mode 100644 lib/source.vala delete mode 100644 lib/unity-greeter.vala delete mode 100644 lib/unity-session.vala delete mode 100644 lib/window-stack.vala create mode 100644 src/Makefile.am create mode 100644 src/common.vala create mode 100644 src/ibus-menu.vala create mode 100644 src/ibus-panel.vala create mode 100644 src/indicator-menu.vala create mode 100644 src/keyboard-plugin.vala create mode 100644 src/main.vala create mode 100644 src/source.vala create mode 100644 src/unity-greeter.vala create mode 100644 src/unity-session.vala create mode 100644 src/window-stack.vala diff --git a/.bzrignore b/.bzrignore deleted file mode 100644 index 434bf43c..00000000 --- a/.bzrignore +++ /dev/null @@ -1,47 +0,0 @@ -*.c -*.log -*.pyc -*.stamp -*.stamp-t -*.substvars -*.valid -.deps -.libs -.timestamp -Makefile -Makefile.in -aclocal.m4 -autom4te.cache -build-aux -config.log -config.status -configure -data/org.ayatana.indicator.keyboard -data/gschemas.compiled -data/indicator-keyboard-icon-generator -data/indicator-keyboard.conf -data/indicator-keyboard.desktop -data/indicator-keyboard.service -data/upstart/indicator-keyboard.desktop -debian/autoreconf.after -debian/autoreconf.before -debian/files -debian/indicator-keyboard -debian/tmp -lib/indicator-keyboard-service -libtool -m4/intltool.m4 -m4/libtool.m4 -m4/ltoptions.m4 -m4/ltsugar.m4 -m4/ltversion.m4 -m4/lt~obsolete.m4 -po/POTFILES -po/stamp-it -tests/config.vala -tests/dconf-service -tests/gvfs -tests/indicator-keyboard-test -tests/indicator-keyboard-test.trs -tests/indicator-keyboard-tests -tests/services/indicator-keyboard.service diff --git a/AUTHORS b/AUTHORS index b3718204..62ba656d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ +Mike Gabriel William Hua diff --git a/AUTHORS.Canonical b/AUTHORS.Canonical new file mode 100644 index 00000000..b3718204 --- /dev/null +++ b/AUTHORS.Canonical @@ -0,0 +1 @@ +William Hua diff --git a/MERGE-REVIEW b/MERGE-REVIEW deleted file mode 100644 index d77c0f10..00000000 --- a/MERGE-REVIEW +++ /dev/null @@ -1,17 +0,0 @@ - -This documents the expections that the project has on what both submitters -and reviewers should ensure that they've done for a merge into the project. - -== Submitter Responsibilities == - - * Ensure the project compiles and the test suite executes without error - * Ensure that non-obvious code has comments explaining it - * If the change works on specific profiles, please include those in the merge description. - -== Reviewer Responsibilities == - - * Did the Jenkins build compile? Pass? Run unit tests successfully? - * Are there appropriate tests to cover any new functionality? - * If the description says this effects the desktop profile: - * Run tests indicator-keyboard/unity7* - diff --git a/Makefile.am b/Makefile.am index 94cd26da..bc9f0c5b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = po lib data tests +SUBDIRS = po src data tests include $(top_srcdir)/Makefile.am.coverage diff --git a/README b/README index 2928a41c..bb33d3c6 100644 --- a/README +++ b/README @@ -1,11 +1,11 @@ -indicator-keyboard-icon-generator -================================= +ayatana-indicator-keyboard-icon-generator +========================================== To refresh the keyboard layout icons, I usually enter the data directory and run: -./indicator-keyboard-icon-generator -i icon.svg.in -I icon-1.svg.in -o dark -c '#dfdbd2' -./indicator-keyboard-icon-generator -i icon.svg.in -I icon-1.svg.in -o light -c '#3c3c3c' +./ayatana-indicator-keyboard-icon-generator -i icon.svg.in -I icon-1.svg.in -o dark -c '#dfdbd2' +./ayatana-indicator-keyboard-icon-generator -i icon.svg.in -I icon-1.svg.in -o light -c '#3c3c3c' ('#dfdbd2' and '#3c3c3c' are the colours for the dark and light themes.) Then I just copy the files in the dark and light directories to @@ -14,4 +14,4 @@ source package. data/icon.svg.in and data/icon-1.svg.in are template files for the un-subscripted and subscripted icons. Parameters can be tweaked on the command -line for the generator: './indicator-keyboard-icon-generator -?' for details. +line for the generator: './ayatana-indicator-keyboard-icon-generator -h' for details. diff --git a/autogen.sh b/autogen.sh index 1d70f697..4498c240 100755 --- a/autogen.sh +++ b/autogen.sh @@ -3,4 +3,5 @@ mkdir -p m4 autoreconf -i intltoolize -c -f + test -n "$NOCONFIGURE" || ./configure "$@" diff --git a/configure.ac b/configure.ac index 058c84dd..0b9ccfb7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([ayatana-indicator-keyboard], [0.1.0]) +AC_INIT([ayatana-indicator-keyboard], [0.1.1]) AC_REVISION([0.1.0]) AC_CONFIG_SRCDIR([configure.ac]) @@ -39,7 +39,6 @@ PKG_CHECK_MODULES([GIO], [gio-2.0]) PKG_CHECK_MODULES([GTK], [gtk+-3.0]) PKG_CHECK_MODULES([PANGOFT2], [pangoft2]) PKG_CHECK_MODULES([PANGOCAIRO], [pangocairo]) -PKG_CHECK_MODULES([GNOME_DESKTOP], [gnome-desktop-3.0]) PKG_CHECK_MODULES([LIBXKLAVIER], [libxklavier]) PKG_CHECK_MODULES([LIBGNOMEKBD], [libgnomekbdui]) PKG_CHECK_MODULES([IBUS], [ibus-1.0]) @@ -59,7 +58,7 @@ AC_SUBST(COVERAGE_LDFLAGS) AC_CONFIG_FILES([Makefile data/Makefile data/upstart/Makefile - lib/Makefile + src/Makefile po/Makefile.in tests/Makefile tests/autopilot/Makefile diff --git a/data/Makefile.am b/data/Makefile.am index bb9a1f82..6f33aeaf 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -13,7 +13,7 @@ AM_VALAFLAGS = --enable-experimental-non-null \ --metadatadir $(top_srcdir)/deps \ --vapidir $(top_srcdir)/deps -ayatana_indicator_keyboard_icon_generator_SOURCES = main.vala \ +ayatana_indicator_keyboard_icon_generator_SOURCES = icon-generator.vala \ $(top_srcdir)/lib/common.vala ayatana_indicator_keyboard_icon_generator_VALAFLAGS = $(AM_VALAFLAGS) \ --pkg gee-1.0 \ @@ -68,7 +68,7 @@ indicatordir = $(INDICATOR_DIR) org.ayatana.indicator.keyboard: $(AM_V_GEN) (echo '[Indicator Service]'; \ - echo 'Name=indicator-keyboard'; \ + echo 'Name=ayatana-indicator-keyboard'; \ echo 'ObjectPath=/org/ayatana/indicator/keyboard'; \ echo 'Position=80'; \ echo ''; \ @@ -101,6 +101,6 @@ gschemas.compiled: $(gsettings_SCHEMAS) EXTRA_DIST = $(dist_service_DATA) \ $(dist_indicator_DATA) \ $(gsettings_SCHEMAS) \ - indicator-keyboard.conf.in \ - indicator-keyboard.desktop.in + ayatana-indicator-keyboard.conf.in \ + ayatana-indicator-keyboard.desktop.in diff --git a/data/ayatana-indicator-keyboard.conf.in b/data/ayatana-indicator-keyboard.conf.in new file mode 100644 index 00000000..5083a485 --- /dev/null +++ b/data/ayatana-indicator-keyboard.conf.in @@ -0,0 +1,9 @@ +description "Ayatana Indicator Keyboard Backend" + +start on ayatana-indicator-services-start +stop on desktop-end or ayatana-indicator-services-end + +respawn +respawn limit 2 10 + +exec @pkglibexecdir@/ayatana-indicator-keyboard-service --use-gtk diff --git a/data/ayatana-indicator-keyboard.desktop.in b/data/ayatana-indicator-keyboard.desktop.in new file mode 100644 index 00000000..b31bec51 --- /dev/null +++ b/data/ayatana-indicator-keyboard.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Ayatana Indicator Keyboard +Exec=@pkglibexecdir@/ayatana-indicator-keyboard-service +OnlyShowIn=MATE; +NoDisplay=true +StartupNotify=false +Terminal=false diff --git a/data/icon-generator.vala b/data/icon-generator.vala new file mode 100644 index 00000000..8077f093 --- /dev/null +++ b/data/icon-generator.vala @@ -0,0 +1,241 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +int main (string[] args) { + var force = false; + var width = 22.0; + var height = 22.0; + var icon_width = 20.0; + var icon_height = 20.0; + var radius = 2.0; + var colour = "black"; + var font = "Ubuntu"; + var weight = 500; + var layout_size = 12; + var subscript_size = 8; + string? output_path = null; + string? no_subscript_path = null; + string? with_subscript_path = null; + + OptionEntry[] options = new OptionEntry[15]; + options[0] = { "force", 'f', 0, OptionArg.NONE, ref force, "Overwrite existing files" }; + options[1] = { "width", 'w', 0, OptionArg.DOUBLE, ref width, "Template width", "DOUBLE" }; + options[2] = { "height", 'h', 0, OptionArg.DOUBLE, ref height, "Template height", "DOUBLE" }; + options[3] = { "icon-width", 'W', 0, OptionArg.DOUBLE, ref icon_width, "Icon width", "DOUBLE" }; + options[4] = { "icon-height", 'H', 0, OptionArg.DOUBLE, ref icon_height, "Icon height", "DOUBLE" }; + options[5] = { "radius", 'r', 0, OptionArg.DOUBLE, ref radius, "Icon radius", "DOUBLE" }; + options[6] = { "colour", 'c', 0, OptionArg.STRING, ref colour, "Icon colour", "COLOUR" }; + options[7] = { "font", 'F', 0, OptionArg.STRING, ref font, "Font family", "NAME" }; + options[8] = { "weight", 'G', 0, OptionArg.INT, ref weight, "Font weight (100 to 1000)", "INT" }; + options[9] = { "layout-size", 's', 0, OptionArg.INT, ref layout_size, "Layout font size", "INT" }; + options[10] = { "subscript-size", 'S', 0, OptionArg.INT, ref subscript_size, "Subscript font size", "INT" }; + options[11] = { "output", 'o', 0, OptionArg.FILENAME, ref output_path, "Output directory", "PATH" }; + options[12] = { "no-subscript", 'i', 0, OptionArg.FILENAME, ref no_subscript_path, "Icon template", "PATH" }; + options[13] = { "with-subscript", 'I', 0, OptionArg.FILENAME, ref with_subscript_path, "Subscript icon template", "PATH" }; + options[14] = { }; + + try { + var context = new OptionContext ("- generate keyboard layout icons"); + context.add_main_entries (options, null); + context.set_help_enabled (true); + context.parse (ref args); + } catch (OptionError error) { + GLib.error ("error: %s", error.message); + } + + if (no_subscript_path == null && with_subscript_path == null) { + error ("error: No icon template"); + } else if (no_subscript_path == null) { + no_subscript_path = with_subscript_path; + } else if (with_subscript_path == null) { + with_subscript_path = no_subscript_path; + } + + if (output_path != null) { + var file = File.new_for_path ((!) output_path); + + if (!file.query_exists (null)) { + try { + file.make_directory_with_parents (null); + } catch (Error error) { + GLib.error ("error: %s", error.message); + } + } + } else { + output_path = "."; + } + + Gtk.init (ref args); + + var info = new Gnome.XkbInfo (); + var layouts = info.get_all_layouts (); + var occurrences = new Gee.HashMap (); + + layouts.foreach ((name) => { + string? short_name; + + info.get_layout_info (name, null, out short_name, null, null); + + var abbreviation = abbreviate (short_name); + var has_abbreviation = abbreviation != null && ((!) abbreviation).get_char () != '\0'; + + if (has_abbreviation) { + if (!occurrences.has_key ((!) abbreviation)) { + occurrences[(!) abbreviation] = 1; + } else { + occurrences[(!) abbreviation] = occurrences[(!) abbreviation] + 1; + } + } + }); + + string no_subscript_data; + string with_subscript_data; + + try { + uint8[] contents; + + var icon_x = 0.5 * (width - icon_width); + var icon_y = 0.5 * (height - icon_height); + var layout_font = @"font-family:$font;font-weight:$weight;font-size:$layout_size"; + var subscript_font = @"font-family:$font;font-weight:$weight;font-size:$subscript_size"; + + File.new_for_path ((!) no_subscript_path).load_contents (null, out contents, null); + no_subscript_data = (string) contents; + no_subscript_data = no_subscript_data.replace ("@WIDTH@", @"$width"); + no_subscript_data = no_subscript_data.replace ("@HEIGHT@", @"$height"); + no_subscript_data = no_subscript_data.replace ("@ICON_X@", @"$icon_x"); + no_subscript_data = no_subscript_data.replace ("@ICON_Y@", @"$icon_y"); + no_subscript_data = no_subscript_data.replace ("@ICON_WIDTH@", @"$icon_width"); + no_subscript_data = no_subscript_data.replace ("@ICON_HEIGHT@", @"$icon_height"); + no_subscript_data = no_subscript_data.replace ("@RADIUS@", @"$radius"); + no_subscript_data = no_subscript_data.replace ("@COLOUR@", colour); + no_subscript_data = no_subscript_data.replace ("@LAYOUT_FONT@", layout_font); + no_subscript_data = no_subscript_data.replace ("@SUBSCRIPT_FONT@", subscript_font); + + File.new_for_path ((!) with_subscript_path).load_contents (null, out contents, null); + with_subscript_data = (string) contents; + with_subscript_data = with_subscript_data.replace ("@WIDTH@", @"$width"); + with_subscript_data = with_subscript_data.replace ("@HEIGHT@", @"$height"); + with_subscript_data = with_subscript_data.replace ("@ICON_X@", @"$icon_x"); + with_subscript_data = with_subscript_data.replace ("@ICON_Y@", @"$icon_y"); + with_subscript_data = with_subscript_data.replace ("@ICON_WIDTH@", @"$icon_width"); + with_subscript_data = with_subscript_data.replace ("@ICON_HEIGHT@", @"$icon_height"); + with_subscript_data = with_subscript_data.replace ("@RADIUS@", @"$radius"); + with_subscript_data = with_subscript_data.replace ("@COLOUR@", colour); + with_subscript_data = with_subscript_data.replace ("@LAYOUT_FONT@", layout_font); + with_subscript_data = with_subscript_data.replace ("@SUBSCRIPT_FONT@", subscript_font); + } catch (Error error) { + GLib.error ("error: %s", error.message); + } + + var font_map = new PangoFT2.FontMap (); + var layout_layout = new Pango.Layout (font_map.create_context ()); + var subscript_layout = new Pango.Layout (font_map.create_context ()); + + var font_description = new Pango.FontDescription (); + font_description.set_family (font); + font_description.set_weight ((Pango.Weight) weight); + font_description.set_size (layout_size * Pango.SCALE); + layout_layout.set_font_description (font_description); + + font_description = new Pango.FontDescription (); + font_description.set_family (font); + font_description.set_weight ((Pango.Weight) weight); + font_description.set_size (subscript_size * Pango.SCALE); + subscript_layout.set_font_description (font_description); + + foreach (var entry in occurrences.entries) { + var layout = entry.key; + var count = entry.value; + var file = File.new_for_path (@"$((!) output_path)/indicator-keyboard-$layout.svg"); + + if (force || !file.query_exists (null)) { + int layout_width; + int layout_height; + + layout_layout.set_text (layout, -1); + layout_layout.get_size (out layout_width, out layout_height); + var layout_baseline = layout_layout.get_baseline (); + + var layout_x = 0.5 * (width - 1.0 * layout_width / Pango.SCALE); + var layout_y = 0.5 * (height - 1.0 * layout_height / Pango.SCALE) + 1.0 * layout_baseline / Pango.SCALE; + + var output_data = no_subscript_data; + output_data = output_data.replace ("@LAYOUT@", layout); + output_data = output_data.replace ("@LAYOUT_X@", @"$layout_x"); + output_data = output_data.replace ("@LAYOUT_Y@", @"$layout_y"); + output_data = output_data.replace ("@SUBSCRIPT@", ""); + output_data = output_data.replace ("@SUBSCRIPT_X@", "0"); + output_data = output_data.replace ("@SUBSCRIPT_Y@", "0"); + + try { + file.replace_contents (output_data.data, null, false, FileCreateFlags.REPLACE_DESTINATION, null, null); + } catch (Error error) { + GLib.error ("error: %s", error.message); + } + } + + if (count > 1) { + int layout_width; + int layout_height; + + layout_layout.set_text (layout, -1); + layout_layout.get_size (out layout_width, out layout_height); + var layout_baseline = layout_layout.get_baseline (); + + var layout_y = 0.5 * (height - 1.0 * layout_height / Pango.SCALE) + 1.0 * layout_baseline / Pango.SCALE; + + var partial_data = with_subscript_data; + partial_data = partial_data.replace ("@LAYOUT@", layout); + partial_data = partial_data.replace ("@LAYOUT_Y@", @"$layout_y"); + + for (var i = 1; i <= count; i++) { + file = File.new_for_path (@"$((!) output_path)/indicator-keyboard-$layout-$i.svg"); + + if (force || !file.query_exists (null)) { + var subscript = @"$i"; + int subscript_width; + int subscript_height; + + subscript_layout.set_text (subscript, -1); + subscript_layout.get_size (out subscript_width, out subscript_height); + var subscript_baseline = subscript_layout.get_baseline (); + + var layout_x = 0.5 * (width - 1.0 * (layout_width + subscript_width) / Pango.SCALE); + var subscript_x = layout_x + 1.0 * layout_width / Pango.SCALE; + var subscript_y = layout_y - 0.5 * subscript_height / Pango.SCALE + 1.0 * subscript_baseline / Pango.SCALE; + + var output_data = partial_data; + output_data = output_data.replace ("@LAYOUT_X@", @"$layout_x"); + output_data = output_data.replace ("@LAYOUT_Y@", @"$layout_y"); + output_data = output_data.replace ("@SUBSCRIPT@", subscript); + output_data = output_data.replace ("@SUBSCRIPT_X@", @"$subscript_x"); + output_data = output_data.replace ("@SUBSCRIPT_Y@", @"$subscript_y"); + + try { + file.replace_contents (output_data.data, null, false, FileCreateFlags.REPLACE_DESTINATION, null, null); + } catch (Error error) { + GLib.error ("error: %s", error.message); + } + } + } + } + } + + return 0; +} diff --git a/data/indicator-keyboard.conf.in b/data/indicator-keyboard.conf.in deleted file mode 100644 index 737a4830..00000000 --- a/data/indicator-keyboard.conf.in +++ /dev/null @@ -1,9 +0,0 @@ -description "Indicator Keyboard Backend" - -start on indicator-services-start -stop on desktop-end or indicator-services-end - -respawn -respawn limit 2 10 - -exec @pkglibexecdir@/indicator-keyboard-service --use-gtk diff --git a/data/indicator-keyboard.desktop.in b/data/indicator-keyboard.desktop.in deleted file mode 100644 index 3fca71fb..00000000 --- a/data/indicator-keyboard.desktop.in +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Type=Application -Name=Indicator Keyboard -Exec=@pkglibexecdir@/indicator-keyboard-service -OnlyShowIn=Unity;GNOME; -NoDisplay=true -StartupNotify=false -Terminal=false -AutostartCondition=GNOME3 unless-session gnome diff --git a/data/main.vala b/data/main.vala deleted file mode 100644 index 8077f093..00000000 --- a/data/main.vala +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -int main (string[] args) { - var force = false; - var width = 22.0; - var height = 22.0; - var icon_width = 20.0; - var icon_height = 20.0; - var radius = 2.0; - var colour = "black"; - var font = "Ubuntu"; - var weight = 500; - var layout_size = 12; - var subscript_size = 8; - string? output_path = null; - string? no_subscript_path = null; - string? with_subscript_path = null; - - OptionEntry[] options = new OptionEntry[15]; - options[0] = { "force", 'f', 0, OptionArg.NONE, ref force, "Overwrite existing files" }; - options[1] = { "width", 'w', 0, OptionArg.DOUBLE, ref width, "Template width", "DOUBLE" }; - options[2] = { "height", 'h', 0, OptionArg.DOUBLE, ref height, "Template height", "DOUBLE" }; - options[3] = { "icon-width", 'W', 0, OptionArg.DOUBLE, ref icon_width, "Icon width", "DOUBLE" }; - options[4] = { "icon-height", 'H', 0, OptionArg.DOUBLE, ref icon_height, "Icon height", "DOUBLE" }; - options[5] = { "radius", 'r', 0, OptionArg.DOUBLE, ref radius, "Icon radius", "DOUBLE" }; - options[6] = { "colour", 'c', 0, OptionArg.STRING, ref colour, "Icon colour", "COLOUR" }; - options[7] = { "font", 'F', 0, OptionArg.STRING, ref font, "Font family", "NAME" }; - options[8] = { "weight", 'G', 0, OptionArg.INT, ref weight, "Font weight (100 to 1000)", "INT" }; - options[9] = { "layout-size", 's', 0, OptionArg.INT, ref layout_size, "Layout font size", "INT" }; - options[10] = { "subscript-size", 'S', 0, OptionArg.INT, ref subscript_size, "Subscript font size", "INT" }; - options[11] = { "output", 'o', 0, OptionArg.FILENAME, ref output_path, "Output directory", "PATH" }; - options[12] = { "no-subscript", 'i', 0, OptionArg.FILENAME, ref no_subscript_path, "Icon template", "PATH" }; - options[13] = { "with-subscript", 'I', 0, OptionArg.FILENAME, ref with_subscript_path, "Subscript icon template", "PATH" }; - options[14] = { }; - - try { - var context = new OptionContext ("- generate keyboard layout icons"); - context.add_main_entries (options, null); - context.set_help_enabled (true); - context.parse (ref args); - } catch (OptionError error) { - GLib.error ("error: %s", error.message); - } - - if (no_subscript_path == null && with_subscript_path == null) { - error ("error: No icon template"); - } else if (no_subscript_path == null) { - no_subscript_path = with_subscript_path; - } else if (with_subscript_path == null) { - with_subscript_path = no_subscript_path; - } - - if (output_path != null) { - var file = File.new_for_path ((!) output_path); - - if (!file.query_exists (null)) { - try { - file.make_directory_with_parents (null); - } catch (Error error) { - GLib.error ("error: %s", error.message); - } - } - } else { - output_path = "."; - } - - Gtk.init (ref args); - - var info = new Gnome.XkbInfo (); - var layouts = info.get_all_layouts (); - var occurrences = new Gee.HashMap (); - - layouts.foreach ((name) => { - string? short_name; - - info.get_layout_info (name, null, out short_name, null, null); - - var abbreviation = abbreviate (short_name); - var has_abbreviation = abbreviation != null && ((!) abbreviation).get_char () != '\0'; - - if (has_abbreviation) { - if (!occurrences.has_key ((!) abbreviation)) { - occurrences[(!) abbreviation] = 1; - } else { - occurrences[(!) abbreviation] = occurrences[(!) abbreviation] + 1; - } - } - }); - - string no_subscript_data; - string with_subscript_data; - - try { - uint8[] contents; - - var icon_x = 0.5 * (width - icon_width); - var icon_y = 0.5 * (height - icon_height); - var layout_font = @"font-family:$font;font-weight:$weight;font-size:$layout_size"; - var subscript_font = @"font-family:$font;font-weight:$weight;font-size:$subscript_size"; - - File.new_for_path ((!) no_subscript_path).load_contents (null, out contents, null); - no_subscript_data = (string) contents; - no_subscript_data = no_subscript_data.replace ("@WIDTH@", @"$width"); - no_subscript_data = no_subscript_data.replace ("@HEIGHT@", @"$height"); - no_subscript_data = no_subscript_data.replace ("@ICON_X@", @"$icon_x"); - no_subscript_data = no_subscript_data.replace ("@ICON_Y@", @"$icon_y"); - no_subscript_data = no_subscript_data.replace ("@ICON_WIDTH@", @"$icon_width"); - no_subscript_data = no_subscript_data.replace ("@ICON_HEIGHT@", @"$icon_height"); - no_subscript_data = no_subscript_data.replace ("@RADIUS@", @"$radius"); - no_subscript_data = no_subscript_data.replace ("@COLOUR@", colour); - no_subscript_data = no_subscript_data.replace ("@LAYOUT_FONT@", layout_font); - no_subscript_data = no_subscript_data.replace ("@SUBSCRIPT_FONT@", subscript_font); - - File.new_for_path ((!) with_subscript_path).load_contents (null, out contents, null); - with_subscript_data = (string) contents; - with_subscript_data = with_subscript_data.replace ("@WIDTH@", @"$width"); - with_subscript_data = with_subscript_data.replace ("@HEIGHT@", @"$height"); - with_subscript_data = with_subscript_data.replace ("@ICON_X@", @"$icon_x"); - with_subscript_data = with_subscript_data.replace ("@ICON_Y@", @"$icon_y"); - with_subscript_data = with_subscript_data.replace ("@ICON_WIDTH@", @"$icon_width"); - with_subscript_data = with_subscript_data.replace ("@ICON_HEIGHT@", @"$icon_height"); - with_subscript_data = with_subscript_data.replace ("@RADIUS@", @"$radius"); - with_subscript_data = with_subscript_data.replace ("@COLOUR@", colour); - with_subscript_data = with_subscript_data.replace ("@LAYOUT_FONT@", layout_font); - with_subscript_data = with_subscript_data.replace ("@SUBSCRIPT_FONT@", subscript_font); - } catch (Error error) { - GLib.error ("error: %s", error.message); - } - - var font_map = new PangoFT2.FontMap (); - var layout_layout = new Pango.Layout (font_map.create_context ()); - var subscript_layout = new Pango.Layout (font_map.create_context ()); - - var font_description = new Pango.FontDescription (); - font_description.set_family (font); - font_description.set_weight ((Pango.Weight) weight); - font_description.set_size (layout_size * Pango.SCALE); - layout_layout.set_font_description (font_description); - - font_description = new Pango.FontDescription (); - font_description.set_family (font); - font_description.set_weight ((Pango.Weight) weight); - font_description.set_size (subscript_size * Pango.SCALE); - subscript_layout.set_font_description (font_description); - - foreach (var entry in occurrences.entries) { - var layout = entry.key; - var count = entry.value; - var file = File.new_for_path (@"$((!) output_path)/indicator-keyboard-$layout.svg"); - - if (force || !file.query_exists (null)) { - int layout_width; - int layout_height; - - layout_layout.set_text (layout, -1); - layout_layout.get_size (out layout_width, out layout_height); - var layout_baseline = layout_layout.get_baseline (); - - var layout_x = 0.5 * (width - 1.0 * layout_width / Pango.SCALE); - var layout_y = 0.5 * (height - 1.0 * layout_height / Pango.SCALE) + 1.0 * layout_baseline / Pango.SCALE; - - var output_data = no_subscript_data; - output_data = output_data.replace ("@LAYOUT@", layout); - output_data = output_data.replace ("@LAYOUT_X@", @"$layout_x"); - output_data = output_data.replace ("@LAYOUT_Y@", @"$layout_y"); - output_data = output_data.replace ("@SUBSCRIPT@", ""); - output_data = output_data.replace ("@SUBSCRIPT_X@", "0"); - output_data = output_data.replace ("@SUBSCRIPT_Y@", "0"); - - try { - file.replace_contents (output_data.data, null, false, FileCreateFlags.REPLACE_DESTINATION, null, null); - } catch (Error error) { - GLib.error ("error: %s", error.message); - } - } - - if (count > 1) { - int layout_width; - int layout_height; - - layout_layout.set_text (layout, -1); - layout_layout.get_size (out layout_width, out layout_height); - var layout_baseline = layout_layout.get_baseline (); - - var layout_y = 0.5 * (height - 1.0 * layout_height / Pango.SCALE) + 1.0 * layout_baseline / Pango.SCALE; - - var partial_data = with_subscript_data; - partial_data = partial_data.replace ("@LAYOUT@", layout); - partial_data = partial_data.replace ("@LAYOUT_Y@", @"$layout_y"); - - for (var i = 1; i <= count; i++) { - file = File.new_for_path (@"$((!) output_path)/indicator-keyboard-$layout-$i.svg"); - - if (force || !file.query_exists (null)) { - var subscript = @"$i"; - int subscript_width; - int subscript_height; - - subscript_layout.set_text (subscript, -1); - subscript_layout.get_size (out subscript_width, out subscript_height); - var subscript_baseline = subscript_layout.get_baseline (); - - var layout_x = 0.5 * (width - 1.0 * (layout_width + subscript_width) / Pango.SCALE); - var subscript_x = layout_x + 1.0 * layout_width / Pango.SCALE; - var subscript_y = layout_y - 0.5 * subscript_height / Pango.SCALE + 1.0 * subscript_baseline / Pango.SCALE; - - var output_data = partial_data; - output_data = output_data.replace ("@LAYOUT_X@", @"$layout_x"); - output_data = output_data.replace ("@LAYOUT_Y@", @"$layout_y"); - output_data = output_data.replace ("@SUBSCRIPT@", subscript); - output_data = output_data.replace ("@SUBSCRIPT_X@", @"$subscript_x"); - output_data = output_data.replace ("@SUBSCRIPT_Y@", @"$subscript_y"); - - try { - file.replace_contents (output_data.data, null, false, FileCreateFlags.REPLACE_DESTINATION, null, null); - } catch (Error error) { - GLib.error ("error: %s", error.message); - } - } - } - } - } - - return 0; -} diff --git a/data/upstart/Makefile.am b/data/upstart/Makefile.am index c0b6a2eb..c762a603 100644 --- a/data/upstart/Makefile.am +++ b/data/upstart/Makefile.am @@ -3,10 +3,10 @@ ########################### xdg_autostartdir = $(datadir)/upstart/xdg/autostart -xdg_autostart_DATA = indicator-keyboard.desktop +xdg_autostart_DATA = ayatana-indicator-keyboard.desktop %.desktop: %.desktop.in $(AM_V_GEN) sed -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@ -EXTRA_DIST = indicator-keyboard.desktop.in +EXTRA_DIST = ayatana-indicator-keyboard.desktop.in diff --git a/data/upstart/ayatana-indicator-keyboard.desktop.in b/data/upstart/ayatana-indicator-keyboard.desktop.in new file mode 100644 index 00000000..f20783be --- /dev/null +++ b/data/upstart/ayatana-indicator-keyboard.desktop.in @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=Ayatana Indicator Keyboard +Exec=@pkglibexecdir@/ayatana-indicator-keyboard-service +OnlyShowIn=Unity; +NoDisplay=true +StartupNotify=false +Terminal=false +Hidden=true diff --git a/data/upstart/indicator-keyboard.desktop.in b/data/upstart/indicator-keyboard.desktop.in deleted file mode 100644 index ec7e565c..00000000 --- a/data/upstart/indicator-keyboard.desktop.in +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Type=Application -Name=Indicator Keyboard -Exec=@pkglibexecdir@/indicator-keyboard-service -OnlyShowIn=Unity; -NoDisplay=true -StartupNotify=false -Terminal=false -Hidden=true diff --git a/debian/changelog b/debian/changelog index 111842a9..348789e6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,10 @@ -ayatana-indicator-keyboard (0.1.0-0) UNRELEASED; urgency=medium +ayatana-indicator-keyboard (0.1.1-0) UNRELEASED; urgency=medium * Fork from Ubuntu's indicator-keyboard. + * Upstream-provided Debian package for ayatana-indicator-keyboard. + See upstream ChangeLog for recent changes. + -- Mike Gabriel Thu, 12 Nov 2015 13:32:10 +0100 indicator-keyboard (0.0.0+15.10.20151006-0ubuntu1) wily; urgency=medium diff --git a/debian/control b/debian/control index 3bf1be69..d5bf8862 100644 --- a/debian/control +++ b/debian/control @@ -1,27 +1,27 @@ -Source: indicator-keyboard -Section: gnome +Source: ayatana-indicator-keyboard +Section: x11 Priority: optional Maintainer: Mike Gabriel -Build-Depends: debhelper (>= 9.0.0), - dh-autoreconf, +Build-Depends: debhelper (>= 9), dbus, + dpkg-dev (>= 1.16.1.1), fcitx-libs-dev (>= 1:4.2.8.3), gir1.2-fcitx-1.0, libaccountsservice-dev, - libgee-dev, + libgee-0.8-dev | libgee-dev, libgirepository1.0-dev, libgnome-desktop-3-dev, libgnomekbd-dev, libgtk-3-dev, libibus-1.0-dev (>= 1.5.0), - liblightdm-gobject-1-dev, + lightdm-vala | liblightdm-gobject-1-dev, valac, xauth, xvfb, -Standards-Version: 3.9.4 -Homepage: https://github.com/ArcticaProject/ayatana-indicator-keyboard -Vcs-Git: git://cgit.arctica-project.org/ayatana/ayatana-indicator-keyboard.git -Vcs-Browser: https://cgit.arctica-project.org/ayatana/ayatana-indicator-keyboard.git/ +Standards-Version: 4.1.1 +Homepage: https://github.com/AyatanaIndicators/ayatana-indicator-keyboard +Vcs-Git: https://github.com/AyatanaIndicators/ayatana-indicator-keyboard +Vcs-Browser: https://github.com/AyatanaIndicators/ayatana-indicator-keyboard Package: ayatana-indicator-keyboard Architecture: any @@ -30,7 +30,7 @@ Depends: ${misc:Depends}, Breaks: ibus (<< 1.5.5-1ubuntu3), Description: Ayatana Keyboard indicator This package contains the keyboard indicator, which should show as an - icon in the top panel of indicator aware destkop environments. + icon in the top panel of indicator aware desktop environments. . - It can be used to switch key layouts or languages, and helps the user - identifying which layouts are currently in use. + It can be used to switch keyboard layouts or languages, and helps the + user identifying which layouts are currently in use. diff --git a/debian/copyright b/debian/copyright index 6354e29b..8ad93a5c 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,16 +1,18 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: indicator-keyboard +Upstream-Name: ayatana-indicator-keyboard +Upstream-Contact: Mike Gabriel Source: https://code.launchpad.net/indicator-keyboard Files: * Copyright: 2013 Canonical Ltd. -License: GPL-3.0 +License: GPL-3 Files: debian/* Copyright: 2013 Canonical Ltd. -License: GPL-3.0 + 2017, Mike Gabriel +License: GPL-3 -License: GPL-3.0 +License: GPL-3 This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 of the diff --git a/debian/rules b/debian/rules index e290d9e1..5da073ca 100755 --- a/debian/rules +++ b/debian/rules @@ -4,13 +4,17 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/buildflags.mk + export DPKG_GENSYMBOLS_CHECK_LEVEL=4 %: - dh $@ --with autoreconf + dh $@ override_dh_autoreconf: - NOCONFIGURE=1 dh_autoreconf ./autogen.sh + NOCONFIGURE=1 ./autogen.sh override_dh_install: find debian/ayatana-indicator-keyboard/usr/lib -name *.la -delete diff --git a/lib/Makefile.am b/lib/Makefile.am deleted file mode 100644 index e458475c..00000000 --- a/lib/Makefile.am +++ /dev/null @@ -1,55 +0,0 @@ -pkglibexec_PROGRAMS = ayatana-indicator-keyboard-service - -AM_CFLAGS = -w -DGNOME_DESKTOP_USE_UNSTABLE_API -AM_LDFLAGS = -lm -AM_VALAFLAGS = --enable-experimental-non-null \ - --metadatadir $(top_srcdir)/deps \ - --vapidir $(top_srcdir)/deps - -indicator_keyboard_service_SOURCES = main.vala \ - source.vala \ - common.vala \ - ibus-menu.vala \ - ibus-panel.vala \ - indicator-menu.vala \ - keyboard-plugin.vala \ - window-stack.vala \ - unity-session.vala \ - unity-greeter.vala -indicator_keyboard_service_VALAFLAGS = $(AM_VALAFLAGS) \ - --pkg gee-1.0 \ - --pkg posix \ - --pkg pangocairo \ - --pkg gtk+-3.0 \ - --pkg GDesktopEnums-3.0 \ - --pkg GnomeDesktop-3.0 \ - --pkg Xkl-1.0 \ - --pkg Gkbd-3.0 \ - --pkg ibus-1.0 \ - --pkg Fcitx-1.0 \ - --pkg AccountsService-1.0 \ - --pkg liblightdm-gobject-1 -indicator_keyboard_service_CFLAGS = $(AM_CFLAGS) \ - $(GEE_CFLAGS) \ - $(PANGOCAIRO_CFLAGS) \ - $(GTK_CFLAGS) \ - $(GNOME_DESKTOP_CFLAGS) \ - $(LIBXKLAVIER_CFLAGS) \ - $(LIBGNOMEKBD_CFLAGS) \ - $(IBUS_CFLAGS) \ - $(FCITX_GCLIENT_CFLAGS) \ - $(ACCOUNTSSERVICE_CFLAGS) \ - $(LIGHTDM_CFLAGS) \ - $(COVERAGE_CFLAGS) -indicator_keyboard_service_LDFLAGS = $(AM_LDFLAGS) \ - $(GEE_LIBS) \ - $(PANGOCAIRO_LIBS) \ - $(GTK_LIBS) \ - $(GNOME_DESKTOP_LIBS) \ - $(LIBXKLAVIER_LIBS) \ - $(LIBGNOMEKBD_LIBS) \ - $(IBUS_LIBS) \ - $(FCITX_GCLIENT_LIBS) \ - $(ACCOUNTSSERVICE_LIBS) \ - $(LIGHTDM_LIBS) \ - $(COVERAGE_LDFLAGS) diff --git a/lib/common.vala b/lib/common.vala deleted file mode 100644 index 9824bc26..00000000 --- a/lib/common.vala +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -string? abbreviate (string? name) { - var index = 0; - unichar first; - unichar second; - - if (name != null) { - if (((!) name).get_next_char (ref index, out first)) { - if (((!) name).get_next_char (ref index, out second)) { - return @"$((!) first.toupper ().to_string ())$((!) second.to_string ())"; - } else { - return first.toupper ().to_string (); - } - } else { - return ""; - } - } else { - return null; - } -} diff --git a/lib/ibus-menu.vala b/lib/ibus-menu.vala deleted file mode 100644 index a240f00b..00000000 --- a/lib/ibus-menu.vala +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -public class Indicator.Keyboard.IBusMenu : MenuModel { - - private static uint radio_counter = 0; - - private IBus.PropList? properties; - - private Menu menu; - private ActionMap? action_map; - - private string? radio_name; - private SimpleAction? radio_action; - private Gee.HashMap radio_properties; - - /* A list of the action names this menu registers. */ - private Gee.LinkedList names; - - public IBusMenu (ActionMap? action_map = null, IBus.PropList? properties = null) { - menu = new Menu (); - - menu.items_changed.connect ((position, removed, added) => { - items_changed (position, removed, added); - }); - - names = new Gee.LinkedList (); - set_action_map (action_map); - set_properties (properties); - } - - ~IBusMenu () { - remove_actions (); - } - - public signal void activate (IBus.Property property, IBus.PropState state); - - private string get_action_name (string key) { - string name; - - if (!action_name_is_valid (key)) { - var builder = new StringBuilder.sized (key.length + 1); - - unichar letter = 0; - int index = 0; - - while (key.get_next_char (ref index, out letter)) { - if (letter == '-' || letter == '.' || letter.isalnum ()) { - builder.append_unichar (letter); - } else { - builder.append_c ('-'); - } - } - - name = @"ibus-$(builder.str)"; - } else { - name = @"ibus-$key"; - } - - /* Find an unused action name using a counter. */ - if (action_map != null && (Action?) ((!) action_map).lookup_action (name) != null) { - var i = 0; - var unique_name = @"$name-$i"; - - while ((Action?) ((!) action_map).lookup_action (unique_name) != null) { - i++; - unique_name = @"$name-$i"; - } - - name = unique_name; - } - - return name; - } - - private string? get_label (IBus.Property property) { - string? label = null; - - if ((IBus.Text?) property.label != null) { - label = property.label.text; - } - - if (label == null && (IBus.Text?) property.symbol != null) { - label = property.symbol.text; - } - - return label; - } - - private void append_normal_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.NORMAL) { - if ((string?) property.key != null) { - var name = get_action_name (property.key); - - if (action_map != null) { - var action = new SimpleAction (name, null); - action.activate.connect ((parameter) => { activate (property, property.state); }); - ((!) action_map).add_action (action); - names.add (name); - } - - menu.append (get_label (property), property.sensitive ? @"indicator.$name" : "-private-disabled"); - } - } - } - - private void append_toggle_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.TOGGLE) { - if ((string?) property.key != null) { - var name = get_action_name (property.key); - - if (action_map != null) { - var state = new Variant.boolean (property.state == IBus.PropState.CHECKED); - var action = new SimpleAction.stateful (name, null, state); - - action.change_state.connect ((value) => { - if (value != null) { - action.set_state ((!) value); - activate (property, ((!) value).get_boolean () ? IBus.PropState.CHECKED : IBus.PropState.UNCHECKED); - } - }); - - ((!) action_map).add_action (action); - names.add (name); - } - - menu.append (get_label (property), property.sensitive ? @"indicator.$name" : "-private-disabled"); - } - } - } - - private void append_radio_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.RADIO) { - if ((string?) property.key != null) { - /* Create a single action for all radio properties. */ - if (action_map != null && radio_name == null) { - radio_counter++; - - var name = @"-private-radio-$radio_counter"; - var action = new SimpleAction.stateful (name, VariantType.STRING, new Variant.string ("")); - - action.change_state.connect ((value) => { - if (value != null) { - var key = ((!) value).get_string (); - - if (radio_properties.has_key (key)) { - action.set_state ((!) value); - activate (radio_properties[key], IBus.PropState.CHECKED); - } - } - }); - - ((!) action_map).add_action (action); - names.add (name); - - radio_name = name; - radio_action = action; - } - - radio_properties[property.key] = property; - - if (property.state == IBus.PropState.CHECKED) { - ((!) radio_action).change_state (new Variant.string (property.key)); - } - - var item = new MenuItem (get_label (property), "-private-disabled"); - - if (property.sensitive) { - item.set_action_and_target_value (@"indicator.$((!) radio_name)", new Variant.string (property.key)); - } - - menu.append_item (item); - } - } - } - - private void append_menu_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.MENU) { - var submenu = new IBusMenu (action_map, property.sub_props); - submenu.activate.connect ((property, state) => { activate (property, state); }); - menu.append_submenu (get_label (property), submenu); - } - } - - private void append_property (IBus.Property? property) { - if (property != null && ((!) property).visible) { - switch (((!) property).prop_type) { - case IBus.PropType.NORMAL: - append_normal_property ((!) property); - break; - - case IBus.PropType.TOGGLE: - append_toggle_property ((!) property); - break; - - case IBus.PropType.RADIO: - append_radio_property ((!) property); - break; - - case IBus.PropType.MENU: - append_menu_property ((!) property); - break; - - case IBus.PropType.SEPARATOR: - break; - } - } - } - - private void update_menu () { - /* Break reference cycle between action map and submenus. */ - for (var i = 0; i < menu.get_n_items (); i++) { - var submenu = menu.get_item_link (i, Menu.LINK_SUBMENU) as IBusMenu; - - if (submenu != null) { - ((!) submenu).remove_actions (); - } - } - - menu.remove_all (); - - if (properties != null) { - for (var i = 0; i < ((!) properties).properties.length; i++) { - append_property (((!) properties).get (i)); - } - } - } - - private void remove_actions () { - radio_action = null; - radio_name = null; - - if (action_map != null) { - foreach (var name in names) { - ((!) action_map).remove_action (name); - } - } - - names.clear (); - } - - public void set_action_map (ActionMap? action_map) { - if (action_map != this.action_map) { - remove_actions (); - this.action_map = action_map; - update_menu (); - } - } - - public void set_properties (IBus.PropList? properties) { - if (properties != this.properties) { - remove_actions (); - radio_properties = new Gee.HashMap (); - this.properties = properties; - update_menu (); - } - } - - public void update_property (IBus.Property property) { - remove_actions (); - radio_properties = new Gee.HashMap (); - update_menu (); - } - - /* Forward all menu model calls to our internal menu. */ - - public override Variant get_item_attribute_value (int item_index, string attribute, VariantType? expected_type) { - return menu.get_item_attribute_value (item_index, attribute, expected_type); - } - - public override void get_item_attributes (int item_index, out HashTable? attributes) { - menu.get_item_attributes (item_index, out attributes); - } - - public override MenuModel get_item_link (int item_index, string link) { - return menu.get_item_link (item_index, link); - } - - public override void get_item_links (int item_index, out HashTable links) { - menu.get_item_links (item_index, out links); - } - - public override int get_n_items () { - return menu.get_n_items (); - } - - public override bool is_mutable () { - return menu.is_mutable (); - } - - public override MenuAttributeIter iterate_item_attributes (int item_index) { - return menu.iterate_item_attributes (item_index); - } - - public override MenuLinkIter iterate_item_links (int item_index) { - return menu.iterate_item_links (item_index); - } -} diff --git a/lib/ibus-panel.vala b/lib/ibus-panel.vala deleted file mode 100644 index 2a380efd..00000000 --- a/lib/ibus-panel.vala +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -[DBus (name="org.ayatana.IBus.Panel.Private")] -public interface IBusPanel : Object { - - public abstract void activate_property (string name, uint state) throws IOError; - - public signal void properties_registered (Variant variant); - public signal void property_updated (Variant variant); -} diff --git a/lib/indicator-menu.vala b/lib/indicator-menu.vala deleted file mode 100644 index 2cfa52c6..00000000 --- a/lib/indicator-menu.vala +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -public class Indicator.Keyboard.IndicatorMenu : MenuModel { - - public enum Options { - NONE = 0, - DCONF = 1 << 0, - XKB = 1 << 1, - IBUS = 1 << 2, - SETTINGS = 1 << 3 - } - - private Options options; - - private Menu indicator_menu; - private Menu sources_section; - private IBusMenu properties_section; - - public IndicatorMenu (ActionMap? action_map = null, Options options = Options.NONE) { - this.options = options; - - indicator_menu = new Menu (); - sources_section = new Menu (); - - if ((options & ~Options.DCONF) != Options.NONE) { - var submenu = new Menu (); - - submenu.append_section (null, sources_section); - - if (Options.IBUS in options) { - properties_section = new IBusMenu (action_map); - properties_section.activate.connect ((property, state) => { activate (property, state); }); - submenu.append_section (null, properties_section); - } - - if (Options.SETTINGS in options) { - var settings_section = new Menu (); - settings_section.append (_ ("Character Map"), "indicator.map"); - settings_section.append (_ ("Keyboard Layout Chart"), "indicator.chart"); - settings_section.append (_ ("Text Entry Settings..."), "indicator.settings"); - submenu.append_section (null, settings_section); - } - - var indicator = new MenuItem.submenu (null, submenu); - indicator.set_detailed_action ("indicator.indicator"); - indicator.set_attribute ("x-canonical-type", "s", "org.ayatana.indicator.root"); - - /* We need special mouse actions on the lock screen. */ - if (Options.DCONF in options) { - indicator.set_attribute ("x-canonical-secondary-action", "s", "indicator.next"); - indicator.set_attribute ("x-canonical-scroll-action", "s", "indicator.scroll"); - } else { - indicator.set_attribute ("x-canonical-secondary-action", "s", "indicator.locked_next"); - indicator.set_attribute ("x-canonical-scroll-action", "s", "indicator.locked_scroll"); - } - - indicator_menu.append_item (indicator); - } - } - - public signal void activate (IBus.Property property, IBus.PropState state); - - public void set_sources (Source[] sources) { - sources_section.remove_all (); - - for (var i = 0; i < sources.length; i++) { - var visible = (sources[i].is_xkb && Options.XKB in options) || - (sources[i].is_ibus && Options.IBUS in options); - - if (visible) { - string action; - - if (Options.DCONF in options) { - action = "indicator.current"; - } else { - action = "indicator.active"; - } - - var item = new MenuItem (sources[i].name, action); - - item.set_attribute (Menu.ATTRIBUTE_TARGET, "u", i); - - if (sources[i].icon != null) { - item.set_icon ((!) sources[i].icon); - } - - sources_section.append_item (item); - } - } - } - - public void set_properties (IBus.PropList properties) { - if (Options.IBUS in options) { - properties_section.set_properties (properties); - } - } - - public void update_property (IBus.Property property) { - if (Options.IBUS in options) { - properties_section.update_property (property); - } - } - - public override bool is_mutable () { - return indicator_menu.is_mutable (); - } - - public override int get_n_items () { - return indicator_menu.get_n_items (); - } - - public override void get_item_attributes (int item_index, out HashTable? attributes) { - indicator_menu.get_item_attributes (item_index, out attributes); - } - - public override void get_item_links (int item_index, out HashTable links) { - indicator_menu.get_item_links (item_index, out links); - } - - public override Variant get_item_attribute_value (int item_index, string attribute, VariantType? expected_type) { - return indicator_menu.get_item_attribute_value (item_index, attribute, expected_type); - } - - public override MenuModel get_item_link (int item_index, string link) { - return indicator_menu.get_item_link (item_index, link); - } - - public override MenuAttributeIter iterate_item_attributes (int item_index) { - return indicator_menu.iterate_item_attributes (item_index); - } - - public override MenuLinkIter iterate_item_links (int item_index) { - return indicator_menu.iterate_item_links (item_index); - } -} diff --git a/lib/keyboard-plugin.vala b/lib/keyboard-plugin.vala deleted file mode 100644 index af1628f6..00000000 --- a/lib/keyboard-plugin.vala +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -[DBus (name="org.ayatana.SettingsDaemon.Keyboard.Private")] -public interface KeyboardPlugin : Object { - - public abstract void activate_input_source (uint index) throws IOError; -} diff --git a/lib/main.vala b/lib/main.vala deleted file mode 100644 index 9bb3eb1f..00000000 --- a/lib/main.vala +++ /dev/null @@ -1,1265 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -[DBus (name = "org.ayatana.indicator.keyboard")] -public class Indicator.Keyboard.Service : Object { - - private static const uint PROPERTIES_DELAY = 250; - - private static Service service; - - private bool force; - private bool use_gtk; - - private MainLoop? loop; - private Settings indicator_settings; - private Settings source_settings; - private Settings per_window_settings; - private SList users; - - private WindowStack? window_stack; - private Gee.HashMap? window_sources; - private uint focused_window_id; - - private IBus.Bus? ibus; - private IBusPanel? ibus_panel; - private ulong ibus_connected_id; - private uint panel_timeout; - - private Fcitx.InputMethod? fcitx; - private bool fcitx_initialized; - - private Source[]? sources; - - private SimpleActionGroup? action_group; - private SimpleAction? indicator_action; - private SimpleAction? active_action; - private IndicatorMenu? desktop_menu; - private IndicatorMenu? desktop_greeter_menu; - private IndicatorMenu? desktop_lockscreen_menu; - - private KeyboardPlugin? keyboard_plugin; - private UnitySession? unity_session; - private UnityGreeter? unity_greeter; - private string? greeter_user; - private uint lightdm_current; - - [DBus (visible = false)] - public Service (ref unowned string[] args) { - force = "--force" in args; - use_gtk = "--use-gtk" in args; - - if (use_gtk) { - use_gtk = Gtk.init_check (ref args); - - Gtk.IconTheme? icon_theme = Gtk.IconTheme.get_default (); - - if (icon_theme != null) { - ((!) icon_theme).changed.connect (() => { - if (sources != null) { - foreach (var source in (!) sources) { - source.icon = null; - } - } - - if (desktop_menu != null) { - get_desktop_menu ().set_sources (get_sources ()); - } - - if (desktop_greeter_menu != null) { - get_desktop_greeter_menu ().set_sources (get_sources ()); - } - - if (desktop_lockscreen_menu != null) { - get_desktop_lockscreen_menu ().set_sources (get_sources ()); - } - - if (indicator_action != null) { - update_indicator_action (); - } - }); - } - } else { - Gdk.init (ref args); - } - - if (is_login_user ()) { - var name = Environment.get_variable ("UNITY_GREETER_DBUS_NAME"); - - if (name != null) { - Bus.watch_name (BusType.SESSION, - (!) name, - BusNameWatcherFlags.NONE, - handle_unity_greeter_name_appeared, - handle_unity_greeter_name_vanished); - } - } else { - Bus.watch_name (BusType.SESSION, - "org.gnome.SettingsDaemon.Keyboard", - BusNameWatcherFlags.NONE, - handle_keyboard_name_appeared, - handle_keyboard_name_vanished); - - Bus.watch_name (BusType.SESSION, - "org.ayatana.Unity", - BusNameWatcherFlags.NONE, - handle_unity_name_appeared, - handle_unity_name_vanished); - - if (!is_fcitx_active ()) { - Bus.watch_name (BusType.SESSION, - "org.ayatana.Unity.WindowStack", - BusNameWatcherFlags.NONE, - handle_window_stack_name_appeared, - handle_window_stack_name_vanished); - } - } - - indicator_settings = new Settings ("org.ayatana.indicator.keyboard"); - indicator_settings.changed["visible"].connect (handle_changed_visible); - - source_settings = new Settings ("org.gnome.desktop.input-sources"); - source_settings.changed["current"].connect (handle_changed_current); - source_settings.changed["sources"].connect (handle_changed_sources); - - per_window_settings = new Settings ("org.gnome.libgnomekbd.desktop"); - per_window_settings.changed["group-per-window"].connect (handle_changed_group_per_window); - - migrate_keyboard_layouts (); - update_window_sources (); - acquire_bus_name (); - } - - [DBus (visible = false)] - private static bool is_login_user () { - return Environment.get_user_name () == "lightdm"; - } - - [DBus (visible = false)] - private static bool is_ibus_active () { - if (is_login_user ()) { - return false; - } - - var module = Environment.get_variable ("GTK_IM_MODULE"); - return module != null && (!) module == "ibus"; - } - - [DBus (visible = false)] - private static bool is_fcitx_active () { - if (is_login_user ()) { - return false; - } - - var module = Environment.get_variable ("GTK_IM_MODULE"); - return module != null && (!) module == "fcitx"; - } - - [DBus (visible = false)] - private IBus.Bus get_ibus () { - if (ibus == null) { - IBus.init (); - - var proxy = new IBus.Bus (); - - proxy.connected.connect (() => { - if (desktop_menu != null) { - get_desktop_menu ().set_sources (get_sources ()); - } - - if (desktop_greeter_menu != null) { - get_desktop_greeter_menu ().set_sources (get_sources ()); - } - - if (desktop_lockscreen_menu != null) { - get_desktop_lockscreen_menu ().set_sources (get_sources ()); - } - - if (indicator_action != null) { - update_indicator_action (); - } - }); - - ibus = proxy; - } - - return (!) ibus; - } - - [DBus (visible = false)] - private IBusPanel? get_ibus_panel () { - if (ibus_panel == null && get_ibus ().is_connected ()) { - var connection = get_ibus ().get_connection (); - var name = "org.freedesktop.IBus.Panel"; - var path = "/org/freedesktop/IBus/Panel"; - - try { - var proxy = connection.get_proxy_sync (name, path); - - proxy.properties_registered.connect ((variant) => { - var properties = new IBus.PropList (); - properties.deserialize (variant); - - if (properties is IBus.PropList) { - handle_properties_registered ((!) (properties as IBus.PropList)); - } - }); - proxy.property_updated.connect ((variant) => { - var type = IBus.PropType.NORMAL; - var state = IBus.PropState.INCONSISTENT; - var text = new IBus.Text.from_static_string (""); - var property = new IBus.Property ("", type, text, null, text, false, false, state, null); - property.deserialize (variant); - - if (property is IBus.Property) { - handle_property_updated ((!) (property as IBus.Property)); - } - }); - - ibus_panel = proxy; - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - - return ibus_panel; - } - - [DBus (visible = false)] - private Fcitx.InputMethod? get_fcitx () { - if (!fcitx_initialized) { - fcitx_initialized = true; - - if (is_fcitx_active ()) { - try { - var proxy = new Fcitx.InputMethod (BusType.SESSION, DBusProxyFlags.NONE, 0); - proxy.notify["current-im"].connect ((pspec) => { handle_changed_current ("current"); }); - fcitx = proxy; - } catch (Error error) { - warning ("error: %s", error.message); - } - } - } - - return fcitx; - } - - [DBus (visible = false)] - public void up () { - if (loop == null) { - var main_loop = new MainLoop (); - loop = main_loop; - main_loop.run (); - } - } - - [DBus (visible = false)] - public void down () { - if (loop != null) { - ((!) loop).quit (); - loop = null; - } - } - - [DBus (visible = false)] - private void acquire_bus_name () { - Bus.own_name (BusType.SESSION, - "org.ayatana.indicator.keyboard", - BusNameOwnerFlags.ALLOW_REPLACEMENT | (force ? BusNameOwnerFlags.REPLACE : 0), - handle_bus_acquired, - null, - handle_name_lost); - } - - [DBus (visible = false)] - private void update_greeter_user () { - if (greeter_user == null && unity_greeter != null) { - try { - greeter_user = ((!) unity_greeter).get_active_entry (); - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - - string? source = null; - - if (greeter_user != null) { - var manager = Act.UserManager.get_default (); - - if (manager.is_loaded) { - Act.User? user = manager.get_user ((!) greeter_user); - - if (user != null && ((!) user).is_loaded) { - foreach (var outer in ((!) user).input_sources) { - foreach (var inner in (!) outer) { - unowned string key; - unowned string value; - - ((!) inner).get ("{&s&s}", out key, out value); - - if (key == "xkb") { - source = value; - break; - } - } - - if (source != null) { - break; - } - } - - if (source == null) { - var layouts = ((!) user).xkeyboard_layouts; - - if (layouts.length <= 0) { - var user_list = LightDM.UserList.get_instance (); - LightDM.User? light_user = user_list.get_user_by_name ((!) greeter_user); - - if (light_user != null) { - layouts = ((!) light_user).get_layouts (); - } - } - - if (layouts.length > 0) { - source = layouts[0].replace (" ", "+").replace ("\t", "+"); - } - } - } - } - } - - if (source == null) { - LightDM.Layout? layout = LightDM.get_layout (); - - if (layout != null) { - source = ((!) layout).name; - - if (source != null) { - source = ((!) source).replace (" ", "+"); - source = ((!) source).replace ("\t", "+"); - } - } - } - - if (source != null) { - var array = source_settings.get_value ("sources"); - - for (var i = 0; i < array.n_children (); i++) { - unowned string type; - unowned string name; - - array.get_child (i, "(&s&s)", out type, out name); - - if (type == "xkb" && name == (!) source) { - source_settings.set_uint ("current", i); - break; - } - } - } - } - - [DBus (visible = false)] - private void handle_entry_selected (string entry_name) { - if (greeter_user == null || entry_name != (!) greeter_user) { - greeter_user = entry_name; - - update_greeter_user (); - } - } - - [DBus (visible = false)] - private void migrate_keyboard_layouts () { - if (is_login_user ()) { - lightdm_current = get_current (); - - var manager = Act.UserManager.get_default (); - - if (manager.is_loaded) { - users = manager.list_users (); - - foreach (var user in users) { - if (user.is_loaded) { - migrate_input_sources (); - } else { - user.notify["is-loaded"].connect ((pspec) => { - if (user.is_loaded) { - migrate_input_sources (); - } - }); - } - } - } else { - manager.notify["is-loaded"].connect ((pspec) => { - if (manager.is_loaded) { - users = manager.list_users (); - - foreach (var user in users) { - if (user.is_loaded) { - migrate_input_sources (); - } else { - user.notify["is-loaded"].connect ((pspec) => { - if (user.is_loaded) { - migrate_input_sources (); - } - }); - } - } - } - }); - } - - var user_list = LightDM.UserList.get_instance (); - - user_list.user_added.connect ((user) => { migrate_input_sources (); }); - user_list.user_changed.connect ((user) => { migrate_input_sources (); }); - user_list.user_removed.connect ((user) => { migrate_input_sources (); }); - - /* Force the loading of the user list. */ - user_list.get_user_by_name (""); - } else { - if (!indicator_settings.get_boolean ("migrated")) { - var builder = new VariantBuilder (new VariantType ("a(ss)")); - var length = 0; - - var layout_settings = new Settings ("org.gnome.libgnomekbd.keyboard"); - var layouts = layout_settings.get_strv ("layouts"); - - foreach (var layout in layouts) { - var source = layout; - source = source.replace (" ", "+"); - source = source.replace ("\t", "+"); - - builder.add ("(ss)", "xkb", source); - length++; - } - - var engines = get_ibus ().list_active_engines (); - - foreach (var engine in engines) { - if (length == 0 || engine.name.has_prefix ("xkb")) { - var source = "us"; - string? layout = engine.get_layout (); - string? variant = engine.get_layout_variant (); - - if (layout != null && ((!) layout).length == 0) { - layout = null; - } - - if (variant != null && ((!) variant).length == 0) { - variant = null; - } - - if (layout != null && variant != null) { - source = @"$((!) layout)+$((!) variant)"; - } else if (layout != null) { - source = (!) layout; - } - - builder.add ("(ss)", "xkb", source); - length++; - } - - if (!engine.name.has_prefix ("xkb")) { - builder.add ("(ss)", "ibus", engine.name); - length++; - } - } - - source_settings.set_value ("sources", builder.end ()); - indicator_settings.set_boolean ("migrated", true); - } - } - } - - [DBus (visible = false)] - private void migrate_input_sources () { - var list = new Gee.LinkedList (); - var added = new Gee.HashSet (); - - foreach (var user in users) { - if (user.is_loaded) { - var done = false; - - foreach (var outer in user.input_sources) { - foreach (var inner in (!) outer) { - unowned string key; - unowned string source; - - ((!) inner).get ("{&s&s}", out key, out source); - - if (key == "xkb") { - done = true; - - if (!added.contains (source)) { - list.add (source); - added.add (source); - } - } - } - } - - if (!done) { - var layouts = user.xkeyboard_layouts; - foreach (var layout in layouts) { - done = true; - - var source = layout; - source = source.replace (" ", "+"); - source = source.replace ("\t", "+"); - - if (!added.contains (source)) { - list.add (source); - added.add (source); - } - } - } - - if (!done) { - var user_list = LightDM.UserList.get_instance (); - LightDM.User? light_user = user_list.get_user_by_name (user.user_name); - - if (light_user != null) { - var layouts = ((!) light_user).get_layouts (); - foreach (var layout in layouts) { - done = true; - - var source = layout; - source = source.replace (" ", "+"); - source = source.replace ("\t", "+"); - - if (!added.contains (source)) { - list.add (source); - added.add (source); - } - } - } - } - } - } - - LightDM.Layout? layout = LightDM.get_layout (); - - if (layout != null) { - string? source = ((!) layout).name; - - if (source != null) { - source = ((!) source).replace (" ", "+"); - source = ((!) source).replace ("\t", "+"); - - if (!added.contains ((!) source)) { - list.add ((!) source); - added.add ((!) source); - } - } - } - - var builder = new VariantBuilder (new VariantType ("a(ss)")); - - foreach (var name in list) { - builder.add ("(ss)", "xkb", name); - } - - if (lightdm_current < list.size) { - source_settings.set_uint ("current", lightdm_current); - } else { - source_settings.set_uint ("current", list.size - 1); - } - - source_settings.set_value ("sources", builder.end ()); - - update_greeter_user (); - } - - [DBus (visible = false)] - private void update_login_layout () { - if (is_login_user ()) { - unowned List layouts = LightDM.get_layouts (); - var current = get_current (); - - if (current < get_sources ().length) { - var source = get_sources ()[current]; - string? name = null; - - if (source.layout != null && source.variant != null) { - name = @"$((!) source.layout)\t$((!) source.variant)"; - } else if (source.layout != null) { - name = source.layout; - } - - if (name != null) { - foreach (var layout in layouts) { - if (layout.name == (!) name) { - LightDM.set_layout (layout); - break; - } - } - } - } - } - } - - [DBus (visible = false)] - private void update_window_sources () { - if (window_stack != null) { - var group_per_window = per_window_settings.get_boolean ("group-per-window"); - - if (group_per_window != (window_sources != null)) { - if (group_per_window) { - focused_window_id = 0; - - try { - var windows = ((!) window_stack).get_window_stack (); - - foreach (var window in windows) { - if (window.focused) { - focused_window_id = window.window_id; - break; - } - } - } catch (IOError error) { - warning ("error: %s", error.message); - } - - window_sources = new Gee.HashMap (); - ((!) window_stack).window_destroyed.connect (handle_window_destroyed); - ((!) window_stack).focused_window_changed.connect (handle_focused_window_changed); - } else { - ((!) window_stack).focused_window_changed.disconnect (handle_focused_window_changed); - ((!) window_stack).window_destroyed.disconnect (handle_window_destroyed); - window_sources = null; - } - } - } - } - - [DBus (visible = false)] - private void handle_changed_group_per_window (string key) { - update_window_sources (); - } - - [DBus (visible = false)] - private void handle_window_destroyed (uint window_id, string app_id) { - ((!) window_sources).unset (window_id); - } - - [DBus (visible = false)] - private void handle_focused_window_changed (uint window_id, string app_id, uint stage) { - var sources = get_sources (); - var old_current = get_current (); - - if (old_current < sources.length) { - ((!) window_sources)[focused_window_id] = sources[old_current]; - } - - if (!(((!) window_sources).has_key (window_id))) { - var default_group = per_window_settings.get_int ("default-group"); - - if (default_group >= 0) { - for (var offset = 0; offset < sources.length; offset++) { - var current = (default_group + offset) % sources.length; - var source = sources[current]; - - if (source.is_xkb || - (source.is_ibus && is_ibus_active ()) || - (source.is_fcitx && is_fcitx_active ())) { - if (current != old_current) { - source_settings.set_uint ("current", current); - } - - break; - } - } - } - } else { - var source = ((!) window_sources)[window_id]; - - for (var current = 0; current < sources.length; current++) { - if (sources[current] == source) { - if (current != old_current) { - source_settings.set_uint ("current", current); - } - - break; - } - } - } - - focused_window_id = window_id; - } - - [DBus (visible = false)] - private uint get_current () { - if (is_fcitx_active () && get_fcitx () != null) { - string? engine = ((!) get_fcitx ()).current_im; - - if (engine != null) { - var is_xkb = ((!) engine).has_prefix ("fcitx-keyboard-"); - var type = is_xkb ? "xkb" : "fcitx"; - var name = (!) engine; - - if (is_xkb) { - name = name.substring ("fcitx-keyboard-".length); - var index = name.index_of ("-"); - if (index >= 0) { - name.data[index] = '+'; - } - } - - var i = 0; - - foreach (var pair in source_settings.get_value ("sources")) { - unowned string source_type; - unowned string source_name; - - ((!) pair).get ("(&s&s)", out source_type, out source_name); - - if (source_name == name && source_type == type) { - return i; - } - - i++; - } - } - } - - return source_settings.get_uint ("current"); - } - - [DBus (visible = false)] - private Source[] get_sources () { - if (sources == null) { - var array = source_settings.get_value ("sources"); - - sources = new Source[array.n_children ()]; - - for (var i = 0; i < ((!) sources).length; i++) { - sources[i] = new Source(array.get_child_value (i), use_gtk); - sources[i].show_subscript = false; - sources[i].subscript = 1; - - for (var j = (int) i - 1; j >= 0; j--) { - if ((!) sources[j].short_name == (!) sources[i].short_name) { - sources[i].subscript = sources[j].subscript + 1; - sources[i].show_subscript = true; - sources[j].show_subscript = true; - - break; - } - } - - if (ibus_connected_id == 0 && sources[i].is_ibus) { - ibus_connected_id = get_ibus ().connected.connect (() => { get_ibus_panel (); }); - get_ibus ().disconnected.connect (() => { ibus_panel = null; }); - - if (get_ibus ().is_connected ()) { - get_ibus_panel (); - } - } - } - } - - return (!) sources; - } - - [DBus (visible = false)] - private void handle_properties_registered (IBus.PropList list) { - if (panel_timeout > 0) { - GLib.Source.remove (panel_timeout); - panel_timeout = 0; - } - - panel_timeout = Timeout.add (PROPERTIES_DELAY, () => { - get_desktop_menu ().set_properties (list); - panel_timeout = 0; - return false; - }); - } - - [DBus (visible = false)] - private void handle_property_updated (IBus.Property property) { - get_desktop_menu ().update_property (property); - } - - [DBus (visible = false)] - private void update_indicator_action () { - Icon? icon = null; - string? name = null; - - var sources = get_sources (); - var active = get_active_action ().get_state ().get_uint32 (); - - if (active < sources.length) { - icon = sources[active].icon; - name = sources[active].name; - } - - var builder = new VariantBuilder (new VariantType ("a{sv}")); - builder.add ("{sv}", "visible", indicator_settings.get_value ("visible")); - if (name != null) { - var description = _ ("%s input source").printf ((!) name); - builder.add ("{sv}", "accessible-desc", new Variant.string (description)); - } - if (icon != null) { - builder.add ("{sv}", "icon", ((!) icon).serialize ()); - } - - get_indicator_action ().set_state (builder.end ()); - } - - [DBus (visible = false)] - private SimpleAction get_indicator_action () { - if (indicator_action == null) { - var state = new Variant.parsed ("{ 'visible' : }"); - indicator_action = new SimpleAction.stateful ("indicator", null, state); - update_indicator_action (); - } - - return (!) indicator_action; - } - - [DBus (visible = false)] - private void handle_changed_active (Variant? value) { - if (value != null) { - ((!) active_action).set_state ((!) value); - update_indicator_action (); - - if (keyboard_plugin != null) { - try { - ((!) keyboard_plugin).activate_input_source (((!) value).get_uint32 ()); - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - } - } - - [DBus (visible = false)] - private void update_active_action () { - if (active_action != null) { - ((!) active_action).set_state (new Variant.uint32 (get_current ())); - update_indicator_action (); - } - } - - [DBus (visible = false)] - private Action get_active_action () { - if (active_action == null) { - var action = new SimpleAction.stateful ("active", VariantType.UINT32, new Variant.uint32 (get_current ())); - action.change_state.connect (handle_changed_active); - active_action = action; - } - - return (!) active_action; - } - - [DBus (visible = false)] - private void handle_middle_click (Variant? parameter) { - handle_scroll_wheel (new Variant.int32 (-1)); - } - - [DBus (visible = false)] - private void handle_scroll_wheel (Variant? parameter) { - if (parameter != null) { - var old_current = get_current (); - var sources = get_sources (); - var length = 0; - - foreach (var source in sources) { - if (source.is_xkb || - (source.is_ibus && is_ibus_active ()) || - (source.is_fcitx && is_fcitx_active ())) { - length++; - } - } - - if (length > 1) { - var current = old_current; - var offset = -((!) parameter).get_int32 () % length; - var jump = 1; - - if (offset < 0) { - offset = -offset; - jump = sources.length - jump; - } - - /* - * We need to cycle through offset valid input sources, skipping those that aren't - * valid for this session (i.e. skipping Fcitx ones if IBus is active and vice-versa. - * jump is the direction we need to cycle in, which is 1 if we want to cycle forward - * and -1 (mod sources.length) if we want to cycle backward. - */ - - for (; offset > 0; offset--) { - do { - current = (current + jump) % sources.length; - } while ((sources[current].is_ibus && !is_ibus_active ()) || - (sources[current].is_fcitx && !is_fcitx_active ())); - } - - if (current != old_current) { - source_settings.set_uint ("current", current); - } - } - } - } - - [DBus (visible = false)] - private void handle_middle_click_when_locked (Variant? parameter) { - handle_scroll_wheel_when_locked (new Variant.int32 (-1)); - } - - [DBus (visible = false)] - private void handle_scroll_wheel_when_locked (Variant? parameter) { - if (parameter != null) { - var sources = get_sources (); - var xkb_length = 0; - - /* Figure out how many Xkb sources we have. */ - foreach (var source in sources) { - if (source.is_xkb) { - xkb_length++; - } - } - - if (xkb_length > 1) { - var active_action = get_active_action (); - var active = active_action.get_state ().get_uint32 (); - var offset = -((!) parameter).get_int32 () % xkb_length; - - /* Make offset positive modulo xkb_length. */ - if (offset < 0) { - offset += xkb_length; - } - - /* We need to cycle through Xkb sources only. */ - while (offset > 0) { - do { - active = (active + 1) % sources.length; - } while (!sources[active].is_xkb); - - offset--; - } - - active_action.change_state (new Variant.uint32 (active)); - } - } - } - - [DBus (visible = false)] - protected virtual SimpleActionGroup create_action_group (Action root_action) { - var group = new SimpleActionGroup (); - - /* - * The 'current' action reflects the current setting in - * GSettings and the 'active' action only exists to set the - * active input source without persisting it. - * - * The lock screen menu uses the 'active' action while the - * other menus instead persist the current input source. - */ - - group.add_action (root_action); - group.add_action (get_active_action ()); - group.add_action (source_settings.create_action ("current")); - - var action = new SimpleAction ("next", null); - action.activate.connect (handle_middle_click); - group.add_action (action); - - action = new SimpleAction ("scroll", VariantType.INT32); - action.activate.connect (handle_scroll_wheel); - group.add_action (action); - - action = new SimpleAction ("locked_next", null); - action.activate.connect (handle_middle_click_when_locked); - group.add_action (action); - - action = new SimpleAction ("locked_scroll", VariantType.INT32); - action.activate.connect (handle_scroll_wheel_when_locked); - group.add_action (action); - - action = new SimpleAction ("map", null); - action.activate.connect (handle_activate_map); - group.add_action (action); - - action = new SimpleAction ("chart", null); - action.activate.connect (handle_activate_chart); - group.add_action (action); - - action = new SimpleAction ("settings", null); - action.activate.connect (handle_activate_settings); - group.add_action (action); - - return group; - } - - [DBus (visible = false)] - public SimpleActionGroup get_action_group () { - if (action_group == null) { - action_group = create_action_group (get_indicator_action ()); - } - - return (!) action_group; - } - - [DBus (visible = false)] - public IndicatorMenu get_desktop_menu () { - if (desktop_menu == null) { - var options = IndicatorMenu.Options.DCONF; - - if (!is_fcitx_active ()) { - options |= IndicatorMenu.Options.XKB | IndicatorMenu.Options.SETTINGS; - - if (is_ibus_active ()) { - options |= IndicatorMenu.Options.IBUS; - } - } - - var menu = new IndicatorMenu (get_action_group (), options); - - menu.set_sources (get_sources ()); - menu.activate.connect ((property, state) => { - var panel = get_ibus_panel (); - - if (panel != null) { - try { - ((!) panel).activate_property (property.key, state); - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - }); - - desktop_menu = menu; - } - - return (!) desktop_menu; - } - - [DBus (visible = false)] - public IndicatorMenu get_desktop_greeter_menu () { - if (desktop_greeter_menu == null) { - var options = IndicatorMenu.Options.DCONF | - IndicatorMenu.Options.XKB; - - var menu = new IndicatorMenu (get_action_group (), options); - menu.set_sources (get_sources ()); - desktop_greeter_menu = menu; - } - - return (!) desktop_greeter_menu; - } - - [DBus (visible = false)] - public IndicatorMenu get_desktop_lockscreen_menu () { - if (desktop_lockscreen_menu == null) { - var options = IndicatorMenu.Options.XKB; - - var menu = new IndicatorMenu (get_action_group (), options); - menu.set_sources (get_sources ()); - desktop_lockscreen_menu = menu; - } - - return (!) desktop_lockscreen_menu; - } - - [DBus (visible = false)] - private void handle_changed_visible (string key) { - update_indicator_action (); - } - - [DBus (visible = false)] - private void handle_changed_current (string key) { - update_indicator_action (); - update_active_action (); - update_login_layout (); - } - - [DBus (visible = false)] - private void handle_changed_sources (string key) { - sources = null; - - get_desktop_menu ().set_sources (get_sources ()); - get_desktop_greeter_menu ().set_sources (get_sources ()); - get_desktop_lockscreen_menu ().set_sources (get_sources ()); - update_indicator_action (); - update_login_layout (); - } - - [DBus (visible = false)] - private void handle_activate_map (Variant? parameter) { - try { - Process.spawn_command_line_async ("gucharmap"); - } catch (SpawnError error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_activate_chart (Variant? parameter) { - string? layout = "us"; - string? variant = null; - - var sources = get_sources (); - var current = get_current (); - - if (current < sources.length) { - layout = sources[current].layout; - variant = sources[current].variant; - } - - var has_layout = layout != null && ((!) layout).get_char () != '\0'; - var has_variant = variant != null && ((!) variant).get_char () != '\0'; - - try { - string command; - - if (has_layout && has_variant) { - command = @"gkbd-keyboard-display -l \"$((!) layout)\t$((!) variant)\""; - } else if (has_layout) { - command = @"gkbd-keyboard-display -l $((!) layout)"; - } else { - command = @"gkbd-keyboard-display -l us"; - } - - Process.spawn_command_line_async (command); - } catch (SpawnError error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_activate_settings (Variant? parameter) { - try { - Process.spawn_command_line_async ("unity-control-center region layouts"); - } catch (SpawnError error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_unity_greeter_name_appeared (DBusConnection connection, string name, string name_owner) { - try { - var greeter = Bus.get_proxy_sync (BusType.SESSION, name, "/list"); - greeter.entry_selected.connect (handle_entry_selected); - unity_greeter = greeter; - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_unity_greeter_name_vanished (DBusConnection connection, string name) { - unity_greeter = null; - } - - [DBus (visible = false)] - private void handle_keyboard_name_appeared (DBusConnection connection, string name, string name_owner) { - try { - keyboard_plugin = Bus.get_proxy_sync (BusType.SESSION, name, "/org/gnome/SettingsDaemon/Keyboard"); - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_keyboard_name_vanished (DBusConnection connection, string name) { - keyboard_plugin = null; - } - - [DBus (visible = false)] - private void handle_unity_name_appeared (DBusConnection connection, string name, string name_owner) { - try { - var session = Bus.get_proxy_sync (BusType.SESSION, name, "/org/ayatana/Unity/Session"); - - session.locked.connect (() => { - var sources = get_sources (); - - if (sources.length > 0) { - var current = get_current (); - - if (current < sources.length && !sources[current].is_xkb) { - for (var i = 0; i < sources.length; i++) { - if (sources[i].is_xkb) { - get_active_action ().change_state (new Variant.uint32 (i)); - break; - } - } - } - } - }); - session.unlocked.connect (() => { - get_active_action ().change_state (new Variant.uint32 (get_current ())); - }); - - unity_session = session; - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_unity_name_vanished (DBusConnection connection, string name) { - unity_session = null; - } - - [DBus (visible = false)] - private void handle_window_stack_name_appeared (DBusConnection connection, string name, string name_owner) { - try { - window_stack = Bus.get_proxy_sync (BusType.SESSION, name, "/org/ayatana/Unity/WindowStack"); - update_window_sources (); - } catch (IOError error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_window_stack_name_vanished (DBusConnection connection, string name) { - window_stack = null; - } - - [DBus (visible = false)] - private void handle_bus_acquired (DBusConnection connection, string name) { - try { - connection.export_action_group ("/org/ayatana/indicator/keyboard", get_action_group ()); - connection.export_menu_model ("/org/ayatana/indicator/keyboard/desktop", get_desktop_menu ()); - connection.export_menu_model ("/org/ayatana/indicator/keyboard/desktop_greeter", get_desktop_greeter_menu ()); - connection.export_menu_model ("/org/ayatana/indicator/keyboard/desktop_lockscreen", get_desktop_lockscreen_menu ()); - } catch (Error error) { - warning ("error: %s", error.message); - } - } - - [DBus (visible = false)] - private void handle_name_lost (DBusConnection? connection, string name) { - down (); - } - - [DBus (visible = false)] - public static int main (string[] args) { - Service.service = new Service (ref args); - - Posix.signal (Posix.SIGTERM, (code) => { - Service.service.down (); - }); - - Service.service.up (); - - return 0; - } -} diff --git a/lib/source.vala b/lib/source.vala deleted file mode 100644 index b7d7a971..00000000 --- a/lib/source.vala +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -public class Indicator.Keyboard.Source : Object { - - private static Gnome.XkbInfo? xkb_info; - private static IBus.Bus? ibus_bus; - private static Fcitx.InputMethod? fcitx_proxy; - - private string? xkb; - private string? ibus; - private string? fcitx; - - private string? _name; - private string? _short_name; - private string? _layout; - private string? _variant; - private Icon? _icon; - private uint _subscript; - private bool _show_subscript; - private bool _use_gtk; - - public string? name { - get { if (_name == null) { _name = _get_name (); } return _name; } - } - - public string? short_name { - get { if (_short_name == null) { _short_name = _get_short_name (); } return _short_name; } - } - - public string? layout { - get { if (_layout == null) { _layout = _get_layout (); } return _layout; } - } - - public string? variant { - get { if (_variant == null) { _variant = _get_variant (); } return _variant; } - } - - public Icon? icon { - get { if (_icon == null) { _icon = _get_icon (); } return _icon; } - set { _icon = value; } - } - - public uint subscript { - get { return _subscript; } - set { _subscript = value; icon = null; } - } - - public bool show_subscript { - get { return _show_subscript; } - set { _show_subscript = value; icon = null; } - } - - public bool use_gtk { - get { return _use_gtk; } - construct set { _use_gtk = value; icon = null; } - } - - public bool is_xkb { - get { return xkb != null; } - } - - public bool is_ibus { - get { return ibus != null; } - } - - public bool is_fcitx { - get { return fcitx != null; } - } - - public Source (Variant variant, bool use_gtk = false) { - Object (use_gtk: use_gtk); - - if (variant.is_of_type (new VariantType ("(ss)"))) { - unowned string type; - unowned string name; - - variant.get ("(&s&s)", out type, out name); - - if (type == "xkb") { - xkb = name; - } else if (type == "ibus") { - ibus = name; - } else if (type == "fcitx") { - fcitx = name; - } - } else if (variant.is_of_type (new VariantType ("a{ss}"))) { - foreach (var pair in variant) { - unowned string key; - unowned string value; - - ((!) pair).get ("{&s&s}", out key, out value); - - if (key == "xkb") { - xkb = value; - } else if (key == "ibus") { - ibus = value; - } else if (key == "fcitx") { - fcitx = value; - } - } - } - } - - private static Gnome.XkbInfo get_xkb_info () { - if (xkb_info == null) { - xkb_info = new Gnome.XkbInfo (); - } - - return (!) xkb_info; - } - - private static IBus.Bus get_ibus_bus () { - if (ibus_bus == null) { - IBus.init (); - ibus_bus = new IBus.Bus (); - } - - return (!) ibus_bus; - } - - private static Fcitx.InputMethod get_fcitx_proxy () throws Error { - if (fcitx_proxy == null) { - fcitx_proxy = new Fcitx.InputMethod (BusType.SESSION, DBusProxyFlags.NONE, 0); - } - - return (!) fcitx_proxy; - } - - private IBus.EngineDesc? get_engine () { - IBus.EngineDesc? engine = null; - - if (ibus != null) { - var names = new string[2]; - names[0] = (!) ibus; - - var engines = get_ibus_bus ().get_engines_by_names (names); - - if (engines.length > 0) { - engine = engines[0]; - } - } - - return engine; - } - - protected virtual string? _get_name () { - string? name = null; - - if (xkb != null) { - string? display_name = null; - string? layout = null; - - get_xkb_info ().get_layout_info ((!) xkb, out display_name, null, out layout, null); - - var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; - var has_layout = layout != null && ((!) layout).get_char () != '\0'; - - if (has_display_name) { - name = display_name; - } else if (has_layout) { - string? language = Xkl.get_language_name ((!) layout); - string? country = Xkl.get_country_name ((!) layout); - var has_language = language != null && ((!) language).get_char () != '\0'; - var has_country = country != null && ((!) country).get_char () != '\0'; - - if (has_language && has_country) { - name = @"$((!) language) ($((!) country))"; - } else if (has_language) { - name = language; - } else if (has_country) { - name = country; - } - } - - if (name == null || ((!) name).get_char () == '\0') { - name = xkb; - } - } else if (ibus != null) { - var engine = get_engine (); - - if (engine != null) { - string? language = ((!) engine).get_language (); - string? display_name = ((!) engine).get_longname (); - var has_language = language != null && ((!) language).get_char () != '\0'; - var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; - - if (has_language) { - language = Xkl.get_language_name ((!) language); - has_language = language != null && ((!) language).get_char () != '\0'; - } - - if (has_language && has_display_name) { - name = @"$((!) language) ($((!) display_name))"; - } else if (has_language) { - name = language; - } else if (has_display_name) { - name = display_name; - } - } - - if (name == null || ((!) name).get_char () == '\0') { - name = ibus; - } - } else if (fcitx != null) { - try { - var input_methods = get_fcitx_proxy ().get_imlist_nofree (); - - for (var i = 0; i < input_methods.length; i++) { - if (input_methods.get (i).unique_name == (!) fcitx) { - name = input_methods.get (i).name; - break; - } - } - } catch (Error error) { - warning ("error: %s", error.message); - } - - if (name == null || ((!) name).get_char () == '\0') { - name = fcitx; - } - } - - return name; - } - - protected virtual string? _get_short_name () { - string? short_name = null; - - if (xkb != null) { - get_xkb_info ().get_layout_info ((!) xkb, null, out short_name, null, null); - - if (short_name == null || ((!) short_name).get_char () == '\0') { - short_name = xkb; - } - } else if (ibus != null) { - var engine = get_engine (); - - if (engine != null) { - short_name = ((!) engine).get_name (); - } - - if (short_name == null || ((!) short_name).get_char () == '\0') { - short_name = ibus; - } - } else if (fcitx != null) { - try { - var input_methods = get_fcitx_proxy ().get_imlist_nofree (); - - for (var i = 0; i < input_methods.length; i++) { - if (input_methods.get (i).unique_name == (!) fcitx) { - short_name = input_methods.get (i).langcode; - break; - } - } - } catch (Error error) { - warning ("error: %s", error.message); - } - - if (short_name == null || ((!) short_name).get_char () == '\0') { - short_name = fcitx; - } - } - - return abbreviate (short_name); - } - - protected virtual string? _get_layout () { - string? layout = null; - - if (xkb != null) { - get_xkb_info ().get_layout_info ((!) xkb, null, null, out layout, null); - } - - var has_layout = layout != null && ((!) layout).get_char () != '\0'; - - if (!has_layout) { - var engine = get_engine (); - - if (engine != null) { - layout = ((!) engine).get_layout (); - } - } - - if (layout == null || ((!) layout).get_char () == '\0') { - layout = xkb; - } - - return layout; - } - - protected virtual string? _get_variant () { - string? variant = null; - - if (xkb != null) { - get_xkb_info ().get_layout_info ((!) xkb, null, null, null, out variant); - } - - var has_variant = variant != null && ((!) variant).get_char () != '\0'; - - if (!has_variant) { - var engine = get_engine (); - - if (engine != null) { - variant = ((!) engine).get_layout_variant (); - } - } - - if (variant == null || ((!) variant).get_char () == '\0') { - variant = null; - } - - return variant; - } - - private Gtk.StyleContext? get_style_context () { - Gtk.StyleContext? context = null; - - if (_use_gtk) { - Gdk.Screen? screen = Gdk.Screen.get_default (); - - if (screen != null) { - var style_context = new Gtk.StyleContext (); - style_context.set_screen ((!) screen); - - var path = new Gtk.WidgetPath (); - path.append_type (typeof (Gtk.MenuItem)); - style_context.set_path (path); - - context = style_context; - } - } - - return context; - } - - protected virtual Icon? create_icon () { - Icon? icon = null; - - var style = get_style_context (); - - if (style != null) { - const int W = 22; - const int H = 22; - const int w = 20; - const int h = 20; - const double R = 2.0; - const double TEXT_SIZE = 12.0; - const double SUBSCRIPT_SIZE = 8.0; - - Pango.FontDescription description; - var colour = ((!) style).get_color (Gtk.StateFlags.NORMAL); - colour = { 0.5, 0.5, 0.5, 1.0 }; - ((!) style).get (Gtk.StateFlags.NORMAL, Gtk.STYLE_PROPERTY_FONT, out description); - - var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, W, H); - var context = new Cairo.Context (surface); - - context.translate (0.5 * (W - w), 0.5 * (H - h)); - - context.new_sub_path (); - context.arc (R, R, R, Math.PI, -0.5 * Math.PI); - context.arc (w - R, R, R, -0.5 * Math.PI, 0); - context.arc (w - R, h - R, R, 0, 0.5 * Math.PI); - context.arc (R, h - R, R, 0.5 * Math.PI, Math.PI); - context.close_path (); - - context.set_source_rgba (colour.red, colour.green, colour.blue, colour.alpha); - context.fill (); - context.set_operator (Cairo.Operator.CLEAR); - - if (short_name != null) { - var text_layout = Pango.cairo_create_layout (context); - text_layout.set_alignment (Pango.Alignment.CENTER); - description.set_absolute_size (Pango.units_from_double (TEXT_SIZE)); - text_layout.set_font_description (description); - text_layout.set_text ((!) short_name, -1); - Pango.cairo_update_layout (context, text_layout); - int text_width; - int text_height; - text_layout.get_pixel_size (out text_width, out text_height); - - if (_show_subscript) { - var subscript_layout = Pango.cairo_create_layout (context); - subscript_layout.set_alignment (Pango.Alignment.CENTER); - description.set_absolute_size (Pango.units_from_double (SUBSCRIPT_SIZE)); - subscript_layout.set_font_description (description); - subscript_layout.set_text (@"$_subscript", -1); - Pango.cairo_update_layout (context, subscript_layout); - int subscript_width; - int subscript_height; - subscript_layout.get_pixel_size (out subscript_width, out subscript_height); - - context.save (); - context.translate ((w - (text_width + subscript_width)) / 2, (h - text_height) / 2); - Pango.cairo_layout_path (context, text_layout); - context.fill (); - context.restore (); - - context.save (); - context.translate ((w + (text_width - subscript_width)) / 2, (h + text_height) / 2 - subscript_height); - Pango.cairo_layout_path (context, subscript_layout); - context.fill (); - context.restore (); - } else { - context.save (); - context.translate ((w - text_width) / 2, (h - text_height) / 2); - Pango.cairo_layout_path (context, text_layout); - context.fill (); - context.restore (); - } - } - - var buffer = new ByteArray (); - - surface.write_to_png_stream ((data) => { - buffer.append (data); - return Cairo.Status.SUCCESS; - }); - - icon = new BytesIcon (ByteArray.free_to_bytes ((owned) buffer)); - } - - return icon; - } - - private Icon? _get_icon () { - Icon? icon = null; - - var engine = get_engine (); - - if (engine != null) { - string? icon_name = ((!) engine).get_icon (); - var has_icon_name = icon_name != null && ((!) icon_name).get_char () != '\0'; - - if (has_icon_name) { - try { - icon = Icon.new_for_string ((!) icon_name); - } catch (Error error) { - warning ("error: %s", error.message); - } - } - } - - if (icon == null && short_name != null) { - string icon_name; - - if (_show_subscript) { - icon_name = @"indicator-keyboard-$((!) short_name)-$_subscript"; - } else { - icon_name = @"indicator-keyboard-$((!) short_name)"; - } - - if (_use_gtk) { - var icon_theme = Gtk.IconTheme.get_default (); - Gtk.IconInfo? icon_info = icon_theme.lookup_icon (icon_name, 22, 0); - - if (icon_info != null) { - icon = new ThemedIcon (icon_name); - } - } else { - icon = new ThemedIcon (icon_name); - } - } - - if (icon == null) { - icon = create_icon (); - } - - return icon; - } -} diff --git a/lib/unity-greeter.vala b/lib/unity-greeter.vala deleted file mode 100644 index 5ca398ec..00000000 --- a/lib/unity-greeter.vala +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -[DBus (name="org.ayatana.UnityGreeter.List")] -public interface UnityGreeter : Object { - - public abstract string get_active_entry () throws IOError; - public abstract void set_active_entry (string entry_name) throws IOError; - - public signal void entry_selected (string entry_name); -} diff --git a/lib/unity-session.vala b/lib/unity-session.vala deleted file mode 100644 index 15337c36..00000000 --- a/lib/unity-session.vala +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -[DBus (name="org.ayatana.Unity.Session")] -public interface UnitySession : Object { - - public signal void locked (); - public signal void unlocked (); -} diff --git a/lib/window-stack.vala b/lib/window-stack.vala deleted file mode 100644 index a943da6a..00000000 --- a/lib/window-stack.vala +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Authors: William Hua - */ - -public struct WindowInfo { - - public uint window_id; - public string app_id; - public bool focused; - public uint stage; -} - -[DBus (name="org.ayatana.Unity.WindowStack")] -public interface WindowStack : Object { - - public abstract string get_app_id_from_pid (uint pid) throws IOError; - public abstract string[] get_window_properties (uint window_id, string app_id, string[] property_names) throws IOError; - public abstract WindowInfo[] get_window_stack () throws IOError; - - public signal void focused_window_changed (uint window_id, string app_id, uint stage); - public signal void window_created (uint window_id, string app_id); - public signal void window_destroyed (uint window_id, string app_id); -} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..a0fc4bb5 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,55 @@ +pkglibexec_PROGRAMS = ayatana-indicator-keyboard-service + +AM_CFLAGS = -w -DGNOME_DESKTOP_USE_UNSTABLE_API +AM_LDFLAGS = -lm +AM_VALAFLAGS = --enable-experimental-non-null \ + --metadatadir $(top_srcdir)/deps \ + --vapidir $(top_srcdir)/deps + +ayatana_indicator_keyboard_service_SOURCES = main.vala \ + source.vala \ + common.vala \ + ibus-menu.vala \ + ibus-panel.vala \ + indicator-menu.vala \ + keyboard-plugin.vala \ + window-stack.vala \ + unity-session.vala \ + unity-greeter.vala +ayatana_indicator_keyboard_service_VALAFLAGS = $(AM_VALAFLAGS) \ + --pkg gee-1.0 \ + --pkg posix \ + --pkg pangocairo \ + --pkg gtk+-3.0 \ + --pkg GDesktopEnums-3.0 \ + --pkg GnomeDesktop-3.0 \ + --pkg Xkl-1.0 \ + --pkg Gkbd-3.0 \ + --pkg ibus-1.0 \ + --pkg Fcitx-1.0 \ + --pkg AccountsService-1.0 \ + --pkg liblightdm-gobject-1 +ayatana_indicator_keyboard_service_CFLAGS = $(AM_CFLAGS) \ + $(GEE_CFLAGS) \ + $(PANGOCAIRO_CFLAGS) \ + $(GTK_CFLAGS) \ + $(GNOME_DESKTOP_CFLAGS) \ + $(LIBXKLAVIER_CFLAGS) \ + $(LIBGNOMEKBD_CFLAGS) \ + $(IBUS_CFLAGS) \ + $(FCITX_GCLIENT_CFLAGS) \ + $(ACCOUNTSSERVICE_CFLAGS) \ + $(LIGHTDM_CFLAGS) \ + $(COVERAGE_CFLAGS) +ayatana_indicator_keyboard_service_LDFLAGS = $(AM_LDFLAGS) \ + $(GEE_LIBS) \ + $(PANGOCAIRO_LIBS) \ + $(GTK_LIBS) \ + $(GNOME_DESKTOP_LIBS) \ + $(LIBXKLAVIER_LIBS) \ + $(LIBGNOMEKBD_LIBS) \ + $(IBUS_LIBS) \ + $(FCITX_GCLIENT_LIBS) \ + $(ACCOUNTSSERVICE_LIBS) \ + $(LIGHTDM_LIBS) \ + $(COVERAGE_LDFLAGS) diff --git a/src/common.vala b/src/common.vala new file mode 100644 index 00000000..9824bc26 --- /dev/null +++ b/src/common.vala @@ -0,0 +1,37 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +string? abbreviate (string? name) { + var index = 0; + unichar first; + unichar second; + + if (name != null) { + if (((!) name).get_next_char (ref index, out first)) { + if (((!) name).get_next_char (ref index, out second)) { + return @"$((!) first.toupper ().to_string ())$((!) second.to_string ())"; + } else { + return first.toupper ().to_string (); + } + } else { + return ""; + } + } else { + return null; + } +} diff --git a/src/ibus-menu.vala b/src/ibus-menu.vala new file mode 100644 index 00000000..a240f00b --- /dev/null +++ b/src/ibus-menu.vala @@ -0,0 +1,313 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +public class Indicator.Keyboard.IBusMenu : MenuModel { + + private static uint radio_counter = 0; + + private IBus.PropList? properties; + + private Menu menu; + private ActionMap? action_map; + + private string? radio_name; + private SimpleAction? radio_action; + private Gee.HashMap radio_properties; + + /* A list of the action names this menu registers. */ + private Gee.LinkedList names; + + public IBusMenu (ActionMap? action_map = null, IBus.PropList? properties = null) { + menu = new Menu (); + + menu.items_changed.connect ((position, removed, added) => { + items_changed (position, removed, added); + }); + + names = new Gee.LinkedList (); + set_action_map (action_map); + set_properties (properties); + } + + ~IBusMenu () { + remove_actions (); + } + + public signal void activate (IBus.Property property, IBus.PropState state); + + private string get_action_name (string key) { + string name; + + if (!action_name_is_valid (key)) { + var builder = new StringBuilder.sized (key.length + 1); + + unichar letter = 0; + int index = 0; + + while (key.get_next_char (ref index, out letter)) { + if (letter == '-' || letter == '.' || letter.isalnum ()) { + builder.append_unichar (letter); + } else { + builder.append_c ('-'); + } + } + + name = @"ibus-$(builder.str)"; + } else { + name = @"ibus-$key"; + } + + /* Find an unused action name using a counter. */ + if (action_map != null && (Action?) ((!) action_map).lookup_action (name) != null) { + var i = 0; + var unique_name = @"$name-$i"; + + while ((Action?) ((!) action_map).lookup_action (unique_name) != null) { + i++; + unique_name = @"$name-$i"; + } + + name = unique_name; + } + + return name; + } + + private string? get_label (IBus.Property property) { + string? label = null; + + if ((IBus.Text?) property.label != null) { + label = property.label.text; + } + + if (label == null && (IBus.Text?) property.symbol != null) { + label = property.symbol.text; + } + + return label; + } + + private void append_normal_property (IBus.Property property) { + if (property.prop_type == IBus.PropType.NORMAL) { + if ((string?) property.key != null) { + var name = get_action_name (property.key); + + if (action_map != null) { + var action = new SimpleAction (name, null); + action.activate.connect ((parameter) => { activate (property, property.state); }); + ((!) action_map).add_action (action); + names.add (name); + } + + menu.append (get_label (property), property.sensitive ? @"indicator.$name" : "-private-disabled"); + } + } + } + + private void append_toggle_property (IBus.Property property) { + if (property.prop_type == IBus.PropType.TOGGLE) { + if ((string?) property.key != null) { + var name = get_action_name (property.key); + + if (action_map != null) { + var state = new Variant.boolean (property.state == IBus.PropState.CHECKED); + var action = new SimpleAction.stateful (name, null, state); + + action.change_state.connect ((value) => { + if (value != null) { + action.set_state ((!) value); + activate (property, ((!) value).get_boolean () ? IBus.PropState.CHECKED : IBus.PropState.UNCHECKED); + } + }); + + ((!) action_map).add_action (action); + names.add (name); + } + + menu.append (get_label (property), property.sensitive ? @"indicator.$name" : "-private-disabled"); + } + } + } + + private void append_radio_property (IBus.Property property) { + if (property.prop_type == IBus.PropType.RADIO) { + if ((string?) property.key != null) { + /* Create a single action for all radio properties. */ + if (action_map != null && radio_name == null) { + radio_counter++; + + var name = @"-private-radio-$radio_counter"; + var action = new SimpleAction.stateful (name, VariantType.STRING, new Variant.string ("")); + + action.change_state.connect ((value) => { + if (value != null) { + var key = ((!) value).get_string (); + + if (radio_properties.has_key (key)) { + action.set_state ((!) value); + activate (radio_properties[key], IBus.PropState.CHECKED); + } + } + }); + + ((!) action_map).add_action (action); + names.add (name); + + radio_name = name; + radio_action = action; + } + + radio_properties[property.key] = property; + + if (property.state == IBus.PropState.CHECKED) { + ((!) radio_action).change_state (new Variant.string (property.key)); + } + + var item = new MenuItem (get_label (property), "-private-disabled"); + + if (property.sensitive) { + item.set_action_and_target_value (@"indicator.$((!) radio_name)", new Variant.string (property.key)); + } + + menu.append_item (item); + } + } + } + + private void append_menu_property (IBus.Property property) { + if (property.prop_type == IBus.PropType.MENU) { + var submenu = new IBusMenu (action_map, property.sub_props); + submenu.activate.connect ((property, state) => { activate (property, state); }); + menu.append_submenu (get_label (property), submenu); + } + } + + private void append_property (IBus.Property? property) { + if (property != null && ((!) property).visible) { + switch (((!) property).prop_type) { + case IBus.PropType.NORMAL: + append_normal_property ((!) property); + break; + + case IBus.PropType.TOGGLE: + append_toggle_property ((!) property); + break; + + case IBus.PropType.RADIO: + append_radio_property ((!) property); + break; + + case IBus.PropType.MENU: + append_menu_property ((!) property); + break; + + case IBus.PropType.SEPARATOR: + break; + } + } + } + + private void update_menu () { + /* Break reference cycle between action map and submenus. */ + for (var i = 0; i < menu.get_n_items (); i++) { + var submenu = menu.get_item_link (i, Menu.LINK_SUBMENU) as IBusMenu; + + if (submenu != null) { + ((!) submenu).remove_actions (); + } + } + + menu.remove_all (); + + if (properties != null) { + for (var i = 0; i < ((!) properties).properties.length; i++) { + append_property (((!) properties).get (i)); + } + } + } + + private void remove_actions () { + radio_action = null; + radio_name = null; + + if (action_map != null) { + foreach (var name in names) { + ((!) action_map).remove_action (name); + } + } + + names.clear (); + } + + public void set_action_map (ActionMap? action_map) { + if (action_map != this.action_map) { + remove_actions (); + this.action_map = action_map; + update_menu (); + } + } + + public void set_properties (IBus.PropList? properties) { + if (properties != this.properties) { + remove_actions (); + radio_properties = new Gee.HashMap (); + this.properties = properties; + update_menu (); + } + } + + public void update_property (IBus.Property property) { + remove_actions (); + radio_properties = new Gee.HashMap (); + update_menu (); + } + + /* Forward all menu model calls to our internal menu. */ + + public override Variant get_item_attribute_value (int item_index, string attribute, VariantType? expected_type) { + return menu.get_item_attribute_value (item_index, attribute, expected_type); + } + + public override void get_item_attributes (int item_index, out HashTable? attributes) { + menu.get_item_attributes (item_index, out attributes); + } + + public override MenuModel get_item_link (int item_index, string link) { + return menu.get_item_link (item_index, link); + } + + public override void get_item_links (int item_index, out HashTable links) { + menu.get_item_links (item_index, out links); + } + + public override int get_n_items () { + return menu.get_n_items (); + } + + public override bool is_mutable () { + return menu.is_mutable (); + } + + public override MenuAttributeIter iterate_item_attributes (int item_index) { + return menu.iterate_item_attributes (item_index); + } + + public override MenuLinkIter iterate_item_links (int item_index) { + return menu.iterate_item_links (item_index); + } +} diff --git a/src/ibus-panel.vala b/src/ibus-panel.vala new file mode 100644 index 00000000..2a380efd --- /dev/null +++ b/src/ibus-panel.vala @@ -0,0 +1,26 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +[DBus (name="org.ayatana.IBus.Panel.Private")] +public interface IBusPanel : Object { + + public abstract void activate_property (string name, uint state) throws IOError; + + public signal void properties_registered (Variant variant); + public signal void property_updated (Variant variant); +} diff --git a/src/indicator-menu.vala b/src/indicator-menu.vala new file mode 100644 index 00000000..2cfa52c6 --- /dev/null +++ b/src/indicator-menu.vala @@ -0,0 +1,151 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +public class Indicator.Keyboard.IndicatorMenu : MenuModel { + + public enum Options { + NONE = 0, + DCONF = 1 << 0, + XKB = 1 << 1, + IBUS = 1 << 2, + SETTINGS = 1 << 3 + } + + private Options options; + + private Menu indicator_menu; + private Menu sources_section; + private IBusMenu properties_section; + + public IndicatorMenu (ActionMap? action_map = null, Options options = Options.NONE) { + this.options = options; + + indicator_menu = new Menu (); + sources_section = new Menu (); + + if ((options & ~Options.DCONF) != Options.NONE) { + var submenu = new Menu (); + + submenu.append_section (null, sources_section); + + if (Options.IBUS in options) { + properties_section = new IBusMenu (action_map); + properties_section.activate.connect ((property, state) => { activate (property, state); }); + submenu.append_section (null, properties_section); + } + + if (Options.SETTINGS in options) { + var settings_section = new Menu (); + settings_section.append (_ ("Character Map"), "indicator.map"); + settings_section.append (_ ("Keyboard Layout Chart"), "indicator.chart"); + settings_section.append (_ ("Text Entry Settings..."), "indicator.settings"); + submenu.append_section (null, settings_section); + } + + var indicator = new MenuItem.submenu (null, submenu); + indicator.set_detailed_action ("indicator.indicator"); + indicator.set_attribute ("x-canonical-type", "s", "org.ayatana.indicator.root"); + + /* We need special mouse actions on the lock screen. */ + if (Options.DCONF in options) { + indicator.set_attribute ("x-canonical-secondary-action", "s", "indicator.next"); + indicator.set_attribute ("x-canonical-scroll-action", "s", "indicator.scroll"); + } else { + indicator.set_attribute ("x-canonical-secondary-action", "s", "indicator.locked_next"); + indicator.set_attribute ("x-canonical-scroll-action", "s", "indicator.locked_scroll"); + } + + indicator_menu.append_item (indicator); + } + } + + public signal void activate (IBus.Property property, IBus.PropState state); + + public void set_sources (Source[] sources) { + sources_section.remove_all (); + + for (var i = 0; i < sources.length; i++) { + var visible = (sources[i].is_xkb && Options.XKB in options) || + (sources[i].is_ibus && Options.IBUS in options); + + if (visible) { + string action; + + if (Options.DCONF in options) { + action = "indicator.current"; + } else { + action = "indicator.active"; + } + + var item = new MenuItem (sources[i].name, action); + + item.set_attribute (Menu.ATTRIBUTE_TARGET, "u", i); + + if (sources[i].icon != null) { + item.set_icon ((!) sources[i].icon); + } + + sources_section.append_item (item); + } + } + } + + public void set_properties (IBus.PropList properties) { + if (Options.IBUS in options) { + properties_section.set_properties (properties); + } + } + + public void update_property (IBus.Property property) { + if (Options.IBUS in options) { + properties_section.update_property (property); + } + } + + public override bool is_mutable () { + return indicator_menu.is_mutable (); + } + + public override int get_n_items () { + return indicator_menu.get_n_items (); + } + + public override void get_item_attributes (int item_index, out HashTable? attributes) { + indicator_menu.get_item_attributes (item_index, out attributes); + } + + public override void get_item_links (int item_index, out HashTable links) { + indicator_menu.get_item_links (item_index, out links); + } + + public override Variant get_item_attribute_value (int item_index, string attribute, VariantType? expected_type) { + return indicator_menu.get_item_attribute_value (item_index, attribute, expected_type); + } + + public override MenuModel get_item_link (int item_index, string link) { + return indicator_menu.get_item_link (item_index, link); + } + + public override MenuAttributeIter iterate_item_attributes (int item_index) { + return indicator_menu.iterate_item_attributes (item_index); + } + + public override MenuLinkIter iterate_item_links (int item_index) { + return indicator_menu.iterate_item_links (item_index); + } +} diff --git a/src/keyboard-plugin.vala b/src/keyboard-plugin.vala new file mode 100644 index 00000000..af1628f6 --- /dev/null +++ b/src/keyboard-plugin.vala @@ -0,0 +1,23 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +[DBus (name="org.ayatana.SettingsDaemon.Keyboard.Private")] +public interface KeyboardPlugin : Object { + + public abstract void activate_input_source (uint index) throws IOError; +} diff --git a/src/main.vala b/src/main.vala new file mode 100644 index 00000000..9bb3eb1f --- /dev/null +++ b/src/main.vala @@ -0,0 +1,1265 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +[DBus (name = "org.ayatana.indicator.keyboard")] +public class Indicator.Keyboard.Service : Object { + + private static const uint PROPERTIES_DELAY = 250; + + private static Service service; + + private bool force; + private bool use_gtk; + + private MainLoop? loop; + private Settings indicator_settings; + private Settings source_settings; + private Settings per_window_settings; + private SList users; + + private WindowStack? window_stack; + private Gee.HashMap? window_sources; + private uint focused_window_id; + + private IBus.Bus? ibus; + private IBusPanel? ibus_panel; + private ulong ibus_connected_id; + private uint panel_timeout; + + private Fcitx.InputMethod? fcitx; + private bool fcitx_initialized; + + private Source[]? sources; + + private SimpleActionGroup? action_group; + private SimpleAction? indicator_action; + private SimpleAction? active_action; + private IndicatorMenu? desktop_menu; + private IndicatorMenu? desktop_greeter_menu; + private IndicatorMenu? desktop_lockscreen_menu; + + private KeyboardPlugin? keyboard_plugin; + private UnitySession? unity_session; + private UnityGreeter? unity_greeter; + private string? greeter_user; + private uint lightdm_current; + + [DBus (visible = false)] + public Service (ref unowned string[] args) { + force = "--force" in args; + use_gtk = "--use-gtk" in args; + + if (use_gtk) { + use_gtk = Gtk.init_check (ref args); + + Gtk.IconTheme? icon_theme = Gtk.IconTheme.get_default (); + + if (icon_theme != null) { + ((!) icon_theme).changed.connect (() => { + if (sources != null) { + foreach (var source in (!) sources) { + source.icon = null; + } + } + + if (desktop_menu != null) { + get_desktop_menu ().set_sources (get_sources ()); + } + + if (desktop_greeter_menu != null) { + get_desktop_greeter_menu ().set_sources (get_sources ()); + } + + if (desktop_lockscreen_menu != null) { + get_desktop_lockscreen_menu ().set_sources (get_sources ()); + } + + if (indicator_action != null) { + update_indicator_action (); + } + }); + } + } else { + Gdk.init (ref args); + } + + if (is_login_user ()) { + var name = Environment.get_variable ("UNITY_GREETER_DBUS_NAME"); + + if (name != null) { + Bus.watch_name (BusType.SESSION, + (!) name, + BusNameWatcherFlags.NONE, + handle_unity_greeter_name_appeared, + handle_unity_greeter_name_vanished); + } + } else { + Bus.watch_name (BusType.SESSION, + "org.gnome.SettingsDaemon.Keyboard", + BusNameWatcherFlags.NONE, + handle_keyboard_name_appeared, + handle_keyboard_name_vanished); + + Bus.watch_name (BusType.SESSION, + "org.ayatana.Unity", + BusNameWatcherFlags.NONE, + handle_unity_name_appeared, + handle_unity_name_vanished); + + if (!is_fcitx_active ()) { + Bus.watch_name (BusType.SESSION, + "org.ayatana.Unity.WindowStack", + BusNameWatcherFlags.NONE, + handle_window_stack_name_appeared, + handle_window_stack_name_vanished); + } + } + + indicator_settings = new Settings ("org.ayatana.indicator.keyboard"); + indicator_settings.changed["visible"].connect (handle_changed_visible); + + source_settings = new Settings ("org.gnome.desktop.input-sources"); + source_settings.changed["current"].connect (handle_changed_current); + source_settings.changed["sources"].connect (handle_changed_sources); + + per_window_settings = new Settings ("org.gnome.libgnomekbd.desktop"); + per_window_settings.changed["group-per-window"].connect (handle_changed_group_per_window); + + migrate_keyboard_layouts (); + update_window_sources (); + acquire_bus_name (); + } + + [DBus (visible = false)] + private static bool is_login_user () { + return Environment.get_user_name () == "lightdm"; + } + + [DBus (visible = false)] + private static bool is_ibus_active () { + if (is_login_user ()) { + return false; + } + + var module = Environment.get_variable ("GTK_IM_MODULE"); + return module != null && (!) module == "ibus"; + } + + [DBus (visible = false)] + private static bool is_fcitx_active () { + if (is_login_user ()) { + return false; + } + + var module = Environment.get_variable ("GTK_IM_MODULE"); + return module != null && (!) module == "fcitx"; + } + + [DBus (visible = false)] + private IBus.Bus get_ibus () { + if (ibus == null) { + IBus.init (); + + var proxy = new IBus.Bus (); + + proxy.connected.connect (() => { + if (desktop_menu != null) { + get_desktop_menu ().set_sources (get_sources ()); + } + + if (desktop_greeter_menu != null) { + get_desktop_greeter_menu ().set_sources (get_sources ()); + } + + if (desktop_lockscreen_menu != null) { + get_desktop_lockscreen_menu ().set_sources (get_sources ()); + } + + if (indicator_action != null) { + update_indicator_action (); + } + }); + + ibus = proxy; + } + + return (!) ibus; + } + + [DBus (visible = false)] + private IBusPanel? get_ibus_panel () { + if (ibus_panel == null && get_ibus ().is_connected ()) { + var connection = get_ibus ().get_connection (); + var name = "org.freedesktop.IBus.Panel"; + var path = "/org/freedesktop/IBus/Panel"; + + try { + var proxy = connection.get_proxy_sync (name, path); + + proxy.properties_registered.connect ((variant) => { + var properties = new IBus.PropList (); + properties.deserialize (variant); + + if (properties is IBus.PropList) { + handle_properties_registered ((!) (properties as IBus.PropList)); + } + }); + proxy.property_updated.connect ((variant) => { + var type = IBus.PropType.NORMAL; + var state = IBus.PropState.INCONSISTENT; + var text = new IBus.Text.from_static_string (""); + var property = new IBus.Property ("", type, text, null, text, false, false, state, null); + property.deserialize (variant); + + if (property is IBus.Property) { + handle_property_updated ((!) (property as IBus.Property)); + } + }); + + ibus_panel = proxy; + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + + return ibus_panel; + } + + [DBus (visible = false)] + private Fcitx.InputMethod? get_fcitx () { + if (!fcitx_initialized) { + fcitx_initialized = true; + + if (is_fcitx_active ()) { + try { + var proxy = new Fcitx.InputMethod (BusType.SESSION, DBusProxyFlags.NONE, 0); + proxy.notify["current-im"].connect ((pspec) => { handle_changed_current ("current"); }); + fcitx = proxy; + } catch (Error error) { + warning ("error: %s", error.message); + } + } + } + + return fcitx; + } + + [DBus (visible = false)] + public void up () { + if (loop == null) { + var main_loop = new MainLoop (); + loop = main_loop; + main_loop.run (); + } + } + + [DBus (visible = false)] + public void down () { + if (loop != null) { + ((!) loop).quit (); + loop = null; + } + } + + [DBus (visible = false)] + private void acquire_bus_name () { + Bus.own_name (BusType.SESSION, + "org.ayatana.indicator.keyboard", + BusNameOwnerFlags.ALLOW_REPLACEMENT | (force ? BusNameOwnerFlags.REPLACE : 0), + handle_bus_acquired, + null, + handle_name_lost); + } + + [DBus (visible = false)] + private void update_greeter_user () { + if (greeter_user == null && unity_greeter != null) { + try { + greeter_user = ((!) unity_greeter).get_active_entry (); + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + + string? source = null; + + if (greeter_user != null) { + var manager = Act.UserManager.get_default (); + + if (manager.is_loaded) { + Act.User? user = manager.get_user ((!) greeter_user); + + if (user != null && ((!) user).is_loaded) { + foreach (var outer in ((!) user).input_sources) { + foreach (var inner in (!) outer) { + unowned string key; + unowned string value; + + ((!) inner).get ("{&s&s}", out key, out value); + + if (key == "xkb") { + source = value; + break; + } + } + + if (source != null) { + break; + } + } + + if (source == null) { + var layouts = ((!) user).xkeyboard_layouts; + + if (layouts.length <= 0) { + var user_list = LightDM.UserList.get_instance (); + LightDM.User? light_user = user_list.get_user_by_name ((!) greeter_user); + + if (light_user != null) { + layouts = ((!) light_user).get_layouts (); + } + } + + if (layouts.length > 0) { + source = layouts[0].replace (" ", "+").replace ("\t", "+"); + } + } + } + } + } + + if (source == null) { + LightDM.Layout? layout = LightDM.get_layout (); + + if (layout != null) { + source = ((!) layout).name; + + if (source != null) { + source = ((!) source).replace (" ", "+"); + source = ((!) source).replace ("\t", "+"); + } + } + } + + if (source != null) { + var array = source_settings.get_value ("sources"); + + for (var i = 0; i < array.n_children (); i++) { + unowned string type; + unowned string name; + + array.get_child (i, "(&s&s)", out type, out name); + + if (type == "xkb" && name == (!) source) { + source_settings.set_uint ("current", i); + break; + } + } + } + } + + [DBus (visible = false)] + private void handle_entry_selected (string entry_name) { + if (greeter_user == null || entry_name != (!) greeter_user) { + greeter_user = entry_name; + + update_greeter_user (); + } + } + + [DBus (visible = false)] + private void migrate_keyboard_layouts () { + if (is_login_user ()) { + lightdm_current = get_current (); + + var manager = Act.UserManager.get_default (); + + if (manager.is_loaded) { + users = manager.list_users (); + + foreach (var user in users) { + if (user.is_loaded) { + migrate_input_sources (); + } else { + user.notify["is-loaded"].connect ((pspec) => { + if (user.is_loaded) { + migrate_input_sources (); + } + }); + } + } + } else { + manager.notify["is-loaded"].connect ((pspec) => { + if (manager.is_loaded) { + users = manager.list_users (); + + foreach (var user in users) { + if (user.is_loaded) { + migrate_input_sources (); + } else { + user.notify["is-loaded"].connect ((pspec) => { + if (user.is_loaded) { + migrate_input_sources (); + } + }); + } + } + } + }); + } + + var user_list = LightDM.UserList.get_instance (); + + user_list.user_added.connect ((user) => { migrate_input_sources (); }); + user_list.user_changed.connect ((user) => { migrate_input_sources (); }); + user_list.user_removed.connect ((user) => { migrate_input_sources (); }); + + /* Force the loading of the user list. */ + user_list.get_user_by_name (""); + } else { + if (!indicator_settings.get_boolean ("migrated")) { + var builder = new VariantBuilder (new VariantType ("a(ss)")); + var length = 0; + + var layout_settings = new Settings ("org.gnome.libgnomekbd.keyboard"); + var layouts = layout_settings.get_strv ("layouts"); + + foreach (var layout in layouts) { + var source = layout; + source = source.replace (" ", "+"); + source = source.replace ("\t", "+"); + + builder.add ("(ss)", "xkb", source); + length++; + } + + var engines = get_ibus ().list_active_engines (); + + foreach (var engine in engines) { + if (length == 0 || engine.name.has_prefix ("xkb")) { + var source = "us"; + string? layout = engine.get_layout (); + string? variant = engine.get_layout_variant (); + + if (layout != null && ((!) layout).length == 0) { + layout = null; + } + + if (variant != null && ((!) variant).length == 0) { + variant = null; + } + + if (layout != null && variant != null) { + source = @"$((!) layout)+$((!) variant)"; + } else if (layout != null) { + source = (!) layout; + } + + builder.add ("(ss)", "xkb", source); + length++; + } + + if (!engine.name.has_prefix ("xkb")) { + builder.add ("(ss)", "ibus", engine.name); + length++; + } + } + + source_settings.set_value ("sources", builder.end ()); + indicator_settings.set_boolean ("migrated", true); + } + } + } + + [DBus (visible = false)] + private void migrate_input_sources () { + var list = new Gee.LinkedList (); + var added = new Gee.HashSet (); + + foreach (var user in users) { + if (user.is_loaded) { + var done = false; + + foreach (var outer in user.input_sources) { + foreach (var inner in (!) outer) { + unowned string key; + unowned string source; + + ((!) inner).get ("{&s&s}", out key, out source); + + if (key == "xkb") { + done = true; + + if (!added.contains (source)) { + list.add (source); + added.add (source); + } + } + } + } + + if (!done) { + var layouts = user.xkeyboard_layouts; + foreach (var layout in layouts) { + done = true; + + var source = layout; + source = source.replace (" ", "+"); + source = source.replace ("\t", "+"); + + if (!added.contains (source)) { + list.add (source); + added.add (source); + } + } + } + + if (!done) { + var user_list = LightDM.UserList.get_instance (); + LightDM.User? light_user = user_list.get_user_by_name (user.user_name); + + if (light_user != null) { + var layouts = ((!) light_user).get_layouts (); + foreach (var layout in layouts) { + done = true; + + var source = layout; + source = source.replace (" ", "+"); + source = source.replace ("\t", "+"); + + if (!added.contains (source)) { + list.add (source); + added.add (source); + } + } + } + } + } + } + + LightDM.Layout? layout = LightDM.get_layout (); + + if (layout != null) { + string? source = ((!) layout).name; + + if (source != null) { + source = ((!) source).replace (" ", "+"); + source = ((!) source).replace ("\t", "+"); + + if (!added.contains ((!) source)) { + list.add ((!) source); + added.add ((!) source); + } + } + } + + var builder = new VariantBuilder (new VariantType ("a(ss)")); + + foreach (var name in list) { + builder.add ("(ss)", "xkb", name); + } + + if (lightdm_current < list.size) { + source_settings.set_uint ("current", lightdm_current); + } else { + source_settings.set_uint ("current", list.size - 1); + } + + source_settings.set_value ("sources", builder.end ()); + + update_greeter_user (); + } + + [DBus (visible = false)] + private void update_login_layout () { + if (is_login_user ()) { + unowned List layouts = LightDM.get_layouts (); + var current = get_current (); + + if (current < get_sources ().length) { + var source = get_sources ()[current]; + string? name = null; + + if (source.layout != null && source.variant != null) { + name = @"$((!) source.layout)\t$((!) source.variant)"; + } else if (source.layout != null) { + name = source.layout; + } + + if (name != null) { + foreach (var layout in layouts) { + if (layout.name == (!) name) { + LightDM.set_layout (layout); + break; + } + } + } + } + } + } + + [DBus (visible = false)] + private void update_window_sources () { + if (window_stack != null) { + var group_per_window = per_window_settings.get_boolean ("group-per-window"); + + if (group_per_window != (window_sources != null)) { + if (group_per_window) { + focused_window_id = 0; + + try { + var windows = ((!) window_stack).get_window_stack (); + + foreach (var window in windows) { + if (window.focused) { + focused_window_id = window.window_id; + break; + } + } + } catch (IOError error) { + warning ("error: %s", error.message); + } + + window_sources = new Gee.HashMap (); + ((!) window_stack).window_destroyed.connect (handle_window_destroyed); + ((!) window_stack).focused_window_changed.connect (handle_focused_window_changed); + } else { + ((!) window_stack).focused_window_changed.disconnect (handle_focused_window_changed); + ((!) window_stack).window_destroyed.disconnect (handle_window_destroyed); + window_sources = null; + } + } + } + } + + [DBus (visible = false)] + private void handle_changed_group_per_window (string key) { + update_window_sources (); + } + + [DBus (visible = false)] + private void handle_window_destroyed (uint window_id, string app_id) { + ((!) window_sources).unset (window_id); + } + + [DBus (visible = false)] + private void handle_focused_window_changed (uint window_id, string app_id, uint stage) { + var sources = get_sources (); + var old_current = get_current (); + + if (old_current < sources.length) { + ((!) window_sources)[focused_window_id] = sources[old_current]; + } + + if (!(((!) window_sources).has_key (window_id))) { + var default_group = per_window_settings.get_int ("default-group"); + + if (default_group >= 0) { + for (var offset = 0; offset < sources.length; offset++) { + var current = (default_group + offset) % sources.length; + var source = sources[current]; + + if (source.is_xkb || + (source.is_ibus && is_ibus_active ()) || + (source.is_fcitx && is_fcitx_active ())) { + if (current != old_current) { + source_settings.set_uint ("current", current); + } + + break; + } + } + } + } else { + var source = ((!) window_sources)[window_id]; + + for (var current = 0; current < sources.length; current++) { + if (sources[current] == source) { + if (current != old_current) { + source_settings.set_uint ("current", current); + } + + break; + } + } + } + + focused_window_id = window_id; + } + + [DBus (visible = false)] + private uint get_current () { + if (is_fcitx_active () && get_fcitx () != null) { + string? engine = ((!) get_fcitx ()).current_im; + + if (engine != null) { + var is_xkb = ((!) engine).has_prefix ("fcitx-keyboard-"); + var type = is_xkb ? "xkb" : "fcitx"; + var name = (!) engine; + + if (is_xkb) { + name = name.substring ("fcitx-keyboard-".length); + var index = name.index_of ("-"); + if (index >= 0) { + name.data[index] = '+'; + } + } + + var i = 0; + + foreach (var pair in source_settings.get_value ("sources")) { + unowned string source_type; + unowned string source_name; + + ((!) pair).get ("(&s&s)", out source_type, out source_name); + + if (source_name == name && source_type == type) { + return i; + } + + i++; + } + } + } + + return source_settings.get_uint ("current"); + } + + [DBus (visible = false)] + private Source[] get_sources () { + if (sources == null) { + var array = source_settings.get_value ("sources"); + + sources = new Source[array.n_children ()]; + + for (var i = 0; i < ((!) sources).length; i++) { + sources[i] = new Source(array.get_child_value (i), use_gtk); + sources[i].show_subscript = false; + sources[i].subscript = 1; + + for (var j = (int) i - 1; j >= 0; j--) { + if ((!) sources[j].short_name == (!) sources[i].short_name) { + sources[i].subscript = sources[j].subscript + 1; + sources[i].show_subscript = true; + sources[j].show_subscript = true; + + break; + } + } + + if (ibus_connected_id == 0 && sources[i].is_ibus) { + ibus_connected_id = get_ibus ().connected.connect (() => { get_ibus_panel (); }); + get_ibus ().disconnected.connect (() => { ibus_panel = null; }); + + if (get_ibus ().is_connected ()) { + get_ibus_panel (); + } + } + } + } + + return (!) sources; + } + + [DBus (visible = false)] + private void handle_properties_registered (IBus.PropList list) { + if (panel_timeout > 0) { + GLib.Source.remove (panel_timeout); + panel_timeout = 0; + } + + panel_timeout = Timeout.add (PROPERTIES_DELAY, () => { + get_desktop_menu ().set_properties (list); + panel_timeout = 0; + return false; + }); + } + + [DBus (visible = false)] + private void handle_property_updated (IBus.Property property) { + get_desktop_menu ().update_property (property); + } + + [DBus (visible = false)] + private void update_indicator_action () { + Icon? icon = null; + string? name = null; + + var sources = get_sources (); + var active = get_active_action ().get_state ().get_uint32 (); + + if (active < sources.length) { + icon = sources[active].icon; + name = sources[active].name; + } + + var builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "visible", indicator_settings.get_value ("visible")); + if (name != null) { + var description = _ ("%s input source").printf ((!) name); + builder.add ("{sv}", "accessible-desc", new Variant.string (description)); + } + if (icon != null) { + builder.add ("{sv}", "icon", ((!) icon).serialize ()); + } + + get_indicator_action ().set_state (builder.end ()); + } + + [DBus (visible = false)] + private SimpleAction get_indicator_action () { + if (indicator_action == null) { + var state = new Variant.parsed ("{ 'visible' : }"); + indicator_action = new SimpleAction.stateful ("indicator", null, state); + update_indicator_action (); + } + + return (!) indicator_action; + } + + [DBus (visible = false)] + private void handle_changed_active (Variant? value) { + if (value != null) { + ((!) active_action).set_state ((!) value); + update_indicator_action (); + + if (keyboard_plugin != null) { + try { + ((!) keyboard_plugin).activate_input_source (((!) value).get_uint32 ()); + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + } + } + + [DBus (visible = false)] + private void update_active_action () { + if (active_action != null) { + ((!) active_action).set_state (new Variant.uint32 (get_current ())); + update_indicator_action (); + } + } + + [DBus (visible = false)] + private Action get_active_action () { + if (active_action == null) { + var action = new SimpleAction.stateful ("active", VariantType.UINT32, new Variant.uint32 (get_current ())); + action.change_state.connect (handle_changed_active); + active_action = action; + } + + return (!) active_action; + } + + [DBus (visible = false)] + private void handle_middle_click (Variant? parameter) { + handle_scroll_wheel (new Variant.int32 (-1)); + } + + [DBus (visible = false)] + private void handle_scroll_wheel (Variant? parameter) { + if (parameter != null) { + var old_current = get_current (); + var sources = get_sources (); + var length = 0; + + foreach (var source in sources) { + if (source.is_xkb || + (source.is_ibus && is_ibus_active ()) || + (source.is_fcitx && is_fcitx_active ())) { + length++; + } + } + + if (length > 1) { + var current = old_current; + var offset = -((!) parameter).get_int32 () % length; + var jump = 1; + + if (offset < 0) { + offset = -offset; + jump = sources.length - jump; + } + + /* + * We need to cycle through offset valid input sources, skipping those that aren't + * valid for this session (i.e. skipping Fcitx ones if IBus is active and vice-versa. + * jump is the direction we need to cycle in, which is 1 if we want to cycle forward + * and -1 (mod sources.length) if we want to cycle backward. + */ + + for (; offset > 0; offset--) { + do { + current = (current + jump) % sources.length; + } while ((sources[current].is_ibus && !is_ibus_active ()) || + (sources[current].is_fcitx && !is_fcitx_active ())); + } + + if (current != old_current) { + source_settings.set_uint ("current", current); + } + } + } + } + + [DBus (visible = false)] + private void handle_middle_click_when_locked (Variant? parameter) { + handle_scroll_wheel_when_locked (new Variant.int32 (-1)); + } + + [DBus (visible = false)] + private void handle_scroll_wheel_when_locked (Variant? parameter) { + if (parameter != null) { + var sources = get_sources (); + var xkb_length = 0; + + /* Figure out how many Xkb sources we have. */ + foreach (var source in sources) { + if (source.is_xkb) { + xkb_length++; + } + } + + if (xkb_length > 1) { + var active_action = get_active_action (); + var active = active_action.get_state ().get_uint32 (); + var offset = -((!) parameter).get_int32 () % xkb_length; + + /* Make offset positive modulo xkb_length. */ + if (offset < 0) { + offset += xkb_length; + } + + /* We need to cycle through Xkb sources only. */ + while (offset > 0) { + do { + active = (active + 1) % sources.length; + } while (!sources[active].is_xkb); + + offset--; + } + + active_action.change_state (new Variant.uint32 (active)); + } + } + } + + [DBus (visible = false)] + protected virtual SimpleActionGroup create_action_group (Action root_action) { + var group = new SimpleActionGroup (); + + /* + * The 'current' action reflects the current setting in + * GSettings and the 'active' action only exists to set the + * active input source without persisting it. + * + * The lock screen menu uses the 'active' action while the + * other menus instead persist the current input source. + */ + + group.add_action (root_action); + group.add_action (get_active_action ()); + group.add_action (source_settings.create_action ("current")); + + var action = new SimpleAction ("next", null); + action.activate.connect (handle_middle_click); + group.add_action (action); + + action = new SimpleAction ("scroll", VariantType.INT32); + action.activate.connect (handle_scroll_wheel); + group.add_action (action); + + action = new SimpleAction ("locked_next", null); + action.activate.connect (handle_middle_click_when_locked); + group.add_action (action); + + action = new SimpleAction ("locked_scroll", VariantType.INT32); + action.activate.connect (handle_scroll_wheel_when_locked); + group.add_action (action); + + action = new SimpleAction ("map", null); + action.activate.connect (handle_activate_map); + group.add_action (action); + + action = new SimpleAction ("chart", null); + action.activate.connect (handle_activate_chart); + group.add_action (action); + + action = new SimpleAction ("settings", null); + action.activate.connect (handle_activate_settings); + group.add_action (action); + + return group; + } + + [DBus (visible = false)] + public SimpleActionGroup get_action_group () { + if (action_group == null) { + action_group = create_action_group (get_indicator_action ()); + } + + return (!) action_group; + } + + [DBus (visible = false)] + public IndicatorMenu get_desktop_menu () { + if (desktop_menu == null) { + var options = IndicatorMenu.Options.DCONF; + + if (!is_fcitx_active ()) { + options |= IndicatorMenu.Options.XKB | IndicatorMenu.Options.SETTINGS; + + if (is_ibus_active ()) { + options |= IndicatorMenu.Options.IBUS; + } + } + + var menu = new IndicatorMenu (get_action_group (), options); + + menu.set_sources (get_sources ()); + menu.activate.connect ((property, state) => { + var panel = get_ibus_panel (); + + if (panel != null) { + try { + ((!) panel).activate_property (property.key, state); + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + }); + + desktop_menu = menu; + } + + return (!) desktop_menu; + } + + [DBus (visible = false)] + public IndicatorMenu get_desktop_greeter_menu () { + if (desktop_greeter_menu == null) { + var options = IndicatorMenu.Options.DCONF | + IndicatorMenu.Options.XKB; + + var menu = new IndicatorMenu (get_action_group (), options); + menu.set_sources (get_sources ()); + desktop_greeter_menu = menu; + } + + return (!) desktop_greeter_menu; + } + + [DBus (visible = false)] + public IndicatorMenu get_desktop_lockscreen_menu () { + if (desktop_lockscreen_menu == null) { + var options = IndicatorMenu.Options.XKB; + + var menu = new IndicatorMenu (get_action_group (), options); + menu.set_sources (get_sources ()); + desktop_lockscreen_menu = menu; + } + + return (!) desktop_lockscreen_menu; + } + + [DBus (visible = false)] + private void handle_changed_visible (string key) { + update_indicator_action (); + } + + [DBus (visible = false)] + private void handle_changed_current (string key) { + update_indicator_action (); + update_active_action (); + update_login_layout (); + } + + [DBus (visible = false)] + private void handle_changed_sources (string key) { + sources = null; + + get_desktop_menu ().set_sources (get_sources ()); + get_desktop_greeter_menu ().set_sources (get_sources ()); + get_desktop_lockscreen_menu ().set_sources (get_sources ()); + update_indicator_action (); + update_login_layout (); + } + + [DBus (visible = false)] + private void handle_activate_map (Variant? parameter) { + try { + Process.spawn_command_line_async ("gucharmap"); + } catch (SpawnError error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_activate_chart (Variant? parameter) { + string? layout = "us"; + string? variant = null; + + var sources = get_sources (); + var current = get_current (); + + if (current < sources.length) { + layout = sources[current].layout; + variant = sources[current].variant; + } + + var has_layout = layout != null && ((!) layout).get_char () != '\0'; + var has_variant = variant != null && ((!) variant).get_char () != '\0'; + + try { + string command; + + if (has_layout && has_variant) { + command = @"gkbd-keyboard-display -l \"$((!) layout)\t$((!) variant)\""; + } else if (has_layout) { + command = @"gkbd-keyboard-display -l $((!) layout)"; + } else { + command = @"gkbd-keyboard-display -l us"; + } + + Process.spawn_command_line_async (command); + } catch (SpawnError error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_activate_settings (Variant? parameter) { + try { + Process.spawn_command_line_async ("unity-control-center region layouts"); + } catch (SpawnError error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_unity_greeter_name_appeared (DBusConnection connection, string name, string name_owner) { + try { + var greeter = Bus.get_proxy_sync (BusType.SESSION, name, "/list"); + greeter.entry_selected.connect (handle_entry_selected); + unity_greeter = greeter; + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_unity_greeter_name_vanished (DBusConnection connection, string name) { + unity_greeter = null; + } + + [DBus (visible = false)] + private void handle_keyboard_name_appeared (DBusConnection connection, string name, string name_owner) { + try { + keyboard_plugin = Bus.get_proxy_sync (BusType.SESSION, name, "/org/gnome/SettingsDaemon/Keyboard"); + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_keyboard_name_vanished (DBusConnection connection, string name) { + keyboard_plugin = null; + } + + [DBus (visible = false)] + private void handle_unity_name_appeared (DBusConnection connection, string name, string name_owner) { + try { + var session = Bus.get_proxy_sync (BusType.SESSION, name, "/org/ayatana/Unity/Session"); + + session.locked.connect (() => { + var sources = get_sources (); + + if (sources.length > 0) { + var current = get_current (); + + if (current < sources.length && !sources[current].is_xkb) { + for (var i = 0; i < sources.length; i++) { + if (sources[i].is_xkb) { + get_active_action ().change_state (new Variant.uint32 (i)); + break; + } + } + } + } + }); + session.unlocked.connect (() => { + get_active_action ().change_state (new Variant.uint32 (get_current ())); + }); + + unity_session = session; + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_unity_name_vanished (DBusConnection connection, string name) { + unity_session = null; + } + + [DBus (visible = false)] + private void handle_window_stack_name_appeared (DBusConnection connection, string name, string name_owner) { + try { + window_stack = Bus.get_proxy_sync (BusType.SESSION, name, "/org/ayatana/Unity/WindowStack"); + update_window_sources (); + } catch (IOError error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_window_stack_name_vanished (DBusConnection connection, string name) { + window_stack = null; + } + + [DBus (visible = false)] + private void handle_bus_acquired (DBusConnection connection, string name) { + try { + connection.export_action_group ("/org/ayatana/indicator/keyboard", get_action_group ()); + connection.export_menu_model ("/org/ayatana/indicator/keyboard/desktop", get_desktop_menu ()); + connection.export_menu_model ("/org/ayatana/indicator/keyboard/desktop_greeter", get_desktop_greeter_menu ()); + connection.export_menu_model ("/org/ayatana/indicator/keyboard/desktop_lockscreen", get_desktop_lockscreen_menu ()); + } catch (Error error) { + warning ("error: %s", error.message); + } + } + + [DBus (visible = false)] + private void handle_name_lost (DBusConnection? connection, string name) { + down (); + } + + [DBus (visible = false)] + public static int main (string[] args) { + Service.service = new Service (ref args); + + Posix.signal (Posix.SIGTERM, (code) => { + Service.service.down (); + }); + + Service.service.up (); + + return 0; + } +} diff --git a/src/source.vala b/src/source.vala new file mode 100644 index 00000000..b7d7a971 --- /dev/null +++ b/src/source.vala @@ -0,0 +1,487 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +public class Indicator.Keyboard.Source : Object { + + private static Gnome.XkbInfo? xkb_info; + private static IBus.Bus? ibus_bus; + private static Fcitx.InputMethod? fcitx_proxy; + + private string? xkb; + private string? ibus; + private string? fcitx; + + private string? _name; + private string? _short_name; + private string? _layout; + private string? _variant; + private Icon? _icon; + private uint _subscript; + private bool _show_subscript; + private bool _use_gtk; + + public string? name { + get { if (_name == null) { _name = _get_name (); } return _name; } + } + + public string? short_name { + get { if (_short_name == null) { _short_name = _get_short_name (); } return _short_name; } + } + + public string? layout { + get { if (_layout == null) { _layout = _get_layout (); } return _layout; } + } + + public string? variant { + get { if (_variant == null) { _variant = _get_variant (); } return _variant; } + } + + public Icon? icon { + get { if (_icon == null) { _icon = _get_icon (); } return _icon; } + set { _icon = value; } + } + + public uint subscript { + get { return _subscript; } + set { _subscript = value; icon = null; } + } + + public bool show_subscript { + get { return _show_subscript; } + set { _show_subscript = value; icon = null; } + } + + public bool use_gtk { + get { return _use_gtk; } + construct set { _use_gtk = value; icon = null; } + } + + public bool is_xkb { + get { return xkb != null; } + } + + public bool is_ibus { + get { return ibus != null; } + } + + public bool is_fcitx { + get { return fcitx != null; } + } + + public Source (Variant variant, bool use_gtk = false) { + Object (use_gtk: use_gtk); + + if (variant.is_of_type (new VariantType ("(ss)"))) { + unowned string type; + unowned string name; + + variant.get ("(&s&s)", out type, out name); + + if (type == "xkb") { + xkb = name; + } else if (type == "ibus") { + ibus = name; + } else if (type == "fcitx") { + fcitx = name; + } + } else if (variant.is_of_type (new VariantType ("a{ss}"))) { + foreach (var pair in variant) { + unowned string key; + unowned string value; + + ((!) pair).get ("{&s&s}", out key, out value); + + if (key == "xkb") { + xkb = value; + } else if (key == "ibus") { + ibus = value; + } else if (key == "fcitx") { + fcitx = value; + } + } + } + } + + private static Gnome.XkbInfo get_xkb_info () { + if (xkb_info == null) { + xkb_info = new Gnome.XkbInfo (); + } + + return (!) xkb_info; + } + + private static IBus.Bus get_ibus_bus () { + if (ibus_bus == null) { + IBus.init (); + ibus_bus = new IBus.Bus (); + } + + return (!) ibus_bus; + } + + private static Fcitx.InputMethod get_fcitx_proxy () throws Error { + if (fcitx_proxy == null) { + fcitx_proxy = new Fcitx.InputMethod (BusType.SESSION, DBusProxyFlags.NONE, 0); + } + + return (!) fcitx_proxy; + } + + private IBus.EngineDesc? get_engine () { + IBus.EngineDesc? engine = null; + + if (ibus != null) { + var names = new string[2]; + names[0] = (!) ibus; + + var engines = get_ibus_bus ().get_engines_by_names (names); + + if (engines.length > 0) { + engine = engines[0]; + } + } + + return engine; + } + + protected virtual string? _get_name () { + string? name = null; + + if (xkb != null) { + string? display_name = null; + string? layout = null; + + get_xkb_info ().get_layout_info ((!) xkb, out display_name, null, out layout, null); + + var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; + var has_layout = layout != null && ((!) layout).get_char () != '\0'; + + if (has_display_name) { + name = display_name; + } else if (has_layout) { + string? language = Xkl.get_language_name ((!) layout); + string? country = Xkl.get_country_name ((!) layout); + var has_language = language != null && ((!) language).get_char () != '\0'; + var has_country = country != null && ((!) country).get_char () != '\0'; + + if (has_language && has_country) { + name = @"$((!) language) ($((!) country))"; + } else if (has_language) { + name = language; + } else if (has_country) { + name = country; + } + } + + if (name == null || ((!) name).get_char () == '\0') { + name = xkb; + } + } else if (ibus != null) { + var engine = get_engine (); + + if (engine != null) { + string? language = ((!) engine).get_language (); + string? display_name = ((!) engine).get_longname (); + var has_language = language != null && ((!) language).get_char () != '\0'; + var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; + + if (has_language) { + language = Xkl.get_language_name ((!) language); + has_language = language != null && ((!) language).get_char () != '\0'; + } + + if (has_language && has_display_name) { + name = @"$((!) language) ($((!) display_name))"; + } else if (has_language) { + name = language; + } else if (has_display_name) { + name = display_name; + } + } + + if (name == null || ((!) name).get_char () == '\0') { + name = ibus; + } + } else if (fcitx != null) { + try { + var input_methods = get_fcitx_proxy ().get_imlist_nofree (); + + for (var i = 0; i < input_methods.length; i++) { + if (input_methods.get (i).unique_name == (!) fcitx) { + name = input_methods.get (i).name; + break; + } + } + } catch (Error error) { + warning ("error: %s", error.message); + } + + if (name == null || ((!) name).get_char () == '\0') { + name = fcitx; + } + } + + return name; + } + + protected virtual string? _get_short_name () { + string? short_name = null; + + if (xkb != null) { + get_xkb_info ().get_layout_info ((!) xkb, null, out short_name, null, null); + + if (short_name == null || ((!) short_name).get_char () == '\0') { + short_name = xkb; + } + } else if (ibus != null) { + var engine = get_engine (); + + if (engine != null) { + short_name = ((!) engine).get_name (); + } + + if (short_name == null || ((!) short_name).get_char () == '\0') { + short_name = ibus; + } + } else if (fcitx != null) { + try { + var input_methods = get_fcitx_proxy ().get_imlist_nofree (); + + for (var i = 0; i < input_methods.length; i++) { + if (input_methods.get (i).unique_name == (!) fcitx) { + short_name = input_methods.get (i).langcode; + break; + } + } + } catch (Error error) { + warning ("error: %s", error.message); + } + + if (short_name == null || ((!) short_name).get_char () == '\0') { + short_name = fcitx; + } + } + + return abbreviate (short_name); + } + + protected virtual string? _get_layout () { + string? layout = null; + + if (xkb != null) { + get_xkb_info ().get_layout_info ((!) xkb, null, null, out layout, null); + } + + var has_layout = layout != null && ((!) layout).get_char () != '\0'; + + if (!has_layout) { + var engine = get_engine (); + + if (engine != null) { + layout = ((!) engine).get_layout (); + } + } + + if (layout == null || ((!) layout).get_char () == '\0') { + layout = xkb; + } + + return layout; + } + + protected virtual string? _get_variant () { + string? variant = null; + + if (xkb != null) { + get_xkb_info ().get_layout_info ((!) xkb, null, null, null, out variant); + } + + var has_variant = variant != null && ((!) variant).get_char () != '\0'; + + if (!has_variant) { + var engine = get_engine (); + + if (engine != null) { + variant = ((!) engine).get_layout_variant (); + } + } + + if (variant == null || ((!) variant).get_char () == '\0') { + variant = null; + } + + return variant; + } + + private Gtk.StyleContext? get_style_context () { + Gtk.StyleContext? context = null; + + if (_use_gtk) { + Gdk.Screen? screen = Gdk.Screen.get_default (); + + if (screen != null) { + var style_context = new Gtk.StyleContext (); + style_context.set_screen ((!) screen); + + var path = new Gtk.WidgetPath (); + path.append_type (typeof (Gtk.MenuItem)); + style_context.set_path (path); + + context = style_context; + } + } + + return context; + } + + protected virtual Icon? create_icon () { + Icon? icon = null; + + var style = get_style_context (); + + if (style != null) { + const int W = 22; + const int H = 22; + const int w = 20; + const int h = 20; + const double R = 2.0; + const double TEXT_SIZE = 12.0; + const double SUBSCRIPT_SIZE = 8.0; + + Pango.FontDescription description; + var colour = ((!) style).get_color (Gtk.StateFlags.NORMAL); + colour = { 0.5, 0.5, 0.5, 1.0 }; + ((!) style).get (Gtk.StateFlags.NORMAL, Gtk.STYLE_PROPERTY_FONT, out description); + + var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, W, H); + var context = new Cairo.Context (surface); + + context.translate (0.5 * (W - w), 0.5 * (H - h)); + + context.new_sub_path (); + context.arc (R, R, R, Math.PI, -0.5 * Math.PI); + context.arc (w - R, R, R, -0.5 * Math.PI, 0); + context.arc (w - R, h - R, R, 0, 0.5 * Math.PI); + context.arc (R, h - R, R, 0.5 * Math.PI, Math.PI); + context.close_path (); + + context.set_source_rgba (colour.red, colour.green, colour.blue, colour.alpha); + context.fill (); + context.set_operator (Cairo.Operator.CLEAR); + + if (short_name != null) { + var text_layout = Pango.cairo_create_layout (context); + text_layout.set_alignment (Pango.Alignment.CENTER); + description.set_absolute_size (Pango.units_from_double (TEXT_SIZE)); + text_layout.set_font_description (description); + text_layout.set_text ((!) short_name, -1); + Pango.cairo_update_layout (context, text_layout); + int text_width; + int text_height; + text_layout.get_pixel_size (out text_width, out text_height); + + if (_show_subscript) { + var subscript_layout = Pango.cairo_create_layout (context); + subscript_layout.set_alignment (Pango.Alignment.CENTER); + description.set_absolute_size (Pango.units_from_double (SUBSCRIPT_SIZE)); + subscript_layout.set_font_description (description); + subscript_layout.set_text (@"$_subscript", -1); + Pango.cairo_update_layout (context, subscript_layout); + int subscript_width; + int subscript_height; + subscript_layout.get_pixel_size (out subscript_width, out subscript_height); + + context.save (); + context.translate ((w - (text_width + subscript_width)) / 2, (h - text_height) / 2); + Pango.cairo_layout_path (context, text_layout); + context.fill (); + context.restore (); + + context.save (); + context.translate ((w + (text_width - subscript_width)) / 2, (h + text_height) / 2 - subscript_height); + Pango.cairo_layout_path (context, subscript_layout); + context.fill (); + context.restore (); + } else { + context.save (); + context.translate ((w - text_width) / 2, (h - text_height) / 2); + Pango.cairo_layout_path (context, text_layout); + context.fill (); + context.restore (); + } + } + + var buffer = new ByteArray (); + + surface.write_to_png_stream ((data) => { + buffer.append (data); + return Cairo.Status.SUCCESS; + }); + + icon = new BytesIcon (ByteArray.free_to_bytes ((owned) buffer)); + } + + return icon; + } + + private Icon? _get_icon () { + Icon? icon = null; + + var engine = get_engine (); + + if (engine != null) { + string? icon_name = ((!) engine).get_icon (); + var has_icon_name = icon_name != null && ((!) icon_name).get_char () != '\0'; + + if (has_icon_name) { + try { + icon = Icon.new_for_string ((!) icon_name); + } catch (Error error) { + warning ("error: %s", error.message); + } + } + } + + if (icon == null && short_name != null) { + string icon_name; + + if (_show_subscript) { + icon_name = @"indicator-keyboard-$((!) short_name)-$_subscript"; + } else { + icon_name = @"indicator-keyboard-$((!) short_name)"; + } + + if (_use_gtk) { + var icon_theme = Gtk.IconTheme.get_default (); + Gtk.IconInfo? icon_info = icon_theme.lookup_icon (icon_name, 22, 0); + + if (icon_info != null) { + icon = new ThemedIcon (icon_name); + } + } else { + icon = new ThemedIcon (icon_name); + } + } + + if (icon == null) { + icon = create_icon (); + } + + return icon; + } +} diff --git a/src/unity-greeter.vala b/src/unity-greeter.vala new file mode 100644 index 00000000..5ca398ec --- /dev/null +++ b/src/unity-greeter.vala @@ -0,0 +1,26 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +[DBus (name="org.ayatana.UnityGreeter.List")] +public interface UnityGreeter : Object { + + public abstract string get_active_entry () throws IOError; + public abstract void set_active_entry (string entry_name) throws IOError; + + public signal void entry_selected (string entry_name); +} diff --git a/src/unity-session.vala b/src/unity-session.vala new file mode 100644 index 00000000..15337c36 --- /dev/null +++ b/src/unity-session.vala @@ -0,0 +1,24 @@ +/* + * Copyright 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +[DBus (name="org.ayatana.Unity.Session")] +public interface UnitySession : Object { + + public signal void locked (); + public signal void unlocked (); +} diff --git a/src/window-stack.vala b/src/window-stack.vala new file mode 100644 index 00000000..a943da6a --- /dev/null +++ b/src/window-stack.vala @@ -0,0 +1,37 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: William Hua + */ + +public struct WindowInfo { + + public uint window_id; + public string app_id; + public bool focused; + public uint stage; +} + +[DBus (name="org.ayatana.Unity.WindowStack")] +public interface WindowStack : Object { + + public abstract string get_app_id_from_pid (uint pid) throws IOError; + public abstract string[] get_window_properties (uint window_id, string app_id, string[] property_names) throws IOError; + public abstract WindowInfo[] get_window_stack () throws IOError; + + public signal void focused_window_changed (uint window_id, string app_id, uint stage); + public signal void window_created (uint window_id, string app_id); + public signal void window_destroyed (uint window_id, string app_id); +} -- cgit v1.2.3