[gnome-settings-daemon] media-keys: Apply volume on the device the key came from
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon] media-keys: Apply volume on the device the key came from
- Date: Wed, 2 Nov 2011 20:12:21 +0000 (UTC)
commit 03b43fc4a35464718dee5ca0574e184c2c10023a
Author: Bastien Nocera <hadess hadess net>
Date: Mon Oct 31 16:17:35 2011 +0000
media-keys: Apply volume on the device the key came from
Using XInput2 events to detect which device the event came from,
X.org and gudev to match the input device to the sound output
device, and force the canberra/PulseAudio changes through that
particular audio output.
This only works with USB devices, but PulseAudio already handles
Bluetooth devices internally (without visual feedback or OSD).
https://bugzilla.gnome.org/show_bug.cgi?id=340720
plugins/media-keys/gsd-media-keys-manager.c | 293 +++++++++++++++++++++++----
1 files changed, 252 insertions(+), 41 deletions(-)
---
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index 79b4455..e3f0a11 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -40,6 +40,10 @@
#include <gtk/gtk.h>
#include <gio/gdesktopappinfo.h>
+#ifdef HAVE_GUDEV
+#include <gudev/gudev.h>
+#endif
+
#include "gnome-settings-profile.h"
#include "gsd-marshal.h"
#include "gsd-media-keys-manager.h"
@@ -50,7 +54,7 @@
#include "gsd-input-helper.h"
#include "gsd-enums.h"
-#include <canberra-gtk.h>
+#include <canberra.h>
#include <pulse/pulseaudio.h>
#include "gvc-mixer-control.h"
@@ -105,6 +109,12 @@ struct GsdMediaKeysManagerPrivate
/* Volume bits */
GvcMixerControl *volume;
GvcMixerStream *stream;
+ ca_context *ca;
+ GtkSettings *gtksettings;
+#ifdef HAVE_GUDEV
+ GHashTable *streams; /* key = X device ID, value = stream id */
+ GUdevClient *udev_client;
+#endif /* HAVE_GUDEV */
GtkWidget *dialog;
GSettings *settings;
@@ -123,6 +133,7 @@ struct GsdMediaKeysManagerPrivate
/* Multihead stuff */
GdkScreen *current_screen;
GSList *screens;
+ int opcode;
GList *media_players;
@@ -666,10 +677,11 @@ do_touchpad_action (GsdMediaKeysManager *manager)
static void
update_dialog (GsdMediaKeysManager *manager,
- guint vol,
- gboolean muted,
- gboolean sound_changed,
- gboolean quiet)
+ GvcMixerStream *stream,
+ guint vol,
+ gboolean muted,
+ gboolean sound_changed,
+ gboolean quiet)
{
if (!muted) {
vol = (int) (100 * (double) vol / PA_VOLUME_NORM);
@@ -686,32 +698,145 @@ update_dialog (GsdMediaKeysManager *manager,
GSD_MEDIA_KEYS_WINDOW_ACTION_VOLUME);
dialog_show (manager);
- if (quiet == FALSE && sound_changed != FALSE && muted == FALSE)
- ca_gtk_play_for_widget (manager->priv->dialog, 0,
+ if (quiet == FALSE && sound_changed != FALSE && muted == FALSE) {
+ ca_context_change_device (manager->priv->ca,
+ gvc_mixer_stream_get_name (stream));
+ ca_context_play (manager->priv->ca, 1,
CA_PROP_EVENT_ID, "audio-volume-change",
CA_PROP_EVENT_DESCRIPTION, "volume changed through key press",
- CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
NULL);
+ }
+}
+
+#ifdef HAVE_GUDEV
+/* PulseAudio gives us /devices/... paths, when udev
+ * expects /sys/devices/... paths. */
+static GUdevDevice *
+get_udev_device_for_sysfs_path (GsdMediaKeysManager *manager,
+ const char *sysfs_path)
+{
+ char *path;
+ GUdevDevice *dev;
+
+ path = g_strdup_printf ("/sys%s", sysfs_path);
+ dev = g_udev_client_query_by_sysfs_path (manager->priv->udev_client, path);
+ g_free (path);
+
+ return dev;
+}
+
+static GvcMixerStream *
+get_stream_for_device_id (GsdMediaKeysManager *manager,
+ guint deviceid)
+{
+ char *devnode;
+ gpointer id_ptr;
+ GvcMixerStream *res;
+ GUdevDevice *dev, *parent;
+ GSList *sinks, *l;
+
+ id_ptr = g_hash_table_lookup (manager->priv->streams, GUINT_TO_POINTER (deviceid));
+ if (id_ptr != NULL) {
+ if (GPOINTER_TO_UINT (id_ptr) == (guint) -1)
+ return NULL;
+ else
+ return gvc_mixer_control_lookup_stream_id (manager->priv->volume, GPOINTER_TO_UINT (id_ptr));
+ }
+
+ devnode = xdevice_get_device_node (deviceid);
+ if (devnode == NULL) {
+ g_debug ("Could not find device node for XInput device %d", deviceid);
+ return NULL;
+ }
+
+ dev = g_udev_client_query_by_device_file (manager->priv->udev_client, devnode);
+ if (dev == NULL) {
+ g_debug ("Could not find udev device for device path '%s'", devnode);
+ g_free (devnode);
+ return NULL;
+ }
+ g_free (devnode);
+
+ if (g_strcmp0 (g_udev_device_get_property (dev, "ID_BUS"), "usb") != 0) {
+ g_debug ("Not handling XInput device %d, not USB", deviceid);
+ g_hash_table_insert (manager->priv->streams,
+ GUINT_TO_POINTER (deviceid),
+ GUINT_TO_POINTER ((guint) -1));
+ g_object_unref (dev);
+ return NULL;
+ }
+
+ parent = g_udev_device_get_parent_with_subsystem (dev, "usb", "usb_device");
+ if (parent == NULL) {
+ g_warning ("No USB device parent for XInput device %d even though it's USB", deviceid);
+ g_object_unref (dev);
+ return NULL;
+ }
+
+ res = NULL;
+ sinks = gvc_mixer_control_get_sinks (manager->priv->volume);
+ for (l = sinks; l; l = l->next) {
+ GvcMixerStream *stream = l->data;
+ const char *sysfs_path;
+ GUdevDevice *sink_dev, *sink_parent;
+
+ sysfs_path = gvc_mixer_stream_get_sysfs_path (stream);
+ sink_dev = get_udev_device_for_sysfs_path (manager, sysfs_path);
+ if (sink_dev == NULL)
+ continue;
+ sink_parent = g_udev_device_get_parent_with_subsystem (sink_dev, "usb", "usb_device");
+ g_object_unref (sink_dev);
+ if (sink_parent == NULL)
+ continue;
+
+ if (g_strcmp0 (g_udev_device_get_sysfs_path (sink_parent),
+ g_udev_device_get_sysfs_path (parent)) == 0) {
+ res = stream;
+ }
+ g_object_unref (sink_parent);
+ if (res != NULL)
+ break;
+ }
+
+ if (res)
+ g_hash_table_insert (manager->priv->streams,
+ GUINT_TO_POINTER (deviceid),
+ GUINT_TO_POINTER (gvc_mixer_stream_get_id (res)));
+ else
+ g_hash_table_insert (manager->priv->streams,
+ GUINT_TO_POINTER (deviceid),
+ GUINT_TO_POINTER ((guint) -1));
+
+ return res;
}
+#endif /* HAVE_GUDEV */
static void
do_sound_action (GsdMediaKeysManager *manager,
+ guint deviceid,
int type,
gboolean quiet)
{
+ GvcMixerStream *stream;
gboolean old_muted, new_muted;
guint old_vol, new_vol, norm_vol_step;
gboolean sound_changed;
- if (manager->priv->stream == NULL)
+ /* Find the stream that corresponds to the device, if any */
+#ifdef HAVE_GUDEV
+ stream = get_stream_for_device_id (manager, deviceid);
+ if (stream == NULL)
+#endif /* HAVE_GUDEV */
+ stream = manager->priv->stream;
+ if (stream == NULL)
return;
norm_vol_step = PA_VOLUME_NORM * VOLUME_STEP / 100;
/* FIXME: this is racy */
- new_vol = old_vol = gvc_mixer_stream_get_volume (manager->priv->stream);
- new_muted = old_muted = gvc_mixer_stream_get_is_muted (manager->priv->stream);
+ new_vol = old_vol = gvc_mixer_stream_get_volume (stream);
+ new_muted = old_muted = gvc_mixer_stream_get_is_muted (stream);
sound_changed = FALSE;
switch (type) {
@@ -735,18 +860,31 @@ do_sound_action (GsdMediaKeysManager *manager,
}
if (old_muted != new_muted) {
- gvc_mixer_stream_change_is_muted (manager->priv->stream, new_muted);
+ gvc_mixer_stream_change_is_muted (stream, new_muted);
sound_changed = TRUE;
}
if (old_vol != new_vol) {
- if (gvc_mixer_stream_set_volume (manager->priv->stream, new_vol) != FALSE) {
- gvc_mixer_stream_push_volume (manager->priv->stream);
+ if (gvc_mixer_stream_set_volume (stream, new_vol) != FALSE) {
+ gvc_mixer_stream_push_volume (stream);
sound_changed = TRUE;
}
}
- update_dialog (manager, new_vol, new_muted, sound_changed, quiet);
+ update_dialog (manager, stream, new_vol, new_muted, sound_changed, quiet);
+}
+
+static void
+sound_theme_changed (GtkSettings *settings,
+ GParamSpec *pspec,
+ GsdMediaKeysManager *manager)
+{
+ char *theme_name;
+
+ g_object_get (G_OBJECT (manager->priv->gtksettings), "gtk-sound-theme-name", &theme_name, NULL);
+ if (theme_name)
+ ca_context_change_props (manager->priv->ca, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL);
+ g_free (theme_name);
}
static void
@@ -786,6 +924,18 @@ on_control_default_sink_changed (GvcMixerControl *control,
update_default_sink (manager);
}
+#ifdef HAVE_GUDEV
+static gboolean
+remove_stream (gpointer key,
+ gpointer value,
+ gpointer id)
+{
+ if (GPOINTER_TO_UINT (value) == GPOINTER_TO_UINT (id))
+ return TRUE;
+ return FALSE;
+}
+#endif /* HAVE_GUDEV */
+
static void
on_control_stream_removed (GvcMixerControl *control,
guint id,
@@ -797,6 +947,10 @@ on_control_stream_removed (GvcMixerControl *control,
manager->priv->stream = NULL;
}
}
+
+#ifdef HAVE_GUDEV
+ g_hash_table_foreach_remove (manager->priv->streams, (GHRFunc) remove_stream, GUINT_TO_POINTER (id));
+#endif
}
static void
@@ -1426,12 +1580,13 @@ do_keyboard_brightness_action (GsdMediaKeysManager *manager,
static gboolean
do_action (GsdMediaKeysManager *manager,
+ guint deviceid,
MediaKeyType type,
gint64 timestamp)
{
char *cmd;
- g_debug ("Launching action for key type '%d'", type);
+ g_debug ("Launching action for key type '%d' (on device id %d)", type, deviceid);
switch (type) {
case TOUCHPAD_KEY:
@@ -1446,16 +1601,16 @@ do_action (GsdMediaKeysManager *manager,
case MUTE_KEY:
case VOLUME_DOWN_KEY:
case VOLUME_UP_KEY:
- do_sound_action (manager, type, FALSE);
+ do_sound_action (manager, deviceid, type, FALSE);
break;
case MUTE_QUIET_KEY:
- do_sound_action (manager, MUTE_KEY, TRUE);
+ do_sound_action (manager, deviceid, MUTE_KEY, TRUE);
break;
case VOLUME_DOWN_QUIET_KEY:
- do_sound_action (manager, VOLUME_DOWN_KEY, TRUE);
+ do_sound_action (manager, deviceid, VOLUME_DOWN_KEY, TRUE);
break;
case VOLUME_UP_QUIET_KEY:
- do_sound_action (manager, VOLUME_UP_KEY, TRUE);
+ do_sound_action (manager, deviceid, VOLUME_UP_KEY, TRUE);
break;
case LOGOUT_KEY:
do_logout_action (manager);
@@ -1571,61 +1726,71 @@ do_action (GsdMediaKeysManager *manager,
}
static GdkScreen *
-acme_get_screen_from_event (GsdMediaKeysManager *manager,
- XAnyEvent *xanyev)
+acme_get_screen_from_root (GsdMediaKeysManager *manager,
+ Window root)
{
- GdkWindow *window;
- GdkScreen *screen;
GSList *l;
/* Look for which screen we're receiving events */
for (l = manager->priv->screens; l != NULL; l = l->next) {
- screen = (GdkScreen *) l->data;
- window = gdk_screen_get_root_window (screen);
+ GdkScreen *screen = (GdkScreen *) l->data;
+ GdkWindow *window = gdk_screen_get_root_window (screen);
- if (GDK_WINDOW_XID (window) == xanyev->window) {
+ if (GDK_WINDOW_XID (window) == root)
return screen;
- }
}
return NULL;
}
static GdkFilterReturn
-acme_filter_events (GdkXEvent *xevent,
+acme_filter_events (XEvent *xevent,
GdkEvent *event,
GsdMediaKeysManager *manager)
{
- XEvent *xev = (XEvent *) xevent;
- XAnyEvent *xany = (XAnyEvent *) xevent;
- int i;
+ XIEvent *xiev;
+ XIDeviceEvent *xev;
+ XGenericEventCookie *cookie;
+ guint i;
+ guint deviceid;
/* verify we have a key event */
- if (xev->type != KeyPress && xev->type != KeyRelease) {
- return GDK_FILTER_CONTINUE;
- }
+ if (xevent->type != GenericEvent)
+ return GDK_FILTER_CONTINUE;
+ cookie = &xevent->xcookie;
+ if (cookie->extension != manager->priv->opcode)
+ return GDK_FILTER_CONTINUE;
+
+ xiev = (XIEvent *) xevent->xcookie.data;
+
+ if (xiev->evtype != XI_KeyPress &&
+ xiev->evtype != XI_KeyRelease)
+ return GDK_FILTER_CONTINUE;
+
+ xev = (XIDeviceEvent *) xiev;
+
+ deviceid = xev->sourceid;
for (i = 0; i < HANDLED_KEYS; i++) {
- if (match_key (keys[i].key, xev)) {
+ if (match_xi2_key (keys[i].key, xev)) {
switch (keys[i].key_type) {
case VOLUME_DOWN_KEY:
case VOLUME_UP_KEY:
case VOLUME_DOWN_QUIET_KEY:
case VOLUME_UP_QUIET_KEY:
/* auto-repeatable keys */
- if (xev->type != KeyPress) {
+ if (xiev->evtype != XI_KeyPress)
return GDK_FILTER_CONTINUE;
- }
break;
default:
- if (xev->type != KeyRelease) {
+ if (xiev->evtype != XI_KeyRelease) {
return GDK_FILTER_CONTINUE;
}
}
- manager->priv->current_screen = acme_get_screen_from_event (manager, xany);
+ manager->priv->current_screen = acme_get_screen_from_root (manager, xev->root);
- if (do_action (manager, keys[i].key_type, xev->xkey.time) == FALSE) {
+ if (do_action (manager, deviceid, keys[i].key_type, xev->time) == FALSE) {
return GDK_FILTER_REMOVE;
} else {
return GDK_FILTER_CONTINUE;
@@ -1661,6 +1826,7 @@ static gboolean
start_media_keys_idle_cb (GsdMediaKeysManager *manager)
{
GSList *l;
+ char *theme_name;
g_debug ("Starting media_keys manager");
gnome_settings_profile_start (NULL);
@@ -1668,6 +1834,20 @@ start_media_keys_idle_cb (GsdMediaKeysManager *manager)
g_signal_connect (G_OBJECT (manager->priv->settings), "changed",
G_CALLBACK (update_kbd_cb), manager);
+ /* Sound events */
+ ca_context_create (&manager->priv->ca);
+ ca_context_set_driver (manager->priv->ca, "pulse");
+ ca_context_change_props (manager->priv->ca, 0,
+ CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
+ NULL);
+ manager->priv->gtksettings = gtk_settings_get_for_screen (gdk_screen_get_default ());
+ g_object_get (G_OBJECT (manager->priv->gtksettings), "gtk-sound-theme-name", &theme_name, NULL);
+ if (theme_name)
+ ca_context_change_props (manager->priv->ca, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL);
+ g_free (theme_name);
+ g_signal_connect (manager->priv->gtksettings, "notify::gtk-sound-theme-name",
+ G_CALLBACK (sound_theme_changed), manager);
+
/* for the power plugin interface code */
manager->priv->power_settings = g_settings_new (SETTINGS_POWER_DIR);
@@ -1711,8 +1891,20 @@ gboolean
gsd_media_keys_manager_start (GsdMediaKeysManager *manager,
GError **error)
{
+ const char * const subsystems[] = { "input", "usb", "sound", NULL };
+
gnome_settings_profile_start (NULL);
+ if (supports_xinput2_devices (&manager->priv->opcode) == FALSE) {
+ g_debug ("No Xinput2 support, disabling plugin");
+ return TRUE;
+ }
+
+#ifdef HAVE_GUDEV
+ manager->priv->streams = g_hash_table_new (g_direct_hash, g_direct_equal);
+ manager->priv->udev_client = g_udev_client_new (subsystems);
+#endif
+
/* initialise Volume handler
*
* We do this one here to force checking gstreamer cache, etc.
@@ -1772,6 +1964,25 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
manager);
}
+ g_signal_handlers_disconnect_by_func (manager->priv->gtksettings, sound_theme_changed, manager);
+ manager->priv->gtksettings = NULL;
+
+ if (manager->priv->ca) {
+ ca_context_destroy (manager->priv->ca);
+ manager->priv->ca = NULL;
+ }
+
+#ifdef HAVE_GUDEV
+ if (priv->streams) {
+ g_hash_table_destroy (priv->streams);
+ priv->streams = NULL;
+ }
+ if (priv->udev_client) {
+ g_object_unref (priv->udev_client);
+ priv->udev_client = NULL;
+ }
+#endif /* HAVE_GUDEV */
+
if (priv->settings) {
g_object_unref (priv->settings);
priv->settings = NULL;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]