Blob Blame History Raw
diff -uNrp a/cinnamon-settings-daemon/main.c b/cinnamon-settings-daemon/main.c
--- a/cinnamon-settings-daemon/main.c	2013-11-03 15:50:04.000000000 +0000
+++ b/cinnamon-settings-daemon/main.c	2013-11-04 12:45:23.258089163 +0000
@@ -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-11-03 15:50:04.000000000 +0000
+++ b/configure.ac	2013-11-04 12:45:23.259089128 +0000
@@ -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)
 
@@ -193,8 +194,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-11-03 15:50:04.000000000 +0000
+++ b/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in	2013-11-04 12:45:23.259089128 +0000
@@ -175,6 +175,15 @@
       <_summary>Magnifier zoom out</_summary>
       <_description>Binding for the magnifier to zoom out</_description>
     </key>
+    <key name="switch-input-source" type="s">
+      <default>''</default>
+      <_summary>Switch input source</_summary>
+      <_description>Binding to select the next input source</_description>
+    </key>
+    <key name="switch-input-source-backward" type="s">
+      <default>''</default>
+      <_summary>Switch input source backward</_summary>
+      <_description>Binding to select the previous input source</_description>
+    </key>
   </schema>
-
-</schemalist>
+</schemalist>
\ 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-11-03 15:50:04.000000000 +0000
+++ b/plugins/keyboard/csd-keyboard-manager.c	2013-11-04 12:45:23.263088985 +0000
@@ -40,19 +40,22 @@
 
 #include <X11/XKBlib.h>
 #include <X11/keysym.h>
