/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2001-2003 Bastien Nocera * Copyright (C) 2006-2007 William Jon McCann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mate-settings-profile.h" #include "msd-marshal.h" #include "msd-media-keys-manager.h" #include "msd-media-keys-manager-glue.h" #include "eggaccelerators.h" #include "acme.h" #include "msd-media-keys-window.h" #ifdef HAVE_PULSE #include #include "gvc-mixer-control.h" #elif defined(HAVE_GSTREAMER) #include "gvc-gstreamer-acme-vol.h" #endif /* HAVE_PULSE */ #define MSD_DBUS_PATH "/org/mate/SettingsDaemon" #define MSD_DBUS_NAME "org.mate.SettingsDaemon" #define MSD_MEDIA_KEYS_DBUS_PATH MSD_DBUS_PATH "/MediaKeys" #define MSD_MEDIA_KEYS_DBUS_NAME MSD_DBUS_NAME ".MediaKeys" #define TOUCHPAD_ENABLED_KEY "/desktop/mate/peripherals/touchpad/touchpad_enabled" #define VOLUME_STEP 6 /* percents for one volume button press */ #define MAX_VOLUME 65536.0 #define MSD_MEDIA_KEYS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_MEDIA_KEYS_MANAGER, MsdMediaKeysManagerPrivate)) typedef struct { char *application; guint32 time; } MediaPlayer; struct MsdMediaKeysManagerPrivate { #ifdef HAVE_PULSE /* Volume bits */ GvcMixerControl *volume; GvcMixerStream *stream; #elif defined(HAVE_GSTREAMER) AcmeVolume *volume; #endif /* HAVE_PULSE */ GtkWidget *dialog; MateConfClient *conf_client; GVolumeMonitor *volume_monitor; /* Multihead stuff */ GdkScreen *current_screen; GSList *screens; GList *media_players; DBusGConnection *connection; guint notify[HANDLED_KEYS]; }; enum { MEDIA_PLAYER_KEY_PRESSED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; static void msd_media_keys_manager_class_init (MsdMediaKeysManagerClass *klass); static void msd_media_keys_manager_init (MsdMediaKeysManager *media_keys_manager); static void msd_media_keys_manager_finalize (GObject *object); G_DEFINE_TYPE (MsdMediaKeysManager, msd_media_keys_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static void init_screens (MsdMediaKeysManager *manager) { GdkDisplay *display; int i; display = gdk_display_get_default (); for (i = 0; i < gdk_display_get_n_screens (display); i++) { GdkScreen *screen; screen = gdk_display_get_screen (display, i); if (screen == NULL) { continue; } manager->priv->screens = g_slist_append (manager->priv->screens, screen); } manager->priv->current_screen = manager->priv->screens->data; } static void acme_error (char * msg) { GtkWidget *error_dialog; error_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, msg, NULL); gtk_dialog_set_default_response (GTK_DIALOG (error_dialog), GTK_RESPONSE_OK); gtk_widget_show (error_dialog); g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); } static char * get_term_command (MsdMediaKeysManager *manager) { char *cmd_term; char *cmd = NULL; cmd_term = mateconf_client_get_string (manager->priv->conf_client, "/desktop/mate/applications/terminal/exec", NULL); if ((cmd_term != NULL) && (strcmp (cmd_term, "") != 0)) { char *cmd_args; cmd_args = mateconf_client_get_string (manager->priv->conf_client, "/desktop/mate/applications/terminal/exec_arg", NULL); if ((cmd_args != NULL) && (strcmp (cmd_term, "") != 0)) { cmd = g_strdup_printf ("%s %s -e", cmd_term, cmd_args); } else { cmd = g_strdup_printf ("%s -e", cmd_term); } g_free (cmd_args); } g_free (cmd_term); return cmd; } static void execute (MsdMediaKeysManager *manager, char *cmd, gboolean sync, gboolean need_term) { gboolean retval; char **argv; int argc; char *exec; char *term = NULL; retval = FALSE; if (need_term) { term = get_term_command (manager); if (term == NULL) { acme_error (_("Could not get default terminal. Verify that your default " "terminal command is set and points to a valid application.")); return; } } if (term) { exec = g_strdup_printf ("%s %s", term, cmd); g_free (term); } else { exec = g_strdup (cmd); } if (g_shell_parse_argv (exec, &argc, &argv, NULL)) { if (sync != FALSE) { retval = g_spawn_sync (g_get_home_dir (), argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, NULL, NULL); } else { retval = g_spawn_async (g_get_home_dir (), argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); } g_strfreev (argv); } if (retval == FALSE) { char *msg; msg = g_strdup_printf (_("Couldn't execute command: %s\n" "Verify that this is a valid command."), exec); acme_error (msg); g_free (msg); } g_free (exec); } static void dialog_init (MsdMediaKeysManager *manager) { if (manager->priv->dialog != NULL && !msd_osd_window_is_valid (MSD_OSD_WINDOW (manager->priv->dialog))) { gtk_widget_destroy (manager->priv->dialog); manager->priv->dialog = NULL; } if (manager->priv->dialog == NULL) { manager->priv->dialog = msd_media_keys_window_new (); } } static gboolean is_valid_shortcut (const char *string) { if (string == NULL || string[0] == '\0') { return FALSE; } if (strcmp (string, "disabled") == 0) { return FALSE; } return TRUE; } static void update_kbd_cb (MateConfClient *client, guint id, MateConfEntry *entry, MsdMediaKeysManager *manager) { int i; gboolean need_flush = TRUE; g_return_if_fail (entry->key != NULL); gdk_error_trap_push (); /* Find the key that was modified */ for (i = 0; i < HANDLED_KEYS; i++) { if (strcmp (entry->key, keys[i].mateconf_key) == 0) { char *tmp; Key *key; if (keys[i].key != NULL) { need_flush = TRUE; grab_key_unsafe (keys[i].key, FALSE, manager->priv->screens); } g_free (keys[i].key); keys[i].key = NULL; tmp = mateconf_client_get_string (manager->priv->conf_client, keys[i].mateconf_key, NULL); if (is_valid_shortcut (tmp) == FALSE) { g_free (tmp); break; } key = g_new0 (Key, 1); if (!egg_accelerator_parse_virtual (tmp, &key->keysym, &key->keycodes, &key->state)) { g_free (tmp); g_free (key); break; } need_flush = TRUE; grab_key_unsafe (key, TRUE, manager->priv->screens); keys[i].key = key; g_free (tmp); break; } } if (need_flush) gdk_flush (); if (gdk_error_trap_pop ()) g_warning ("Grab failed for some keys, another application may already have access the them."); } static void init_kbd(MsdMediaKeysManager* manager) { int i; gboolean need_flush = FALSE; mate_settings_profile_start(NULL); gdk_error_trap_push(); for (i = 0; i < HANDLED_KEYS; i++) { char* tmp; Key* key; manager->priv->notify[i] = mateconf_client_notify_add(manager->priv->conf_client, keys[i].mateconf_key, (MateConfClientNotifyFunc) update_kbd_cb, manager, NULL, NULL); tmp = mateconf_client_get_string(manager->priv->conf_client, keys[i].mateconf_key, NULL); if (!is_valid_shortcut(tmp)) { g_debug("Not a valid shortcut: '%s'", tmp); g_free(tmp); continue; } key = g_new0(Key, 1); if (!egg_accelerator_parse_virtual(tmp, &key->keysym, &key->keycodes, &key->state)) { g_debug("Unable to parse: '%s'", tmp); g_free(tmp); g_free(key); continue; } g_free(tmp); keys[i].key = key; need_flush = TRUE; grab_key_unsafe(key, TRUE, manager->priv->screens); } if (need_flush) { gdk_flush(); } if (gdk_error_trap_pop ()) { g_warning("Grab failed for some keys, another application may already have access the them."); } mate_settings_profile_end(NULL); } static void dialog_show (MsdMediaKeysManager *manager) { int orig_w; int orig_h; int screen_w; int screen_h; int x; int y; int pointer_x; int pointer_y; GtkRequisition win_req; GdkScreen *pointer_screen; GdkRectangle geometry; int monitor; gtk_window_set_screen (GTK_WINDOW (manager->priv->dialog), manager->priv->current_screen); /* * get the window size * if the window hasn't been mapped, it doesn't necessarily * know its true size, yet, so we need to jump through hoops */ gtk_window_get_default_size (GTK_WINDOW (manager->priv->dialog), &orig_w, &orig_h); gtk_widget_size_request (manager->priv->dialog, &win_req); if (win_req.width > orig_w) { orig_w = win_req.width; } if (win_req.height > orig_h) { orig_h = win_req.height; } pointer_screen = NULL; gdk_display_get_pointer (gdk_screen_get_display (manager->priv->current_screen), &pointer_screen, &pointer_x, &pointer_y, NULL); if (pointer_screen != manager->priv->current_screen) { /* The pointer isn't on the current screen, so just * assume the default monitor */ monitor = 0; } else { monitor = gdk_screen_get_monitor_at_point (manager->priv->current_screen, pointer_x, pointer_y); } gdk_screen_get_monitor_geometry (manager->priv->current_screen, monitor, &geometry); screen_w = geometry.width; screen_h = geometry.height; x = ((screen_w - orig_w) / 2) + geometry.x; y = geometry.y + (screen_h / 2) + (screen_h / 2 - orig_h) / 2; gtk_window_move (GTK_WINDOW (manager->priv->dialog), x, y); gtk_widget_show (manager->priv->dialog); gdk_display_sync (gdk_screen_get_display (manager->priv->current_screen)); } static void do_unknown_action (MsdMediaKeysManager *manager, const char *url) { char *string; g_return_if_fail (url != NULL); string = mateconf_client_get_string (manager->priv->conf_client, "/desktop/mate/url-handlers/unknown/command", NULL); if ((string != NULL) && (strcmp (string, "") != 0)) { char *cmd; cmd = g_strdup_printf (string, url); execute (manager, cmd, FALSE, FALSE); g_free (cmd); } g_free (string); } static void do_help_action (MsdMediaKeysManager *manager) { char *string; string = mateconf_client_get_string (manager->priv->conf_client, "/desktop/mate/url-handlers/ghelp/command", NULL); if ((string != NULL) && (strcmp (string, "") != 0)) { char *cmd; cmd = g_strdup_printf (string, ""); execute (manager, cmd, FALSE, FALSE); g_free (cmd); } else { do_unknown_action (manager, "ghelp:"); } g_free (string); } static void do_mail_action (MsdMediaKeysManager *manager) { char *string; string = mateconf_client_get_string (manager->priv->conf_client, "/desktop/mate/url-handlers/mailto/command", NULL); if ((string != NULL) && (strcmp (string, "") != 0)) { char *cmd; cmd = g_strdup_printf (string, ""); execute (manager, cmd, FALSE, mateconf_client_get_bool (manager->priv->conf_client, "/desktop/mate/url-handlers/mailto/needs_terminal", NULL)); g_free (cmd); } g_free (string); } static void do_media_action (MsdMediaKeysManager *manager) { char *command; command = mateconf_client_get_string (manager->priv->conf_client, "/desktop/mate/applications/media/exec", NULL); if ((command != NULL) && (strcmp (command, "") != 0)) { execute (manager, command, FALSE, mateconf_client_get_bool (manager->priv->conf_client, "/desktop/mate/applications/media/needs_term", NULL)); } g_free (command); } static void do_www_action (MsdMediaKeysManager *manager, const char *url) { char *string; string = mateconf_client_get_string (manager->priv->conf_client, "/desktop/mate/url-handlers/http/command", NULL); if ((string != NULL) && (strcmp (string, "") != 0)) { gchar *cmd; if (url == NULL) { cmd = g_strdup_printf (string, ""); } else { cmd = g_strdup_printf (string, url); } execute (manager, cmd, FALSE, mateconf_client_get_bool (manager->priv->conf_client, "/desktop/mate/url-handlers/http/needs_terminal", NULL)); g_free (cmd); } else { do_unknown_action (manager, url ? url : ""); } g_free (string); } static void do_exit_action (MsdMediaKeysManager *manager) { execute (manager, "mate-session-save --shutdown-dialog", FALSE, FALSE); } static void do_eject_action_cb (GDrive *drive, GAsyncResult *res, MsdMediaKeysManager *manager) { g_drive_eject_with_operation_finish (drive, res, NULL); } #define NO_SCORE 0 #define SCORE_CAN_EJECT 50 #define SCORE_HAS_MEDIA 100 static void do_eject_action (MsdMediaKeysManager *manager) { GList *drives, *l; GDrive *fav_drive; guint score; /* Find the best drive to eject */ fav_drive = NULL; score = NO_SCORE; drives = g_volume_monitor_get_connected_drives (manager->priv->volume_monitor); for (l = drives; l != NULL; l = l->next) { GDrive *drive = l->data; if (g_drive_can_eject (drive) == FALSE) continue; if (g_drive_is_media_removable (drive) == FALSE) continue; if (score < SCORE_CAN_EJECT) { fav_drive = drive; score = SCORE_CAN_EJECT; } if (g_drive_has_media (drive) == FALSE) continue; if (score < SCORE_HAS_MEDIA) { fav_drive = drive; score = SCORE_HAS_MEDIA; break; } } /* Show the dialogue */ dialog_init (manager); msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), "media-eject", FALSE); dialog_show (manager); /* Clean up the drive selection and exit if no suitable * drives are found */ if (fav_drive != NULL) fav_drive = g_object_ref (fav_drive); g_list_foreach (drives, (GFunc) g_object_unref, NULL); if (fav_drive == NULL) return; /* Eject! */ g_drive_eject_with_operation (fav_drive, G_MOUNT_UNMOUNT_FORCE, NULL, NULL, (GAsyncReadyCallback) do_eject_action_cb, manager); g_object_unref (fav_drive); } static void do_touchpad_action (MsdMediaKeysManager *manager) { MateConfClient *client = manager->priv->conf_client; gboolean state = mateconf_client_get_bool (client, TOUCHPAD_ENABLED_KEY, NULL); dialog_init (manager); msd_media_keys_window_set_action_custom (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), (!state) ? "touchpad-enabled" : "touchpad-disabled", FALSE); dialog_show (manager); mateconf_client_set_bool (client, TOUCHPAD_ENABLED_KEY, !state, NULL); } #ifdef HAVE_PULSE static void update_dialog (MsdMediaKeysManager *manager, guint vol, gboolean muted, gboolean sound_changed) { vol = (int) (100 * (double) vol / PA_VOLUME_NORM); vol = CLAMP (vol, 0, 100); dialog_init (manager); msd_media_keys_window_set_volume_muted (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), muted); msd_media_keys_window_set_volume_level (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), vol); msd_media_keys_window_set_action (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), MSD_MEDIA_KEYS_WINDOW_ACTION_VOLUME); dialog_show (manager); if (sound_changed != FALSE && muted == FALSE) ca_gtk_play_for_widget (manager->priv->dialog, 0, CA_PROP_EVENT_ID, "audio-volume-change", CA_PROP_EVENT_DESCRIPTION, "volume changed through key press", CA_PROP_APPLICATION_ID, "org.mate.VolumeControl", NULL); } #endif /* HAVE_PULSE */ #if defined(HAVE_PULSE) || defined(HAVE_GSTREAMER) static void do_sound_action (MsdMediaKeysManager *manager, int type) { gboolean muted; guint vol, norm_vol_step; int vol_step; gboolean sound_changed; #ifdef HAVE_PULSE if (manager->priv->stream == NULL) return; #elif defined(HAVE_GSTREAMER) if (manager->priv->volume == NULL) return; #endif vol_step = mateconf_client_get_int (manager->priv->conf_client, MATECONF_MISC_DIR "/volume_step", NULL); if (vol_step <= 0 || vol_step > 100) vol_step = VOLUME_STEP; #ifdef HAVE_PULSE norm_vol_step = PA_VOLUME_NORM * vol_step / 100; /* FIXME: this is racy */ vol = gvc_mixer_stream_get_volume (manager->priv->stream); muted = gvc_mixer_stream_get_is_muted (manager->priv->stream); #else if (vol_step > 0) { gint threshold = acme_volume_get_threshold (manager->priv->volume); if (vol_step < threshold) vol_step = threshold; g_debug ("Using volume step of %d", vol_step); } vol = acme_volume_get_volume (manager->priv->volume); muted = acme_volume_get_mute (manager->priv->volume); #endif sound_changed = FALSE; switch (type) { case MUTE_KEY: #ifdef HAVE_PULSE muted = !muted; gvc_mixer_stream_change_is_muted (manager->priv->stream, muted); sound_changed = TRUE; #else acme_volume_mute_toggle (manager->priv->volume); #endif break; case VOLUME_DOWN_KEY: #ifdef HAVE_PULSE if (!muted && (vol <= norm_vol_step)) { muted = !muted; vol = 0; gvc_mixer_stream_change_is_muted (manager->priv->stream, muted); if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE) { gvc_mixer_stream_push_volume (manager->priv->stream); sound_changed = TRUE; } } else if (!muted) { vol = vol - norm_vol_step; if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE) { gvc_mixer_stream_push_volume (manager->priv->stream); sound_changed = TRUE; } } #else if (!muted && (vol <= vol_step)) acme_volume_mute_toggle (manager->priv->volume); acme_volume_set_volume (manager->priv->volume, vol - vol_step); #endif break; case VOLUME_UP_KEY: if (muted) { muted = !muted; if (vol == 0) { #ifdef HAVE_PULSE vol = vol + norm_vol_step; gvc_mixer_stream_change_is_muted (manager->priv->stream, muted); if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE) { gvc_mixer_stream_push_volume (manager->priv->stream); sound_changed = TRUE; } } else { gvc_mixer_stream_change_is_muted (manager->priv->stream, muted); sound_changed = TRUE; } #else /* We need to unmute otherwise vol is blocked (and muted) */ acme_volume_set_mute (manager->priv->volume, FALSE); } acme_volume_set_volume (manager->priv->volume, vol + vol_step); #endif } else { #ifdef HAVE_PULSE if (vol < MAX_VOLUME) { if (vol + norm_vol_step >= MAX_VOLUME) { vol = MAX_VOLUME; } else { vol = vol + norm_vol_step; } if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE) { gvc_mixer_stream_push_volume (manager->priv->stream); sound_changed = TRUE; } } #else acme_volume_set_volume (manager->priv->volume, vol + vol_step); #endif } break; } #ifdef HAVE_PULSE update_dialog (manager, vol, muted, sound_changed); #else muted = acme_volume_get_mute (manager->priv->volume); vol = acme_volume_get_volume (manager->priv->volume); /* FIXME: AcmeVolume should probably emit signals instead of doing it like this */ dialog_init (manager); msd_media_keys_window_set_volume_muted (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), muted); msd_media_keys_window_set_volume_level (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), vol); msd_media_keys_window_set_action (MSD_MEDIA_KEYS_WINDOW (manager->priv->dialog), MSD_MEDIA_KEYS_WINDOW_ACTION_VOLUME); dialog_show (manager); #endif /* HAVE_PULSE */ } #endif /* defined(HAVE_PULSE) || defined(HAVE_GSTREAMER) */ #ifdef HAVE_PULSE static void update_default_sink (MsdMediaKeysManager *manager) { GvcMixerStream *stream; stream = gvc_mixer_control_get_default_sink (manager->priv->volume); if (stream == manager->priv->stream) return; if (manager->priv->stream != NULL) { g_object_unref (manager->priv->stream); manager->priv->stream = NULL; } if (stream != NULL) { manager->priv->stream = g_object_ref (stream); } else { g_warning ("Unable to get default sink"); } } static void on_control_ready (GvcMixerControl *control, MsdMediaKeysManager *manager) { update_default_sink (manager); } static void on_control_default_sink_changed (GvcMixerControl *control, guint id, MsdMediaKeysManager *manager) { update_default_sink (manager); } #endif /* HAVE_PULSE */ static gint find_by_application (gconstpointer a, gconstpointer b) { return strcmp (((MediaPlayer *)a)->application, b); } static gint find_by_time (gconstpointer a, gconstpointer b) { return ((MediaPlayer *)a)->time < ((MediaPlayer *)b)->time; } /* * Register a new media player. Most applications will want to call * this with time = GDK_CURRENT_TIME. This way, the last registered * player will receive media events. In some cases, applications * may want to register with a lower priority (usually 1), to grab * events only nobody is interested. */ gboolean msd_media_keys_manager_grab_media_player_keys (MsdMediaKeysManager *manager, const char *application, guint32 time, GError **error) { GList *iter; MediaPlayer *media_player; if (time == GDK_CURRENT_TIME) { GTimeVal tv; g_get_current_time (&tv); time = tv.tv_sec * 1000 + tv.tv_usec / 1000; } iter = g_list_find_custom (manager->priv->media_players, application, find_by_application); if (iter != NULL) { if (((MediaPlayer *)iter->data)->time < time) { g_free (((MediaPlayer *)iter->data)->application); g_free (iter->data); manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter); } else { return TRUE; } } g_debug ("Registering %s at %u", application, time); media_player = g_new0 (MediaPlayer, 1); media_player->application = g_strdup (application); media_player->time = time; manager->priv->media_players = g_list_insert_sorted (manager->priv->media_players, media_player, find_by_time); return TRUE; } gboolean msd_media_keys_manager_release_media_player_keys (MsdMediaKeysManager *manager, const char *application, GError **error) { GList *iter; iter = g_list_find_custom (manager->priv->media_players, application, find_by_application); if (iter != NULL) { g_debug ("Deregistering %s", application); g_free (((MediaPlayer *)iter->data)->application); g_free (iter->data); manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter); } return TRUE; } static gboolean msd_media_player_key_pressed (MsdMediaKeysManager *manager, const char *key) { const char *application = NULL; gboolean have_listeners; have_listeners = (manager->priv->media_players != NULL); if (have_listeners) { application = ((MediaPlayer *)manager->priv->media_players->data)->application; } g_signal_emit (manager, signals[MEDIA_PLAYER_KEY_PRESSED], 0, application, key); return !have_listeners; } static gboolean do_multimedia_player_action (MsdMediaKeysManager *manager, const char *key) { return msd_media_player_key_pressed (manager, key); } static gboolean do_action (MsdMediaKeysManager *manager, int type) { char *cmd; char *path; switch (type) { case TOUCHPAD_KEY: do_touchpad_action (manager); break; case MUTE_KEY: case VOLUME_DOWN_KEY: case VOLUME_UP_KEY: #if defined(HAVE_PULSE) || defined(HAVE_GSTREAMER) do_sound_action (manager, type); #endif /* HAVE_PULSE || HAVE_GSTREAMER */ break; case POWER_KEY: do_exit_action (manager); break; case EJECT_KEY: do_eject_action (manager); break; case HOME_KEY: path = g_shell_quote (g_get_home_dir ()); cmd = g_strconcat ("caja --no-desktop ", path, NULL); g_free (path); execute (manager, cmd, FALSE, FALSE); g_free (cmd); break; case SEARCH_KEY: cmd = NULL; if ((cmd = g_find_program_in_path ("beagle-search"))) { execute (manager, "beagle-search", FALSE, FALSE); } else if ((cmd = g_find_program_in_path ("tracker-search-tool"))) { execute (manager, "tracker-search-tool", FALSE, FALSE); } else { execute (manager, "mate-search-tool", FALSE, FALSE); } g_free (cmd); break; case EMAIL_KEY: do_mail_action (manager); break; case SCREENSAVER_KEY: if ((cmd = g_find_program_in_path ("mate-screensaver-command"))) { execute (manager, "mate-screensaver-command --lock", FALSE, FALSE); } else { execute (manager, "xscreensaver-command -lock", FALSE, FALSE); } g_free (cmd); break; case HELP_KEY: do_help_action (manager); break; case WWW_KEY: do_www_action (manager, NULL); break; case MEDIA_KEY: do_media_action (manager); break; case CALCULATOR_KEY: execute (manager, "gcalctool", FALSE, FALSE); break; case PLAY_KEY: return do_multimedia_player_action (manager, "Play"); break; case PAUSE_KEY: return do_multimedia_player_action (manager, "Pause"); break; case STOP_KEY: return do_multimedia_player_action (manager, "Stop"); break; case PREVIOUS_KEY: return do_multimedia_player_action (manager, "Previous"); break; case NEXT_KEY: return do_multimedia_player_action (manager, "Next"); break; default: g_assert_not_reached (); } return FALSE; } static GdkScreen * acme_get_screen_from_event (MsdMediaKeysManager *manager, XAnyEvent *xanyev) { GdkWindow *window; GdkScreen *screen; GSList *l; /* Look for which screen we're receiving events */ for (l = manager->priv->screens; l != NULL; l = l->next) { screen = (GdkScreen *) l->data; window = gdk_screen_get_root_window (screen); if (GDK_WINDOW_XID (window) == xanyev->window) { return screen; } } return NULL; } static GdkFilterReturn acme_filter_events (GdkXEvent *xevent, GdkEvent *event, MsdMediaKeysManager *manager) { XEvent *xev = (XEvent *) xevent; XAnyEvent *xany = (XAnyEvent *) xevent; int i; /* verify we have a key event */ if (xev->type != KeyPress && xev->type != KeyRelease) { return GDK_FILTER_CONTINUE; } for (i = 0; i < HANDLED_KEYS; i++) { if (match_key (keys[i].key, xev)) { switch (keys[i].key_type) { case VOLUME_DOWN_KEY: case VOLUME_UP_KEY: /* auto-repeatable keys */ if (xev->type != KeyPress) { return GDK_FILTER_CONTINUE; } break; default: if (xev->type != KeyRelease) { return GDK_FILTER_CONTINUE; } } manager->priv->current_screen = acme_get_screen_from_event (manager, xany); if (do_action (manager, keys[i].key_type) == FALSE) { return GDK_FILTER_REMOVE; } else { return GDK_FILTER_CONTINUE; } } } return GDK_FILTER_CONTINUE; } static gboolean start_media_keys_idle_cb (MsdMediaKeysManager *manager) { GSList *l; g_debug ("Starting media_keys manager"); mate_settings_profile_start (NULL); manager->priv->volume_monitor = g_volume_monitor_get (); manager->priv->conf_client = mateconf_client_get_default (); mateconf_client_add_dir (manager->priv->conf_client, MATECONF_BINDING_DIR, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); init_screens (manager); init_kbd (manager); /* Start filtering the events */ for (l = manager->priv->screens; l != NULL; l = l->next) { mate_settings_profile_start ("gdk_window_add_filter"); g_debug ("adding key filter for screen: %d", gdk_screen_get_number (l->data)); gdk_window_add_filter (gdk_screen_get_root_window (l->data), (GdkFilterFunc)acme_filter_events, manager); mate_settings_profile_end ("gdk_window_add_filter"); } mate_settings_profile_end (NULL); return FALSE; } gboolean msd_media_keys_manager_start (MsdMediaKeysManager *manager, GError **error) { mate_settings_profile_start (NULL); #ifdef HAVE_PULSE /* initialise Volume handler * * We do this one here to force checking gstreamer cache, etc. * The rest (grabbing and setting the keys) can happen in an * idle. */ mate_settings_profile_start ("gvc_mixer_control_new"); manager->priv->volume = gvc_mixer_control_new ("MATE Volume Control Media Keys"); g_signal_connect (manager->priv->volume, "ready", G_CALLBACK (on_control_ready), manager); g_signal_connect (manager->priv->volume, "default-sink-changed", G_CALLBACK (on_control_default_sink_changed), manager); gvc_mixer_control_open (manager->priv->volume); mate_settings_profile_end ("gvc_mixer_control_new"); #elif defined(HAVE_GSTREAMER) mate_settings_profile_start ("acme_volume_new"); manager->priv->volume = acme_volume_new (); mate_settings_profile_end ("acme_volume_new"); #endif /* HAVE_PULSE */ g_idle_add ((GSourceFunc) start_media_keys_idle_cb, manager); mate_settings_profile_end (NULL); return TRUE; } void msd_media_keys_manager_stop (MsdMediaKeysManager *manager) { MsdMediaKeysManagerPrivate *priv = manager->priv; GSList *ls; GList *l; int i; gboolean need_flush; g_debug ("Stopping media_keys manager"); for (ls = priv->screens; ls != NULL; ls = ls->next) { gdk_window_remove_filter (gdk_screen_get_root_window (ls->data), (GdkFilterFunc) acme_filter_events, manager); } if (priv->conf_client) { mateconf_client_remove_dir (priv->conf_client, MATECONF_BINDING_DIR, NULL); for (i = 0; i < HANDLED_KEYS; ++i) { if (priv->notify[i] != 0) { mateconf_client_notify_remove (priv->conf_client, priv->notify[i]); priv->notify[i] = 0; } } g_object_unref (priv->conf_client); priv->conf_client = NULL; } if (priv->volume_monitor != NULL) { g_object_unref (priv->volume_monitor); priv->volume_monitor = NULL; } if (priv->connection != NULL) { dbus_g_connection_unref (priv->connection); priv->connection = NULL; } need_flush = FALSE; gdk_error_trap_push (); for (i = 0; i < HANDLED_KEYS; ++i) { if (keys[i].key) { need_flush = TRUE; grab_key_unsafe (keys[i].key, FALSE, priv->screens); g_free (keys[i].key->keycodes); g_free (keys[i].key); keys[i].key = NULL; } } if (need_flush) gdk_flush (); gdk_error_trap_pop (); g_slist_free (priv->screens); priv->screens = NULL; #ifdef HAVE_PULSE if (priv->stream) { g_object_unref (priv->stream); priv->stream = NULL; } #endif /* HAVE_PULSE */ #if defined(HAVE_PULSE) || defined(HAVE_GSTREAMER) if (priv->volume) { g_object_unref (priv->volume); priv->volume = NULL; } #endif /* defined(HAVE_PULSE) || defined(HAVE_GSTREAMER) */ if (priv->dialog != NULL) { gtk_widget_destroy (priv->dialog); priv->dialog = NULL; } for (l = priv->media_players; l; l = l->next) { MediaPlayer *mp = l->data; g_free (mp->application); g_free (mp); } g_list_free (priv->media_players); priv->media_players = NULL; } static void msd_media_keys_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MsdMediaKeysManager *self; self = MSD_MEDIA_KEYS_MANAGER (object); switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void msd_media_keys_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MsdMediaKeysManager *self; self = MSD_MEDIA_KEYS_MANAGER (object); switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * msd_media_keys_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { MsdMediaKeysManager *media_keys_manager; MsdMediaKeysManagerClass *klass; klass = MSD_MEDIA_KEYS_MANAGER_CLASS (g_type_class_peek (MSD_TYPE_MEDIA_KEYS_MANAGER)); media_keys_manager = MSD_MEDIA_KEYS_MANAGER (G_OBJECT_CLASS (msd_media_keys_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (media_keys_manager); } static void msd_media_keys_manager_dispose (GObject *object) { MsdMediaKeysManager *media_keys_manager; media_keys_manager = MSD_MEDIA_KEYS_MANAGER (object); G_OBJECT_CLASS (msd_media_keys_manager_parent_class)->dispose (object); } static void msd_media_keys_manager_class_init (MsdMediaKeysManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = msd_media_keys_manager_get_property; object_class->set_property = msd_media_keys_manager_set_property; object_class->constructor = msd_media_keys_manager_constructor; object_class->dispose = msd_media_keys_manager_dispose; object_class->finalize = msd_media_keys_manager_finalize; signals[MEDIA_PLAYER_KEY_PRESSED] = g_signal_new ("media-player-key-pressed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MsdMediaKeysManagerClass, media_player_key_pressed), NULL, NULL, msd_marshal_VOID__STRING_STRING, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); dbus_g_object_type_install_info (MSD_TYPE_MEDIA_KEYS_MANAGER, &dbus_glib_msd_media_keys_manager_object_info); g_type_class_add_private (klass, sizeof (MsdMediaKeysManagerPrivate)); } static void msd_media_keys_manager_init (MsdMediaKeysManager *manager) { manager->priv = MSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager); } static void msd_media_keys_manager_finalize (GObject *object) { MsdMediaKeysManager *media_keys_manager; g_return_if_fail (object != NULL); g_return_if_fail (MSD_IS_MEDIA_KEYS_MANAGER (object)); media_keys_manager = MSD_MEDIA_KEYS_MANAGER (object); g_return_if_fail (media_keys_manager->priv != NULL); G_OBJECT_CLASS (msd_media_keys_manager_parent_class)->finalize (object); } static gboolean register_manager (MsdMediaKeysManager *manager) { GError *error = NULL; manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); if (manager->priv->connection == NULL) { if (error != NULL) { g_error ("Error getting session bus: %s", error->message); g_error_free (error); } return FALSE; } dbus_g_connection_register_g_object (manager->priv->connection, MSD_MEDIA_KEYS_DBUS_PATH, G_OBJECT (manager)); return TRUE; } MsdMediaKeysManager * msd_media_keys_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { gboolean res; manager_object = g_object_new (MSD_TYPE_MEDIA_KEYS_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); res = register_manager (manager_object); if (! res) { g_object_unref (manager_object); return NULL; } } return MSD_MEDIA_KEYS_MANAGER (manager_object); }