diff options
author | Robert Tari <robert@tari.in> | 2021-01-24 03:38:44 +0100 |
---|---|---|
committer | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2021-01-27 17:10:40 +0100 |
commit | b85daf94830a3ca39d59237fdb07bc33ede1c450 (patch) | |
tree | 8d704b1d8614759da71fbe42dca737c0e2261142 /src | |
parent | 9b8fb0492c8c05630644d840aef520f3c6b17829 (diff) | |
download | ayatana-indicator-keyboard-b85daf94830a3ca39d59237fdb07bc33ede1c450.tar.gz ayatana-indicator-keyboard-b85daf94830a3ca39d59237fdb07bc33ede1c450.tar.bz2 ayatana-indicator-keyboard-b85daf94830a3ca39d59237fdb07bc33ede1c450.zip |
100% re-write of the keyboard indicator in plain C.
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 28 | ||||
-rw-r--r-- | src/Makefile.am | 55 | ||||
-rw-r--r-- | src/common.vala | 37 | ||||
-rw-r--r-- | src/ibus-menu.vala | 313 | ||||
-rw-r--r-- | src/ibus-panel.vala | 26 | ||||
-rw-r--r-- | src/indicator-menu.vala | 151 | ||||
-rw-r--r-- | src/keyboard-plugin.vala | 23 | ||||
-rw-r--r-- | src/keyboard.c | 287 | ||||
-rw-r--r-- | src/keyboard.h | 37 | ||||
-rw-r--r-- | src/main.c | 40 | ||||
-rw-r--r-- | src/main.vala | 1265 | ||||
-rw-r--r-- | src/service.c | 412 | ||||
-rw-r--r-- | src/service.h | 36 | ||||
-rw-r--r-- | src/source.vala | 487 | ||||
-rw-r--r-- | src/unity-greeter.vala | 26 | ||||
-rw-r--r-- | src/unity-session.vala | 24 | ||||
-rw-r--r-- | src/utils.c | 167 | ||||
-rw-r--r-- | src/utils.h | 38 | ||||
-rw-r--r-- | src/window-stack.vala | 37 |
19 files changed, 1045 insertions, 2444 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..3eb7caf9 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 2.8.12) +set (SERVICE_LIB "ayatanaindicatorkeyboardservice") +set (SERVICE_EXEC "ayatana-indicator-keyboard-service") + +add_definitions(-DG_LOG_DOMAIN="ayatana-indicator-keyboard") + +# handwritten sources +set(SERVICE_MANUAL_SOURCES keyboard.c service.c utils.c) + +# generated sources +set(SERVICE_GENERATED_SOURCES) + +# add the bin dir to our include path so the code can find the generated header files +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +# add warnings/coverage info on handwritten files but not the autogenerated ones... +set_source_files_properties(${SERVICE_MANUAL_SOURCES} PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -g -std=c99") + +# the service library for tests to link against (basically, everything except main()) +add_library(${SERVICE_LIB} STATIC ${SERVICE_MANUAL_SOURCES} ${SERVICE_GENERATED_SOURCES}) +include_directories(${CMAKE_SOURCE_DIR}) +link_directories(${SERVICE_DEPS_LIBRARY_DIRS}) + +# the executable: lib + main() +add_executable (${SERVICE_EXEC} main.c) +set_source_files_properties(${SERVICE_SOURCES} main.c PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -std=c99") +target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES}) +install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index a0fc4bb5..00000000 --- a/src/Makefile.am +++ /dev/null @@ -1,55 +0,0 @@ -pkglibexec_PROGRAMS = ayatana-indicator-keyboard-service - -AM_CFLAGS = -w -DGNOME_DESKTOP_USE_UNSTABLE_API -AM_LDFLAGS = -lm -AM_VALAFLAGS = --enable-experimental-non-null \ - --metadatadir $(top_srcdir)/deps \ - --vapidir $(top_srcdir)/deps - -ayatana_indicator_keyboard_service_SOURCES = main.vala \ - source.vala \ - common.vala \ - ibus-menu.vala \ - ibus-panel.vala \ - indicator-menu.vala \ - keyboard-plugin.vala \ - window-stack.vala \ - unity-session.vala \ - unity-greeter.vala -ayatana_indicator_keyboard_service_VALAFLAGS = $(AM_VALAFLAGS) \ - --pkg gee-1.0 \ - --pkg posix \ - --pkg pangocairo \ - --pkg gtk+-3.0 \ - --pkg GDesktopEnums-3.0 \ - --pkg GnomeDesktop-3.0 \ - --pkg Xkl-1.0 \ - --pkg Gkbd-3.0 \ - --pkg ibus-1.0 \ - --pkg Fcitx-1.0 \ - --pkg AccountsService-1.0 \ - --pkg liblightdm-gobject-1 -ayatana_indicator_keyboard_service_CFLAGS = $(AM_CFLAGS) \ - $(GEE_CFLAGS) \ - $(PANGOCAIRO_CFLAGS) \ - $(GTK_CFLAGS) \ - $(GNOME_DESKTOP_CFLAGS) \ - $(LIBXKLAVIER_CFLAGS) \ - $(LIBGNOMEKBD_CFLAGS) \ - $(IBUS_CFLAGS) \ - $(FCITX_GCLIENT_CFLAGS) \ - $(ACCOUNTSSERVICE_CFLAGS) \ - $(LIGHTDM_CFLAGS) \ - $(COVERAGE_CFLAGS) -ayatana_indicator_keyboard_service_LDFLAGS = $(AM_LDFLAGS) \ - $(GEE_LIBS) \ - $(PANGOCAIRO_LIBS) \ - $(GTK_LIBS) \ - $(GNOME_DESKTOP_LIBS) \ - $(LIBXKLAVIER_LIBS) \ - $(LIBGNOMEKBD_LIBS) \ - $(IBUS_LIBS) \ - $(FCITX_GCLIENT_LIBS) \ - $(ACCOUNTSSERVICE_LIBS) \ - $(LIGHTDM_LIBS) \ - $(COVERAGE_LDFLAGS) diff --git a/src/common.vala b/src/common.vala deleted file mode 100644 index 9824bc26..00000000 --- a/src/common.vala +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -string? abbreviate (string? name) { - var index = 0; - unichar first; - unichar second; - - if (name != null) { - if (((!) name).get_next_char (ref index, out first)) { - if (((!) name).get_next_char (ref index, out second)) { - return @"$((!) first.toupper ().to_string ())$((!) second.to_string ())"; - } else { - return first.toupper ().to_string (); - } - } else { - return ""; - } - } else { - return null; - } -} diff --git a/src/ibus-menu.vala b/src/ibus-menu.vala deleted file mode 100644 index a240f00b..00000000 --- a/src/ibus-menu.vala +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -public class Indicator.Keyboard.IBusMenu : MenuModel { - - private static uint radio_counter = 0; - - private IBus.PropList? properties; - - private Menu menu; - private ActionMap? action_map; - - private string? radio_name; - private SimpleAction? radio_action; - private Gee.HashMap<string, IBus.Property> radio_properties; - - /* A list of the action names this menu registers. */ - private Gee.LinkedList<string> names; - - public IBusMenu (ActionMap? action_map = null, IBus.PropList? properties = null) { - menu = new Menu (); - - menu.items_changed.connect ((position, removed, added) => { - items_changed (position, removed, added); - }); - - names = new Gee.LinkedList<string> (); - set_action_map (action_map); - set_properties (properties); - } - - ~IBusMenu () { - remove_actions (); - } - - public signal void activate (IBus.Property property, IBus.PropState state); - - private string get_action_name (string key) { - string name; - - if (!action_name_is_valid (key)) { - var builder = new StringBuilder.sized (key.length + 1); - - unichar letter = 0; - int index = 0; - - while (key.get_next_char (ref index, out letter)) { - if (letter == '-' || letter == '.' || letter.isalnum ()) { - builder.append_unichar (letter); - } else { - builder.append_c ('-'); - } - } - - name = @"ibus-$(builder.str)"; - } else { - name = @"ibus-$key"; - } - - /* Find an unused action name using a counter. */ - if (action_map != null && (Action?) ((!) action_map).lookup_action (name) != null) { - var i = 0; - var unique_name = @"$name-$i"; - - while ((Action?) ((!) action_map).lookup_action (unique_name) != null) { - i++; - unique_name = @"$name-$i"; - } - - name = unique_name; - } - - return name; - } - - private string? get_label (IBus.Property property) { - string? label = null; - - if ((IBus.Text?) property.label != null) { - label = property.label.text; - } - - if (label == null && (IBus.Text?) property.symbol != null) { - label = property.symbol.text; - } - - return label; - } - - private void append_normal_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.NORMAL) { - if ((string?) property.key != null) { - var name = get_action_name (property.key); - - if (action_map != null) { - var action = new SimpleAction (name, null); - action.activate.connect ((parameter) => { activate (property, property.state); }); - ((!) action_map).add_action (action); - names.add (name); - } - - menu.append (get_label (property), property.sensitive ? @"indicator.$name" : "-private-disabled"); - } - } - } - - private void append_toggle_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.TOGGLE) { - if ((string?) property.key != null) { - var name = get_action_name (property.key); - - if (action_map != null) { - var state = new Variant.boolean (property.state == IBus.PropState.CHECKED); - var action = new SimpleAction.stateful (name, null, state); - - action.change_state.connect ((value) => { - if (value != null) { - action.set_state ((!) value); - activate (property, ((!) value).get_boolean () ? IBus.PropState.CHECKED : IBus.PropState.UNCHECKED); - } - }); - - ((!) action_map).add_action (action); - names.add (name); - } - - menu.append (get_label (property), property.sensitive ? @"indicator.$name" : "-private-disabled"); - } - } - } - - private void append_radio_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.RADIO) { - if ((string?) property.key != null) { - /* Create a single action for all radio properties. */ - if (action_map != null && radio_name == null) { - radio_counter++; - - var name = @"-private-radio-$radio_counter"; - var action = new SimpleAction.stateful (name, VariantType.STRING, new Variant.string ("")); - - action.change_state.connect ((value) => { - if (value != null) { - var key = ((!) value).get_string (); - - if (radio_properties.has_key (key)) { - action.set_state ((!) value); - activate (radio_properties[key], IBus.PropState.CHECKED); - } - } - }); - - ((!) action_map).add_action (action); - names.add (name); - - radio_name = name; - radio_action = action; - } - - radio_properties[property.key] = property; - - if (property.state == IBus.PropState.CHECKED) { - ((!) radio_action).change_state (new Variant.string (property.key)); - } - - var item = new MenuItem (get_label (property), "-private-disabled"); - - if (property.sensitive) { - item.set_action_and_target_value (@"indicator.$((!) radio_name)", new Variant.string (property.key)); - } - - menu.append_item (item); - } - } - } - - private void append_menu_property (IBus.Property property) { - if (property.prop_type == IBus.PropType.MENU) { - var submenu = new IBusMenu (action_map, property.sub_props); - submenu.activate.connect ((property, state) => { activate (property, state); }); - menu.append_submenu (get_label (property), submenu); - } - } - - private void append_property (IBus.Property? property) { - if (property != null && ((!) property).visible) { - switch (((!) property).prop_type) { - case IBus.PropType.NORMAL: - append_normal_property ((!) property); - break; - - case IBus.PropType.TOGGLE: - append_toggle_property ((!) property); - break; - - case IBus.PropType.RADIO: - append_radio_property ((!) property); - break; - - case IBus.PropType.MENU: - append_menu_property ((!) property); - break; - - case IBus.PropType.SEPARATOR: - break; - } - } - } - - private void update_menu () { - /* Break reference cycle between action map and submenus. */ - for (var i = 0; i < menu.get_n_items (); i++) { - var submenu = menu.get_item_link (i, Menu.LINK_SUBMENU) as IBusMenu; - - if (submenu != null) { - ((!) submenu).remove_actions (); - } - } - - menu.remove_all (); - - if (properties != null) { - for (var i = 0; i < ((!) properties).properties.length; i++) { - append_property (((!) properties).get (i)); - } - } - } - - private void remove_actions () { - radio_action = null; - radio_name = null; - - if (action_map != null) { - foreach (var name in names) { - ((!) action_map).remove_action (name); - } - } - - names.clear (); - } - - public void set_action_map (ActionMap? action_map) { - if (action_map != this.action_map) { - remove_actions (); - this.action_map = action_map; - update_menu (); - } - } - - public void set_properties (IBus.PropList? properties) { - if (properties != this.properties) { - remove_actions (); - radio_properties = new Gee.HashMap<string, IBus.Property> (); - this.properties = properties; - update_menu (); - } - } - - public void update_property (IBus.Property property) { - remove_actions (); - radio_properties = new Gee.HashMap<string, IBus.Property> (); - update_menu (); - } - - /* Forward all menu model calls to our internal menu. */ - - public override Variant get_item_attribute_value (int item_index, string attribute, VariantType? expected_type) { - return menu.get_item_attribute_value (item_index, attribute, expected_type); - } - - public override void get_item_attributes (int item_index, out HashTable<string, Variant>? attributes) { - menu.get_item_attributes (item_index, out attributes); - } - - public override MenuModel get_item_link (int item_index, string link) { - return menu.get_item_link (item_index, link); - } - - public override void get_item_links (int item_index, out HashTable<string, MenuModel> links) { - menu.get_item_links (item_index, out links); - } - - public override int get_n_items () { - return menu.get_n_items (); - } - - public override bool is_mutable () { - return menu.is_mutable (); - } - - public override MenuAttributeIter iterate_item_attributes (int item_index) { - return menu.iterate_item_attributes (item_index); - } - - public override MenuLinkIter iterate_item_links (int item_index) { - return menu.iterate_item_links (item_index); - } -} diff --git a/src/ibus-panel.vala b/src/ibus-panel.vala deleted file mode 100644 index 2a380efd..00000000 --- a/src/ibus-panel.vala +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -[DBus (name="org.ayatana.IBus.Panel.Private")] -public interface IBusPanel : Object { - - public abstract void activate_property (string name, uint state) throws IOError; - - public signal void properties_registered (Variant variant); - public signal void property_updated (Variant variant); -} diff --git a/src/indicator-menu.vala b/src/indicator-menu.vala deleted file mode 100644 index 2cfa52c6..00000000 --- a/src/indicator-menu.vala +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -public class Indicator.Keyboard.IndicatorMenu : MenuModel { - - public enum Options { - NONE = 0, - DCONF = 1 << 0, - XKB = 1 << 1, - IBUS = 1 << 2, - SETTINGS = 1 << 3 - } - - private Options options; - - private Menu indicator_menu; - private Menu sources_section; - private IBusMenu properties_section; - - public IndicatorMenu (ActionMap? action_map = null, Options options = Options.NONE) { - this.options = options; - - indicator_menu = new Menu (); - sources_section = new Menu (); - - if ((options & ~Options.DCONF) != Options.NONE) { - var submenu = new Menu (); - - submenu.append_section (null, sources_section); - - if (Options.IBUS in options) { - properties_section = new IBusMenu (action_map); - properties_section.activate.connect ((property, state) => { activate (property, state); }); - submenu.append_section (null, properties_section); - } - - if (Options.SETTINGS in options) { - var settings_section = new Menu (); - settings_section.append (_ ("Character Map"), "indicator.map"); - settings_section.append (_ ("Keyboard Layout Chart"), "indicator.chart"); - settings_section.append (_ ("Text Entry Settings..."), "indicator.settings"); - submenu.append_section (null, settings_section); - } - - var indicator = new MenuItem.submenu (null, submenu); - indicator.set_detailed_action ("indicator.indicator"); - indicator.set_attribute ("x-canonical-type", "s", "org.ayatana.indicator.root"); - - /* We need special mouse actions on the lock screen. */ - if (Options.DCONF in options) { - indicator.set_attribute ("x-canonical-secondary-action", "s", "indicator.next"); - indicator.set_attribute ("x-canonical-scroll-action", "s", "indicator.scroll"); - } else { - indicator.set_attribute ("x-canonical-secondary-action", "s", "indicator.locked_next"); - indicator.set_attribute ("x-canonical-scroll-action", "s", "indicator.locked_scroll"); - } - - indicator_menu.append_item (indicator); - } - } - - public signal void activate (IBus.Property property, IBus.PropState state); - - public void set_sources (Source[] sources) { - sources_section.remove_all (); - - for (var i = 0; i < sources.length; i++) { - var visible = (sources[i].is_xkb && Options.XKB in options) || - (sources[i].is_ibus && Options.IBUS in options); - - if (visible) { - string action; - - if (Options.DCONF in options) { - action = "indicator.current"; - } else { - action = "indicator.active"; - } - - var item = new MenuItem (sources[i].name, action); - - item.set_attribute (Menu.ATTRIBUTE_TARGET, "u", i); - - if (sources[i].icon != null) { - item.set_icon ((!) sources[i].icon); - } - - sources_section.append_item (item); - } - } - } - - public void set_properties (IBus.PropList properties) { - if (Options.IBUS in options) { - properties_section.set_properties (properties); - } - } - - public void update_property (IBus.Property property) { - if (Options.IBUS in options) { - properties_section.update_property (property); - } - } - - public override bool is_mutable () { - return indicator_menu.is_mutable (); - } - - public override int get_n_items () { - return indicator_menu.get_n_items (); - } - - public override void get_item_attributes (int item_index, out HashTable<string, Variant>? attributes) { - indicator_menu.get_item_attributes (item_index, out attributes); - } - - public override void get_item_links (int item_index, out HashTable<string, MenuModel> links) { - indicator_menu.get_item_links (item_index, out links); - } - - public override Variant get_item_attribute_value (int item_index, string attribute, VariantType? expected_type) { - return indicator_menu.get_item_attribute_value (item_index, attribute, expected_type); - } - - public override MenuModel get_item_link (int item_index, string link) { - return indicator_menu.get_item_link (item_index, link); - } - - public override MenuAttributeIter iterate_item_attributes (int item_index) { - return indicator_menu.iterate_item_attributes (item_index); - } - - public override MenuLinkIter iterate_item_links (int item_index) { - return indicator_menu.iterate_item_links (item_index); - } -} diff --git a/src/keyboard-plugin.vala b/src/keyboard-plugin.vala deleted file mode 100644 index af1628f6..00000000 --- a/src/keyboard-plugin.vala +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -[DBus (name="org.ayatana.SettingsDaemon.Keyboard.Private")] -public interface KeyboardPlugin : Object { - - public abstract void activate_input_source (uint index) throws IOError; -} diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100644 index 00000000..1c0b22a0 --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,287 @@ +#include <X11/XKBlib.h> +#include <libxklavier/xklavier.h> +#include "keyboard.h" + +enum +{ + LAYOUT_CHANGED, + CONFIG_CHANGED, + LAST_SIGNAL +}; + +static guint m_lSignals[LAST_SIGNAL]; + +struct _KeyboardPrivate +{ + GPollFD cPollFD; + GSourceFuncs cSourceFuncs; + XklEngine *pEngine; + GHashTable *lLayouts; + Display *pDisplay; + guint nLayout; + gint nXkbEventType; + XklConfigRec *pConfigRec; +}; + +typedef KeyboardPrivate priv_t; + +G_DEFINE_TYPE_WITH_PRIVATE(Keyboard, keyboard, G_TYPE_OBJECT) + +typedef struct _Layout +{ + gchar *sId; + gchar *sLanguage; + gchar *sDescription; + +} Layout; + +typedef struct _LayoutParser +{ + const gchar *sLayout; + const gchar *sLanguage; + Keyboard *pKeyboard; + +} LayoutParser; + +typedef struct _Source +{ + GSource cSource; + Keyboard *pKeyboard; + +} Source; + +static gboolean onCheckEvent(Display *pDisplay, XEvent *pEvent, XPointer pData) +{ + gint *pXkbEventType = (gint*)pData; + + if (pEvent->type == *pXkbEventType) + { + XkbEvent *pXkbEvent = (XkbEvent*)pEvent; + + if (pXkbEvent->any.xkb_type == XkbStateNotify || pXkbEvent->any.xkb_type == XkbNamesNotify) + { + return TRUE; + } + } + + return FALSE; +} + +static gboolean onCheck(GSource *pSource) +{ + Keyboard *pKeyboard = ((Source*)pSource)->pKeyboard; + XEvent cEvent; + gboolean bEvent = XCheckIfEvent(pKeyboard->pPrivate->pDisplay, &cEvent, onCheckEvent, (XPointer)&pKeyboard->pPrivate->nXkbEventType); + + if (bEvent) + { + XklConfigRec *pConfigRec = xkl_config_rec_new(); + xkl_config_rec_get_from_server(pConfigRec, pKeyboard->pPrivate->pEngine); + gboolean bConfigChanged = FALSE; + gboolean bLayoutChanged = FALSE; + + if (!xkl_config_rec_equals(pKeyboard->pPrivate->pConfigRec, pConfigRec)) + { + if (g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts) > g_strv_length(pConfigRec->layouts)) + { + xkl_engine_lock_group(pKeyboard->pPrivate->pEngine, 0); + pKeyboard->pPrivate->nLayout = 0; + bLayoutChanged = TRUE; + } + + xkl_config_rec_get_from_server(pKeyboard->pPrivate->pConfigRec, pKeyboard->pPrivate->pEngine); + bConfigChanged = TRUE; + } + + g_object_unref(pConfigRec); + pConfigRec = NULL; + + if (((XkbEvent*)&cEvent)->any.xkb_type == XkbStateNotify && !bConfigChanged && ((XkbEvent*)&cEvent)->state.group != pKeyboard->pPrivate->nLayout && ((XkbEvent*)&cEvent)->state.group < g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts)) + { + pKeyboard->pPrivate->nLayout = ((XkbEvent*)&cEvent)->state.group; + bLayoutChanged = TRUE; + } + + if (bLayoutChanged) + { + g_signal_emit(pKeyboard, m_lSignals[LAYOUT_CHANGED], 0); + } + + if (bConfigChanged) + { + g_signal_emit(pKeyboard, m_lSignals[CONFIG_CHANGED], 0); + } + } + + return FALSE; +} + +static void freeLayout(gpointer pData) +{ + Layout *pLayout = pData; + + g_return_if_fail(pLayout != NULL); + + g_free(pLayout->sId); + g_free(pLayout->sLanguage); + g_free(pLayout->sDescription); + g_slice_free(Layout, pLayout); +} + +static void onParseLayouts(XklConfigRegistry *pRegistry, const XklConfigItem * pItem, gpointer pData) +{ + LayoutParser *pLayoutParser = (LayoutParser*)pData; + Layout *pLayout = g_slice_new0(Layout); + + if (pLayoutParser->sLayout) + { + pLayout->sId = g_strjoin("+", pLayoutParser->sLayout, pItem->name, NULL); + pLayout->sLanguage = g_strdup(pLayoutParser->sLanguage); + pLayout->sDescription = g_strdup(pItem->description); + } + else + { + pLayout->sId = g_strdup(pItem->name); + pLayout->sLanguage = g_strdup(pItem->short_description); + pLayout->sDescription = g_strdup(pItem->description); + } + + g_hash_table_replace(pLayoutParser->pKeyboard->pPrivate->lLayouts, pLayout->sId, pLayout); + + if (pLayoutParser->sLayout == NULL) + { + LayoutParser cLayoutParser; + cLayoutParser.sLayout = pItem->name; + cLayoutParser.pKeyboard = pLayoutParser->pKeyboard; + cLayoutParser.sLanguage = pItem->short_description; + + xkl_config_registry_foreach_layout_variant(pRegistry, pItem->name, onParseLayouts, &cLayoutParser); + } +} + +void keyboard_AddSource(Keyboard *pKeyboard) +{ + XkbQueryExtension(pKeyboard->pPrivate->pDisplay, 0, &pKeyboard->pPrivate->nXkbEventType, 0, 0, 0); + XkbSelectEventDetails(pKeyboard->pPrivate->pDisplay, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); + + pKeyboard->pPrivate->cPollFD.fd = ConnectionNumber(pKeyboard->pPrivate->pDisplay); + pKeyboard->pPrivate->cPollFD.events = G_IO_IN | G_IO_HUP | G_IO_ERR; + pKeyboard->pPrivate->cPollFD.revents = 0; + pKeyboard->pPrivate->cSourceFuncs.prepare = NULL; + pKeyboard->pPrivate->cSourceFuncs.check = onCheck; + pKeyboard->pPrivate->cSourceFuncs.dispatch = NULL; + pKeyboard->pPrivate->cSourceFuncs.finalize = NULL; + + GSource *pSource = g_source_new(&pKeyboard->pPrivate->cSourceFuncs, sizeof(Source)); + ((Source*)pSource)->pKeyboard = pKeyboard; + g_source_add_poll(pSource, &pKeyboard->pPrivate->cPollFD); + g_source_attach(pSource, NULL); +} + +guint keyboard_GetNumLayouts(Keyboard *pKeyboard) +{ + return g_strv_length(pKeyboard->pPrivate->pConfigRec->layouts); +} + +void keyboard_GetLayout(Keyboard *pKeyboard, gint nLayout, gchar **pLanguage, gchar **pDescription) +{ + if (nLayout == -1) + { + nLayout = pKeyboard->pPrivate->nLayout; + } + + gchar *sLayout = pKeyboard->pPrivate->pConfigRec->layouts[nLayout]; + gchar *sVariant = pKeyboard->pPrivate->pConfigRec->variants[nLayout]; + gchar *sId; + + if (strlen(sVariant)) + { + sId = g_strconcat(sLayout, "+", sVariant, NULL); + } + else + { + sId = g_strdup(sLayout); + } + + const Layout *pLayout; + g_hash_table_lookup_extended(pKeyboard->pPrivate->lLayouts, sId, NULL, (gpointer*)&pLayout); + + if (pLanguage != NULL) + { + *pLanguage = g_strndup(pLayout->sLanguage, 2); + } + + if (pDescription != NULL) + { + *pDescription = g_strdup(pLayout->sDescription); + } + + g_free(sId); +} + +void keyboard_SetLayout(Keyboard *pKeyboard, gint nLayout) +{ + xkl_engine_lock_group(pKeyboard->pPrivate->pEngine, nLayout); +} + +static void onDispose(GObject *pObject) +{ + Keyboard *self = G_KEYBOARD(pObject); + + if (self->pPrivate->lLayouts) + { + g_hash_table_destroy(self->pPrivate->lLayouts); + } + + if (self->pPrivate->pConfigRec) + { + g_object_unref(self->pPrivate->pConfigRec); + self->pPrivate->pConfigRec = NULL; + } + + G_OBJECT_CLASS(keyboard_parent_class)->dispose(pObject); +} + +static void keyboard_class_init(KeyboardClass *klass) +{ + GObjectClass *pClass = G_OBJECT_CLASS(klass); + pClass->dispose = onDispose; + m_lSignals[LAYOUT_CHANGED] = g_signal_new(KEYBOARD_LAYOUT_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + m_lSignals[CONFIG_CHANGED] = g_signal_new(KEYBOARD_CONFIG_CHANGED, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +Keyboard* keyboard_new() +{ + GObject *pObject = g_object_new(G_TYPE_KEYBOARD, NULL); + + return G_KEYBOARD(pObject); +} + +static void keyboard_init(Keyboard *self) +{ + self->pPrivate = keyboard_get_instance_private(self); + self->pPrivate->pDisplay = XOpenDisplay(NULL); + + g_assert(self->pPrivate->pDisplay); + + self->pPrivate->pEngine = xkl_engine_get_instance(self->pPrivate->pDisplay); + + g_assert(self->pPrivate->pEngine); + + self->pPrivate->lLayouts = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, freeLayout); + XklConfigRegistry *pRegistry = xkl_config_registry_get_instance(self->pPrivate->pEngine); + xkl_config_registry_load(pRegistry, TRUE); + + LayoutParser cLayoutParser; + cLayoutParser.sLayout = NULL; + cLayoutParser.pKeyboard = self; + cLayoutParser.sLanguage = NULL; + xkl_config_registry_foreach_layout(pRegistry, onParseLayouts, &cLayoutParser); + + xkl_engine_start_listen(self->pPrivate->pEngine, XKLL_TRACK_KEYBOARD_STATE); + + self->pPrivate->pConfigRec = xkl_config_rec_new(); + xkl_config_rec_get_from_server(self->pPrivate->pConfigRec, self->pPrivate->pEngine); + XklState *pState = xkl_engine_get_current_state(self->pPrivate->pEngine); + self->pPrivate->nLayout = pState->group; +} diff --git a/src/keyboard.h b/src/keyboard.h new file mode 100644 index 00000000..50e8bff0 --- /dev/null +++ b/src/keyboard.h @@ -0,0 +1,37 @@ +#ifndef __KEYBOARD_H__ +#define __KEYBOARD_H__ + +G_BEGIN_DECLS + +#define KEYBOARD_LAYOUT_CHANGED "layout-changed" +#define KEYBOARD_CONFIG_CHANGED "config-changed" + +#define G_KEYBOARD(o) (G_TYPE_CHECK_INSTANCE_CAST((o), G_TYPE_KEYBOARD, Keyboard)) +#define G_TYPE_KEYBOARD (keyboard_get_type()) +#define G_IS_KEYBOARD(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), G_TYPE_KEYBOARD)) + +typedef struct _Keyboard Keyboard; +typedef struct _KeyboardClass KeyboardClass; +typedef struct _KeyboardPrivate KeyboardPrivate; + +struct _Keyboard +{ + GObject parent; + KeyboardPrivate *pPrivate; +}; + +struct _KeyboardClass +{ + GObjectClass parent_class; +}; + +GType keyboard_get_type(void); +Keyboard* keyboard_new(); +void keyboard_AddSource(Keyboard *pKeyboard); +guint keyboard_GetNumLayouts(Keyboard *pKeyboard); +void keyboard_GetLayout(Keyboard *pKeyboard, gint nLayout, gchar **pLanguage, gchar **pDescription); +void keyboard_SetLayout(Keyboard *pKeyboard, gint nLayout); + +G_END_DECLS + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..eb6a1b9a --- /dev/null +++ b/src/main.c @@ -0,0 +1,40 @@ +#include <locale.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-unix.h> +#include "service.h" + +static void onNameLost(gpointer instance G_GNUC_UNUSED, gpointer pLoop) +{ + g_message("exiting: service couldn't acquire or lost ownership of busname"); + g_main_loop_quit((GMainLoop*)pLoop); +} + +static gboolean onQuit(gpointer pData) +{ + GMainLoop *pLoop = (GMainLoop*)pData; + g_main_loop_quit(pLoop); + + return G_SOURCE_REMOVE; +} + +int main(int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) +{ + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + textdomain(GETTEXT_PACKAGE); + + IndicatorKeyboardService *pService = indicator_keyboard_service_new(NULL); + GMainLoop *pLoop = g_main_loop_new(NULL, FALSE); + + g_signal_connect(pService, "name-lost", G_CALLBACK(onNameLost), pLoop); + g_unix_signal_add(SIGINT, onQuit, pLoop); + + indicator_keyboard_service_AddKeyboardSource(pService); + + g_main_loop_run(pLoop); + g_main_loop_unref(pLoop); + g_clear_object(&pService); + + return 0; +} diff --git a/src/main.vala b/src/main.vala deleted file mode 100644 index 9bb3eb1f..00000000 --- a/src/main.vala +++ /dev/null @@ -1,1265 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <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; - } -} diff --git a/src/service.c b/src/service.c new file mode 100644 index 00000000..5353fb24 --- /dev/null +++ b/src/service.c @@ -0,0 +1,412 @@ +#include <glib/gi18n.h> +#include <gio/gio.h> +#include "service.h" +#include "utils.h" + +#define BUS_NAME "org.ayatana.indicator.keyboard" +#define BUS_PATH "/org/ayatana/indicator/keyboard" + +static guint m_nSignal = 0; + +enum +{ + SECTION_HEADER = (1 << 0), + SECTION_LAYOUTS = (1 << 1), + SECTION_SETTINGS = (1 << 2) +}; + +enum +{ + PROFILE_PHONE, + PROFILE_DESKTOP, + PROFILE_GREETER, + N_PROFILES +}; + +static const char * const m_lMenuNames[N_PROFILES] = +{ + "phone", + "desktop", + "greeter" +}; + +struct ProfileMenuInfo +{ + GMenu *pMenu; + GMenu *pSubmenu; + guint nExportId; +}; + +struct _IndicatorKeyboardServicePrivate +{ + GCancellable *pCancellable; + guint nOwnId; + guint nActionsId; + GDBusConnection *pConnection; + gboolean bMenusBuilt; + struct ProfileMenuInfo lMenus[N_PROFILES]; + GSimpleActionGroup *pActionGroup; + GSimpleAction *pHeaderAction; + GSimpleAction *pSettingsAction; + GSimpleAction *pLayoutAction; + GMenu *pLayoutSection; + Keyboard *pKeyboard; +}; + +typedef IndicatorKeyboardServicePrivate priv_t; + +G_DEFINE_TYPE_WITH_PRIVATE(IndicatorKeyboardService, indicator_keyboard_service, G_TYPE_OBJECT) + +static GVariant* createHeaderState(IndicatorKeyboardService *self) +{ + GVariantBuilder cBuilder; + g_variant_builder_init(&cBuilder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&cBuilder, "{sv}", "title", g_variant_new_string(_("Keyboard"))); + g_variant_builder_add(&cBuilder, "{sv}", "visible", g_variant_new_boolean(TRUE)); + + gchar *sLanguage; + keyboard_GetLayout(self->pPrivate->pKeyboard, -1, &sLanguage, NULL); + + gchar *sIcon = g_strconcat("ayatana-indicator-keyboard-", sLanguage, NULL); + g_free(sLanguage); + + GIcon *pIcon = g_themed_icon_new_with_default_fallbacks(sIcon); + g_free(sIcon); + g_variant_builder_add(&cBuilder, "{sv}", "accessible-desc", g_variant_new_string(_("Current keyboard layout"))); + + if (pIcon) + { + GVariant *pSerialized = g_icon_serialize(pIcon); + + if (pSerialized != NULL) + { + g_variant_builder_add(&cBuilder, "{sv}", "icon", pSerialized); + g_variant_unref(pSerialized); + } + + g_object_unref(pIcon); + } + + return g_variant_builder_end(&cBuilder); +} + +static GMenuModel* createDesktopLayoutSection(IndicatorKeyboardService *self, int nProfile) +{ + self->pPrivate->pLayoutSection = g_menu_new(); + + guint nLayouts = keyboard_GetNumLayouts(self->pPrivate->pKeyboard); + + for (guint nLayout = 0; nLayout < nLayouts; nLayout++) + { + gchar *sLanguage; + gchar *sDescription; + keyboard_GetLayout(self->pPrivate->pKeyboard, nLayout, &sLanguage, &sDescription); + GMenuItem *pItem = g_menu_item_new(sDescription, NULL); + g_free(sDescription); + g_menu_item_set_action_and_target_value(pItem, "indicator.layout", g_variant_new_byte(nLayout)); + g_menu_item_set_attribute_value(pItem, "x-ayatana-layout", g_variant_new_byte(nLayout)); + gchar *sIcon = g_strconcat("ayatana-indicator-keyboard-", sLanguage, NULL); + g_free(sLanguage); + GIcon *pIcon = g_themed_icon_new_with_default_fallbacks(sIcon); + g_free(sIcon); + GVariant *pSerialized = g_icon_serialize(pIcon); + + if (pSerialized != NULL) + { + g_menu_item_set_attribute_value(pItem, G_MENU_ATTRIBUTE_ICON, pSerialized); + g_variant_unref(pSerialized); + } + + g_object_unref(pIcon); + + g_menu_append_item(self->pPrivate->pLayoutSection, pItem); + g_object_unref(pItem); + } + + return G_MENU_MODEL(self->pPrivate->pLayoutSection); +} + +static GMenuModel* createDesktopSettingsSection(IndicatorKeyboardService *self) +{ + GMenu * pMenu = g_menu_new(); + g_menu_append(pMenu, _("Keyboard Settings..."), "indicator.settings"); + + return G_MENU_MODEL(pMenu); +} + +static void rebuildSection(GMenu *pMenu, int nPos, GMenuModel *pModel) +{ + g_menu_remove(pMenu, nPos); + g_menu_insert_section(pMenu, nPos, NULL, pModel); + g_object_unref(pModel); +} + +static void rebuildNow(IndicatorKeyboardService *self, guint nSections) +{ + struct ProfileMenuInfo *pInfoDesktop = &self->pPrivate->lMenus[PROFILE_DESKTOP]; + struct ProfileMenuInfo *pInfoGreeter = &self->pPrivate->lMenus[PROFILE_GREETER]; + + if (nSections & SECTION_HEADER) + { + g_simple_action_set_state(self->pPrivate->pHeaderAction, createHeaderState(self)); + } + + if (!self->pPrivate->bMenusBuilt) + { + return; + } + + if (nSections & SECTION_LAYOUTS) + { + rebuildSection(pInfoDesktop->pSubmenu, 0, createDesktopLayoutSection(self, PROFILE_DESKTOP)); + rebuildSection(pInfoGreeter->pSubmenu, 0, createDesktopLayoutSection(self, PROFILE_GREETER)); + } + + if (nSections & SECTION_SETTINGS) + { + rebuildSection(pInfoDesktop->pSubmenu, 1, createDesktopSettingsSection(self)); + } +} + +static void createMenu(IndicatorKeyboardService *self, int nProfile) +{ + GMenu *pMenu; + GMenu *pSubmenu; + GMenuItem *pItem; + GMenuModel *lSections[16]; + guint nSection = 0; + + g_assert(0 <= nProfile && nProfile < N_PROFILES); + g_assert(self->pPrivate->lMenus[nProfile].pMenu == NULL); + + // Build the sections + if (nProfile == PROFILE_PHONE) + { + lSections[nSection++] = createDesktopLayoutSection(self, nProfile); + lSections[nSection++] = createDesktopSettingsSection(self); + } + else if (nProfile == PROFILE_DESKTOP) + { + lSections[nSection++] = createDesktopLayoutSection(self, nProfile); + lSections[nSection++] = createDesktopSettingsSection(self); + } + else if (nProfile == PROFILE_GREETER) + { + lSections[nSection++] = createDesktopLayoutSection(self, nProfile); + } + + // Add sections to the submenu + pSubmenu = g_menu_new(); + + for (guint i = 0; i < nSection; ++i) + { + g_menu_append_section(pSubmenu, NULL, lSections[i]); + g_object_unref(lSections[i]); + } + + // Add submenu to the header + pItem = g_menu_item_new(NULL, "indicator._header"); + g_menu_item_set_attribute(pItem, "x-ayatana-type", "s", "org.ayatana.indicator.root"); + g_menu_item_set_submenu(pItem, G_MENU_MODEL(pSubmenu)); + g_object_unref(pSubmenu); + + // Add header to the menu + pMenu = g_menu_new(); + g_menu_append_item(pMenu, pItem); + g_object_unref(pItem); + + self->pPrivate->lMenus[nProfile].pMenu = pMenu; + self->pPrivate->lMenus[nProfile].pSubmenu = pSubmenu; +} + +static void onLayoutChanged(Keyboard *pKeyboard, gpointer pData) +{ + IndicatorKeyboardService *self = INDICATOR_KEYBOARD_SERVICE(pData); + rebuildNow(self, SECTION_HEADER); +} + +static void onConfigChanged(Keyboard *pKeyboard, gpointer pData) +{ + IndicatorKeyboardService *self = INDICATOR_KEYBOARD_SERVICE(pData); + rebuildNow(self, SECTION_LAYOUTS); +} + +static void onLayoutSelected(GSimpleAction *pAction, GVariant *pVariant, gpointer pData) +{ + IndicatorKeyboardService *self = INDICATOR_KEYBOARD_SERVICE(pData); + const guint8 nLayout = g_variant_get_byte(pVariant); + keyboard_SetLayout(self->pPrivate->pKeyboard, nLayout); +} + +static void onSettings(GSimpleAction *pAction, GVariant *pVariant, gpointer pUserData) +{ + if (is_mate()) + { + execute_command("mate-keyboard-properties"); + } +} + +static void initActions(IndicatorKeyboardService *self) +{ + GSimpleAction *pAction; + self->pPrivate->pActionGroup = g_simple_action_group_new(); + + pAction = g_simple_action_new_stateful("_header", NULL, createHeaderState(self)); + g_action_map_add_action(G_ACTION_MAP(self->pPrivate->pActionGroup), G_ACTION(pAction)); + self->pPrivate->pHeaderAction = pAction; + + pAction = g_simple_action_new("layout", G_VARIANT_TYPE_BYTE); + g_action_map_add_action(G_ACTION_MAP(self->pPrivate->pActionGroup), G_ACTION(pAction)); + self->pPrivate->pLayoutAction = pAction; + g_signal_connect(pAction, "activate", G_CALLBACK(onLayoutSelected), self); + + pAction = g_simple_action_new("settings", NULL); + g_action_map_add_action(G_ACTION_MAP(self->pPrivate->pActionGroup), G_ACTION(pAction)); + self->pPrivate->pSettingsAction = pAction; + g_signal_connect(pAction, "activate", G_CALLBACK(onSettings), self); +} + +static void onBusAcquired(GDBusConnection *pConnection, const gchar *sName, gpointer pData) +{ + IndicatorKeyboardService *self = INDICATOR_KEYBOARD_SERVICE(pData); + + g_debug("bus acquired: %s", sName); + + self->pPrivate->pConnection = (GDBusConnection*)g_object_ref(G_OBJECT (pConnection)); + guint nId; + GError *pError = NULL; + + // Export the actions + if ((nId = g_dbus_connection_export_action_group(pConnection, BUS_PATH, G_ACTION_GROUP(self->pPrivate->pActionGroup), &pError))) + { + self->pPrivate->nActionsId = nId; + } + else + { + g_warning("cannot export action group: %s", pError->message); + g_clear_error(&pError); + } + + GString *pPath = g_string_new(NULL); + + // Export the menus + for (int nProfile = 0; nProfile < N_PROFILES; ++nProfile) + { + struct ProfileMenuInfo *pInfo = &self->pPrivate->lMenus[nProfile]; + + g_string_printf(pPath, "%s/%s", BUS_PATH, m_lMenuNames[nProfile]); + + if ((nId = g_dbus_connection_export_menu_model(pConnection, pPath->str, G_MENU_MODEL(pInfo->pMenu), &pError))) + { + pInfo->nExportId = nId; + } + else + { + g_warning("cannot export %s menu: %s", pPath->str, pError->message); + g_clear_error (&pError); + } + } + + g_string_free(pPath, TRUE); +} + +static void unexport(IndicatorKeyboardService *self) +{ + // Unexport the menus + for (int nProfile = 0; nProfile < N_PROFILES; ++nProfile) + { + guint *nId = &self->pPrivate->lMenus[nProfile].nExportId; + + if (*nId) + { + g_dbus_connection_unexport_menu_model(self->pPrivate->pConnection, *nId); + *nId = 0; + } + } + + // Unexport the actions + if (self->pPrivate->nActionsId) + { + g_dbus_connection_unexport_action_group(self->pPrivate->pConnection, self->pPrivate->nActionsId); + self->pPrivate->nActionsId = 0; + } +} + +static void onNameLost(GDBusConnection *pConnection, const gchar *sName, gpointer pData) +{ + IndicatorKeyboardService *self = INDICATOR_KEYBOARD_SERVICE(pData); + + g_debug("%s %s name lost %s", G_STRLOC, G_STRFUNC, sName); + + unexport(self); +} + +static void onDispose(GObject *pObject) +{ + IndicatorKeyboardService *self = INDICATOR_KEYBOARD_SERVICE(pObject); + + if (self->pPrivate->pKeyboard != NULL) + { + g_object_unref(G_OBJECT(self->pPrivate->pKeyboard)); + self->pPrivate->pKeyboard = NULL; + } + + if (self->pPrivate->nOwnId) + { + g_bus_unown_name(self->pPrivate->nOwnId); + self->pPrivate->nOwnId = 0; + } + + unexport(self); + + if (self->pPrivate->pCancellable != NULL) + { + g_cancellable_cancel(self->pPrivate->pCancellable); + g_clear_object(&self->pPrivate->pCancellable); + } + + g_clear_object (&self->pPrivate->pSettingsAction); + g_clear_object (&self->pPrivate->pLayoutAction); + g_clear_object (&self->pPrivate->pHeaderAction); + g_clear_object (&self->pPrivate->pActionGroup); + g_clear_object (&self->pPrivate->pConnection); + + G_OBJECT_CLASS(indicator_keyboard_service_parent_class)->dispose(pObject); +} + +static void indicator_keyboard_service_init(IndicatorKeyboardService *self) +{ + self->pPrivate = indicator_keyboard_service_get_instance_private(self); + self->pPrivate->pCancellable = g_cancellable_new(); + self->pPrivate->pKeyboard = keyboard_new(); + g_signal_connect(self->pPrivate->pKeyboard, KEYBOARD_LAYOUT_CHANGED, G_CALLBACK(onLayoutChanged), self); + g_signal_connect(self->pPrivate->pKeyboard, KEYBOARD_CONFIG_CHANGED, G_CALLBACK(onConfigChanged), self); + initActions(self); + + for (int nProfile = 0; nProfile < N_PROFILES; ++nProfile) + { + createMenu(self, nProfile); + } + + self->pPrivate->bMenusBuilt = TRUE; + self->pPrivate->nOwnId = g_bus_own_name(G_BUS_TYPE_SESSION, BUS_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, onBusAcquired, NULL, onNameLost, self, NULL); +} + +static void indicator_keyboard_service_class_init(IndicatorKeyboardServiceClass *klass) +{ + GObjectClass *pClass = G_OBJECT_CLASS(klass); + pClass->dispose = onDispose; + m_nSignal = g_signal_new("name-lost", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(IndicatorKeyboardServiceClass, pNameLost), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +IndicatorKeyboardService *indicator_keyboard_service_new() +{ + GObject *pObject = g_object_new(INDICATOR_TYPE_KEYBOARD_SERVICE, NULL); + + return INDICATOR_KEYBOARD_SERVICE(pObject); +} + +void indicator_keyboard_service_AddKeyboardSource(IndicatorKeyboardService *self) +{ + keyboard_AddSource(self->pPrivate->pKeyboard); +} diff --git a/src/service.h b/src/service.h new file mode 100644 index 00000000..9a758568 --- /dev/null +++ b/src/service.h @@ -0,0 +1,36 @@ +#ifndef __INDICATOR_KEYBOARD_SERVICE_H__ +#define __INDICATOR_KEYBOARD_SERVICE_H__ + +#include <glib.h> +#include <glib-object.h> +#include "keyboard.h" + +G_BEGIN_DECLS + +#define INDICATOR_KEYBOARD_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), INDICATOR_TYPE_KEYBOARD_SERVICE, IndicatorKeyboardService)) +#define INDICATOR_TYPE_KEYBOARD_SERVICE (indicator_keyboard_service_get_type()) +#define INDICATOR_IS_KEYBOARD_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), INDICATOR_TYPE_KEYBOARD_SERVICE)) + +typedef struct _IndicatorKeyboardService IndicatorKeyboardService; +typedef struct _IndicatorKeyboardServiceClass IndicatorKeyboardServiceClass; +typedef struct _IndicatorKeyboardServicePrivate IndicatorKeyboardServicePrivate; + +struct _IndicatorKeyboardService +{ + GObject parent; + IndicatorKeyboardServicePrivate *pPrivate; +}; + +struct _IndicatorKeyboardServiceClass +{ + GObjectClass parent_class; + void (*pNameLost)(IndicatorKeyboardService *self); +}; + +GType indicator_keyboard_service_get_type(void); +IndicatorKeyboardService* indicator_keyboard_service_new(); +void indicator_keyboard_service_AddKeyboardSource(IndicatorKeyboardService *pService); + +G_END_DECLS + +#endif /* __INDICATOR_KEYBOARD_SERVICE_H__ */ diff --git a/src/source.vala b/src/source.vala deleted file mode 100644 index b7d7a971..00000000 --- a/src/source.vala +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -public class Indicator.Keyboard.Source : Object { - - private static Gnome.XkbInfo? xkb_info; - private static IBus.Bus? ibus_bus; - private static Fcitx.InputMethod? fcitx_proxy; - - private string? xkb; - private string? ibus; - private string? fcitx; - - private string? _name; - private string? _short_name; - private string? _layout; - private string? _variant; - private Icon? _icon; - private uint _subscript; - private bool _show_subscript; - private bool _use_gtk; - - public string? name { - get { if (_name == null) { _name = _get_name (); } return _name; } - } - - public string? short_name { - get { if (_short_name == null) { _short_name = _get_short_name (); } return _short_name; } - } - - public string? layout { - get { if (_layout == null) { _layout = _get_layout (); } return _layout; } - } - - public string? variant { - get { if (_variant == null) { _variant = _get_variant (); } return _variant; } - } - - public Icon? icon { - get { if (_icon == null) { _icon = _get_icon (); } return _icon; } - set { _icon = value; } - } - - public uint subscript { - get { return _subscript; } - set { _subscript = value; icon = null; } - } - - public bool show_subscript { - get { return _show_subscript; } - set { _show_subscript = value; icon = null; } - } - - public bool use_gtk { - get { return _use_gtk; } - construct set { _use_gtk = value; icon = null; } - } - - public bool is_xkb { - get { return xkb != null; } - } - - public bool is_ibus { - get { return ibus != null; } - } - - public bool is_fcitx { - get { return fcitx != null; } - } - - public Source (Variant variant, bool use_gtk = false) { - Object (use_gtk: use_gtk); - - if (variant.is_of_type (new VariantType ("(ss)"))) { - unowned string type; - unowned string name; - - variant.get ("(&s&s)", out type, out name); - - if (type == "xkb") { - xkb = name; - } else if (type == "ibus") { - ibus = name; - } else if (type == "fcitx") { - fcitx = name; - } - } else if (variant.is_of_type (new VariantType ("a{ss}"))) { - foreach (var pair in variant) { - unowned string key; - unowned string value; - - ((!) pair).get ("{&s&s}", out key, out value); - - if (key == "xkb") { - xkb = value; - } else if (key == "ibus") { - ibus = value; - } else if (key == "fcitx") { - fcitx = value; - } - } - } - } - - private static Gnome.XkbInfo get_xkb_info () { - if (xkb_info == null) { - xkb_info = new Gnome.XkbInfo (); - } - - return (!) xkb_info; - } - - private static IBus.Bus get_ibus_bus () { - if (ibus_bus == null) { - IBus.init (); - ibus_bus = new IBus.Bus (); - } - - return (!) ibus_bus; - } - - private static Fcitx.InputMethod get_fcitx_proxy () throws Error { - if (fcitx_proxy == null) { - fcitx_proxy = new Fcitx.InputMethod (BusType.SESSION, DBusProxyFlags.NONE, 0); - } - - return (!) fcitx_proxy; - } - - private IBus.EngineDesc? get_engine () { - IBus.EngineDesc? engine = null; - - if (ibus != null) { - var names = new string[2]; - names[0] = (!) ibus; - - var engines = get_ibus_bus ().get_engines_by_names (names); - - if (engines.length > 0) { - engine = engines[0]; - } - } - - return engine; - } - - protected virtual string? _get_name () { - string? name = null; - - if (xkb != null) { - string? display_name = null; - string? layout = null; - - get_xkb_info ().get_layout_info ((!) xkb, out display_name, null, out layout, null); - - var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; - var has_layout = layout != null && ((!) layout).get_char () != '\0'; - - if (has_display_name) { - name = display_name; - } else if (has_layout) { - string? language = Xkl.get_language_name ((!) layout); - string? country = Xkl.get_country_name ((!) layout); - var has_language = language != null && ((!) language).get_char () != '\0'; - var has_country = country != null && ((!) country).get_char () != '\0'; - - if (has_language && has_country) { - name = @"$((!) language) ($((!) country))"; - } else if (has_language) { - name = language; - } else if (has_country) { - name = country; - } - } - - if (name == null || ((!) name).get_char () == '\0') { - name = xkb; - } - } else if (ibus != null) { - var engine = get_engine (); - - if (engine != null) { - string? language = ((!) engine).get_language (); - string? display_name = ((!) engine).get_longname (); - var has_language = language != null && ((!) language).get_char () != '\0'; - var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; - - if (has_language) { - language = Xkl.get_language_name ((!) language); - has_language = language != null && ((!) language).get_char () != '\0'; - } - - if (has_language && has_display_name) { - name = @"$((!) language) ($((!) display_name))"; - } else if (has_language) { - name = language; - } else if (has_display_name) { - name = display_name; - } - } - - if (name == null || ((!) name).get_char () == '\0') { - name = ibus; - } - } else if (fcitx != null) { - try { - var input_methods = get_fcitx_proxy ().get_imlist_nofree (); - - for (var i = 0; i < input_methods.length; i++) { - if (input_methods.get (i).unique_name == (!) fcitx) { - name = input_methods.get (i).name; - break; - } - } - } catch (Error error) { - warning ("error: %s", error.message); - } - - if (name == null || ((!) name).get_char () == '\0') { - name = fcitx; - } - } - - return name; - } - - protected virtual string? _get_short_name () { - string? short_name = null; - - if (xkb != null) { - get_xkb_info ().get_layout_info ((!) xkb, null, out short_name, null, null); - - if (short_name == null || ((!) short_name).get_char () == '\0') { - short_name = xkb; - } - } else if (ibus != null) { - var engine = get_engine (); - - if (engine != null) { - short_name = ((!) engine).get_name (); - } - - if (short_name == null || ((!) short_name).get_char () == '\0') { - short_name = ibus; - } - } else if (fcitx != null) { - try { - var input_methods = get_fcitx_proxy ().get_imlist_nofree (); - - for (var i = 0; i < input_methods.length; i++) { - if (input_methods.get (i).unique_name == (!) fcitx) { - short_name = input_methods.get (i).langcode; - break; - } - } - } catch (Error error) { - warning ("error: %s", error.message); - } - - if (short_name == null || ((!) short_name).get_char () == '\0') { - short_name = fcitx; - } - } - - return abbreviate (short_name); - } - - protected virtual string? _get_layout () { - string? layout = null; - - if (xkb != null) { - get_xkb_info ().get_layout_info ((!) xkb, null, null, out layout, null); - } - - var has_layout = layout != null && ((!) layout).get_char () != '\0'; - - if (!has_layout) { - var engine = get_engine (); - - if (engine != null) { - layout = ((!) engine).get_layout (); - } - } - - if (layout == null || ((!) layout).get_char () == '\0') { - layout = xkb; - } - - return layout; - } - - protected virtual string? _get_variant () { - string? variant = null; - - if (xkb != null) { - get_xkb_info ().get_layout_info ((!) xkb, null, null, null, out variant); - } - - var has_variant = variant != null && ((!) variant).get_char () != '\0'; - - if (!has_variant) { - var engine = get_engine (); - - if (engine != null) { - variant = ((!) engine).get_layout_variant (); - } - } - - if (variant == null || ((!) variant).get_char () == '\0') { - variant = null; - } - - return variant; - } - - private Gtk.StyleContext? get_style_context () { - Gtk.StyleContext? context = null; - - if (_use_gtk) { - Gdk.Screen? screen = Gdk.Screen.get_default (); - - if (screen != null) { - var style_context = new Gtk.StyleContext (); - style_context.set_screen ((!) screen); - - var path = new Gtk.WidgetPath (); - path.append_type (typeof (Gtk.MenuItem)); - style_context.set_path (path); - - context = style_context; - } - } - - return context; - } - - protected virtual Icon? create_icon () { - Icon? icon = null; - - var style = get_style_context (); - - if (style != null) { - const int W = 22; - const int H = 22; - const int w = 20; - const int h = 20; - const double R = 2.0; - const double TEXT_SIZE = 12.0; - const double SUBSCRIPT_SIZE = 8.0; - - Pango.FontDescription description; - var colour = ((!) style).get_color (Gtk.StateFlags.NORMAL); - colour = { 0.5, 0.5, 0.5, 1.0 }; - ((!) style).get (Gtk.StateFlags.NORMAL, Gtk.STYLE_PROPERTY_FONT, out description); - - var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, W, H); - var context = new Cairo.Context (surface); - - context.translate (0.5 * (W - w), 0.5 * (H - h)); - - context.new_sub_path (); - context.arc (R, R, R, Math.PI, -0.5 * Math.PI); - context.arc (w - R, R, R, -0.5 * Math.PI, 0); - context.arc (w - R, h - R, R, 0, 0.5 * Math.PI); - context.arc (R, h - R, R, 0.5 * Math.PI, Math.PI); - context.close_path (); - - context.set_source_rgba (colour.red, colour.green, colour.blue, colour.alpha); - context.fill (); - context.set_operator (Cairo.Operator.CLEAR); - - if (short_name != null) { - var text_layout = Pango.cairo_create_layout (context); - text_layout.set_alignment (Pango.Alignment.CENTER); - description.set_absolute_size (Pango.units_from_double (TEXT_SIZE)); - text_layout.set_font_description (description); - text_layout.set_text ((!) short_name, -1); - Pango.cairo_update_layout (context, text_layout); - int text_width; - int text_height; - text_layout.get_pixel_size (out text_width, out text_height); - - if (_show_subscript) { - var subscript_layout = Pango.cairo_create_layout (context); - subscript_layout.set_alignment (Pango.Alignment.CENTER); - description.set_absolute_size (Pango.units_from_double (SUBSCRIPT_SIZE)); - subscript_layout.set_font_description (description); - subscript_layout.set_text (@"$_subscript", -1); - Pango.cairo_update_layout (context, subscript_layout); - int subscript_width; - int subscript_height; - subscript_layout.get_pixel_size (out subscript_width, out subscript_height); - - context.save (); - context.translate ((w - (text_width + subscript_width)) / 2, (h - text_height) / 2); - Pango.cairo_layout_path (context, text_layout); - context.fill (); - context.restore (); - - context.save (); - context.translate ((w + (text_width - subscript_width)) / 2, (h + text_height) / 2 - subscript_height); - Pango.cairo_layout_path (context, subscript_layout); - context.fill (); - context.restore (); - } else { - context.save (); - context.translate ((w - text_width) / 2, (h - text_height) / 2); - Pango.cairo_layout_path (context, text_layout); - context.fill (); - context.restore (); - } - } - - var buffer = new ByteArray (); - - surface.write_to_png_stream ((data) => { - buffer.append (data); - return Cairo.Status.SUCCESS; - }); - - icon = new BytesIcon (ByteArray.free_to_bytes ((owned) buffer)); - } - - return icon; - } - - private Icon? _get_icon () { - Icon? icon = null; - - var engine = get_engine (); - - if (engine != null) { - string? icon_name = ((!) engine).get_icon (); - var has_icon_name = icon_name != null && ((!) icon_name).get_char () != '\0'; - - if (has_icon_name) { - try { - icon = Icon.new_for_string ((!) icon_name); - } catch (Error error) { - warning ("error: %s", error.message); - } - } - } - - if (icon == null && short_name != null) { - string icon_name; - - if (_show_subscript) { - icon_name = @"indicator-keyboard-$((!) short_name)-$_subscript"; - } else { - icon_name = @"indicator-keyboard-$((!) short_name)"; - } - - if (_use_gtk) { - var icon_theme = Gtk.IconTheme.get_default (); - Gtk.IconInfo? icon_info = icon_theme.lookup_icon (icon_name, 22, 0); - - if (icon_info != null) { - icon = new ThemedIcon (icon_name); - } - } else { - icon = new ThemedIcon (icon_name); - } - } - - if (icon == null) { - icon = create_icon (); - } - - return icon; - } -} diff --git a/src/unity-greeter.vala b/src/unity-greeter.vala deleted file mode 100644 index 5ca398ec..00000000 --- a/src/unity-greeter.vala +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -[DBus (name="org.ayatana.UnityGreeter.List")] -public interface UnityGreeter : Object { - - public abstract string get_active_entry () throws IOError; - public abstract void set_active_entry (string entry_name) throws IOError; - - public signal void entry_selected (string entry_name); -} diff --git a/src/unity-session.vala b/src/unity-session.vala deleted file mode 100644 index 15337c36..00000000 --- a/src/unity-session.vala +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -[DBus (name="org.ayatana.Unity.Session")] -public interface UnitySession : Object { - - public signal void locked (); - public signal void unlocked (); -} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 00000000..82f9cff0 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,167 @@ +/* + * Copyright 2021 Marius Gripsgard <marius@ubports.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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/>. + */ + +#include "utils.h" + +#include <string.h> + +#ifdef HAS_URLDISPATCHER +# include <url-dispatcher.h> +#endif + +// TODO: make case insensitive +gboolean +is_xdg_current_desktop (const gchar* desktop) +{ + const gchar *xdg_current_desktop; + gchar **desktop_names; + int i; + + xdg_current_desktop = g_getenv ("XDG_CURRENT_DESKTOP"); + if (xdg_current_desktop != NULL) { + desktop_names = g_strsplit (xdg_current_desktop, ":", 0); + for (i = 0; desktop_names[i]; ++i) { + if (!g_strcmp0 (desktop_names[i], desktop)) { + g_strfreev (desktop_names); + return TRUE; + } + } + g_strfreev (desktop_names); + } + return FALSE; +} + +gboolean +is_lomiri () +{ + // For legacy reasons keep the MIR_SOCKET hack + return (g_getenv ("MIR_SOCKET") != NULL || + is_xdg_current_desktop(DESKTOP_LOMIRI)); +} + +gboolean +is_gnome () +{ + return is_xdg_current_desktop(DESKTOP_GNOME); +} + +gboolean +is_unity () +{ + return is_xdg_current_desktop(DESKTOP_UNITY); +} + +gboolean +is_mate () +{ + return is_xdg_current_desktop(DESKTOP_MATE); +} + +gboolean +is_xfce () +{ + return is_xdg_current_desktop(DESKTOP_XFCE); +} + +gboolean +is_pantheon () +{ + return is_xdg_current_desktop(DESKTOP_PANTHEON); +} + +// Bit of a hacky way? should use xdg open +char * +find_browser () +{ + static char * browser_path = NULL; + char* tmp_browser_path; + gchar **browser_names; + + int i; + + if (browser_path == NULL) + { + browser_names = g_strsplit ("x-www-browser,google-chrome,firefox,chromium", ",", 0); + + for (i = 0; browser_names[i]; ++i) { + tmp_browser_path = g_find_program_in_path (browser_names[i]); + + if (tmp_browser_path) { + browser_path = g_strdup (tmp_browser_path); + g_free (tmp_browser_path); + g_strfreev (browser_names); + break; + } + } + } + + return browser_path; +} + +gboolean +execute_command (const gchar * cmd) +{ + GError * err = NULL; + + g_debug ("Issuing command '%s'", cmd); + + if (!g_spawn_command_line_async (cmd, &err)) + { + g_warning ("Unable to start %s: %s", cmd, err->message); + g_error_free (err); + return FALSE; + } + + return TRUE; +} + +gboolean +open_url (const gchar * url) +{ + char * browser = NULL; + + if (is_lomiri()) + { +#ifdef HAS_URLDISPATCHER + url_dispatch_send("settings:///system/battery", NULL, NULL); + return TRUE; +#else + g_warning("Built without url-dispatcher, is not able to open url"); +#endif + } + + if (browser == NULL) + browser = find_browser(); + + if (browser != NULL) + return execute_command(g_strdup_printf("%s '%s'", browser, url)); + else + return FALSE; + +} + +gboolean +have_program (const gchar * program) +{ + gchar *path; + gboolean have; + + path = g_find_program_in_path(program); + have = path != NULL; + g_free(path); + + return have; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 00000000..52e491f2 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,38 @@ +/* + * Copyright 2021 Marius Gripsgard <marius@ubports.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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/>. + */ + + +#pragma once + +#include <glib.h> + +#define DESKTOP_LOMIRI "Lomiri" +#define DESKTOP_UNITY "Unity" +#define DESKTOP_MATE "MATE" +#define DESKTOP_GNOME "GNOME" +#define DESKTOP_XFCE "XFCE" +#define DESKTOP_PANTHEON "PANTHEON" + +gboolean is_lomiri(); +gboolean is_unity(); +gboolean is_gnome(); +gboolean is_mate(); +gboolean is_xfce(); +gboolean is_pantheon(); + +gboolean execute_command(const gchar * cmd); +gboolean open_url(const gchar * url); +gboolean have_program(const gchar * program); diff --git a/src/window-stack.vala b/src/window-stack.vala deleted file mode 100644 index a943da6a..00000000 --- a/src/window-stack.vala +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: William Hua <william.hua@canonical.com> - */ - -public struct WindowInfo { - - public uint window_id; - public string app_id; - public bool focused; - public uint stage; -} - -[DBus (name="org.ayatana.Unity.WindowStack")] -public interface WindowStack : Object { - - public abstract string get_app_id_from_pid (uint pid) throws IOError; - public abstract string[] get_window_properties (uint window_id, string app_id, string[] property_names) throws IOError; - public abstract WindowInfo[] get_window_stack () throws IOError; - - public signal void focused_window_changed (uint window_id, string app_id, uint stage); - public signal void window_created (uint window_id, string app_id); - public signal void window_destroyed (uint window_id, string app_id); -} |