+#include <X11/extensions/XKBrules.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libcinnamon-desktop/gnome-xkb-info.h>
+
+#ifdef HAVE_IBUS
+#include <ibus.h>
+#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-11-03 15:50:04.000000000 +0000
+++ b/plugins/keyboard/csd-keyboard-manager.h	2013-11-04 12:45:23.263088985 +0000
@@ -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-11-03 15:50:04.000000000 +0000
+++ b/plugins/keyboard/csd-keyboard-plugin.h	2013-11-04 12:45:23.263088985 +0000
@@ -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-11-03 15:50:04.000000000 +0000
+++ 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 <svu@users.sourceforge.net>
- *
- * 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 <string.h>
-#include <time.h>
-
-#include <glib/gi18n.h>
-#include <gdk/gdk.h>
-#include <gdk/gdkx.h>
-#include <gtk/gtk.h>
-
-#include <libgnomekbd/gkbd-status.h>
-
-#include <libgnomekbd/gkbd-keyboard-drawing.h>
-#include <libgnomekbd/gkbd-desktop-config.h>
-#include <libgnomekbd/gkbd-indicator-config.h>
-#include <libgnomekbd/gkbd-keyboard-config.h>
-#include <libgnomekbd/gkbd-util.h>
-
-#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"
-						      " • <b>%s</b>\n"
-						      " • <b>%s</b>\n"
-						      " • <b>%s</b>\n"
-						      " • <b>%s</b>"),
-						     "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 (&current_config);
-	/* again, probably it would be nice to compare things
-	   before activating them */
-	gkbd_desktop_config_activate (&current_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
-	    (&current_kbd_config, current_sys_kbd_config)) {
-		if (gkbd_keyboard_config_activate (&current_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 (&current_sys_kbd_config, xkl_engine);
-
-	gkbd_keyboard_config_load (&current_kbd_config,
-				   &initial_sys_kbd_config);
-
-	gkbd_keyboard_config_load_from_x_current (&current_sys_kbd_config,
-						  NULL);
-
-	if (!try_activating_xkb_config_if_new (&current_sys_kbd_config)) {
-		if (filter_xkb_config ()) {
-			if (!try_activating_xkb_config_if_new
-			    (&current_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 (&current_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 (&current_config, xkl_engine);
-		gkbd_keyboard_config_init (&current_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-11-03 15:50:04.000000000 +0000
+++ 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 <svu@users.sourceforge.net>
- *
- * 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 <libxklavier/xklavier.h>
-#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-11-03 15:50:04.000000000 +0000
+++ 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 <stdlib.h>
-#include <string.h>
-
-#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
-
-#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-11-03 15:50:04.000000000 +0000
+++ 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 <gtk/gtk.h>
-
-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-11-03 15:50:04.000000000 +0000
+++ 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 <jpetersen@openismus.com>
- * 
- * Based on gkbd-status.c by Sergey V. Udaltsov <svu@gnome.org>
- *
- * 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 <memory.h>
-
-#include <gdk/gdkkeysyms.h>
-#include <gdk/gdkx.h>
-#include <glib/gi18n.h>
-
-#include <libgnomekbd/gkbd-desktop-config.h>
-#include <libgnomekbd/gkbd-indicator-config.h>
-
-#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-11-03 15:50:04.000000000 +0000
+++ 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 <jpetersen@openismus.com>
- * 
- * Based on gkbd-status.h by Sergey V. Udaltsov <svu@gnome.org>
- *
- * 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 <glib-object.h>
-
-#include <libxklavier/xklavier.h>
-
-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-11-04 12:45:23.267088842 +0000
@@ -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-11-03 15:50:04.000000000 +0000
+++ b/plugins/keyboard/Makefile.am	2013-11-04 12:45:23.267088842 +0000
@@ -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-11-04 12:45:23.267088842 +0000
@@ -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-11-04 12:45:23.268088806 +0000
@@ -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-11-04 12:45:23.269088770 +0000
@@ -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 <svu@users.sourceforge.net>
+ *
+ * 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 <string.h>
+#include <time.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include <libgnomekbd/gkbd-status.h>
+
+#include <libgnomekbd/gkbd-keyboard-drawing.h>
+#include <libgnomekbd/gkbd-desktop-config.h>
+#include <libgnomekbd/gkbd-indicator-config.h>
+#include <libgnomekbd/gkbd-keyboard-config.h>
+#include <libgnomekbd/gkbd-util.h>
+
+#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"
+						      " • <b>%s</b>\n"
+						      " • <b>%s</b>\n"
+						      " • <b>%s</b>\n"
+						      " • <b>%s</b>"),
+						     "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 (&current_config);
+	/* again, probably it would be nice to compare things
+	   before activating them */
+	gkbd_desktop_config_activate (&current_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
+	    (&current_kbd_config, current_sys_kbd_config)) {
+		if (gkbd_keyboard_config_activate (&current_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 (&current_sys_kbd_config, xkl_engine);
+
+	gkbd_keyboard_config_load (&current_kbd_config,
+				   &initial_sys_kbd_config);
+
+	gkbd_keyboard_config_load_from_x_current (&current_sys_kbd_config,
+						  NULL);
+
+	if (!try_activating_xkb_config_if_new (&current_sys_kbd_config)) {
+		if (filter_xkb_config ()) {
+			if (!try_activating_xkb_config_if_new
+			    (&current_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 (&current_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 (&current_config, xkl_engine);
+		gkbd_keyboard_config_init (&current_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-11-04 12:45:23.269088770 +0000
@@ -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 <svu@users.sourceforge.net>
+ *
+ * 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 <libxklavier/xklavier.h>
+#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-11-04 12:45:23.269088770 +0000
@@ -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 <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#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-11-04 12:45:23.270088734 +0000
@@ -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 <gtk/gtk.h>
+
+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-11-04 12:45:23.270088734 +0000
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2010 Canonical Ltd.
+ * 
+ * Authors: Jan Arne Petersen <jpetersen@openismus.com>
+ * 
+ * Based on gkbd-status.c by Sergey V. Udaltsov <svu@gnome.org>
+ *
+ * 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 <memory.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <glib/gi18n.h>
+
+#include <libgnomekbd/gkbd-desktop-config.h>
+#include <libgnomekbd/gkbd-indicator-config.h>
+
+#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-11-04 12:45:23.271088698 +0000
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 Canonical Ltd.
+ * 
+ * Authors: Jan Arne Petersen <jpetersen@openismus.com>
+ * 
+ * Based on gkbd-status.h by Sergey V. Udaltsov <svu@gnome.org>
+ *
+ * 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 <glib-object.h>
+
+#include <libxklavier/xklavier.h>
+
+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-11-03 15:50:04.000000000 +0000
+++ b/plugins/media-keys/csd-media-keys-manager.c	2013-11-04 12:45:23.272088663 +0000
@@ -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 {
@@ -1752,6 +1756,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,
@@ -1910,6 +1948,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-11-03 15:50:04.000000000 +0000
+++ b/plugins/media-keys/shortcuts-list.h	2013-11-04 12:45:23.273088627 +0000
@@ -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" },
 };