/* * 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; } }