From cd175929ed3d08a935cbbf10d664411e1227db99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Billeter?= Date: Fri, 5 May 2006 13:24:27 +0000 Subject: [PATCH] Add sound device selection using HAL and GStreamer (Closes: #329112). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2006-05-05 Jürg Billeter * Makefile.am: * pipeline-tests.c: (user_test_pipeline_response), (user_test_pipeline_timeout), (build_test_pipeline), (pipeline_error_dlg), (user_test_pipeline): * pipeline-tests.h: * sound-properties-capplet.c: (element_available), (add_device), (remove_device), (device_added_callback), (device_removed_callback), (setup_hal_devices), (device_test_button_clicked), (get_gconf_key_for_profile), (device_changed), (get_gconf_description_for_profile), (gconf_key_changed), (setup_device_chooser), (add_selected_device), (setup_devices), (setup_dialog), (main): * sound-properties.glade: Add sound device selection using HAL and GStreamer (Closes: #329112). --- capplets/sound/ChangeLog | 18 + capplets/sound/Makefile.am | 7 +- capplets/sound/pipeline-tests.c | 187 +++++ capplets/sound/pipeline-tests.h | 36 + capplets/sound/sound-properties-capplet.c | 531 ++++++++++++- capplets/sound/sound-properties.glade | 902 +++++++++++++++++++++- 6 files changed, 1671 insertions(+), 10 deletions(-) create mode 100644 capplets/sound/pipeline-tests.c create mode 100644 capplets/sound/pipeline-tests.h diff --git a/capplets/sound/ChangeLog b/capplets/sound/ChangeLog index dbe537c93..884b46c7e 100644 --- a/capplets/sound/ChangeLog +++ b/capplets/sound/ChangeLog @@ -1,3 +1,21 @@ +2006-05-05 Jürg Billeter + + * Makefile.am: + * pipeline-tests.c: (user_test_pipeline_response), + (user_test_pipeline_timeout), (build_test_pipeline), + (pipeline_error_dlg), (user_test_pipeline): + * pipeline-tests.h: + * sound-properties-capplet.c: (element_available), (add_device), + (remove_device), (device_added_callback), + (device_removed_callback), (setup_hal_devices), + (device_test_button_clicked), (get_gconf_key_for_profile), + (device_changed), (get_gconf_description_for_profile), + (gconf_key_changed), (setup_device_chooser), (add_selected_device), + (setup_devices), (setup_dialog), (main): + * sound-properties.glade: + + Add sound device selection using HAL and GStreamer (Closes: #329112). + 2006-01-11 Scott Reeves * sound-properties.glade: new GUI. diff --git a/capplets/sound/Makefile.am b/capplets/sound/Makefile.am index 457c5f00b..2a35a31d3 100644 --- a/capplets/sound/Makefile.am +++ b/capplets/sound/Makefile.am @@ -3,8 +3,9 @@ bin_PROGRAMS = gnome-sound-properties gnome_sound_properties_LDADD = \ $(top_builddir)/libsounds/libsounds.a \ $(GNOMECC_CAPPLETS_LIBS) \ - $(SOUND_CAPPLET_LIBS) -gnome_sound_properties_SOURCES = sound-properties-capplet.c + $(SOUND_CAPPLET_LIBS) \ + $(HAL_LIBS) +gnome_sound_properties_SOURCES = sound-properties-capplet.c pipeline-tests.c @INTLTOOL_DESKTOP_RULE@ @@ -18,6 +19,6 @@ desktopdir = $(datadir)/applications Desktop_in_files = gnome-settings-sound.desktop.in desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) -INCLUDES = $(GNOMECC_CAPPLETS_CFLAGS) $(SOUND_CAPPLET_CFLAGS) +INCLUDES = $(GNOMECC_CAPPLETS_CFLAGS) $(SOUND_CAPPLET_CFLAGS) $(HAL_CFLAGS) CLEANFILES = $(GNOMECC_CAPPLETS_CLEANFILES) $(Desktop_in_files) $(desktop_DATA) EXTRA_DIST = $(Glade_DATA) $(desktop_icons_DATA) diff --git a/capplets/sound/pipeline-tests.c b/capplets/sound/pipeline-tests.c new file mode 100644 index 000000000..90166a05d --- /dev/null +++ b/capplets/sound/pipeline-tests.c @@ -0,0 +1,187 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* pipeline-tests.c + * Copyright (C) 2002 Jan Schmidt + * Copyright (C) 2005 Tim-Philipp Müller + * Copyright (C) 2006 Jürg Billeter + * + * Written by: Jan Schmidt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "pipeline-tests.h" +#define WID(s) glade_xml_get_widget (interface_xml, s) +static gint timeout_tag; + +static GstElement *gst_test_pipeline; + +static void pipeline_error_dlg (GtkWindow * parent, + const gchar * pipeline, const gchar * error_message); + +/* User responded in the dialog */ +static void +user_test_pipeline_response (GtkDialog * widget, gint response_id, + GladeXML * dialog) +{ + /* Close the window causing the test to end */ + gtk_widget_hide (GTK_WIDGET (widget)); +} + +/* Timer timeout has been occurred */ +static gint +user_test_pipeline_timeout (gpointer data) +{ + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data)); + return TRUE; +} + +/* Build the pipeline */ +static gboolean +build_test_pipeline (const gchar * pipeline, GError ** p_err) +{ + gst_test_pipeline = gst_parse_launch (pipeline, p_err); + + if (*p_err == NULL && gst_test_pipeline != NULL) + return TRUE; + + return FALSE; +} + +static void +pipeline_error_dlg (GtkWindow * parent, + const gchar * pipeline, const gchar * error_message) +{ + gchar *errstr; + + if (error_message) { + errstr = g_strdup_printf ("%s: %s", pipeline, error_message); + } else { + errstr = g_strdup_printf (_("Failed to construct test pipeline for '%s'"), + pipeline); + } + + if (parent == NULL) { + g_printerr ("%s", errstr); + } else { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", errstr); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + + g_free (errstr); +} + +/* Construct and run the pipeline. Use the indicated parent + * for any user interaction window. + */ +void +user_test_pipeline (GladeXML * interface_xml, + GtkWindow * parent, const gchar * pipeline) +{ + GstStateChangeReturn ret; + GtkDialog *dialog = NULL; + GstMessage *msg; + GError *err = NULL; + GstBus *bus; + + gst_test_pipeline = NULL; + + /* Build the pipeline */ + if (!build_test_pipeline (pipeline, &err)) { + /* Show the error pipeline */ + pipeline_error_dlg (parent, pipeline, (err) ? err->message : NULL); + if (err) + g_error_free (err); + return; + } + + /* Setup the 'click ok when done' dialog */ + if (parent) { + dialog = GTK_DIALOG (WID ("test_pipeline")); + /* g_return_if_fail(dialog != NULL); */ + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + g_signal_connect (G_OBJECT (dialog), "response", + (GCallback) user_test_pipeline_response, interface_xml); + } + + /* Start the pipeline and wait for max. 3 seconds for it to start up */ + gst_element_set_state (gst_test_pipeline, GST_STATE_PLAYING); + ret = gst_element_get_state (gst_test_pipeline, NULL, NULL, 3 * GST_SECOND); + + /* Check if any error messages were posted on the bus */ + bus = gst_element_get_bus (gst_test_pipeline); + msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0); + gst_object_unref (bus); + + if (msg != NULL) { + gchar *dbg = NULL; + + gst_message_parse_error (msg, &err, &dbg); + gst_message_unref (msg); + + g_message ("Error running pipeline '%s': %s [%s]", pipeline, + (err) ? err->message : "(null error)", + (dbg) ? dbg : "no additional debugging details"); + pipeline_error_dlg (parent, pipeline, err->message); + g_error_free (err); + g_free (dbg); + } else if (ret != GST_STATE_CHANGE_SUCCESS) { + pipeline_error_dlg (parent, pipeline, NULL); + } else { + /* Show the dialog */ + if (dialog) { + gtk_window_present (GTK_WINDOW (dialog)); + timeout_tag = + gtk_timeout_add (50, user_test_pipeline_timeout, + WID ("test_pipeline_progress")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_timeout_remove (timeout_tag); + gtk_widget_hide (GTK_WIDGET (dialog)); + } else { + gint secs; + + /* A bit hacky: No parent dialog, run in limited test mode */ + for (secs = 0; secs < 5; ++secs) { + g_print ("."); + g_usleep (G_USEC_PER_SEC); /* 1 second */ + } + } + } + + if (gst_test_pipeline) { + gst_element_set_state (gst_test_pipeline, GST_STATE_NULL); + gst_object_unref (gst_test_pipeline); + gst_test_pipeline = NULL; + } +} diff --git a/capplets/sound/pipeline-tests.h b/capplets/sound/pipeline-tests.h new file mode 100644 index 000000000..e2e396d35 --- /dev/null +++ b/capplets/sound/pipeline-tests.h @@ -0,0 +1,36 @@ +/* -*- mode: c; style: linux -*- */ +/* -*- c-basic-offset: 2 -*- */ + +/* pipeline-tests.h + * Copyright (C) 2002 Jan Schmidt + * Copyright (C) 2006 Jürg Billeter + * + * Written by: Jan Schmidt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef __PIPELINE_TESTS_HH__ +#define __PIPELINE_TESTS_HH__ + +#include +#include + +void user_test_pipeline(GladeXML *interface_xml, + GtkWindow *parent, + const gchar *pipeline); + + +#endif diff --git a/capplets/sound/sound-properties-capplet.c b/capplets/sound/sound-properties-capplet.c index b45f5cc43..20b68fa10 100644 --- a/capplets/sound/sound-properties-capplet.c +++ b/capplets/sound/sound-properties-capplet.c @@ -40,22 +40,52 @@ #include +#include +#include +#include + /* Needed only for the sound capplet */ #include #include #include "activate-settings-daemon.h" +#include "pipeline-tests.h" + +typedef enum { + AUDIO_PLAYBACK, + AUDIO_CAPTURE, + VIDEO_PLAYBACK, + VIDEO_CAPTURE +} device_type; + +typedef struct _DeviceChooser +{ + const gchar *profile; + int type; + GtkWidget *combobox; + GtkListStore *model; + const gchar *test_pipeline; +} DeviceChooser; #define ENABLE_ESD_KEY "/desktop/gnome/sound/enable_esd" #define EVENT_SOUNDS_KEY "/desktop/gnome/sound/event_sounds" #define VISUAL_BELL_KEY "/apps/metacity/general/visual_bell" #define AUDIO_BELL_KEY "/apps/metacity/general/audible_bell" #define VISUAL_BELL_TYPE_KEY "/apps/metacity/general/visual_bell_type" +#define GST_GCONF_DIR "/system/gstreamer/0.10" + +#define AUDIO_TEST_SOURCE "audiotestsrc wave=sine freq=512" +#define VIDEO_TEST_SOURCE "videotestsrc" +#define AUDIO_TEST_IN_BETWEEN " ! audioconvert ! audioresample ! " +#define VIDEO_TEST_IN_BETWEEN " ! ffmpegcolorspace ! " /* Capplet-specific prototypes */ static SoundProperties *props = NULL; +static GConfClient *gconf_client = NULL; +static GtkWidget *dialog_win = NULL; +static GList *device_choosers = NULL; static gboolean CheckXKB (void) @@ -163,6 +193,464 @@ create_dialog (void) return dialog; } +static gboolean +element_available (const gchar *pipeline) +{ + gboolean res = FALSE; + gchar *p, *first_space; + + if (pipeline == NULL || *pipeline == '\0') + return FALSE; + + p = g_strdup (pipeline); + + g_strstrip (p); + + /* skip the check and pretend all is fine if it's something that does + * not look like an element name (e.g. parentheses to signify a bin) */ + if (!g_ascii_isalpha (*p)) { + g_free (p); + return TRUE; + } + + /* just the element name, no arguments */ + first_space = strchr (p, ' '); + if (first_space != NULL) + *first_space = '\0'; + + /* check if element is available */ + res = gst_default_registry_check_feature_version (p, GST_VERSION_MAJOR, + GST_VERSION_MINOR, 0); + + g_free (p); + return res; +} +static void +add_device (int type, const gchar *pipeline, const gchar *description, const gchar *selection) +{ + GList *l; + DeviceChooser *device_chooser; + gchar *row_pipeline; + gchar *description_mod; + const gchar *suffix; + gboolean selected, row_selected; + gboolean found_pipeline, found_selected; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeIter found_pipeline_iter = { }; + GtkTreeIter found_selected_iter = { }; + + /* only display pipelines available on this system */ + if (!element_available (pipeline)) + return; + + for (l = device_choosers; l != NULL; l = l->next) { + device_chooser = (DeviceChooser *) l->data; + + if (device_chooser->type != type) + continue; + + /* if we're adding/updating the currently selected device + * we're only interested in the DeviceChooser for the + * corresponding profile */ + if (selection != NULL && strcmp (selection, device_chooser->profile) != 0) + continue; + + selected = (selection != NULL); + + found_pipeline = FALSE; + found_selected = FALSE; + + model = GTK_TREE_MODEL (device_chooser->model); + + if (gtk_tree_model_get_iter_first (model, &iter)) { + do { + gtk_tree_model_get (model, &iter, 0, &row_pipeline, 2, &row_selected, -1); + + if (strcmp (row_pipeline, pipeline) == 0) { + found_pipeline_iter = iter; + found_pipeline = TRUE; + } + g_free (row_pipeline); + + if (row_selected) { + found_selected_iter = iter; + found_selected = TRUE; + } + + } while (gtk_tree_model_iter_next (model, &iter)); + } + + if (found_pipeline) { + /* the specified pipeline is already in the model */ + iter = found_pipeline_iter; + if (!selected) { + /* if the specified pipeline was only already in + * the model because it's currently selected, + * clear the corresponding flag in the model + * (happens when reconnecting usb audio devices) */ + gtk_list_store_set (device_chooser->model, &iter, 2, FALSE, -1); + } + } else if (selected && found_selected) { + /* we only allow one custom pipeline in the model */ + iter = found_selected_iter; + } else { + /* no existing pipeline found that would be appropriate + * to reuse, so append a new row */ + gtk_list_store_append (device_chooser->model, &iter); + gtk_list_store_set (device_chooser->model, &iter, 2, selected, -1); + } + + gtk_list_store_set (device_chooser->model, &iter, 0, pipeline, -1); + + if (!selected || !found_pipeline) { + /* either we're appending a new row or we're updating + * a row with non-appropriate description */ + + if (selected) { + /* we add the specified pipeline only because + * it's the preferred device but it's neither + * a device currently detected by HAL nor a + * standard pipeline */ + if (g_str_has_prefix (pipeline, "hal")) { + suffix = _("Not connected"); + } else { + suffix = _("Custom"); + } + description_mod = g_strdup_printf ("%s (%s)", description, suffix); + } else { + description_mod = g_strdup (description); + } + gtk_list_store_set (device_chooser->model, &iter, 1, description_mod, -1); + + /* we need to store the unmodified description to not + * modify already modified descriptions */ + gtk_list_store_set (device_chooser->model, &iter, 3, description, -1); + + g_free (description_mod); + } + + if (selected) { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (device_chooser->combobox), &iter); + } + } +} + +static void +remove_device (int type, const gchar *pipeline) +{ + GList *l; + DeviceChooser *device_chooser; + GtkTreeIter iter; + gchar *row_pipeline; + gchar *description; + gchar *description_mod; + GtkTreeModel *model; + + for (l = device_choosers; l != NULL; l = l->next) { + device_chooser = (DeviceChooser *) l->data; + + if (device_chooser->type != type) + continue; + + model = GTK_TREE_MODEL (device_chooser->model); + + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (device_chooser->combobox), &iter); + gtk_tree_model_get (model, &iter, 0, &row_pipeline, -1); + + /* don't remove currently selected devices */ + if (strcmp (row_pipeline, pipeline) == 0) { + g_free (row_pipeline); + + /* let the user know that they have to reconnect the + * sound device */ + gtk_tree_model_get (model, &iter, 3, &description, -1); + description_mod = g_strdup_printf ("%s (%s)", description, _("Not connected")); + g_free (description); + gtk_list_store_set (device_chooser->model, &iter, 1, description_mod, -1); + g_free (description_mod); + } else { + g_free (row_pipeline); + + if (gtk_tree_model_get_iter_first (model, &iter)) { + do { + gtk_tree_model_get (model, &iter, 0, &row_pipeline, -1); + if (strcmp (row_pipeline, pipeline) == 0) { + g_free (row_pipeline); + gtk_list_store_remove (device_chooser->model, &iter); + break; + } + g_free (row_pipeline); + } while (gtk_tree_model_iter_next (model, &iter)); + } + } + } +} + +#if USE_HAL +static void +device_added_callback (LibHalContext *ctx, const char *udi) +{ + DBusError error; + gchar *type_string; + int type; + const gchar *element; + gchar *pipeline, *description; + + dbus_error_init (&error); + + if (!libhal_device_query_capability (ctx, udi, "alsa", &error)) { + dbus_error_free (&error); + return; + } + + type_string = libhal_device_get_property_string (ctx, udi, "alsa.type", &error); + if (strcmp (type_string, "playback") == 0) { + type = AUDIO_PLAYBACK; + element = "halaudiosink"; + } else if (strcmp (type_string, "capture") == 0) { + type = AUDIO_CAPTURE; + element = "halaudiosrc"; + } else { + type = -1; + element = NULL; + } + libhal_free_string (type_string); + if (type == -1) { + dbus_error_free (&error); + return; + } + + pipeline = g_strdup_printf ("%s udi=%s", element, udi); + description = libhal_device_get_property_string (ctx, udi, "alsa.device_id", &error); + + add_device (type, pipeline, description, NULL); + + g_free (pipeline); + libhal_free_string (description); + + dbus_error_free (&error); +} + +static void +device_removed_callback (LibHalContext *ctx, const char *udi) +{ + gchar *pipeline; + + pipeline = g_strdup_printf ("halaudiosink udi=%s", udi); + remove_device (AUDIO_PLAYBACK, pipeline); + g_free (pipeline); + + pipeline = g_strdup_printf ("halaudiosrc udi=%s", udi); + remove_device (AUDIO_CAPTURE, pipeline); + g_free (pipeline); +} + +static void +setup_hal_devices () +{ + DBusConnection *connection; + DBusError error; + LibHalContext *ctx; + char **devices; + int i, num; + + dbus_error_init (&error); + + connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error); + g_return_if_fail (connection != NULL); + + dbus_connection_setup_with_g_main (connection, g_main_context_default ()); + + ctx = libhal_ctx_new (); + g_return_if_fail (ctx != NULL); + + libhal_ctx_set_device_added (ctx, device_added_callback); + libhal_ctx_set_device_removed (ctx, device_removed_callback); + libhal_ctx_set_dbus_connection (ctx, connection); + + libhal_ctx_init (ctx, &error); + + devices = libhal_find_device_by_capability (ctx, "alsa", &num, &error); + + for (i = 0; i < num; i++) { + device_added_callback (ctx, devices[i]); + } + + dbus_free_string_array (devices); + + dbus_error_free (&error); +} +#endif + +static void +device_test_button_clicked (GtkWidget *button, gpointer user_data) +{ + DeviceChooser *device_chooser = (DeviceChooser *) user_data; + + GladeXML *dialog = glade_xml_new (GNOMECC_DATA_DIR "/interfaces/sound-properties.glade", NULL, NULL); + + user_test_pipeline (dialog, GTK_WINDOW (dialog_win), device_chooser->test_pipeline); + + g_object_unref (dialog); +} + +static gchar * +get_gconf_key_for_profile (const gchar *profile, int type) +{ + const gchar *element_type; + + switch (type) { + case AUDIO_PLAYBACK: + element_type = "audiosink"; + break; + case AUDIO_CAPTURE: + element_type = "audiosrc"; + break; + case VIDEO_PLAYBACK: + element_type = "videosink"; + break; + case VIDEO_CAPTURE: + element_type = "videosrc"; + break; + default: + g_return_val_if_reached (NULL); + } + + return g_strdup_printf ("%s/default/%s%s", GST_GCONF_DIR, profile, element_type); +} + +static void +device_changed (GtkComboBox *widget, gpointer user_data) +{ + DeviceChooser *device_chooser = (DeviceChooser *) user_data; + GtkTreeIter iter; + gchar *pipeline, *description; + gchar *base_key, *key; + + gtk_combo_box_get_active_iter (widget, &iter); + gtk_tree_model_get (GTK_TREE_MODEL (device_chooser->model), &iter, 0, &pipeline, 3, &description, -1); + + base_key = get_gconf_key_for_profile (device_chooser->profile, device_chooser->type); + gconf_client_set_string (gconf_client, base_key, pipeline, NULL); + g_free (pipeline); + key = g_strconcat (base_key, "_description", NULL); + g_free (base_key); + gconf_client_set_string (gconf_client, key, description, NULL); + g_free (description); + g_free (key); +} + +static gchar * +get_gconf_description_for_profile (const gchar *profile, int type) +{ + gchar *base_key; + gchar *key; + gchar *description; + + base_key = get_gconf_key_for_profile (profile, type); + key = g_strconcat (base_key, "_description", NULL); + g_free (base_key); + + description = gconf_client_get_string (gconf_client, key, NULL); + g_free (key); + + return description; +} + +static void +gconf_key_changed (GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer data) +{ + DeviceChooser *device_chooser = (DeviceChooser *) data; + gchar *description; + + description = get_gconf_description_for_profile (device_chooser->profile, device_chooser->type); + + add_device (device_chooser->type, gconf_value_get_string (gconf_entry_get_value (entry)), description, device_chooser->profile); + + g_free (description); +} + +static void +setup_device_chooser (const gchar *profile, int type, GtkWidget *combobox, GtkWidget *test_button, const gchar *test_pipeline) +{ + DeviceChooser *device_chooser; + GtkCellRenderer *cell; + gchar *gconf_key; + + g_return_if_fail (GTK_IS_COMBO_BOX (combobox)); + g_return_if_fail (GTK_IS_BUTTON (test_button)); + + device_chooser = g_malloc0 (sizeof (DeviceChooser)); + + device_chooser->profile = profile; + device_chooser->type = type; + device_chooser->combobox = combobox; + device_chooser->model = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING); + device_chooser->test_pipeline = test_pipeline; + gtk_combo_box_set_model (GTK_COMBO_BOX (combobox), GTK_TREE_MODEL (device_chooser->model)); + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), cell, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox), cell, "text", 1); + + device_choosers = g_list_prepend (device_choosers, device_chooser); + + gconf_key = get_gconf_key_for_profile (profile, type); + gconf_client_notify_add (gconf_client, gconf_key, gconf_key_changed, + device_chooser, NULL, NULL); + g_free (gconf_key); + + g_signal_connect (combobox, "changed", G_CALLBACK (device_changed), device_chooser); + + g_signal_connect (test_button, "clicked", G_CALLBACK (device_test_button_clicked), device_chooser); +} + +static void +add_selected_device (const gchar *profile, int type) +{ + gchar *gconf_key; + gchar *description; + gchar *pipeline; + + gconf_key = get_gconf_key_for_profile (profile, type); + pipeline = gconf_client_get_string (gconf_client, gconf_key, NULL); + g_free (gconf_key); + + g_return_if_fail (pipeline != NULL); + + description = get_gconf_description_for_profile (profile, type); + + add_device (type, pipeline, description, profile); + + g_free (pipeline); +} + +static void +setup_devices () +{ + add_device (AUDIO_PLAYBACK, "autoaudiosink", "Autodetect", NULL); + +#if USE_HAL + setup_hal_devices (); +#endif + add_device (AUDIO_PLAYBACK, "alsasink", "ALSA - Advanced Linux Sound Architecture", NULL); + add_device (AUDIO_CAPTURE, "alsasrc", "ALSA - Advanced Linux Sound Architecture", NULL); + add_device (AUDIO_PLAYBACK, "artsdsink", "Artsd - ART Sound Daemon", NULL); + add_device (AUDIO_PLAYBACK, "esdsink", "ESD - Enlightenment Sound Daemon", NULL); + add_device (AUDIO_CAPTURE, "esdmon", "ESD - Enlightenment Sound Daemon", NULL); + add_device (AUDIO_PLAYBACK, "osssink", "OSS - Open Sound System", NULL); + add_device (AUDIO_CAPTURE, "osssrc", "OSS - Open Sound System", NULL); + add_device (AUDIO_PLAYBACK, "polypsink", "Polypaudio Sound Server", NULL); + add_device (AUDIO_CAPTURE, "polypsrc", "Polypaudio Sound Server", NULL); + add_device (AUDIO_CAPTURE, "audiotestsrc wave=triangle is-live=true", "Test Sound", NULL); + add_device (AUDIO_CAPTURE, "audiotestsrc wave=silence is-live=true", "Silence", NULL); + + add_selected_device ("", AUDIO_PLAYBACK); + add_selected_device ("music", AUDIO_PLAYBACK); + add_selected_device ("chat", AUDIO_PLAYBACK); + add_selected_device ("", AUDIO_CAPTURE); +} + /* setup_dialog * * Set up the property editors for our dialog @@ -172,6 +660,36 @@ static void setup_dialog (GladeXML *dialog, GConfChangeSet *changeset) { GObject *peditor; + GtkSizeGroup *combobox_size_group; + GtkSizeGroup *label_size_group; + + gconf_client_add_dir (gconf_client, GST_GCONF_DIR, + GCONF_CLIENT_PRELOAD_RECURSIVE, NULL); + + combobox_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + label_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + gtk_size_group_add_widget (label_size_group, WID ("sounds_playback_label")); + gtk_size_group_add_widget (label_size_group, WID ("music_playback_label")); + gtk_size_group_add_widget (label_size_group, WID ("chat_audio_playback_label")); + gtk_size_group_add_widget (label_size_group, WID ("chat_audio_capture_label")); + gtk_size_group_add_widget (combobox_size_group, WID ("sounds_playback_device")); + gtk_size_group_add_widget (combobox_size_group, WID ("music_playback_device")); + gtk_size_group_add_widget (combobox_size_group, WID ("chat_audio_playback_device")); + gtk_size_group_add_widget (combobox_size_group, WID ("chat_audio_capture_device")); + + setup_device_chooser ("", AUDIO_PLAYBACK, WID ("sounds_playback_device"), + WID ("sounds_playback_test"), + AUDIO_TEST_SOURCE AUDIO_TEST_IN_BETWEEN "gconfaudiosink"); + setup_device_chooser ("music", AUDIO_PLAYBACK, WID ("music_playback_device"), + WID ("music_playback_test"), + AUDIO_TEST_SOURCE AUDIO_TEST_IN_BETWEEN "gconfaudiosink profile=music"); + setup_device_chooser ("chat", AUDIO_PLAYBACK, WID ("chat_audio_playback_device"), + WID ("chat_audio_playback_test"), + AUDIO_TEST_SOURCE AUDIO_TEST_IN_BETWEEN "gconfaudiosink profile=chat"); + setup_device_chooser ("", AUDIO_CAPTURE, WID ("chat_audio_capture_device"), + WID ("chat_audio_capture_test"), + "gconfaudiosrc" AUDIO_TEST_IN_BETWEEN "gconfaudiosink profile=chat"); peditor = gconf_peditor_new_boolean (NULL, ENABLE_ESD_KEY, WID ("enable_toggle"), NULL); gconf_peditor_widget_set_guard (GCONF_PROPERTY_EDITOR (peditor), WID ("events_toggle")); @@ -228,10 +746,8 @@ dialog_button_clicked_cb (GtkDialog *dialog, gint response_id, GConfChangeSet *c int main (int argc, char **argv) { - GConfClient *client; GConfChangeSet *changeset; GladeXML *dialog = NULL; - GtkWidget *dialog_win; static gboolean apply_only; static gboolean get_legacy; @@ -249,6 +765,8 @@ main (int argc, char **argv) bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); + gst_init (&argc, &argv); + gnome_program_init ("gnome-sound-properties", VERSION, LIBGNOMEUI_MODULE, argc, argv, GNOME_PARAM_POPT_TABLE, cap_options, @@ -256,9 +774,9 @@ main (int argc, char **argv) activate_settings_daemon (); - client = gconf_client_get_default (); - gconf_client_add_dir (client, "/desktop/gnome/sound", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); - gconf_client_add_dir (client, "/apps/metacity/general", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); + gconf_client = gconf_client_get_default (); + gconf_client_add_dir (gconf_client, "/desktop/gnome/sound", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); + gconf_client_add_dir (gconf_client, "/apps/metacity/general", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); if (get_legacy) { get_legacy_settings (); @@ -266,6 +784,7 @@ main (int argc, char **argv) changeset = gconf_change_set_new (); dialog = create_dialog (); setup_dialog (dialog, changeset); + setup_devices (); dialog_win = gtk_dialog_new_with_buttons (_("Sound Preferences"), NULL, GTK_DIALOG_NO_SEPARATOR, @@ -285,7 +804,7 @@ main (int argc, char **argv) gconf_change_set_unref (changeset); } - g_object_unref (client); + g_object_unref (gconf_client); g_object_unref (dialog); return 0; } diff --git a/capplets/sound/sound-properties.glade b/capplets/sound/sound-properties.glade index 22202d62b..bd5277711 100644 --- a/capplets/sound/sound-properties.glade +++ b/capplets/sound/sound-properties.glade @@ -6,7 +6,7 @@ 5 - Sound Preferences + Sound & Video Preferences GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False @@ -18,6 +18,7 @@ GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST True + False @@ -30,6 +31,772 @@ False False + + + 12 + True + False + 18 + + + + True + False + 6 + + + + True + <b>Sound Events</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + 6 + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Sound Playback: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + True + + + 0 + True + True + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-apply + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Test + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + 0 + False + False + + + + + 0 + False + False + + + + + 0 + False + False + + + + + + True + False + 6 + + + + True + <b>Music and Movies</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + 6 + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Sound Playback: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + True + + + 0 + True + True + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-apply + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Test + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + 0 + False + False + + + + + 0 + False + False + + + + + 0 + False + False + + + + + + True + False + 6 + + + + True + <b>Audio Conferencing</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + 6 + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Sound Playback: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + True + + + 0 + True + True + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-apply + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Test + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + 0 + False + False + + + + + 0 + False + False + + + + + + True + False + 6 + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + Sound Capture: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + False + True + + + 0 + True + True + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-apply + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Test + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + 0 + False + False + + + + + 0 + False + False + + + + + 0 + False + False + + + + + False + True + + + + + + True + Devices + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + 12 @@ -353,4 +1120,137 @@ + + 5 + True + True + Testing Pipeline + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + False + True + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + False + + + + True + False + 2 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + -5 + + + + + 0 + False + False + GTK_PACK_END + + + + + + 5 + True + False + 6 + + + + True + <span weight="bold" size="x-large">Testing...</span> + False + True + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + GTK_PROGRESS_LEFT_TO_RIGHT + 0.10000000149 + 0.0500000007451 + PANGO_ELLIPSIZE_NONE + + + 0 + False + False + + + + + + True + Click Ok to finish. + False + False + GTK_JUSTIFY_CENTER + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + 0 + False + False + + + + + +