1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2001-2003 Bastien Nocera <hadess@hadess.net>
4  * Copyright (C) 2006-2007 William Jon McCann <mccann@jhu.edu>
5  * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
6  * Copyright (C) 2012-2021 MATE Developers
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  */
23 
24 #include "config.h"
25 
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdk.h>
29 #include <gdk/gdkx.h>
30 #include <gtk/gtk.h>
31 #include <gio/gio.h>
32 
33 #include <dbus/dbus-glib.h>
34 #include <dbus/dbus-glib-lowlevel.h>
35 
36 #ifdef HAVE_LIBMATEMIXER
37 #include <libmatemixer/matemixer.h>
38 #endif
39 
40 #ifdef HAVE_LIBCANBERRA
41 #include <canberra-gtk.h>
42 #endif
43 
44 #include "mate-settings-profile.h"
45 #include "msd-marshal.h"
46 #include "msd-media-keys-manager.h"
47 #include "msd-media-keys-manager-glue.h"
48 
49 #include "eggaccelerators.h"
50 #include "acme.h"
51 #include "msd-media-keys-window.h"
52 #include "msd-input-helper.h"
53 
54 #define MSD_DBUS_PATH "/org/mate/SettingsDaemon"
55 #define MSD_DBUS_NAME "org.mate.SettingsDaemon"
56 #define MSD_MEDIA_KEYS_DBUS_PATH MSD_DBUS_PATH "/MediaKeys"
57 #define MSD_MEDIA_KEYS_DBUS_NAME MSD_DBUS_NAME ".MediaKeys"
58 
59 #define TOUCHPAD_SCHEMA "org.mate.peripherals-touchpad"
60 #define TOUCHPAD_ENABLED_KEY "touchpad-enabled"
61 
62 typedef struct {
63         char   *application;
64         guint32 time;
65 } MediaPlayer;
66 
67 struct _MsdMediaKeysManagerPrivate
68 {
69 #ifdef HAVE_LIBMATEMIXER
70         /* Volume bits */
71         MateMixerContext       *context;
72         MateMixerStream        *stream;
73         MateMixerStream        *source_stream;
74         MateMixerStreamControl *control;
75         MateMixerStreamControl *source_control;
76 #endif
77         GtkWidget        *dialog;
78         GSettings        *settings;
79         GVolumeMonitor   *volume_monitor;
80 
81         /* Multihead stuff */
82         GdkScreen        *current_screen;
83         GSList           *screens;
84 
85         /* RFKill stuff */
86         guint            rfkill_watch_id;
87         GDBusProxy      *rfkill_proxy;
88         GCancellable    *rfkill_cancellable;
89 
90         GList            *media_players;
91 
92         DBusGConnection  *connection;
93         guint             notify[HANDLED_KEYS];
94 };
95 
96 enum {
97         MEDIA_PLAYER_KEY_PRESSED,
98         LAST_SIGNAL
99 };
100 
101 static guint signals[LAST_SIGNAL] = { 0 };
102 
103 G_DEFINE_TYPE_WITH_PRIVATE (MsdMediaKeysManager, msd_media_keys_manager, G_TYPE_OBJECT)
104 
105 static gpointer manager_object = NULL;
106 
107 static void
init_screens(MsdMediaKeysManager * manager)108 init_screens (MsdMediaKeysManager *manager)
109 {
110         GdkDisplay *display;
111 
112         display = gdk_display_get_default ();
113 
114         GdkScreen *screen;
115 
116         screen = gdk_display_get_default_screen (display);
117 
118         if (screen != NULL) {
119                 manager->priv->screens = g_slist_append (manager->priv->screens, screen);
120         }
121 
122         manager->priv->current_screen = manager->priv->screens->data;
123 }
124 
125 static void
acme_error(char * msg)126 acme_error (char * msg)
127 {
128         GtkWidget *error_dialog;
129 
130         error_dialog = gtk_message_dialog_new (NULL,
131                                                GTK_DIALOG_MODAL,
132                                                GTK_MESSAGE_ERROR,
133                                                GTK_BUTTONS_OK,
134                                                msg, NULL);
135         gtk_dialog_set_default_response (GTK_DIALOG (error_dialog),
136                                          GTK_RESPONSE_OK);
137         gtk_widget_show (error_dialog);
138         g_signal_connect (error_dialog,
139                           "response",
140                           G_CALLBACK (gtk_widget_destroy),
141                           NULL);
142 }
143 
144 static char *
get_term_command(MsdMediaKeysManager * manager)145 get_term_command (MsdMediaKeysManager *manager)
146 {
147 	char *cmd_term, *cmd_args;
148 	char *cmd = NULL;
149 	GSettings *settings;
150 
151 	settings = g_settings_new ("org.mate.applications-terminal");
152 	cmd_term = g_settings_get_string (settings, "exec");
153 	cmd_args = g_settings_get_string (settings, "exec-arg");
154 
155 	if (cmd_term[0] != '\0') {
156 		cmd = g_strdup_printf ("%s %s -e", cmd_term, cmd_args);
157 	} else {
158 		cmd = g_strdup_printf ("mate-terminal -e");
159 	}
160 
161 	g_free (cmd_args);
162 	g_free (cmd_term);
163 	g_object_unref (settings);
164 
165         return cmd;
166 }
167 
168 static void
execute(MsdMediaKeysManager * manager,char * cmd,gboolean sync,gboolean need_term)169 execute (MsdMediaKeysManager *manager,
170          char                *cmd,
171          gboolean             sync,
172          gboolean             need_term)
173 {
174         gboolean retval;
175         char   **argv;
176         int      argc;
177         char    *exec;
178         char    *term = NULL;
179 
180         retval = FALSE;
181 
182         if (need_term) {
183                 term = get_term_command (manager);
184                 if (term == NULL) {
185                         acme_error (_("Could not get default terminal. Verify that your default "
186                                       "terminal command is set and points to a valid application."));
187                         return;
188                 }
189         }
190 
191         if (term) {
192                 exec = g_strdup_printf ("%s %s", term, cmd);
193                 g_free (term);
194         } else {
195                 exec = g_strdup (cmd);
196         }
197 
198         if (g_shell_parse_argv (exec, &argc, &argv, NULL)) {
199                 if (sync != FALSE) {
200                         retval = g_spawn_sync (g_get_home_dir (),
201                                                argv,
202                                                NULL,
203                                                G_SPAWN_SEARCH_PATH,
204                                                NULL,
205                                                NULL,
206                                                NULL,
207                                                NULL,
208                                                NULL,
209                                                NULL);
210                 } else {
211                         retval = g_spawn_async (g_get_home_dir (),
212                                                 argv,
213                                                 NULL,
214                                                 G_SPAWN_SEARCH_PATH,
215                                                 NULL,
216                                                 NULL,
217                                                 NULL,
218                                                 NULL);
219                 }
220                 g_strfreev (argv);
221         }
222 
223         if (retval == FALSE) {
224                 char *msg;
225                 msg = g_strdup_printf (_("Couldn't execute command: %s\n"
226                                          "Verify that this is a valid command."),
227                                        exec);
228 
229                 acme_error (msg);
230                 g_free (msg);
231         }
232         g_free (exec);
233 }
234 
235 static void
dialog_init(MsdMediaKeysManager * manager)236 dialog_init (MsdMediaKeysManager *manager)
237 {
238         if (manager->priv->dialog != NULL
239             && !msd_osd_window_is_valid (MSD_OSD_WINDOW (manager->priv->dialog))) {
240                 gtk_widget_destroy (manager->priv->dialog);
241                 manager->priv->dialog = NULL;
242         }
243 
244         if (manager->priv->dialog == NULL) {
245                 manager->priv->dialog = msd_media_keys_window_new ();
246         }
247 }
248 
249 static gboolean
is_valid_shortcut(const char * string)250 is_valid_shortcut (const char *string)
251 {
252         if (string == NULL || string[0] == '\0') {
253                 return FALSE;
254         }
255         if (strcmp (string, "disabled") == 0) {
256                 return FALSE;
257         }
258 
259         return TRUE;
260 }
261 
262 static void
update_kbd_cb(GSettings * settings,gchar * settings_key,MsdMediaKeysManager * manager)263 update_kbd_cb (GSettings           *settings,
264                gchar               *settings_key,
265                MsdMediaKeysManager *manager)
266 {
267         int      i;
268         GdkDisplay *dpy;
269         gboolean need_flush = TRUE;
270 
271         g_return_if_fail (settings_key != NULL);
272 
273         dpy = gdk_display_get_default ();
274         gdk_x11_display_error_trap_push (dpy);
275 
276         /* Find the key that was modified */
277         for (i = 0; i < HANDLED_KEYS; i++) {
278                 if (g_strcmp0 (settings_key, keys[i].settings_key) == 0) {
279                         char *tmp;
280                         Key  *key;
281 
282                         if (keys[i].key != NULL) {
283                                 need_flush = TRUE;
284                                 grab_key_unsafe (keys[i].key, FALSE, manager->priv->screens);
285                         }
286 
287                         g_free (keys[i].key);
288                         keys[i].key = NULL;
289 
290                         /* We can't have a change in a hard-coded key */
291                         g_assert (keys[i].settings_key != NULL);
292 
293                         tmp = g_settings_get_string (settings,
294                                                      keys[i].settings_key);
295 
296                         if (is_valid_shortcut (tmp) == FALSE) {
297                                 g_free (tmp);
298                                 break;
299                         }
300 
301                         key = g_new0 (Key, 1);
302                         if (!egg_accelerator_parse_virtual (tmp, &key->keysym, &key->keycodes, &key->state)) {
303                                 g_free (tmp);
304                                 g_free (key);
305                                 break;
306                         }
307 
308                         need_flush = TRUE;
309                         grab_key_unsafe (key, TRUE, manager->priv->screens);
310                         keys[i].key = key;
311 
312                         g_free (tmp);
313 
314                         break;
315                 }
316         }
317 
318         if (need_flush)
319                 gdk_display_flush (dpy);
320         if (gdk_x11_display_error_trap_pop (dpy))
321                 g_warning ("Grab failed for some keys, another application may already have access the them.");
322 }
323 
init_kbd(MsdMediaKeysManager * manager)324 static void init_kbd(MsdMediaKeysManager* manager)
325 {
326 	int i;
327 	GdkDisplay *dpy;
328 	gboolean need_flush = FALSE;
329 
330 	mate_settings_profile_start(NULL);
331 
332 	dpy = gdk_display_get_default ();
333 	gdk_x11_display_error_trap_push (dpy);
334 
335 	for (i = 0; i < HANDLED_KEYS; i++)
336 	{
337 		char* tmp;
338 		Key* key;
339 
340 		gchar* signal_name;
341 		signal_name = g_strdup_printf ("changed::%s", keys[i].settings_key);
342 		g_signal_connect (manager->priv->settings,
343 						  signal_name,
344 						  G_CALLBACK (update_kbd_cb),
345 						  manager);
346 		g_free (signal_name);
347 
348 		if (keys[i].settings_key != NULL) {
349 			tmp = g_settings_get_string (manager->priv->settings, keys[i].settings_key);
350 		} else {
351 			tmp = g_strdup (keys[i].hard_coded);
352 		}
353 
354 		if (!is_valid_shortcut(tmp))
355 		{
356 			g_debug("Not a valid shortcut: '%s'", tmp);
357 			g_free(tmp);
358 			continue;
359 		}
360 
361 		key = g_new0(Key, 1);
362 
363 		if (!egg_accelerator_parse_virtual(tmp, &key->keysym, &key->keycodes, &key->state))
364 		{
365 			g_debug("Unable to parse: '%s'", tmp);
366 			g_free(tmp);
367 			g_free(key);
368 			continue;
369 		}
370 
371 		g_free(tmp);
372 
373 		keys[i].key = key;
374 
375 		need_flush = TRUE;
376 		grab_key_unsafe(key, TRUE, manager->priv->screens);
377 	}
378 
379 	if (need_flush)
380 	{
381 		gdk_display_flush (dpy);
382 	}
383 
384 	if (gdk_x11_display_error_trap_pop (dpy))
385 	{
386 		g_warning("Grab failed for some keys, another application may already have access the them.");
387 	}
388 
389 	mate_settings_profile_end(NULL);
390 }
391 
392 static void
ensure_cancellable(GCancellable ** cancellable)393 ensure_cancellable (GCancellable **cancellable)
394 {
395         if (*cancellable == NULL) {
396                 *cancellable = g_cancellable_new ();
397                 g_object_add_weak_pointer (G_OBJECT (*cancellable),
398                                            (gpointer *)cancellable);
399         } else {
400                 g_object_ref (*cancellable);
401         }
402 }
403 
404 static void
dialog_show(MsdMediaKeysManager * manager)405 dialog_show (MsdMediaKeysManager *manager)
406 {
407         int            orig_w;
408         int            orig_h;
409         int            screen_w;
410         int            screen_h;
411         int            x;
412         int            y;
413         GdkDisplay    *display;
414         GdkSeat       *seat;
415         GdkDevice     *pointer;
416         int            pointer_x;
417         int            pointer_y;
418         GtkRequisition win_req;
419         GdkScreen     *pointer_screen;
420         GdkRectangle   geometry;
421         GdkMonitor    *monitor;
422 
423         gtk_window_set_screen (GTK_WINDOW (manager->priv->dialog),
424                                manager->priv->current_screen);
425 
426         /* Return if OSD notifications are disabled */
427         if (!g_settings_get_boolean (manager->priv->settings, "enable-osd"))
428                 return;
429 
430         /*
431          * get the window size
432          * if the window hasn't been mapped, it doesn't necessarily
433          * know its true size, yet, so we need to jump through hoops
434          */
435         gtk_window_get_default_size (GTK_WINDOW (manager->priv->dialog), &orig_w, &orig_h);
436         gtk_widget_get_preferred_size (manager->priv->dialog, NULL, &win_req);
437 
438         if (win_req.width > orig_w) {
439                 orig_w = win_req.width;
440         }
441         if (win_req.height > orig_h) {
442                 orig_h = win_req.height;
443         }
444 
445         pointer_screen = NULL;
446         display = gdk_screen_get_display (manager->priv->current_screen);
447         seat = gdk_display_get_default_seat (display);
448         pointer = gdk_seat_get_pointer (seat);
449 
450         gdk_device_get_position (pointer,
451                                  &pointer_screen,
452                                  &pointer_x,
453                                  &pointer_y);
454 
455         if (pointer_screen != manager->priv->current_screen) {
456                 /* The pointer isn't on the current screen, so just
457                  * assume the default monitor
458                  */
459                 monitor = gdk_display_get_monitor (display, 0);
460         } else {
461                 monitor = gdk_display_get_monitor_at_point (display, pointer_x, pointer_y);
462         }
463 
464         gdk_monitor_get_geometry (monitor, &geometry);
465 
466         screen_w = geometry.width;
467         screen_h = geometry.height;
468 
469         x = ((screen_w - orig_w) / 2) + geometry.x;
470         y = geometry.y + (screen_h / 2) + (screen_h / 2 - orig_h) / 2;
471 
472         gtk_window_move (GTK_WINDOW (manager->priv->dialog), x, y);
473 
474         gtk_widget_show (manager->priv->dialog);
475 
476         gdk_display_sync (gdk_screen_get_display (manager->priv->current_screen));
477 }
478 
479 static void
do_url_action(MsdMediaKeysManager * manager,const gchar * scheme)480 do_url_action (MsdMediaKeysManager *manager,
481                const gchar         *scheme)
482 {
483         GError *error = NULL;
484         GAppInfo *app_info;
485 
486         app_info = g_app_info_get_default_for_uri_scheme (scheme);
487 
488         if (app_info != NULL) {
489            if (!g_app_info_launch (app_info, NULL, NULL, &error)) {
490                 g_warning ("Could not launch '%s': %s",
491                     g_app_info_get_commandline (app_info),
492                     error->message);
493 		g_object_unref (app_info);
494                 g_error_free (error);
495             }
496         }
497         else {
498             g_warning ("Could not find default application for '%s' scheme", scheme);
499         }
500 }
501 
502 static void
do_media_action(MsdMediaKeysManager * manager)503 do_media_action (MsdMediaKeysManager *manager)
504 {
505         GError *error = NULL;
506         GAppInfo *app_info;
507 
508         app_info = g_app_info_get_default_for_type ("audio/x-vorbis+ogg", FALSE);
509 
510         if (app_info != NULL) {
511            if (!g_app_info_launch (app_info, NULL, NULL, &error)) {
512                 g_warning ("Could not launch '%s': %s",
513                     g_app_info_get_commandline (app_info),
514                     error->message);
515                 g_error_free (error);
516             }
517         }
518         else {
519             g_warning ("Could not find default application for '%s' mime-type", "audio/x-vorbis+ogg");
520         }
521 }
522 
523 static void
do_calculator_action(MsdMediaKeysManager * manager)524 do_calculator_action (MsdMediaKeysManager *manager)
525 {
526         GSettings *settings;
527         char *calc;
528 
529         settings = g_settings_new ("org.mate.applications-calculator");
530         calc = g_settings_get_string (settings, "exec");
531 
532         if (calc)
533                 execute (manager, calc, FALSE, FALSE);
534 
535         g_free (calc);
536         g_object_unref (settings);
537 }
538 
539 static void
do_messenger_action(MsdMediaKeysManager * manager)540 do_messenger_action (MsdMediaKeysManager *manager)
541 {
542         GSettings *settings;
543         char *messenger;
544 
545         settings = g_settings_new ("org.mate.applications-messenger");
546         messenger = g_settings_get_string (settings, "exec");
547 
548         if (messenger)
549                 execute (manager, messenger, FALSE, FALSE);
550 
551         g_free (messenger);
552         g_object_unref (settings);
553 }
554 
555 static void
do_shutdown_action(MsdMediaKeysManager * manager)556 do_shutdown_action (MsdMediaKeysManager *manager)
557 {
558         execute (manager, "mate-session-save --shutdown-dialog", FALSE, FALSE);
559 }
560 
561 static void
do_logout_action(MsdMediaKeysManager * manager)562 do_logout_action (MsdMediaKeysManager *manager)
563 {
564         execute (manager, "mate-session-save --logout-dialog", FALSE, FALSE);
565 }
566 
567 static void
do_eject_action_cb(GDrive * drive,GAsyncResult * res,MsdMediaKeysManager * manager)568 do_eject_action_cb (GDrive              *drive,
569                     GAsyncResult        *res,
570                     MsdMediaKeysManager *manager)
571 {
572         g_drive_eject_with_operation_finish (drive, res, NULL);
573 }
574 
575 #define NO_SCORE 0
576 #define SCORE_CAN_EJECT 50
577 #define SCORE_HAS_MEDIA 100
578 static void
do_eject_action(MsdMediaKeysManager * manager)579 do_eject_action (MsdMediaKeysManager *manager)
580 {
581         GList *drives, *l;
582         GDrive *fav_drive;
583         guint score;
584 
585         /* Find the best drive to eject */
586         fav_drive = NULL;
587         score = NO_SCORE;
588         drives = g_volume_monitor_get_connected_drives (manager->priv->volume_monitor);
589         for (l = drives; l != NULL; l = l->next) {
590                 GDrive *drive = l->data;
591 
592                 if (g_drive_can_eject (drive) == FALSE)
593                         continue;
594                 if (g_drive_is_media_removable (drive) == FALSE)
595                         continue;
596                 if (score < SCORE_CAN_EJECT) {
597                         fav_drive = drive;
598                         score = SCORE_CAN_EJECT;
599                 }
600                 if (g_drive_has_media (drive) == FALSE)
601                         continue;
602                 if (score < SCORE_HAS_MEDIA) {
603                         fav_drive = drive;
604                         break;
605                 }
606         }
607 
608         /* Show the dialogue */
609         dialog_init (manager);
610         msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
611                                                  "media-eject",
612                                                  NULL);
613         dialog_show (manager);
614 
615         /* Clean up the drive selection and exit if no suitable
616          * drives are found */
617         if (fav_drive != NULL)
618                 fav_drive = g_object_ref (fav_drive);
619 
620         g_list_free_full (drives, g_object_unref);
621         if (fav_drive == NULL)
622                 return;
623 
624         /* Eject! */
625         g_drive_eject_with_operation (fav_drive, G_MOUNT_UNMOUNT_FORCE,
626                                       NULL, NULL,
627                                       (GAsyncReadyCallback) do_eject_action_cb,
628                                       manager);
629         g_object_unref (fav_drive);
630 }
631 
632 static void
do_touchpad_osd_action(MsdMediaKeysManager * manager,gboolean state)633 do_touchpad_osd_action (MsdMediaKeysManager *manager, gboolean state)
634 {
635         dialog_init (manager);
636         msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
637                                                  state ? "input-touchpad" : "touchpad-disabled",
638                                                  state ? _("Touchpad enabled") : _("Touchpad disabled"));
639         dialog_show (manager);
640 }
641 
642 static void
do_touchpad_action(MsdMediaKeysManager * manager)643 do_touchpad_action (MsdMediaKeysManager *manager)
644 {
645         GSettings *settings = g_settings_new (TOUCHPAD_SCHEMA);
646         gboolean state = g_settings_get_boolean (settings, TOUCHPAD_ENABLED_KEY);
647 
648         if (touchpad_is_present () == FALSE) {
649                 do_touchpad_osd_action (manager, FALSE);
650                 return;
651         }
652 
653         do_touchpad_osd_action (manager, !state);
654 
655         g_settings_set_boolean (settings, TOUCHPAD_ENABLED_KEY, !state);
656         g_object_unref (settings);
657 }
658 
659 #ifdef HAVE_LIBMATEMIXER
660 static void
update_dialog(MsdMediaKeysManager * manager,guint volume,gboolean muted,gboolean sound_changed,gboolean quiet,gboolean is_mic)661 update_dialog (MsdMediaKeysManager *manager,
662                guint                volume,
663                gboolean             muted,
664                gboolean             sound_changed,
665                gboolean             quiet,
666                gboolean             is_mic)
667 {
668         if (muted)
669                 volume = 0;
670 
671         dialog_init (manager);
672 
673         if (is_mic)
674                 msd_media_keys_window_set_mic_muted (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
675                                                      muted);
676         else
677                 msd_media_keys_window_set_volume_muted (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
678                                                         muted);
679 
680         msd_media_keys_window_set_volume_level (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
681                                                 volume);
682 
683         msd_media_keys_window_set_action (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
684                                           MSD_MEDIA_KEYS_WINDOW_ACTION_VOLUME);
685         dialog_show (manager);
686 
687 #ifdef HAVE_LIBCANBERRA
688         if (quiet == FALSE && sound_changed != FALSE && muted == FALSE && is_mic == FALSE)
689                 ca_gtk_play_for_widget (manager->priv->dialog, 0,
690                                         CA_PROP_EVENT_ID, "audio-volume-change",
691                                         CA_PROP_EVENT_DESCRIPTION, "Volume changed through key press",
692                                         CA_PROP_APPLICATION_NAME, PACKAGE_NAME,
693                                         CA_PROP_APPLICATION_VERSION, PACKAGE_VERSION,
694                                         CA_PROP_APPLICATION_ID, "org.mate.SettingsDaemon",
695                                         NULL);
696 #endif
697 }
698 
699 static void
do_sound_action(MsdMediaKeysManager * manager,int type,gboolean quiet)700 do_sound_action (MsdMediaKeysManager *manager,
701                  int type,
702                  gboolean quiet)
703 {
704         gboolean muted;
705         gboolean muted_last;
706         gboolean sound_changed = FALSE;
707         guint    volume;
708         guint    volume_min, volume_max;
709         gint     volume_step;
710         guint    volume_step_scaled;
711         guint    volume_last;
712         MateMixerStreamControl *control;
713 
714         gboolean is_input_control =
715                 type == MIC_MUTE_KEY ? TRUE : FALSE;
716         if (is_input_control)
717                 control = manager->priv->source_control;
718         else
719                 control = manager->priv->control;
720         if (control == NULL)
721                 return;
722 
723         /* Theoretically the volume limits might be different for different
724          * streams, also the minimum might not always start at 0 */
725         volume_min = mate_mixer_stream_control_get_min_volume (control);
726         volume_max = mate_mixer_stream_control_get_normal_volume (control);
727 
728         volume_step = g_settings_get_int (manager->priv->settings, "volume-step");
729         if (volume_step <= 0 || volume_step > 100) {
730                 GVariant *variant = g_settings_get_default_value (manager->priv->settings, "volume-step");
731                 gint32 volume_step_default = g_variant_get_int32 (variant);
732                 volume_step = (gint) volume_step_default;
733                 g_variant_unref (variant);
734         }
735 
736         /* Scale the volume step size accordingly to the range used by the control */
737         volume_step_scaled = (volume_max - volume_min) * (guint) volume_step / 100;
738 
739         volume = volume_last =
740                 mate_mixer_stream_control_get_volume (control);
741         muted = muted_last =
742                 mate_mixer_stream_control_get_mute (control);
743 
744         switch (type) {
745         case MUTE_KEY:
746         case MIC_MUTE_KEY:
747                 muted = !muted;
748                 break;
749         case VOLUME_DOWN_KEY:
750                 if (volume <= (volume_min + volume_step_scaled)) {
751                         volume = volume_min;
752                         muted  = TRUE;
753                 } else {
754                         volume -= volume_step_scaled;
755                         muted  = FALSE;
756                 }
757                 break;
758         case VOLUME_UP_KEY:
759                 if (muted) {
760                         muted = FALSE;
761                         if (volume <= volume_min)
762                                volume = volume_min + volume_step_scaled;
763                 } else
764                         volume = CLAMP (volume + volume_step_scaled,
765                                         volume_min,
766                                         volume_max);
767                 break;
768         }
769 
770         if (muted != muted_last) {
771                 if (mate_mixer_stream_control_set_mute (control, muted))
772                         sound_changed = TRUE;
773                 else
774                         muted = muted_last;
775         }
776 
777         if (volume != mate_mixer_stream_control_get_volume (control)) {
778                 if (mate_mixer_stream_control_set_volume (control, volume))
779                         sound_changed = TRUE;
780                 else
781                         volume = volume_last;
782         }
783 
784         update_dialog (manager,
785                        MIN (100 * volume / (volume_max - volume_min), 100),
786                        muted,
787                        sound_changed,
788                        quiet,
789                        is_input_control);
790 }
791 
792 static void
update_default_output(MsdMediaKeysManager * manager)793 update_default_output (MsdMediaKeysManager *manager)
794 {
795         MateMixerStream        *stream;
796         MateMixerStreamControl *control = NULL;
797 
798         stream = mate_mixer_context_get_default_output_stream (manager->priv->context);
799         if (stream != NULL)
800                 control = mate_mixer_stream_get_default_control (stream);
801 
802         if (stream == manager->priv->stream)
803                 return;
804 
805         g_clear_object (&manager->priv->stream);
806         g_clear_object (&manager->priv->control);
807 
808         if (control != NULL) {
809                 MateMixerStreamControlFlags flags = mate_mixer_stream_control_get_flags (control);
810 
811                 /* Do not use the stream if it is not possible to mute it or
812                  * change the volume */
813                 if (!(flags & MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE) &&
814                     !(flags & MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE))
815                         return;
816 
817                 manager->priv->stream  = g_object_ref (stream);
818                 manager->priv->control = g_object_ref (control);
819                 g_debug ("Default output stream updated to %s",
820                          mate_mixer_stream_get_name (stream));
821         } else
822                 g_debug ("Default output stream unset");
823 }
824 
825 static void
update_default_input(MsdMediaKeysManager * manager)826 update_default_input (MsdMediaKeysManager *manager)
827 {
828         MateMixerStream        *stream;
829         MateMixerStreamControl *control = NULL;
830 
831         stream = mate_mixer_context_get_default_input_stream (manager->priv->context);
832         if (stream != NULL)
833                 control = mate_mixer_stream_get_default_control (stream);
834 
835         if (stream == manager->priv->source_stream)
836                 return;
837 
838         g_clear_object (&manager->priv->source_stream);
839         g_clear_object (&manager->priv->source_control);
840 
841         if (control != NULL) {
842                 MateMixerStreamControlFlags flags = mate_mixer_stream_control_get_flags (control);
843 
844                 /* Do not use the stream if it is not possible to mute it or
845                  * change the volume */
846                 if (!(flags & MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE))
847                         return;
848 
849                 manager->priv->source_stream  = g_object_ref (stream);
850                 manager->priv->source_control = g_object_ref (control);
851                 g_debug ("Default input stream updated to %s",
852                          mate_mixer_stream_get_name (stream));
853         } else
854                 g_debug ("Default input stream unset");
855 }
856 
857 static void
on_context_state_notify(MateMixerContext * context G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,MsdMediaKeysManager * manager)858 on_context_state_notify (MateMixerContext    *context G_GNUC_UNUSED,
859                          GParamSpec          *pspec G_GNUC_UNUSED,
860                          MsdMediaKeysManager *manager)
861 {
862         update_default_output (manager);
863         update_default_input (manager);
864 }
865 
866 static void
on_context_default_output_notify(MateMixerContext * context G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,MsdMediaKeysManager * manager)867 on_context_default_output_notify (MateMixerContext    *context G_GNUC_UNUSED,
868                                   GParamSpec          *pspec G_GNUC_UNUSED,
869                                   MsdMediaKeysManager *manager)
870 {
871         update_default_output (manager);
872 }
873 
874 static void
on_context_default_input_notify(MateMixerContext * context G_GNUC_UNUSED,GParamSpec * pspec G_GNUC_UNUSED,MsdMediaKeysManager * manager)875 on_context_default_input_notify (MateMixerContext    *context G_GNUC_UNUSED,
876                                  GParamSpec          *pspec G_GNUC_UNUSED,
877                                  MsdMediaKeysManager *manager)
878 {
879         update_default_input (manager);
880 }
881 
882 static void
on_context_stream_removed(MateMixerContext * context G_GNUC_UNUSED,const gchar * name,MsdMediaKeysManager * manager)883 on_context_stream_removed (MateMixerContext    *context G_GNUC_UNUSED,
884                            const gchar         *name,
885                            MsdMediaKeysManager *manager)
886 {
887         if (manager->priv->stream != NULL) {
888                 MateMixerStream *stream =
889                         mate_mixer_context_get_stream (manager->priv->context, name);
890 
891                 if (stream == manager->priv->stream) {
892                         g_clear_object (&manager->priv->stream);
893                         g_clear_object (&manager->priv->control);
894                 }
895         }
896         if (manager->priv->source_stream != NULL) {
897                 MateMixerStream *stream =
898                         mate_mixer_context_get_stream (manager->priv->context, name);
899 
900                 if (stream == manager->priv->source_stream) {
901                         g_clear_object (&manager->priv->source_stream);
902                         g_clear_object (&manager->priv->source_control);
903                 }
904         }
905 }
906 #endif /* HAVE_LIBMATEMIXER */
907 
908 static gboolean
get_rfkill_property(MsdMediaKeysManager * manager,const char * property)909 get_rfkill_property (MsdMediaKeysManager *manager,
910                      const char          *property)
911 {
912         GVariant *v;
913         gboolean ret;
914 
915         v = g_dbus_proxy_get_cached_property (manager->priv->rfkill_proxy, property);
916         if (!v)
917                 return FALSE;
918         ret = g_variant_get_boolean (v);
919         g_variant_unref (v);
920 
921         return ret;
922 }
923 
924 typedef struct {
925         MsdMediaKeysManager *manager;
926         char *property;
927         gboolean bluetooth;
928         gboolean target_state;
929 } RfkillData;
930 
931 static void
set_rfkill_complete(GObject * object,GAsyncResult * result,gpointer user_data)932 set_rfkill_complete (GObject      *object,
933                      GAsyncResult *result,
934                      gpointer      user_data)
935 {
936         GError *error = NULL;
937         GVariant *variant;
938         RfkillData *data = user_data;
939 
940         variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), result, &error);
941 
942         if (variant == NULL) {
943                 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
944                         g_warning ("Failed to set '%s' property: %s", data->property, error->message);
945                 g_error_free (error);
946                 goto out;
947         }
948         g_variant_unref (variant);
949 
950         g_debug ("Finished changing rfkill, property %s is now %s",
951                  data->property, data->target_state ? "true" : "false");
952 
953         if (data->bluetooth){
954                 if (data->target_state)
955                         msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (data->manager->priv->dialog),
956                                                                  "bluetooth-disabled-symbolic", _("Bluetooth disabled"));
957                 else
958                         msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (data->manager->priv->dialog),
959                                                                  "bluetooth-active-symbolic", _("Bluetooth enabled"));
960         } else {
961                 if (data->target_state)
962                         msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (data->manager->priv->dialog),
963                                                                  "airplane-mode-symbolic", _("Airplane mode enabled"));
964                 else
965                         msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (data->manager->priv->dialog),
966                                                                  "network-wireless-signal-excellent-symbolic", _("Airplane mode disabled"));
967         }
968         dialog_show (data->manager);
969 out:
970         g_free (data->property);
971         g_free (data);
972 }
973 
974 static void
do_rfkill_action(MsdMediaKeysManager * manager,gboolean bluetooth)975 do_rfkill_action (MsdMediaKeysManager *manager,
976                   gboolean             bluetooth)
977 {
978         const char *has_mode, *hw_mode, *mode;
979         gboolean new_state;
980         RfkillData *data;
981 
982         dialog_init (manager);
983 
984         has_mode = bluetooth ? "BluetoothHasAirplaneMode" : "HasAirplaneMode";
985         hw_mode = bluetooth ? "BluetoothHardwareAirplaneMode" : "HardwareAirplaneMode";
986         mode = bluetooth ? "BluetoothAirplaneMode" : "AirplaneMode";
987 
988         if (manager->priv->rfkill_proxy == NULL)
989                 return;
990 
991         if (get_rfkill_property (manager, has_mode) == FALSE)
992                 return;
993 
994         if (get_rfkill_property (manager, hw_mode)) {
995                 msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
996                                                         "airplane-mode-symbolic",
997                                                         _("Hardware Airplane Mode"));
998                 dialog_show (manager);
999                 return;
1000         }
1001 
1002         new_state = !get_rfkill_property (manager, mode);
1003         data = g_new0 (RfkillData, 1);
1004         data->manager = manager;
1005         data->property = g_strdup (mode);
1006         data->bluetooth = bluetooth;
1007         data->target_state = new_state;
1008         g_dbus_proxy_call (manager->priv->rfkill_proxy,
1009                            "org.freedesktop.DBus.Properties.Set",
1010                            g_variant_new ("(ssv)",
1011                                           "org.mate.SettingsDaemon.Rfkill",
1012                                           data->property,
1013                                           g_variant_new_boolean (new_state)),
1014                            G_DBUS_CALL_FLAGS_NONE, -1,
1015                            manager->priv->rfkill_cancellable,
1016                            set_rfkill_complete, data);
1017 
1018         g_debug ("Setting rfkill property %s to %s",
1019                  data->property, new_state ? "true" : "false");
1020 }
1021 
1022 static void
do_display_osd_action(MsdMediaKeysManager * manager)1023 do_display_osd_action (MsdMediaKeysManager *manager)
1024 {
1025         GdkDisplay *display;
1026         int         n_monitors;
1027 
1028         display = gdk_display_get_default ();
1029         n_monitors = gdk_display_get_n_monitors (display);
1030 
1031         dialog_init (manager);
1032         if (n_monitors > 1)
1033                 msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1034                                                          "video-joined-displays-symbolic",
1035                                                          _("Changing Screen Layout"));
1036         else
1037                 msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
1038                                                          "video-single-display-symbolic",
1039                                                          _("No External Display"));
1040         dialog_show (manager);
1041 }
1042 
1043 static gint
find_by_application(gconstpointer a,gconstpointer b)1044 find_by_application (gconstpointer a,
1045                      gconstpointer b)
1046 {
1047         return strcmp (((MediaPlayer *)a)->application, b);
1048 }
1049 
1050 static gint
find_by_time(gconstpointer a,gconstpointer b)1051 find_by_time (gconstpointer a,
1052               gconstpointer b)
1053 {
1054         return ((MediaPlayer *)a)->time < ((MediaPlayer *)b)->time;
1055 }
1056 
1057 /*
1058  * Register a new media player. Most applications will want to call
1059  * this with time = GDK_CURRENT_TIME. This way, the last registered
1060  * player will receive media events. In some cases, applications
1061  * may want to register with a lower priority (usually 1), to grab
1062  * events only nobody is interested.
1063  */
1064 gboolean
msd_media_keys_manager_grab_media_player_keys(MsdMediaKeysManager * manager,const char * application,guint32 time,GError ** error)1065 msd_media_keys_manager_grab_media_player_keys (MsdMediaKeysManager *manager,
1066                                                const char          *application,
1067                                                guint32              time,
1068                                                GError             **error)
1069 {
1070         GList       *iter;
1071         MediaPlayer *media_player;
1072 
1073         if (time == GDK_CURRENT_TIME) {
1074                 time = (guint32)(g_get_monotonic_time () / 1000);
1075         }
1076 
1077         iter = g_list_find_custom (manager->priv->media_players,
1078                                    application,
1079                                    find_by_application);
1080 
1081         if (iter != NULL) {
1082                 if (((MediaPlayer *)iter->data)->time < time) {
1083                         g_free (((MediaPlayer *)iter->data)->application);
1084                         g_free (iter->data);
1085                         manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter);
1086                 } else {
1087                         return TRUE;
1088                 }
1089         }
1090 
1091         g_debug ("Registering %s at %u", application, time);
1092         media_player = g_new0 (MediaPlayer, 1);
1093         media_player->application = g_strdup (application);
1094         media_player->time = time;
1095 
1096         manager->priv->media_players = g_list_insert_sorted (manager->priv->media_players,
1097                                                              media_player,
1098                                                              find_by_time);
1099 
1100         return TRUE;
1101 }
1102 
1103 gboolean
msd_media_keys_manager_release_media_player_keys(MsdMediaKeysManager * manager,const char * application,GError ** error)1104 msd_media_keys_manager_release_media_player_keys (MsdMediaKeysManager *manager,
1105                                                   const char          *application,
1106                                                   GError             **error)
1107 {
1108         GList *iter;
1109 
1110         iter = g_list_find_custom (manager->priv->media_players,
1111                                    application,
1112                                    find_by_application);
1113 
1114         if (iter != NULL) {
1115                 g_debug ("Deregistering %s", application);
1116                 g_free (((MediaPlayer *)iter->data)->application);
1117                 g_free (iter->data);
1118                 manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter);
1119         }
1120 
1121         return TRUE;
1122 }
1123 
1124 static gboolean
msd_media_player_key_pressed(MsdMediaKeysManager * manager,const char * key)1125 msd_media_player_key_pressed (MsdMediaKeysManager *manager,
1126                               const char          *key)
1127 {
1128         const char *application = NULL;
1129         gboolean    have_listeners;
1130 
1131         have_listeners = (manager->priv->media_players != NULL);
1132 
1133         if (have_listeners) {
1134                 application = ((MediaPlayer *)manager->priv->media_players->data)->application;
1135         }
1136 
1137         g_signal_emit (manager, signals[MEDIA_PLAYER_KEY_PRESSED], 0, application, key);
1138 
1139         return !have_listeners;
1140 }
1141 
1142 static gboolean
do_multimedia_player_action(MsdMediaKeysManager * manager,const char * key)1143 do_multimedia_player_action (MsdMediaKeysManager *manager,
1144                              const char          *key)
1145 {
1146         return msd_media_player_key_pressed (manager, key);
1147 }
1148 
1149 static void
do_toggle_accessibility_key(const char * key)1150 do_toggle_accessibility_key (const char *key)
1151 {
1152         GSettings *settings;
1153         gboolean state;
1154 
1155         settings = g_settings_new ("org.gnome.desktop.a11y.applications");
1156         state = g_settings_get_boolean (settings, key);
1157         g_settings_set_boolean (settings, key, !state);
1158         g_object_unref (settings);
1159 }
1160 
1161 static void
do_magnifier_action(MsdMediaKeysManager * manager)1162 do_magnifier_action (MsdMediaKeysManager *manager)
1163 {
1164         do_toggle_accessibility_key ("screen-magnifier-enabled");
1165 }
1166 
1167 static void
do_screenreader_action(MsdMediaKeysManager * manager)1168 do_screenreader_action (MsdMediaKeysManager *manager)
1169 {
1170         do_toggle_accessibility_key ("screen-reader-enabled");
1171 }
1172 
1173 static void
do_on_screen_keyboard_action(MsdMediaKeysManager * manager)1174 do_on_screen_keyboard_action (MsdMediaKeysManager *manager)
1175 {
1176         do_toggle_accessibility_key ("screen-keyboard-enabled");
1177 }
1178 
1179 static gboolean
do_action(MsdMediaKeysManager * manager,int type)1180 do_action (MsdMediaKeysManager *manager,
1181            int                  type)
1182 {
1183         char *cmd;
1184         char *path;
1185 
1186         switch (type) {
1187         case TOUCHPAD_KEY:
1188                 do_touchpad_action (manager);
1189                 break;
1190         case TOUCHPAD_ON_KEY:
1191                 do_touchpad_osd_action(manager, TRUE);
1192                 break;
1193         case TOUCHPAD_OFF_KEY:
1194                 do_touchpad_osd_action(manager, FALSE);
1195                 break;
1196         case MUTE_KEY:
1197         case VOLUME_DOWN_KEY:
1198         case VOLUME_UP_KEY:
1199         case MIC_MUTE_KEY:
1200 #ifdef HAVE_LIBMATEMIXER
1201                 do_sound_action (manager, type, FALSE);
1202 #endif
1203                 break;
1204         case MUTE_QUIET_KEY:
1205 #ifdef HAVE_LIBMATEMIXER
1206                 do_sound_action (manager, MUTE_KEY, TRUE);
1207 #endif
1208                 break;
1209         case VOLUME_DOWN_QUIET_KEY:
1210 #ifdef HAVE_LIBMATEMIXER
1211                 do_sound_action (manager, VOLUME_DOWN_KEY, TRUE);
1212 #endif
1213                 break;
1214         case VOLUME_UP_QUIET_KEY:
1215 #ifdef HAVE_LIBMATEMIXER
1216                 do_sound_action (manager, VOLUME_UP_KEY, TRUE);
1217 #endif
1218                 break;
1219         case POWER_KEY:
1220                 do_shutdown_action (manager);
1221                 break;
1222 	case LOGOUT_KEY:
1223 		do_logout_action (manager);
1224 		break;
1225         case EJECT_KEY:
1226                 do_eject_action (manager);
1227                 break;
1228         case HOME_KEY:
1229                 path = g_shell_quote (g_get_home_dir ());
1230                 cmd = g_strconcat ("caja --no-desktop ", path, NULL);
1231                 g_free (path);
1232                 execute (manager, cmd, FALSE, FALSE);
1233                 g_free (cmd);
1234                 break;
1235         case SEARCH_KEY:
1236                 cmd = NULL;
1237                 if ((cmd = g_find_program_in_path ("beagle-search"))) {
1238                         execute (manager, "beagle-search", FALSE, FALSE);
1239                 } else if ((cmd = g_find_program_in_path ("tracker-search-tool"))) {
1240                         execute (manager, "tracker-search-tool", FALSE, FALSE);
1241                 } else {
1242                         execute (manager, "mate-search-tool", FALSE, FALSE);
1243                 }
1244                 g_free (cmd);
1245                 break;
1246         case EMAIL_KEY:
1247                 do_url_action (manager, "mailto");
1248                 break;
1249         case CONTROL_CENTER_KEY:
1250                 if ((cmd = g_find_program_in_path ("mate-control-center")))
1251                         execute (manager, "mate-control-center", FALSE, FALSE);
1252 
1253                 g_free (cmd);
1254                 break;
1255         case SCREENSAVER_KEY:
1256                 if ((cmd = g_find_program_in_path ("mate-screensaver-command"))) {
1257                         execute (manager, "mate-screensaver-command --lock", FALSE, FALSE);
1258                 } else {
1259                         execute (manager, "xscreensaver-command -lock", FALSE, FALSE);
1260                 }
1261 
1262                 g_free (cmd);
1263                 break;
1264         case HELP_KEY:
1265                 do_url_action (manager, "help");
1266                 break;
1267         case WWW_KEY:
1268                 do_url_action (manager, "http");
1269                 break;
1270         case MEDIA_KEY:
1271                 do_media_action (manager);
1272                 break;
1273         case CALCULATOR_KEY:
1274                 do_calculator_action (manager);
1275                 break;
1276         case MESSENGER_KEY:
1277                 do_messenger_action (manager);
1278                 break;
1279         case PLAY_KEY:
1280                 return do_multimedia_player_action (manager, "Play");
1281         case PAUSE_KEY:
1282                 return do_multimedia_player_action (manager, "Pause");
1283         case STOP_KEY:
1284                 return do_multimedia_player_action (manager, "Stop");
1285         case PREVIOUS_KEY:
1286                 return do_multimedia_player_action (manager, "Previous");
1287         case NEXT_KEY:
1288                 return do_multimedia_player_action (manager, "Next");
1289         case REWIND_KEY:
1290                 return do_multimedia_player_action (manager, "Rewind");
1291         case FORWARD_KEY:
1292                 return do_multimedia_player_action (manager, "FastForward");
1293         case REPEAT_KEY:
1294                 return do_multimedia_player_action (manager, "Repeat");
1295         case RANDOM_KEY:
1296                 return do_multimedia_player_action (manager, "Shuffle");
1297         case MAGNIFIER_KEY:
1298                 do_magnifier_action (manager);
1299                 break;
1300         case SCREENREADER_KEY:
1301                 do_screenreader_action (manager);
1302                 break;
1303         case ON_SCREEN_KEYBOARD_KEY:
1304                 do_on_screen_keyboard_action (manager);
1305                 break;
1306         case RFKILL_KEY:
1307                 do_rfkill_action (manager, FALSE);
1308                 break;
1309         case BLUETOOTH_RFKILL_KEY:
1310                 do_rfkill_action (manager, TRUE);
1311                 break;
1312         case DISPLAY_KEY:
1313                 do_display_osd_action (manager);
1314                 break;
1315         default:
1316                 g_assert_not_reached ();
1317         }
1318 
1319         return FALSE;
1320 }
1321 
1322 static GdkScreen *
acme_get_screen_from_event(MsdMediaKeysManager * manager,XAnyEvent * xanyev)1323 acme_get_screen_from_event (MsdMediaKeysManager *manager,
1324                             XAnyEvent           *xanyev)
1325 {
1326         GdkWindow *window;
1327         GdkScreen *screen;
1328         GSList    *l;
1329 
1330         /* Look for which screen we're receiving events */
1331         for (l = manager->priv->screens; l != NULL; l = l->next) {
1332                 screen = (GdkScreen *) l->data;
1333                 window = gdk_screen_get_root_window (screen);
1334 
1335                 if (GDK_WINDOW_XID (window) == xanyev->window) {
1336                         return screen;
1337                 }
1338         }
1339 
1340         return NULL;
1341 }
1342 
1343 static GdkFilterReturn
acme_filter_events(GdkXEvent * xevent,GdkEvent * event,MsdMediaKeysManager * manager)1344 acme_filter_events (GdkXEvent           *xevent,
1345                     GdkEvent            *event,
1346                     MsdMediaKeysManager *manager)
1347 {
1348         XEvent    *xev = (XEvent *) xevent;
1349         XAnyEvent *xany = (XAnyEvent *) xevent;
1350         int        i;
1351 
1352         /* verify we have a key event */
1353         if (xev->type != KeyPress) {
1354                 return GDK_FILTER_CONTINUE;
1355         }
1356 
1357         for (i = 0; i < HANDLED_KEYS; i++) {
1358                 if (match_key (keys[i].key, xev)) {
1359                         switch (keys[i].key_type) {
1360                         case VOLUME_DOWN_KEY:
1361                         case VOLUME_UP_KEY:
1362                         case VOLUME_DOWN_QUIET_KEY:
1363                         case VOLUME_UP_QUIET_KEY:
1364                                 /* auto-repeatable keys */
1365                                 if (xev->type != KeyPress) {
1366                                         return GDK_FILTER_CONTINUE;
1367                                 }
1368                                 break;
1369                         }
1370 
1371                         manager->priv->current_screen = acme_get_screen_from_event (manager, xany);
1372 
1373                         if (do_action (manager, keys[i].key_type) == FALSE) {
1374                                 return GDK_FILTER_REMOVE;
1375                         } else {
1376                                 return GDK_FILTER_CONTINUE;
1377                         }
1378                 }
1379         }
1380 
1381         return GDK_FILTER_CONTINUE;
1382 }
1383 
1384 static void
on_rfkill_proxy_ready(GObject * source,GAsyncResult * result,gpointer data)1385 on_rfkill_proxy_ready (GObject      *source,
1386                        GAsyncResult *result,
1387                        gpointer      data)
1388 {
1389         MsdMediaKeysManager *manager = data;
1390 
1391         manager->priv->rfkill_proxy =
1392                 g_dbus_proxy_new_for_bus_finish (result, NULL);
1393 }
1394 
1395 static void
rfkill_appeared_cb(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)1396 rfkill_appeared_cb (GDBusConnection *connection,
1397                     const gchar     *name,
1398                     const gchar     *name_owner,
1399                     gpointer         user_data)
1400 {
1401         MsdMediaKeysManager *manager = user_data;
1402 
1403         g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
1404                                   0, NULL,
1405                                   "org.mate.SettingsDaemon.Rfkill",
1406                                   "/org/mate/SettingsDaemon/Rfkill",
1407                                   "org.mate.SettingsDaemon.Rfkill",
1408                                   manager->priv->rfkill_cancellable,
1409                                   on_rfkill_proxy_ready, manager);
1410 }
1411 
1412 static gboolean
start_media_keys_idle_cb(MsdMediaKeysManager * manager)1413 start_media_keys_idle_cb (MsdMediaKeysManager *manager)
1414 {
1415         GSList *l;
1416         GdkDisplay *dpy;
1417         Display *xdpy;
1418 
1419         g_debug ("Starting media_keys manager");
1420         mate_settings_profile_start (NULL);
1421 
1422         dpy = gdk_display_get_default ();
1423         xdpy = GDK_DISPLAY_XDISPLAY (dpy);
1424 
1425         manager->priv->volume_monitor = g_volume_monitor_get ();
1426         manager->priv->settings = g_settings_new (BINDING_SCHEMA);
1427 
1428         ensure_cancellable (&manager->priv->rfkill_cancellable);
1429 
1430         init_screens (manager);
1431         init_kbd (manager);
1432 
1433         /* Start filtering the events */
1434         for (l = manager->priv->screens; l != NULL; l = l->next) {
1435                 GdkWindow *window;
1436                 Window xwindow;
1437                 XWindowAttributes atts;
1438 
1439                 mate_settings_profile_start ("gdk_window_add_filter");
1440 
1441                 window = gdk_screen_get_root_window (l->data);
1442                 xwindow = GDK_WINDOW_XID (window);
1443 
1444                 g_debug ("adding key filter for screen: %d",
1445                          gdk_x11_screen_get_screen_number (l->data));
1446 
1447                 gdk_window_add_filter (window,
1448                                        (GdkFilterFunc)acme_filter_events,
1449                                        manager);
1450 
1451                 gdk_x11_display_error_trap_push (dpy);
1452                 /* Add KeyPressMask to the currently reportable event masks */
1453                 XGetWindowAttributes (xdpy, xwindow, &atts);
1454                 XSelectInput (xdpy, xwindow, atts.your_event_mask | KeyPressMask);
1455                 gdk_x11_display_error_trap_pop_ignored (dpy);
1456 
1457                 mate_settings_profile_end ("gdk_window_add_filter");
1458         }
1459 
1460         manager->priv->rfkill_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
1461                                                            "org.mate.SettingsDaemon.Rfkill",
1462                                                            G_BUS_NAME_WATCHER_FLAGS_NONE,
1463                                                            rfkill_appeared_cb,
1464                                                            NULL,
1465                                                            manager, NULL);
1466 
1467         mate_settings_profile_end (NULL);
1468 
1469         return FALSE;
1470 }
1471 
1472 gboolean
msd_media_keys_manager_start(MsdMediaKeysManager * manager,GError ** error)1473 msd_media_keys_manager_start (MsdMediaKeysManager *manager, GError **error)
1474 {
1475         mate_settings_profile_start (NULL);
1476 
1477 #ifdef HAVE_LIBMATEMIXER
1478         if (G_LIKELY (mate_mixer_is_initialized ())) {
1479                 mate_settings_profile_start ("mate_mixer_context_new");
1480 
1481                 manager->priv->context = mate_mixer_context_new ();
1482 
1483                 g_signal_connect (manager->priv->context,
1484                                   "notify::state",
1485                                   G_CALLBACK (on_context_state_notify),
1486                                   manager);
1487                 g_signal_connect (manager->priv->context,
1488                                   "notify::default-output-stream",
1489                                   G_CALLBACK (on_context_default_output_notify),
1490                                   manager);
1491                 g_signal_connect (manager->priv->context,
1492                                   "notify::default-input-stream",
1493                                   G_CALLBACK (on_context_default_input_notify),
1494                                   manager);
1495                 g_signal_connect (manager->priv->context,
1496                                   "stream-removed",
1497                                   G_CALLBACK (on_context_stream_removed),
1498                                   manager);
1499 
1500                 mate_mixer_context_open (manager->priv->context);
1501 
1502                 mate_settings_profile_end ("mate_mixer_context_new");
1503         }
1504 #endif
1505         g_idle_add ((GSourceFunc) start_media_keys_idle_cb, manager);
1506 
1507         mate_settings_profile_end (NULL);
1508 
1509         return TRUE;
1510 }
1511 
1512 void
msd_media_keys_manager_stop(MsdMediaKeysManager * manager)1513 msd_media_keys_manager_stop (MsdMediaKeysManager *manager)
1514 {
1515         MsdMediaKeysManagerPrivate *priv = manager->priv;
1516         GdkDisplay *dpy;
1517         GSList *ls;
1518         GList *l;
1519         int i;
1520         gboolean need_flush;
1521 
1522         g_debug ("Stopping media_keys manager");
1523 
1524         for (ls = priv->screens; ls != NULL; ls = ls->next) {
1525                 gdk_window_remove_filter (gdk_screen_get_root_window (ls->data),
1526                                           (GdkFilterFunc) acme_filter_events,
1527                                           manager);
1528         }
1529 
1530         if (manager->priv->rfkill_watch_id > 0) {
1531                 g_bus_unwatch_name (manager->priv->rfkill_watch_id);
1532                 manager->priv->rfkill_watch_id = 0;
1533         }
1534 
1535         if (priv->settings != NULL) {
1536                 g_object_unref (priv->settings);
1537                 priv->settings = NULL;
1538         }
1539 
1540         if (priv->volume_monitor != NULL) {
1541                 g_object_unref (priv->volume_monitor);
1542                 priv->volume_monitor = NULL;
1543         }
1544 
1545         if (priv->connection != NULL) {
1546                 dbus_g_connection_unref (priv->connection);
1547                 priv->connection = NULL;
1548         }
1549 
1550         need_flush = FALSE;
1551         dpy = gdk_display_get_default ();
1552         gdk_x11_display_error_trap_push (dpy);
1553 
1554         for (i = 0; i < HANDLED_KEYS; ++i) {
1555                 if (keys[i].key) {
1556                         need_flush = TRUE;
1557                         grab_key_unsafe (keys[i].key, FALSE, priv->screens);
1558 
1559                         g_free (keys[i].key->keycodes);
1560                         g_free (keys[i].key);
1561                         keys[i].key = NULL;
1562                 }
1563         }
1564 
1565         if (need_flush)
1566                 gdk_display_flush (dpy);
1567 
1568         gdk_x11_display_error_trap_pop_ignored (dpy);
1569 
1570         g_slist_free (priv->screens);
1571         priv->screens = NULL;
1572 
1573         if (priv->rfkill_cancellable != NULL) {
1574                 g_cancellable_cancel (priv->rfkill_cancellable);
1575                 g_clear_object (&priv->rfkill_cancellable);
1576         }
1577 
1578 #ifdef HAVE_LIBMATEMIXER
1579         g_clear_object (&priv->stream);
1580         g_clear_object (&priv->source_stream);
1581         g_clear_object (&priv->control);
1582         g_clear_object (&priv->source_control);
1583         g_clear_object (&priv->context);
1584 #endif
1585 
1586         if (priv->dialog != NULL) {
1587                 gtk_widget_destroy (priv->dialog);
1588                 priv->dialog = NULL;
1589         }
1590 
1591         for (l = priv->media_players; l; l = l->next) {
1592                 MediaPlayer *mp = l->data;
1593                 g_free (mp->application);
1594                 g_free (mp);
1595         }
1596         g_list_free (priv->media_players);
1597         priv->media_players = NULL;
1598 }
1599 
1600 static void
msd_media_keys_manager_class_init(MsdMediaKeysManagerClass * klass)1601 msd_media_keys_manager_class_init (MsdMediaKeysManagerClass *klass)
1602 {
1603         signals[MEDIA_PLAYER_KEY_PRESSED] =
1604                 g_signal_new ("media-player-key-pressed",
1605                               G_OBJECT_CLASS_TYPE (klass),
1606                               G_SIGNAL_RUN_LAST,
1607                               G_STRUCT_OFFSET (MsdMediaKeysManagerClass, media_player_key_pressed),
1608                               NULL,
1609                               NULL,
1610                               msd_marshal_VOID__STRING_STRING,
1611                               G_TYPE_NONE,
1612                               2,
1613                               G_TYPE_STRING,
1614                               G_TYPE_STRING);
1615 
1616         dbus_g_object_type_install_info (MSD_TYPE_MEDIA_KEYS_MANAGER, &dbus_glib_msd_media_keys_manager_object_info);
1617 }
1618 
1619 static void
msd_media_keys_manager_init(MsdMediaKeysManager * manager)1620 msd_media_keys_manager_init (MsdMediaKeysManager *manager)
1621 {
1622         manager->priv = msd_media_keys_manager_get_instance_private (manager);
1623 }
1624 
1625 static gboolean
register_manager(MsdMediaKeysManager * manager)1626 register_manager (MsdMediaKeysManager *manager)
1627 {
1628         GError *error = NULL;
1629 
1630         manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
1631         if (manager->priv->connection == NULL) {
1632                 if (error != NULL) {
1633                         g_error ("Error getting session bus: %s", error->message);
1634                         g_error_free (error);
1635                 }
1636                 return FALSE;
1637         }
1638 
1639         dbus_g_connection_register_g_object (manager->priv->connection, MSD_MEDIA_KEYS_DBUS_PATH, G_OBJECT (manager));
1640 
1641         return TRUE;
1642 }
1643 
1644 MsdMediaKeysManager *
msd_media_keys_manager_new(void)1645 msd_media_keys_manager_new (void)
1646 {
1647         if (manager_object != NULL) {
1648                 g_object_ref (manager_object);
1649         } else {
1650                 gboolean res;
1651 
1652                 manager_object = g_object_new (MSD_TYPE_MEDIA_KEYS_MANAGER, NULL);
1653                 g_object_add_weak_pointer (manager_object,
1654                                            (gpointer *) &manager_object);
1655                 res = register_manager (manager_object);
1656                 if (! res) {
1657                         g_object_unref (manager_object);
1658                         return NULL;
1659                 }
1660         }
1661 
1662         return MSD_MEDIA_KEYS_MANAGER (manager_object);
1663 }
1664