diff options
-rw-r--r-- | .bzrignore | 19 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | debian/control | 1 | ||||
-rw-r--r-- | deps/Fcitx-1.0.metadata | 7 | ||||
-rw-r--r-- | deps/README | 47 | ||||
-rw-r--r-- | deps/fcitx.vapi | 98 | ||||
-rw-r--r-- | lib/Makefile.am | 3 | ||||
-rw-r--r-- | lib/indicator-menu.vala | 77 | ||||
-rw-r--r-- | lib/main.vala | 236 | ||||
-rw-r--r-- | lib/source.vala | 141 | ||||
-rw-r--r-- | tests/indicator-keyboard-test.in | 1 |
11 files changed, 491 insertions, 143 deletions
@@ -10,15 +10,19 @@ .timestamp Makefile Makefile.in +aclocal.m4 autom4te.cache build-aux config.log config.status configure data/com.canonical.indicator.keyboard +data/gschemas.compiled data/indicator-keyboard-icon-generator +data/indicator-keyboard.conf +data/indicator-keyboard.desktop data/indicator-keyboard.service -data/gschemas.compiled +data/upstart/indicator-keyboard.desktop debian/autoreconf.after debian/autoreconf.before debian/files @@ -26,6 +30,12 @@ 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 @@ -35,10 +45,3 @@ tests/indicator-keyboard-test tests/indicator-keyboard-test.trs tests/indicator-keyboard-tests tests/services/indicator-keyboard.service -aclocal.m4 -m4/intltool.m4 -m4/libtool.m4 -m4/lt~obsolete.m4 -m4/ltoptions.m4 -m4/ltsugar.m4 -m4/ltversion.m4 diff --git a/configure.ac b/configure.ac index 5c09f8fc..dc90e15d 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,10 @@ PKG_CHECK_MODULES([IBUS], [ibus-1.0]) AC_SUBST([IBUS_CFLAGS]) AC_SUBST([IBUS_LIBS]) +PKG_CHECK_MODULES([FCITX_GCLIENT], [fcitx-gclient]) +AC_SUBST([FCITX_GCLIENT_CFLAGS]) +AC_SUBST([FCITX_GCLIENT_LIBS]) + PKG_CHECK_MODULES([ACCOUNTSSERVICE], [accountsservice]) AC_SUBST([ACCOUNTSSERVICE_CFLAGS]) AC_SUBST([ACCOUNTSSERVICE_LIBS]) diff --git a/debian/control b/debian/control index 585af9c5..a359f3d9 100644 --- a/debian/control +++ b/debian/control @@ -7,6 +7,7 @@ Build-Depends: debhelper (>= 9.0.0), dh-autoreconf, dh-translations, dbus, + fcitx-libs-dev (>= 1:4.2.8.3), libaccountsservice-dev, libgee-dev, libgirepository1.0-dev, diff --git a/deps/Fcitx-1.0.metadata b/deps/Fcitx-1.0.metadata new file mode 100644 index 00000000..81c20f3c --- /dev/null +++ b/deps/Fcitx-1.0.metadata @@ -0,0 +1,7 @@ +Client cheader_filename="fcitx-gclient/fcitxclient.h" +Connection cheader_filename="fcitx-gclient/fcitxconnection.h" +IMItem cheader_filename="fcitx-gclient/fcitxinputmethod.h" +InputMethod cheader_filename="fcitx-gclient/fcitxinputmethod.h" +Kbd cheader_filename="fcitx-gclient/fcitxkbd.h" +LayoutItem cheader_filename="fcitx-gclient/fcitxkbd.h" +PreeditItem cheader_filename="fcitx-gclient/fcitxclient.h" diff --git a/deps/README b/deps/README new file mode 100644 index 00000000..c3666140 --- /dev/null +++ b/deps/README @@ -0,0 +1,47 @@ +To generate fontconfig.vapi: + +vapigen --library fontconfig /path/to/fontconfig-2.0.gir + +To generate freetype2.vapi: + +vapigen --library freetype2 /path/to/freetype2-2.0.gir + +To generate pangoft2.vapi: + +vapigen --library pangoft2 /path/to/PangoFT2-1.0.gir + +To generate gnome-desktop-3.0.vapi: + +vapigen --pkg gio-2.0 --pkg gtk+-3.0 --library gnome-desktop-3.0 /path/to/GnomeDesktop-3.0.gir + +To generate gnome-desktop-3.0.vapi docs: + +valadoc --vapidir /path/to/vapi/dir --pkg gdk-3.0 -o gnome-desktop-3.0 gnome-desktop-3.0.vapi + +To generate libxklavier.vapi: + +vapigen --metadatadir . --pkg x11 --library libxklavier /path/to/Xkl-1.0.gir + +To generate libxklavier.vapi docs: + +valadoc --vapidir /path/to/vapi/dir --pkg x11 -o libxklavier libxklavier.vapi + +To generate libgnomekbd.vapi: + +vapigen --metadatadir . --pkg gtk+-3.0 --library libgnomekbd /path/to/Gkbd-3.0.gir + +To generate libgnomekbd.vapi docs: + +valadoc --metadatadir . --vapidir /path/to/vapi/dir --pkg gtk+-3.0 --pkg Xkl-1.0 -o libgnomekbd libgnomekbd.vapi + +To generate ibus-1.0.vapi docs: + +valadoc --vapidir /path/to/vapi/dir -o ibus-1.0 /path/to/ibus-1.0.vapi + +To generate fcitx.vapi: + +vapigen --metadatadir . --pkg gio-2.0 --library fcitx /path/to/Fcitx-1.0.gir + +To generate libbamf3.vapi: + +vapigen --library libbamf3 /path/to/Bamf-3.gir diff --git a/deps/fcitx.vapi b/deps/fcitx.vapi new file mode 100644 index 00000000..70ce5849 --- /dev/null +++ b/deps/fcitx.vapi @@ -0,0 +1,98 @@ +/* fcitx.vapi generated by vapigen, do not modify. */ + +[CCode (cprefix = "Fcitx", gir_namespace = "Fcitx", gir_version = "1.0", lower_case_cprefix = "fcitx_")] +namespace Fcitx { + [CCode (cheader_filename = "fcitx-gclient/fcitxclient.h", type_id = "fcitx_client_get_type ()")] + public class Client : GLib.Object { + [CCode (has_construct_function = false)] + public Client (); + public void close_ic (); + public void enable_ic (); + public void focus_in (); + public void focus_out (); + public bool is_valid (); + [Deprecated] + public async int process_key (uint32 keyval, uint32 keycode, uint32 state, int type, uint32 t); + public async int process_key_async (uint32 keyval, uint32 keycode, uint32 state, int type, uint32 t, int timeout_msec, GLib.Cancellable? cancellable); + public int process_key_sync (uint32 keyval, uint32 keycode, uint32 state, int type, uint32 t); + public void reset (); + public void set_capacity (uint flags); + public void set_cursor_rect (int x, int y, int w, int h); + [Deprecated] + public void set_cusor_rect (int x, int y, int w, int h); + public void set_surrounding_text (string? text, uint cursor, uint anchor); + public signal void close_im (); + public signal void commit_string (string string); + public signal void connected (); + public signal void delete_surrounding_text (int cursor, uint len); + public signal void disconnected (); + public signal void enable_im (); + public signal void forward_key (uint keyval, int state, int type); + public signal void update_client_side_ui (string auxup, string auxdown, string preedit, string candidateword, string imname, int cursor_pos); + public signal void update_formatted_preedit (GLib.GenericArray<Fcitx.PreeditItem> preedit, int cursor); + } + [CCode (cheader_filename = "fcitx-gclient/fcitxconnection.h", type_id = "fcitx_connection_get_type ()")] + public class Connection : GLib.Object { + [CCode (has_construct_function = false)] + public Connection (); + public unowned GLib.DBusConnection get_g_dbus_connection (); + public bool is_valid (); + public signal void connected (); + public signal void disconnected (); + } + [CCode (cheader_filename = "fcitx-gclient/fcitxinputmethod.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "fcitx_im_item_get_type ()")] + [Compact] + public class IMItem { + public bool enable; + public weak string langcode; + public weak string name; + public weak string unique_name; + [CCode (has_construct_function = false)] + public IMItem (string name, string unique_name, string langcode, bool enable); + } + [CCode (cheader_filename = "fcitx-gclient/fcitxinputmethod.h", type_id = "fcitx_input_method_get_type ()")] + public class InputMethod : GLib.DBusProxy, GLib.AsyncInitable, GLib.DBusInterface, GLib.Initable { + [CCode (has_construct_function = false)] + public InputMethod (GLib.BusType bus_type, GLib.DBusProxyFlags flags, int display_number, GLib.Cancellable? cancellable = null) throws GLib.Error; + public void activate (); + public void configure (); + public void configure_addon (string addon); + public void configure_im (string imname); + public void exit (); + public string get_current_im (); + public int get_current_state (); + public string get_current_ui (); + public string get_im_addon (string imname); + public GLib.GenericArray<Fcitx.IMItem> get_imlist_nofree (); + public void inactivate (); + public void reload_config (); + public void restart (); + public void set_current_im (string imname); + public void set_imlist (GLib.GenericArray<Fcitx.IMItem> array); + public void toggle (); + public string current_im { owned get; set construct; } + public signal void imlist_changed (); + } + [CCode (cheader_filename = "fcitx-gclient/fcitxkbd.h", type_id = "fcitx_kbd_get_type ()")] + public class Kbd : GLib.DBusProxy, GLib.AsyncInitable, GLib.DBusInterface, GLib.Initable { + [CCode (has_construct_function = false)] + public Kbd (GLib.BusType bus_type, GLib.DBusProxyFlags flags, int display_number, GLib.Cancellable? cancellable = null) throws GLib.Error; + public void get_layout_for_im (string imname, out string layout, out string variant); + public GLib.GenericArray<Fcitx.LayoutItem> get_layouts_nofree (); + public void set_default_layout (string layout, string variant); + public void set_layout_for_im (string imname, string layout, string variant); + } + [CCode (cheader_filename = "fcitx-gclient/fcitxkbd.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "fcitx_layout_item_get_type ()")] + [Compact] + public class LayoutItem { + public weak string langcode; + public weak string layout; + public weak string name; + public weak string variant; + } + [CCode (cheader_filename = "fcitx-gclient/fcitxclient.h", has_type_id = false)] + public struct PreeditItem { + public weak global::string string; + public int32 type; + } +} diff --git a/lib/Makefile.am b/lib/Makefile.am index c57725c1..14c8f1ba 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -26,6 +26,7 @@ indicator_keyboard_service_VALAFLAGS = $(AM_VALAFLAGS) \ --pkg Xkl-1.0 \ --pkg Gkbd-3.0 \ --pkg ibus-1.0 \ + --pkg fcitx \ --pkg accountsservice \ --pkg liblightdm-gobject-1 indicator_keyboard_service_CFLAGS = $(AM_CFLAGS) \ @@ -36,6 +37,7 @@ indicator_keyboard_service_CFLAGS = $(AM_CFLAGS) \ $(LIBXKLAVIER_CFLAGS) \ $(LIBGNOMEKBD_CFLAGS) \ $(IBUS_CFLAGS) \ + $(FCITX_GCLIENT_CFLAGS) \ $(ACCOUNTSSERVICE_CFLAGS) \ $(LIGHTDM_CFLAGS) \ $(COVERAGE_CFLAGS) @@ -47,6 +49,7 @@ indicator_keyboard_service_LDFLAGS = $(AM_LDFLAGS) \ $(LIBXKLAVIER_LIBS) \ $(LIBGNOMEKBD_LIBS) \ $(IBUS_LIBS) \ + $(FCITX_GCLIENT_LIBS) \ $(ACCOUNTSSERVICE_LIBS) \ $(LIGHTDM_LIBS) \ $(COVERAGE_LDFLAGS) diff --git a/lib/indicator-menu.vala b/lib/indicator-menu.vala index 8e5661e2..209fcbf1 100644 --- a/lib/indicator-menu.vala +++ b/lib/indicator-menu.vala @@ -19,10 +19,12 @@ public class Indicator.Keyboard.IndicatorMenu : MenuModel { public enum Options { - NONE = 0x0, - DCONF = 0x1, - IBUS = 0x2, - SETTINGS = 0x4 + NONE = 0x00, + DCONF = 0x01, + XKB = 0x02, + IBUS = 0x04, + FCITX = 0x08, + SETTINGS = 0x10 } private Options options; @@ -32,42 +34,45 @@ public class Indicator.Keyboard.IndicatorMenu : MenuModel { private IBusMenu properties_section; public IndicatorMenu (ActionMap? action_map = null, Options options = Options.NONE) { - var submenu = new Menu (); + this.options = options; + indicator_menu = new Menu (); sources_section = new Menu (); - submenu.append_section (null, sources_section); - if ((options & Options.IBUS) != Options.NONE) { - properties_section = new IBusMenu (action_map); - properties_section.activate.connect ((property, state) => { activate (property, state); }); - submenu.append_section (null, properties_section); - } + if ((options & ~Options.DCONF) != Options.NONE) { + var submenu = new Menu (); - if ((options & Options.SETTINGS) != Options.NONE) { - 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); - } + submenu.append_section (null, sources_section); - var indicator = new MenuItem.submenu (null, submenu); - indicator.set_detailed_action ("indicator.indicator"); - indicator.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); - - /* We need special mouse actions on the lock screen. */ - if ((options & Options.DCONF) != Options.NONE) { - 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"); - } + if ((options & Options.IBUS) != Options.NONE) { + properties_section = new IBusMenu (action_map); + properties_section.activate.connect ((property, state) => { activate (property, state); }); + submenu.append_section (null, properties_section); + } - indicator_menu = new Menu (); - indicator_menu.append_item (indicator); + if ((options & Options.SETTINGS) != Options.NONE) { + 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); + } - this.options = options; + var indicator = new MenuItem.submenu (null, submenu); + indicator.set_detailed_action ("indicator.indicator"); + indicator.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); + + /* We need special mouse actions on the lock screen. */ + if ((options & Options.DCONF) != Options.NONE) { + 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); @@ -76,7 +81,11 @@ public class Indicator.Keyboard.IndicatorMenu : MenuModel { sources_section.remove_all (); for (var i = 0; i < sources.length; i++) { - if (!sources[i].is_ibus || (options & Options.IBUS) != Options.NONE) { + var visible = (sources[i].is_xkb && (options & Options.XKB) != Options.NONE) || + (sources[i].is_ibus && (options & Options.IBUS) != Options.NONE) || + (sources[i].is_fcitx && (options & Options.FCITX) != Options.NONE); + + if (visible) { string action; if ((options & Options.DCONF) != Options.NONE) { diff --git a/lib/main.vala b/lib/main.vala index 1cb3896c..41ff5ff7 100644 --- a/lib/main.vala +++ b/lib/main.vala @@ -33,7 +33,7 @@ public class Indicator.Keyboard.Service : Object { private SList<Act.User> users; private WindowStack? window_stack; - private Gee.HashMap<uint, uint>? window_sources; + private Gee.HashMap<uint, Source>? window_sources; private uint focused_window_id; private IBus.Bus? ibus; @@ -41,6 +41,8 @@ public class Indicator.Keyboard.Service : Object { private ulong ibus_connected_id; private uint panel_timeout; + private Fcitx.InputMethod? fcitx; + private Source[]? sources; private SimpleActionGroup? action_group; @@ -118,11 +120,13 @@ public class Indicator.Keyboard.Service : Object { handle_unity_name_appeared, handle_unity_name_vanished); - Bus.watch_name (BusType.SESSION, - "com.canonical.Unity.WindowStack", - BusNameWatcherFlags.NONE, - handle_window_stack_name_appeared, - handle_window_stack_name_vanished); + if (!is_fcitx_active ()) { + Bus.watch_name (BusType.SESSION, + "com.canonical.Unity.WindowStack", + BusNameWatcherFlags.NONE, + handle_window_stack_name_appeared, + handle_window_stack_name_vanished); + } } indicator_settings = new Settings ("com.canonical.indicator.keyboard"); @@ -146,6 +150,26 @@ public class Indicator.Keyboard.Service : Object { } [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 (); @@ -210,6 +234,20 @@ public class Indicator.Keyboard.Service : Object { } [DBus (visible = false)] + private Fcitx.InputMethod? get_fcitx () { + if (is_fcitx_active () && fcitx == null) { + try { + fcitx = new Fcitx.InputMethod (BusType.SESSION, DBusProxyFlags.NONE, 0); + ((!) fcitx).notify["current-im"].connect ((pspec) => { handle_changed_current ("current"); }); + } catch (Error error) { + warning ("error: %s", error.message); + } + } + + return fcitx; + } + + [DBus (visible = false)] public void up () { if (loop == null) { loop = new MainLoop (); @@ -254,11 +292,9 @@ public class Indicator.Keyboard.Service : Object { Act.User? user = manager.get_user ((!) greeter_user); if (user != null && ((!) user).is_loaded) { - VariantIter outer; - VariantIter inner; + var outer = ((!) user).input_sources.iterator (); - var sources = ((!) user).input_sources; - sources.get ("aa{ss}", out outer); + VariantIter inner; while (outer.next ("a{ss}", out inner)) { unowned string key; @@ -340,7 +376,7 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private void migrate_keyboard_layouts () { if (is_login_user ()) { - lightdm_current = source_settings.get_uint ("current"); + lightdm_current = get_current (); var manager = Act.UserManager.get_default (); @@ -448,14 +484,11 @@ public class Indicator.Keyboard.Service : Object { foreach (var user in users) { if (user.is_loaded) { + var outer = user.input_sources.iterator (); var done = false; - VariantIter outer; VariantIter inner; - var sources = user.input_sources; - sources.get ("aa{ss}", out outer); - while (outer.next ("a{ss}", out inner)) { unowned string key; unowned string source; @@ -548,7 +581,7 @@ public class Indicator.Keyboard.Service : Object { private void update_login_layout () { if (is_login_user ()) { unowned List<LightDM.Layout> layouts = LightDM.get_layouts (); - var current = source_settings.get_uint ("current"); + var current = get_current (); if (current < get_sources ().length) { var source = get_sources ()[current]; @@ -594,10 +627,12 @@ public class Indicator.Keyboard.Service : Object { warning ("error: %s", error.message); } - window_sources = new Gee.HashMap<uint, uint> (); + 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; } } @@ -610,22 +645,49 @@ public class Indicator.Keyboard.Service : Object { } [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 old_current = source_settings.get_uint ("current"); + var sources = get_sources (); + var old_current = get_current (); - ((!) window_sources)[focused_window_id] = old_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 && default_group != old_current) { - source_settings.set_uint ("current", (uint) 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 current = ((!) window_sources)[window_id]; + var source = ((!) window_sources)[window_id]; - if (current != old_current) { - source_settings.set_uint ("current", current); + for (var current = 0; current < sources.length; current++) { + if (sources[current] == source) { + if (current != old_current) { + source_settings.set_uint ("current", current); + } + + break; + } } } @@ -633,6 +695,40 @@ public class Indicator.Keyboard.Service : Object { } [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 iter = source_settings.get_value ("sources").iterator (); + + unowned string source_type; + unowned string source_name; + + for (var i = 0; iter.next ("(&s&s)", out source_type, out source_name); i++) { + if (source_name == name && source_type == type) { + return i; + } + } + } + } + + return source_settings.get_uint ("current"); + } + + [DBus (visible = false)] private Source[] get_sources () { if (sources == null) { var array = source_settings.get_value ("sources"); @@ -743,7 +839,7 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private void update_active_action () { if (active_action != null) { - ((!) active_action).set_state (source_settings.get_value ("current")); + ((!) active_action).set_state (new Variant.uint32 (get_current ())); update_indicator_action (); } } @@ -751,8 +847,7 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private Action get_active_action () { if (active_action == null) { - var current = source_settings.get_value ("current"); - active_action = new SimpleAction.stateful ("active", VariantType.UINT32, current); + active_action = new SimpleAction.stateful ("active", VariantType.UINT32, new Variant.uint32 (get_current ())); ((!) active_action).activate.connect ((parameter) => { ((!) active_action).change_state (parameter); }); ((!) active_action).change_state.connect (handle_changed_active); } @@ -768,13 +863,41 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private void handle_scroll_wheel (Variant? parameter) { if (parameter != null) { - var sources = source_settings.get_value ("sources"); - var current = source_settings.get_uint ("current"); - var length = (int) sources.n_children (); + var old_current = get_current (); + var sources = get_sources (); + var length = 0; - if (length > 0) { - var offset = ((!) parameter).get_int32 () % length; - source_settings.set_uint ("current", (current + (length - offset)) % length); + 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; + + /* Go backward. */ + for (; offset < 0; offset++) { + do { + current = (current + sources.length - 1) % sources.length; + } while ((sources[current].is_ibus && !is_ibus_active ()) || + (sources[current].is_fcitx && !is_fcitx_active ())); + } + + /* Go forward. */ + for (; offset > 0; offset--) { + do { + current = (current + 1) % 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); + } } } } @@ -788,30 +911,30 @@ public class Indicator.Keyboard.Service : Object { private void handle_scroll_wheel_when_locked (Variant? parameter) { if (parameter != null) { var sources = get_sources (); - var non_ibus_length = 0; + var xkb_length = 0; - /* Figure out how many non-IBus sources we have. */ + /* Figure out how many Xkb sources we have. */ foreach (var source in sources) { - if (!source.is_ibus) { - non_ibus_length++; + if (source.is_xkb) { + xkb_length++; } } - if (non_ibus_length > 1) { + if (xkb_length > 1) { var active_action = get_active_action (); var active = active_action.get_state ().get_uint32 (); - var offset = -((!) parameter).get_int32 () % non_ibus_length; + var offset = -((!) parameter).get_int32 () % xkb_length; - /* Make offset positive modulo non_ibus_length. */ + /* Make offset positive modulo xkb_length. */ if (offset < 0) { - offset += non_ibus_length; + offset += xkb_length; } - /* We need to cycle through non-IBus sources only. */ + /* We need to cycle through Xkb sources only. */ while (offset > 0) { do { active = (active + 1) % sources.length; - } while (sources[active].is_ibus); + } while (!sources[active].is_xkb); offset--; } @@ -881,9 +1004,15 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] public IndicatorMenu get_desktop_menu () { if (desktop_menu == null) { - var options = IndicatorMenu.Options.DCONF - | IndicatorMenu.Options.IBUS - | IndicatorMenu.Options.SETTINGS; + var options = IndicatorMenu.Options.DCONF; + + if (!is_fcitx_active ()) { + options |= IndicatorMenu.Options.XKB | IndicatorMenu.Options.SETTINGS; + + if (is_ibus_active ()) { + options |= IndicatorMenu.Options.IBUS; + } + } desktop_menu = new IndicatorMenu (get_action_group (), options); ((!) desktop_menu).set_sources (get_sources ()); @@ -906,7 +1035,8 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] public IndicatorMenu get_desktop_greeter_menu () { if (desktop_greeter_menu == null) { - var options = IndicatorMenu.Options.DCONF; + var options = IndicatorMenu.Options.DCONF | + IndicatorMenu.Options.XKB; desktop_greeter_menu = new IndicatorMenu (get_action_group (), options); ((!) desktop_greeter_menu).set_sources (get_sources ()); @@ -918,7 +1048,7 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] public IndicatorMenu get_desktop_lockscreen_menu () { if (desktop_lockscreen_menu == null) { - var options = IndicatorMenu.Options.NONE; + var options = IndicatorMenu.Options.XKB; desktop_lockscreen_menu = new IndicatorMenu (get_action_group (), options); ((!) desktop_lockscreen_menu).set_sources (get_sources ()); @@ -965,7 +1095,7 @@ public class Indicator.Keyboard.Service : Object { string? variant = null; var sources = get_sources (); - var current = source_settings.get_uint ("current"); + var current = get_current (); if (current < sources.length) { layout = sources[current].layout; @@ -1038,11 +1168,11 @@ public class Indicator.Keyboard.Service : Object { var sources = get_sources (); if (sources.length > 0) { - var current = source_settings.get_uint ("current"); + var current = get_current (); - if (current < sources.length && sources[current].is_ibus) { + if (current < sources.length && !sources[current].is_xkb) { for (var i = 0; i < sources.length; i++) { - if (!sources[i].is_ibus) { + if (sources[i].is_xkb) { get_active_action ().change_state (new Variant.uint32 (i)); break; } @@ -1051,7 +1181,7 @@ public class Indicator.Keyboard.Service : Object { } }); ((!) unity_session).unlocked.connect (() => { - get_active_action ().change_state (source_settings.get_value ("current")); + get_active_action ().change_state (new Variant.uint32 (get_current ())); }); } catch (IOError error) { warning ("error: %s", error.message); diff --git a/lib/source.vala b/lib/source.vala index 5fe7157d..f73b4b69 100644 --- a/lib/source.vala +++ b/lib/source.vala @@ -19,10 +19,12 @@ public class Indicator.Keyboard.Source : Object { private static Gnome.XkbInfo? xkb_info; - private static IBus.Bus? bus; + 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; @@ -77,6 +79,10 @@ public class Indicator.Keyboard.Source : Object { 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); @@ -90,19 +96,22 @@ public class Indicator.Keyboard.Source : Object { xkb = name; } else if (type == "ibus") { ibus = name; + } else if (type == "fcitx") { + fcitx = name; } } else if (variant.is_of_type (new VariantType ("a{ss}"))) { - VariantIter iter; + var iter = variant.iterator (); + unowned string key; unowned string value; - variant.get ("a{ss}", out iter); - while (iter.next ("{&s&s}", out key, out value)) { if (key == "xkb") { xkb = value; } else if (key == "ibus") { ibus = value; + } else if (key == "fcitx") { + fcitx = value; } } } @@ -116,13 +125,21 @@ public class Indicator.Keyboard.Source : Object { return (!) xkb_info; } - private static IBus.Bus get_bus () { - if (bus == null) { + private static IBus.Bus get_ibus_bus () { + if (ibus_bus == null) { IBus.init (); - bus = new IBus.Bus (); + ibus_bus = new IBus.Bus (); } - return (!) 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 () { @@ -132,7 +149,7 @@ public class Indicator.Keyboard.Source : Object { var names = new string[2]; names[0] = (!) ibus; - var engines = get_bus ().get_engines_by_names (names); + var engines = get_ibus_bus ().get_engines_by_names (names); if (engines.length > 0) { engine = engines[0]; @@ -145,31 +162,7 @@ public class Indicator.Keyboard.Source : Object { protected virtual string? _get_name () { string? name = 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; - } - } - - var has_name = name != null && ((!) name).get_char () != '\0'; - - if (!has_name && xkb != null) { + if (xkb != null) { string? display_name = null; string? layout = null; @@ -194,14 +187,53 @@ public class Indicator.Keyboard.Source : Object { name = country; } } - } - if (name == null || ((!) name).get_char () == '\0') { - if (ibus != null) { - name = ibus; - } else if (xkb != null) { + 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; @@ -212,23 +244,36 @@ public class Indicator.Keyboard.Source : Object { if (xkb != null) { get_xkb_info ().get_layout_info ((!) xkb, null, out short_name, null, null); - } - var has_short_name = short_name != null && ((!) short_name).get_char () != '\0'; - - if (!has_short_name) { + 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') { - if (ibus != null) { + if (short_name == null || ((!) short_name).get_char () == '\0') { short_name = ibus; - } else if (xkb != null) { - short_name = xkb; + } + } 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; } } diff --git a/tests/indicator-keyboard-test.in b/tests/indicator-keyboard-test.in index 621133dd..3d43d8ab 100644 --- a/tests/indicator-keyboard-test.in +++ b/tests/indicator-keyboard-test.in @@ -4,6 +4,7 @@ export PATH="@abs_top_builddir@/tests/execute:$PATH" export DCONF_PROFILE="@abs_top_builddir@/tests/profiles/indicator-keyboard-test" export GSETTINGS_SCHEMA_DIR="@abs_top_builddir@/data" export XDG_RUNTIME_DIR="@abs_top_builddir@/tests" +export GTK_IM_MODULE="ibus" if xvfb-run -a ./indicator-keyboard-tests then |