diff options
author | root <root@rshg054.dnsready.net> | 2013-10-10 01:59:31 -0700 |
---|---|---|
committer | root <root@rshg054.dnsready.net> | 2013-10-10 01:59:31 -0700 |
commit | 361f95d1ff881daf5f87cb14917bd524511abfc5 (patch) | |
tree | 80a94568027dded4548d525a8a3ed7621ccf325d /community/cinnamon-settings-daemon | |
parent | 737832e1bd70820f477143512b5c89a30a6e81d0 (diff) |
Thu Oct 10 01:58:46 PDT 2013
Diffstat (limited to 'community/cinnamon-settings-daemon')
-rw-r--r-- | community/cinnamon-settings-daemon/PKGBUILD | 28 | ||||
-rw-r--r-- | community/cinnamon-settings-daemon/automount-plugin.patch | 447 | ||||
-rw-r--r-- | community/cinnamon-settings-daemon/keyboard.patch | 4728 |
3 files changed, 5195 insertions, 8 deletions
diff --git a/community/cinnamon-settings-daemon/PKGBUILD b/community/cinnamon-settings-daemon/PKGBUILD index 127c715ba..0d3417363 100644 --- a/community/cinnamon-settings-daemon/PKGBUILD +++ b/community/cinnamon-settings-daemon/PKGBUILD @@ -4,25 +4,37 @@ pkgname=cinnamon-settings-daemon pkgver=2.0.1 -pkgrel=1 +pkgrel=2 pkgdesc="The Cinnamon Settings daemon" arch=('i686' 'x86_64') license=('GPL') -depends=('dconf' 'cinnamon-desktop' 'gsettings-desktop-schemas' 'hicolor-icon-theme' 'libcanberra-pulse' 'libnotify' - 'libsystemd' 'libwacom' 'pulseaudio' 'pulseaudio-alsa' 'upower' 'ibus' 'librsvg' 'libgweather' 'libxklavier' - 'libgnomekbd') -makedepends=('intltool' 'xf86-input-wacom' 'libxslt' 'docbook-xsl' 'gnome-common') +depends=('cinnamon-desktop' 'ibus' 'libcanberra-pulse' 'librsvg' 'nss' 'pulseaudio-alsa' 'upower') +makedepends=('intltool' 'docbook-xsl' 'gnome-common') options=('!emptydirs' '!libtool') install=cinnamon-settings-daemon.install url="https://github.com/linuxmint/cinnamon-settings-daemon" -source=("${pkgname}-${pkgver}.tar.gz::https://github.com/linuxmint/cinnamon-settings-daemon/archive/${pkgver}.tar.gz") -sha256sums=('f03f5ea46bec11a6a0ed02b232dab8cb99a84fd8a9b52a76a454a7ba74dc1168') +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/linuxmint/cinnamon-settings-daemon/archive/${pkgver}.tar.gz" + keyboard.patch + automount-plugin.patch) +sha256sums=('f03f5ea46bec11a6a0ed02b232dab8cb99a84fd8a9b52a76a454a7ba74dc1168' + '3c05b978cb414c8fc1530f211bab58627a64ac9c156630c9d486f5926a85109c' + '0a6dfbea6f28e5107212a8e8091ded6a3b5b34b1710c11c2f04c4d02846f9398') + +prepare() { + cd $pkgname-$pkgver + + # Fix crasher + patch -Np1 -i ../keyboard.patch + + # Turn automount helper into a plugin + patch -Np1 -i ../automount-plugin.patch +} build() { cd $pkgname-$pkgver ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var \ - --libexecdir=/usr/lib/$pkgname --disable-static + --libexecdir=/usr/lib/$pkgname --disable-static --enable-systemd #https://bugzilla.gnome.org/show_bug.cgi?id=656231 sed -i -e 's/ -shared / -Wl,-O1,--as-needed\0/g' libtool diff --git a/community/cinnamon-settings-daemon/automount-plugin.patch b/community/cinnamon-settings-daemon/automount-plugin.patch new file mode 100644 index 000000000..f6ca1181f --- /dev/null +++ b/community/cinnamon-settings-daemon/automount-plugin.patch @@ -0,0 +1,447 @@ +diff -Naur cinnamon-settings-daemon-2.0.1.orig/data/org.cinnamon.settings-daemon.plugins.gschema.xml.in.in cinnamon-settings-daemon-2.0.1/data/org.cinnamon.settings-daemon.plugins.gschema.xml.in.in +--- cinnamon-settings-daemon-2.0.1.orig/data/org.cinnamon.settings-daemon.plugins.gschema.xml.in.in 2013-10-02 16:13:56.000000000 +0200 ++++ cinnamon-settings-daemon-2.0.1/data/org.cinnamon.settings-daemon.plugins.gschema.xml.in.in 2013-10-08 22:49:15.438929150 +0200 +@@ -2,6 +2,7 @@ + <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.cinnamon.settings-daemon.plugins" path="/org/cinnamon/settings-daemon/plugins/"> + <child name="a11y-keyboard" schema="org.cinnamon.settings-daemon.plugins.a11y-keyboard"/> + <child name="a11y-settings" schema="org.cinnamon.settings-daemon.plugins.a11y-settings"/> ++ <child name="automount" schema="org.cinnamon.settings-daemon.plugins.automount"/> + <child name="background" schema="org.cinnamon.settings-daemon.plugins.background"/> + <child name="clipboard" schema="org.cinnamon.settings-daemon.plugins.clipboard"/> + <child name="color" schema="org.cinnamon.settings-daemon.plugins.color"/> +@@ -43,6 +44,18 @@ + <_summary>Priority to use for this plugin</_summary> + <_description>Priority to use for this plugin in cinnamon-settings-daemon startup queue</_description> + </key> ++ </schema> ++ <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.cinnamon.settings-daemon.plugins.automount" path="/org/cinnamon/settings-daemon/plugins/automount/"> ++ <key name="active" type="b"> ++ <default>true</default> ++ <_summary>Activation of this plugin</_summary> ++ <_description>Whether this plugin would be activated by cinnamon-settings-daemon or not</_description> ++ </key> ++ <key name="priority" type="i"> ++ <default>97</default> ++ <_summary>Priority to use for this plugin</_summary> ++ <_description>Priority to use for this plugin in cinnamon-settings-daemon startup queue</_description> ++ </key> + </schema> + <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.cinnamon.settings-daemon.plugins.clipboard" path="/org/cinnamon/settings-daemon/plugins/clipboard/"> + <key name="active" type="b"> +diff -Naur cinnamon-settings-daemon-2.0.1.orig/plugins/automount/automount.cinnamon-settings-plugin.in cinnamon-settings-daemon-2.0.1/plugins/automount/automount.cinnamon-settings-plugin.in +--- cinnamon-settings-daemon-2.0.1.orig/plugins/automount/automount.cinnamon-settings-plugin.in 1970-01-01 01:00:00.000000000 +0100 ++++ cinnamon-settings-daemon-2.0.1/plugins/automount/automount.cinnamon-settings-plugin.in 2013-10-08 22:35:10.771472456 +0200 +@@ -0,0 +1,8 @@ ++[Cinnamon Settings Plugin] ++Module=automount ++IAge=0 ++_Name=Automount ++_Description=Automounter plugin ++Authors=Tomas Bzatek ++Copyright=Copyright © 2010 Red Hat, Inc. ++Website= +diff -Naur cinnamon-settings-daemon-2.0.1.orig/plugins/automount/cinnamon-fallback-mount-helper.c cinnamon-settings-daemon-2.0.1/plugins/automount/cinnamon-fallback-mount-helper.c +--- cinnamon-settings-daemon-2.0.1.orig/plugins/automount/cinnamon-fallback-mount-helper.c 2013-10-02 16:13:56.000000000 +0200 ++++ cinnamon-settings-daemon-2.0.1/plugins/automount/cinnamon-fallback-mount-helper.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,65 +0,0 @@ +-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- +- * +- * Copyright (C) 2010 Red Hat, 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 of the License, 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 +- * +- * Author: Tomas Bzatek <tbzatek@redhat.com> +- */ +- +-#include "config.h" +- +-#include <glib.h> +-#include <glib/gi18n.h> +-#include <unistd.h> +-#include <gtk/gtk.h> +- +-#include "csd-automount-manager.h" +- +-int +-main (int argc, +- char **argv) +-{ +- GMainLoop *loop; +- CsdAutomountManager *manager; +- GError *error = NULL; +- +- g_type_init (); +- gtk_init (&argc, &argv); +- +- bindtextdomain (GETTEXT_PACKAGE, CINNAMON_SETTINGS_LOCALEDIR); +- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +- textdomain (GETTEXT_PACKAGE); +- +- loop = g_main_loop_new (NULL, FALSE); +- manager = csd_automount_manager_new (); +- +- csd_automount_manager_start (manager, &error); +- +- if (error != NULL) { +- g_printerr ("Unable to start the mount manager: %s", +- error->message); +- +- g_error_free (error); +- _exit (1); +- } +- +- g_main_loop_run (loop); +- +- csd_automount_manager_stop (manager); +- g_main_loop_unref (loop); +- +- return 0; +-} +diff -Naur cinnamon-settings-daemon-2.0.1.orig/plugins/automount/cinnamon-fallback-mount-helper.desktop.in.in cinnamon-settings-daemon-2.0.1/plugins/automount/cinnamon-fallback-mount-helper.desktop.in.in +--- cinnamon-settings-daemon-2.0.1.orig/plugins/automount/cinnamon-fallback-mount-helper.desktop.in.in 2013-10-02 16:13:56.000000000 +0200 ++++ cinnamon-settings-daemon-2.0.1/plugins/automount/cinnamon-fallback-mount-helper.desktop.in.in 1970-01-01 01:00:00.000000000 +0100 +@@ -1,12 +0,0 @@ +-[Desktop Entry] +-_Name=Mount Helper +-_Comment=Automount and autorun plugged devices +-Exec=@LIBEXECDIR@/cinnamon-fallback-mount-helper +-Icon=drive-optical +-Terminal=false +-Type=Application +-Categories= +-NoDisplay=true +-OnlyShowIn=GNOME; +-X-GNOME-Autostart-Notify=true +- +diff -Naur cinnamon-settings-daemon-2.0.1.orig/plugins/automount/csd-automount-plugin.c cinnamon-settings-daemon-2.0.1/plugins/automount/csd-automount-plugin.c +--- cinnamon-settings-daemon-2.0.1.orig/plugins/automount/csd-automount-plugin.c 1970-01-01 01:00:00.000000000 +0100 ++++ cinnamon-settings-daemon-2.0.1/plugins/automount/csd-automount-plugin.c 2013-10-08 22:35:10.771472456 +0200 +@@ -0,0 +1,106 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2010 Red Hat, 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Tomas Bzatek <tbzatek@redhat.com> ++ */ ++ ++#include "config.h" ++ ++#include <glib/gi18n-lib.h> ++#include <gmodule.h> ++ ++#include "cinnamon-settings-plugin.h" ++#include "csd-automount-plugin.h" ++#include "csd-automount-manager.h" ++ ++struct CsdAutomountPluginPrivate { ++ CsdAutomountManager *manager; ++}; ++ ++#define CSD_AUTOMOUNT_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), CSD_TYPE_AUTOMOUNT_PLUGIN, CsdAutomountPluginPrivate)) ++ ++CINNAMON_SETTINGS_PLUGIN_REGISTER (CsdAutomountPlugin, csd_automount_plugin) ++ ++static void ++csd_automount_plugin_init (CsdAutomountPlugin *plugin) ++{ ++ plugin->priv = CSD_AUTOMOUNT_PLUGIN_GET_PRIVATE (plugin); ++ ++ g_debug ("Automount plugin initializing"); ++ ++ plugin->priv->manager = csd_automount_manager_new (); ++} ++ ++static void ++csd_automount_plugin_finalize (GObject *object) ++{ ++ CsdAutomountPlugin *plugin; ++ ++ g_return_if_fail (object != NULL); ++ g_return_if_fail (CSD_IS_AUTOMOUNT_PLUGIN (object)); ++ ++ g_debug ("Automount plugin finalizing"); ++ ++ plugin = CSD_AUTOMOUNT_PLUGIN (object); ++ ++ g_return_if_fail (plugin->priv != NULL); ++ ++ if (plugin->priv->manager != NULL) { ++ g_object_unref (plugin->priv->manager); ++ } ++ ++ G_OBJECT_CLASS (csd_automount_plugin_parent_class)->finalize (object); ++} ++ ++static void ++impl_activate (CinnamonSettingsPlugin *plugin) ++{ ++ gboolean res; ++ GError *error; ++ ++ g_debug ("Activating automount plugin"); ++ ++ error = NULL; ++ res = csd_automount_manager_start (CSD_AUTOMOUNT_PLUGIN (plugin)->priv->manager, &error); ++ if (! res) { ++ g_warning ("Unable to start automount manager: %s", error->message); ++ g_error_free (error); ++ } ++} ++ ++static void ++impl_deactivate (CinnamonSettingsPlugin *plugin) ++{ ++ g_debug ("Deactivating automount plugin"); ++ csd_automount_manager_stop (CSD_AUTOMOUNT_PLUGIN (plugin)->priv->manager); ++} ++ ++static void ++csd_automount_plugin_class_init (CsdAutomountPluginClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ CinnamonSettingsPluginClass *plugin_class = CINNAMON_SETTINGS_PLUGIN_CLASS (klass); ++ ++ object_class->finalize = csd_automount_plugin_finalize; ++ ++ plugin_class->activate = impl_activate; ++ plugin_class->deactivate = impl_deactivate; ++ ++ g_type_class_add_private (klass, sizeof (CsdAutomountPluginPrivate)); ++} ++ +diff -Naur cinnamon-settings-daemon-2.0.1.orig/plugins/automount/csd-automount-plugin.h cinnamon-settings-daemon-2.0.1/plugins/automount/csd-automount-plugin.h +--- cinnamon-settings-daemon-2.0.1.orig/plugins/automount/csd-automount-plugin.h 1970-01-01 01:00:00.000000000 +0100 ++++ cinnamon-settings-daemon-2.0.1/plugins/automount/csd-automount-plugin.h 2013-10-08 22:35:10.771472456 +0200 +@@ -0,0 +1,60 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2010 Red Hat, 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Tomas Bzatek <tbzatek@redhat.com> ++ */ ++ ++#ifndef __CSD_AUTOMOUNT_PLUGIN_H__ ++#define __CSD_AUTOMOUNT_PLUGIN_H__ ++ ++#include <glib.h> ++#include <glib-object.h> ++#include <gmodule.h> ++ ++#include "cinnamon-settings-plugin.h" ++ ++G_BEGIN_DECLS ++ ++#define CSD_TYPE_AUTOMOUNT_PLUGIN (csd_automount_plugin_get_type ()) ++#define CSD_AUTOMOUNT_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CSD_TYPE_AUTOMOUNT_PLUGIN, CsdAutomountPlugin)) ++#define CSD_AUTOMOUNT_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CSD_TYPE_AUTOMOUNT_PLUGIN, CsdAutomountPluginClass)) ++#define CSD_IS_AUTOMOUNT_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CSD_TYPE_AUTOMOUNT_PLUGIN)) ++#define CSD_IS_AUTOMOUNT_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CSD_TYPE_AUTOMOUNT_PLUGIN)) ++#define CSD_AUTOMOUNT_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CSD_TYPE_AUTOMOUNT_PLUGIN, CsdAutomountPluginClass)) ++ ++typedef struct CsdAutomountPluginPrivate CsdAutomountPluginPrivate; ++ ++typedef struct ++{ ++ CinnamonSettingsPlugin parent; ++ CsdAutomountPluginPrivate *priv; ++} CsdAutomountPlugin; ++ ++typedef struct ++{ ++ CinnamonSettingsPluginClass parent_class; ++} CsdAutomountPluginClass; ++ ++GType csd_automount_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_END_DECLS ++ ++#endif /* __CSD_AUTOMOUNT_PLUGIN_H__ */ +diff -Naur cinnamon-settings-daemon-2.0.1.orig/plugins/automount/Makefile.am cinnamon-settings-daemon-2.0.1/plugins/automount/Makefile.am +--- cinnamon-settings-daemon-2.0.1.orig/plugins/automount/Makefile.am 2013-10-02 16:13:56.000000000 +0200 ++++ cinnamon-settings-daemon-2.0.1/plugins/automount/Makefile.am 2013-10-08 22:48:19.240865461 +0200 +@@ -1,38 +1,87 @@ +-libexec_PROGRAMS = cinnamon-fallback-mount-helper ++NULL = + +-cinnamon_fallback_mount_helper_SOURCES = \ +- cinnamon-fallback-mount-helper.c \ +- csd-automount-manager.c \ +- csd-automount-manager.h \ +- csd-autorun.c \ +- csd-autorun.h ++plugin_name = automount + +-cinnamon_fallback_mount_helper_CPPFLAGS = \ ++libexec_PROGRAMS = csd-test-automount ++ ++csd_test_automount_SOURCES = \ ++ test-automount.c \ ++ csd-automount-manager.h \ ++ csd-automount-manager.c \ ++ csd-autorun.c \ ++ csd-autorun.h \ ++ $(NULL) ++ ++csd_test_automount_CPPFLAGS = \ + -I$(top_srcdir)/cinnamon-settings-daemon \ ++ -I$(top_srcdir)/plugins/common \ + -DCINNAMON_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ + $(AM_CPPFLAGS) + +-cinnamon_fallback_mount_helper_CFLAGS = \ ++csd_test_automount_CFLAGS = \ ++ $(PLUGIN_CFLAGS) \ + $(SETTINGS_PLUGIN_CFLAGS) \ + $(SYSTEMD_CFLAGS) \ + $(AUTOMOUNT_CFLAGS) ++ $(AM_CFLAGS) ++ ++csd_test_automount_LDADD = \ ++ $(top_builddir)/cinnamon-settings-daemon/libcsd.la \ ++ $(SETTINGS_PLUGIN_LIBS) \ ++ $(SYSTEMD_LIBS) \ ++ $(AUTOMOUNT_LIBS) \ ++ $(NULL) ++ ++plugin_LTLIBRARIES = \ ++ libautomount.la \ ++ $(NULL) ++ ++libautomount_la_SOURCES = \ ++ csd-automount-plugin.h \ ++ csd-automount-plugin.c \ ++ csd-automount-manager.h \ ++ csd-automount-manager.c \ ++ csd-autorun.c \ ++ csd-autorun.h \ ++ $(NULL) ++ ++libautomount_la_CPPFLAGS = \ ++ -I$(top_srcdir)/cinnamon-settings-daemon \ ++ -DCINNAMON_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ ++ $(AM_CPPFLAGS) ++ ++libautomount_la_CFLAGS = \ ++ $(SETTINGS_PLUGIN_CFLAGS) \ ++ $(SYSTEMD_CFLAGS) \ ++ $(AUTOMOUNT_CFLAGS) \ ++ $(AM_CFLAGS) ++ ++libautomount_la_LDFLAGS = \ ++ $(CSD_PLUGIN_LDFLAGS) \ ++ $(NULL) + +-cinnamon_fallback_mount_helper_LDADD = \ ++libautomount_la_LIBADD = \ + $(SETTINGS_PLUGIN_LIBS) \ + $(SYSTEMD_LIBS) \ + $(AUTOMOUNT_LIBS) \ +- $(top_builddir)/cinnamon-settings-daemon/libcsd.la ++ $(NULL) + +-autostartdir = $(sysconfdir)/xdg/autostart +-autostart_in_files = cinnamon-fallback-mount-helper.desktop.in +-autostart_in_in_files = cinnamon-fallback-mount-helper.desktop.in.in +-autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) ++plugin_in_files = \ ++ automount.cinnamon-settings-plugin.in \ ++ $(NULL) + +-$(autostart_in_files): $(autostart_in_in_files) +- @sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@ ++plugin_DATA = $(plugin_in_files:.cinnamon-settings-plugin.in=.cinnamon-settings-plugin) + +-@INTLTOOL_DESKTOP_RULE@ ++EXTRA_DIST = \ ++ $(plugin_in_files) \ ++ $(NULL) + +-EXTRA_DIST = $(autostart_in_in_files) ++CLEANFILES = \ ++ $(plugin_DATA) \ ++ $(NULL) + +-CLEANFILES = $(autostart_DATA) $(autostart_in_files) ++DISTCLEANFILES = \ ++ $(plugin_DATA) \ ++ $(NULL) ++ ++@CSD_INTLTOOL_PLUGIN_RULE@ +diff -Naur cinnamon-settings-daemon-2.0.1.orig/plugins/automount/test-automount.c cinnamon-settings-daemon-2.0.1/plugins/automount/test-automount.c +--- cinnamon-settings-daemon-2.0.1.orig/plugins/automount/test-automount.c 1970-01-01 01:00:00.000000000 +0100 ++++ cinnamon-settings-daemon-2.0.1/plugins/automount/test-automount.c 2013-10-08 22:42:53.759486525 +0200 +@@ -0,0 +1,7 @@ ++#define NEW csd_automount_manager_new ++#define START csd_automount_manager_start ++#define STOP csd_automount_manager_stop ++#define MANAGER CsdAutomountManager ++#include "csd-automount-manager.h" ++ ++#include "test-plugin.h" +diff -Naur cinnamon-settings-daemon-2.0.1.orig/po/POTFILES.in cinnamon-settings-daemon-2.0.1/po/POTFILES.in +--- cinnamon-settings-daemon-2.0.1.orig/po/POTFILES.in 2013-10-02 16:13:56.000000000 +0200 ++++ cinnamon-settings-daemon-2.0.1/po/POTFILES.in 2013-10-08 22:35:10.771472456 +0200 +@@ -18,8 +18,9 @@ + plugins/a11y-keyboard/csd-a11y-preferences-dialog.c + [type: gettext/glade]plugins/a11y-keyboard/csd-a11y-preferences-dialog.ui + [type: gettext/ini]plugins/a11y-settings/a11y-settings.cinnamon-settings-plugin.in +-plugins/automount/cinnamon-fallback-mount-helper.desktop.in.in ++[type: gettext/ini]plugins/automount/automount.cinnamon-settings-plugin.in + plugins/automount/csd-automount-manager.c ++plugins/automount/csd-automount-plugin.c + plugins/automount/csd-autorun.c + [type: gettext/ini]plugins/background/background.cinnamon-settings-plugin.in + [type: gettext/ini]plugins/clipboard/clipboard.cinnamon-settings-plugin.in +diff -Naur cinnamon-settings-daemon-2.0.1.orig/po/POTFILES.skip cinnamon-settings-daemon-2.0.1/po/POTFILES.skip +--- cinnamon-settings-daemon-2.0.1.orig/po/POTFILES.skip 2013-10-02 16:13:56.000000000 +0200 ++++ cinnamon-settings-daemon-2.0.1/po/POTFILES.skip 2013-10-08 22:37:20.224645009 +0200 +@@ -20,6 +20,5 @@ + data/org.cinnamon.settings-daemon.plugins.updates.gschema.xml.in + data/org.cinnamon.settings-daemon.plugins.xrandr.gschema.xml.in + data/org.cinnamon.settings-daemon.plugins.xsettings.gschema.xml.in +-plugins/automount/gnome-fallback-mount-helper.desktop.in + plugins/power/org.cinnamon.settings-daemon.plugins.power.policy.in + plugins/wacom/org.cinnamon.settings-daemon.plugins.wacom.policy.in diff --git a/community/cinnamon-settings-daemon/keyboard.patch b/community/cinnamon-settings-daemon/keyboard.patch new file mode 100644 index 000000000..927910d8a --- /dev/null +++ b/community/cinnamon-settings-daemon/keyboard.patch @@ -0,0 +1,4728 @@ +diff -uNrp a/cinnamon-settings-daemon/main.c b/cinnamon-settings-daemon/main.c +--- a/cinnamon-settings-daemon/main.c 2013-08-24 18:04:31.000000000 +0100 ++++ b/cinnamon-settings-daemon/main.c 2013-08-25 16:36:02.000000000 +0100 +@@ -319,6 +319,29 @@ set_legacy_ibus_env_vars (GDBusProxy *pr + } + #endif + ++static void ++got_session_proxy (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ GDBusProxy *proxy; ++ GError *error = NULL; ++ ++ proxy = g_dbus_proxy_new_finish (res, &error); ++ if (proxy == NULL) { ++ g_debug ("Could not connect to the Session manager: %s", error->message); ++ g_error_free (error); ++ } else { ++ set_locale (proxy); ++#ifdef HAVE_IBUS ++ /* This will register with cinnamon-session after calling Setenv. */ ++ set_legacy_ibus_env_vars (proxy); ++#else ++ register_with_gnome_session (proxy); ++#endif ++ } ++} ++ + static gboolean + on_term_signal_pipe_closed (GIOChannel *source, + GIOCondition condition, +@@ -368,6 +391,16 @@ set_session_over_handler (GDBusConnectio + { + g_assert (bus != NULL); + ++ g_dbus_proxy_new (bus, ++ G_DBUS_PROXY_FLAGS_NONE, ++ NULL, ++ GNOME_SESSION_DBUS_NAME, ++ GNOME_SESSION_DBUS_OBJECT, ++ GNOME_SESSION_DBUS_INTERFACE, ++ NULL, ++ (GAsyncReadyCallback) got_session_proxy, ++ NULL); ++ + watch_for_term_signal (manager); + } + +@@ -390,56 +423,6 @@ name_lost_handler (GDBusConnection *conn + gtk_main_quit (); + } + +-static gboolean +-do_register_client (gpointer user_data) +-{ +- GDBusProxy *proxy = (GDBusProxy *) user_data; +- g_assert (proxy != NULL); +- +- const char *startup_id = g_getenv ("DESKTOP_AUTOSTART_ID"); +- g_dbus_proxy_call (proxy, +- "RegisterClient", +- g_variant_new ("(ss)", "cinnamon-settings-daemon", startup_id ? startup_id : ""), +- G_DBUS_CALL_FLAGS_NONE, +- -1, +- NULL, +- (GAsyncReadyCallback) on_client_registered, +- manager); +- +- return FALSE; +-} +- +-static void +-queue_register_client (void) +-{ +- GDBusConnection *bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); +- if (!bus) +- return; +- +- GError *error = NULL; +- GDBusProxy *proxy = g_dbus_proxy_new_sync (bus, +- G_DBUS_PROXY_FLAGS_NONE, +- NULL, +- GNOME_SESSION_DBUS_NAME, +- GNOME_SESSION_DBUS_OBJECT, +- GNOME_SESSION_DBUS_INTERFACE, +- NULL, +- &error); +- g_object_unref (bus); +- +- if (proxy == NULL) { +- g_debug ("Could not connect to the Session manager: %s", error->message); +- g_error_free (error); +- return; +- } +- +- /* Register the daemon with cinnamon-session */ +- g_signal_connect (G_OBJECT (proxy), "g-signal", +- G_CALLBACK (on_session_over), NULL); +- +- g_idle_add_full (G_PRIORITY_DEFAULT, do_register_client, proxy, NULL); +-} +- + static void + bus_register (void) + { +@@ -541,8 +524,6 @@ main (int argc, char *argv[]) + + notify_init ("cinnamon-settings-daemon"); + +- queue_register_client (); +- + bus_register (); + + cinnamon_settings_profile_start ("cinnamon_settings_manager_new"); +diff -uNrp a/configure.ac b/configure.ac +--- a/configure.ac 2013-08-24 18:04:31.000000000 +0100 ++++ b/configure.ac 2013-08-25 16:36:02.000000000 +0100 +@@ -53,6 +53,7 @@ UPOWER_GLIB_REQUIRED_VERSION=0.9.1 + PA_REQUIRED_VERSION=0.9.16 + UPOWER_REQUIRED_VERSION=0.9.11 + GTK_XINPUT_2_3_VERSION=3.7.8 ++IBUS_REQUIRED_VERSION=1.4.2 + + #EXTRA_COMPILE_WARNINGS(yes) + +@@ -199,8 +200,21 @@ dnl ------------------------------------ + dnl - Keyboard plugin stuff + dnl --------------------------------------------------------------------------- + +-LIBGNOMEKBD_REQUIRED=2.91.1 +-PKG_CHECK_MODULES(KEYBOARD, [libgnomekbdui >= $LIBGNOMEKBD_REQUIRED libgnomekbd >= $LIBGNOMEKBD_REQUIRED libxklavier >= 5.0 kbproto]) ++AC_ARG_ENABLE(ibus, ++ AS_HELP_STRING([--disable-ibus], ++ [Disable IBus support]), ++ enable_ibus=$enableval, ++ enable_ibus=yes) ++ ++if test "x$enable_ibus" = "xyes" ; then ++ IBUS_MODULE="ibus-1.0 >= $IBUS_REQUIRED_VERSION" ++ AC_DEFINE(HAVE_IBUS, 1, [Defined if IBus support is enabled]) ++else ++ IBUS_MODULE= ++fi ++AM_CONDITIONAL(HAVE_IBUS, test "x$enable_ibus" == "xyes") ++ ++PKG_CHECK_MODULES(KEYBOARD, xkbfile $IBUS_MODULE cinnamon-desktop >= $CINNAMON_DESKTOP_REQUIRED_VERSION) + + dnl --------------------------------------------------------------------------- + dnl - Housekeeping plugin stuff +diff -uNrp a/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in b/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in +--- a/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in 2013-08-24 18:04:31.000000000 +0100 ++++ b/data/org.cinnamon.settings-daemon.plugins.media-keys.gschema.xml.in.in 2013-08-25 16:36:02.000000000 +0100 +@@ -175,6 +175,15 @@ + <_summary>Magnifier zoom out</_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-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/csd-keyboard-manager.c 2013-08-25 16:36:02.000000000 +0100 +@@ -40,19 +40,22 @@ + + #include <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-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/csd-keyboard-manager.h 2013-08-25 16:36:02.000000000 +0100 +@@ -51,7 +51,6 @@ CsdKeyboardManager * csd_keyboard_ + gboolean csd_keyboard_manager_start (CsdKeyboardManager *manager, + GError **error); + void csd_keyboard_manager_stop (CsdKeyboardManager *manager); +-void csd_keyboard_manager_apply_settings (CsdKeyboardManager *manager); + + G_END_DECLS + +diff -uNrp a/plugins/keyboard/csd-keyboard-plugin.h b/plugins/keyboard/csd-keyboard-plugin.h +--- a/plugins/keyboard/csd-keyboard-plugin.h 2013-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/csd-keyboard-plugin.h 2013-08-25 16:36:02.000000000 +0100 +@@ -52,7 +52,7 @@ typedef struct + GType csd_keyboard_plugin_get_type (void) G_GNUC_CONST; + + /* All the plugins must implement this function */ +-G_MODULE_EXPORT GType register_cinnamon_settings_plugin (GTypeModule *module); ++G_MODULE_EXPORT GType register_gnome_settings_plugin (GTypeModule *module); + + G_END_DECLS + +diff -uNrp a/plugins/keyboard/csd-keyboard-xkb.c b/plugins/keyboard/csd-keyboard-xkb.c +--- a/plugins/keyboard/csd-keyboard-xkb.c 2013-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/csd-keyboard-xkb.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,579 +0,0 @@ +-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- +- * +- * Copyright (C) 2001 Udaltsoft +- * +- * Written by Sergey V. Oudaltsov <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 (¤t_config); +- /* again, probably it would be nice to compare things +- before activating them */ +- gkbd_desktop_config_activate (¤t_config); +-} +- +-static void +-popup_menu_launch_capplet () +-{ +- GAppInfo *info; +- GdkAppLaunchContext *ctx; +- GError *error = NULL; +- +- info = +- g_app_info_create_from_commandline +- ("cinnamon-settings region", NULL, 0, &error); +- +- if (info != NULL) { +- ctx = +- gdk_display_get_app_launch_context +- (gdk_display_get_default ()); +- +- if (g_app_info_launch (info, NULL, +- G_APP_LAUNCH_CONTEXT (ctx), &error) == FALSE) { +- g_warning +- ("Could not execute keyboard properties capplet: [%s]\n", +- error->message); +- g_error_free (error); +- } +- +- g_object_unref (info); +- g_object_unref (ctx); +- } +- +-} +- +-static void +-show_layout_destroy (GtkWidget * dialog, gint group) +-{ +- g_hash_table_remove (preview_dialogs, GINT_TO_POINTER (group)); +-} +- +-static void +-popup_menu_show_layout () +-{ +- GtkWidget *dialog; +- XklEngine *engine = +- xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY +- (gdk_display_get_default ())); +- XklState *xkl_state = xkl_engine_get_current_state (engine); +- +- gchar **group_names = gkbd_status_get_group_names (); +- +- gpointer p = g_hash_table_lookup (preview_dialogs, +- GINT_TO_POINTER +- (xkl_state->group)); +- +- if (xkl_state->group < 0 +- || xkl_state->group >= g_strv_length (group_names)) { +- return; +- } +- +- if (p != NULL) { +- /* existing window */ +- gtk_window_present (GTK_WINDOW (p)); +- return; +- } +- +- if (!ensure_xkl_registry ()) +- return; +- +- dialog = gkbd_keyboard_drawing_dialog_new (); +- gkbd_keyboard_drawing_dialog_set_group (dialog, xkl_registry, xkl_state->group); +- +- g_signal_connect (dialog, "destroy", +- G_CALLBACK (show_layout_destroy), +- GINT_TO_POINTER (xkl_state->group)); +- g_hash_table_insert (preview_dialogs, +- GINT_TO_POINTER (xkl_state->group), dialog); +- gtk_widget_show_all (dialog); +-} +- +-static void +-popup_menu_set_group (gint group_number, gboolean only_menu) +-{ +- +- XklEngine *engine = gkbd_status_get_xkl_engine (); +- +- XklState *st = xkl_engine_get_current_state(engine); +- Window cur; +- st->group = group_number; +- xkl_engine_allow_one_switch_to_secondary_group (engine); +- cur = xkl_engine_get_current_window (engine); +- if (cur != (Window) NULL) { +- xkl_debug (150, "Enforcing the state %d for window %lx\n", +- st->group, cur); +- +- xkl_engine_save_state (engine, +- xkl_engine_get_current_window +- (engine), st); +-/* XSetInputFocus( GDK_DISPLAY(), cur, RevertToNone, CurrentTime );*/ +- } else { +- xkl_debug (150, +- "??? Enforcing the state %d for unknown window\n", +- st->group); +- /* strange situation - bad things can happen */ +- } +- if (!only_menu) +- xkl_engine_lock_group (engine, st->group); +-} +- +-static void +-popup_menu_set_group_cb (GtkMenuItem * item, gpointer param) +-{ +- gint group_number = GPOINTER_TO_INT (param); +- +- popup_menu_set_group(group_number, FALSE); +-} +- +- +-static GtkMenu * +-create_status_menu (void) +-{ +- GtkMenu *popup_menu = GTK_MENU (gtk_menu_new ()); +- int i = 0; +- +- GtkMenu *groups_menu = GTK_MENU (gtk_menu_new ()); +- gchar **current_name = gkbd_status_get_group_names (); +- +- GtkWidget *item = gtk_menu_item_new_with_mnemonic (_("_Layouts")); +- gtk_widget_show (item); +- gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); +- gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), +- GTK_WIDGET (groups_menu)); +- +- item = gtk_menu_item_new_with_mnemonic (_("Show _Keyboard Layout...")); +- gtk_widget_show (item); +- g_signal_connect (item, "activate", popup_menu_show_layout, NULL); +- gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); +- +- /* translators note: +- * This is the name of the cinnamon-settings "region" panel */ +- item = gtk_menu_item_new_with_mnemonic (_("Region and Language Settings")); +- gtk_widget_show (item); +- g_signal_connect (item, "activate", popup_menu_launch_capplet, NULL); +- gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); +- +- for (i = 0; current_name && *current_name; i++, current_name++) { +- +- gchar *image_file = gkbd_status_get_image_filename (i); +- +- if (image_file == NULL) { +- item = +- gtk_menu_item_new_with_label (*current_name); +- } else { +- GdkPixbuf *pixbuf = +- gdk_pixbuf_new_from_file_at_size (image_file, +- 24, 24, +- NULL); +- GtkWidget *img = +- gtk_image_new_from_pixbuf (pixbuf); +- item = +- gtk_image_menu_item_new_with_label +- (*current_name); +- gtk_widget_show (img); +- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM +- (item), img); +- gtk_image_menu_item_set_always_show_image +- (GTK_IMAGE_MENU_ITEM (item), TRUE); +- g_free (image_file); +- } +- gtk_widget_show (item); +- gtk_menu_shell_append (GTK_MENU_SHELL (groups_menu), item); +- g_signal_connect (item, "activate", +- G_CALLBACK (popup_menu_set_group_cb), +- GINT_TO_POINTER (i)); +- } +- +- return popup_menu; +-} +- +-static void +-status_icon_popup_menu_cb (GtkStatusIcon * icon, guint button, guint time) +-{ +- GtkMenu *popup_menu = create_status_menu (); +- +- gtk_menu_popup (popup_menu, NULL, NULL, +- gtk_status_icon_position_menu, +- (gpointer) icon, button, time); +-} +- +-static void +-show_hide_icon () +-{ +- if (g_strv_length (current_kbd_config.layouts_variants) > 1) { +- if (icon == NULL) { +- xkl_debug (150, "Creating keyboard status icon\n"); +- icon = gkbd_status_new (); +- g_signal_connect (icon, "popup-menu", +- G_CALLBACK +- (status_icon_popup_menu_cb), +- NULL); +- +- } +- } else { +- if (icon != NULL) { +- xkl_debug (150, "Destroying icon\n"); +- g_object_unref (icon); +- icon = NULL; +- } +- } +-} +- +-static gboolean +-try_activating_xkb_config_if_new (GkbdKeyboardConfig * +- current_sys_kbd_config) +-{ +- /* Activate - only if different! */ +- if (!gkbd_keyboard_config_equals +- (¤t_kbd_config, current_sys_kbd_config)) { +- if (gkbd_keyboard_config_activate (¤t_kbd_config)) { +- if (pa_callback != NULL) { +- (*pa_callback) (pa_callback_user_data); +- return TRUE; +- } +- } else { +- return FALSE; +- } +- } +- return TRUE; +-} +- +-static gboolean +-filter_xkb_config (void) +-{ +- XklConfigItem *item; +- gchar *lname; +- gchar *vname; +- gchar **lv; +- gboolean any_change = FALSE; +- +- xkl_debug (100, "Filtering configuration against the registry\n"); +- if (!ensure_xkl_registry ()) +- return FALSE; +- +- lv = current_kbd_config.layouts_variants; +- item = xkl_config_item_new (); +- while (*lv) { +- xkl_debug (100, "Checking [%s]\n", *lv); +- if (gkbd_keyboard_config_split_items (*lv, &lname, &vname)) { +- gboolean should_be_dropped = FALSE; +- g_snprintf (item->name, sizeof (item->name), "%s", +- lname); +- if (!xkl_config_registry_find_layout +- (xkl_registry, item)) { +- xkl_debug (100, "Bad layout [%s]\n", +- lname); +- should_be_dropped = TRUE; +- } else if (vname) { +- g_snprintf (item->name, +- sizeof (item->name), "%s", +- vname); +- if (!xkl_config_registry_find_variant +- (xkl_registry, lname, item)) { +- xkl_debug (100, +- "Bad variant [%s(%s)]\n", +- lname, vname); +- should_be_dropped = TRUE; +- } +- } +- if (should_be_dropped) { +- gkbd_strv_behead (lv); +- any_change = TRUE; +- continue; +- } +- } +- lv++; +- } +- g_object_unref (item); +- return any_change; +-} +- +-static void +-apply_xkb_settings (void) +-{ +- GkbdKeyboardConfig current_sys_kbd_config; +- +- if (!inited_ok) +- return; +- +- gkbd_keyboard_config_init (¤t_sys_kbd_config, xkl_engine); +- +- gkbd_keyboard_config_load (¤t_kbd_config, +- &initial_sys_kbd_config); +- +- gkbd_keyboard_config_load_from_x_current (¤t_sys_kbd_config, +- NULL); +- +- if (!try_activating_xkb_config_if_new (¤t_sys_kbd_config)) { +- if (filter_xkb_config ()) { +- if (!try_activating_xkb_config_if_new +- (¤t_sys_kbd_config)) { +- g_warning +- ("Could not activate the filtered XKB configuration"); +- activation_error (); +- } +- } else { +- g_warning +- ("Could not activate the XKB configuration"); +- activation_error (); +- } +- } else +- xkl_debug (100, +- "Actual KBD configuration was not changed: redundant notification\n"); +- +- gkbd_keyboard_config_term (¤t_sys_kbd_config); +- show_hide_icon (); +-} +- +-static void +-csd_keyboard_xkb_analyze_sysconfig (void) +-{ +- if (!inited_ok) +- return; +- +- gkbd_keyboard_config_init (&initial_sys_kbd_config, xkl_engine); +- gkbd_keyboard_config_load_from_x_initial (&initial_sys_kbd_config, +- NULL); +-} +- +-void +-csd_keyboard_xkb_set_post_activation_callback (PostActivationCallback fun, +- void *user_data) +-{ +- pa_callback = fun; +- pa_callback_user_data = user_data; +-} +- +-static GdkFilterReturn +-csd_keyboard_xkb_evt_filter (GdkXEvent * xev, GdkEvent * event) +-{ +- XEvent *xevent = (XEvent *) xev; +- xkl_engine_filter_events (xkl_engine, xevent); +- return GDK_FILTER_CONTINUE; +-} +- +-/* When new Keyboard is plugged in - reload the settings */ +-static void +-csd_keyboard_new_device (XklEngine * engine) +-{ +- apply_desktop_settings (); +- apply_xkb_settings (); +-} +- +-void +-csd_keyboard_xkb_init (CsdKeyboardManager * kbd_manager) +-{ +- Display *display = +- GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); +- cinnamon_settings_profile_start (NULL); +- +- gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), +- DATADIR G_DIR_SEPARATOR_S +- "icons"); +- +- manager = kbd_manager; +- cinnamon_settings_profile_start ("xkl_engine_get_instance"); +- xkl_engine = xkl_engine_get_instance (display); +- cinnamon_settings_profile_end ("xkl_engine_get_instance"); +- if (xkl_engine) { +- inited_ok = TRUE; +- +- gkbd_desktop_config_init (¤t_config, xkl_engine); +- gkbd_keyboard_config_init (¤t_kbd_config, +- xkl_engine); +- xkl_engine_backup_names_prop (xkl_engine); +- csd_keyboard_xkb_analyze_sysconfig (); +- +- settings_desktop = g_settings_new (GKBD_DESKTOP_SCHEMA); +- settings_keyboard = g_settings_new (GKBD_KEYBOARD_SCHEMA); +- g_signal_connect (settings_desktop, "changed", +- (GCallback) apply_desktop_settings, +- NULL); +- g_signal_connect (settings_keyboard, "changed", +- (GCallback) apply_xkb_settings, NULL); +- +- gdk_window_add_filter (NULL, (GdkFilterFunc) +- csd_keyboard_xkb_evt_filter, NULL); +- +- if (xkl_engine_get_features (xkl_engine) & +- XKLF_DEVICE_DISCOVERY) +- g_signal_connect (xkl_engine, "X-new-device", +- G_CALLBACK +- (csd_keyboard_new_device), NULL); +- +- cinnamon_settings_profile_start ("xkl_engine_start_listen"); +- xkl_engine_start_listen (xkl_engine, +- XKLL_MANAGE_LAYOUTS | +- XKLL_MANAGE_WINDOW_STATES); +- cinnamon_settings_profile_end ("xkl_engine_start_listen"); +- +- cinnamon_settings_profile_start ("apply_desktop_settings"); +- apply_desktop_settings (); +- cinnamon_settings_profile_end ("apply_desktop_settings"); +- cinnamon_settings_profile_start ("apply_xkb_settings"); +- apply_xkb_settings (); +- cinnamon_settings_profile_end ("apply_xkb_settings"); +- } +- preview_dialogs = g_hash_table_new (g_direct_hash, g_direct_equal); +- +- cinnamon_settings_profile_end (NULL); +-} +- +-void +-csd_keyboard_xkb_shutdown (void) +-{ +- if (!inited_ok) +- return; +- +- pa_callback = NULL; +- pa_callback_user_data = NULL; +- manager = NULL; +- +- if (preview_dialogs != NULL) +- g_hash_table_destroy (preview_dialogs); +- +- if (!inited_ok) +- return; +- +- xkl_engine_stop_listen (xkl_engine, +- XKLL_MANAGE_LAYOUTS | +- XKLL_MANAGE_WINDOW_STATES); +- +- gdk_window_remove_filter (NULL, (GdkFilterFunc) +- csd_keyboard_xkb_evt_filter, NULL); +- +- g_object_unref (settings_desktop); +- settings_desktop = NULL; +- g_object_unref (settings_keyboard); +- settings_keyboard = NULL; +- +- if (xkl_registry) { +- g_object_unref (xkl_registry); +- } +- +- g_object_unref (xkl_engine); +- +- xkl_engine = NULL; +- +- inited_ok = FALSE; +-} +diff -uNrp a/plugins/keyboard/csd-keyboard-xkb.h b/plugins/keyboard/csd-keyboard-xkb.h +--- a/plugins/keyboard/csd-keyboard-xkb.h 2013-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/csd-keyboard-xkb.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,39 +0,0 @@ +-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- +- * cinnamon-settings-keyboard-xkb.h +- * +- * Copyright (C) 2001 Udaltsoft +- * +- * Written by Sergey V. Oudaltsov <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-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/delayed-dialog.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,128 +0,0 @@ +-/* +- * Copyright © 2006 Novell, Inc. +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2, or (at +- * your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, but +- * WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA +- * 02110-1335, USA. +- */ +- +-#include <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-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/delayed-dialog.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,32 +0,0 @@ +-/* +- * Copyright © 2006 Novell, Inc. +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2, or (at +- * your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, but +- * WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA +- * 02110-1335, USA. +- */ +- +- +-#ifndef __DELAYED_DIALOG_H +-#define __DELAYED_DIALOG_H +- +-#include <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-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/gkbd-configuration.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,350 +0,0 @@ +-/* +- * Copyright (C) 2010 Canonical Ltd. +- * +- * Authors: Jan Arne Petersen <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-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/gkbd-configuration.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,65 +0,0 @@ +-/* +- * Copyright (C) 2010 Canonical Ltd. +- * +- * Authors: Jan Arne Petersen <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-08-25 16:36:02.000000000 +0100 +@@ -0,0 +1,2 @@ ++-kr -i8 -pcs -lps -psl ++ +diff -uNrp a/plugins/keyboard/Makefile.am b/plugins/keyboard/Makefile.am +--- a/plugins/keyboard/Makefile.am 2013-08-24 18:04:31.000000000 +0100 ++++ b/plugins/keyboard/Makefile.am 2013-08-25 16:36:02.000000000 +0100 +@@ -20,25 +20,20 @@ libkeyboard_la_SOURCES = \ + csd-keyboard-plugin.c \ + csd-keyboard-manager.h \ + csd-keyboard-manager.c \ +- csd-keyboard-xkb.h \ +- csd-keyboard-xkb.c \ +- delayed-dialog.h \ +- delayed-dialog.c \ +- gkbd-configuration.c \ +- gkbd-configuration.h \ + $(NULL) + + libkeyboard_la_CPPFLAGS = \ + -I$(top_srcdir)/cinnamon-settings-daemon \ + -I$(top_srcdir)/data \ ++ -I$(top_srcdir)/plugins/common \ + -DDATADIR=\""$(pkgdatadir)"\" \ ++ -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DCINNAMON_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ + $(AM_CPPFLAGS) + + libkeyboard_la_CFLAGS = \ + $(PLUGIN_CFLAGS) \ + $(SETTINGS_PLUGIN_CFLAGS) \ +- $(APPINDICATOR_CFLAGS) \ + $(KEYBOARD_CFLAGS) \ + $(AM_CFLAGS) + +@@ -46,19 +41,63 @@ libkeyboard_la_LDFLAGS = \ + $(CSD_PLUGIN_LDFLAGS) \ + $(NULL) + +-libkeyboard_la_LIBADD = \ +- $(SETTINGS_PLUGIN_LIBS) \ +- $(XF86MISC_LIBS) \ +- $(KEYBOARD_LIBS) \ +- $(APPINDICATOR_LIBS) \ ++libkeyboard_la_LIBADD = \ ++ $(top_builddir)/plugins/common/libcommon.la \ ++ $(SETTINGS_PLUGIN_LIBS) \ ++ $(XF86MISC_LIBS) \ ++ $(KEYBOARD_LIBS) \ + $(NULL) + ++libexec_PROGRAMS = csd-test-keyboard ++csd_test_keyboard_SOURCES = \ ++ test-keyboard.c \ ++ csd-keyboard-manager.h \ ++ csd-keyboard-manager.c \ ++ $(NULL) ++ ++csd_test_keyboard_CFLAGS = $(libkeyboard_la_CFLAGS) ++csd_test_keyboard_CPPFLAGS = $(libkeyboard_la_CPPFLAGS) ++csd_test_keyboard_LDADD = $(libkeyboard_la_LIBADD) $(top_builddir)/cinnamon-settings-daemon/libcsd.la ++ + plugin_in_files = \ + keyboard.cinnamon-settings-plugin.in \ + $(NULL) + + plugin_DATA = $(plugin_in_files:.cinnamon-settings-plugin.in=.cinnamon-settings-plugin) + ++if HAVE_IBUS ++noinst_PROGRAMS = test-keyboard-ibus-utils ++test_keyboard_ibus_utils_SOURCES = test-keyboard-ibus-utils.c ++test_keyboard_ibus_utils_CFLAGS = $(libkeyboard_la_CFLAGS) ++test_keyboard_ibus_utils_CPPFLAGS = $(libkeyboard_la_CPPFLAGS) ++test_keyboard_ibus_utils_LDADD = $(libkeyboard_la_LIBADD) $(top_builddir)/cinnamon-settings-daemon/libcsd.la ++ ++check-local: test-keyboard-ibus-utils ++ $(builddir)/test-keyboard-ibus-utils > /dev/null ++endif ++ ++libexec_PROGRAMS += csd-input-sources-switcher ++ ++csd_input_sources_switcher_SOURCES = \ ++ csd-input-sources-switcher.c \ ++ $(NULL) ++ ++csd_input_sources_switcher_CPPFLAGS = \ ++ -I$(top_srcdir)/data \ ++ -I$(top_srcdir)/plugins/common \ ++ $(AM_CPPFLAGS) \ ++ $(NULL) ++ ++csd_input_sources_switcher_CFLAGS = \ ++ $(SETTINGS_PLUGIN_CFLAGS) \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++ ++csd_input_sources_switcher_LDADD = \ ++ $(top_builddir)/plugins/common/libcommon.la \ ++ $(SETTINGS_PLUGIN_LIBS) \ ++ $(NULL) ++ + EXTRA_DIST = \ + $(icons_DATA) \ + $(plugin_in_files) \ +diff -uNrp a/plugins/keyboard/test-keyboard.c b/plugins/keyboard/test-keyboard.c +--- a/plugins/keyboard/test-keyboard.c 1970-01-01 01:00:00.000000000 +0100 ++++ b/plugins/keyboard/test-keyboard.c 2013-08-25 16:36:02.000000000 +0100 +@@ -0,0 +1,7 @@ ++#define NEW csd_keyboard_manager_new ++#define START csd_keyboard_manager_start ++#define STOP csd_keyboard_manager_stop ++#define MANAGER CsdKeyboardManager ++#include "csd-keyboard-manager.h" ++ ++#include "test-plugin.h" +diff -uNrp a/plugins/keyboard/test-keyboard-ibus-utils.c b/plugins/keyboard/test-keyboard-ibus-utils.c +--- a/plugins/keyboard/test-keyboard-ibus-utils.c 1970-01-01 01:00:00.000000000 +0100 ++++ b/plugins/keyboard/test-keyboard-ibus-utils.c 2013-08-25 16:36:02.000000000 +0100 +@@ -0,0 +1,116 @@ ++#include "csd-keyboard-manager.c" ++ ++static void ++test_make_xkb_source_id (void) ++{ ++ gint i; ++ const gchar *test_strings[][2] = { ++ /* input output */ ++ { "xkb:aa:bb:cc", "aa+bb" }, ++ { "xkb:aa:bb:", "aa+bb" }, ++ { "xkb:aa::cc", "aa" }, ++ { "xkb:aa::", "aa" }, ++ { "xkb::bb:cc", "+bb" }, ++ { "xkb::bb:", "+bb" }, ++ { "xkb:::cc", "" }, ++ { "xkb:::", "" }, ++ }; ++ ++ for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) ++ g_assert_cmpstr (make_xkb_source_id (test_strings[i][0]), ==, test_strings[i][1]); ++} ++ ++static void ++test_layout_from_ibus_layout (void) ++{ ++ gint i; ++ const gchar *test_strings[][2] = { ++ /* input output */ ++ { "", "" }, ++ { "a", "a" }, ++ { "a(", "a" }, ++ { "a[", "a" }, ++ }; ++ ++ for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) ++ g_assert_cmpstr (layout_from_ibus_layout (test_strings[i][0]), ==, test_strings[i][1]); ++} ++ ++static void ++test_variant_from_ibus_layout (void) ++{ ++ gint i; ++ const gchar *test_strings[][2] = { ++ /* input output */ ++ { "", NULL }, ++ { "a", NULL }, ++ { "(", NULL }, ++ { "()", "" }, ++ { "(b)", "b" }, ++ { "a(", NULL }, ++ { "a()", "" }, ++ { "a(b)", "b" }, ++ }; ++ ++ for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) ++ g_assert_cmpstr (variant_from_ibus_layout (test_strings[i][0]), ==, test_strings[i][1]); ++} ++ ++static void ++test_options_from_ibus_layout (void) ++{ ++ gint i, j; ++ gchar *output_0[] = { ++ NULL ++ }; ++ gchar *output_1[] = { ++ "", ++ NULL ++ }; ++ gchar *output_2[] = { ++ "b", ++ NULL ++ }; ++ gchar *output_3[] = { ++ "b", "", ++ NULL ++ }; ++ gchar *output_4[] = { ++ "b", "c", ++ NULL ++ }; ++ const gpointer tests[][2] = { ++ /* input output */ ++ { "", NULL }, ++ { "a", NULL }, ++ { "a[", output_0 }, ++ { "a[]", output_1 }, ++ { "a[b]", output_2 }, ++ { "a[b,]", output_3 }, ++ { "a[b,c]", output_4 }, ++ }; ++ ++ for (i = 0; i < G_N_ELEMENTS (tests); ++i) { ++ if (tests[i][1] == NULL) { ++ g_assert (options_from_ibus_layout (tests[i][0]) == NULL); ++ } else { ++ gchar **strv_a = options_from_ibus_layout (tests[i][0]); ++ gchar **strv_b = tests[i][1]; ++ ++ g_assert (g_strv_length (strv_a) == g_strv_length (strv_b)); ++ for (j = 0; j < g_strv_length (strv_a); ++j) ++ g_assert_cmpstr (strv_a[j], ==, strv_b[j]); ++ } ++ } ++} ++ ++int ++main (void) ++{ ++ test_make_xkb_source_id (); ++ test_layout_from_ibus_layout (); ++ test_variant_from_ibus_layout (); ++ test_options_from_ibus_layout (); ++ ++ return 0; ++} +diff -uNrp a/plugins/keyboard/xxx/csd-keyboard-xkb.c b/plugins/keyboard/xxx/csd-keyboard-xkb.c +--- a/plugins/keyboard/xxx/csd-keyboard-xkb.c 1970-01-01 01:00:00.000000000 +0100 ++++ b/plugins/keyboard/xxx/csd-keyboard-xkb.c 2013-08-25 16:36:02.000000000 +0100 +@@ -0,0 +1,579 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2001 Udaltsoft ++ * ++ * Written by Sergey V. Oudaltsov <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 (¤t_config); ++ /* again, probably it would be nice to compare things ++ before activating them */ ++ gkbd_desktop_config_activate (¤t_config); ++} ++ ++static void ++popup_menu_launch_capplet () ++{ ++ GAppInfo *info; ++ GdkAppLaunchContext *ctx; ++ GError *error = NULL; ++ ++ info = ++ g_app_info_create_from_commandline ++ ("cinnamon-settings region", NULL, 0, &error); ++ ++ if (info != NULL) { ++ ctx = ++ gdk_display_get_app_launch_context ++ (gdk_display_get_default ()); ++ ++ if (g_app_info_launch (info, NULL, ++ G_APP_LAUNCH_CONTEXT (ctx), &error) == FALSE) { ++ g_warning ++ ("Could not execute keyboard properties capplet: [%s]\n", ++ error->message); ++ g_error_free (error); ++ } ++ ++ g_object_unref (info); ++ g_object_unref (ctx); ++ } ++ ++} ++ ++static void ++show_layout_destroy (GtkWidget * dialog, gint group) ++{ ++ g_hash_table_remove (preview_dialogs, GINT_TO_POINTER (group)); ++} ++ ++static void ++popup_menu_show_layout () ++{ ++ GtkWidget *dialog; ++ XklEngine *engine = ++ xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY ++ (gdk_display_get_default ())); ++ XklState *xkl_state = xkl_engine_get_current_state (engine); ++ ++ gchar **group_names = gkbd_status_get_group_names (); ++ ++ gpointer p = g_hash_table_lookup (preview_dialogs, ++ GINT_TO_POINTER ++ (xkl_state->group)); ++ ++ if (xkl_state->group < 0 ++ || xkl_state->group >= g_strv_length (group_names)) { ++ return; ++ } ++ ++ if (p != NULL) { ++ /* existing window */ ++ gtk_window_present (GTK_WINDOW (p)); ++ return; ++ } ++ ++ if (!ensure_xkl_registry ()) ++ return; ++ ++ dialog = gkbd_keyboard_drawing_dialog_new (); ++ gkbd_keyboard_drawing_dialog_set_group (dialog, xkl_registry, xkl_state->group); ++ ++ g_signal_connect (dialog, "destroy", ++ G_CALLBACK (show_layout_destroy), ++ GINT_TO_POINTER (xkl_state->group)); ++ g_hash_table_insert (preview_dialogs, ++ GINT_TO_POINTER (xkl_state->group), dialog); ++ gtk_widget_show_all (dialog); ++} ++ ++static void ++popup_menu_set_group (gint group_number, gboolean only_menu) ++{ ++ ++ XklEngine *engine = gkbd_status_get_xkl_engine (); ++ ++ XklState *st = xkl_engine_get_current_state(engine); ++ Window cur; ++ st->group = group_number; ++ xkl_engine_allow_one_switch_to_secondary_group (engine); ++ cur = xkl_engine_get_current_window (engine); ++ if (cur != (Window) NULL) { ++ xkl_debug (150, "Enforcing the state %d for window %lx\n", ++ st->group, cur); ++ ++ xkl_engine_save_state (engine, ++ xkl_engine_get_current_window ++ (engine), st); ++/* XSetInputFocus( GDK_DISPLAY(), cur, RevertToNone, CurrentTime );*/ ++ } else { ++ xkl_debug (150, ++ "??? Enforcing the state %d for unknown window\n", ++ st->group); ++ /* strange situation - bad things can happen */ ++ } ++ if (!only_menu) ++ xkl_engine_lock_group (engine, st->group); ++} ++ ++static void ++popup_menu_set_group_cb (GtkMenuItem * item, gpointer param) ++{ ++ gint group_number = GPOINTER_TO_INT (param); ++ ++ popup_menu_set_group(group_number, FALSE); ++} ++ ++ ++static GtkMenu * ++create_status_menu (void) ++{ ++ GtkMenu *popup_menu = GTK_MENU (gtk_menu_new ()); ++ int i = 0; ++ ++ GtkMenu *groups_menu = GTK_MENU (gtk_menu_new ()); ++ gchar **current_name = gkbd_status_get_group_names (); ++ ++ GtkWidget *item = gtk_menu_item_new_with_mnemonic (_("_Layouts")); ++ gtk_widget_show (item); ++ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); ++ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), ++ GTK_WIDGET (groups_menu)); ++ ++ item = gtk_menu_item_new_with_mnemonic (_("Show _Keyboard Layout...")); ++ gtk_widget_show (item); ++ g_signal_connect (item, "activate", popup_menu_show_layout, NULL); ++ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); ++ ++ /* translators note: ++ * This is the name of the cinnamon-settings "region" panel */ ++ item = gtk_menu_item_new_with_mnemonic (_("Region and Language Settings")); ++ gtk_widget_show (item); ++ g_signal_connect (item, "activate", popup_menu_launch_capplet, NULL); ++ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item); ++ ++ for (i = 0; current_name && *current_name; i++, current_name++) { ++ ++ gchar *image_file = gkbd_status_get_image_filename (i); ++ ++ if (image_file == NULL) { ++ item = ++ gtk_menu_item_new_with_label (*current_name); ++ } else { ++ GdkPixbuf *pixbuf = ++ gdk_pixbuf_new_from_file_at_size (image_file, ++ 24, 24, ++ NULL); ++ GtkWidget *img = ++ gtk_image_new_from_pixbuf (pixbuf); ++ item = ++ gtk_image_menu_item_new_with_label ++ (*current_name); ++ gtk_widget_show (img); ++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM ++ (item), img); ++ gtk_image_menu_item_set_always_show_image ++ (GTK_IMAGE_MENU_ITEM (item), TRUE); ++ g_free (image_file); ++ } ++ gtk_widget_show (item); ++ gtk_menu_shell_append (GTK_MENU_SHELL (groups_menu), item); ++ g_signal_connect (item, "activate", ++ G_CALLBACK (popup_menu_set_group_cb), ++ GINT_TO_POINTER (i)); ++ } ++ ++ return popup_menu; ++} ++ ++static void ++status_icon_popup_menu_cb (GtkStatusIcon * icon, guint button, guint time) ++{ ++ GtkMenu *popup_menu = create_status_menu (); ++ ++ gtk_menu_popup (popup_menu, NULL, NULL, ++ gtk_status_icon_position_menu, ++ (gpointer) icon, button, time); ++} ++ ++static void ++show_hide_icon () ++{ ++ if (g_strv_length (current_kbd_config.layouts_variants) > 1) { ++ if (icon == NULL) { ++ xkl_debug (150, "Creating keyboard status icon\n"); ++ icon = gkbd_status_new (); ++ g_signal_connect (icon, "popup-menu", ++ G_CALLBACK ++ (status_icon_popup_menu_cb), ++ NULL); ++ ++ } ++ } else { ++ if (icon != NULL) { ++ xkl_debug (150, "Destroying icon\n"); ++ g_object_unref (icon); ++ icon = NULL; ++ } ++ } ++} ++ ++static gboolean ++try_activating_xkb_config_if_new (GkbdKeyboardConfig * ++ current_sys_kbd_config) ++{ ++ /* Activate - only if different! */ ++ if (!gkbd_keyboard_config_equals ++ (¤t_kbd_config, current_sys_kbd_config)) { ++ if (gkbd_keyboard_config_activate (¤t_kbd_config)) { ++ if (pa_callback != NULL) { ++ (*pa_callback) (pa_callback_user_data); ++ return TRUE; ++ } ++ } else { ++ return FALSE; ++ } ++ } ++ return TRUE; ++} ++ ++static gboolean ++filter_xkb_config (void) ++{ ++ XklConfigItem *item; ++ gchar *lname; ++ gchar *vname; ++ gchar **lv; ++ gboolean any_change = FALSE; ++ ++ xkl_debug (100, "Filtering configuration against the registry\n"); ++ if (!ensure_xkl_registry ()) ++ return FALSE; ++ ++ lv = current_kbd_config.layouts_variants; ++ item = xkl_config_item_new (); ++ while (*lv) { ++ xkl_debug (100, "Checking [%s]\n", *lv); ++ if (gkbd_keyboard_config_split_items (*lv, &lname, &vname)) { ++ gboolean should_be_dropped = FALSE; ++ g_snprintf (item->name, sizeof (item->name), "%s", ++ lname); ++ if (!xkl_config_registry_find_layout ++ (xkl_registry, item)) { ++ xkl_debug (100, "Bad layout [%s]\n", ++ lname); ++ should_be_dropped = TRUE; ++ } else if (vname) { ++ g_snprintf (item->name, ++ sizeof (item->name), "%s", ++ vname); ++ if (!xkl_config_registry_find_variant ++ (xkl_registry, lname, item)) { ++ xkl_debug (100, ++ "Bad variant [%s(%s)]\n", ++ lname, vname); ++ should_be_dropped = TRUE; ++ } ++ } ++ if (should_be_dropped) { ++ gkbd_strv_behead (lv); ++ any_change = TRUE; ++ continue; ++ } ++ } ++ lv++; ++ } ++ g_object_unref (item); ++ return any_change; ++} ++ ++static void ++apply_xkb_settings (void) ++{ ++ GkbdKeyboardConfig current_sys_kbd_config; ++ ++ if (!inited_ok) ++ return; ++ ++ gkbd_keyboard_config_init (¤t_sys_kbd_config, xkl_engine); ++ ++ gkbd_keyboard_config_load (¤t_kbd_config, ++ &initial_sys_kbd_config); ++ ++ gkbd_keyboard_config_load_from_x_current (¤t_sys_kbd_config, ++ NULL); ++ ++ if (!try_activating_xkb_config_if_new (¤t_sys_kbd_config)) { ++ if (filter_xkb_config ()) { ++ if (!try_activating_xkb_config_if_new ++ (¤t_sys_kbd_config)) { ++ g_warning ++ ("Could not activate the filtered XKB configuration"); ++ activation_error (); ++ } ++ } else { ++ g_warning ++ ("Could not activate the XKB configuration"); ++ activation_error (); ++ } ++ } else ++ xkl_debug (100, ++ "Actual KBD configuration was not changed: redundant notification\n"); ++ ++ gkbd_keyboard_config_term (¤t_sys_kbd_config); ++ show_hide_icon (); ++} ++ ++static void ++csd_keyboard_xkb_analyze_sysconfig (void) ++{ ++ if (!inited_ok) ++ return; ++ ++ gkbd_keyboard_config_init (&initial_sys_kbd_config, xkl_engine); ++ gkbd_keyboard_config_load_from_x_initial (&initial_sys_kbd_config, ++ NULL); ++} ++ ++void ++csd_keyboard_xkb_set_post_activation_callback (PostActivationCallback fun, ++ void *user_data) ++{ ++ pa_callback = fun; ++ pa_callback_user_data = user_data; ++} ++ ++static GdkFilterReturn ++csd_keyboard_xkb_evt_filter (GdkXEvent * xev, GdkEvent * event) ++{ ++ XEvent *xevent = (XEvent *) xev; ++ xkl_engine_filter_events (xkl_engine, xevent); ++ return GDK_FILTER_CONTINUE; ++} ++ ++/* When new Keyboard is plugged in - reload the settings */ ++static void ++csd_keyboard_new_device (XklEngine * engine) ++{ ++ apply_desktop_settings (); ++ apply_xkb_settings (); ++} ++ ++void ++csd_keyboard_xkb_init (CsdKeyboardManager * kbd_manager) ++{ ++ Display *display = ++ GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); ++ cinnamon_settings_profile_start (NULL); ++ ++ gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), ++ DATADIR G_DIR_SEPARATOR_S ++ "icons"); ++ ++ manager = kbd_manager; ++ cinnamon_settings_profile_start ("xkl_engine_get_instance"); ++ xkl_engine = xkl_engine_get_instance (display); ++ cinnamon_settings_profile_end ("xkl_engine_get_instance"); ++ if (xkl_engine) { ++ inited_ok = TRUE; ++ ++ gkbd_desktop_config_init (¤t_config, xkl_engine); ++ gkbd_keyboard_config_init (¤t_kbd_config, ++ xkl_engine); ++ xkl_engine_backup_names_prop (xkl_engine); ++ csd_keyboard_xkb_analyze_sysconfig (); ++ ++ settings_desktop = g_settings_new (GKBD_DESKTOP_SCHEMA); ++ settings_keyboard = g_settings_new (GKBD_KEYBOARD_SCHEMA); ++ g_signal_connect (settings_desktop, "changed", ++ (GCallback) apply_desktop_settings, ++ NULL); ++ g_signal_connect (settings_keyboard, "changed", ++ (GCallback) apply_xkb_settings, NULL); ++ ++ gdk_window_add_filter (NULL, (GdkFilterFunc) ++ csd_keyboard_xkb_evt_filter, NULL); ++ ++ if (xkl_engine_get_features (xkl_engine) & ++ XKLF_DEVICE_DISCOVERY) ++ g_signal_connect (xkl_engine, "X-new-device", ++ G_CALLBACK ++ (csd_keyboard_new_device), NULL); ++ ++ cinnamon_settings_profile_start ("xkl_engine_start_listen"); ++ xkl_engine_start_listen (xkl_engine, ++ XKLL_MANAGE_LAYOUTS | ++ XKLL_MANAGE_WINDOW_STATES); ++ cinnamon_settings_profile_end ("xkl_engine_start_listen"); ++ ++ cinnamon_settings_profile_start ("apply_desktop_settings"); ++ apply_desktop_settings (); ++ cinnamon_settings_profile_end ("apply_desktop_settings"); ++ cinnamon_settings_profile_start ("apply_xkb_settings"); ++ apply_xkb_settings (); ++ cinnamon_settings_profile_end ("apply_xkb_settings"); ++ } ++ preview_dialogs = g_hash_table_new (g_direct_hash, g_direct_equal); ++ ++ cinnamon_settings_profile_end (NULL); ++} ++ ++void ++csd_keyboard_xkb_shutdown (void) ++{ ++ if (!inited_ok) ++ return; ++ ++ pa_callback = NULL; ++ pa_callback_user_data = NULL; ++ manager = NULL; ++ ++ if (preview_dialogs != NULL) ++ g_hash_table_destroy (preview_dialogs); ++ ++ if (!inited_ok) ++ return; ++ ++ xkl_engine_stop_listen (xkl_engine, ++ XKLL_MANAGE_LAYOUTS | ++ XKLL_MANAGE_WINDOW_STATES); ++ ++ gdk_window_remove_filter (NULL, (GdkFilterFunc) ++ csd_keyboard_xkb_evt_filter, NULL); ++ ++ g_object_unref (settings_desktop); ++ settings_desktop = NULL; ++ g_object_unref (settings_keyboard); ++ settings_keyboard = NULL; ++ ++ if (xkl_registry) { ++ g_object_unref (xkl_registry); ++ } ++ ++ g_object_unref (xkl_engine); ++ ++ xkl_engine = NULL; ++ ++ inited_ok = FALSE; ++} +diff -uNrp a/plugins/keyboard/xxx/csd-keyboard-xkb.h b/plugins/keyboard/xxx/csd-keyboard-xkb.h +--- a/plugins/keyboard/xxx/csd-keyboard-xkb.h 1970-01-01 01:00:00.000000000 +0100 ++++ b/plugins/keyboard/xxx/csd-keyboard-xkb.h 2013-08-25 16:36:02.000000000 +0100 +@@ -0,0 +1,39 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * cinnamon-settings-keyboard-xkb.h ++ * ++ * Copyright (C) 2001 Udaltsoft ++ * ++ * Written by Sergey V. Oudaltsov <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-08-25 16:36:02.000000000 +0100 +@@ -0,0 +1,128 @@ ++/* ++ * Copyright © 2006 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2, or (at ++ * your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA ++ * 02110-1335, USA. ++ */ ++ ++#include <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-08-25 16:36:02.000000000 +0100 +@@ -0,0 +1,32 @@ ++/* ++ * Copyright © 2006 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2, or (at ++ * your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA ++ * 02110-1335, USA. ++ */ ++ ++ ++#ifndef __DELAYED_DIALOG_H ++#define __DELAYED_DIALOG_H ++ ++#include <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-08-25 16:36:02.000000000 +0100 +@@ -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-08-25 16:36:02.000000000 +0100 +@@ -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-08-24 18:04:31.000000000 +0100 ++++ b/plugins/media-keys/csd-media-keys-manager.c 2013-08-25 16:36:02.000000000 +0100 +@@ -120,6 +120,10 @@ static const gchar kb_introspection_xml[ + #define VOLUME_STEP 6 /* percents for one volume button press */ + #define MAX_VOLUME 65536.0 + ++#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.cinnamon.desktop.input-sources" ++#define KEY_CURRENT_INPUT_SOURCE "current" ++#define KEY_INPUT_SOURCES "sources" ++ + #define CSD_MEDIA_KEYS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CSD_TYPE_MEDIA_KEYS_MANAGER, CsdMediaKeysManagerPrivate)) + + typedef struct { +@@ -1750,6 +1754,40 @@ do_keyboard_brightness_action (CsdMediaK + manager); + } + ++static void ++do_switch_input_source_action (CsdMediaKeysManager *manager, ++ MediaKeyType type) ++{ ++ GSettings *settings; ++ GVariant *sources; ++ gint i, n; ++ ++ settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); ++ sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); ++ ++ n = g_variant_n_children (sources); ++ if (n < 2) ++ goto out; ++ ++ i = g_settings_get_uint (settings, KEY_CURRENT_INPUT_SOURCE); ++ ++ if (type == SWITCH_INPUT_SOURCE_KEY) ++ i += 1; ++ else ++ i -= 1; ++ ++ if (i < 0) ++ i = n - 1; ++ else if (i >= n) ++ i = 0; ++ ++ g_settings_set_uint (settings, KEY_CURRENT_INPUT_SOURCE, i); ++ ++ out: ++ g_variant_unref (sources); ++ g_object_unref (settings); ++} ++ + static gboolean + do_action (CsdMediaKeysManager *manager, + guint deviceid, +@@ -1908,6 +1946,10 @@ do_action (CsdMediaKeysManager *manager, + case BATTERY_KEY: + do_execute_desktop (manager, "gnome-power-statistics.desktop", timestamp); + break; ++ case SWITCH_INPUT_SOURCE_KEY: ++ case SWITCH_INPUT_SOURCE_BACKWARD_KEY: ++ do_switch_input_source_action (manager, type); ++ break; + /* Note, no default so compiler catches missing keys */ + case CUSTOM_KEY: + g_assert_not_reached (); +diff -uNrp a/plugins/media-keys/shortcuts-list.h b/plugins/media-keys/shortcuts-list.h +--- a/plugins/media-keys/shortcuts-list.h 2013-08-24 18:04:31.000000000 +0100 ++++ b/plugins/media-keys/shortcuts-list.h 2013-08-25 16:36:02.000000000 +0100 +@@ -81,6 +81,8 @@ typedef enum { + KEYBOARD_BRIGHTNESS_DOWN_KEY, + KEYBOARD_BRIGHTNESS_TOGGLE_KEY, + BATTERY_KEY, ++ SWITCH_INPUT_SOURCE_KEY, ++ SWITCH_INPUT_SOURCE_BACKWARD_KEY, + CUSTOM_KEY + } MediaKeyType; + +@@ -148,6 +150,9 @@ static struct { + { KEYBOARD_BRIGHTNESS_UP_KEY, NULL, "XF86KbdBrightnessUp" }, + { KEYBOARD_BRIGHTNESS_DOWN_KEY, NULL, "XF86KbdBrightnessDown" }, + { KEYBOARD_BRIGHTNESS_TOGGLE_KEY, NULL, "XF86KbdLightOnOff" }, ++ { SWITCH_INPUT_SOURCE_KEY, "switch-input-source", NULL }, ++ { SWITCH_INPUT_SOURCE_BACKWARD_KEY, "switch-input-source-backward", NULL }, ++ + { BATTERY_KEY, NULL, "XF86Battery" }, + }; + |