diff -uNrp a/cinnamon-settings-daemon/main.c b/cinnamon-settings-daemon/main.c --- a/cinnamon-settings-daemon/main.c 2013-08-24 18:04:31.000000000 +0100 +++ b/cinnamon-settings-daemon/main.c 2013-08-25 16:36:02.000000000 +0100 @@ -319,6 +319,29 @@ set_legacy_ibus_env_vars (GDBusProxy *pr } #endif +static void +got_session_proxy (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GDBusProxy *proxy; + GError *error = NULL; + + proxy = g_dbus_proxy_new_finish (res, &error); + if (proxy == NULL) { + g_debug ("Could not connect to the Session manager: %s", error->message); + g_error_free (error); + } else { + set_locale (proxy); +#ifdef HAVE_IBUS + /* This will register with cinnamon-session after calling Setenv. */ + set_legacy_ibus_env_vars (proxy); +#else + register_with_gnome_session (proxy); +#endif + } +} + static gboolean on_term_signal_pipe_closed (GIOChannel *source, GIOCondition condition, @@ -368,6 +391,16 @@ set_session_over_handler (GDBusConnectio { g_assert (bus != NULL); + g_dbus_proxy_new (bus, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + GNOME_SESSION_DBUS_NAME, + GNOME_SESSION_DBUS_OBJECT, + GNOME_SESSION_DBUS_INTERFACE, + NULL, + (GAsyncReadyCallback) got_session_proxy, + NULL); + watch_for_term_signal (manager); } @@ -390,56 +423,6 @@ name_lost_handler (GDBusConnection *conn gtk_main_quit (); } -static gboolean -do_register_client (gpointer user_data) -{ - GDBusProxy *proxy = (GDBusProxy *) user_data; - g_assert (proxy != NULL); - - const char *startup_id = g_getenv ("DESKTOP_AUTOSTART_ID"); - g_dbus_proxy_call (proxy, - "RegisterClient", - g_variant_new ("(ss)", "cinnamon-settings-daemon", startup_id ? startup_id : ""), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) on_client_registered, - manager); - - return FALSE; -} - -static void -queue_register_client (void) -{ - GDBusConnection *bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - if (!bus) - return; - - GError *error = NULL; - GDBusProxy *proxy = g_dbus_proxy_new_sync (bus, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - GNOME_SESSION_DBUS_NAME, - GNOME_SESSION_DBUS_OBJECT, - GNOME_SESSION_DBUS_INTERFACE, - NULL, - &error); - g_object_unref (bus); - - if (proxy == NULL) { - g_debug ("Could not connect to the Session manager: %s", error->message); - g_error_free (error); - return; - } - - /* Register the daemon with cinnamon-session */ - g_signal_connect (G_OBJECT (proxy), "g-signal", - G_CALLBACK (on_session_over), NULL); - - g_idle_add_full (G_PRIORITY_DEFAULT, do_register_client, proxy, NULL); -} - static void bus_register (void) { @@ -541,8 +524,6 @@ main (int argc, char *argv[]) notify_init ("cinnamon-settings-daemon"); - queue_register_client (); - bus_register (); cinnamon_settings_profile_start ("cinnamon_settings_manager_new"); diff -uNrp a/configure.ac b/configure.ac --- a/configure.ac 2013-08-24 18:04:31.000000000 +0100 +++ b/configure.ac 2013-08-25 16:36:02.000000000 +0100 @@ -53,6 +53,7 @@ UPOWER_GLIB_REQUIRED_VERSION=0.9.1 PA_REQUIRED_VERSION=0.9.16 UPOWER_REQUIRED_VERSION=0.9.11 GTK_XINPUT_2_3_VERSION=3.7.8 +IBUS_REQUIRED_VERSION=1.4.2 #EXTRA_COMPILE_WARNINGS(yes) @@ -199,8 +200,21 @@ dnl ------------------------------------ dnl - Keyboard plugin stuff dnl --------------------------------------------------------------------------- -LIBGNOMEKBD_REQUIRED=2.91.1 -PKG_CHECK_MODULES(KEYBOARD, [libgnomekbdui >= $LIBGNOMEKBD_REQUIRED libgnomekbd >= $LIBGNOMEKBD_REQUIRED libxklavier >= 5.0 kbproto]) +AC_ARG_ENABLE(ibus, + AS_HELP_STRING([--disable-ibus], + [Disable IBus support]), + enable_ibus=$enableval, + enable_ibus=yes) + +if test "x$enable_ibus" = "xyes" ; then + IBUS_MODULE="ibus-1.0 >= $IBUS_REQUIRED_VERSION" + AC_DEFINE(HAVE_IBUS, 1, [Defined if IBus support is enabled]) +else + IBUS_MODULE= +fi +AM_CONDITIONAL(HAVE_IBUS, test "x$enable_ibus" == "xyes") + +PKG_CHECK_MODULES(KEYBOARD, xkbfile $IBUS_MODULE cinnamon-desktop >= $CINNAMON_DESKTOP_REQUIRED_VERSION) dnl --------------------------------------------------------------------------- dnl - Housekeeping plugin stuff diff -uNrp a/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in b/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in --- a/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in 2013-08-24 18:04:31.000000000 +0100 +++ b/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in 2013-08-25 16:36:02.000000000 +0100 @@ -175,6 +175,15 @@ <_summary>Magnifier zoom out <_description>Binding for the magnifier to zoom out + + '' + <_summary>Switch input source + <_description>Binding to select the next input source + + + '' + <_summary>Switch input source backward + <_description>Binding to select the previous input source + - - + \ No newline at end of file diff -uNrp a/plugins/keyboard/csd-keyboard-manager.c b/plugins/keyboard/csd-keyboard-manager.c --- a/plugins/keyboard/csd-keyboard-manager.c 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/csd-keyboard-manager.c 2013-08-25 16:36:02.000000000 +0100 @@ -40,19 +40,22 @@ #include #include +#include + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include + +#ifdef HAVE_IBUS +#include +#endif #include "cinnamon-settings-profile.h" #include "csd-keyboard-manager.h" +#include "csd-input-helper.h" #include "csd-enums.h" -#include "csd-keyboard-xkb.h" - #define CSD_KEYBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_KEYBOARD_MANAGER, CsdKeyboardManagerPrivate)) -#ifndef HOST_NAME_MAX -# define HOST_NAME_MAX 255 -#endif - #define CSD_KEYBOARD_DIR "org.cinnamon.settings-daemon.peripherals.keyboard" #define KEY_REPEAT "repeat" @@ -60,6 +63,7 @@ #define KEY_INTERVAL "repeat-interval" #define KEY_DELAY "delay" #define KEY_CLICK_VOLUME "click-volume" +#define KEY_REMEMBER_NUMLOCK_STATE "remember-numlock-state" #define KEY_NUMLOCK_STATE "numlock-state" #define KEY_BELL_VOLUME "bell-volume" @@ -67,27 +71,560 @@ #define KEY_BELL_DURATION "bell-duration" #define KEY_BELL_MODE "bell-mode" -#define LIBGNOMEKBD_KEYBOARD_DIR "org.gnome.libgnomekbd.keyboard" -#define LIBGNOMEKBD_KEY_LAYOUTS "layouts" +#define KEY_SWITCHER "input-sources-switcher" + +#define GNOME_DESKTOP_INTERFACE_DIR "org.cinnamon.desktop.interface" + +#define KEY_GTK_IM_MODULE "gtk-im-module" +#define GTK_IM_MODULE_SIMPLE "gtk-im-context-simple" +#define GTK_IM_MODULE_IBUS "ibus" + +#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.cinnamon.desktop.input-sources" + +#define KEY_CURRENT_INPUT_SOURCE "current" +#define KEY_INPUT_SOURCES "sources" +#define KEY_KEYBOARD_OPTIONS "xkb-options" + +#define INPUT_SOURCE_TYPE_XKB "xkb" +#define INPUT_SOURCE_TYPE_IBUS "ibus" + +#define DEFAULT_LANGUAGE "en_US" struct CsdKeyboardManagerPrivate { guint start_idle_id; GSettings *settings; - GSettings *libgnomekbd_settings; - gboolean have_xkb; + GSettings *input_sources_settings; + GSettings *interface_settings; + GnomeXkbInfo *xkb_info; +#ifdef HAVE_IBUS + IBusBus *ibus; + GHashTable *ibus_engines; + GHashTable *ibus_xkb_engines; + GCancellable *ibus_cancellable; + gboolean session_is_fallback; +#endif gint xkb_event_base; CsdNumLockState old_state; + GdkDeviceManager *device_manager; + guint device_added_id; + guint device_removed_id; + + gboolean input_sources_switcher_spawned; + GPid input_sources_switcher_pid; }; static void csd_keyboard_manager_class_init (CsdKeyboardManagerClass *klass); static void csd_keyboard_manager_init (CsdKeyboardManager *keyboard_manager); static void csd_keyboard_manager_finalize (GObject *object); +static gboolean apply_input_sources_settings (GSettings *settings, + gpointer keys, + gint n_keys, + CsdKeyboardManager *manager); +static void set_gtk_im_module (CsdKeyboardManager *manager, + const gchar *new_module); G_DEFINE_TYPE (CsdKeyboardManager, csd_keyboard_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; +static void +init_builder_with_sources (GVariantBuilder *builder, + GSettings *settings) +{ + const gchar *type; + const gchar *id; + GVariantIter iter; + GVariant *sources; + + sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); + + g_variant_builder_init (builder, G_VARIANT_TYPE ("a(ss)")); + + g_variant_iter_init (&iter, sources); + while (g_variant_iter_next (&iter, "(&s&s)", &type, &id)) + g_variant_builder_add (builder, "(ss)", type, id); + + g_variant_unref (sources); +} + +static gboolean +schema_is_installed (const gchar *name) +{ + const gchar * const *schemas; + const gchar * const *s; + + schemas = g_settings_list_schemas (); + for (s = schemas; *s; ++s) + if (g_str_equal (*s, name)) + return TRUE; + + return FALSE; +} + +#ifdef HAVE_IBUS +static void +clear_ibus (CsdKeyboardManager *manager) +{ + CsdKeyboardManagerPrivate *priv = manager->priv; + + g_cancellable_cancel (priv->ibus_cancellable); + g_clear_object (&priv->ibus_cancellable); + g_clear_pointer (&priv->ibus_engines, g_hash_table_destroy); + g_clear_pointer (&priv->ibus_xkb_engines, g_hash_table_destroy); + g_clear_object (&priv->ibus); +} + +static gchar * +make_xkb_source_id (const gchar *engine_id) +{ + gchar *id; + gchar *p; + gint n_colons = 0; + + /* engine_id is like "xkb:layout:variant:lang" where + * 'variant' and 'lang' might be empty */ + + engine_id += 4; + + for (p = (gchar *)engine_id; *p; ++p) + if (*p == ':') + if (++n_colons == 2) + break; + if (!*p) + return NULL; + + id = g_strndup (engine_id, p - engine_id + 1); + + id[p - engine_id] = '\0'; + + /* id is "layout:variant" where 'variant' might be empty */ + + for (p = id; *p; ++p) + if (*p == ':') { + if (*(p + 1) == '\0') + *p = '\0'; + else + *p = '+'; + break; + } + + /* id is "layout+variant" or "layout" */ + + return id; +} + +static void +fetch_ibus_engines_result (GObject *object, + GAsyncResult *result, + CsdKeyboardManager *manager) +{ + CsdKeyboardManagerPrivate *priv = manager->priv; + GList *list, *l; + GError *error = NULL; + + /* engines shouldn't be there yet */ + g_return_if_fail (priv->ibus_engines == NULL); + + g_clear_object (&priv->ibus_cancellable); + + list = ibus_bus_list_engines_async_finish (priv->ibus, + result, + &error); + if (!list && error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Couldn't finish IBus request: %s", error->message); + g_error_free (error); + + clear_ibus (manager); + return; + } + + /* Maps IBus engine ids to engine description objects */ + priv->ibus_engines = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + /* Maps XKB source id strings to engine description objects */ + priv->ibus_xkb_engines = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + for (l = list; l; l = l->next) { + IBusEngineDesc *engine = l->data; + const gchar *engine_id = ibus_engine_desc_get_name (engine); + + g_hash_table_replace (priv->ibus_engines, (gpointer)engine_id, engine); + + if (strncmp ("xkb:", engine_id, 4) == 0) { + gchar *xkb_source_id = make_xkb_source_id (engine_id); + if (xkb_source_id) + g_hash_table_replace (priv->ibus_xkb_engines, + xkb_source_id, + engine); + } + } + g_list_free (list); + + apply_input_sources_settings (priv->input_sources_settings, NULL, 0, manager); +} + +static void +fetch_ibus_engines (CsdKeyboardManager *manager) +{ + CsdKeyboardManagerPrivate *priv = manager->priv; + + /* engines shouldn't be there yet */ + g_return_if_fail (priv->ibus_engines == NULL); + g_return_if_fail (priv->ibus_cancellable == NULL); + + priv->ibus_cancellable = g_cancellable_new (); + + ibus_bus_list_engines_async (priv->ibus, + -1, + priv->ibus_cancellable, + (GAsyncReadyCallback)fetch_ibus_engines_result, + manager); +} + +static void +maybe_start_ibus (CsdKeyboardManager *manager, + GVariant *sources) +{ + gboolean need_ibus = FALSE; + GVariantIter iter; + const gchar *type; + + if (manager->priv->session_is_fallback) + return; + + g_variant_iter_init (&iter, sources); + while (g_variant_iter_next (&iter, "(&s&s)", &type, NULL)) + if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { + need_ibus = TRUE; + break; + } + + if (!need_ibus) + return; + + if (!manager->priv->ibus) { + ibus_init (); + manager->priv->ibus = ibus_bus_new (); + g_signal_connect_swapped (manager->priv->ibus, "connected", + G_CALLBACK (fetch_ibus_engines), manager); + g_signal_connect_swapped (manager->priv->ibus, "disconnected", + G_CALLBACK (clear_ibus), manager); + } + /* IBus doesn't export API in the session bus. The only thing + * we have there is a well known name which we can use as a + * sure-fire way to activate it. */ + g_bus_unwatch_name (g_bus_watch_name (G_BUS_TYPE_SESSION, + IBUS_SERVICE_IBUS, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + NULL, + NULL, + NULL, + NULL)); +} + +static void +got_session_name (GObject *object, + GAsyncResult *res, + CsdKeyboardManager *manager) +{ + GVariant *result, *variant; + GDBusConnection *connection = G_DBUS_CONNECTION (object); + CsdKeyboardManagerPrivate *priv = manager->priv; + const gchar *session_name = NULL; + GError *error = NULL; + + /* IBus shouldn't have been touched yet */ + g_return_if_fail (priv->ibus == NULL); + + g_clear_object (&priv->ibus_cancellable); + + result = g_dbus_connection_call_finish (connection, res, &error); + if (!result) { + g_warning ("Couldn't get session name: %s", error->message); + g_error_free (error); + goto out; + } + + g_variant_get (result, "(v)", &variant); + g_variant_unref (result); + + g_variant_get (variant, "&s", &session_name); + + if (g_strcmp0 (session_name, "gnome") == 0) + manager->priv->session_is_fallback = FALSE; + + g_variant_unref (variant); + out: + apply_input_sources_settings (manager->priv->input_sources_settings, NULL, 0, manager); + g_object_unref (connection); +} + +static void +got_bus (GObject *object, + GAsyncResult *res, + CsdKeyboardManager *manager) +{ + GDBusConnection *connection; + CsdKeyboardManagerPrivate *priv = manager->priv; + GError *error = NULL; + + /* IBus shouldn't have been touched yet */ + g_return_if_fail (priv->ibus == NULL); + + g_clear_object (&priv->ibus_cancellable); + + connection = g_bus_get_finish (res, &error); + if (!connection) { + g_warning ("Couldn't get session bus: %s", error->message); + g_error_free (error); + apply_input_sources_settings (priv->input_sources_settings, NULL, 0, manager); + return; + } + + priv->ibus_cancellable = g_cancellable_new (); + + g_dbus_connection_call (connection, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + "org.gnome.SessionManager", + "SessionName"), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->ibus_cancellable, + (GAsyncReadyCallback)got_session_name, + manager); +} + +static void +set_ibus_engine_finish (GObject *object, + GAsyncResult *res, + CsdKeyboardManager *manager) +{ + gboolean result; + IBusBus *ibus = IBUS_BUS (object); + CsdKeyboardManagerPrivate *priv = manager->priv; + GError *error = NULL; + + g_clear_object (&priv->ibus_cancellable); + + result = ibus_bus_set_global_engine_async_finish (ibus, res, &error); + if (!result) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Couldn't set IBus engine: %s", error->message); + g_error_free (error); + } +} + +static void +set_ibus_engine (CsdKeyboardManager *manager, + const gchar *engine_id) +{ + CsdKeyboardManagerPrivate *priv = manager->priv; + + g_return_if_fail (priv->ibus != NULL); + g_return_if_fail (priv->ibus_engines != NULL); + + g_cancellable_cancel (priv->ibus_cancellable); + g_clear_object (&priv->ibus_cancellable); + priv->ibus_cancellable = g_cancellable_new (); + + ibus_bus_set_global_engine_async (priv->ibus, + engine_id, + -1, + priv->ibus_cancellable, + (GAsyncReadyCallback)set_ibus_engine_finish, + manager); +} + +static void +set_ibus_xkb_engine (CsdKeyboardManager *manager, + const gchar *xkb_id) +{ + IBusEngineDesc *engine; + CsdKeyboardManagerPrivate *priv = manager->priv; + + if (!priv->ibus_xkb_engines) + return; + + engine = g_hash_table_lookup (priv->ibus_xkb_engines, xkb_id); + if (!engine) + return; + + set_ibus_engine (manager, ibus_engine_desc_get_name (engine)); +} + +/* XXX: See upstream bug: + * https://codereview.appspot.com/6586075/ */ +static gchar * +layout_from_ibus_layout (const gchar *ibus_layout) +{ + const gchar *p; + + /* we get something like "layout(variant)[option1,option2]" */ + + p = ibus_layout; + while (*p) { + if (*p == '(' || *p == '[') + break; + p += 1; + } + + return g_strndup (ibus_layout, p - ibus_layout); +} + +static gchar * +variant_from_ibus_layout (const gchar *ibus_layout) +{ + const gchar *a, *b; + + /* we get something like "layout(variant)[option1,option2]" */ + + a = ibus_layout; + while (*a) { + if (*a == '(') + break; + a += 1; + } + if (!*a) + return NULL; + + a += 1; + b = a; + while (*b) { + if (*b == ')') + break; + b += 1; + } + if (!*b) + return NULL; + + return g_strndup (a, b - a); +} + +static gchar ** +options_from_ibus_layout (const gchar *ibus_layout) +{ + const gchar *a, *b; + GPtrArray *opt_array; + + /* we get something like "layout(variant)[option1,option2]" */ + + a = ibus_layout; + while (*a) { + if (*a == '[') + break; + a += 1; + } + if (!*a) + return NULL; + + opt_array = g_ptr_array_new (); + + do { + a += 1; + b = a; + while (*b) { + if (*b == ',' || *b == ']') + break; + b += 1; + } + if (!*b) + goto out; + + g_ptr_array_add (opt_array, g_strndup (a, b - a)); + + a = b; + } while (*a && *a == ','); + +out: + g_ptr_array_add (opt_array, NULL); + return (gchar **) g_ptr_array_free (opt_array, FALSE); +} + +static const gchar * +engine_from_locale (void) +{ + const gchar *locale; + const gchar *locale_engine[][2] = { + { "as_IN", "m17n:as:phonetic" }, + { "bn_IN", "m17n:bn:inscript" }, + { "gu_IN", "m17n:gu:inscript" }, + { "hi_IN", "m17n:hi:inscript" }, + { "ja_JP", "anthy" }, + { "kn_IN", "m17n:kn:kgp" }, + { "ko_KR", "hangul" }, + { "mai_IN", "m17n:mai:inscript" }, + { "ml_IN", "m17n:ml:inscript" }, + { "mr_IN", "m17n:mr:inscript" }, + { "or_IN", "m17n:or:inscript" }, + { "pa_IN", "m17n:pa:inscript" }, + { "sd_IN", "m17n:sd:inscript" }, + { "ta_IN", "m17n:ta:tamil99" }, + { "te_IN", "m17n:te:inscript" }, + { "zh_CN", "pinyin" }, + { "zh_HK", "cangjie3" }, + { "zh_TW", "chewing" }, + }; + gint i; + + locale = setlocale (LC_CTYPE, NULL); + if (!locale) + return NULL; + + for (i = 0; i < G_N_ELEMENTS (locale_engine); ++i) + if (g_str_has_prefix (locale, locale_engine[i][0])) + return locale_engine[i][1]; + + return NULL; +} + +static void +add_ibus_sources_from_locale (GSettings *settings) +{ + const gchar *locale_engine; + GVariantBuilder builder; + + locale_engine = engine_from_locale (); + if (!locale_engine) + return; + + init_builder_with_sources (&builder, settings); + g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_IBUS, locale_engine); + g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); +} + +static void +convert_ibus (GSettings *settings) +{ + GVariantBuilder builder; + GSettings *ibus_settings; + gchar **engines, **e; + + if (!schema_is_installed ("org.freedesktop.ibus.general")) + return; + + init_builder_with_sources (&builder, settings); + + ibus_settings = g_settings_new ("org.freedesktop.ibus.general"); + engines = g_settings_get_strv (ibus_settings, "preload-engines"); + for (e = engines; *e; ++e) { + if (g_str_has_prefix (*e, "xkb:")) + continue; + g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_IBUS, *e); + } + + g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); + + g_strfreev (engines); + g_object_unref (ibus_settings); +} +#endif /* HAVE_IBUS */ + static gboolean xkb_set_keyboard_autorepeat_rate (guint delay, guint interval) { @@ -97,32 +634,33 @@ xkb_set_keyboard_autorepeat_rate (guint interval); } -static void -numlock_xkb_init (CsdKeyboardManager *manager) +static gboolean +check_xkb_extension (CsdKeyboardManager *manager) { Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - gboolean have_xkb; int opcode, error_base, major, minor; + gboolean have_xkb; have_xkb = XkbQueryExtension (dpy, &opcode, &manager->priv->xkb_event_base, &error_base, &major, - &minor) - && XkbUseExtension (dpy, &major, &minor); + &minor); + return have_xkb; +} - if (have_xkb) { - XkbSelectEventDetails (dpy, - XkbUseCoreKbd, - XkbStateNotifyMask, - XkbModifierLockMask, - XkbModifierLockMask); - } else { - g_warning ("XKB extension not available"); - } +static void +xkb_init (CsdKeyboardManager *manager) +{ + Display *dpy; - manager->priv->have_xkb = have_xkb; + dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + XkbSelectEventDetails (dpy, + XkbUseCoreKbd, + XkbStateNotify, + XkbModifierLockMask, + XkbModifierLockMask); } static unsigned @@ -143,19 +681,32 @@ numlock_set_xkb_state (CsdNumLockState n XkbLockModifiers (dpy, XkbUseCoreKbd, num_mask, new_state == CSD_NUM_LOCK_STATE_ON ? num_mask : 0); } +static const char * +num_lock_state_to_string (CsdNumLockState numlock_state) +{ + switch (numlock_state) { + case CSD_NUM_LOCK_STATE_UNKNOWN: + return "CSD_NUM_LOCK_STATE_UNKNOWN"; + case CSD_NUM_LOCK_STATE_ON: + return "CSD_NUM_LOCK_STATE_ON"; + case CSD_NUM_LOCK_STATE_OFF: + return "CSD_NUM_LOCK_STATE_OFF"; + default: + return "UNKNOWN"; + } +} + static GdkFilterReturn -numlock_xkb_callback (GdkXEvent *xev_, - GdkEvent *gdkev_, - gpointer user_data) +xkb_events_filter (GdkXEvent *xev_, + GdkEvent *gdkev_, + gpointer user_data) { XEvent *xev = (XEvent *) xev_; XkbEvent *xkbev = (XkbEvent *) xev; CsdKeyboardManager *manager = (CsdKeyboardManager *) user_data; - if (xev->type != manager->priv->xkb_event_base) - return GDK_FILTER_CONTINUE; - - if (xkbev->any.xkb_type != XkbStateNotify) + if (xev->type != manager->priv->xkb_event_base || + xkbev->any.xkb_type != XkbStateNotify) return GDK_FILTER_CONTINUE; if (xkbev->state.changed & XkbModifierLockMask) { @@ -166,6 +717,9 @@ numlock_xkb_callback (GdkXEvent *xev_, numlock_state = (num_mask & locked_mods) ? CSD_NUM_LOCK_STATE_ON : CSD_NUM_LOCK_STATE_OFF; if (numlock_state != manager->priv->old_state) { + g_debug ("New num-lock state '%s' != Old num-lock state '%s'", + num_lock_state_to_string (numlock_state), + num_lock_state_to_string (manager->priv->old_state)); g_settings_set_enum (manager->priv->settings, KEY_NUMLOCK_STATE, numlock_state); @@ -177,57 +731,509 @@ numlock_xkb_callback (GdkXEvent *xev_, } static void -numlock_install_xkb_callback (CsdKeyboardManager *manager) +install_xkb_filter (CsdKeyboardManager *manager) { - if (!manager->priv->have_xkb) - return; - gdk_window_add_filter (NULL, - numlock_xkb_callback, + xkb_events_filter, manager); } -static guint -_csd_settings_get_uint (GSettings *settings, - const char *key) +static void +remove_xkb_filter (CsdKeyboardManager *manager) { - guint value; + gdk_window_remove_filter (NULL, + xkb_events_filter, + manager); +} - g_settings_get (settings, key, "u", &value); - return value; +static void +free_xkb_component_names (XkbComponentNamesRec *p) +{ + g_return_if_fail (p != NULL); + + free (p->keymap); + free (p->keycodes); + free (p->types); + free (p->compat); + free (p->symbols); + free (p->geometry); + + g_free (p); +} + +static void +upload_xkb_description (const gchar *rules_file_path, + XkbRF_VarDefsRec *var_defs, + XkbComponentNamesRec *comp_names) +{ + Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + XkbDescRec *xkb_desc; + gchar *rules_file; + + /* Upload it to the X server using the same method as setxkbmap */ + xkb_desc = XkbGetKeyboardByName (display, + XkbUseCoreKbd, + comp_names, + XkbGBN_AllComponentsMask, + XkbGBN_AllComponentsMask & + (~XkbGBN_GeometryMask), True); + if (!xkb_desc) { + g_warning ("Couldn't upload new XKB keyboard description"); + return; + } + + XkbFreeKeyboard (xkb_desc, 0, True); + + rules_file = g_path_get_basename (rules_file_path); + + if (!XkbRF_SetNamesProp (display, rules_file, var_defs)) + g_warning ("Couldn't update the XKB root window property"); + + g_free (rules_file); +} + +static gchar * +language_code_from_locale (const gchar *locale) +{ + if (!locale || !locale[0] || !locale[1]) + return NULL; + + if (!locale[2] || locale[2] == '_' || locale[2] == '.') + return g_strndup (locale, 2); + + if (!locale[3] || locale[3] == '_' || locale[3] == '.') + return g_strndup (locale, 3); + + return NULL; +} + +static gchar * +build_xkb_group_string (const gchar *user, + const gchar *locale, + const gchar *latin) +{ + gchar *string; + gsize length = 0; + guint commas = 2; + + if (latin) + length += strlen (latin); + else + commas -= 1; + + if (locale) + length += strlen (locale); + else + commas -= 1; + + length += strlen (user) + commas + 1; + + string = malloc (length); + + if (locale && latin) + sprintf (string, "%s,%s,%s", user, locale, latin); + else if (locale) + sprintf (string, "%s,%s", user, locale); + else if (latin) + sprintf (string, "%s,%s", user, latin); + else + sprintf (string, "%s", user); + + return string; +} + +static gboolean +layout_equal (const gchar *layout_a, + const gchar *variant_a, + const gchar *layout_b, + const gchar *variant_b) +{ + return !g_strcmp0 (layout_a, layout_b) && !g_strcmp0 (variant_a, variant_b); } static void -apply_settings (GSettings *settings, - const char *key, - CsdKeyboardManager *manager) +replace_layout_and_variant (CsdKeyboardManager *manager, + XkbRF_VarDefsRec *xkb_var_defs, + const gchar *layout, + const gchar *variant) { + /* Toolkits need to know about both a latin layout to handle + * accelerators which are usually defined like Ctrl+C and a + * layout with the symbols for the language used in UI strings + * to handle mnemonics like Alt+Ф, so we try to find and add + * them in XKB group slots after the layout which the user + * actually intends to type with. */ + const gchar *latin_layout = "us"; + const gchar *latin_variant = ""; + const gchar *locale_layout = NULL; + const gchar *locale_variant = NULL; + const gchar *locale; + gchar *language; + + if (!layout) + return; + + if (!variant) + variant = ""; + + locale = setlocale (LC_MESSAGES, NULL); + /* If LANG is empty, default to en_US */ + if (!locale) + language = g_strdup (DEFAULT_LANGUAGE); + else + language = language_code_from_locale (locale); + + if (!language) + language = language_code_from_locale (DEFAULT_LANGUAGE); + + gnome_xkb_info_get_layout_info_for_language (manager->priv->xkb_info, + language, + NULL, + NULL, + NULL, + &locale_layout, + &locale_variant); + g_free (language); + + /* We want to minimize the number of XKB groups if we have + * duplicated layout+variant pairs. + * + * Also, if a layout doesn't have a variant we still have to + * include it in the variants string because the number of + * variants must agree with the number of layouts. For + * instance: + * + * layouts: "us,ru,us" + * variants: "dvorak,," + */ + if (layout_equal (latin_layout, latin_variant, locale_layout, locale_variant) || + layout_equal (latin_layout, latin_variant, layout, variant)) { + latin_layout = NULL; + latin_variant = NULL; + } + + if (layout_equal (locale_layout, locale_variant, layout, variant)) { + locale_layout = NULL; + locale_variant = NULL; + } + + free (xkb_var_defs->layout); + xkb_var_defs->layout = build_xkb_group_string (layout, locale_layout, latin_layout); + + free (xkb_var_defs->variant); + xkb_var_defs->variant = build_xkb_group_string (variant, locale_variant, latin_variant); +} + +static gchar * +build_xkb_options_string (gchar **options) +{ + gchar *string; + + if (*options) { + gint i; + gsize len; + gchar *ptr; + + /* First part, getting length */ + len = 1 + strlen (options[0]); + for (i = 1; options[i] != NULL; i++) + len += strlen (options[i]); + len += (i - 1); /* commas */ + + /* Second part, building string */ + string = malloc (len); + ptr = g_stpcpy (string, *options); + for (i = 1; options[i] != NULL; i++) { + ptr = g_stpcpy (ptr, ","); + ptr = g_stpcpy (ptr, options[i]); + } + } else { + string = malloc (1); + *string = '\0'; + } + + return string; +} + +static gchar ** +append_options (gchar **a, + gchar **b) +{ + gchar **c, **p; + + if (!a && !b) + return NULL; + else if (!a) + return g_strdupv (b); + else if (!b) + return g_strdupv (a); + + c = g_new0 (gchar *, g_strv_length (a) + g_strv_length (b) + 1); + p = c; + + while (*a) { + *p = g_strdup (*a); + p += 1; + a += 1; + } + while (*b) { + *p = g_strdup (*b); + p += 1; + b += 1; + } + + return c; +} + +static void +add_xkb_options (CsdKeyboardManager *manager, + XkbRF_VarDefsRec *xkb_var_defs, + gchar **extra_options) +{ + gchar **options; + gchar **settings_options; + + settings_options = g_settings_get_strv (manager->priv->input_sources_settings, + KEY_KEYBOARD_OPTIONS); + options = append_options (settings_options, extra_options); + g_strfreev (settings_options); + + free (xkb_var_defs->options); + xkb_var_defs->options = build_xkb_options_string (options); + + g_strfreev (options); +} + +static void +apply_xkb_settings (CsdKeyboardManager *manager, + const gchar *layout, + const gchar *variant, + gchar **options) +{ + XkbRF_RulesRec *xkb_rules; + XkbRF_VarDefsRec *xkb_var_defs; + gchar *rules_file_path; + + gnome_xkb_info_get_var_defs (&rules_file_path, &xkb_var_defs); + + add_xkb_options (manager, xkb_var_defs, options); + replace_layout_and_variant (manager, xkb_var_defs, layout, variant); + + gdk_error_trap_push (); + + xkb_rules = XkbRF_Load (rules_file_path, NULL, True, True); + if (xkb_rules) { + XkbComponentNamesRec *xkb_comp_names; + xkb_comp_names = g_new0 (XkbComponentNamesRec, 1); + + XkbRF_GetComponents (xkb_rules, xkb_var_defs, xkb_comp_names); + upload_xkb_description (rules_file_path, xkb_var_defs, xkb_comp_names); + + free_xkb_component_names (xkb_comp_names); + XkbRF_Free (xkb_rules, True); + } else { + g_warning ("Couldn't load XKB rules"); + } + + if (gdk_error_trap_pop ()) + g_warning ("Error loading XKB rules"); + + gnome_xkb_info_free_var_defs (xkb_var_defs); + g_free (rules_file_path); +} + +static void +set_gtk_im_module (CsdKeyboardManager *manager, + const gchar *new_module) +{ + CsdKeyboardManagerPrivate *priv = manager->priv; + gchar *current_module; + + current_module = g_settings_get_string (priv->interface_settings, + KEY_GTK_IM_MODULE); + if (!g_str_equal (current_module, new_module)) + g_settings_set_string (priv->interface_settings, + KEY_GTK_IM_MODULE, + new_module); + g_free (current_module); +} + +static gboolean +apply_input_sources_settings (GSettings *settings, + gpointer keys, + gint n_keys, + CsdKeyboardManager *manager) +{ + CsdKeyboardManagerPrivate *priv = manager->priv; + GVariant *sources; + guint current; + guint n_sources; + const gchar *type = NULL; + const gchar *id = NULL; + gchar *layout = NULL; + gchar *variant = NULL; + gchar **options = NULL; + + sources = g_settings_get_value (priv->input_sources_settings, KEY_INPUT_SOURCES); + current = g_settings_get_uint (priv->input_sources_settings, KEY_CURRENT_INPUT_SOURCE); + n_sources = g_variant_n_children (sources); + + if (n_sources < 1) + goto exit; + + if (current >= n_sources) { + g_settings_set_uint (priv->input_sources_settings, + KEY_CURRENT_INPUT_SOURCE, + n_sources - 1); + goto exit; + } + +#ifdef HAVE_IBUS + maybe_start_ibus (manager, sources); +#endif + + g_variant_get_child (sources, current, "(&s&s)", &type, &id); + + if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { + const gchar *l, *v; + gnome_xkb_info_get_layout_info (priv->xkb_info, id, NULL, NULL, &l, &v); + + layout = g_strdup (l); + variant = g_strdup (v); + + if (!layout || !layout[0]) { + g_warning ("Couldn't find XKB input source '%s'", id); + goto exit; + } + set_gtk_im_module (manager, GTK_IM_MODULE_SIMPLE); +#ifdef HAVE_IBUS + set_ibus_xkb_engine (manager, id); +#endif + } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { +#ifdef HAVE_IBUS + IBusEngineDesc *engine_desc = NULL; + + if (priv->session_is_fallback) + goto exit; + + if (priv->ibus_engines) + engine_desc = g_hash_table_lookup (priv->ibus_engines, id); + else + goto exit; /* we'll be called again when ibus is up and running */ + + if (engine_desc) { + const gchar *ibus_layout; + ibus_layout = ibus_engine_desc_get_layout (engine_desc); + + if (ibus_layout) { + layout = layout_from_ibus_layout (ibus_layout); + variant = variant_from_ibus_layout (ibus_layout); + options = options_from_ibus_layout (ibus_layout); + } + } else { + g_warning ("Couldn't find IBus input source '%s'", id); + goto exit; + } + + set_gtk_im_module (manager, GTK_IM_MODULE_IBUS); + set_ibus_engine (manager, id); +#else + g_warning ("IBus input source type specified but IBus support was not compiled"); +#endif + } else { + g_warning ("Unknown input source type '%s'", type); + } + + exit: + apply_xkb_settings (manager, layout, variant, options); + g_variant_unref (sources); + g_free (layout); + g_free (variant); + g_strfreev (options); + /* Prevent individual "changed" signal invocations since we + don't need them. */ + return TRUE; +} + +static void +apply_bell (CsdKeyboardManager *manager) +{ + GSettings *settings; XKeyboardControl kbdcontrol; - gboolean repeat; gboolean click; - guint interval; - guint delay; - int click_volume; int bell_volume; int bell_pitch; int bell_duration; CsdBellMode bell_mode; - gboolean rnumlock; - - if (g_strcmp0 (key, KEY_NUMLOCK_STATE) == 0) - return; + int click_volume; - repeat = g_settings_get_boolean (settings, KEY_REPEAT); + g_debug ("Applying the bell settings"); + settings = manager->priv->settings; click = g_settings_get_boolean (settings, KEY_CLICK); - interval = _csd_settings_get_uint (settings, KEY_INTERVAL); - delay = _csd_settings_get_uint (settings, KEY_DELAY); click_volume = g_settings_get_int (settings, KEY_CLICK_VOLUME); + bell_pitch = g_settings_get_int (settings, KEY_BELL_PITCH); bell_duration = g_settings_get_int (settings, KEY_BELL_DURATION); bell_mode = g_settings_get_enum (settings, KEY_BELL_MODE); bell_volume = (bell_mode == CSD_BELL_MODE_ON) ? 50 : 0; + /* as percentage from 0..100 inclusive */ + if (click_volume < 0) { + click_volume = 0; + } else if (click_volume > 100) { + click_volume = 100; + } + kbdcontrol.key_click_percent = click ? click_volume : 0; + kbdcontrol.bell_percent = bell_volume; + kbdcontrol.bell_pitch = bell_pitch; + kbdcontrol.bell_duration = bell_duration; + + gdk_error_trap_push (); + XChangeKeyboardControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + KBKeyClickPercent | KBBellPercent | KBBellPitch | KBBellDuration, + &kbdcontrol); + + XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); + gdk_error_trap_pop_ignored (); +} + +static void +apply_numlock (CsdKeyboardManager *manager) +{ + GSettings *settings; + gboolean rnumlock; + + g_debug ("Applying the num-lock settings"); + settings = manager->priv->settings; + rnumlock = g_settings_get_boolean (settings, KEY_REMEMBER_NUMLOCK_STATE); + manager->priv->old_state = g_settings_get_enum (manager->priv->settings, KEY_NUMLOCK_STATE); + + gdk_error_trap_push (); + if (rnumlock) { + g_debug ("Remember num-lock is set, so applying setting '%s'", + num_lock_state_to_string (manager->priv->old_state)); + numlock_set_xkb_state (manager->priv->old_state); + } + + XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); + gdk_error_trap_pop_ignored (); +} + +static void +apply_repeat (CsdKeyboardManager *manager) +{ + GSettings *settings; + gboolean repeat; + guint interval; + guint delay; + + g_debug ("Applying the repeat settings"); + settings = manager->priv->settings; + repeat = g_settings_get_boolean (settings, KEY_REPEAT); + interval = g_settings_get_uint (settings, KEY_INTERVAL); + delay = g_settings_get_uint (settings, KEY_DELAY); + gdk_error_trap_push (); if (repeat) { gboolean rate_set = FALSE; @@ -243,124 +1249,319 @@ apply_settings (GSettings *sett XAutoRepeatOff (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); } - /* as percentage from 0..100 inclusive */ - if (click_volume < 0) { - click_volume = 0; - } else if (click_volume > 100) { - click_volume = 100; + XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); + gdk_error_trap_pop_ignored (); +} + +static void +apply_all_settings (CsdKeyboardManager *manager) +{ + apply_repeat (manager); + apply_bell (manager); + apply_numlock (manager); +} + +static void +set_input_sources_switcher (CsdKeyboardManager *manager, + gboolean state) +{ + if (state) { + GError *error = NULL; + char *args[2]; + + if (manager->priv->input_sources_switcher_spawned) + set_input_sources_switcher (manager, FALSE); + + args[0] = LIBEXECDIR "/csd-input-sources-switcher"; + args[1] = NULL; + + g_spawn_async (NULL, args, NULL, + 0, NULL, NULL, + &manager->priv->input_sources_switcher_pid, &error); + + manager->priv->input_sources_switcher_spawned = (error == NULL); + + if (error) { + g_warning ("Couldn't spawn %s: %s", args[0], error->message); + g_error_free (error); + } + } else if (manager->priv->input_sources_switcher_spawned) { + kill (manager->priv->input_sources_switcher_pid, SIGHUP); + g_spawn_close_pid (manager->priv->input_sources_switcher_pid); + manager->priv->input_sources_switcher_spawned = FALSE; } - kbdcontrol.key_click_percent = click ? click_volume : 0; - kbdcontrol.bell_percent = bell_volume; - kbdcontrol.bell_pitch = bell_pitch; - kbdcontrol.bell_duration = bell_duration; - XChangeKeyboardControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - KBKeyClickPercent | KBBellPercent | KBBellPitch | KBBellDuration, - &kbdcontrol); +} - if (g_strcmp0 (key, "remember-numlock-state") == 0 || key == NULL) { - rnumlock = g_settings_get_boolean (settings, "remember-numlock-state"); +static gboolean +enable_switcher (CsdKeyboardManager *manager) +{ + CsdInputSourcesSwitcher switcher; - manager->priv->old_state = g_settings_get_enum (manager->priv->settings, KEY_NUMLOCK_STATE); + switcher = g_settings_get_enum (manager->priv->settings, KEY_SWITCHER); - if (manager->priv->have_xkb && rnumlock) - numlock_set_xkb_state (manager->priv->old_state); + return switcher != CSD_INPUT_SOURCES_SWITCHER_OFF; +} + +static void +settings_changed (GSettings *settings, + const char *key, + CsdKeyboardManager *manager) +{ + if (g_strcmp0 (key, KEY_CLICK) == 0|| + g_strcmp0 (key, KEY_CLICK_VOLUME) == 0 || + g_strcmp0 (key, KEY_BELL_PITCH) == 0 || + g_strcmp0 (key, KEY_BELL_DURATION) == 0 || + g_strcmp0 (key, KEY_BELL_MODE) == 0) { + g_debug ("Bell setting '%s' changed, applying bell settings", key); + apply_bell (manager); + } else if (g_strcmp0 (key, KEY_REMEMBER_NUMLOCK_STATE) == 0) { + g_debug ("Remember Num-Lock state '%s' changed, applying num-lock settings", key); + apply_numlock (manager); + } else if (g_strcmp0 (key, KEY_NUMLOCK_STATE) == 0) { + g_debug ("Num-Lock state '%s' changed, will apply at next startup", key); + } else if (g_strcmp0 (key, KEY_REPEAT) == 0 || + g_strcmp0 (key, KEY_INTERVAL) == 0 || + g_strcmp0 (key, KEY_DELAY) == 0) { + g_debug ("Key repeat setting '%s' changed, applying key repeat settings", key); + apply_repeat (manager); + } else if (g_strcmp0 (key, KEY_SWITCHER) == 0) { + set_input_sources_switcher (manager, enable_switcher (manager)); + } else { + g_warning ("Unhandled settings change, key '%s'", key); } - XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); - gdk_error_trap_pop_ignored (); } -void -csd_keyboard_manager_apply_settings (CsdKeyboardManager *manager) +static void +device_added_cb (GdkDeviceManager *device_manager, + GdkDevice *device, + CsdKeyboardManager *manager) { - apply_settings (manager->priv->settings, NULL, manager); + GdkInputSource source; + + source = gdk_device_get_source (device); + if (source == GDK_SOURCE_KEYBOARD) { + g_debug ("New keyboard plugged in, applying all settings"); + apply_all_settings (manager); + apply_input_sources_settings (manager->priv->input_sources_settings, NULL, 0, manager); + run_custom_command (device, COMMAND_DEVICE_ADDED); + } } static void -apply_libgnomekbd_settings (GSettings *settings, - const char *key, - CsdKeyboardManager *manager) +device_removed_cb (GdkDeviceManager *device_manager, + GdkDevice *device, + CsdKeyboardManager *manager) { - gchar **layouts; + GdkInputSource source; - layouts = g_settings_get_strv (settings, LIBGNOMEKBD_KEY_LAYOUTS); + source = gdk_device_get_source (device); + if (source == GDK_SOURCE_KEYBOARD) { + run_custom_command (device, COMMAND_DEVICE_REMOVED); + } +} - /* Get accounts daemon */ - GDBusProxy *proxy = NULL; - GDBusProxy *user = NULL; - GVariant *variant = NULL; - GError *error = NULL; - gchar *object_path = NULL; +static void +set_devicepresence_handler (CsdKeyboardManager *manager) +{ + GdkDeviceManager *device_manager; - proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.freedesktop.Accounts", - "/org/freedesktop/Accounts", - "org.freedesktop.Accounts", - NULL, - &error); + device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); - if (proxy == NULL) { - g_warning ("Failed to contact accounts service: %s", error->message); - g_error_free (error); - goto bail; + manager->priv->device_added_id = g_signal_connect (G_OBJECT (device_manager), "device-added", + G_CALLBACK (device_added_cb), manager); + manager->priv->device_removed_id = g_signal_connect (G_OBJECT (device_manager), "device-removed", + G_CALLBACK (device_removed_cb), manager); + manager->priv->device_manager = device_manager; +} + +static void +create_sources_from_current_xkb_config (GSettings *settings) +{ + GVariantBuilder builder; + XkbRF_VarDefsRec *xkb_var_defs; + gchar *tmp; + gchar **layouts = NULL; + gchar **variants = NULL; + guint i, n; + + gnome_xkb_info_get_var_defs (&tmp, &xkb_var_defs); + g_free (tmp); + + if (xkb_var_defs->layout) + layouts = g_strsplit (xkb_var_defs->layout, ",", 0); + if (xkb_var_defs->variant) + variants = g_strsplit (xkb_var_defs->variant, ",", 0); + + gnome_xkb_info_free_var_defs (xkb_var_defs); + + if (!layouts) + goto out; + + if (variants && variants[0]) + n = MIN (g_strv_length (layouts), g_strv_length (variants)); + else + n = g_strv_length (layouts); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + for (i = 0; i < n && layouts[i][0]; ++i) { + if (variants && variants[i] && variants[i][0]) + tmp = g_strdup_printf ("%s+%s", layouts[i], variants[i]); + else + tmp = g_strdup (layouts[i]); + + g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_XKB, tmp); + g_free (tmp); } + g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); +out: + g_strfreev (layouts); + g_strfreev (variants); +} - variant = g_dbus_proxy_call_sync (proxy, - "FindUserByName", - g_variant_new ("(s)", g_get_user_name ()), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - &error); +static void +convert_libgnomekbd_options (GSettings *settings) +{ + GPtrArray *opt_array; + GSettings *libgnomekbd_settings; + gchar **options, **o; - if (variant == NULL) { - g_warning ("Could not contact accounts service to look up '%s': %s", - g_get_user_name (), error->message); - g_error_free (error); - goto bail; + if (!schema_is_installed ("org.gnome.libgnomekbd.keyboard")) + return; + + opt_array = g_ptr_array_new_with_free_func (g_free); + + libgnomekbd_settings = g_settings_new ("org.gnome.libgnomekbd.keyboard"); + options = g_settings_get_strv (libgnomekbd_settings, "options"); + + for (o = options; *o; ++o) { + gchar **strv; + + strv = g_strsplit (*o, "\t", 2); + if (strv[0] && strv[1]) { + /* We don't want the group switcher because + * it's incompatible with the way we use XKB + * groups. */ + if (!g_str_has_prefix (strv[1], "grp:")) + g_ptr_array_add (opt_array, g_strdup (strv[1])); + } + g_strfreev (strv); } + g_ptr_array_add (opt_array, NULL); - g_variant_get (variant, "(o)", &object_path); - user = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.freedesktop.Accounts", - object_path, - "org.freedesktop.Accounts.User", - NULL, - &error); - g_free (object_path); + g_settings_set_strv (settings, KEY_KEYBOARD_OPTIONS, (const gchar * const*) opt_array->pdata); - if (user == NULL) { - g_warning ("Could not create proxy for user '%s': %s", - g_variant_get_string (variant, NULL), error->message); - g_error_free (error); - goto bail; + g_strfreev (options); + g_object_unref (libgnomekbd_settings); + g_ptr_array_free (opt_array, TRUE); +} + +static void +convert_libgnomekbd_layouts (GSettings *settings) +{ + GVariantBuilder builder; + GSettings *libgnomekbd_settings; + gchar **layouts, **l; + + if (!schema_is_installed ("org.gnome.libgnomekbd.keyboard")) + return; + + init_builder_with_sources (&builder, settings); + + libgnomekbd_settings = g_settings_new ("org.gnome.libgnomekbd.keyboard"); + layouts = g_settings_get_strv (libgnomekbd_settings, "layouts"); + + for (l = layouts; *l; ++l) { + gchar *id; + gchar **strv; + + strv = g_strsplit (*l, "\t", 2); + if (strv[0] && !strv[1]) + id = g_strdup (strv[0]); + else if (strv[0] && strv[1]) + id = g_strdup_printf ("%s+%s", strv[0], strv[1]); + else + id = NULL; + + if (id) + g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_XKB, id); + + g_free (id); + g_strfreev (strv); } - g_variant_unref (variant); - variant = g_dbus_proxy_call_sync (user, - "SetXKeyboardLayouts", - g_variant_new ("(^as)", layouts), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - &error); + g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); + + g_strfreev (layouts); + g_object_unref (libgnomekbd_settings); +} - if (variant == NULL) { - g_warning ("Failed to set the keyboard layouts: %s", error->message); +static void +maybe_convert_old_settings (GSettings *settings) +{ + GVariant *sources; + gchar **options; + gchar *stamp_dir_path = NULL; + gchar *stamp_file_path = NULL; + GError *error = NULL; + + stamp_dir_path = g_build_filename (g_get_user_data_dir (), PACKAGE_NAME, NULL); + if (g_mkdir_with_parents (stamp_dir_path, 0755)) { + g_warning ("Failed to create directory %s: %s", stamp_dir_path, g_strerror (errno)); + goto out; + } + + stamp_file_path = g_build_filename (stamp_dir_path, "input-sources-converted", NULL); + if (g_file_test (stamp_file_path, G_FILE_TEST_EXISTS)) + goto out; + + sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); + if (g_variant_n_children (sources) < 1) { + convert_libgnomekbd_layouts (settings); +#ifdef HAVE_IBUS + convert_ibus (settings); +#endif + } + g_variant_unref (sources); + + options = g_settings_get_strv (settings, KEY_KEYBOARD_OPTIONS); + if (g_strv_length (options) < 1) + convert_libgnomekbd_options (settings); + g_strfreev (options); + + if (!g_file_set_contents (stamp_file_path, "", 0, &error)) { + g_warning ("%s", error->message); g_error_free (error); - goto bail; } +out: + g_free (stamp_file_path); + g_free (stamp_dir_path); +} -bail: - if (proxy != NULL) - g_object_unref (proxy); - if (variant != NULL) - g_variant_unref (variant); - g_strfreev (layouts); +static void +maybe_create_input_sources (CsdKeyboardManager *manager) +{ + GSettings *settings; + GVariant *sources; + + settings = manager->priv->input_sources_settings; + + if (g_getenv ("RUNNING_UNDER_GDM")) { + create_sources_from_current_xkb_config (settings); + return; + } + + maybe_convert_old_settings (settings); + + /* if we still don't have anything do some educated guesses */ + sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); + if (g_variant_n_children (sources) < 1) { + create_sources_from_current_xkb_config (settings); +#ifdef HAVE_IBUS + add_ibus_sources_from_locale (settings); +#endif + } + g_variant_unref (sources); } static gboolean @@ -370,26 +1571,41 @@ start_keyboard_idle_cb (CsdKeyboardManag g_debug ("Starting keyboard manager"); - manager->priv->have_xkb = 0; manager->priv->settings = g_settings_new (CSD_KEYBOARD_DIR); - manager->priv->libgnomekbd_settings = g_settings_new (LIBGNOMEKBD_KEYBOARD_DIR); - /* Essential - xkb initialization should happen before */ - csd_keyboard_xkb_init (manager); + xkb_init (manager); - numlock_xkb_init (manager); + set_devicepresence_handler (manager); + manager->priv->input_sources_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + manager->priv->interface_settings = g_settings_new (GNOME_DESKTOP_INTERFACE_DIR); + manager->priv->xkb_info = gnome_xkb_info_new (); + + maybe_create_input_sources (manager); + +#ifdef HAVE_IBUS + /* We don't want to touch IBus until we are sure this isn't a + fallback session. */ + manager->priv->session_is_fallback = TRUE; + manager->priv->ibus_cancellable = g_cancellable_new (); + g_bus_get (G_BUS_TYPE_SESSION, + manager->priv->ibus_cancellable, + (GAsyncReadyCallback)got_bus, + manager); +#else + apply_input_sources_settings (manager->priv->input_sources_settings, NULL, 0, manager); +#endif /* apply current settings before we install the callback */ - csd_keyboard_manager_apply_settings (manager); + g_debug ("Started the keyboard plugin, applying all settings"); + apply_all_settings (manager); g_signal_connect (G_OBJECT (manager->priv->settings), "changed", - G_CALLBACK (apply_settings), manager); - - apply_libgnomekbd_settings (manager->priv->libgnomekbd_settings, NULL, manager); - g_signal_connect (G_OBJECT (manager->priv->libgnomekbd_settings), "changed", - G_CALLBACK (apply_libgnomekbd_settings), manager); + G_CALLBACK (settings_changed), manager); + g_signal_connect (G_OBJECT (manager->priv->input_sources_settings), "change-event", + G_CALLBACK (apply_input_sources_settings), manager); - numlock_install_xkb_callback (manager); + install_xkb_filter (manager); + set_input_sources_switcher (manager, enable_switcher (manager)); cinnamon_settings_profile_end (NULL); @@ -404,6 +1620,11 @@ csd_keyboard_manager_start (CsdKeyboardM { cinnamon_settings_profile_start (NULL); + if (check_xkb_extension (manager) == FALSE) { + g_debug ("XKB is not supported, not applying any settings"); + return TRUE; + } + manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_keyboard_idle_cb, manager); cinnamon_settings_profile_end (NULL); @@ -418,37 +1639,24 @@ csd_keyboard_manager_stop (CsdKeyboardMa g_debug ("Stopping keyboard manager"); - if (p->settings != NULL) { - g_object_unref (p->settings); - p->settings = NULL; - } + g_clear_object (&p->settings); + g_clear_object (&p->input_sources_settings); + g_clear_object (&p->interface_settings); + g_clear_object (&p->xkb_info); - if (p->libgnomekbd_settings != NULL) { - g_object_unref (p->libgnomekbd_settings); - p->libgnomekbd_settings = NULL; - } +#ifdef HAVE_IBUS + clear_ibus (manager); +#endif - if (p->have_xkb) { - gdk_window_remove_filter (NULL, - numlock_xkb_callback, - manager); + if (p->device_manager != NULL) { + g_signal_handler_disconnect (p->device_manager, p->device_added_id); + g_signal_handler_disconnect (p->device_manager, p->device_removed_id); + p->device_manager = NULL; } - csd_keyboard_xkb_shutdown (); -} - -static GObject * -csd_keyboard_manager_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties) -{ - CsdKeyboardManager *keyboard_manager; - - keyboard_manager = CSD_KEYBOARD_MANAGER (G_OBJECT_CLASS (csd_keyboard_manager_parent_class)->constructor (type, - n_construct_properties, - construct_properties)); + remove_xkb_filter (manager); - return G_OBJECT (keyboard_manager); + set_input_sources_switcher (manager, FALSE); } static void @@ -456,7 +1664,6 @@ csd_keyboard_manager_class_init (CsdKeyb { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->constructor = csd_keyboard_manager_constructor; object_class->finalize = csd_keyboard_manager_finalize; g_type_class_add_private (klass, sizeof (CsdKeyboardManagerPrivate)); diff -uNrp a/plugins/keyboard/csd-keyboard-manager.h b/plugins/keyboard/csd-keyboard-manager.h --- a/plugins/keyboard/csd-keyboard-manager.h 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/csd-keyboard-manager.h 2013-08-25 16:36:02.000000000 +0100 @@ -51,7 +51,6 @@ CsdKeyboardManager * csd_keyboard_ gboolean csd_keyboard_manager_start (CsdKeyboardManager *manager, GError **error); void csd_keyboard_manager_stop (CsdKeyboardManager *manager); -void csd_keyboard_manager_apply_settings (CsdKeyboardManager *manager); G_END_DECLS diff -uNrp a/plugins/keyboard/csd-keyboard-plugin.h b/plugins/keyboard/csd-keyboard-plugin.h --- a/plugins/keyboard/csd-keyboard-plugin.h 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/csd-keyboard-plugin.h 2013-08-25 16:36:02.000000000 +0100 @@ -52,7 +52,7 @@ typedef struct GType csd_keyboard_plugin_get_type (void) G_GNUC_CONST; /* All the plugins must implement this function */ -G_MODULE_EXPORT GType register_cinnamon_settings_plugin (GTypeModule *module); +G_MODULE_EXPORT GType register_gnome_settings_plugin (GTypeModule *module); G_END_DECLS diff -uNrp a/plugins/keyboard/csd-keyboard-xkb.c b/plugins/keyboard/csd-keyboard-xkb.c --- a/plugins/keyboard/csd-keyboard-xkb.c 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/csd-keyboard-xkb.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,579 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2001 Udaltsoft - * - * Written by Sergey V. Oudaltsov - * - * 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; either version 2, or (at your option) - * any later version. - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA - * 02110-1335, USA. - */ - -#include "config.h" - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "csd-keyboard-xkb.h" -#include "delayed-dialog.h" -#include "cinnamon-settings-profile.h" - -#define SETTINGS_KEYBOARD_DIR "org.cinnamon.settings-daemon.plugins.keyboard" - -static CsdKeyboardManager *manager = NULL; - -static XklEngine *xkl_engine; -static XklConfigRegistry *xkl_registry = NULL; - -static GkbdDesktopConfig current_config; -static GkbdKeyboardConfig current_kbd_config; - -/* never terminated */ -static GkbdKeyboardConfig initial_sys_kbd_config; - -static gboolean inited_ok = FALSE; - -static GSettings *settings_desktop = NULL; -static GSettings *settings_keyboard = NULL; - -static PostActivationCallback pa_callback = NULL; -static void *pa_callback_user_data = NULL; - -static GtkStatusIcon *icon = NULL; - -static GHashTable *preview_dialogs = NULL; - -static void -activation_error (void) -{ - char const *vendor; - GtkWidget *dialog; - - vendor = - ServerVendor (GDK_DISPLAY_XDISPLAY - (gdk_display_get_default ())); - - /* VNC viewers will not work, do not barrage them with warnings */ - if (NULL != vendor && NULL != strstr (vendor, "VNC")) - return; - - dialog = gtk_message_dialog_new_with_markup (NULL, - 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _ - ("Error activating XKB configuration.\n" - "There can be various reasons for that.\n\n" - "If you report this situation as a bug, include the results of\n" - " • %s\n" - " • %s\n" - " • %s\n" - " • %s"), - "xprop -root | grep XKB", - "gsettings get org.gnome.libgnomekbd.keyboard model", - "gsettings get org.gnome.libgnomekbd.keyboard layouts", - "gsettings get org.gnome.libgnomekbd.keyboard options"); - g_signal_connect (dialog, "response", - G_CALLBACK (gtk_widget_destroy), NULL); - csd_delayed_show_dialog (dialog); -} - -static gboolean -ensure_xkl_registry (void) -{ - if (!xkl_registry) { - xkl_registry = - xkl_config_registry_get_instance (xkl_engine); - /* load all materials, unconditionally! */ - if (!xkl_config_registry_load (xkl_registry, TRUE)) { - g_object_unref (xkl_registry); - xkl_registry = NULL; - return FALSE; - } - } - - return TRUE; -} - -static void -apply_desktop_settings (void) -{ - if (!inited_ok) - return; - - csd_keyboard_manager_apply_settings (manager); - gkbd_desktop_config_load (¤t_config); - /* again, probably it would be nice to compare things - before activating them */ - gkbd_desktop_config_activate (¤t_config); -} - -static void -popup_menu_launch_capplet () -{ - GAppInfo *info; - GdkAppLaunchContext *ctx; - GError *error = NULL; - - info = - g_app_info_create_from_commandline - ("cinnamon-settings region", NULL, 0, &error); - - if (info != NULL) { - ctx = - gdk_display_get_app_launch_context - (gdk_display_get_default ()); - - if (g_app_info_launch (info, NULL, - G_APP_LAUNCH_CONTEXT (ctx), &error) == FALSE) { - g_warning - ("Could not execute keyboard properties capplet: [%s]\n", - error->message); - g_error_free (error); - } - - g_object_unref (info); - g_object_unref (ctx); - } - -} - -static void -show_layout_destroy (GtkWidget * dialog, gint group) -{ - g_hash_table_remove (preview_dialogs, GINT_TO_POINTER (group)); -} - -static void -popup_menu_show_layout () -{ - GtkWidget *dialog; - XklEngine *engine = - xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY - (gdk_display_get_default ())); - XklState *xkl_state = xkl_engine_get_current_state (engine); - - gchar **group_names = gkbd_status_get_group_names (); - - gpointer p = g_hash_table_lookup (preview_dialogs, - GINT_TO_POINTER - (xkl_state->group)); - - if (xkl_state->group < 0 - || xkl_state->group >= g_strv_length (group_names)) { - return; - } - - if (p != NULL) { - /* existing window */ - gtk_window_present (GTK_WINDOW (p)); - return; - } - - if (!ensure_xkl_registry ()) - return; - - dialog = gkbd_keyboard_drawing_dialog_new (); - gkbd_keyboard_drawing_dialog_set_group (dialog, xkl_registry, xkl_state->group); - - g_signal_connect (dialog, "destroy", - G_CALLBACK (show_layout_destroy), - GINT_TO_POINTER (xkl_state->group)); - g_hash_table_insert (preview_dialogs, - GINT_TO_POINTER (xkl_state->group), dialog); - gtk_widget_show_all (dialog); -} - -static void -popup_menu_set_group (gint group_number, gboolean only_menu) -{ - - XklEngine *engine = gkbd_status_get_xkl_engine (); - - XklState *st = xkl_engine_get_current_state(engine); - Window cur; - st->group = group_number; - xkl_engine_allow_one_switch_to_secondary_group (engine); - cur = xkl_engine_get_current_window (engine); - if (cur != (Window) NULL) { - xkl_debug (150, "Enforcing the state %d for window %lx\n", - st->group, cur); - - xkl_engine_save_state (engine, - xkl_engine_get_current_window - (engine), st); -/* XSetInputFocus( GDK_DISPLAY(), cur, RevertToNone, CurrentTime );*/ - } else { - xkl_debug (150, - "??? Enforcing the state %d for unknown window\n", - st->group); - /* strange situation - bad things can happen */ - } - if (!only_menu) - xkl_engine_lock_group (engine, st->group); -} - -static void -popup_menu_set_group_cb (GtkMenuItem * item, gpointer param) -{ - gint group_number = GPOINTER_TO_INT (param); - - popup_menu_set_group(group_number, FALSE); -} - - -static GtkMenu * -create_status_menu (void) -{ - GtkMenu *popup_menu = GTK_MENU (gtk_menu_new ()); - int i = 0; - - GtkMenu *groups_menu = GTK_MENU (gtk_menu_new ()); - gchar **current_name = gkbd_status_get_group_names (); - - GtkWidget *item = gtk_menu_item_new_with_mnemonic (_("_Layouts")); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), - GTK_WIDGET (groups_menu)); - - item = gtk_menu_item_new_with_mnemonic (_("Show _Keyboard Layout...")); - gtk_widget_show (item); - g_signal_connect (item, "activate", popup_menu_show_layout, NULL); - gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); - - /* translators note: - * This is the name of the cinnamon-settings "region" panel */ - item = gtk_menu_item_new_with_mnemonic (_("Region and Language Settings")); - gtk_widget_show (item); - g_signal_connect (item, "activate", popup_menu_launch_capplet, NULL); - gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); - - for (i = 0; current_name && *current_name; i++, current_name++) { - - gchar *image_file = gkbd_status_get_image_filename (i); - - if (image_file == NULL) { - item = - gtk_menu_item_new_with_label (*current_name); - } else { - GdkPixbuf *pixbuf = - gdk_pixbuf_new_from_file_at_size (image_file, - 24, 24, - NULL); - GtkWidget *img = - gtk_image_new_from_pixbuf (pixbuf); - item = - gtk_image_menu_item_new_with_label - (*current_name); - gtk_widget_show (img); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM - (item), img); - gtk_image_menu_item_set_always_show_image - (GTK_IMAGE_MENU_ITEM (item), TRUE); - g_free (image_file); - } - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (groups_menu), item); - g_signal_connect (item, "activate", - G_CALLBACK (popup_menu_set_group_cb), - GINT_TO_POINTER (i)); - } - - return popup_menu; -} - -static void -status_icon_popup_menu_cb (GtkStatusIcon * icon, guint button, guint time) -{ - GtkMenu *popup_menu = create_status_menu (); - - gtk_menu_popup (popup_menu, NULL, NULL, - gtk_status_icon_position_menu, - (gpointer) icon, button, time); -} - -static void -show_hide_icon () -{ - if (g_strv_length (current_kbd_config.layouts_variants) > 1) { - if (icon == NULL) { - xkl_debug (150, "Creating keyboard status icon\n"); - icon = gkbd_status_new (); - g_signal_connect (icon, "popup-menu", - G_CALLBACK - (status_icon_popup_menu_cb), - NULL); - - } - } else { - if (icon != NULL) { - xkl_debug (150, "Destroying icon\n"); - g_object_unref (icon); - icon = NULL; - } - } -} - -static gboolean -try_activating_xkb_config_if_new (GkbdKeyboardConfig * - current_sys_kbd_config) -{ - /* Activate - only if different! */ - if (!gkbd_keyboard_config_equals - (¤t_kbd_config, current_sys_kbd_config)) { - if (gkbd_keyboard_config_activate (¤t_kbd_config)) { - if (pa_callback != NULL) { - (*pa_callback) (pa_callback_user_data); - return TRUE; - } - } else { - return FALSE; - } - } - return TRUE; -} - -static gboolean -filter_xkb_config (void) -{ - XklConfigItem *item; - gchar *lname; - gchar *vname; - gchar **lv; - gboolean any_change = FALSE; - - xkl_debug (100, "Filtering configuration against the registry\n"); - if (!ensure_xkl_registry ()) - return FALSE; - - lv = current_kbd_config.layouts_variants; - item = xkl_config_item_new (); - while (*lv) { - xkl_debug (100, "Checking [%s]\n", *lv); - if (gkbd_keyboard_config_split_items (*lv, &lname, &vname)) { - gboolean should_be_dropped = FALSE; - g_snprintf (item->name, sizeof (item->name), "%s", - lname); - if (!xkl_config_registry_find_layout - (xkl_registry, item)) { - xkl_debug (100, "Bad layout [%s]\n", - lname); - should_be_dropped = TRUE; - } else if (vname) { - g_snprintf (item->name, - sizeof (item->name), "%s", - vname); - if (!xkl_config_registry_find_variant - (xkl_registry, lname, item)) { - xkl_debug (100, - "Bad variant [%s(%s)]\n", - lname, vname); - should_be_dropped = TRUE; - } - } - if (should_be_dropped) { - gkbd_strv_behead (lv); - any_change = TRUE; - continue; - } - } - lv++; - } - g_object_unref (item); - return any_change; -} - -static void -apply_xkb_settings (void) -{ - GkbdKeyboardConfig current_sys_kbd_config; - - if (!inited_ok) - return; - - gkbd_keyboard_config_init (¤t_sys_kbd_config, xkl_engine); - - gkbd_keyboard_config_load (¤t_kbd_config, - &initial_sys_kbd_config); - - gkbd_keyboard_config_load_from_x_current (¤t_sys_kbd_config, - NULL); - - if (!try_activating_xkb_config_if_new (¤t_sys_kbd_config)) { - if (filter_xkb_config ()) { - if (!try_activating_xkb_config_if_new - (¤t_sys_kbd_config)) { - g_warning - ("Could not activate the filtered XKB configuration"); - activation_error (); - } - } else { - g_warning - ("Could not activate the XKB configuration"); - activation_error (); - } - } else - xkl_debug (100, - "Actual KBD configuration was not changed: redundant notification\n"); - - gkbd_keyboard_config_term (¤t_sys_kbd_config); - show_hide_icon (); -} - -static void -csd_keyboard_xkb_analyze_sysconfig (void) -{ - if (!inited_ok) - return; - - gkbd_keyboard_config_init (&initial_sys_kbd_config, xkl_engine); - gkbd_keyboard_config_load_from_x_initial (&initial_sys_kbd_config, - NULL); -} - -void -csd_keyboard_xkb_set_post_activation_callback (PostActivationCallback fun, - void *user_data) -{ - pa_callback = fun; - pa_callback_user_data = user_data; -} - -static GdkFilterReturn -csd_keyboard_xkb_evt_filter (GdkXEvent * xev, GdkEvent * event) -{ - XEvent *xevent = (XEvent *) xev; - xkl_engine_filter_events (xkl_engine, xevent); - return GDK_FILTER_CONTINUE; -} - -/* When new Keyboard is plugged in - reload the settings */ -static void -csd_keyboard_new_device (XklEngine * engine) -{ - apply_desktop_settings (); - apply_xkb_settings (); -} - -void -csd_keyboard_xkb_init (CsdKeyboardManager * kbd_manager) -{ - Display *display = - GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - cinnamon_settings_profile_start (NULL); - - gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), - DATADIR G_DIR_SEPARATOR_S - "icons"); - - manager = kbd_manager; - cinnamon_settings_profile_start ("xkl_engine_get_instance"); - xkl_engine = xkl_engine_get_instance (display); - cinnamon_settings_profile_end ("xkl_engine_get_instance"); - if (xkl_engine) { - inited_ok = TRUE; - - gkbd_desktop_config_init (¤t_config, xkl_engine); - gkbd_keyboard_config_init (¤t_kbd_config, - xkl_engine); - xkl_engine_backup_names_prop (xkl_engine); - csd_keyboard_xkb_analyze_sysconfig (); - - settings_desktop = g_settings_new (GKBD_DESKTOP_SCHEMA); - settings_keyboard = g_settings_new (GKBD_KEYBOARD_SCHEMA); - g_signal_connect (settings_desktop, "changed", - (GCallback) apply_desktop_settings, - NULL); - g_signal_connect (settings_keyboard, "changed", - (GCallback) apply_xkb_settings, NULL); - - gdk_window_add_filter (NULL, (GdkFilterFunc) - csd_keyboard_xkb_evt_filter, NULL); - - if (xkl_engine_get_features (xkl_engine) & - XKLF_DEVICE_DISCOVERY) - g_signal_connect (xkl_engine, "X-new-device", - G_CALLBACK - (csd_keyboard_new_device), NULL); - - cinnamon_settings_profile_start ("xkl_engine_start_listen"); - xkl_engine_start_listen (xkl_engine, - XKLL_MANAGE_LAYOUTS | - XKLL_MANAGE_WINDOW_STATES); - cinnamon_settings_profile_end ("xkl_engine_start_listen"); - - cinnamon_settings_profile_start ("apply_desktop_settings"); - apply_desktop_settings (); - cinnamon_settings_profile_end ("apply_desktop_settings"); - cinnamon_settings_profile_start ("apply_xkb_settings"); - apply_xkb_settings (); - cinnamon_settings_profile_end ("apply_xkb_settings"); - } - preview_dialogs = g_hash_table_new (g_direct_hash, g_direct_equal); - - cinnamon_settings_profile_end (NULL); -} - -void -csd_keyboard_xkb_shutdown (void) -{ - if (!inited_ok) - return; - - pa_callback = NULL; - pa_callback_user_data = NULL; - manager = NULL; - - if (preview_dialogs != NULL) - g_hash_table_destroy (preview_dialogs); - - if (!inited_ok) - return; - - xkl_engine_stop_listen (xkl_engine, - XKLL_MANAGE_LAYOUTS | - XKLL_MANAGE_WINDOW_STATES); - - gdk_window_remove_filter (NULL, (GdkFilterFunc) - csd_keyboard_xkb_evt_filter, NULL); - - g_object_unref (settings_desktop); - settings_desktop = NULL; - g_object_unref (settings_keyboard); - settings_keyboard = NULL; - - if (xkl_registry) { - g_object_unref (xkl_registry); - } - - g_object_unref (xkl_engine); - - xkl_engine = NULL; - - inited_ok = FALSE; -} diff -uNrp a/plugins/keyboard/csd-keyboard-xkb.h b/plugins/keyboard/csd-keyboard-xkb.h --- a/plugins/keyboard/csd-keyboard-xkb.h 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/csd-keyboard-xkb.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,39 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * cinnamon-settings-keyboard-xkb.h - * - * Copyright (C) 2001 Udaltsoft - * - * Written by Sergey V. Oudaltsov - * - * 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; either version 2, or (at your option) - * any later version. - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA - * 02110-1335, USA. - */ - -#ifndef __CSD_KEYBOARD_XKB_H -#define __CSD_KEYBOARD_XKB_H - -#include -#include "csd-keyboard-manager.h" - -void csd_keyboard_xkb_init (CsdKeyboardManager *manager); -void csd_keyboard_xkb_shutdown (void); - -typedef void (*PostActivationCallback) (void *userData); - -void -csd_keyboard_xkb_set_post_activation_callback (PostActivationCallback fun, - void *userData); - -#endif diff -uNrp a/plugins/keyboard/delayed-dialog.c b/plugins/keyboard/delayed-dialog.c --- a/plugins/keyboard/delayed-dialog.c 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/delayed-dialog.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,128 +0,0 @@ -/* - * Copyright © 2006 Novell, Inc. - * - * 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; either version 2, or (at - * your option) any later version. - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA - * 02110-1335, USA. - */ - -#include -#include - -#include -#include - -#include "delayed-dialog.h" - -static gboolean delayed_show_timeout (gpointer data); -static GdkFilterReturn message_filter (GdkXEvent *xevent, - GdkEvent *event, - gpointer data); - -static GSList *dialogs = NULL; - -/** - * csd_delayed_show_dialog: - * @dialog: the dialog - * - * Shows the dialog as with gtk_widget_show(), unless a window manager - * hasn't been started yet, in which case it will wait up to 5 seconds - * for that to happen before showing the dialog. - **/ -void -csd_delayed_show_dialog (GtkWidget *dialog) -{ - GdkDisplay *display = gtk_widget_get_display (dialog); - Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); - GdkScreen *screen = gtk_widget_get_screen (dialog); - char selection_name[10]; - Atom selection_atom; - - /* We can't use gdk_selection_owner_get() for this, because - * it's an unknown out-of-process window. - */ - snprintf (selection_name, sizeof (selection_name), "WM_S%d", - gdk_screen_get_number (screen)); - selection_atom = XInternAtom (xdisplay, selection_name, True); - if (selection_atom && - XGetSelectionOwner (xdisplay, selection_atom) != None) { - gtk_widget_show (dialog); - return; - } - - dialogs = g_slist_prepend (dialogs, dialog); - - gdk_window_add_filter (NULL, message_filter, NULL); - - g_timeout_add (5000, delayed_show_timeout, NULL); -} - -static gboolean -delayed_show_timeout (gpointer data) -{ - GSList *l; - - for (l = dialogs; l; l = l->next) - gtk_widget_show (l->data); - g_slist_free (dialogs); - dialogs = NULL; - - /* FIXME: There's no gdk_display_remove_client_message_filter */ - - return FALSE; -} - -static GdkFilterReturn -message_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data) -{ - XClientMessageEvent *evt; - char *selection_name; - int screen; - GSList *l, *next; - - if (((XEvent *)xevent)->type != ClientMessage) - return GDK_FILTER_CONTINUE; - - evt = (XClientMessageEvent *)xevent; - - if (evt->message_type != XInternAtom (evt->display, "MANAGER", FALSE)) - return GDK_FILTER_CONTINUE; - - selection_name = XGetAtomName (evt->display, evt->data.l[1]); - - if (strncmp (selection_name, "WM_S", 4) != 0) { - XFree (selection_name); - return GDK_FILTER_CONTINUE; - } - - screen = atoi (selection_name + 4); - - for (l = dialogs; l; l = next) { - GtkWidget *dialog = l->data; - next = l->next; - - if (gdk_screen_get_number (gtk_widget_get_screen (dialog)) == screen) { - gtk_widget_show (dialog); - dialogs = g_slist_remove (dialogs, dialog); - } - } - - if (!dialogs) { - gdk_window_remove_filter (NULL, message_filter, NULL); - } - - XFree (selection_name); - - return GDK_FILTER_CONTINUE; -} diff -uNrp a/plugins/keyboard/delayed-dialog.h b/plugins/keyboard/delayed-dialog.h --- a/plugins/keyboard/delayed-dialog.h 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/delayed-dialog.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,32 +0,0 @@ -/* - * Copyright © 2006 Novell, Inc. - * - * 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; either version 2, or (at - * your option) any later version. - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA - * 02110-1335, USA. - */ - - -#ifndef __DELAYED_DIALOG_H -#define __DELAYED_DIALOG_H - -#include - -G_BEGIN_DECLS - -void csd_delayed_show_dialog (GtkWidget *dialog); - -G_END_DECLS - -#endif diff -uNrp a/plugins/keyboard/gkbd-configuration.c b/plugins/keyboard/gkbd-configuration.c --- a/plugins/keyboard/gkbd-configuration.c 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/gkbd-configuration.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,350 +0,0 @@ -/* - * Copyright (C) 2010 Canonical Ltd. - * - * Authors: Jan Arne Petersen - * - * Based on gkbd-status.c by Sergey V. Udaltsov - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, - * Boston, MA 02110-1335, USA. - */ - -#include - -#include -#include -#include - -#include -#include - -#include "gkbd-configuration.h" - -struct _GkbdConfigurationPrivate { - XklEngine *engine; - XklConfigRegistry *registry; - - GkbdDesktopConfig cfg; - GkbdIndicatorConfig ind_cfg; - GkbdKeyboardConfig kbd_cfg; - - gchar **full_group_names; - gchar **short_group_names; - - gulong state_changed_handler; - gulong config_changed_handler; -}; - -enum { - SIGNAL_CHANGED, - SIGNAL_GROUP_CHANGED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0, }; - -#define GKBD_CONFIGURATION_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GKBD_TYPE_CONFIGURATION, GkbdConfigurationPrivate)) - -G_DEFINE_TYPE (GkbdConfiguration, gkbd_configuration, G_TYPE_OBJECT) - -/* Should be called once for all widgets */ -static void -gkbd_configuration_cfg_changed (GSettings *settings, - const char *key, - GkbdConfiguration * configuration) -{ - GkbdConfigurationPrivate *priv = configuration->priv; - - xkl_debug (100, - "General configuration changed in GSettings - reiniting...\n"); - gkbd_desktop_config_load (&priv->cfg); - gkbd_desktop_config_activate (&priv->cfg); - - g_signal_emit (configuration, - signals[SIGNAL_CHANGED], 0); -} - -/* Should be called once for all widgets */ -static void -gkbd_configuration_ind_cfg_changed (GSettings *settings, - const char *key, - GkbdConfiguration * configuration) -{ - GkbdConfigurationPrivate *priv = configuration->priv; - xkl_debug (100, - "Applet configuration changed in GSettings - reiniting...\n"); - gkbd_indicator_config_load (&priv->ind_cfg); - - gkbd_indicator_config_free_image_filenames (&priv->ind_cfg); - gkbd_indicator_config_load_image_filenames (&priv->ind_cfg, - &priv->kbd_cfg); - - gkbd_indicator_config_activate (&priv->ind_cfg); - - g_signal_emit (configuration, - signals[SIGNAL_CHANGED], 0); -} - -static void -gkbd_configuration_load_group_names (GkbdConfiguration * configuration, - XklConfigRec * xklrec) -{ - GkbdConfigurationPrivate *priv = configuration->priv; - - if (!gkbd_desktop_config_load_group_descriptions (&priv->cfg, - priv->registry, - (const char **) xklrec->layouts, - (const char **) xklrec->variants, - &priv->short_group_names, - &priv->full_group_names)) { - /* We just populate no short names (remain NULL) - - * full names are going to be used anyway */ - gint i, total_groups = - xkl_engine_get_num_groups (priv->engine); - xkl_debug (150, "group descriptions loaded: %d!\n", - total_groups); - priv->full_group_names = - g_new0 (char *, total_groups + 1); - - if (xkl_engine_get_features (priv->engine) & - XKLF_MULTIPLE_LAYOUTS_SUPPORTED) { - for (i = 0; priv->kbd_cfg.layouts_variants[i]; i++) { - priv->full_group_names[i] = - g_strdup ((char *) priv->kbd_cfg.layouts_variants[i]); - } - } else { - for (i = total_groups; --i >= 0;) { - priv->full_group_names[i] = - g_strdup_printf ("Group %d", i); - } - } - } -} - -/* Should be called once for all widgets */ -static void -gkbd_configuration_kbd_cfg_callback (XklEngine *engine, - GkbdConfiguration *configuration) -{ - GkbdConfigurationPrivate *priv = configuration->priv; - XklConfigRec *xklrec = xkl_config_rec_new (); - xkl_debug (100, - "XKB configuration changed on X Server - reiniting...\n"); - - gkbd_keyboard_config_load_from_x_current (&priv->kbd_cfg, - xklrec); - - gkbd_indicator_config_free_image_filenames (&priv->ind_cfg); - gkbd_indicator_config_load_image_filenames (&priv->ind_cfg, - &priv->kbd_cfg); - - g_strfreev (priv->full_group_names); - priv->full_group_names = NULL; - - g_strfreev (priv->short_group_names); - priv->short_group_names = NULL; - - gkbd_configuration_load_group_names (configuration, - xklrec); - - g_signal_emit (configuration, - signals[SIGNAL_CHANGED], - 0); - - g_object_unref (G_OBJECT (xklrec)); -} - -/* Should be called once for all applets */ -static void -gkbd_configuration_state_callback (XklEngine * engine, - XklEngineStateChange changeType, - gint group, gboolean restore, - GkbdConfiguration * configuration) -{ - xkl_debug (150, "group is now %d, restore: %d\n", group, restore); - - if (changeType == GROUP_CHANGED) { - g_signal_emit (configuration, - signals[SIGNAL_GROUP_CHANGED], 0, - group); - } -} - -static void -gkbd_configuration_init (GkbdConfiguration *configuration) -{ - GkbdConfigurationPrivate *priv; - XklConfigRec *xklrec = xkl_config_rec_new (); - - priv = GKBD_CONFIGURATION_GET_PRIVATE (configuration); - configuration->priv = priv; - - priv->engine = xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); - if (priv->engine == NULL) { - xkl_debug (0, "Libxklavier initialization error"); - return; - } - - priv->state_changed_handler = - g_signal_connect (priv->engine, "X-state-changed", - G_CALLBACK (gkbd_configuration_state_callback), - configuration); - priv->config_changed_handler = - g_signal_connect (priv->engine, "X-config-changed", - G_CALLBACK (gkbd_configuration_kbd_cfg_callback), - configuration); - - gkbd_desktop_config_init (&priv->cfg, priv->engine); - gkbd_keyboard_config_init (&priv->kbd_cfg, priv->engine); - gkbd_indicator_config_init (&priv->ind_cfg, priv->engine); - - gkbd_desktop_config_load (&priv->cfg); - gkbd_desktop_config_activate (&priv->cfg); - - priv->registry = xkl_config_registry_get_instance (priv->engine); - xkl_config_registry_load (priv->registry, - priv->cfg.load_extra_items); - - gkbd_keyboard_config_load_from_x_current (&priv->kbd_cfg, - xklrec); - - gkbd_indicator_config_load (&priv->ind_cfg); - - gkbd_indicator_config_load_image_filenames (&priv->ind_cfg, - &priv->kbd_cfg); - - gkbd_indicator_config_activate (&priv->ind_cfg); - - gkbd_configuration_load_group_names (configuration, - xklrec); - g_object_unref (G_OBJECT (xklrec)); - - gkbd_desktop_config_start_listen (&priv->cfg, - G_CALLBACK (gkbd_configuration_cfg_changed), - configuration); - gkbd_indicator_config_start_listen (&priv->ind_cfg, - G_CALLBACK (gkbd_configuration_ind_cfg_changed), - configuration); - xkl_engine_start_listen (priv->engine, - XKLL_TRACK_KEYBOARD_STATE); - - xkl_debug (100, "Initiating the widget startup process for %p\n", - configuration); -} - -static void -gkbd_configuration_finalize (GObject * obj) -{ - GkbdConfiguration *configuration = GKBD_CONFIGURATION (obj); - GkbdConfigurationPrivate *priv = configuration->priv; - - xkl_debug (100, - "Starting the gnome-kbd-configuration widget shutdown process for %p\n", - configuration); - - xkl_engine_stop_listen (priv->engine, - XKLL_TRACK_KEYBOARD_STATE); - - gkbd_desktop_config_stop_listen (&priv->cfg); - gkbd_indicator_config_stop_listen (&priv->ind_cfg); - - gkbd_indicator_config_term (&priv->ind_cfg); - gkbd_keyboard_config_term (&priv->kbd_cfg); - gkbd_desktop_config_term (&priv->cfg); - - if (g_signal_handler_is_connected (priv->engine, - priv->state_changed_handler)) { - g_signal_handler_disconnect (priv->engine, - priv->state_changed_handler); - priv->state_changed_handler = 0; - } - if (g_signal_handler_is_connected (priv->engine, - priv->config_changed_handler)) { - g_signal_handler_disconnect (priv->engine, - priv->config_changed_handler); - priv->config_changed_handler = 0; - } - - g_object_unref (priv->registry); - priv->registry = NULL; - g_object_unref (priv->engine); - priv->engine = NULL; - - G_OBJECT_CLASS (gkbd_configuration_parent_class)->finalize (obj); -} - -static void -gkbd_configuration_class_init (GkbdConfigurationClass * klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - /* Initing vtable */ - object_class->finalize = gkbd_configuration_finalize; - - /* Signals */ - signals[SIGNAL_CHANGED] = g_signal_new ("changed", - GKBD_TYPE_CONFIGURATION, - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - signals[SIGNAL_GROUP_CHANGED] = g_signal_new ("group-changed", - GKBD_TYPE_CONFIGURATION, - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, - 1, - G_TYPE_INT); - - g_type_class_add_private (klass, sizeof (GkbdConfigurationPrivate)); -} - -GkbdConfiguration * -gkbd_configuration_get (void) -{ - static gpointer instance = NULL; - - if (!instance) { - instance = g_object_new (GKBD_TYPE_CONFIGURATION, NULL); - g_object_add_weak_pointer (instance, &instance); - } else { - g_object_ref (instance); - } - - return instance; -} - -XklEngine * -gkbd_configuration_get_xkl_engine (GkbdConfiguration *configuration) -{ - return configuration->priv->engine; -} - -const char * const * -gkbd_configuration_get_group_names (GkbdConfiguration *configuration) -{ - return configuration->priv->full_group_names; -} - -const char * const * -gkbd_configuration_get_short_group_names (GkbdConfiguration *configuration) -{ - return configuration->priv->short_group_names; -} diff -uNrp a/plugins/keyboard/gkbd-configuration.h b/plugins/keyboard/gkbd-configuration.h --- a/plugins/keyboard/gkbd-configuration.h 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/gkbd-configuration.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010 Canonical Ltd. - * - * Authors: Jan Arne Petersen - * - * Based on gkbd-status.h by Sergey V. Udaltsov - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, - * Boston, MA 02110-1335, USA. - */ - -#ifndef __GKBD_CONFIGURATION_H__ -#define __GKBD_CONFIGURATION_H__ - -#include - -#include - -G_BEGIN_DECLS - -typedef struct _GkbdConfiguration GkbdConfiguration; -typedef struct _GkbdConfigurationPrivate GkbdConfigurationPrivate; -typedef struct _GkbdConfigurationClass GkbdConfigurationClass; - -#define GKBD_TYPE_CONFIGURATION (gkbd_configuration_get_type ()) -#define GKBD_CONFIGURATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GKBD_TYPE_CONFIGURATION, GkbdConfiguration)) -#define GKBD_INDCATOR_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GKBD_TYPE_CONFIGURATION, GkbdConfigurationClass)) -#define GKBD_IS_CONFIGURATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GKBD_TYPE_CONFIGURATION)) -#define GKBD_IS_CONFIGURATION_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GKBD_TYPE_CONFIGURATION)) -#define GKBD_CONFIGURATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GKBD_TYPE_CONFIGURATION, GkbdConfigurationClass)) - -struct _GkbdConfiguration { - GObject parent; - - GkbdConfigurationPrivate *priv; -}; - -struct _GkbdConfigurationClass { - GObjectClass parent_class; -}; - -extern GType gkbd_configuration_get_type (void); - -extern GkbdConfiguration *gkbd_configuration_get (void); - -extern XklEngine *gkbd_configuration_get_xkl_engine (GkbdConfiguration *configuration); - -extern const char * const *gkbd_configuration_get_group_names (GkbdConfiguration *configuration); -extern const char * const *gkbd_configuration_get_short_group_names (GkbdConfiguration *configuration); - -G_END_DECLS - -#endif diff -uNrp a/plugins/keyboard/.indent.pro b/plugins/keyboard/.indent.pro --- a/plugins/keyboard/.indent.pro 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/.indent.pro 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,2 @@ +-kr -i8 -pcs -lps -psl + diff -uNrp a/plugins/keyboard/Makefile.am b/plugins/keyboard/Makefile.am --- a/plugins/keyboard/Makefile.am 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/keyboard/Makefile.am 2013-08-25 16:36:02.000000000 +0100 @@ -20,25 +20,20 @@ libkeyboard_la_SOURCES = \ csd-keyboard-plugin.c \ csd-keyboard-manager.h \ csd-keyboard-manager.c \ - csd-keyboard-xkb.h \ - csd-keyboard-xkb.c \ - delayed-dialog.h \ - delayed-dialog.c \ - gkbd-configuration.c \ - gkbd-configuration.h \ $(NULL) libkeyboard_la_CPPFLAGS = \ -I$(top_srcdir)/cinnamon-settings-daemon \ -I$(top_srcdir)/data \ + -I$(top_srcdir)/plugins/common \ -DDATADIR=\""$(pkgdatadir)"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ -DCINNAMON_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libkeyboard_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ - $(APPINDICATOR_CFLAGS) \ $(KEYBOARD_CFLAGS) \ $(AM_CFLAGS) @@ -46,19 +41,63 @@ libkeyboard_la_LDFLAGS = \ $(CSD_PLUGIN_LDFLAGS) \ $(NULL) -libkeyboard_la_LIBADD = \ - $(SETTINGS_PLUGIN_LIBS) \ - $(XF86MISC_LIBS) \ - $(KEYBOARD_LIBS) \ - $(APPINDICATOR_LIBS) \ +libkeyboard_la_LIBADD = \ + $(top_builddir)/plugins/common/libcommon.la \ + $(SETTINGS_PLUGIN_LIBS) \ + $(XF86MISC_LIBS) \ + $(KEYBOARD_LIBS) \ $(NULL) +libexec_PROGRAMS = csd-test-keyboard +csd_test_keyboard_SOURCES = \ + test-keyboard.c \ + csd-keyboard-manager.h \ + csd-keyboard-manager.c \ + $(NULL) + +csd_test_keyboard_CFLAGS = $(libkeyboard_la_CFLAGS) +csd_test_keyboard_CPPFLAGS = $(libkeyboard_la_CPPFLAGS) +csd_test_keyboard_LDADD = $(libkeyboard_la_LIBADD) $(top_builddir)/cinnamon-settings-daemon/libcsd.la + plugin_in_files = \ keyboard.cinnamon-settings-plugin.in \ $(NULL) plugin_DATA = $(plugin_in_files:.cinnamon-settings-plugin.in=.cinnamon-settings-plugin) +if HAVE_IBUS +noinst_PROGRAMS = test-keyboard-ibus-utils +test_keyboard_ibus_utils_SOURCES = test-keyboard-ibus-utils.c +test_keyboard_ibus_utils_CFLAGS = $(libkeyboard_la_CFLAGS) +test_keyboard_ibus_utils_CPPFLAGS = $(libkeyboard_la_CPPFLAGS) +test_keyboard_ibus_utils_LDADD = $(libkeyboard_la_LIBADD) $(top_builddir)/cinnamon-settings-daemon/libcsd.la + +check-local: test-keyboard-ibus-utils + $(builddir)/test-keyboard-ibus-utils > /dev/null +endif + +libexec_PROGRAMS += csd-input-sources-switcher + +csd_input_sources_switcher_SOURCES = \ + csd-input-sources-switcher.c \ + $(NULL) + +csd_input_sources_switcher_CPPFLAGS = \ + -I$(top_srcdir)/data \ + -I$(top_srcdir)/plugins/common \ + $(AM_CPPFLAGS) \ + $(NULL) + +csd_input_sources_switcher_CFLAGS = \ + $(SETTINGS_PLUGIN_CFLAGS) \ + $(AM_CFLAGS) \ + $(NULL) + +csd_input_sources_switcher_LDADD = \ + $(top_builddir)/plugins/common/libcommon.la \ + $(SETTINGS_PLUGIN_LIBS) \ + $(NULL) + EXTRA_DIST = \ $(icons_DATA) \ $(plugin_in_files) \ diff -uNrp a/plugins/keyboard/test-keyboard.c b/plugins/keyboard/test-keyboard.c --- a/plugins/keyboard/test-keyboard.c 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/test-keyboard.c 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,7 @@ +#define NEW csd_keyboard_manager_new +#define START csd_keyboard_manager_start +#define STOP csd_keyboard_manager_stop +#define MANAGER CsdKeyboardManager +#include "csd-keyboard-manager.h" + +#include "test-plugin.h" diff -uNrp a/plugins/keyboard/test-keyboard-ibus-utils.c b/plugins/keyboard/test-keyboard-ibus-utils.c --- a/plugins/keyboard/test-keyboard-ibus-utils.c 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/test-keyboard-ibus-utils.c 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,116 @@ +#include "csd-keyboard-manager.c" + +static void +test_make_xkb_source_id (void) +{ + gint i; + const gchar *test_strings[][2] = { + /* input output */ + { "xkb:aa:bb:cc", "aa+bb" }, + { "xkb:aa:bb:", "aa+bb" }, + { "xkb:aa::cc", "aa" }, + { "xkb:aa::", "aa" }, + { "xkb::bb:cc", "+bb" }, + { "xkb::bb:", "+bb" }, + { "xkb:::cc", "" }, + { "xkb:::", "" }, + }; + + for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) + g_assert_cmpstr (make_xkb_source_id (test_strings[i][0]), ==, test_strings[i][1]); +} + +static void +test_layout_from_ibus_layout (void) +{ + gint i; + const gchar *test_strings[][2] = { + /* input output */ + { "", "" }, + { "a", "a" }, + { "a(", "a" }, + { "a[", "a" }, + }; + + for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) + g_assert_cmpstr (layout_from_ibus_layout (test_strings[i][0]), ==, test_strings[i][1]); +} + +static void +test_variant_from_ibus_layout (void) +{ + gint i; + const gchar *test_strings[][2] = { + /* input output */ + { "", NULL }, + { "a", NULL }, + { "(", NULL }, + { "()", "" }, + { "(b)", "b" }, + { "a(", NULL }, + { "a()", "" }, + { "a(b)", "b" }, + }; + + for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) + g_assert_cmpstr (variant_from_ibus_layout (test_strings[i][0]), ==, test_strings[i][1]); +} + +static void +test_options_from_ibus_layout (void) +{ + gint i, j; + gchar *output_0[] = { + NULL + }; + gchar *output_1[] = { + "", + NULL + }; + gchar *output_2[] = { + "b", + NULL + }; + gchar *output_3[] = { + "b", "", + NULL + }; + gchar *output_4[] = { + "b", "c", + NULL + }; + const gpointer tests[][2] = { + /* input output */ + { "", NULL }, + { "a", NULL }, + { "a[", output_0 }, + { "a[]", output_1 }, + { "a[b]", output_2 }, + { "a[b,]", output_3 }, + { "a[b,c]", output_4 }, + }; + + for (i = 0; i < G_N_ELEMENTS (tests); ++i) { + if (tests[i][1] == NULL) { + g_assert (options_from_ibus_layout (tests[i][0]) == NULL); + } else { + gchar **strv_a = options_from_ibus_layout (tests[i][0]); + gchar **strv_b = tests[i][1]; + + g_assert (g_strv_length (strv_a) == g_strv_length (strv_b)); + for (j = 0; j < g_strv_length (strv_a); ++j) + g_assert_cmpstr (strv_a[j], ==, strv_b[j]); + } + } +} + +int +main (void) +{ + test_make_xkb_source_id (); + test_layout_from_ibus_layout (); + test_variant_from_ibus_layout (); + test_options_from_ibus_layout (); + + return 0; +} diff -uNrp a/plugins/keyboard/xxx/csd-keyboard-xkb.c b/plugins/keyboard/xxx/csd-keyboard-xkb.c --- a/plugins/keyboard/xxx/csd-keyboard-xkb.c 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/xxx/csd-keyboard-xkb.c 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,579 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2001 Udaltsoft + * + * Written by Sergey V. Oudaltsov + * + * 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; either version 2, or (at your option) + * any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA + * 02110-1335, USA. + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "csd-keyboard-xkb.h" +#include "delayed-dialog.h" +#include "cinnamon-settings-profile.h" + +#define SETTINGS_KEYBOARD_DIR "org.cinnamon.settings-daemon.plugins.keyboard" + +static CsdKeyboardManager *manager = NULL; + +static XklEngine *xkl_engine; +static XklConfigRegistry *xkl_registry = NULL; + +static GkbdDesktopConfig current_config; +static GkbdKeyboardConfig current_kbd_config; + +/* never terminated */ +static GkbdKeyboardConfig initial_sys_kbd_config; + +static gboolean inited_ok = FALSE; + +static GSettings *settings_desktop = NULL; +static GSettings *settings_keyboard = NULL; + +static PostActivationCallback pa_callback = NULL; +static void *pa_callback_user_data = NULL; + +static GtkStatusIcon *icon = NULL; + +static GHashTable *preview_dialogs = NULL; + +static void +activation_error (void) +{ + char const *vendor; + GtkWidget *dialog; + + vendor = + ServerVendor (GDK_DISPLAY_XDISPLAY + (gdk_display_get_default ())); + + /* VNC viewers will not work, do not barrage them with warnings */ + if (NULL != vendor && NULL != strstr (vendor, "VNC")) + return; + + dialog = gtk_message_dialog_new_with_markup (NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _ + ("Error activating XKB configuration.\n" + "There can be various reasons for that.\n\n" + "If you report this situation as a bug, include the results of\n" + " • %s\n" + " • %s\n" + " • %s\n" + " • %s"), + "xprop -root | grep XKB", + "gsettings get org.gnome.libgnomekbd.keyboard model", + "gsettings get org.gnome.libgnomekbd.keyboard layouts", + "gsettings get org.gnome.libgnomekbd.keyboard options"); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + csd_delayed_show_dialog (dialog); +} + +static gboolean +ensure_xkl_registry (void) +{ + if (!xkl_registry) { + xkl_registry = + xkl_config_registry_get_instance (xkl_engine); + /* load all materials, unconditionally! */ + if (!xkl_config_registry_load (xkl_registry, TRUE)) { + g_object_unref (xkl_registry); + xkl_registry = NULL; + return FALSE; + } + } + + return TRUE; +} + +static void +apply_desktop_settings (void) +{ + if (!inited_ok) + return; + + csd_keyboard_manager_apply_settings (manager); + gkbd_desktop_config_load (¤t_config); + /* again, probably it would be nice to compare things + before activating them */ + gkbd_desktop_config_activate (¤t_config); +} + +static void +popup_menu_launch_capplet () +{ + GAppInfo *info; + GdkAppLaunchContext *ctx; + GError *error = NULL; + + info = + g_app_info_create_from_commandline + ("cinnamon-settings region", NULL, 0, &error); + + if (info != NULL) { + ctx = + gdk_display_get_app_launch_context + (gdk_display_get_default ()); + + if (g_app_info_launch (info, NULL, + G_APP_LAUNCH_CONTEXT (ctx), &error) == FALSE) { + g_warning + ("Could not execute keyboard properties capplet: [%s]\n", + error->message); + g_error_free (error); + } + + g_object_unref (info); + g_object_unref (ctx); + } + +} + +static void +show_layout_destroy (GtkWidget * dialog, gint group) +{ + g_hash_table_remove (preview_dialogs, GINT_TO_POINTER (group)); +} + +static void +popup_menu_show_layout () +{ + GtkWidget *dialog; + XklEngine *engine = + xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY + (gdk_display_get_default ())); + XklState *xkl_state = xkl_engine_get_current_state (engine); + + gchar **group_names = gkbd_status_get_group_names (); + + gpointer p = g_hash_table_lookup (preview_dialogs, + GINT_TO_POINTER + (xkl_state->group)); + + if (xkl_state->group < 0 + || xkl_state->group >= g_strv_length (group_names)) { + return; + } + + if (p != NULL) { + /* existing window */ + gtk_window_present (GTK_WINDOW (p)); + return; + } + + if (!ensure_xkl_registry ()) + return; + + dialog = gkbd_keyboard_drawing_dialog_new (); + gkbd_keyboard_drawing_dialog_set_group (dialog, xkl_registry, xkl_state->group); + + g_signal_connect (dialog, "destroy", + G_CALLBACK (show_layout_destroy), + GINT_TO_POINTER (xkl_state->group)); + g_hash_table_insert (preview_dialogs, + GINT_TO_POINTER (xkl_state->group), dialog); + gtk_widget_show_all (dialog); +} + +static void +popup_menu_set_group (gint group_number, gboolean only_menu) +{ + + XklEngine *engine = gkbd_status_get_xkl_engine (); + + XklState *st = xkl_engine_get_current_state(engine); + Window cur; + st->group = group_number; + xkl_engine_allow_one_switch_to_secondary_group (engine); + cur = xkl_engine_get_current_window (engine); + if (cur != (Window) NULL) { + xkl_debug (150, "Enforcing the state %d for window %lx\n", + st->group, cur); + + xkl_engine_save_state (engine, + xkl_engine_get_current_window + (engine), st); +/* XSetInputFocus( GDK_DISPLAY(), cur, RevertToNone, CurrentTime );*/ + } else { + xkl_debug (150, + "??? Enforcing the state %d for unknown window\n", + st->group); + /* strange situation - bad things can happen */ + } + if (!only_menu) + xkl_engine_lock_group (engine, st->group); +} + +static void +popup_menu_set_group_cb (GtkMenuItem * item, gpointer param) +{ + gint group_number = GPOINTER_TO_INT (param); + + popup_menu_set_group(group_number, FALSE); +} + + +static GtkMenu * +create_status_menu (void) +{ + GtkMenu *popup_menu = GTK_MENU (gtk_menu_new ()); + int i = 0; + + GtkMenu *groups_menu = GTK_MENU (gtk_menu_new ()); + gchar **current_name = gkbd_status_get_group_names (); + + GtkWidget *item = gtk_menu_item_new_with_mnemonic (_("_Layouts")); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), + GTK_WIDGET (groups_menu)); + + item = gtk_menu_item_new_with_mnemonic (_("Show _Keyboard Layout...")); + gtk_widget_show (item); + g_signal_connect (item, "activate", popup_menu_show_layout, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); + + /* translators note: + * This is the name of the cinnamon-settings "region" panel */ + item = gtk_menu_item_new_with_mnemonic (_("Region and Language Settings")); + gtk_widget_show (item); + g_signal_connect (item, "activate", popup_menu_launch_capplet, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); + + for (i = 0; current_name && *current_name; i++, current_name++) { + + gchar *image_file = gkbd_status_get_image_filename (i); + + if (image_file == NULL) { + item = + gtk_menu_item_new_with_label (*current_name); + } else { + GdkPixbuf *pixbuf = + gdk_pixbuf_new_from_file_at_size (image_file, + 24, 24, + NULL); + GtkWidget *img = + gtk_image_new_from_pixbuf (pixbuf); + item = + gtk_image_menu_item_new_with_label + (*current_name); + gtk_widget_show (img); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM + (item), img); + gtk_image_menu_item_set_always_show_image + (GTK_IMAGE_MENU_ITEM (item), TRUE); + g_free (image_file); + } + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (groups_menu), item); + g_signal_connect (item, "activate", + G_CALLBACK (popup_menu_set_group_cb), + GINT_TO_POINTER (i)); + } + + return popup_menu; +} + +static void +status_icon_popup_menu_cb (GtkStatusIcon * icon, guint button, guint time) +{ + GtkMenu *popup_menu = create_status_menu (); + + gtk_menu_popup (popup_menu, NULL, NULL, + gtk_status_icon_position_menu, + (gpointer) icon, button, time); +} + +static void +show_hide_icon () +{ + if (g_strv_length (current_kbd_config.layouts_variants) > 1) { + if (icon == NULL) { + xkl_debug (150, "Creating keyboard status icon\n"); + icon = gkbd_status_new (); + g_signal_connect (icon, "popup-menu", + G_CALLBACK + (status_icon_popup_menu_cb), + NULL); + + } + } else { + if (icon != NULL) { + xkl_debug (150, "Destroying icon\n"); + g_object_unref (icon); + icon = NULL; + } + } +} + +static gboolean +try_activating_xkb_config_if_new (GkbdKeyboardConfig * + current_sys_kbd_config) +{ + /* Activate - only if different! */ + if (!gkbd_keyboard_config_equals + (¤t_kbd_config, current_sys_kbd_config)) { + if (gkbd_keyboard_config_activate (¤t_kbd_config)) { + if (pa_callback != NULL) { + (*pa_callback) (pa_callback_user_data); + return TRUE; + } + } else { + return FALSE; + } + } + return TRUE; +} + +static gboolean +filter_xkb_config (void) +{ + XklConfigItem *item; + gchar *lname; + gchar *vname; + gchar **lv; + gboolean any_change = FALSE; + + xkl_debug (100, "Filtering configuration against the registry\n"); + if (!ensure_xkl_registry ()) + return FALSE; + + lv = current_kbd_config.layouts_variants; + item = xkl_config_item_new (); + while (*lv) { + xkl_debug (100, "Checking [%s]\n", *lv); + if (gkbd_keyboard_config_split_items (*lv, &lname, &vname)) { + gboolean should_be_dropped = FALSE; + g_snprintf (item->name, sizeof (item->name), "%s", + lname); + if (!xkl_config_registry_find_layout + (xkl_registry, item)) { + xkl_debug (100, "Bad layout [%s]\n", + lname); + should_be_dropped = TRUE; + } else if (vname) { + g_snprintf (item->name, + sizeof (item->name), "%s", + vname); + if (!xkl_config_registry_find_variant + (xkl_registry, lname, item)) { + xkl_debug (100, + "Bad variant [%s(%s)]\n", + lname, vname); + should_be_dropped = TRUE; + } + } + if (should_be_dropped) { + gkbd_strv_behead (lv); + any_change = TRUE; + continue; + } + } + lv++; + } + g_object_unref (item); + return any_change; +} + +static void +apply_xkb_settings (void) +{ + GkbdKeyboardConfig current_sys_kbd_config; + + if (!inited_ok) + return; + + gkbd_keyboard_config_init (¤t_sys_kbd_config, xkl_engine); + + gkbd_keyboard_config_load (¤t_kbd_config, + &initial_sys_kbd_config); + + gkbd_keyboard_config_load_from_x_current (¤t_sys_kbd_config, + NULL); + + if (!try_activating_xkb_config_if_new (¤t_sys_kbd_config)) { + if (filter_xkb_config ()) { + if (!try_activating_xkb_config_if_new + (¤t_sys_kbd_config)) { + g_warning + ("Could not activate the filtered XKB configuration"); + activation_error (); + } + } else { + g_warning + ("Could not activate the XKB configuration"); + activation_error (); + } + } else + xkl_debug (100, + "Actual KBD configuration was not changed: redundant notification\n"); + + gkbd_keyboard_config_term (¤t_sys_kbd_config); + show_hide_icon (); +} + +static void +csd_keyboard_xkb_analyze_sysconfig (void) +{ + if (!inited_ok) + return; + + gkbd_keyboard_config_init (&initial_sys_kbd_config, xkl_engine); + gkbd_keyboard_config_load_from_x_initial (&initial_sys_kbd_config, + NULL); +} + +void +csd_keyboard_xkb_set_post_activation_callback (PostActivationCallback fun, + void *user_data) +{ + pa_callback = fun; + pa_callback_user_data = user_data; +} + +static GdkFilterReturn +csd_keyboard_xkb_evt_filter (GdkXEvent * xev, GdkEvent * event) +{ + XEvent *xevent = (XEvent *) xev; + xkl_engine_filter_events (xkl_engine, xevent); + return GDK_FILTER_CONTINUE; +} + +/* When new Keyboard is plugged in - reload the settings */ +static void +csd_keyboard_new_device (XklEngine * engine) +{ + apply_desktop_settings (); + apply_xkb_settings (); +} + +void +csd_keyboard_xkb_init (CsdKeyboardManager * kbd_manager) +{ + Display *display = + GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + cinnamon_settings_profile_start (NULL); + + gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), + DATADIR G_DIR_SEPARATOR_S + "icons"); + + manager = kbd_manager; + cinnamon_settings_profile_start ("xkl_engine_get_instance"); + xkl_engine = xkl_engine_get_instance (display); + cinnamon_settings_profile_end ("xkl_engine_get_instance"); + if (xkl_engine) { + inited_ok = TRUE; + + gkbd_desktop_config_init (¤t_config, xkl_engine); + gkbd_keyboard_config_init (¤t_kbd_config, + xkl_engine); + xkl_engine_backup_names_prop (xkl_engine); + csd_keyboard_xkb_analyze_sysconfig (); + + settings_desktop = g_settings_new (GKBD_DESKTOP_SCHEMA); + settings_keyboard = g_settings_new (GKBD_KEYBOARD_SCHEMA); + g_signal_connect (settings_desktop, "changed", + (GCallback) apply_desktop_settings, + NULL); + g_signal_connect (settings_keyboard, "changed", + (GCallback) apply_xkb_settings, NULL); + + gdk_window_add_filter (NULL, (GdkFilterFunc) + csd_keyboard_xkb_evt_filter, NULL); + + if (xkl_engine_get_features (xkl_engine) & + XKLF_DEVICE_DISCOVERY) + g_signal_connect (xkl_engine, "X-new-device", + G_CALLBACK + (csd_keyboard_new_device), NULL); + + cinnamon_settings_profile_start ("xkl_engine_start_listen"); + xkl_engine_start_listen (xkl_engine, + XKLL_MANAGE_LAYOUTS | + XKLL_MANAGE_WINDOW_STATES); + cinnamon_settings_profile_end ("xkl_engine_start_listen"); + + cinnamon_settings_profile_start ("apply_desktop_settings"); + apply_desktop_settings (); + cinnamon_settings_profile_end ("apply_desktop_settings"); + cinnamon_settings_profile_start ("apply_xkb_settings"); + apply_xkb_settings (); + cinnamon_settings_profile_end ("apply_xkb_settings"); + } + preview_dialogs = g_hash_table_new (g_direct_hash, g_direct_equal); + + cinnamon_settings_profile_end (NULL); +} + +void +csd_keyboard_xkb_shutdown (void) +{ + if (!inited_ok) + return; + + pa_callback = NULL; + pa_callback_user_data = NULL; + manager = NULL; + + if (preview_dialogs != NULL) + g_hash_table_destroy (preview_dialogs); + + if (!inited_ok) + return; + + xkl_engine_stop_listen (xkl_engine, + XKLL_MANAGE_LAYOUTS | + XKLL_MANAGE_WINDOW_STATES); + + gdk_window_remove_filter (NULL, (GdkFilterFunc) + csd_keyboard_xkb_evt_filter, NULL); + + g_object_unref (settings_desktop); + settings_desktop = NULL; + g_object_unref (settings_keyboard); + settings_keyboard = NULL; + + if (xkl_registry) { + g_object_unref (xkl_registry); + } + + g_object_unref (xkl_engine); + + xkl_engine = NULL; + + inited_ok = FALSE; +} diff -uNrp a/plugins/keyboard/xxx/csd-keyboard-xkb.h b/plugins/keyboard/xxx/csd-keyboard-xkb.h --- a/plugins/keyboard/xxx/csd-keyboard-xkb.h 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/xxx/csd-keyboard-xkb.h 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * cinnamon-settings-keyboard-xkb.h + * + * Copyright (C) 2001 Udaltsoft + * + * Written by Sergey V. Oudaltsov + * + * 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; either version 2, or (at your option) + * any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA + * 02110-1335, USA. + */ + +#ifndef __CSD_KEYBOARD_XKB_H +#define __CSD_KEYBOARD_XKB_H + +#include +#include "csd-keyboard-manager.h" + +void csd_keyboard_xkb_init (CsdKeyboardManager *manager); +void csd_keyboard_xkb_shutdown (void); + +typedef void (*PostActivationCallback) (void *userData); + +void +csd_keyboard_xkb_set_post_activation_callback (PostActivationCallback fun, + void *userData); + +#endif diff -uNrp a/plugins/keyboard/xxx/delayed-dialog.c b/plugins/keyboard/xxx/delayed-dialog.c --- a/plugins/keyboard/xxx/delayed-dialog.c 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/xxx/delayed-dialog.c 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,128 @@ +/* + * Copyright © 2006 Novell, Inc. + * + * 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; either version 2, or (at + * your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA + * 02110-1335, USA. + */ + +#include +#include + +#include +#include + +#include "delayed-dialog.h" + +static gboolean delayed_show_timeout (gpointer data); +static GdkFilterReturn message_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data); + +static GSList *dialogs = NULL; + +/** + * csd_delayed_show_dialog: + * @dialog: the dialog + * + * Shows the dialog as with gtk_widget_show(), unless a window manager + * hasn't been started yet, in which case it will wait up to 5 seconds + * for that to happen before showing the dialog. + **/ +void +csd_delayed_show_dialog (GtkWidget *dialog) +{ + GdkDisplay *display = gtk_widget_get_display (dialog); + Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); + GdkScreen *screen = gtk_widget_get_screen (dialog); + char selection_name[10]; + Atom selection_atom; + + /* We can't use gdk_selection_owner_get() for this, because + * it's an unknown out-of-process window. + */ + snprintf (selection_name, sizeof (selection_name), "WM_S%d", + gdk_screen_get_number (screen)); + selection_atom = XInternAtom (xdisplay, selection_name, True); + if (selection_atom && + XGetSelectionOwner (xdisplay, selection_atom) != None) { + gtk_widget_show (dialog); + return; + } + + dialogs = g_slist_prepend (dialogs, dialog); + + gdk_window_add_filter (NULL, message_filter, NULL); + + g_timeout_add (5000, delayed_show_timeout, NULL); +} + +static gboolean +delayed_show_timeout (gpointer data) +{ + GSList *l; + + for (l = dialogs; l; l = l->next) + gtk_widget_show (l->data); + g_slist_free (dialogs); + dialogs = NULL; + + /* FIXME: There's no gdk_display_remove_client_message_filter */ + + return FALSE; +} + +static GdkFilterReturn +message_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data) +{ + XClientMessageEvent *evt; + char *selection_name; + int screen; + GSList *l, *next; + + if (((XEvent *)xevent)->type != ClientMessage) + return GDK_FILTER_CONTINUE; + + evt = (XClientMessageEvent *)xevent; + + if (evt->message_type != XInternAtom (evt->display, "MANAGER", FALSE)) + return GDK_FILTER_CONTINUE; + + selection_name = XGetAtomName (evt->display, evt->data.l[1]); + + if (strncmp (selection_name, "WM_S", 4) != 0) { + XFree (selection_name); + return GDK_FILTER_CONTINUE; + } + + screen = atoi (selection_name + 4); + + for (l = dialogs; l; l = next) { + GtkWidget *dialog = l->data; + next = l->next; + + if (gdk_screen_get_number (gtk_widget_get_screen (dialog)) == screen) { + gtk_widget_show (dialog); + dialogs = g_slist_remove (dialogs, dialog); + } + } + + if (!dialogs) { + gdk_window_remove_filter (NULL, message_filter, NULL); + } + + XFree (selection_name); + + return GDK_FILTER_CONTINUE; +} diff -uNrp a/plugins/keyboard/xxx/delayed-dialog.h b/plugins/keyboard/xxx/delayed-dialog.h --- a/plugins/keyboard/xxx/delayed-dialog.h 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/xxx/delayed-dialog.h 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,32 @@ +/* + * Copyright © 2006 Novell, Inc. + * + * 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; either version 2, or (at + * your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA + * 02110-1335, USA. + */ + + +#ifndef __DELAYED_DIALOG_H +#define __DELAYED_DIALOG_H + +#include + +G_BEGIN_DECLS + +void csd_delayed_show_dialog (GtkWidget *dialog); + +G_END_DECLS + +#endif diff -uNrp a/plugins/keyboard/xxx/gkbd-configuration.c b/plugins/keyboard/xxx/gkbd-configuration.c --- a/plugins/keyboard/xxx/gkbd-configuration.c 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/xxx/gkbd-configuration.c 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2010 Canonical Ltd. + * + * Authors: Jan Arne Petersen + * + * Based on gkbd-status.c by Sergey V. Udaltsov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#include + +#include +#include +#include + +#include +#include + +#include "gkbd-configuration.h" + +struct _GkbdConfigurationPrivate { + XklEngine *engine; + XklConfigRegistry *registry; + + GkbdDesktopConfig cfg; + GkbdIndicatorConfig ind_cfg; + GkbdKeyboardConfig kbd_cfg; + + gchar **full_group_names; + gchar **short_group_names; + + gulong state_changed_handler; + gulong config_changed_handler; +}; + +enum { + SIGNAL_CHANGED, + SIGNAL_GROUP_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +#define GKBD_CONFIGURATION_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GKBD_TYPE_CONFIGURATION, GkbdConfigurationPrivate)) + +G_DEFINE_TYPE (GkbdConfiguration, gkbd_configuration, G_TYPE_OBJECT) + +/* Should be called once for all widgets */ +static void +gkbd_configuration_cfg_changed (GSettings *settings, + const char *key, + GkbdConfiguration * configuration) +{ + GkbdConfigurationPrivate *priv = configuration->priv; + + xkl_debug (100, + "General configuration changed in GSettings - reiniting...\n"); + gkbd_desktop_config_load (&priv->cfg); + gkbd_desktop_config_activate (&priv->cfg); + + g_signal_emit (configuration, + signals[SIGNAL_CHANGED], 0); +} + +/* Should be called once for all widgets */ +static void +gkbd_configuration_ind_cfg_changed (GSettings *settings, + const char *key, + GkbdConfiguration * configuration) +{ + GkbdConfigurationPrivate *priv = configuration->priv; + xkl_debug (100, + "Applet configuration changed in GSettings - reiniting...\n"); + gkbd_indicator_config_load (&priv->ind_cfg); + + gkbd_indicator_config_free_image_filenames (&priv->ind_cfg); + gkbd_indicator_config_load_image_filenames (&priv->ind_cfg, + &priv->kbd_cfg); + + gkbd_indicator_config_activate (&priv->ind_cfg); + + g_signal_emit (configuration, + signals[SIGNAL_CHANGED], 0); +} + +static void +gkbd_configuration_load_group_names (GkbdConfiguration * configuration, + XklConfigRec * xklrec) +{ + GkbdConfigurationPrivate *priv = configuration->priv; + + if (!gkbd_desktop_config_load_group_descriptions (&priv->cfg, + priv->registry, + (const char **) xklrec->layouts, + (const char **) xklrec->variants, + &priv->short_group_names, + &priv->full_group_names)) { + /* We just populate no short names (remain NULL) - + * full names are going to be used anyway */ + gint i, total_groups = + xkl_engine_get_num_groups (priv->engine); + xkl_debug (150, "group descriptions loaded: %d!\n", + total_groups); + priv->full_group_names = + g_new0 (char *, total_groups + 1); + + if (xkl_engine_get_features (priv->engine) & + XKLF_MULTIPLE_LAYOUTS_SUPPORTED) { + for (i = 0; priv->kbd_cfg.layouts_variants[i]; i++) { + priv->full_group_names[i] = + g_strdup ((char *) priv->kbd_cfg.layouts_variants[i]); + } + } else { + for (i = total_groups; --i >= 0;) { + priv->full_group_names[i] = + g_strdup_printf ("Group %d", i); + } + } + } +} + +/* Should be called once for all widgets */ +static void +gkbd_configuration_kbd_cfg_callback (XklEngine *engine, + GkbdConfiguration *configuration) +{ + GkbdConfigurationPrivate *priv = configuration->priv; + XklConfigRec *xklrec = xkl_config_rec_new (); + xkl_debug (100, + "XKB configuration changed on X Server - reiniting...\n"); + + gkbd_keyboard_config_load_from_x_current (&priv->kbd_cfg, + xklrec); + + gkbd_indicator_config_free_image_filenames (&priv->ind_cfg); + gkbd_indicator_config_load_image_filenames (&priv->ind_cfg, + &priv->kbd_cfg); + + g_strfreev (priv->full_group_names); + priv->full_group_names = NULL; + + g_strfreev (priv->short_group_names); + priv->short_group_names = NULL; + + gkbd_configuration_load_group_names (configuration, + xklrec); + + g_signal_emit (configuration, + signals[SIGNAL_CHANGED], + 0); + + g_object_unref (G_OBJECT (xklrec)); +} + +/* Should be called once for all applets */ +static void +gkbd_configuration_state_callback (XklEngine * engine, + XklEngineStateChange changeType, + gint group, gboolean restore, + GkbdConfiguration * configuration) +{ + xkl_debug (150, "group is now %d, restore: %d\n", group, restore); + + if (changeType == GROUP_CHANGED) { + g_signal_emit (configuration, + signals[SIGNAL_GROUP_CHANGED], 0, + group); + } +} + +static void +gkbd_configuration_init (GkbdConfiguration *configuration) +{ + GkbdConfigurationPrivate *priv; + XklConfigRec *xklrec = xkl_config_rec_new (); + + priv = GKBD_CONFIGURATION_GET_PRIVATE (configuration); + configuration->priv = priv; + + priv->engine = xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); + if (priv->engine == NULL) { + xkl_debug (0, "Libxklavier initialization error"); + return; + } + + priv->state_changed_handler = + g_signal_connect (priv->engine, "X-state-changed", + G_CALLBACK (gkbd_configuration_state_callback), + configuration); + priv->config_changed_handler = + g_signal_connect (priv->engine, "X-config-changed", + G_CALLBACK (gkbd_configuration_kbd_cfg_callback), + configuration); + + gkbd_desktop_config_init (&priv->cfg, priv->engine); + gkbd_keyboard_config_init (&priv->kbd_cfg, priv->engine); + gkbd_indicator_config_init (&priv->ind_cfg, priv->engine); + + gkbd_desktop_config_load (&priv->cfg); + gkbd_desktop_config_activate (&priv->cfg); + + priv->registry = xkl_config_registry_get_instance (priv->engine); + xkl_config_registry_load (priv->registry, + priv->cfg.load_extra_items); + + gkbd_keyboard_config_load_from_x_current (&priv->kbd_cfg, + xklrec); + + gkbd_indicator_config_load (&priv->ind_cfg); + + gkbd_indicator_config_load_image_filenames (&priv->ind_cfg, + &priv->kbd_cfg); + + gkbd_indicator_config_activate (&priv->ind_cfg); + + gkbd_configuration_load_group_names (configuration, + xklrec); + g_object_unref (G_OBJECT (xklrec)); + + gkbd_desktop_config_start_listen (&priv->cfg, + G_CALLBACK (gkbd_configuration_cfg_changed), + configuration); + gkbd_indicator_config_start_listen (&priv->ind_cfg, + G_CALLBACK (gkbd_configuration_ind_cfg_changed), + configuration); + xkl_engine_start_listen (priv->engine, + XKLL_TRACK_KEYBOARD_STATE); + + xkl_debug (100, "Initiating the widget startup process for %p\n", + configuration); +} + +static void +gkbd_configuration_finalize (GObject * obj) +{ + GkbdConfiguration *configuration = GKBD_CONFIGURATION (obj); + GkbdConfigurationPrivate *priv = configuration->priv; + + xkl_debug (100, + "Starting the gnome-kbd-configuration widget shutdown process for %p\n", + configuration); + + xkl_engine_stop_listen (priv->engine, + XKLL_TRACK_KEYBOARD_STATE); + + gkbd_desktop_config_stop_listen (&priv->cfg); + gkbd_indicator_config_stop_listen (&priv->ind_cfg); + + gkbd_indicator_config_term (&priv->ind_cfg); + gkbd_keyboard_config_term (&priv->kbd_cfg); + gkbd_desktop_config_term (&priv->cfg); + + if (g_signal_handler_is_connected (priv->engine, + priv->state_changed_handler)) { + g_signal_handler_disconnect (priv->engine, + priv->state_changed_handler); + priv->state_changed_handler = 0; + } + if (g_signal_handler_is_connected (priv->engine, + priv->config_changed_handler)) { + g_signal_handler_disconnect (priv->engine, + priv->config_changed_handler); + priv->config_changed_handler = 0; + } + + g_object_unref (priv->registry); + priv->registry = NULL; + g_object_unref (priv->engine); + priv->engine = NULL; + + G_OBJECT_CLASS (gkbd_configuration_parent_class)->finalize (obj); +} + +static void +gkbd_configuration_class_init (GkbdConfigurationClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + /* Initing vtable */ + object_class->finalize = gkbd_configuration_finalize; + + /* Signals */ + signals[SIGNAL_CHANGED] = g_signal_new ("changed", + GKBD_TYPE_CONFIGURATION, + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals[SIGNAL_GROUP_CHANGED] = g_signal_new ("group-changed", + GKBD_TYPE_CONFIGURATION, + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + g_type_class_add_private (klass, sizeof (GkbdConfigurationPrivate)); +} + +GkbdConfiguration * +gkbd_configuration_get (void) +{ + static gpointer instance = NULL; + + if (!instance) { + instance = g_object_new (GKBD_TYPE_CONFIGURATION, NULL); + g_object_add_weak_pointer (instance, &instance); + } else { + g_object_ref (instance); + } + + return instance; +} + +XklEngine * +gkbd_configuration_get_xkl_engine (GkbdConfiguration *configuration) +{ + return configuration->priv->engine; +} + +const char * const * +gkbd_configuration_get_group_names (GkbdConfiguration *configuration) +{ + return configuration->priv->full_group_names; +} + +const char * const * +gkbd_configuration_get_short_group_names (GkbdConfiguration *configuration) +{ + return configuration->priv->short_group_names; +} diff -uNrp a/plugins/keyboard/xxx/gkbd-configuration.h b/plugins/keyboard/xxx/gkbd-configuration.h --- a/plugins/keyboard/xxx/gkbd-configuration.h 1970-01-01 01:00:00.000000000 +0100 +++ b/plugins/keyboard/xxx/gkbd-configuration.h 2013-08-25 16:36:02.000000000 +0100 @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Canonical Ltd. + * + * Authors: Jan Arne Petersen + * + * Based on gkbd-status.h by Sergey V. Udaltsov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#ifndef __GKBD_CONFIGURATION_H__ +#define __GKBD_CONFIGURATION_H__ + +#include + +#include + +G_BEGIN_DECLS + +typedef struct _GkbdConfiguration GkbdConfiguration; +typedef struct _GkbdConfigurationPrivate GkbdConfigurationPrivate; +typedef struct _GkbdConfigurationClass GkbdConfigurationClass; + +#define GKBD_TYPE_CONFIGURATION (gkbd_configuration_get_type ()) +#define GKBD_CONFIGURATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GKBD_TYPE_CONFIGURATION, GkbdConfiguration)) +#define GKBD_INDCATOR_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GKBD_TYPE_CONFIGURATION, GkbdConfigurationClass)) +#define GKBD_IS_CONFIGURATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GKBD_TYPE_CONFIGURATION)) +#define GKBD_IS_CONFIGURATION_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GKBD_TYPE_CONFIGURATION)) +#define GKBD_CONFIGURATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GKBD_TYPE_CONFIGURATION, GkbdConfigurationClass)) + +struct _GkbdConfiguration { + GObject parent; + + GkbdConfigurationPrivate *priv; +}; + +struct _GkbdConfigurationClass { + GObjectClass parent_class; +}; + +extern GType gkbd_configuration_get_type (void); + +extern GkbdConfiguration *gkbd_configuration_get (void); + +extern XklEngine *gkbd_configuration_get_xkl_engine (GkbdConfiguration *configuration); + +extern const char * const *gkbd_configuration_get_group_names (GkbdConfiguration *configuration); +extern const char * const *gkbd_configuration_get_short_group_names (GkbdConfiguration *configuration); + +G_END_DECLS + +#endif diff -uNrp a/plugins/media-keys/csd-media-keys-manager.c b/plugins/media-keys/csd-media-keys-manager.c --- a/plugins/media-keys/csd-media-keys-manager.c 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/media-keys/csd-media-keys-manager.c 2013-08-25 16:36:02.000000000 +0100 @@ -120,6 +120,10 @@ static const gchar kb_introspection_xml[ #define VOLUME_STEP 6 /* percents for one volume button press */ #define MAX_VOLUME 65536.0 +#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.cinnamon.desktop.input-sources" +#define KEY_CURRENT_INPUT_SOURCE "current" +#define KEY_INPUT_SOURCES "sources" + #define CSD_MEDIA_KEYS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_MEDIA_KEYS_MANAGER, CsdMediaKeysManagerPrivate)) typedef struct { @@ -1750,6 +1754,40 @@ do_keyboard_brightness_action (CsdMediaK manager); } +static void +do_switch_input_source_action (CsdMediaKeysManager *manager, + MediaKeyType type) +{ + GSettings *settings; + GVariant *sources; + gint i, n; + + settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); + + n = g_variant_n_children (sources); + if (n < 2) + goto out; + + i = g_settings_get_uint (settings, KEY_CURRENT_INPUT_SOURCE); + + if (type == SWITCH_INPUT_SOURCE_KEY) + i += 1; + else + i -= 1; + + if (i < 0) + i = n - 1; + else if (i >= n) + i = 0; + + g_settings_set_uint (settings, KEY_CURRENT_INPUT_SOURCE, i); + + out: + g_variant_unref (sources); + g_object_unref (settings); +} + static gboolean do_action (CsdMediaKeysManager *manager, guint deviceid, @@ -1908,6 +1946,10 @@ do_action (CsdMediaKeysManager *manager, case BATTERY_KEY: do_execute_desktop (manager, "gnome-power-statistics.desktop", timestamp); break; + case SWITCH_INPUT_SOURCE_KEY: + case SWITCH_INPUT_SOURCE_BACKWARD_KEY: + do_switch_input_source_action (manager, type); + break; /* Note, no default so compiler catches missing keys */ case CUSTOM_KEY: g_assert_not_reached (); diff -uNrp a/plugins/media-keys/shortcuts-list.h b/plugins/media-keys/shortcuts-list.h --- a/plugins/media-keys/shortcuts-list.h 2013-08-24 18:04:31.000000000 +0100 +++ b/plugins/media-keys/shortcuts-list.h 2013-08-25 16:36:02.000000000 +0100 @@ -81,6 +81,8 @@ typedef enum { KEYBOARD_BRIGHTNESS_DOWN_KEY, KEYBOARD_BRIGHTNESS_TOGGLE_KEY, BATTERY_KEY, + SWITCH_INPUT_SOURCE_KEY, + SWITCH_INPUT_SOURCE_BACKWARD_KEY, CUSTOM_KEY } MediaKeyType; @@ -148,6 +150,9 @@ static struct { { KEYBOARD_BRIGHTNESS_UP_KEY, NULL, "XF86KbdBrightnessUp" }, { KEYBOARD_BRIGHTNESS_DOWN_KEY, NULL, "XF86KbdBrightnessDown" }, { KEYBOARD_BRIGHTNESS_TOGGLE_KEY, NULL, "XF86KbdLightOnOff" }, + { SWITCH_INPUT_SOURCE_KEY, "switch-input-source", NULL }, + { SWITCH_INPUT_SOURCE_BACKWARD_KEY, "switch-input-source-backward", NULL }, + { BATTERY_KEY, NULL, "XF86Battery" }, };