Merge lp:~jm-leddy/ubuntu/precise/gnome-settings-daemon/micmute into lp:ubuntu/precise-proposed/gnome-settings-daemon

Proposed by James M. Leddy
Status: Merged
Merge reported by: Sebastien Bacher
Merged at revision: not available
Proposed branch: lp:~jm-leddy/ubuntu/precise/gnome-settings-daemon/micmute
Merge into: lp:ubuntu/precise-proposed/gnome-settings-daemon
Diff against target: 3533 lines (+3328/-16)
8 files modified
.pc/64_micmute.patch/plugins/media-keys/gsd-media-keys-manager.c (+2743/-0)
.pc/64_micmute.patch/plugins/media-keys/shortcuts-list.h (+152/-0)
.pc/applied-patches (+1/-0)
debian/changelog (+8/-0)
debian/patches/64_micmute.patch (+291/-0)
debian/patches/series (+1/-0)
plugins/media-keys/gsd-media-keys-manager.c (+130/-16)
plugins/media-keys/shortcuts-list.h (+2/-0)
To merge this branch: bzr merge lp:~jm-leddy/ubuntu/precise/gnome-settings-daemon/micmute
Reviewer Review Type Date Requested Status
Sebastien Bacher Needs Fixing
Review via email: mp+188676@code.launchpad.net

This proposal supersedes a proposal from 2013-10-01.

To post a comment you must log in.
Revision history for this message
Sebastien Bacher (seb128) wrote :

