diff options
Diffstat (limited to 'src/main.vala')
-rw-r--r-- | src/main.vala | 1265 |
1 files changed, 1265 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>. + * + * Authors: William Hua <william.hua@canonical.com> + */ + +[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<Act.User> users; + + private WindowStack? window_stack; + private Gee.HashMap<uint, Source>? 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<IBusPanel> (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<string> (); + var added = new Gee.HashSet<string> (); + + 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<LightDM.Layout> 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<uint, Source> (); + ((!) 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' : <false> }"); + 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<UnityGreeter> (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<UnitySession> (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; + } +} |