That got uploaded but apprently the update is making the notification not working anymore, we are going to need to revert (or drop the SRU rather)

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc/64_micmute.patch'
2=== added file '.pc/64_micmute.patch/.timestamp'
3=== added directory '.pc/64_micmute.patch/plugins'
4=== added directory '.pc/64_micmute.patch/plugins/media-keys'
5=== added file '.pc/64_micmute.patch/plugins/media-keys/gsd-media-keys-manager.c'
6--- .pc/64_micmute.patch/plugins/media-keys/gsd-media-keys-manager.c 1970-01-01 00:00:00 +0000
7+++ .pc/64_micmute.patch/plugins/media-keys/gsd-media-keys-manager.c 2013-10-01 18:19:26 +0000
8@@ -0,0 +1,2743 @@
9+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
10+ *
11+ * Copyright (C) 2001-2003 Bastien Nocera <hadess@hadess.net>
12+ * Copyright (C) 2006-2007 William Jon McCann <mccann@jhu.edu>
13+ *
14+ * This program is free software; you can redistribute it and/or modify
15+ * it under the terms of the GNU General Public License as published by
16+ * the Free Software Foundation; either version 2 of the License, or
17+ * (at your option) any later version.
18+ *
19+ * This program is distributed in the hope that it will be useful,
20+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+ * GNU General Public License for more details.
23+ *
24+ * You should have received a copy of the GNU General Public License
25+ * along with this program; if not, write to the Free Software
26+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27+ *
28+ */
29+
30+#include "config.h"
31+
32+#include <sys/types.h>
33+#include <sys/wait.h>
34+#include <stdlib.h>
35+#include <stdio.h>
36+#include <unistd.h>
37+#include <string.h>
38+#include <errno.h>
39+#include <math.h>
40+
41+#include <locale.h>
42+
43+#include <glib.h>
44+#include <glib/gi18n.h>
45+#include <gio/gio.h>
46+#include <gdk/gdk.h>
47+#include <gdk/gdkx.h>
48+#include <gtk/gtk.h>
49+#include <gio/gdesktopappinfo.h>
50+#include <gconf/gconf-client.h>
51+
52+#ifdef HAVE_GUDEV
53+#include <gudev/gudev.h>
54+#endif
55+
56+#include "gnome-settings-profile.h"
57+#include "gsd-marshal.h"
58+#include "gsd-media-keys-manager.h"
59+
60+#include "shortcuts-list.h"
61+#include "gsd-media-keys-window.h"
62+#include "gsd-input-helper.h"
63+#include "gsd-enums.h"
64+
65+#include <canberra.h>
66+#include <pulse/pulseaudio.h>
67+#include "gvc-mixer-control.h"
68+
69+#include <libnotify/notify.h>
70+
71+#define GSD_DBUS_PATH "/org/gnome/SettingsDaemon"
72+#define GSD_DBUS_NAME "org.gnome.SettingsDaemon"
73+#define GSD_MEDIA_KEYS_DBUS_PATH GSD_DBUS_PATH "/MediaKeys"
74+#define GSD_MEDIA_KEYS_DBUS_NAME GSD_DBUS_NAME ".MediaKeys"
75+
76+#define GNOME_SESSION_DBUS_NAME "org.gnome.SessionManager"
77+#define GNOME_SESSION_DBUS_PATH "/org/gnome/SessionManager"
78+#define GNOME_SESSION_DBUS_INTERFACE "org.gnome.SessionManager"
79+
80+#define GNOME_KEYRING_DBUS_NAME "org.gnome.keyring"
81+#define GNOME_KEYRING_DBUS_PATH "/org/gnome/keyring/daemon"
82+#define GNOME_KEYRING_DBUS_INTERFACE "org.gnome.keyring.Daemon"
83+
84+#define GCONF_BINDING_DIR "/desktop/gnome/keybindings"
85+
86+static const gchar introspection_xml[] =
87+"<node>"
88+" <interface name='org.gnome.SettingsDaemon.MediaKeys'>"
89+" <annotation name='org.freedesktop.DBus.GLib.CSymbol' value='gsd_media_keys_manager'/>"
90+" <method name='GrabMediaPlayerKeys'>"
91+" <arg name='application' direction='in' type='s'/>"
92+" <arg name='time' direction='in' type='u'/>"
93+" </method>"
94+" <method name='ReleaseMediaPlayerKeys'>"
95+" <arg name='application' direction='in' type='s'/>"
96+" </method>"
97+" <signal name='MediaPlayerKeyPressed'>"
98+" <arg name='application' type='s'/>"
99+" <arg name='key' type='s'/>"
100+" </signal>"
101+" </interface>"
102+"</node>";
103+
104+#define SETTINGS_INTERFACE_DIR "org.gnome.desktop.interface"
105+#define SETTINGS_POWER_DIR "org.gnome.settings-daemon.plugins.power"
106+#define SETTINGS_XSETTINGS_DIR "org.gnome.settings-daemon.plugins.xsettings"
107+#define SETTINGS_TOUCHPAD_DIR "org.gnome.settings-daemon.peripherals.touchpad"
108+#define TOUCHPAD_ENABLED_KEY "touchpad-enabled"
109+#define HIGH_CONTRAST "HighContrast"
110+
111+#define VOLUME_STEP 6 /* percents for one volume button press */
112+#define MAX_VOLUME 65536.0
113+
114+#define GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_MEDIA_KEYS_MANAGER, GsdMediaKeysManagerPrivate))
115+
116+typedef struct {
117+ char *application;
118+ char *name;
119+ guint32 time;
120+ guint watch_id;
121+} MediaPlayer;
122+
123+typedef struct {
124+ MediaKeyType key_type;
125+ const char *settings_key;
126+ const char *hard_coded;
127+ char *gconf_dir;
128+ char *custom_command;
129+ Key *key;
130+} MediaKey;
131+
132+struct GsdMediaKeysManagerPrivate
133+{
134+ /* Volume bits */
135+ GvcMixerControl *volume;
136+ GvcMixerStream *stream;
137+ ca_context *ca;
138+ GtkSettings *gtksettings;
139+#ifdef HAVE_GUDEV
140+ GHashTable *streams; /* key = X device ID, value = stream id */
141+ GUdevClient *udev_client;
142+#endif /* HAVE_GUDEV */
143+
144+ GtkWidget *dialog;
145+ GSettings *settings;
146+
147+ GPtrArray *keys;
148+ GConfClient *gconf;
149+ guint gconf_id;
150+
151+ /* HighContrast theme settings */
152+ GSettings *interface_settings;
153+ char *icon_theme;
154+ char *gtk_theme;
155+
156+ /* Power stuff */
157+ GSettings *power_settings;
158+ GDBusProxy *upower_proxy;
159+ GDBusProxy *power_screen_proxy;
160+ GDBusProxy *power_keyboard_proxy;
161+
162+ /* Multihead stuff */
163+ GdkScreen *current_screen;
164+ GSList *screens;
165+ int opcode;
166+
167+ GList *media_players;
168+
169+ GDBusNodeInfo *introspection_data;
170+ GDBusConnection *connection;
171+ GCancellable *bus_cancellable;
172+ GDBusProxy *xrandr_proxy;
173+ GCancellable *cancellable;
174+
175+ guint start_idle_id;
176+
177+ /* Ubuntu notifications */
178+ NotifyNotification *volume_notification;
179+ NotifyNotification *brightness_notification;
180+ NotifyNotification *kb_backlight_notification;
181+};
182+
183+static void gsd_media_keys_manager_class_init (GsdMediaKeysManagerClass *klass);
184+static void gsd_media_keys_manager_init (GsdMediaKeysManager *media_keys_manager);
185+static void gsd_media_keys_manager_finalize (GObject *object);
186+static void register_manager (GsdMediaKeysManager *manager);
187+G_DEFINE_TYPE (GsdMediaKeysManager, gsd_media_keys_manager, G_TYPE_OBJECT)
188+
189+static gpointer manager_object = NULL;
190+
191+#define NOTIFY_CAP_PRIVATE_SYNCHRONOUS "x-canonical-private-synchronous"
192+#define NOTIFY_CAP_PRIVATE_ICON_ONLY "x-canonical-private-icon-only"
193+#define NOTIFY_HINT_TRUE "true"
194+
195+typedef struct {
196+ GsdMediaKeysManager *manager;
197+ MediaKeyType type;
198+ guint old_percentage;
199+
200+} GsdBrightnessActionData;
201+
202+static const char *volume_icons[] = {
203+ "notification-audio-volume-muted",
204+ "notification-audio-volume-low",
205+ "notification-audio-volume-medium",
206+ "notification-audio-volume-high",
207+ NULL
208+};
209+
210+static const char *brightness_icons[] = {
211+ "notification-display-brightness-off",
212+ "notification-display-brightness-low",
213+ "notification-display-brightness-medium",
214+ "notification-display-brightness-high",
215+ "notification-display-brightness-full",
216+ NULL
217+};
218+
219+static const char *kb_backlight_icons[] = {
220+ "notification-keyboard-brightness-off",
221+ "notification-keyboard-brightness-low",
222+ "notification-keyboard-brightness-medium",
223+ "notification-keyboard-brightness-high",
224+ "notification-keyboard-brightness-full",
225+ NULL
226+};
227+
228+static const char *
229+calculate_icon_name (gint value, const char **icon_names)
230+{
231+ value = CLAMP (value, 0, 100);
232+ gint length = g_strv_length (icon_names);
233+ gint s = (length - 1) * value / 100 + 1;
234+ s = CLAMP (s, 1, length - 1);
235+
236+ return icon_names[s];
237+}
238+
239+static gboolean
240+ubuntu_osd_notification_is_supported (void)
241+{
242+ GList *caps;
243+ gboolean has_cap;
244+
245+ caps = notify_get_server_caps ();
246+ has_cap = (g_list_find_custom (caps, NOTIFY_CAP_PRIVATE_SYNCHRONOUS, (GCompareFunc) g_strcmp0) != NULL);
247+ g_list_foreach (caps, (GFunc) g_free, NULL);
248+ g_list_free (caps);
249+
250+ return has_cap;
251+}
252+
253+static gboolean
254+ubuntu_osd_notification_show_icon (const char *icon,
255+ const char *hint)
256+{
257+ if (!ubuntu_osd_notification_is_supported ())
258+ return FALSE;
259+
260+ NotifyNotification *notification = notify_notification_new (" ", "", icon);
261+ notify_notification_set_hint_string (notification, NOTIFY_CAP_PRIVATE_SYNCHRONOUS, hint);
262+ notify_notification_set_hint_string (notification, NOTIFY_CAP_PRIVATE_ICON_ONLY, NOTIFY_HINT_TRUE);
263+
264+ gboolean res = notify_notification_show (notification, NULL);
265+ g_object_unref (notification);
266+
267+ return res;
268+}
269+
270+static gboolean
271+ubuntu_osd_do_notification (NotifyNotification **notification,
272+ const char *hint,
273+ gint value,
274+ gboolean muted,
275+ const char **icon_names)
276+{
277+ if (!ubuntu_osd_notification_is_supported ())
278+ return FALSE;
279+
280+ if (!*notification) {
281+ *notification = notify_notification_new (" ", "", NULL);
282+ notify_notification_set_hint_string (*notification, NOTIFY_CAP_PRIVATE_SYNCHRONOUS, hint);
283+ }
284+
285+ value = CLAMP (value, -1, 101);
286+ const char *icon = muted ? icon_names[0] : calculate_icon_name (value, icon_names);
287+ notify_notification_set_hint_int32 (*notification, "value", value);
288+ notify_notification_update (*notification, " ", "", icon);
289+
290+ return notify_notification_show (*notification, NULL);
291+}
292+
293+static gboolean
294+ubuntu_osd_notification_show_volume (GsdMediaKeysManager *manager,
295+ gint value,
296+ gboolean muted)
297+{
298+ return ubuntu_osd_do_notification (&manager->priv->volume_notification,
299+ "volume", value, muted, volume_icons);
300+}
301+
302+static gboolean
303+ubuntu_osd_notification_show_brightness (GsdMediaKeysManager *manager,
304+ gint value)
305+{
306+ return ubuntu_osd_do_notification (&manager->priv->brightness_notification,
307+ "brightness", value, value <= 0, brightness_icons);
308+}
309+
310+static gboolean
311+ubuntu_osd_notification_show_kb_backlight (GsdMediaKeysManager *manager,
312+ gint value)
313+{
314+ return ubuntu_osd_do_notification (&manager->priv->kb_backlight_notification,
315+ "keyboard", value, value <= 0, kb_backlight_icons);
316+}
317+
318+static void
319+init_screens (GsdMediaKeysManager *manager)
320+{
321+ GdkDisplay *display;
322+ int i;
323+
324+ display = gdk_display_get_default ();
325+ for (i = 0; i < gdk_display_get_n_screens (display); i++) {
326+ GdkScreen *screen;
327+
328+ screen = gdk_display_get_screen (display, i);
329+ if (screen == NULL) {
330+ continue;
331+ }
332+ manager->priv->screens = g_slist_append (manager->priv->screens, screen);
333+ }
334+
335+ manager->priv->current_screen = manager->priv->screens->data;
336+}
337+
338+static void
339+media_key_free (MediaKey *key)
340+{
341+ if (key == NULL)
342+ return;
343+ g_free (key->gconf_dir);
344+ g_free (key->custom_command);
345+ free_key (key->key);
346+ g_free (key);
347+}
348+
349+static char *
350+get_term_command (GsdMediaKeysManager *manager)
351+{
352+ char *cmd_term, *cmd_args;;
353+ char *cmd = NULL;
354+ GSettings *settings;
355+
356+ settings = g_settings_new ("org.gnome.desktop.default-applications.terminal");
357+ cmd_term = g_settings_get_string (settings, "exec");
358+ if (cmd_term[0] == '\0')
359+ cmd_term = g_strdup ("gnome-terminal");
360+
361+ cmd_args = g_settings_get_string (settings, "exec-arg");
362+ if (strcmp (cmd_term, "") != 0) {
363+ cmd = g_strdup_printf ("%s %s -e", cmd_term, cmd_args);
364+ } else {
365+ cmd = g_strdup_printf ("%s -e", cmd_term);
366+ }
367+
368+ g_free (cmd_args);
369+ g_free (cmd_term);
370+ g_object_unref (settings);
371+
372+ return cmd;
373+}
374+
375+static char **
376+get_keyring_env (GsdMediaKeysManager *manager)
377+{
378+ GError *error = NULL;
379+ GVariant *variant, *item;
380+ GVariantIter *iter;
381+ char **envp;
382+
383+ variant = g_dbus_connection_call_sync (manager->priv->connection,
384+ GNOME_KEYRING_DBUS_NAME,
385+ GNOME_KEYRING_DBUS_PATH,
386+ GNOME_KEYRING_DBUS_INTERFACE,
387+ "GetEnvironment",
388+ NULL,
389+ NULL,
390+ G_DBUS_CALL_FLAGS_NONE,
391+ -1,
392+ NULL,
393+ &error);
394+ if (variant == NULL) {
395+ g_warning ("Failed to call GetEnvironment on keyring daemon: %s", error->message);
396+ g_error_free (error);
397+ return NULL;
398+ }
399+
400+ envp = g_get_environ ();
401+
402+ g_variant_get (variant, "(a{ss})", &iter);
403+
404+ while ((item = g_variant_iter_next_value (iter))) {
405+ char *key;
406+ char *value;
407+
408+ g_variant_get (item,
409+ "{ss}",
410+ &key,
411+ &value);
412+
413+ envp = g_environ_setenv (envp, key, value, TRUE);
414+
415+ g_variant_unref (item);
416+ g_free (key);
417+ g_free (value);
418+ }
419+
420+ g_variant_iter_free (iter);
421+ g_variant_unref (variant);
422+
423+ return envp;
424+}
425+
426+static void
427+execute (GsdMediaKeysManager *manager,
428+ char *cmd,
429+ gboolean need_term)
430+{
431+ gboolean retval;
432+ char **argv;
433+ int argc;
434+ char *exec;
435+ char *term = NULL;
436+ GError *error = NULL;
437+
438+ retval = FALSE;
439+
440+ if (need_term)
441+ term = get_term_command (manager);
442+
443+ if (term) {
444+ exec = g_strdup_printf ("%s %s", term, cmd);
445+ g_free (term);
446+ } else {
447+ exec = g_strdup (cmd);
448+ }
449+
450+ if (g_shell_parse_argv (exec, &argc, &argv, NULL)) {
451+ char **envp;
452+
453+ envp = get_keyring_env (manager);
454+
455+ retval = g_spawn_async (g_get_home_dir (),
456+ argv,
457+ envp,
458+ G_SPAWN_SEARCH_PATH,
459+ NULL,
460+ NULL,
461+ NULL,
462+ &error);
463+
464+ g_strfreev (argv);
465+ g_strfreev (envp);
466+ }
467+
468+ if (retval == FALSE) {
469+ g_warning ("Couldn't execute command: %s: %s", exec, error->message);
470+ g_error_free (error);
471+ }
472+ g_free (exec);
473+}
474+
475+static void
476+dialog_init (GsdMediaKeysManager *manager)
477+{
478+ if (manager->priv->dialog != NULL
479+ && !gsd_osd_window_is_valid (GSD_OSD_WINDOW (manager->priv->dialog))) {
480+ gtk_widget_destroy (manager->priv->dialog);
481+ manager->priv->dialog = NULL;
482+ }
483+
484+ if (manager->priv->dialog == NULL) {
485+ manager->priv->dialog = gsd_media_keys_window_new ();
486+ }
487+}
488+
489+static void
490+print_key_parse_error (MediaKey *key,
491+ const char *str)
492+{
493+ if (str == NULL || *str == '\0')
494+ return;
495+ if (key->settings_key != NULL)
496+ g_debug ("Unable to parse key '%s' for GSettings entry '%s'", str, key->settings_key);
497+ else
498+ g_debug ("Unable to parse hard-coded key '%s'", key->hard_coded);
499+}
500+
501+static char *
502+get_key_string (GsdMediaKeysManager *manager,
503+ MediaKey *key)
504+{
505+ if (key->settings_key != NULL)
506+ return g_settings_get_string (manager->priv->settings, key->settings_key);
507+ else if (key->hard_coded != NULL)
508+ return g_strdup (key->hard_coded);
509+ else if (key->gconf_dir != NULL) {
510+ char *entry, *str;
511+
512+ entry = g_strdup_printf ("%s/binding", key->gconf_dir);
513+ str = gconf_client_get_string (manager->priv->gconf, entry, NULL);
514+ g_free (entry);
515+ return str;
516+ } else
517+ g_assert_not_reached ();
518+}
519+
520+static gboolean
521+grab_media_key (MediaKey *key,
522+ GsdMediaKeysManager *manager)
523+{
524+ char *tmp;
525+ gboolean need_flush;
526+
527+ need_flush = FALSE;
528+
529+ if (key->key != NULL) {
530+ need_flush = TRUE;
531+ grab_key_unsafe (key->key, FALSE, manager->priv->screens);
532+ }
533+
534+ free_key (key->key);
535+ key->key = NULL;
536+
537+ tmp = get_key_string (manager, key);
538+
539+ key->key = parse_key (tmp);
540+ if (key->key == NULL) {
541+ print_key_parse_error (key, tmp);
542+ g_free (tmp);
543+ return need_flush;
544+ }
545+
546+ grab_key_unsafe (key->key, TRUE, manager->priv->screens);
547+
548+ g_free (tmp);
549+
550+ return TRUE;
551+}
552+
553+static void
554+gsettings_changed_cb (GSettings *settings,
555+ const gchar *settings_key,
556+ GsdMediaKeysManager *manager)
557+{
558+ int i;
559+ gboolean need_flush = TRUE;
560+
561+ gdk_error_trap_push ();
562+
563+ /* Find the key that was modified */
564+ for (i = 0; i < manager->priv->keys->len; i++) {
565+ MediaKey *key;
566+
567+ key = g_ptr_array_index (manager->priv->keys, i);
568+
569+ /* Skip over hard-coded and GConf keys */
570+ if (key->settings_key == NULL)
571+ continue;
572+ if (strcmp (settings_key, key->settings_key) == 0) {
573+ if (grab_media_key (key, manager))
574+ need_flush = TRUE;
575+ break;
576+ }
577+ }
578+
579+ if (need_flush)
580+ gdk_flush ();
581+ if (gdk_error_trap_pop ())
582+ g_warning ("Grab failed for some keys, another application may already have access the them.");
583+}
584+
585+static char *
586+entry_get_string (GConfEntry *entry)
587+{
588+ GConfValue *value = gconf_entry_get_value (entry);
589+
590+ if (value == NULL || value->type != GCONF_VALUE_STRING) {
591+ return NULL;
592+ }
593+
594+ return g_strdup (gconf_value_get_string (value));
595+}
596+
597+static MediaKey *
598+media_key_new_for_gconf (GsdMediaKeysManager *manager,
599+ char *dir)
600+{
601+ GSList *list, *l;
602+ char *action, *binding;
603+ MediaKey *key;
604+
605+ /* Get entries for this binding */
606+ list = gconf_client_all_entries (manager->priv->gconf, dir, NULL);
607+ action = NULL;
608+ binding = NULL;
609+
610+ for (l = list; l != NULL; l = l->next) {
611+ GConfEntry *entry = l->data;
612+ char *key_name;
613+
614+ key_name = g_path_get_basename (gconf_entry_get_key (entry));
615+
616+ if (key_name == NULL) {
617+ /* ignore entry */
618+ } else if (strcmp (key_name, "action") == 0) {
619+ action = entry_get_string (entry);
620+ } else if (strcmp (key_name, "binding") == 0) {
621+ binding = entry_get_string (entry);
622+ }
623+
624+ g_free (key_name);
625+ gconf_entry_free (entry);
626+ }
627+
628+ g_slist_free (list);
629+
630+ if (action == NULL && binding == NULL) {
631+ g_debug ("Key binding (%s) is incomplete", dir);
632+ return NULL;
633+ }
634+ g_free (binding);
635+
636+ key = g_new0 (MediaKey, 1);
637+ key->key_type = CUSTOM_KEY;
638+ key->gconf_dir = dir;
639+ key->custom_command = action;
640+
641+ return key;
642+}
643+
644+static void
645+gconf_changed_cb (GConfClient *client,
646+ guint cnxn_id,
647+ GConfEntry *entry,
648+ GsdMediaKeysManager *manager)
649+{
650+ char *gconf_key, **key_elems;
651+ int i;
652+ MediaKey *key;
653+
654+ g_return_if_fail (entry != NULL);
655+ g_return_if_fail (entry->key[0] == '/');
656+
657+ /* Look for the dir that changed, thus the MediaKey */
658+ key_elems = g_strsplit (entry->key + 1, "/", -1);
659+ if (key_elems == NULL ||
660+ (g_strv_length (key_elems) != 4 &&
661+ g_strv_length (key_elems) != 5)) {
662+ g_warning ("Unexpected GConf notification for key '%s'", entry->key);
663+ g_strfreev (key_elems);
664+ return;
665+ }
666+
667+ if (g_strv_length (key_elems) == 5 &&
668+ g_str_equal (key_elems[4], "binding") == FALSE &&
669+ g_str_equal (key_elems[4], "action") == FALSE) {
670+ g_debug ("Not interested in notification for key '%s'", entry->key);
671+ g_strfreev (key_elems);
672+ return;
673+ }
674+ gconf_key = g_strdup_printf ("/%s/%s/%s/%s",
675+ key_elems[0],
676+ key_elems[1],
677+ key_elems[2],
678+ key_elems[3]);
679+ g_strfreev (key_elems);
680+
681+ g_debug ("Got notification for key '%s' (dir: '%s')",
682+ entry->key, gconf_key);
683+
684+ /* Remove the existing key */
685+ for (i = 0; i < manager->priv->keys->len; i++) {
686+ key = g_ptr_array_index (manager->priv->keys, i);
687+
688+ if (key->gconf_dir == NULL)
689+ continue;
690+ if (strcmp (key->gconf_dir, gconf_key) == 0) {
691+ if (key->key) {
692+ gdk_error_trap_push ();
693+
694+ grab_key_unsafe (key->key, FALSE, manager->priv->screens);
695+
696+ gdk_flush ();
697+ if (gdk_error_trap_pop ())
698+ g_warning ("Ungrab failed for GConf key '%s'", gconf_key);
699+ }
700+ g_ptr_array_remove_index_fast (manager->priv->keys, i);
701+ break;
702+ }
703+ }
704+
705+ /* And create a new one! */
706+ key = media_key_new_for_gconf (manager, gconf_key);
707+ if (key) {
708+ g_ptr_array_add (manager->priv->keys, key);
709+
710+ gdk_error_trap_push ();
711+
712+ grab_media_key (key, manager);
713+
714+ gdk_flush ();
715+ if (gdk_error_trap_pop ())
716+ g_warning ("Grab failed for GConf key '%s'", key->gconf_dir);
717+ } else {
718+ g_free (gconf_key);
719+ }
720+}
721+
722+
723+static void
724+add_key (GsdMediaKeysManager *manager, guint i)
725+{
726+ MediaKey *key;
727+
728+ key = g_new0 (MediaKey, 1);
729+ key->key_type = media_keys[i].key_type;
730+ key->settings_key = media_keys[i].settings_key;
731+ key->hard_coded = media_keys[i].hard_coded;
732+
733+ g_ptr_array_add (manager->priv->keys, key);
734+
735+ grab_media_key (key, manager);
736+}
737+
738+static void
739+init_kbd (GsdMediaKeysManager *manager)
740+{
741+ int i;
742+ GSList *list, *l;
743+
744+ gnome_settings_profile_start (NULL);
745+
746+ gdk_error_trap_push ();
747+
748+ manager->priv->keys = g_ptr_array_new_with_free_func ((GDestroyNotify) media_key_free);
749+
750+ /* Media keys
751+ * Add hard-coded shortcuts first so that they can't be preempted */
752+ for (i = 0; i < G_N_ELEMENTS (media_keys); i++) {
753+ if (media_keys[i].hard_coded)
754+ add_key (manager, i);
755+ }
756+ for (i = 0; i < G_N_ELEMENTS (media_keys); i++) {
757+ if (media_keys[i].hard_coded == NULL)
758+ add_key (manager, i);
759+ }
760+
761+ /* Custom shortcuts */
762+ list = gconf_client_all_dirs (manager->priv->gconf, GCONF_BINDING_DIR, NULL);
763+ for (l = list; l != NULL; l = l->next) {
764+ MediaKey *key;
765+
766+ key = media_key_new_for_gconf (manager, l->data);
767+ if (!key) {
768+ g_free (l->data);
769+ continue;
770+ }
771+ g_ptr_array_add (manager->priv->keys, key);
772+
773+ grab_media_key (key, manager);
774+ }
775+ g_slist_free (list);
776+
777+ gdk_flush ();
778+ if (gdk_error_trap_pop ())
779+ g_warning ("Grab failed for some keys, another application may already have access the them.");
780+
781+ gnome_settings_profile_end (NULL);
782+}
783+
784+static void
785+dialog_show (GsdMediaKeysManager *manager)
786+{
787+ int orig_w;
788+ int orig_h;
789+ int screen_w;
790+ int screen_h;
791+ int x;
792+ int y;
793+ GtkRequisition win_req;
794+ GdkRectangle geometry;
795+ int monitor;
796+
797+ gtk_window_set_screen (GTK_WINDOW (manager->priv->dialog),
798+ manager->priv->current_screen);
799+
800+ /*
801+ * get the window size
802+ * if the window hasn't been mapped, it doesn't necessarily
803+ * know its true size, yet, so we need to jump through hoops
804+ */
805+ gtk_window_get_default_size (GTK_WINDOW (manager->priv->dialog), &orig_w, &orig_h);
806+ gtk_widget_size_request (manager->priv->dialog, &win_req);
807+
808+ if (win_req.width > orig_w)
809+ orig_w = win_req.width;
810+ if (win_req.height > orig_h)
811+ orig_h = win_req.height;
812+
813+ monitor = gdk_screen_get_primary_monitor (manager->priv->current_screen);
814+
815+ gdk_screen_get_monitor_geometry (manager->priv->current_screen,
816+ monitor,
817+ &geometry);
818+
819+ screen_w = geometry.width;
820+ screen_h = geometry.height;
821+
822+ x = ((screen_w - orig_w) / 2) + geometry.x;
823+ y = geometry.y + (screen_h / 2) + (screen_h / 2 - orig_h) / 2;
824+
825+ gtk_window_move (GTK_WINDOW (manager->priv->dialog), x, y);
826+
827+ gtk_widget_show (manager->priv->dialog);
828+
829+ gdk_display_sync (gdk_screen_get_display (manager->priv->current_screen));
830+}
831+
832+static void
833+launch_app (GAppInfo *app_info,
834+ gint64 timestamp)
835+{
836+ GError *error = NULL;
837+ GdkAppLaunchContext *launch_context;
838+
839+ /* setup the launch context so the startup notification is correct */
840+ launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ());
841+ gdk_app_launch_context_set_timestamp (launch_context, timestamp);
842+
843+ if (!g_app_info_launch (app_info, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error)) {
844+ g_warning ("Could not launch '%s': %s",
845+ g_app_info_get_commandline (app_info),
846+ error->message);
847+ g_error_free (error);
848+ }
849+ g_object_unref (launch_context);
850+}
851+
852+static void
853+do_url_action (GsdMediaKeysManager *manager,
854+ const char *scheme,
855+ gint64 timestamp)
856+{
857+ GAppInfo *app_info;
858+
859+ app_info = g_app_info_get_default_for_uri_scheme (scheme);
860+ if (app_info != NULL) {
861+ launch_app (app_info, timestamp);
862+ g_object_unref (app_info);
863+ } else {
864+ g_warning ("Could not find default application for '%s' scheme", scheme);
865+ }
866+}
867+
868+static void
869+do_media_action (GsdMediaKeysManager *manager,
870+ gint64 timestamp)
871+{
872+ GAppInfo *app_info;
873+
874+ app_info = g_app_info_get_default_for_type ("audio/x-vorbis+ogg", FALSE);
875+ if (app_info != NULL) {
876+ launch_app (app_info, timestamp);
877+ g_object_unref (app_info);
878+ } else {
879+ g_warning ("Could not find default application for '%s' mime-type", "audio/x-vorbis+ogg");
880+ }
881+}
882+
883+static void
884+gnome_session_shutdown (GsdMediaKeysManager *manager)
885+{
886+ GError *error = NULL;
887+ GVariant *variant;
888+
889+ /* Shouldn't happen, but you never know */
890+ if (manager->priv->connection == NULL) {
891+ execute (manager, "gnome-session-quit --logout", FALSE);
892+ return;
893+ }
894+
895+ variant = g_dbus_connection_call_sync (manager->priv->connection,
896+ GNOME_SESSION_DBUS_NAME,
897+ GNOME_SESSION_DBUS_PATH,
898+ GNOME_SESSION_DBUS_INTERFACE,
899+ "Shutdown",
900+ NULL,
901+ NULL,
902+ G_DBUS_CALL_FLAGS_NONE,
903+ -1,
904+ NULL,
905+ &error);
906+ if (variant == NULL) {
907+ g_warning ("Failed to call Shutdown on session manager: %s", error->message);
908+ g_error_free (error);
909+ return;
910+ }
911+ g_variant_unref (variant);
912+}
913+
914+static void
915+do_logout_action (GsdMediaKeysManager *manager)
916+{
917+ execute (manager, "gnome-session-quit --logout", FALSE);
918+}
919+
920+static void
921+do_eject_action_cb (GDrive *drive,
922+ GAsyncResult *res,
923+ GsdMediaKeysManager *manager)
924+{
925+ g_drive_eject_with_operation_finish (drive, res, NULL);
926+}
927+
928+#define NO_SCORE 0
929+#define SCORE_CAN_EJECT 50
930+#define SCORE_HAS_MEDIA 100
931+static void
932+do_eject_action (GsdMediaKeysManager *manager)
933+{
934+ GList *drives, *l;
935+ GDrive *fav_drive;
936+ guint score;
937+ GVolumeMonitor *volume_monitor;
938+
939+ volume_monitor = g_volume_monitor_get ();
940+
941+ /* Find the best drive to eject */
942+ fav_drive = NULL;
943+ score = NO_SCORE;
944+ drives = g_volume_monitor_get_connected_drives (volume_monitor);
945+ for (l = drives; l != NULL; l = l->next) {
946+ GDrive *drive = l->data;
947+
948+ if (g_drive_can_eject (drive) == FALSE)
949+ continue;
950+ if (g_drive_is_media_removable (drive) == FALSE)
951+ continue;
952+ if (score < SCORE_CAN_EJECT) {
953+ fav_drive = drive;
954+ score = SCORE_CAN_EJECT;
955+ }
956+ if (g_drive_has_media (drive) == FALSE)
957+ continue;
958+ if (score < SCORE_HAS_MEDIA) {
959+ fav_drive = drive;
960+ score = SCORE_HAS_MEDIA;
961+ break;
962+ }
963+ }
964+
965+ /* Show the dialogue */
966+ if (!ubuntu_osd_notification_show_icon ("notification-device-eject", "Eject")) {
967+ dialog_init (manager);
968+ gsd_media_keys_window_set_action_custom (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
969+ "media-eject-symbolic",
970+ FALSE);
971+ dialog_show (manager);
972+ }
973+
974+ /* Clean up the drive selection and exit if no suitable
975+ * drives are found */
976+ if (fav_drive != NULL)
977+ fav_drive = g_object_ref (fav_drive);
978+
979+ g_list_foreach (drives, (GFunc) g_object_unref, NULL);
980+ if (fav_drive == NULL)
981+ return;
982+
983+ /* Eject! */
984+ g_drive_eject_with_operation (fav_drive, G_MOUNT_UNMOUNT_FORCE,
985+ NULL, NULL,
986+ (GAsyncReadyCallback) do_eject_action_cb,
987+ manager);
988+ g_object_unref (fav_drive);
989+ g_object_unref (volume_monitor);
990+}
991+
992+static void
993+do_home_key_action (GsdMediaKeysManager *manager,
994+ gint64 timestamp)
995+{
996+ GFile *file;
997+ GError *error = NULL;
998+ char *uri;
999+
1000+ file = g_file_new_for_path (g_get_home_dir ());
1001+ uri = g_file_get_uri (file);
1002+ g_object_unref (file);
1003+
1004+ if (gtk_show_uri (NULL, uri, timestamp, &error) == FALSE) {
1005+ g_warning ("Failed to launch '%s': %s", uri, error->message);
1006+ g_error_free (error);
1007+ }
1008+ g_free (uri);
1009+}
1010+
1011+static void
1012+do_execute_desktop (GsdMediaKeysManager *manager,
1013+ const char *desktop,
1014+ gint64 timestamp)
1015+{
1016+ GDesktopAppInfo *app_info;
1017+
1018+ app_info = g_desktop_app_info_new (desktop);
1019+ if (app_info != NULL) {
1020+ launch_app (G_APP_INFO (app_info), timestamp);
1021+ g_object_unref (app_info);
1022+ } else {
1023+ g_warning ("Could not find application '%s'", desktop);
1024+ }
1025+}
1026+
1027+static void
1028+do_touchpad_osd_action (GsdMediaKeysManager *manager, gboolean state)
1029+{
1030+ if (!ubuntu_osd_notification_show_icon ((!state) ? "touchpad-disabled-symbolic" : "input-touchpad-symbolic", "Touchpad")) {
1031+ dialog_init (manager);
1032+ gsd_media_keys_window_set_action_custom (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1033+ state ? "input-touchpad-symbolic" : "touchpad-disabled-symbolic",
1034+ FALSE);
1035+ dialog_show (manager);
1036+ }
1037+}
1038+
1039+static void
1040+do_touchpad_action (GsdMediaKeysManager *manager)
1041+{
1042+ GSettings *settings;
1043+ gboolean state;
1044+
1045+ if (touchpad_is_present () == FALSE) {
1046+ do_touchpad_osd_action (manager, FALSE);
1047+ return;
1048+ }
1049+
1050+ settings = g_settings_new (SETTINGS_TOUCHPAD_DIR);
1051+ state = g_settings_get_boolean (settings, TOUCHPAD_ENABLED_KEY);
1052+
1053+ do_touchpad_osd_action (manager, !state);
1054+
1055+ g_settings_set_boolean (settings, TOUCHPAD_ENABLED_KEY, !state);
1056+ g_object_unref (settings);
1057+}
1058+
1059+static void
1060+update_dialog (GsdMediaKeysManager *manager,
1061+ GvcMixerStream *stream,
1062+ guint vol,
1063+ gboolean muted,
1064+ gboolean sound_changed,
1065+ gboolean quiet)
1066+{
1067+ if (ubuntu_osd_notification_show_volume (manager, vol, muted))
1068+ goto done;
1069+
1070+ vol = CLAMP (vol, 0, 100);
1071+
1072+ dialog_init (manager);
1073+ gsd_media_keys_window_set_volume_muted (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1074+ muted);
1075+ gsd_media_keys_window_set_volume_level (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), vol);
1076+ gsd_media_keys_window_set_action (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1077+ GSD_MEDIA_KEYS_WINDOW_ACTION_VOLUME);
1078+ dialog_show (manager);
1079+
1080+done:
1081+ if (quiet == FALSE && sound_changed != FALSE && muted == FALSE) {
1082+ ca_context_change_device (manager->priv->ca,
1083+ gvc_mixer_stream_get_name (stream));
1084+ ca_context_play (manager->priv->ca, 1,
1085+ CA_PROP_EVENT_ID, "audio-volume-change",
1086+ CA_PROP_EVENT_DESCRIPTION, "volume changed through key press",
1087+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
1088+ NULL);
1089+ }
1090+}
1091+
1092+#ifdef HAVE_GUDEV
1093+/* PulseAudio gives us /devices/... paths, when udev
1094+ * expects /sys/devices/... paths. */
1095+static GUdevDevice *
1096+get_udev_device_for_sysfs_path (GsdMediaKeysManager *manager,
1097+ const char *sysfs_path)
1098+{
1099+ char *path;
1100+ GUdevDevice *dev;
1101+
1102+ path = g_strdup_printf ("/sys%s", sysfs_path);
1103+ dev = g_udev_client_query_by_sysfs_path (manager->priv->udev_client, path);
1104+ g_free (path);
1105+
1106+ return dev;
1107+}
1108+
1109+static GvcMixerStream *
1110+get_stream_for_device_id (GsdMediaKeysManager *manager,
1111+ guint deviceid)
1112+{
1113+ char *devnode;
1114+ gpointer id_ptr;
1115+ GvcMixerStream *res;
1116+ GUdevDevice *dev, *parent;
1117+ GSList *sinks, *l;
1118+
1119+ id_ptr = g_hash_table_lookup (manager->priv->streams, GUINT_TO_POINTER (deviceid));
1120+ if (id_ptr != NULL) {
1121+ if (GPOINTER_TO_UINT (id_ptr) == (guint) -1)
1122+ return NULL;
1123+ else
1124+ return gvc_mixer_control_lookup_stream_id (manager->priv->volume, GPOINTER_TO_UINT (id_ptr));
1125+ }
1126+
1127+ devnode = xdevice_get_device_node (deviceid);
1128+ if (devnode == NULL) {
1129+ g_debug ("Could not find device node for XInput device %d", deviceid);
1130+ return NULL;
1131+ }
1132+
1133+ dev = g_udev_client_query_by_device_file (manager->priv->udev_client, devnode);
1134+ if (dev == NULL) {
1135+ g_debug ("Could not find udev device for device path '%s'", devnode);
1136+ g_free (devnode);
1137+ return NULL;
1138+ }
1139+ g_free (devnode);
1140+
1141+ if (g_strcmp0 (g_udev_device_get_property (dev, "ID_BUS"), "usb") != 0) {
1142+ g_debug ("Not handling XInput device %d, not USB", deviceid);
1143+ g_hash_table_insert (manager->priv->streams,
1144+ GUINT_TO_POINTER (deviceid),
1145+ GUINT_TO_POINTER ((guint) -1));
1146+ g_object_unref (dev);
1147+ return NULL;
1148+ }
1149+
1150+ parent = g_udev_device_get_parent_with_subsystem (dev, "usb", "usb_device");
1151+ if (parent == NULL) {
1152+ g_warning ("No USB device parent for XInput device %d even though it's USB", deviceid);
1153+ g_object_unref (dev);
1154+ return NULL;
1155+ }
1156+
1157+ res = NULL;
1158+ sinks = gvc_mixer_control_get_sinks (manager->priv->volume);
1159+ for (l = sinks; l; l = l->next) {
1160+ GvcMixerStream *stream = l->data;
1161+ const char *sysfs_path;
1162+ GUdevDevice *sink_dev, *sink_parent;
1163+
1164+ sysfs_path = gvc_mixer_stream_get_sysfs_path (stream);
1165+ sink_dev = get_udev_device_for_sysfs_path (manager, sysfs_path);
1166+ if (sink_dev == NULL)
1167+ continue;
1168+ sink_parent = g_udev_device_get_parent_with_subsystem (sink_dev, "usb", "usb_device");
1169+ g_object_unref (sink_dev);
1170+ if (sink_parent == NULL)
1171+ continue;
1172+
1173+ if (g_strcmp0 (g_udev_device_get_sysfs_path (sink_parent),
1174+ g_udev_device_get_sysfs_path (parent)) == 0) {
1175+ res = stream;
1176+ }
1177+ g_object_unref (sink_parent);
1178+ if (res != NULL)
1179+ break;
1180+ }
1181+
1182+ if (res)
1183+ g_hash_table_insert (manager->priv->streams,
1184+ GUINT_TO_POINTER (deviceid),
1185+ GUINT_TO_POINTER (gvc_mixer_stream_get_id (res)));
1186+ else
1187+ g_hash_table_insert (manager->priv->streams,
1188+ GUINT_TO_POINTER (deviceid),
1189+ GUINT_TO_POINTER ((guint) -1));
1190+
1191+ return res;
1192+}
1193+#endif /* HAVE_GUDEV */
1194+
1195+static void
1196+do_sound_action (GsdMediaKeysManager *manager,
1197+ guint deviceid,
1198+ int type,
1199+ gboolean quiet)
1200+{
1201+ GvcMixerStream *stream;
1202+ gboolean old_muted, new_muted;
1203+ guint old_vol, new_vol, norm_vol_step, osd_vol;
1204+ gboolean sound_changed;
1205+
1206+ /* Find the stream that corresponds to the device, if any */
1207+#ifdef HAVE_GUDEV
1208+ stream = get_stream_for_device_id (manager, deviceid);
1209+ if (stream == NULL)
1210+#endif /* HAVE_GUDEV */
1211+ stream = manager->priv->stream;
1212+ if (stream == NULL)
1213+ return;
1214+
1215+ norm_vol_step = PA_VOLUME_NORM * VOLUME_STEP / 100;
1216+
1217+ /* FIXME: this is racy */
1218+ new_vol = old_vol = gvc_mixer_stream_get_volume (stream);
1219+ new_muted = old_muted = gvc_mixer_stream_get_is_muted (stream);
1220+ sound_changed = FALSE;
1221+
1222+ switch (type) {
1223+ case MUTE_KEY:
1224+ new_muted = !old_muted;
1225+ break;
1226+ case VOLUME_DOWN_KEY:
1227+ if (old_vol <= norm_vol_step) {
1228+ new_vol = 0;
1229+ new_muted = TRUE;
1230+ } else {
1231+ new_vol = old_vol - norm_vol_step;
1232+ }
1233+ break;
1234+ case VOLUME_UP_KEY:
1235+ new_muted = FALSE;
1236+ /* When coming out of mute only increase the volume if it was 0 */
1237+ if (!old_muted || old_vol == 0)
1238+ new_vol = MIN (old_vol + norm_vol_step, MAX_VOLUME);
1239+ break;
1240+ }
1241+
1242+ if (old_muted != new_muted) {
1243+ gvc_mixer_stream_change_is_muted (stream, new_muted);
1244+ sound_changed = TRUE;
1245+ }
1246+
1247+ if (old_vol != new_vol) {
1248+ if (gvc_mixer_stream_set_volume (stream, new_vol) != FALSE) {
1249+ gvc_mixer_stream_push_volume (stream);
1250+ sound_changed = TRUE;
1251+ }
1252+ }
1253+
1254+ if (type == VOLUME_DOWN_KEY && old_vol == 0 && old_muted)
1255+ osd_vol = -1;
1256+ else if (type == VOLUME_UP_KEY && old_vol == PA_VOLUME_NORM && !old_muted)
1257+ osd_vol = 101;
1258+ else if (!new_muted)
1259+ osd_vol = (int) (100 * (double) new_vol / PA_VOLUME_NORM);
1260+ else
1261+ osd_vol = 0;
1262+
1263+ update_dialog (manager, stream, osd_vol, new_muted, sound_changed, quiet);
1264+}
1265+
1266+static void
1267+sound_theme_changed (GtkSettings *settings,
1268+ GParamSpec *pspec,
1269+ GsdMediaKeysManager *manager)
1270+{
1271+ char *theme_name;
1272+
1273+ g_object_get (G_OBJECT (manager->priv->gtksettings), "gtk-sound-theme-name", &theme_name, NULL);
1274+ if (theme_name)
1275+ ca_context_change_props (manager->priv->ca, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL);
1276+ g_free (theme_name);
1277+}
1278+
1279+static void
1280+update_default_sink (GsdMediaKeysManager *manager)
1281+{
1282+ GvcMixerStream *stream;
1283+
1284+ stream = gvc_mixer_control_get_default_sink (manager->priv->volume);
1285+ if (stream == manager->priv->stream)
1286+ return;
1287+
1288+ if (manager->priv->stream != NULL) {
1289+ g_object_unref (manager->priv->stream);
1290+ manager->priv->stream = NULL;
1291+ }
1292+
1293+ if (stream != NULL) {
1294+ manager->priv->stream = g_object_ref (stream);
1295+ } else {
1296+ g_warning ("Unable to get default sink");
1297+ }
1298+}
1299+
1300+static void
1301+on_control_state_changed (GvcMixerControl *control,
1302+ GvcMixerControlState new_state,
1303+ GsdMediaKeysManager *manager)
1304+{
1305+ update_default_sink (manager);
1306+}
1307+
1308+static void
1309+on_control_default_sink_changed (GvcMixerControl *control,
1310+ guint id,
1311+ GsdMediaKeysManager *manager)
1312+{
1313+ update_default_sink (manager);
1314+}
1315+
1316+#ifdef HAVE_GUDEV
1317+static gboolean
1318+remove_stream (gpointer key,
1319+ gpointer value,
1320+ gpointer id)
1321+{
1322+ if (GPOINTER_TO_UINT (value) == GPOINTER_TO_UINT (id))
1323+ return TRUE;
1324+ return FALSE;
1325+}
1326+#endif /* HAVE_GUDEV */
1327+
1328+static void
1329+on_control_stream_removed (GvcMixerControl *control,
1330+ guint id,
1331+ GsdMediaKeysManager *manager)
1332+{
1333+ if (manager->priv->stream != NULL) {
1334+ if (gvc_mixer_stream_get_id (manager->priv->stream) == id) {
1335+ g_object_unref (manager->priv->stream);
1336+ manager->priv->stream = NULL;
1337+ }
1338+ }
1339+
1340+#ifdef HAVE_GUDEV
1341+ g_hash_table_foreach_remove (manager->priv->streams, (GHRFunc) remove_stream, GUINT_TO_POINTER (id));
1342+#endif
1343+}
1344+
1345+static void
1346+free_media_player (MediaPlayer *player)
1347+{
1348+ if (player->watch_id > 0) {
1349+ g_bus_unwatch_name (player->watch_id);
1350+ player->watch_id = 0;
1351+ }
1352+ g_free (player->application);
1353+ g_free (player->name);
1354+ g_free (player);
1355+}
1356+
1357+static gint
1358+find_by_application (gconstpointer a,
1359+ gconstpointer b)
1360+{
1361+ return strcmp (((MediaPlayer *)a)->application, b);
1362+}
1363+
1364+static gint
1365+find_by_name (gconstpointer a,
1366+ gconstpointer b)
1367+{
1368+ return strcmp (((MediaPlayer *)a)->name, b);
1369+}
1370+
1371+static gint
1372+find_by_time (gconstpointer a,
1373+ gconstpointer b)
1374+{
1375+ return ((MediaPlayer *)a)->time < ((MediaPlayer *)b)->time;
1376+}
1377+
1378+static void
1379+name_vanished_handler (GDBusConnection *connection,
1380+ const gchar *name,
1381+ GsdMediaKeysManager *manager)
1382+{
1383+ GList *iter;
1384+
1385+ iter = g_list_find_custom (manager->priv->media_players,
1386+ name,
1387+ find_by_name);
1388+
1389+ if (iter != NULL) {
1390+ MediaPlayer *player;
1391+
1392+ player = iter->data;
1393+ g_debug ("Deregistering vanished %s (name: %s)", player->application, player->name);
1394+ free_media_player (player);
1395+ manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter);
1396+ }
1397+}
1398+
1399+/*
1400+ * Register a new media player. Most applications will want to call
1401+ * this with time = GDK_CURRENT_TIME. This way, the last registered
1402+ * player will receive media events. In some cases, applications
1403+ * may want to register with a lower priority (usually 1), to grab
1404+ * events only nobody is interested.
1405+ */
1406+static void
1407+gsd_media_keys_manager_grab_media_player_keys (GsdMediaKeysManager *manager,
1408+ const char *application,
1409+ const char *name,
1410+ guint32 time)
1411+{
1412+ GList *iter;
1413+ MediaPlayer *media_player;
1414+ guint watch_id;
1415+
1416+ if (time == GDK_CURRENT_TIME) {
1417+ GTimeVal tv;
1418+
1419+ g_get_current_time (&tv);
1420+ time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
1421+ }
1422+
1423+ iter = g_list_find_custom (manager->priv->media_players,
1424+ application,
1425+ find_by_application);
1426+
1427+ if (iter != NULL) {
1428+ if (((MediaPlayer *)iter->data)->time < time) {
1429+ MediaPlayer *player = iter->data;
1430+ free_media_player (player);
1431+ manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter);
1432+ } else {
1433+ return;
1434+ }
1435+ }
1436+
1437+ watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
1438+ name,
1439+ G_BUS_NAME_WATCHER_FLAGS_NONE,
1440+ NULL,
1441+ (GBusNameVanishedCallback) name_vanished_handler,
1442+ manager,
1443+ NULL);
1444+
1445+ g_debug ("Registering %s at %u", application, time);
1446+ media_player = g_new0 (MediaPlayer, 1);
1447+ media_player->application = g_strdup (application);
1448+ media_player->name = g_strdup (name);
1449+ media_player->time = time;
1450+ media_player->watch_id = watch_id;
1451+
1452+ manager->priv->media_players = g_list_insert_sorted (manager->priv->media_players,
1453+ media_player,
1454+ find_by_time);
1455+}
1456+
1457+static void
1458+gsd_media_keys_manager_release_media_player_keys (GsdMediaKeysManager *manager,
1459+ const char *application,
1460+ const char *name)
1461+{
1462+ GList *iter = NULL;
1463+
1464+ g_return_if_fail (application != NULL || name != NULL);
1465+
1466+ if (application != NULL) {
1467+ iter = g_list_find_custom (manager->priv->media_players,
1468+ application,
1469+ find_by_application);
1470+ }
1471+
1472+ if (iter == NULL && name != NULL) {
1473+ iter = g_list_find_custom (manager->priv->media_players,
1474+ name,
1475+ find_by_name);
1476+ }
1477+
1478+ if (iter != NULL) {
1479+ MediaPlayer *player;
1480+
1481+ player = iter->data;
1482+ g_debug ("Deregistering %s (name: %s)", application, player->name);
1483+ free_media_player (player);
1484+ manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter);
1485+ }
1486+}
1487+
1488+static gboolean
1489+gsd_media_player_key_pressed (GsdMediaKeysManager *manager,
1490+ const char *key)
1491+{
1492+ const char *application;
1493+ gboolean have_listeners;
1494+ GError *error = NULL;
1495+ MediaPlayer *player;
1496+
1497+ g_return_val_if_fail (key != NULL, FALSE);
1498+
1499+ g_debug ("Media key '%s' pressed", key);
1500+
1501+ have_listeners = (manager->priv->media_players != NULL);
1502+
1503+ if (!have_listeners) {
1504+ /* Popup a dialog with an (/) icon */
1505+ dialog_init (manager);
1506+ gsd_media_keys_window_set_action_custom (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1507+ "action-unavailable-symbolic",
1508+ FALSE);
1509+ dialog_show (manager);
1510+ return TRUE;
1511+ }
1512+
1513+ player = manager->priv->media_players->data;
1514+ application = player->application;
1515+
1516+ if (g_dbus_connection_emit_signal (manager->priv->connection,
1517+ player->name,
1518+ GSD_MEDIA_KEYS_DBUS_PATH,
1519+ GSD_MEDIA_KEYS_DBUS_NAME,
1520+ "MediaPlayerKeyPressed",
1521+ g_variant_new ("(ss)", application ? application : "", key),
1522+ &error) == FALSE) {
1523+ g_debug ("Error emitting signal: %s", error->message);
1524+ g_error_free (error);
1525+ }
1526+
1527+ return !have_listeners;
1528+}
1529+
1530+static void
1531+handle_method_call (GDBusConnection *connection,
1532+ const gchar *sender,
1533+ const gchar *object_path,
1534+ const gchar *interface_name,
1535+ const gchar *method_name,
1536+ GVariant *parameters,
1537+ GDBusMethodInvocation *invocation,
1538+ gpointer user_data)
1539+{
1540+ GsdMediaKeysManager *manager = (GsdMediaKeysManager *) user_data;
1541+
1542+ g_debug ("Calling method '%s' for media-keys", method_name);
1543+
1544+ if (g_strcmp0 (method_name, "ReleaseMediaPlayerKeys") == 0) {
1545+ const char *app_name;
1546+
1547+ g_variant_get (parameters, "(&s)", &app_name);
1548+ gsd_media_keys_manager_release_media_player_keys (manager, app_name, sender);
1549+ g_dbus_method_invocation_return_value (invocation, NULL);
1550+ } else if (g_strcmp0 (method_name, "GrabMediaPlayerKeys") == 0) {
1551+ const char *app_name;
1552+ guint32 time;
1553+
1554+ g_variant_get (parameters, "(&su)", &app_name, &time);
1555+ gsd_media_keys_manager_grab_media_player_keys (manager, app_name, sender, time);
1556+ g_dbus_method_invocation_return_value (invocation, NULL);
1557+ }
1558+}
1559+
1560+static const GDBusInterfaceVTable interface_vtable =
1561+{
1562+ handle_method_call,
1563+ NULL, /* Get Property */
1564+ NULL, /* Set Property */
1565+};
1566+
1567+static gboolean
1568+do_multimedia_player_action (GsdMediaKeysManager *manager,
1569+ const char *icon,
1570+ const char *key)
1571+{
1572+ if (icon != NULL)
1573+ ubuntu_osd_notification_show_icon (icon, key);
1574+ return gsd_media_player_key_pressed (manager, key);
1575+}
1576+
1577+static void
1578+on_xrandr_action_call_finished (GObject *source_object,
1579+ GAsyncResult *res,
1580+ GsdMediaKeysManager *manager)
1581+{
1582+ GError *error = NULL;
1583+ GVariant *variant;
1584+ char *action;
1585+
1586+ action = g_object_get_data (G_OBJECT (source_object),
1587+ "gsd-media-keys-manager-xrandr-action");
1588+
1589+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
1590+
1591+ g_object_unref (manager->priv->cancellable);
1592+ manager->priv->cancellable = NULL;
1593+
1594+ if (error != NULL) {
1595+ g_warning ("Unable to call '%s': %s", action, error->message);
1596+ g_error_free (error);
1597+ } else {
1598+ g_variant_unref (variant);
1599+ }
1600+
1601+ g_free (action);
1602+}
1603+
1604+static void
1605+do_xrandr_action (GsdMediaKeysManager *manager,
1606+ const char *action,
1607+ gint64 timestamp)
1608+{
1609+ GsdMediaKeysManagerPrivate *priv = manager->priv;
1610+
1611+ if (priv->connection == NULL || priv->xrandr_proxy == NULL) {
1612+ g_warning ("No existing D-Bus connection trying to handle XRANDR keys");
1613+ return;
1614+ }
1615+
1616+ if (priv->cancellable != NULL) {
1617+ g_debug ("xrandr action already in flight");
1618+ return;
1619+ }
1620+
1621+ priv->cancellable = g_cancellable_new ();
1622+
1623+ g_object_set_data (G_OBJECT (priv->xrandr_proxy),
1624+ "gsd-media-keys-manager-xrandr-action",
1625+ g_strdup (action));
1626+
1627+ g_dbus_proxy_call (priv->xrandr_proxy,
1628+ action,
1629+ g_variant_new ("(x)", timestamp),
1630+ G_DBUS_CALL_FLAGS_NONE,
1631+ -1,
1632+ priv->cancellable,
1633+ (GAsyncReadyCallback) on_xrandr_action_call_finished,
1634+ manager);
1635+}
1636+
1637+static gboolean
1638+do_video_out_action (GsdMediaKeysManager *manager,
1639+ gint64 timestamp)
1640+{
1641+ do_xrandr_action (manager, "VideoModeSwitch", timestamp);
1642+ return FALSE;
1643+}
1644+
1645+static gboolean
1646+do_video_rotate_action (GsdMediaKeysManager *manager,
1647+ gint64 timestamp)
1648+{
1649+ do_xrandr_action (manager, "Rotate", timestamp);
1650+ return FALSE;
1651+}
1652+
1653+static void
1654+do_toggle_accessibility_key (const char *key)
1655+{
1656+ GSettings *settings;
1657+ gboolean state;
1658+
1659+ settings = g_settings_new ("org.gnome.desktop.a11y.applications");
1660+ state = g_settings_get_boolean (settings, key);
1661+ g_settings_set_boolean (settings, key, !state);
1662+ g_object_unref (settings);
1663+}
1664+
1665+static void
1666+do_magnifier_action (GsdMediaKeysManager *manager)
1667+{
1668+ do_toggle_accessibility_key ("screen-magnifier-enabled");
1669+}
1670+
1671+static void
1672+do_screenreader_action (GsdMediaKeysManager *manager)
1673+{
1674+ do_toggle_accessibility_key ("screen-reader-enabled");
1675+}
1676+
1677+static void
1678+do_on_screen_keyboard_action (GsdMediaKeysManager *manager)
1679+{
1680+ do_toggle_accessibility_key ("screen-keyboard-enabled");
1681+}
1682+
1683+static void
1684+do_text_size_action (GsdMediaKeysManager *manager,
1685+ MediaKeyType type)
1686+{
1687+ gdouble factor, best, distance;
1688+ guint i;
1689+
1690+ /* Same values used in the Seeing tab of the Universal Access panel */
1691+ static gdouble factors[] = {
1692+ 0.75,
1693+ 1.0,
1694+ 1.25,
1695+ 1.5
1696+ };
1697+
1698+ /* Figure out the current DPI scaling factor */
1699+ factor = g_settings_get_double (manager->priv->interface_settings, "text-scaling-factor");
1700+ factor += (type == INCREASE_TEXT_KEY ? 0.25 : -0.25);
1701+
1702+ /* Try to find a matching value */
1703+ distance = 1e6;
1704+ best = 1.0;
1705+ for (i = 0; i < G_N_ELEMENTS(factors); i++) {
1706+ gdouble d;
1707+ d = fabs (factor - factors[i]);
1708+ if (d < distance) {
1709+ best = factors[i];
1710+ distance = d;
1711+ }
1712+ }
1713+
1714+ if (best == 1.0)
1715+ g_settings_reset (manager->priv->interface_settings, "text-scaling-factor");
1716+ else
1717+ g_settings_set_double (manager->priv->interface_settings, "text-scaling-factor", best);
1718+}
1719+
1720+static void
1721+do_magnifier_zoom_action (GsdMediaKeysManager *manager,
1722+ MediaKeyType type)
1723+{
1724+ GSettings *settings;
1725+ gdouble offset, value;
1726+
1727+ if (type == MAGNIFIER_ZOOM_IN_KEY)
1728+ offset = 1.0;
1729+ else
1730+ offset = -1.0;
1731+
1732+ settings = g_settings_new ("org.gnome.desktop.a11y.magnifier");
1733+ value = g_settings_get_double (settings, "mag-factor");
1734+ value += offset;
1735+ value = roundl (value);
1736+ g_settings_set_double (settings, "mag-factor", value);
1737+ g_object_unref (settings);
1738+}
1739+
1740+static void
1741+do_toggle_contrast_action (GsdMediaKeysManager *manager)
1742+{
1743+ gboolean high_contrast;
1744+ char *theme;
1745+
1746+ /* Are we using HighContrast now? */
1747+ theme = g_settings_get_string (manager->priv->interface_settings, "gtk-theme");
1748+ high_contrast = g_str_equal (theme, HIGH_CONTRAST);
1749+ g_free (theme);
1750+
1751+ if (high_contrast != FALSE) {
1752+ if (manager->priv->gtk_theme == NULL)
1753+ g_settings_reset (manager->priv->interface_settings, "gtk-theme");
1754+ else
1755+ g_settings_set (manager->priv->interface_settings, "gtk-theme", manager->priv->gtk_theme);
1756+ g_settings_set (manager->priv->interface_settings, "icon-theme", manager->priv->icon_theme);
1757+ } else {
1758+ g_settings_set (manager->priv->interface_settings, "gtk-theme", HIGH_CONTRAST);
1759+ g_settings_set (manager->priv->interface_settings, "icon-theme", HIGH_CONTRAST);
1760+ }
1761+}
1762+
1763+static void
1764+upower_sleep_cb (GObject *source_object,
1765+ GAsyncResult *res,
1766+ gpointer user_data)
1767+{
1768+ GVariant *result;
1769+ GError *error = NULL;
1770+
1771+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
1772+ res,
1773+ &error);
1774+ if (result == NULL) {
1775+ g_warning ("couldn't sleep using UPower: %s",
1776+ error->message);
1777+ g_error_free (error);
1778+ } else {
1779+ g_variant_unref (result);
1780+ }
1781+}
1782+
1783+static void
1784+do_config_power_action (GsdMediaKeysManager *manager,
1785+ const gchar *config_key)
1786+{
1787+ GsdPowerActionType action_type;
1788+
1789+ action_type = g_settings_get_enum (manager->priv->power_settings,
1790+ config_key);
1791+ switch (action_type) {
1792+ case GSD_POWER_ACTION_SUSPEND:
1793+ g_dbus_proxy_call (manager->priv->upower_proxy,
1794+ "Suspend",
1795+ NULL,
1796+ G_DBUS_CALL_FLAGS_NONE,
1797+ -1, NULL,
1798+ upower_sleep_cb, NULL);
1799+ break;
1800+ case GSD_POWER_ACTION_INTERACTIVE:
1801+ case GSD_POWER_ACTION_SHUTDOWN:
1802+ gnome_session_shutdown (manager);
1803+ break;
1804+ case GSD_POWER_ACTION_HIBERNATE:
1805+ g_dbus_proxy_call (manager->priv->upower_proxy,
1806+ "Hibernate",
1807+ NULL,
1808+ G_DBUS_CALL_FLAGS_NONE,
1809+ -1, NULL,
1810+ upower_sleep_cb, NULL);
1811+ break;
1812+ case GSD_POWER_ACTION_BLANK:
1813+ case GSD_POWER_ACTION_NOTHING:
1814+ /* these actions cannot be handled by media-keys and
1815+ * are not used in this context */
1816+ break;
1817+ }
1818+}
1819+
1820+static void
1821+update_screen_cb (GObject *source_object,
1822+ GAsyncResult *res,
1823+ gpointer user_data)
1824+{
1825+ GError *error = NULL;
1826+ guint percentage;
1827+ GVariant *new_percentage;
1828+ GsdBrightnessActionData *data = (GsdBrightnessActionData *) user_data;
1829+ GsdMediaKeysManager *manager = data->manager;
1830+
1831+ new_percentage = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
1832+ res, &error);
1833+ if (new_percentage == NULL) {
1834+ g_warning ("Failed to set new screen percentage: %s",
1835+ error->message);
1836+ g_error_free (error);
1837+ g_free (data);
1838+ return;
1839+ }
1840+
1841+ /* update the dialog with the new value */
1842+ g_variant_get (new_percentage, "(u)", &percentage);
1843+
1844+ guint osd_percentage;
1845+ if (data->old_percentage == 100 && data->type == SCREEN_BRIGHTNESS_UP_KEY)
1846+ osd_percentage = 101;
1847+ else if (data->old_percentage == 0 && data->type == SCREEN_BRIGHTNESS_DOWN_KEY)
1848+ osd_percentage = -1;
1849+ else
1850+ osd_percentage = CLAMP (percentage, 0, 100);
1851+
1852+ if (!ubuntu_osd_notification_show_brightness (manager, osd_percentage)) {
1853+ dialog_init (manager);
1854+ gsd_media_keys_window_set_action_custom (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1855+ "display-brightness-symbolic",
1856+ TRUE);
1857+ gsd_media_keys_window_set_volume_level (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1858+ percentage);
1859+ dialog_show (manager);
1860+ }
1861+ g_free (data);
1862+ g_variant_unref (new_percentage);
1863+}
1864+
1865+static void
1866+do_screen_brightness_action_real (GObject *source_object,
1867+ GAsyncResult *res,
1868+ gpointer user_data)
1869+{
1870+ GsdBrightnessActionData *data = (GsdBrightnessActionData *) user_data;
1871+ GsdMediaKeysManager *manager = data->manager;
1872+ GError *error = NULL;
1873+
1874+ GVariant *old_percentage = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
1875+ res, &error);
1876+ if (old_percentage == NULL) {
1877+ g_warning ("Failed to get old screen percentage: %s", error->message);
1878+ g_error_free (error);
1879+ g_free (data);
1880+ return;
1881+ }
1882+
1883+ g_variant_get (old_percentage, "(u)", &data->old_percentage);
1884+
1885+ /* call into the power plugin */
1886+ g_dbus_proxy_call (manager->priv->power_screen_proxy,
1887+ data->type == SCREEN_BRIGHTNESS_UP_KEY ? "StepUp" : "StepDown",
1888+ NULL,
1889+ G_DBUS_CALL_FLAGS_NONE,
1890+ -1,
1891+ NULL,
1892+ update_screen_cb,
1893+ data);
1894+
1895+ g_variant_unref (old_percentage);
1896+}
1897+
1898+static void
1899+do_screen_brightness_action (GsdMediaKeysManager *manager,
1900+ MediaKeyType type)
1901+{
1902+ if (manager->priv->connection == NULL ||
1903+ manager->priv->power_screen_proxy == NULL) {
1904+ g_warning ("No existing D-Bus connection trying to handle power keys");
1905+ return;
1906+ }
1907+
1908+ GsdBrightnessActionData *data = g_new0 (GsdBrightnessActionData, 1);
1909+ data->manager = manager;
1910+ data->type = type;
1911+
1912+ g_dbus_proxy_call (manager->priv->power_screen_proxy,
1913+ "GetPercentage",
1914+ NULL,
1915+ G_DBUS_CALL_FLAGS_NONE,
1916+ -1,
1917+ NULL,
1918+ do_screen_brightness_action_real,
1919+ data);
1920+}
1921+
1922+static void
1923+update_keyboard_cb (GObject *source_object,
1924+ GAsyncResult *res,
1925+ gpointer user_data)
1926+{
1927+ GError *error = NULL;
1928+ guint percentage;
1929+ GVariant *new_percentage;
1930+ GsdMediaKeysManager *manager = GSD_MEDIA_KEYS_MANAGER (user_data);
1931+
1932+ new_percentage = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
1933+ res, &error);
1934+ if (new_percentage == NULL) {
1935+ g_warning ("Failed to set new keyboard percentage: %s",
1936+ error->message);
1937+ g_error_free (error);
1938+ return;
1939+ }
1940+
1941+ /* update the dialog with the new value */
1942+ g_variant_get (new_percentage, "(u)", &percentage);
1943+
1944+ /* FIXME: No overshoot effect for keyboard, as the power plugin has no interface
1945+ * to get the old brightness */
1946+ if (!ubuntu_osd_notification_show_kb_backlight (manager, CLAMP (percentage, 0, 100))) {
1947+ dialog_init (manager);
1948+ gsd_media_keys_window_set_action_custom (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1949+ "keyboard-brightness-symbolic",
1950+ TRUE);
1951+ gsd_media_keys_window_set_volume_level (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1952+ percentage);
1953+ dialog_show (manager);
1954+ }
1955+ g_variant_unref (new_percentage);
1956+}
1957+
1958+static void
1959+do_keyboard_brightness_action (GsdMediaKeysManager *manager,
1960+ MediaKeyType type)
1961+{
1962+ if (manager->priv->connection == NULL ||
1963+ manager->priv->power_keyboard_proxy == NULL) {
1964+ g_warning ("No existing D-Bus connection trying to handle power keys");
1965+ return;
1966+ }
1967+
1968+ /* call into the power plugin */
1969+ g_dbus_proxy_call (manager->priv->power_keyboard_proxy,
1970+ type == KEYBOARD_BRIGHTNESS_UP_KEY ? "StepUp" : "StepDown",
1971+ NULL,
1972+ G_DBUS_CALL_FLAGS_NONE,
1973+ -1,
1974+ NULL,
1975+ update_keyboard_cb,
1976+ manager);
1977+}
1978+
1979+static void
1980+do_custom_action (GsdMediaKeysManager *manager,
1981+ MediaKey *key,
1982+ gint64 timestamp)
1983+{
1984+ execute (manager, key->custom_command, FALSE);
1985+}
1986+
1987+static gboolean
1988+do_action (GsdMediaKeysManager *manager,
1989+ guint deviceid,
1990+ MediaKeyType type,
1991+ gint64 timestamp)
1992+{
1993+ char *cmd;
1994+
1995+ g_debug ("Launching action for key type '%d' (on device id %d)", type, deviceid);
1996+
1997+ switch (type) {
1998+ case TOUCHPAD_KEY:
1999+ do_touchpad_action (manager);
2000+ break;
2001+ case TOUCHPAD_ON_KEY:
2002+ do_touchpad_osd_action (manager, TRUE);
2003+ break;
2004+ case TOUCHPAD_OFF_KEY:
2005+ do_touchpad_osd_action (manager, FALSE);
2006+ break;
2007+ case MUTE_KEY:
2008+ case VOLUME_DOWN_KEY:
2009+ case VOLUME_UP_KEY:
2010+ do_sound_action (manager, deviceid, type, FALSE);
2011+ break;
2012+ case MUTE_QUIET_KEY:
2013+ do_sound_action (manager, deviceid, MUTE_KEY, TRUE);
2014+ break;
2015+ case VOLUME_DOWN_QUIET_KEY:
2016+ do_sound_action (manager, deviceid, VOLUME_DOWN_KEY, TRUE);
2017+ break;
2018+ case VOLUME_UP_QUIET_KEY:
2019+ do_sound_action (manager, deviceid, VOLUME_UP_KEY, TRUE);
2020+ break;
2021+ case LOGOUT_KEY:
2022+ do_logout_action (manager);
2023+ break;
2024+ case EJECT_KEY:
2025+ do_eject_action (manager);
2026+ break;
2027+ case HOME_KEY:
2028+ do_home_key_action (manager, timestamp);
2029+ break;
2030+ case SEARCH_KEY:
2031+ cmd = NULL;
2032+ if ((cmd = g_find_program_in_path ("tracker-search-tool")))
2033+ do_execute_desktop (manager, "tracker-needle.desktop", timestamp);
2034+ else
2035+ do_execute_desktop (manager, "gnome-search-tool.desktop", timestamp);
2036+ g_free (cmd);
2037+ break;
2038+ case EMAIL_KEY:
2039+ do_url_action (manager, "mailto", timestamp);
2040+ break;
2041+ case SCREENSAVER_KEY:
2042+ execute (manager, "gnome-screensaver-command --lock", FALSE);
2043+ break;
2044+ case HELP_KEY:
2045+ do_url_action (manager, "ghelp", timestamp);
2046+ break;
2047+ case SCREENSHOT_KEY:
2048+ execute (manager, "gnome-screenshot", FALSE);
2049+ break;
2050+ case WINDOW_SCREENSHOT_KEY:
2051+ execute (manager, "gnome-screenshot --window", FALSE);
2052+ break;
2053+ case AREA_SCREENSHOT_KEY:
2054+ execute (manager, "gnome-screenshot --area", FALSE);
2055+ break;
2056+ case SCREENSHOT_CLIP_KEY:
2057+ execute (manager, "gnome-screenshot --clipboard", FALSE);
2058+ break;
2059+ case WINDOW_SCREENSHOT_CLIP_KEY:
2060+ execute (manager, "gnome-screenshot --window --clipboard", FALSE);
2061+ break;
2062+ case AREA_SCREENSHOT_CLIP_KEY:
2063+ execute (manager, "gnome-screenshot --area --clipboard", FALSE);
2064+ break;
2065+ case WWW_KEY:
2066+ do_url_action (manager, "http", timestamp);
2067+ break;
2068+ case MEDIA_KEY:
2069+ do_media_action (manager, timestamp);
2070+ break;
2071+ case CALCULATOR_KEY:
2072+ do_execute_desktop (manager, "gcalctool.desktop", timestamp);
2073+ break;
2074+ case PLAY_KEY:
2075+ return do_multimedia_player_action (manager, NULL, "Play");
2076+ case PAUSE_KEY:
2077+ return do_multimedia_player_action (manager, NULL, "Pause");
2078+ case STOP_KEY:
2079+ return do_multimedia_player_action (manager, NULL, "Stop");
2080+ case PREVIOUS_KEY:
2081+ return do_multimedia_player_action (manager, NULL, "Previous");
2082+ case NEXT_KEY:
2083+ return do_multimedia_player_action (manager, NULL, "Next");
2084+ case REWIND_KEY:
2085+ return do_multimedia_player_action (manager, NULL, "Rewind");
2086+ case FORWARD_KEY:
2087+ return do_multimedia_player_action (manager, NULL, "FastForward");
2088+ case REPEAT_KEY:
2089+ return do_multimedia_player_action (manager, NULL, "Repeat");
2090+ case RANDOM_KEY:
2091+ return do_multimedia_player_action (manager, NULL, "Shuffle");
2092+ case VIDEO_OUT_KEY:
2093+ do_video_out_action (manager, timestamp);
2094+ break;
2095+ case ROTATE_VIDEO_KEY:
2096+ do_video_rotate_action (manager, timestamp);
2097+ break;
2098+ case MAGNIFIER_KEY:
2099+ do_magnifier_action (manager);
2100+ break;
2101+ case SCREENREADER_KEY:
2102+ do_screenreader_action (manager);
2103+ break;
2104+ case ON_SCREEN_KEYBOARD_KEY:
2105+ do_on_screen_keyboard_action (manager);
2106+ break;
2107+ case INCREASE_TEXT_KEY:
2108+ case DECREASE_TEXT_KEY:
2109+ do_text_size_action (manager, type);
2110+ break;
2111+ case MAGNIFIER_ZOOM_IN_KEY:
2112+ case MAGNIFIER_ZOOM_OUT_KEY:
2113+ do_magnifier_zoom_action (manager, type);
2114+ break;
2115+ case TOGGLE_CONTRAST_KEY:
2116+ do_toggle_contrast_action (manager);
2117+ break;
2118+ case POWER_KEY:
2119+ do_config_power_action (manager, "button-power");
2120+ break;
2121+ case SLEEP_KEY:
2122+ do_config_power_action (manager, "button-sleep");
2123+ break;
2124+ case SUSPEND_KEY:
2125+ do_config_power_action (manager, "button-suspend");
2126+ break;
2127+ case HIBERNATE_KEY:
2128+ do_config_power_action (manager, "button-hibernate");
2129+ break;
2130+ case SCREEN_BRIGHTNESS_UP_KEY:
2131+ case SCREEN_BRIGHTNESS_DOWN_KEY:
2132+ do_screen_brightness_action (manager, type);
2133+ break;
2134+ case KEYBOARD_BRIGHTNESS_UP_KEY:
2135+ case KEYBOARD_BRIGHTNESS_DOWN_KEY:
2136+ case KEYBOARD_BRIGHTNESS_TOGGLE_KEY:
2137+ do_keyboard_brightness_action (manager, type);
2138+ break;
2139+ case BATTERY_KEY:
2140+ do_execute_desktop (manager, "gnome-power-statistics.desktop", timestamp);
2141+ break;
2142+ /* Note, no default so compiler catches missing keys */
2143+ case CUSTOM_KEY:
2144+ g_assert_not_reached ();
2145+ }
2146+
2147+ return FALSE;
2148+}
2149+
2150+static GdkScreen *
2151+get_screen_from_root (GsdMediaKeysManager *manager,
2152+ Window root)
2153+{
2154+ GSList *l;
2155+
2156+ /* Look for which screen we're receiving events */
2157+ for (l = manager->priv->screens; l != NULL; l = l->next) {
2158+ GdkScreen *screen = (GdkScreen *) l->data;
2159+ GdkWindow *window = gdk_screen_get_root_window (screen);
2160+
2161+ if (GDK_WINDOW_XID (window) == root)
2162+ return screen;
2163+ }
2164+
2165+ return NULL;
2166+}
2167+
2168+static GdkFilterReturn
2169+filter_key_events (XEvent *xevent,
2170+ GdkEvent *event,
2171+ GsdMediaKeysManager *manager)
2172+{
2173+ XIEvent *xiev;
2174+ XIDeviceEvent *xev;
2175+ XGenericEventCookie *cookie;
2176+ guint i;
2177+ guint deviceid;
2178+
2179+ /* verify we have a key event */
2180+ if (xevent->type != GenericEvent)
2181+ return GDK_FILTER_CONTINUE;
2182+ cookie = &xevent->xcookie;
2183+ if (cookie->extension != manager->priv->opcode)
2184+ return GDK_FILTER_CONTINUE;
2185+
2186+ xiev = (XIEvent *) xevent->xcookie.data;
2187+
2188+ if (xiev->evtype != XI_KeyPress &&
2189+ xiev->evtype != XI_KeyRelease)
2190+ return GDK_FILTER_CONTINUE;
2191+
2192+ xev = (XIDeviceEvent *) xiev;
2193+
2194+ deviceid = xev->sourceid;
2195+
2196+ for (i = 0; i < manager->priv->keys->len; i++) {
2197+ MediaKey *key;
2198+
2199+ key = g_ptr_array_index (manager->priv->keys, i);
2200+
2201+ if (match_xi2_key (key->key, xev)) {
2202+ switch (key->key_type) {
2203+ case VOLUME_DOWN_KEY:
2204+ case VOLUME_UP_KEY:
2205+ case VOLUME_DOWN_QUIET_KEY:
2206+ case VOLUME_UP_QUIET_KEY:
2207+ /* auto-repeatable keys */
2208+ if (xiev->evtype != XI_KeyPress)
2209+ return GDK_FILTER_CONTINUE;
2210+ break;
2211+ default:
2212+ if (xiev->evtype != XI_KeyRelease) {
2213+ return GDK_FILTER_CONTINUE;
2214+ }
2215+ }
2216+
2217+ manager->priv->current_screen = get_screen_from_root (manager, xev->root);
2218+
2219+ if (key->key_type == CUSTOM_KEY) {
2220+ do_custom_action (manager, key, xev->time);
2221+ return GDK_FILTER_REMOVE;
2222+ }
2223+
2224+ if (do_action (manager, deviceid, key->key_type, xev->time) == FALSE) {
2225+ return GDK_FILTER_REMOVE;
2226+ } else {
2227+ return GDK_FILTER_CONTINUE;
2228+ }
2229+ }
2230+ }
2231+
2232+ return GDK_FILTER_CONTINUE;
2233+}
2234+
2235+static void
2236+update_theme_settings (GSettings *settings,
2237+ const char *key,
2238+ GsdMediaKeysManager *manager)
2239+{
2240+ char *theme;
2241+
2242+ theme = g_settings_get_string (manager->priv->interface_settings, key);
2243+ if (g_str_equal (theme, HIGH_CONTRAST)) {
2244+ g_free (theme);
2245+ } else {
2246+ if (g_str_equal (key, "gtk-theme")) {
2247+ g_free (manager->priv->gtk_theme);
2248+ manager->priv->gtk_theme = theme;
2249+ } else {
2250+ g_free (manager->priv->icon_theme);
2251+ manager->priv->icon_theme = theme;
2252+ }
2253+ }
2254+}
2255+
2256+static gboolean
2257+start_media_keys_idle_cb (GsdMediaKeysManager *manager)
2258+{
2259+ GSList *l;
2260+ char *theme_name;
2261+
2262+ g_debug ("Starting media_keys manager");
2263+ gnome_settings_profile_start (NULL);
2264+
2265+
2266+ gvc_mixer_control_open (manager->priv->volume);
2267+
2268+ manager->priv->settings = g_settings_new (SETTINGS_BINDING_DIR);
2269+ g_signal_connect (G_OBJECT (manager->priv->settings), "changed",
2270+ G_CALLBACK (gsettings_changed_cb), manager);
2271+
2272+ manager->priv->gconf = gconf_client_get_default ();
2273+ gconf_client_add_dir (manager->priv->gconf, GCONF_BINDING_DIR, GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
2274+ manager->priv->gconf_id = gconf_client_notify_add (manager->priv->gconf,
2275+ GCONF_BINDING_DIR,
2276+ (GConfClientNotifyFunc) gconf_changed_cb,
2277+ manager,
2278+ NULL,
2279+ NULL);
2280+
2281+ /* Sound events */
2282+ ca_context_create (&manager->priv->ca);
2283+ ca_context_set_driver (manager->priv->ca, "pulse");
2284+ ca_context_change_props (manager->priv->ca, 0,
2285+ CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
2286+ NULL);
2287+ manager->priv->gtksettings = gtk_settings_get_for_screen (gdk_screen_get_default ());
2288+ g_object_get (G_OBJECT (manager->priv->gtksettings), "gtk-sound-theme-name", &theme_name, NULL);
2289+ if (theme_name)
2290+ ca_context_change_props (manager->priv->ca, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL);
2291+ g_free (theme_name);
2292+ g_signal_connect (manager->priv->gtksettings, "notify::gtk-sound-theme-name",
2293+ G_CALLBACK (sound_theme_changed), manager);
2294+
2295+ /* for the power plugin interface code */
2296+ manager->priv->power_settings = g_settings_new (SETTINGS_POWER_DIR);
2297+
2298+ /* Logic from http://git.gnome.org/browse/gnome-shell/tree/js/ui/status/accessibility.js#n163 */
2299+ manager->priv->interface_settings = g_settings_new (SETTINGS_INTERFACE_DIR);
2300+ g_signal_connect (G_OBJECT (manager->priv->interface_settings), "changed::gtk-theme",
2301+ G_CALLBACK (update_theme_settings), manager);
2302+ g_signal_connect (G_OBJECT (manager->priv->interface_settings), "changed::icon-theme",
2303+ G_CALLBACK (update_theme_settings), manager);
2304+ manager->priv->gtk_theme = g_settings_get_string (manager->priv->interface_settings, "gtk-theme");
2305+ if (g_str_equal (manager->priv->gtk_theme, HIGH_CONTRAST)) {
2306+ g_free (manager->priv->gtk_theme);
2307+ manager->priv->gtk_theme = NULL;
2308+ }
2309+ manager->priv->icon_theme = g_settings_get_string (manager->priv->interface_settings, "icon-theme");
2310+
2311+ init_screens (manager);
2312+ init_kbd (manager);
2313+
2314+ /* Start filtering the events */
2315+ for (l = manager->priv->screens; l != NULL; l = l->next) {
2316+ gnome_settings_profile_start ("gdk_window_add_filter");
2317+
2318+ g_debug ("adding key filter for screen: %d",
2319+ gdk_screen_get_number (l->data));
2320+
2321+ gdk_window_add_filter (gdk_screen_get_root_window (l->data),
2322+ (GdkFilterFunc) filter_key_events,
2323+ manager);
2324+ gnome_settings_profile_end ("gdk_window_add_filter");
2325+ }
2326+
2327+ gnome_settings_profile_end (NULL);
2328+
2329+ manager->priv->start_idle_id = 0;
2330+
2331+ return FALSE;
2332+}
2333+
2334+gboolean
2335+gsd_media_keys_manager_start (GsdMediaKeysManager *manager,
2336+ GError **error)
2337+{
2338+ const char * const subsystems[] = { "input", "usb", "sound", NULL };
2339+
2340+ gnome_settings_profile_start (NULL);
2341+
2342+ if (supports_xinput2_devices (&manager->priv->opcode) == FALSE) {
2343+ g_debug ("No Xinput2 support, disabling plugin");
2344+ return TRUE;
2345+ }
2346+
2347+#ifdef HAVE_GUDEV
2348+ manager->priv->streams = g_hash_table_new (g_direct_hash, g_direct_equal);
2349+ manager->priv->udev_client = g_udev_client_new (subsystems);
2350+#endif
2351+
2352+ /* initialise Volume handler
2353+ *
2354+ * We do this one here to force checking gstreamer cache, etc.
2355+ * The rest (grabbing and setting the keys) can happen in an
2356+ * idle.
2357+ */
2358+ gnome_settings_profile_start ("gvc_mixer_control_new");
2359+
2360+ manager->priv->volume = gvc_mixer_control_new ("GNOME Volume Control Media Keys");
2361+
2362+ g_signal_connect (manager->priv->volume,
2363+ "state-changed",
2364+ G_CALLBACK (on_control_state_changed),
2365+ manager);
2366+ g_signal_connect (manager->priv->volume,
2367+ "default-sink-changed",
2368+ G_CALLBACK (on_control_default_sink_changed),
2369+ manager);
2370+ g_signal_connect (manager->priv->volume,
2371+ "stream-removed",
2372+ G_CALLBACK (on_control_stream_removed),
2373+ manager);
2374+
2375+ gnome_settings_profile_end ("gvc_mixer_control_new");
2376+
2377+ manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_media_keys_idle_cb, manager);
2378+
2379+ register_manager (manager_object);
2380+
2381+ gnome_settings_profile_end (NULL);
2382+
2383+ return TRUE;
2384+}
2385+
2386+void
2387+gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
2388+{
2389+ GsdMediaKeysManagerPrivate *priv = manager->priv;
2390+ GSList *ls;
2391+ GList *l;
2392+ int i;
2393+
2394+ g_debug ("Stopping media_keys manager");
2395+
2396+ if (priv->bus_cancellable != NULL) {
2397+ g_cancellable_cancel (priv->bus_cancellable);
2398+ g_object_unref (priv->bus_cancellable);
2399+ priv->bus_cancellable = NULL;
2400+ }
2401+
2402+ for (ls = priv->screens; ls != NULL; ls = ls->next) {
2403+ gdk_window_remove_filter (gdk_screen_get_root_window (ls->data),
2404+ (GdkFilterFunc) filter_key_events,
2405+ manager);
2406+ }
2407+
2408+ if (manager->priv->gtksettings != NULL) {
2409+ g_signal_handlers_disconnect_by_func (manager->priv->gtksettings, sound_theme_changed, manager);
2410+ manager->priv->gtksettings = NULL;
2411+ }
2412+
2413+ if (manager->priv->ca) {
2414+ ca_context_destroy (manager->priv->ca);
2415+ manager->priv->ca = NULL;
2416+ }
2417+
2418+#ifdef HAVE_GUDEV
2419+ if (priv->streams) {
2420+ g_hash_table_destroy (priv->streams);
2421+ priv->streams = NULL;
2422+ }
2423+ if (priv->udev_client) {
2424+ g_object_unref (priv->udev_client);
2425+ priv->udev_client = NULL;
2426+ }
2427+#endif /* HAVE_GUDEV */
2428+
2429+ if (priv->settings) {
2430+ g_object_unref (priv->settings);
2431+ priv->settings = NULL;
2432+ }
2433+
2434+ if (priv->power_settings) {
2435+ g_object_unref (priv->power_settings);
2436+ priv->power_settings = NULL;
2437+ }
2438+
2439+ if (priv->power_screen_proxy) {
2440+ g_object_unref (priv->power_screen_proxy);
2441+ priv->power_screen_proxy = NULL;
2442+ }
2443+
2444+ if (priv->power_keyboard_proxy) {
2445+ g_object_unref (priv->power_keyboard_proxy);
2446+ priv->power_keyboard_proxy = NULL;
2447+ }
2448+
2449+ if (priv->upower_proxy) {
2450+ g_object_unref (priv->upower_proxy);
2451+ priv->upower_proxy = NULL;
2452+ }
2453+
2454+ if (priv->cancellable != NULL) {
2455+ g_cancellable_cancel (priv->cancellable);
2456+ g_object_unref (priv->cancellable);
2457+ priv->cancellable = NULL;
2458+ }
2459+
2460+ if (priv->introspection_data) {
2461+ g_dbus_node_info_unref (priv->introspection_data);
2462+ priv->introspection_data = NULL;
2463+ }
2464+
2465+ if (priv->connection != NULL) {
2466+ g_object_unref (priv->connection);
2467+ priv->connection = NULL;
2468+ }
2469+
2470+ if (priv->volume_notification != NULL) {
2471+ notify_notification_close (priv->volume_notification, NULL);
2472+ g_object_unref (priv->volume_notification);
2473+ priv->volume_notification = NULL;
2474+ }
2475+
2476+ if (priv->brightness_notification != NULL) {
2477+ notify_notification_close (priv->brightness_notification, NULL);
2478+ g_object_unref (priv->brightness_notification);
2479+ priv->brightness_notification = NULL;
2480+ }
2481+
2482+ if (priv->kb_backlight_notification != NULL) {
2483+ notify_notification_close (priv->kb_backlight_notification, NULL);
2484+ g_object_unref (priv->kb_backlight_notification);
2485+ priv->kb_backlight_notification = NULL;
2486+ }
2487+
2488+ if (priv->keys != NULL) {
2489+ gdk_error_trap_push ();
2490+ for (i = 0; i < priv->keys->len; ++i) {
2491+ MediaKey *key;
2492+
2493+ key = g_ptr_array_index (manager->priv->keys, i);
2494+
2495+ if (key->key)
2496+ grab_key_unsafe (key->key, FALSE, priv->screens);
2497+ }
2498+ g_ptr_array_free (priv->keys, TRUE);
2499+ priv->keys = NULL;
2500+
2501+ gdk_flush ();
2502+ gdk_error_trap_pop_ignored ();
2503+ }
2504+
2505+ if (priv->gconf_id) {
2506+ gconf_client_remove_dir (priv->gconf, GCONF_BINDING_DIR, NULL);
2507+ gconf_client_notify_remove (priv->gconf, priv->gconf_id);
2508+ priv->gconf_id = 0;
2509+ }
2510+
2511+ if (priv->gconf) {
2512+ g_object_unref (priv->gconf);
2513+ priv->gconf = NULL;
2514+ }
2515+
2516+ if (priv->screens != NULL) {
2517+ g_slist_free (priv->screens);
2518+ priv->screens = NULL;
2519+ }
2520+
2521+ if (priv->stream) {
2522+ g_object_unref (priv->stream);
2523+ priv->stream = NULL;
2524+ }
2525+
2526+ if (priv->volume) {
2527+ g_object_unref (priv->volume);
2528+ priv->volume = NULL;
2529+ }
2530+
2531+ if (priv->dialog != NULL) {
2532+ gtk_widget_destroy (priv->dialog);
2533+ priv->dialog = NULL;
2534+ }
2535+
2536+ if (priv->media_players != NULL) {
2537+ for (l = priv->media_players; l; l = l->next) {
2538+ MediaPlayer *mp = l->data;
2539+ g_free (mp->application);
2540+ g_free (mp);
2541+ }
2542+ g_list_free (priv->media_players);
2543+ priv->media_players = NULL;
2544+ }
2545+}
2546+
2547+static GObject *
2548+gsd_media_keys_manager_constructor (GType type,
2549+ guint n_construct_properties,
2550+ GObjectConstructParam *construct_properties)
2551+{
2552+ GsdMediaKeysManager *media_keys_manager;
2553+
2554+ media_keys_manager = GSD_MEDIA_KEYS_MANAGER (G_OBJECT_CLASS (gsd_media_keys_manager_parent_class)->constructor (type,
2555+ n_construct_properties,
2556+ construct_properties));
2557+
2558+ return G_OBJECT (media_keys_manager);
2559+}
2560+
2561+static void
2562+gsd_media_keys_manager_class_init (GsdMediaKeysManagerClass *klass)
2563+{
2564+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
2565+
2566+ object_class->constructor = gsd_media_keys_manager_constructor;
2567+ object_class->finalize = gsd_media_keys_manager_finalize;
2568+
2569+ g_type_class_add_private (klass, sizeof (GsdMediaKeysManagerPrivate));
2570+}
2571+
2572+static void
2573+gsd_media_keys_manager_init (GsdMediaKeysManager *manager)
2574+{
2575+ manager->priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager);
2576+}
2577+
2578+static void
2579+gsd_media_keys_manager_finalize (GObject *object)
2580+{
2581+ GsdMediaKeysManager *media_keys_manager;
2582+
2583+ g_return_if_fail (object != NULL);
2584+ g_return_if_fail (GSD_IS_MEDIA_KEYS_MANAGER (object));
2585+
2586+ media_keys_manager = GSD_MEDIA_KEYS_MANAGER (object);
2587+
2588+ g_return_if_fail (media_keys_manager->priv != NULL);
2589+
2590+ if (media_keys_manager->priv->start_idle_id != 0)
2591+ g_source_remove (media_keys_manager->priv->start_idle_id);
2592+
2593+ G_OBJECT_CLASS (gsd_media_keys_manager_parent_class)->finalize (object);
2594+}
2595+
2596+static void
2597+xrandr_ready_cb (GObject *source_object,
2598+ GAsyncResult *res,
2599+ GsdMediaKeysManager *manager)
2600+{
2601+ GError *error = NULL;
2602+
2603+ manager->priv->xrandr_proxy = g_dbus_proxy_new_finish (res, &error);
2604+ if (manager->priv->xrandr_proxy == NULL) {
2605+ g_warning ("Failed to get proxy for XRandR operations: %s", error->message);
2606+ g_error_free (error);
2607+ }
2608+}
2609+
2610+static void
2611+upower_ready_cb (GObject *source_object,
2612+ GAsyncResult *res,
2613+ GsdMediaKeysManager *manager)
2614+{
2615+ GError *error = NULL;
2616+
2617+ manager->priv->upower_proxy = g_dbus_proxy_new_finish (res, &error);
2618+ if (manager->priv->upower_proxy == NULL) {
2619+ g_warning ("Failed to get proxy for upower: %s",
2620+ error->message);
2621+ g_error_free (error);
2622+ }
2623+}
2624+
2625+static void
2626+power_screen_ready_cb (GObject *source_object,
2627+ GAsyncResult *res,
2628+ GsdMediaKeysManager *manager)
2629+{
2630+ GError *error = NULL;
2631+
2632+ manager->priv->power_screen_proxy = g_dbus_proxy_new_finish (res, &error);
2633+ if (manager->priv->power_screen_proxy == NULL) {
2634+ g_warning ("Failed to get proxy for power (screen): %s",
2635+ error->message);
2636+ g_error_free (error);
2637+ }
2638+}
2639+
2640+static void
2641+power_keyboard_ready_cb (GObject *source_object,
2642+ GAsyncResult *res,
2643+ GsdMediaKeysManager *manager)
2644+{
2645+ GError *error = NULL;
2646+
2647+ manager->priv->power_keyboard_proxy = g_dbus_proxy_new_finish (res, &error);
2648+ if (manager->priv->power_keyboard_proxy == NULL) {
2649+ g_warning ("Failed to get proxy for power (keyboard): %s",
2650+ error->message);
2651+ g_error_free (error);
2652+ }
2653+}
2654+
2655+static void
2656+on_bus_gotten (GObject *source_object,
2657+ GAsyncResult *res,
2658+ GsdMediaKeysManager *manager)
2659+{
2660+ GDBusConnection *connection;
2661+ GError *error = NULL;
2662+
2663+ if (manager->priv->bus_cancellable == NULL ||
2664+ g_cancellable_is_cancelled (manager->priv->bus_cancellable)) {
2665+ g_warning ("Operation has been cancelled, so not retrieving session bus");
2666+ return;
2667+ }
2668+
2669+ connection = g_bus_get_finish (res, &error);
2670+ if (connection == NULL) {
2671+ g_warning ("Could not get session bus: %s", error->message);
2672+ g_error_free (error);
2673+ return;
2674+ }
2675+ manager->priv->connection = connection;
2676+
2677+ g_dbus_connection_register_object (connection,
2678+ GSD_MEDIA_KEYS_DBUS_PATH,
2679+ manager->priv->introspection_data->interfaces[0],
2680+ &interface_vtable,
2681+ manager,
2682+ NULL,
2683+ NULL);
2684+
2685+ g_dbus_proxy_new (manager->priv->connection,
2686+ G_DBUS_PROXY_FLAGS_NONE,
2687+ NULL,
2688+ "org.gnome.SettingsDaemon",
2689+ "/org/gnome/SettingsDaemon/XRANDR",
2690+ "org.gnome.SettingsDaemon.XRANDR_2",
2691+ NULL,
2692+ (GAsyncReadyCallback) xrandr_ready_cb,
2693+ manager);
2694+
2695+ g_dbus_proxy_new (manager->priv->connection,
2696+ G_DBUS_PROXY_FLAGS_NONE,
2697+ NULL,
2698+ "org.gnome.SettingsDaemon",
2699+ "/org/gnome/SettingsDaemon/Power",
2700+ "org.gnome.SettingsDaemon.Power.Screen",
2701+ NULL,
2702+ (GAsyncReadyCallback) power_screen_ready_cb,
2703+ manager);
2704+
2705+ g_dbus_proxy_new (manager->priv->connection,
2706+ G_DBUS_PROXY_FLAGS_NONE,
2707+ NULL,
2708+ "org.gnome.SettingsDaemon",
2709+ "/org/gnome/SettingsDaemon/Power",
2710+ "org.gnome.SettingsDaemon.Power.Keyboard",
2711+ NULL,
2712+ (GAsyncReadyCallback) power_keyboard_ready_cb,
2713+ manager);
2714+}
2715+
2716+static void
2717+register_manager (GsdMediaKeysManager *manager)
2718+{
2719+ manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
2720+ manager->priv->bus_cancellable = g_cancellable_new ();
2721+ g_assert (manager->priv->introspection_data != NULL);
2722+
2723+ g_bus_get (G_BUS_TYPE_SESSION,
2724+ manager->priv->bus_cancellable,
2725+ (GAsyncReadyCallback) on_bus_gotten,
2726+ manager);
2727+
2728+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
2729+ G_DBUS_PROXY_FLAGS_NONE,
2730+ NULL,
2731+ "org.freedesktop.UPower",
2732+ "/org/freedesktop/UPower",
2733+ "org.freedesktop.UPower",
2734+ NULL,
2735+ (GAsyncReadyCallback) upower_ready_cb,
2736+ manager);
2737+}
2738+
2739+GsdMediaKeysManager *
2740+gsd_media_keys_manager_new (void)
2741+{
2742+ if (manager_object != NULL) {
2743+ g_object_ref (manager_object);
2744+ } else {
2745+ manager_object = g_object_new (GSD_TYPE_MEDIA_KEYS_MANAGER, NULL);
2746+ g_object_add_weak_pointer (manager_object,
2747+ (gpointer *) &manager_object);
2748+ }
2749+
2750+ return GSD_MEDIA_KEYS_MANAGER (manager_object);
2751+}
2752
2753=== added file '.pc/64_micmute.patch/plugins/media-keys/shortcuts-list.h'
2754--- .pc/64_micmute.patch/plugins/media-keys/shortcuts-list.h 1970-01-01 00:00:00 +0000
2755+++ .pc/64_micmute.patch/plugins/media-keys/shortcuts-list.h 2013-10-01 18:19:26 +0000
2756@@ -0,0 +1,152 @@
2757+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2758+ *
2759+ * Copyright (C) 2001 Bastien Nocera <hadess@hadess.net>
2760+ *
2761+ * This program is free software; you can redistribute it and/or modify
2762+ * it under the terms of the GNU General Public License as published by
2763+ * the Free Software Foundation; either version 2 of the License, or
2764+ * (at your option) any later version.
2765+ *
2766+ * This program is distributed in the hope that it will be useful,
2767+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2768+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2769+ * GNU General Public License for more details.
2770+ *
2771+ * You should have received a copy of the GNU General Public License
2772+ * along with this program; if not, write to the Free Software
2773+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
2774+ * USA.
2775+ */
2776+
2777+#ifndef __ACME_H__
2778+#define __ACME_H__
2779+
2780+#include "gsd-keygrab.h"
2781+
2782+#define SETTINGS_BINDING_DIR "org.gnome.settings-daemon.plugins.media-keys"
2783+
2784+typedef enum {
2785+ TOUCHPAD_KEY,
2786+ TOUCHPAD_ON_KEY,
2787+ TOUCHPAD_OFF_KEY,
2788+ MUTE_KEY,
2789+ VOLUME_DOWN_KEY,
2790+ VOLUME_UP_KEY,
2791+ MUTE_QUIET_KEY,
2792+ VOLUME_DOWN_QUIET_KEY,
2793+ VOLUME_UP_QUIET_KEY,
2794+ LOGOUT_KEY,
2795+ EJECT_KEY,
2796+ HOME_KEY,
2797+ MEDIA_KEY,
2798+ CALCULATOR_KEY,
2799+ SEARCH_KEY,
2800+ EMAIL_KEY,
2801+ SCREENSAVER_KEY,
2802+ HELP_KEY,
2803+ SCREENSHOT_KEY,
2804+ WINDOW_SCREENSHOT_KEY,
2805+ AREA_SCREENSHOT_KEY,
2806+ SCREENSHOT_CLIP_KEY,
2807+ WINDOW_SCREENSHOT_CLIP_KEY,
2808+ AREA_SCREENSHOT_CLIP_KEY,
2809+ WWW_KEY,
2810+ PLAY_KEY,
2811+ PAUSE_KEY,
2812+ STOP_KEY,
2813+ PREVIOUS_KEY,
2814+ NEXT_KEY,
2815+ REWIND_KEY,
2816+ FORWARD_KEY,
2817+ REPEAT_KEY,
2818+ RANDOM_KEY,
2819+ VIDEO_OUT_KEY,
2820+ ROTATE_VIDEO_KEY,
2821+ MAGNIFIER_KEY,
2822+ SCREENREADER_KEY,
2823+ ON_SCREEN_KEYBOARD_KEY,
2824+ INCREASE_TEXT_KEY,
2825+ DECREASE_TEXT_KEY,
2826+ TOGGLE_CONTRAST_KEY,
2827+ MAGNIFIER_ZOOM_IN_KEY,
2828+ MAGNIFIER_ZOOM_OUT_KEY,
2829+ POWER_KEY,
2830+ SLEEP_KEY,
2831+ SUSPEND_KEY,
2832+ HIBERNATE_KEY,
2833+ SCREEN_BRIGHTNESS_UP_KEY,
2834+ SCREEN_BRIGHTNESS_DOWN_KEY,
2835+ KEYBOARD_BRIGHTNESS_UP_KEY,
2836+ KEYBOARD_BRIGHTNESS_DOWN_KEY,
2837+ KEYBOARD_BRIGHTNESS_TOGGLE_KEY,
2838+ BATTERY_KEY,
2839+ CUSTOM_KEY
2840+} MediaKeyType;
2841+
2842+static struct {
2843+ MediaKeyType key_type;
2844+ const char *settings_key;
2845+ const char *hard_coded;
2846+} media_keys[] = {
2847+ { TOUCHPAD_KEY, NULL, "XF86TouchpadToggle" },
2848+ { TOUCHPAD_ON_KEY, NULL, "XF86TouchpadOn" },
2849+ { TOUCHPAD_OFF_KEY, NULL, "XF86TouchpadOff" },
2850+ { MUTE_KEY, "volume-mute", NULL },
2851+ { VOLUME_DOWN_KEY, "volume-down", NULL },
2852+ { VOLUME_UP_KEY, "volume-up", NULL },
2853+ { MUTE_QUIET_KEY, NULL, "<Alt>XF86AudioMute" },
2854+ { VOLUME_DOWN_QUIET_KEY, NULL, "<Alt>XF86AudioLowerVolume" },
2855+ { VOLUME_UP_QUIET_KEY, NULL, "<Alt>XF86AudioRaiseVolume" },
2856+ { LOGOUT_KEY, "logout", NULL },
2857+ { EJECT_KEY, "eject", NULL },
2858+ { HOME_KEY, "home", NULL },
2859+ { MEDIA_KEY, "media", NULL },
2860+ { CALCULATOR_KEY, "calculator", NULL },
2861+ { SEARCH_KEY, "search", NULL },
2862+ { EMAIL_KEY, "email", NULL },
2863+ { SCREENSAVER_KEY, "screensaver", NULL },
2864+ { SCREENSAVER_KEY, NULL, "XF86ScreenSaver" },
2865+ { HELP_KEY, "help", NULL },
2866+ { SCREENSHOT_KEY, "screenshot", NULL },
2867+ { WINDOW_SCREENSHOT_KEY, "window-screenshot", NULL },
2868+ { AREA_SCREENSHOT_KEY, "area-screenshot", NULL },
2869+ { SCREENSHOT_CLIP_KEY, "screenshot-clip", NULL },
2870+ { WINDOW_SCREENSHOT_CLIP_KEY, "window-screenshot-clip", NULL },
2871+ { AREA_SCREENSHOT_CLIP_KEY, "area-screenshot-clip", NULL },
2872+ { WWW_KEY, "www", NULL },
2873+ { PLAY_KEY, "play", NULL },
2874+ { PAUSE_KEY, "pause", NULL },
2875+ { STOP_KEY, "stop", NULL },
2876+ { PREVIOUS_KEY, "previous", NULL },
2877+ { NEXT_KEY, "next", NULL },
2878+ { REWIND_KEY, NULL, "XF86AudioRewind" },
2879+ { FORWARD_KEY, NULL, "XF86AudioForward" },
2880+ { REPEAT_KEY, NULL, "XF86AudioRepeat" },
2881+ { RANDOM_KEY, NULL, "XF86AudioRandomPlay"},
2882+ { VIDEO_OUT_KEY, NULL, "<Super>p" },
2883+ /* Key code of the XF86Display key (Fn-F7 on Thinkpads, Fn-F4 on HP machines, etc.) */
2884+ { VIDEO_OUT_KEY, NULL, "XF86Display" },
2885+ /* Key code of the XF86RotateWindows key (present on some tablets) */
2886+ { ROTATE_VIDEO_KEY, NULL, "XF86RotateWindows" },
2887+ { MAGNIFIER_KEY, "magnifier", NULL },
2888+ { SCREENREADER_KEY, "screenreader", NULL },
2889+ { ON_SCREEN_KEYBOARD_KEY, "on-screen-keyboard", NULL },
2890+ { INCREASE_TEXT_KEY, "increase-text-size", NULL },
2891+ { DECREASE_TEXT_KEY, "decrease-text-size", NULL },
2892+ { TOGGLE_CONTRAST_KEY, "toggle-contrast", NULL },
2893+ { MAGNIFIER_ZOOM_IN_KEY, "magnifier-zoom-in", NULL },
2894+ { MAGNIFIER_ZOOM_OUT_KEY, "magnifier-zoom-out", NULL },
2895+ { POWER_KEY, NULL, "XF86PowerOff" },
2896+ /* the kernel / Xorg names really are like this... */
2897+ { SLEEP_KEY, NULL, "XF86Suspend" },
2898+ { SUSPEND_KEY, NULL, "XF86Sleep" },
2899+ { HIBERNATE_KEY, NULL, "XF86Hibernate" },
2900+ { SCREEN_BRIGHTNESS_UP_KEY, NULL, "XF86MonBrightnessUp" },
2901+ { SCREEN_BRIGHTNESS_DOWN_KEY, NULL, "XF86MonBrightnessDown" },
2902+ { KEYBOARD_BRIGHTNESS_UP_KEY, NULL, "XF86KbdBrightnessUp" },
2903+ { KEYBOARD_BRIGHTNESS_DOWN_KEY, NULL, "XF86KbdBrightnessDown" },
2904+ { KEYBOARD_BRIGHTNESS_TOGGLE_KEY, NULL, "XF86KbdLightOnOff" },
2905+ { BATTERY_KEY, NULL, "XF86Battery" },
2906+};
2907+
2908+#endif /* __ACME_H__ */
2909
2910=== modified file '.pc/applied-patches'
2911--- .pc/applied-patches 2013-01-10 17:23:56 +0000
2912+++ .pc/applied-patches 2013-10-01 18:19:26 +0000
2913@@ -33,3 +33,4 @@
2914 git_no_numlock_eating_cpu_loop.patch
2915 git_new_xinput_handle.patch
2916 git-smartcard-crash.patch
2917+64_micmute.patch
2918
2919=== modified file 'debian/changelog'
2920--- debian/changelog 2013-01-10 17:23:56 +0000
2921+++ debian/changelog 2013-10-01 18:19:26 +0000
2922@@ -1,3 +1,11 @@
2923+gnome-settings-daemon (3.4.2-0ubuntu0.6.3) precise; urgency=low
2924+
2925+ * debian/patches/64_micmute.patch:
2926+ - put the mic mute key used on thinkpad laptops to good use
2927+ (lp: #408903)
2928+
2929+ -- James M Leddy <james.leddy@canonical.com> Sat, 01 Jun 2013 10:01:49 -0400
2930+
2931 gnome-settings-daemon (3.4.2-0ubuntu0.6.2) precise; urgency=low
2932
2933 * debian/patches/git-smartcard-crash.patch:
2934
2935=== added file 'debian/patches/64_micmute.patch'
2936--- debian/patches/64_micmute.patch 1970-01-01 00:00:00 +0000
2937+++ debian/patches/64_micmute.patch 2013-10-01 18:19:26 +0000
2938@@ -0,0 +1,291 @@
2939+--- a/plugins/media-keys/gsd-media-keys-manager.c
2940++++ b/plugins/media-keys/gsd-media-keys-manager.c
2941+@@ -126,6 +126,7 @@
2942+ /* Volume bits */
2943+ GvcMixerControl *volume;
2944+ GvcMixerStream *stream;
2945++ GvcMixerStream *input_stream;
2946+ ca_context *ca;
2947+ GtkSettings *gtksettings;
2948+ #ifdef HAVE_GUDEV
2949+@@ -199,6 +200,14 @@
2950+ NULL
2951+ };
2952+
2953++static const char *mic_icons[] = {
2954++ "microphone-sensitivity-muted-symbolic",
2955++ "microphone-sensitivity-low-symbolic",
2956++ "microphone-sensitivity-medium-symbolic",
2957++ "microphone-sensitivity-high-symbolic",
2958++ NULL
2959++};
2960++
2961+ static const char *brightness_icons[] = {
2962+ "notification-display-brightness-off",
2963+ "notification-display-brightness-low",
2964+@@ -283,15 +292,24 @@
2965+ }
2966+
2967+ static gboolean
2968+-ubuntu_osd_notification_show_volume (GsdMediaKeysManager *manager,
2969+- gint value,
2970+- gboolean muted)
2971++ubuntu_osd_notification_show_speaker_volume (GsdMediaKeysManager *manager,
2972++ gint value,
2973++ gboolean muted)
2974+ {
2975+ return ubuntu_osd_do_notification (&manager->priv->volume_notification,
2976+ "volume", value, muted, volume_icons);
2977+ }
2978+
2979+ static gboolean
2980++ubuntu_osd_notification_show_mic_volume (GsdMediaKeysManager *manager,
2981++ gint value,
2982++ gboolean muted)
2983++{
2984++ return ubuntu_osd_do_notification (&manager->priv->volume_notification,
2985++ "volume", value, muted, mic_icons);
2986++}
2987++
2988++static gboolean
2989+ ubuntu_osd_notification_show_brightness (GsdMediaKeysManager *manager,
2990+ gint value)
2991+ {
2992+@@ -1049,14 +1067,14 @@
2993+ }
2994+
2995+ static void
2996+-update_dialog (GsdMediaKeysManager *manager,
2997+- GvcMixerStream *stream,
2998+- guint vol,
2999+- gboolean muted,
3000+- gboolean sound_changed,
3001+- gboolean quiet)
3002++update_speaker_dialog (GsdMediaKeysManager *manager,
3003++ GvcMixerStream *stream,
3004++ guint vol,
3005++ gboolean muted,
3006++ gboolean sound_changed,
3007++ gboolean quiet)
3008+ {
3009+- if (ubuntu_osd_notification_show_volume (manager, vol, muted))
3010++ if (ubuntu_osd_notification_show_speaker_volume (manager, vol, muted))
3011+ goto done;
3012+
3013+ vol = CLAMP (vol, 0, 100);
3014+@@ -1081,6 +1099,39 @@
3015+ }
3016+ }
3017+
3018++static void
3019++update_mic_dialog (GsdMediaKeysManager *manager,
3020++ GvcMixerStream *stream,
3021++ guint vol,
3022++ gboolean muted,
3023++ gboolean sound_changed,
3024++ gboolean quiet)
3025++{
3026++ vol = CLAMP (vol, 0, 100);
3027++ ubuntu_osd_notification_show_mic_volume (manager, vol, muted);
3028++
3029++ if (quiet == FALSE) {
3030++ GtkWidget *window;
3031++ if (!manager->priv->dialog) {
3032++ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3033++ gtk_window_set_screen (GTK_WINDOW (window), manager->priv->current_screen);
3034++ gtk_widget_realize (window);
3035++
3036++ } else {
3037++ window = manager->priv->dialog;
3038++ }
3039++ ca_gtk_play_for_widget (manager->priv->dialog, 0,
3040++ CA_PROP_EVENT_ID, "audio-volume-change",
3041++ CA_PROP_EVENT_DESCRIPTION, "volume changed through key press",
3042++ CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
3043++ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
3044++ NULL);
3045++
3046++ if (!manager->priv->dialog)
3047++ gtk_widget_destroy(window);
3048++ }
3049++}
3050++
3051+ #ifdef HAVE_GUDEV
3052+ /* PulseAudio gives us /devices/... paths, when udev
3053+ * expects /sys/devices/... paths. */
3054+@@ -1190,10 +1241,12 @@
3055+ int type,
3056+ gboolean quiet)
3057+ {
3058+- GvcMixerStream *stream;
3059++ GvcMixerStream *stream, *input_stream;
3060+ gboolean old_muted, new_muted;
3061+ guint old_vol, new_vol, norm_vol_step, osd_vol;
3062+- gboolean sound_changed;
3063++ gboolean old_mic_muted, new_mic_muted;
3064++ guint old_mic_vol, new_mic_vol;
3065++ gboolean sound_changed, mic_changed;
3066+
3067+ /* Find the stream that corresponds to the device, if any */
3068+ #ifdef HAVE_GUDEV
3069+@@ -1203,13 +1256,18 @@
3070+ stream = manager->priv->stream;
3071+ if (stream == NULL)
3072+ return;
3073++ input_stream = manager->priv->input_stream;
3074++ if (input_stream == NULL)
3075++ return;
3076+
3077+ norm_vol_step = PA_VOLUME_NORM * VOLUME_STEP / 100;
3078+
3079+ /* FIXME: this is racy */
3080+ new_vol = old_vol = gvc_mixer_stream_get_volume (stream);
3081++ new_mic_vol = old_mic_vol = gvc_mixer_stream_get_volume (input_stream);
3082+ new_muted = old_muted = gvc_mixer_stream_get_is_muted (stream);
3083+- sound_changed = FALSE;
3084++ new_mic_muted = old_mic_muted = gvc_mixer_stream_get_is_muted (input_stream);
3085++ mic_changed = sound_changed = FALSE;
3086+
3087+ switch (type) {
3088+ case MUTE_KEY:
3089+@@ -1229,6 +1287,9 @@
3090+ if (!old_muted || old_vol == 0)
3091+ new_vol = MIN (old_vol + norm_vol_step, MAX_VOLUME);
3092+ break;
3093++ case MUTE_MIC_KEY:
3094++ new_mic_muted = !old_mic_muted;
3095++ break;
3096+ }
3097+
3098+ if (old_muted != new_muted) {
3099+@@ -1243,6 +1304,11 @@
3100+ }
3101+ }
3102+
3103++ if (old_mic_muted != new_mic_muted) {
3104++ gvc_mixer_stream_change_is_muted (manager->priv->input_stream, new_mic_muted);
3105++ mic_changed = TRUE;
3106++ }
3107++
3108+ if (type == VOLUME_DOWN_KEY && old_vol == 0 && old_muted)
3109+ osd_vol = -1;
3110+ else if (type == VOLUME_UP_KEY && old_vol == PA_VOLUME_NORM && !old_muted)
3111+@@ -1252,7 +1318,13 @@
3112+ else
3113+ osd_vol = 0;
3114+
3115+- update_dialog (manager, stream, osd_vol, new_muted, sound_changed, quiet);
3116++ if (mic_changed) {
3117++ osd_vol = (int) (100 * (double) new_mic_vol / PA_VOLUME_NORM);
3118++ if (new_mic_muted) osd_vol = 0;
3119++ update_mic_dialog(manager, stream, osd_vol, new_mic_muted, mic_changed, quiet);
3120++ } else {
3121++ update_speaker_dialog (manager, stream, osd_vol, new_muted, sound_changed, quiet);
3122++ }
3123+ }
3124+
3125+ static void
3126+@@ -1290,11 +1362,34 @@
3127+ }
3128+
3129+ static void
3130++update_default_source (GsdMediaKeysManager *manager)
3131++{
3132++ GvcMixerStream *input_stream;
3133++
3134++ input_stream =
3135++ gvc_mixer_control_get_default_source (manager->priv->volume);
3136++ if (input_stream == manager->priv->input_stream)
3137++ return;
3138++
3139++ if (manager->priv->input_stream != NULL) {
3140++ g_object_unref (manager->priv->input_stream);
3141++ manager->priv->input_stream = NULL;
3142++ }
3143++
3144++ if (input_stream != NULL) {
3145++ manager->priv->input_stream = g_object_ref (input_stream);
3146++ } else {
3147++ g_warning ("Unable to get default source");
3148++ }
3149++}
3150++
3151++static void
3152+ on_control_state_changed (GvcMixerControl *control,
3153+ GvcMixerControlState new_state,
3154+- GsdMediaKeysManager *manager)
3155++ GsdMediaKeysManager *manager)
3156+ {
3157+ update_default_sink (manager);
3158++ update_default_source (manager);
3159+ }
3160+
3161+ static void
3162+@@ -1318,6 +1413,14 @@
3163+ #endif /* HAVE_GUDEV */
3164+
3165+ static void
3166++on_control_default_source_changed (GvcMixerControl *control,
3167++ guint id,
3168++ GsdMediaKeysManager *manager)
3169++{
3170++ update_default_source (manager);
3171++}
3172++
3173++static void
3174+ on_control_stream_removed (GvcMixerControl *control,
3175+ guint id,
3176+ GsdMediaKeysManager *manager)
3177+@@ -1327,8 +1430,12 @@
3178+ g_object_unref (manager->priv->stream);
3179+ manager->priv->stream = NULL;
3180+ }
3181++ if (gvc_mixer_stream_get_id (manager->priv->input_stream)
3182++ == id) {
3183++ g_object_unref (manager->priv->input_stream);
3184++ manager->priv->input_stream = NULL;
3185++ }
3186+ }
3187+-
3188+ #ifdef HAVE_GUDEV
3189+ g_hash_table_foreach_remove (manager->priv->streams, (GHRFunc) remove_stream, GUINT_TO_POINTER (id));
3190+ #endif
3191+@@ -2001,6 +2108,9 @@
3192+ case VOLUME_UP_KEY:
3193+ do_sound_action (manager, deviceid, type, FALSE);
3194+ break;
3195++ case MUTE_MIC_KEY:
3196++ do_sound_action (manager, deviceid, type, TRUE);
3197++ break;
3198+ case MUTE_QUIET_KEY:
3199+ do_sound_action (manager, deviceid, MUTE_KEY, TRUE);
3200+ break;
3201+@@ -2360,6 +2470,10 @@
3202+ G_CALLBACK (on_control_default_sink_changed),
3203+ manager);
3204+ g_signal_connect (manager->priv->volume,
3205++ "default-source-changed",
3206++ G_CALLBACK (on_control_default_source_changed),
3207++ manager);
3208++ g_signal_connect (manager->priv->volume,
3209+ "stream-removed",
3210+ G_CALLBACK (on_control_stream_removed),
3211+ manager);
3212+--- a/plugins/media-keys/shortcuts-list.h
3213++++ b/plugins/media-keys/shortcuts-list.h
3214+@@ -32,6 +32,7 @@
3215+ MUTE_KEY,
3216+ VOLUME_DOWN_KEY,
3217+ VOLUME_UP_KEY,
3218++ MUTE_MIC_KEY,
3219+ MUTE_QUIET_KEY,
3220+ VOLUME_DOWN_QUIET_KEY,
3221+ VOLUME_UP_QUIET_KEY,
3222+@@ -94,6 +95,7 @@
3223+ { MUTE_KEY, "volume-mute", NULL },
3224+ { VOLUME_DOWN_KEY, "volume-down", NULL },
3225+ { VOLUME_UP_KEY, "volume-up", NULL },
3226++ { MUTE_MIC_KEY, NULL, "XF86AudioMicMute" },
3227+ { MUTE_QUIET_KEY, NULL, "<Alt>XF86AudioMute" },
3228+ { VOLUME_DOWN_QUIET_KEY, NULL, "<Alt>XF86AudioLowerVolume" },
3229+ { VOLUME_UP_QUIET_KEY, NULL, "<Alt>XF86AudioRaiseVolume" },
3230
3231=== modified file 'debian/patches/series'
3232--- debian/patches/series 2013-01-10 17:23:56 +0000
3233+++ debian/patches/series 2013-10-01 18:19:26 +0000
3234@@ -33,3 +33,4 @@
3235 git_no_numlock_eating_cpu_loop.patch
3236 git_new_xinput_handle.patch
3237 git-smartcard-crash.patch
3238+64_micmute.patch
3239
3240=== modified file 'plugins/media-keys/gsd-media-keys-manager.c'
3241--- plugins/media-keys/gsd-media-keys-manager.c 2012-05-23 21:41:42 +0000
3242+++ plugins/media-keys/gsd-media-keys-manager.c 2013-10-01 18:19:26 +0000
3243@@ -126,6 +126,7 @@
3244 /* Volume bits */
3245 GvcMixerControl *volume;
3246 GvcMixerStream *stream;
3247+ GvcMixerStream *input_stream;
3248 ca_context *ca;
3249 GtkSettings *gtksettings;
3250 #ifdef HAVE_GUDEV
3251@@ -199,6 +200,14 @@
3252 NULL
3253 };
3254
3255+static const char *mic_icons[] = {
3256+ "microphone-sensitivity-muted-symbolic",
3257+ "microphone-sensitivity-low-symbolic",
3258+ "microphone-sensitivity-medium-symbolic",
3259+ "microphone-sensitivity-high-symbolic",
3260+ NULL
3261+};
3262+
3263 static const char *brightness_icons[] = {
3264 "notification-display-brightness-off",
3265 "notification-display-brightness-low",
3266@@ -283,15 +292,24 @@
3267 }
3268
3269 static gboolean
3270-ubuntu_osd_notification_show_volume (GsdMediaKeysManager *manager,
3271- gint value,
3272- gboolean muted)
3273+ubuntu_osd_notification_show_speaker_volume (GsdMediaKeysManager *manager,
3274+ gint value,
3275+ gboolean muted)
3276 {
3277 return ubuntu_osd_do_notification (&manager->priv->volume_notification,
3278 "volume", value, muted, volume_icons);
3279 }
3280
3281 static gboolean
3282+ubuntu_osd_notification_show_mic_volume (GsdMediaKeysManager *manager,
3283+ gint value,
3284+ gboolean muted)
3285+{
3286+ return ubuntu_osd_do_notification (&manager->priv->volume_notification,
3287+ "volume", value, muted, mic_icons);
3288+}
3289+
3290+static gboolean
3291 ubuntu_osd_notification_show_brightness (GsdMediaKeysManager *manager,
3292 gint value)
3293 {
3294@@ -1049,14 +1067,14 @@
3295 }
3296
3297 static void
3298-update_dialog (GsdMediaKeysManager *manager,
3299- GvcMixerStream *stream,
3300- guint vol,
3301- gboolean muted,
3302- gboolean sound_changed,
3303- gboolean quiet)
3304+update_speaker_dialog (GsdMediaKeysManager *manager,
3305+ GvcMixerStream *stream,
3306+ guint vol,
3307+ gboolean muted,
3308+ gboolean sound_changed,
3309+ gboolean quiet)
3310 {
3311- if (ubuntu_osd_notification_show_volume (manager, vol, muted))
3312+ if (ubuntu_osd_notification_show_speaker_volume (manager, vol, muted))
3313 goto done;
3314
3315 vol = CLAMP (vol, 0, 100);
3316@@ -1081,6 +1099,39 @@
3317 }
3318 }
3319
3320+static void
3321+update_mic_dialog (GsdMediaKeysManager *manager,
3322+ GvcMixerStream *stream,
3323+ guint vol,
3324+ gboolean muted,
3325+ gboolean sound_changed,
3326+ gboolean quiet)
3327+{
3328+ vol = CLAMP (vol, 0, 100);
3329+ ubuntu_osd_notification_show_mic_volume (manager, vol, muted);
3330+
3331+ if (quiet == FALSE) {
3332+ GtkWidget *window;
3333+ if (!manager->priv->dialog) {
3334+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3335+ gtk_window_set_screen (GTK_WINDOW (window), manager->priv->current_screen);
3336+ gtk_widget_realize (window);
3337+
3338+ } else {
3339+ window = manager->priv->dialog;
3340+ }
3341+ ca_gtk_play_for_widget (manager->priv->dialog, 0,
3342+ CA_PROP_EVENT_ID, "audio-volume-change",
3343+ CA_PROP_EVENT_DESCRIPTION, "volume changed through key press",
3344+ CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
3345+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
3346+ NULL);
3347+
3348+ if (!manager->priv->dialog)
3349+ gtk_widget_destroy(window);
3350+ }
3351+}
3352+
3353 #ifdef HAVE_GUDEV
3354 /* PulseAudio gives us /devices/... paths, when udev
3355 * expects /sys/devices/... paths. */
3356@@ -1190,10 +1241,12 @@
3357 int type,
3358 gboolean quiet)
3359 {
3360- GvcMixerStream *stream;
3361+ GvcMixerStream *stream, *input_stream;
3362 gboolean old_muted, new_muted;
3363 guint old_vol, new_vol, norm_vol_step, osd_vol;
3364- gboolean sound_changed;
3365+ gboolean old_mic_muted, new_mic_muted;
3366+ guint old_mic_vol, new_mic_vol;
3367+ gboolean sound_changed, mic_changed;
3368
3369 /* Find the stream that corresponds to the device, if any */
3370 #ifdef HAVE_GUDEV
3371@@ -1203,13 +1256,18 @@
3372 stream = manager->priv->stream;
3373 if (stream == NULL)
3374 return;
3375+ input_stream = manager->priv->input_stream;
3376+ if (input_stream == NULL)
3377+ return;
3378
3379 norm_vol_step = PA_VOLUME_NORM * VOLUME_STEP / 100;
3380
3381 /* FIXME: this is racy */
3382 new_vol = old_vol = gvc_mixer_stream_get_volume (stream);
3383+ new_mic_vol = old_mic_vol = gvc_mixer_stream_get_volume (input_stream);
3384 new_muted = old_muted = gvc_mixer_stream_get_is_muted (stream);
3385- sound_changed = FALSE;
3386+ new_mic_muted = old_mic_muted = gvc_mixer_stream_get_is_muted (input_stream);
3387+ mic_changed = sound_changed = FALSE;
3388
3389 switch (type) {
3390 case MUTE_KEY:
3391@@ -1229,6 +1287,9 @@
3392 if (!old_muted || old_vol == 0)
3393 new_vol = MIN (old_vol + norm_vol_step, MAX_VOLUME);
3394 break;
3395+ case MUTE_MIC_KEY:
3396+ new_mic_muted = !old_mic_muted;
3397+ break;
3398 }
3399
3400 if (old_muted != new_muted) {
3401@@ -1243,6 +1304,11 @@
3402 }
3403 }
3404
3405+ if (old_mic_muted != new_mic_muted) {
3406+ gvc_mixer_stream_change_is_muted (manager->priv->input_stream, new_mic_muted);
3407+ mic_changed = TRUE;
3408+ }
3409+
3410 if (type == VOLUME_DOWN_KEY && old_vol == 0 && old_muted)
3411 osd_vol = -1;
3412 else if (type == VOLUME_UP_KEY && old_vol == PA_VOLUME_NORM && !old_muted)
3413@@ -1252,7 +1318,13 @@
3414 else
3415 osd_vol = 0;
3416
3417- update_dialog (manager, stream, osd_vol, new_muted, sound_changed, quiet);
3418+ if (mic_changed) {
3419+ osd_vol = (int) (100 * (double) new_mic_vol / PA_VOLUME_NORM);
3420+ if (new_mic_muted) osd_vol = 0;
3421+ update_mic_dialog(manager, stream, osd_vol, new_mic_muted, mic_changed, quiet);
3422+ } else {
3423+ update_speaker_dialog (manager, stream, osd_vol, new_muted, sound_changed, quiet);
3424+ }
3425 }
3426
3427 static void
3428@@ -1290,11 +1362,34 @@
3429 }
3430
3431 static void
3432+update_default_source (GsdMediaKeysManager *manager)
3433+{
3434+ GvcMixerStream *input_stream;
3435+
3436+ input_stream =
3437+ gvc_mixer_control_get_default_source (manager->priv->volume);
3438+ if (input_stream == manager->priv->input_stream)
3439+ return;
3440+
3441+ if (manager->priv->input_stream != NULL) {
3442+ g_object_unref (manager->priv->input_stream);
3443+ manager->priv->input_stream = NULL;
3444+ }
3445+
3446+ if (input_stream != NULL) {
3447+ manager->priv->input_stream = g_object_ref (input_stream);
3448+ } else {
3449+ g_warning ("Unable to get default source");
3450+ }
3451+}
3452+
3453+static void
3454 on_control_state_changed (GvcMixerControl *control,
3455 GvcMixerControlState new_state,
3456- GsdMediaKeysManager *manager)
3457+ GsdMediaKeysManager *manager)
3458 {
3459 update_default_sink (manager);
3460+ update_default_source (manager);
3461 }
3462
3463 static void
3464@@ -1318,6 +1413,14 @@
3465 #endif /* HAVE_GUDEV */
3466
3467 static void
3468+on_control_default_source_changed (GvcMixerControl *control,
3469+ guint id,
3470+ GsdMediaKeysManager *manager)
3471+{
3472+ update_default_source (manager);
3473+}
3474+
3475+static void
3476 on_control_stream_removed (GvcMixerControl *control,
3477 guint id,
3478 GsdMediaKeysManager *manager)
3479@@ -1327,8 +1430,12 @@
3480 g_object_unref (manager->priv->stream);
3481 manager->priv->stream = NULL;
3482 }
3483+ if (gvc_mixer_stream_get_id (manager->priv->input_stream)
3484+ == id) {
3485+ g_object_unref (manager->priv->input_stream);
3486+ manager->priv->input_stream = NULL;
3487+ }
3488 }
3489-
3490 #ifdef HAVE_GUDEV
3491 g_hash_table_foreach_remove (manager->priv->streams, (GHRFunc) remove_stream, GUINT_TO_POINTER (id));
3492 #endif
3493@@ -2001,6 +2108,9 @@
3494 case VOLUME_UP_KEY:
3495 do_sound_action (manager, deviceid, type, FALSE);
3496 break;
3497+ case MUTE_MIC_KEY:
3498+ do_sound_action (manager, deviceid, type, TRUE);
3499+ break;
3500 case MUTE_QUIET_KEY:
3501 do_sound_action (manager, deviceid, MUTE_KEY, TRUE);
3502 break;
3503@@ -2360,6 +2470,10 @@
3504 G_CALLBACK (on_control_default_sink_changed),
3505 manager);
3506 g_signal_connect (manager->priv->volume,
3507+ "default-source-changed",
3508+ G_CALLBACK (on_control_default_source_changed),
3509+ manager);
3510+ g_signal_connect (manager->priv->volume,
3511 "stream-removed",
3512 G_CALLBACK (on_control_stream_removed),
3513 manager);
3514
3515=== modified file 'plugins/media-keys/shortcuts-list.h'
3516--- plugins/media-keys/shortcuts-list.h 2012-02-15 11:41:52 +0000
3517+++ plugins/media-keys/shortcuts-list.h 2013-10-01 18:19:26 +0000
3518@@ -32,6 +32,7 @@
3519 MUTE_KEY,
3520 VOLUME_DOWN_KEY,
3521 VOLUME_UP_KEY,
3522+ MUTE_MIC_KEY,
3523 MUTE_QUIET_KEY,
3524 VOLUME_DOWN_QUIET_KEY,
3525 VOLUME_UP_QUIET_KEY,
3526@@ -94,6 +95,7 @@
3527 { MUTE_KEY, "volume-mute", NULL },
3528 { VOLUME_DOWN_KEY, "volume-down", NULL },
3529 { VOLUME_UP_KEY, "volume-up", NULL },
3530+ { MUTE_MIC_KEY, NULL, "XF86AudioMicMute" },
3531 { MUTE_QUIET_KEY, NULL, "<Alt>XF86AudioMute" },
3532 { VOLUME_DOWN_QUIET_KEY, NULL, "<Alt>XF86AudioLowerVolume" },
3533 { VOLUME_UP_QUIET_KEY, NULL, "<Alt>XF86AudioRaiseVolume" },

Subscribers

People subscribed via source and target branches