diff options
Diffstat (limited to 'backends/pulse')
44 files changed, 4260 insertions, 3859 deletions
diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am index 0e5a4d6..4c851bf 100644 --- a/backends/pulse/Makefile.am +++ b/backends/pulse/Makefile.am @@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer backend_LTLIBRARIES = libmatemixer-pulse.la AM_CPPFLAGS = \ + -Wno-unknown-pragmas \ -I$(top_srcdir) \ -DG_LOG_DOMAIN=\"libmatemixer-pulse\" @@ -13,12 +14,14 @@ libmatemixer_pulse_la_CFLAGS = \ libmatemixer_pulse_la_SOURCES = \ pulse-backend.c \ pulse-backend.h \ - pulse-client-stream.c \ - pulse-client-stream.h \ pulse-connection.c \ pulse-connection.h \ pulse-device.c \ pulse-device.h \ + pulse-device-profile.c \ + pulse-device-profile.h \ + pulse-device-switch.c \ + pulse-device-switch.h \ pulse-enums.h \ pulse-enum-types.c \ pulse-enum-types.h \ @@ -28,16 +31,31 @@ libmatemixer_pulse_la_SOURCES = \ pulse-helpers.h \ pulse-monitor.c \ pulse-monitor.h \ + pulse-port.c \ + pulse-port.h \ + pulse-port-switch.c \ + pulse-port-switch.h \ pulse-stream.c \ pulse-stream.h \ + pulse-stream-control.c \ + pulse-stream-control.h \ pulse-sink.c \ pulse-sink.h \ + pulse-sink-control.c \ + pulse-sink-control.h \ pulse-sink-input.c \ pulse-sink-input.h \ + pulse-sink-switch.c \ + pulse-sink-switch.h \ pulse-source.c \ pulse-source.h \ + pulse-source-control.c \ + pulse-source-control.h \ pulse-source-output.c \ - pulse-source-output.h + pulse-source-output.h \ + pulse-source-switch.c \ + pulse-source-switch.h \ + pulse-types.h libmatemixer_pulse_la_LIBADD = \ $(GLIB_LIBS) \ diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c index 0494545..e1b7ed5 100644 --- a/backends/pulse/pulse-backend.c +++ b/backends/pulse/pulse-backend.c @@ -19,9 +19,8 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-backend.h> -#include <libmatemixer/matemixer-backend-module.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include <pulse/ext-stream-restore.h> @@ -38,72 +37,110 @@ #include "pulse-source-output.h" #define BACKEND_NAME "PulseAudio" -#define BACKEND_PRIORITY 10 +#define BACKEND_PRIORITY 100 struct _PulseBackendPrivate { - gchar *app_name; - gchar *app_id; - gchar *app_version; - gchar *app_icon; - gchar *server_address; - gboolean connected_once; - GSource *connect_source; - MateMixerStream *default_sink; - MateMixerStream *default_source; - GHashTable *devices; - GHashTable *streams; - GHashTable *ext_streams; - MateMixerState state; - PulseConnection *connection; -}; - -enum { - PROP_0, - PROP_STATE, - PROP_DEFAULT_INPUT, - PROP_DEFAULT_OUTPUT, - N_PROPERTIES + guint connect_tag; + gboolean connected_once; + GHashTable *devices; + GHashTable *sinks; + GHashTable *sources; + GHashTable *sink_inputs; + GHashTable *source_outputs; + GHashTable *ext_streams; + GList *devices_list; + GList *streams_list; + GList *ext_streams_list; + MateMixerAppInfo *app_info; + gchar *server_address; + PulseConnection *connection; }; -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); +#define PULSE_CHANGE_STATE(p, s) \ + (_mate_mixer_backend_set_state (MATE_MIXER_BACKEND (p), (s))) +#define PULSE_GET_DEFAULT_SINK(p) \ + (mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (p))) +#define PULSE_GET_DEFAULT_SOURCE(p) \ + (mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (p))) +#define PULSE_SET_DEFAULT_SINK(p, s) \ + (_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (p), MATE_MIXER_STREAM (s))) +#define PULSE_SET_DEFAULT_SOURCE(p, s) \ + (_mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (p), MATE_MIXER_STREAM (s))) + +#define PULSE_GET_PENDING_SINK(p) \ + (g_object_get_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_sink")) \ + +#define PULSE_SET_PENDING_SINK(p,name) \ + (g_object_set_data_full (G_OBJECT (p), \ + "__matemixer_pulse_pending_sink", \ + g_strdup (name), \ + g_free)) + +#define PULSE_SET_PENDING_SINK_NULL(p) \ + (g_object_set_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_sink", \ + NULL)) + +#define PULSE_GET_PENDING_SOURCE(p) \ + (g_object_get_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_source")) \ + +#define PULSE_SET_PENDING_SOURCE(p,name) \ + (g_object_set_data_full (G_OBJECT (p), \ + "__matemixer_pulse_pending_source", \ + g_strdup (name), \ + g_free)) + +#define PULSE_SET_PENDING_SOURCE_NULL(p) \ + (g_object_set_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_source", \ + NULL)) + +#define PULSE_GET_HANGING(o) \ + ((gboolean) g_object_get_data (G_OBJECT (o), \ + "__matemixer_pulse_hanging")) + +#define PULSE_SET_HANGING(o) \ + (g_object_set_data (G_OBJECT (o), \ + "__matemixer_pulse_hanging", \ + GUINT_TO_POINTER (1))) + +#define PULSE_UNSET_HANGING(o) \ + (g_object_steal_data (G_OBJECT (o), \ + "__matemixer_pulse_hanging")) static void pulse_backend_class_init (PulseBackendClass *klass); static void pulse_backend_class_finalize (PulseBackendClass *klass); -static void pulse_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - static void pulse_backend_init (PulseBackend *pulse); static void pulse_backend_dispose (GObject *object); static void pulse_backend_finalize (GObject *object); -G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend, - G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, - mate_mixer_backend_interface_init)) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_DYNAMIC_TYPE (PulseBackend, pulse_backend, MATE_MIXER_TYPE_BACKEND) +#pragma clang diagnostic pop -static gboolean pulse_backend_open (MateMixerBackend *backend); -static void pulse_backend_close (MateMixerBackend *backend); +static gboolean pulse_backend_open (MateMixerBackend *backend); +static void pulse_backend_close (MateMixerBackend *backend); -static MateMixerState pulse_backend_get_state (MateMixerBackend *backend); +static void pulse_backend_set_app_info (MateMixerBackend *backend, + MateMixerAppInfo *info); -static void pulse_backend_set_data (MateMixerBackend *backend, - const MateMixerBackendData *data); +static void pulse_backend_set_server_address (MateMixerBackend *backend, + const gchar *address); -static GList * pulse_backend_list_devices (MateMixerBackend *backend); -static GList * pulse_backend_list_streams (MateMixerBackend *backend); -static GList * pulse_backend_list_cached_streams (MateMixerBackend *backend); +static const GList * pulse_backend_list_devices (MateMixerBackend *backend); +static const GList * pulse_backend_list_streams (MateMixerBackend *backend); +static const GList * pulse_backend_list_stored_controls (MateMixerBackend *backend); -static MateMixerStream *pulse_backend_get_default_input_stream (MateMixerBackend *backend); -static gboolean pulse_backend_set_default_input_stream (MateMixerBackend *backend, - MateMixerStream *stream); +static gboolean pulse_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); -static MateMixerStream *pulse_backend_get_default_output_stream (MateMixerBackend *backend); -static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend, - MateMixerStream *stream); +static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); static void on_connection_state_notify (PulseConnection *connection, GParamSpec *pspec, @@ -152,32 +189,16 @@ static void on_connection_ext_stream_info (PulseConnection PulseBackend *pulse); static gboolean connect_source_reconnect (PulseBackend *pulse); -static void connect_source_remove (PulseBackend *pulse); static void check_pending_sink (PulseBackend *pulse, PulseStream *stream); static void check_pending_source (PulseBackend *pulse, PulseStream *stream); -static void mark_hanging (PulseBackend *pulse); -static void mark_hanging_hash (GHashTable *hash); - -static void unmark_hanging (PulseBackend *pulse, - GObject *object); - -static void remove_hanging (PulseBackend *pulse); -static void remove_device (PulseBackend *pulse, - PulseDevice *device); -static void remove_stream (PulseBackend *pulse, - PulseStream *stream); - -static void change_state (PulseBackend *backend, - MateMixerState state); +static void free_list_devices (PulseBackend *pulse); +static void free_list_streams (PulseBackend *pulse); +static void free_list_ext_streams (PulseBackend *pulse); -static gint compare_devices (gconstpointer a, - gconstpointer b); -static gint compare_streams (gconstpointer a, - gconstpointer b); static gboolean compare_stream_names (gpointer key, gpointer value, gpointer user_data); @@ -195,78 +216,42 @@ backend_module_init (GTypeModule *module) info.backend_type = MATE_MIXER_BACKEND_PULSEAUDIO; } -const MateMixerBackendInfo * -backend_module_get_info (void) +const MateMixerBackendInfo *backend_module_get_info (void) { return &info; } static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ - iface->open = pulse_backend_open; - iface->close = pulse_backend_close; - iface->get_state = pulse_backend_get_state; - iface->set_data = pulse_backend_set_data; - iface->list_devices = pulse_backend_list_devices; - iface->list_streams = pulse_backend_list_streams; - iface->list_cached_streams = pulse_backend_list_cached_streams; - iface->get_default_input_stream = pulse_backend_get_default_input_stream; - iface->set_default_input_stream = pulse_backend_set_default_input_stream; - iface->get_default_output_stream = pulse_backend_get_default_output_stream; - iface->set_default_output_stream = pulse_backend_set_default_output_stream; -} - -static void pulse_backend_class_init (PulseBackendClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerBackendClass *backend_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = pulse_backend_dispose; - object_class->finalize = pulse_backend_finalize; - object_class->get_property = pulse_backend_get_property; - - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input"); - g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output"); + object_class->dispose = pulse_backend_dispose; + object_class->finalize = pulse_backend_finalize; + + backend_class = MATE_MIXER_BACKEND_CLASS (klass); + backend_class->set_app_info = pulse_backend_set_app_info; + backend_class->set_server_address = pulse_backend_set_server_address; + backend_class->open = pulse_backend_open; + backend_class->close = pulse_backend_close; + backend_class->list_devices = pulse_backend_list_devices; + backend_class->list_streams = pulse_backend_list_streams; + backend_class->list_stored_controls = pulse_backend_list_stored_controls; + backend_class->set_default_input_stream = pulse_backend_set_default_input_stream; + backend_class->set_default_output_stream = pulse_backend_set_default_output_stream; g_type_class_add_private (object_class, sizeof (PulseBackendPrivate)); } -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */ static void pulse_backend_class_finalize (PulseBackendClass *klass) { } static void -pulse_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - PulseBackend *pulse; - - pulse = PULSE_BACKEND (object); - - switch (param_id) { - case PROP_STATE: - g_value_set_enum (value, pulse->priv->state); - break; - case PROP_DEFAULT_INPUT: - g_value_set_object (value, pulse->priv->default_source); - break; - case PROP_DEFAULT_OUTPUT: - g_value_set_object (value, pulse->priv->default_sink); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void pulse_backend_init (PulseBackend *pulse) { pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE (pulse, @@ -279,22 +264,46 @@ pulse_backend_init (PulseBackend *pulse) g_direct_equal, NULL, g_object_unref); - pulse->priv->streams = - g_hash_table_new_full (g_int64_hash, - g_int64_equal, - g_free, + pulse->priv->sinks = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, g_object_unref); + pulse->priv->sources = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + pulse->priv->ext_streams = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + pulse->priv->sink_inputs = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + pulse->priv->source_outputs = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); } static void pulse_backend_dispose (GObject *object) { - pulse_backend_close (MATE_MIXER_BACKEND (object)); + MateMixerBackend *backend; + MateMixerState state; + + backend = MATE_MIXER_BACKEND (object); + + state = mate_mixer_backend_get_state (backend); + if (state != MATE_MIXER_STATE_IDLE) + pulse_backend_close (backend); G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object); } @@ -306,19 +315,24 @@ pulse_backend_finalize (GObject *object) pulse = PULSE_BACKEND (object); - g_free (pulse->priv->app_name); - g_free (pulse->priv->app_id); - g_free (pulse->priv->app_version); - g_free (pulse->priv->app_icon); - g_free (pulse->priv->server_address); + if (pulse->priv->app_info != NULL) + _mate_mixer_app_info_free (pulse->priv->app_info); - g_hash_table_destroy (pulse->priv->devices); - g_hash_table_destroy (pulse->priv->streams); - g_hash_table_destroy (pulse->priv->ext_streams); + g_hash_table_unref (pulse->priv->devices); + g_hash_table_unref (pulse->priv->sinks); + g_hash_table_unref (pulse->priv->sources); + g_hash_table_unref (pulse->priv->ext_streams); + g_hash_table_unref (pulse->priv->sink_inputs); + g_hash_table_unref (pulse->priv->source_outputs); G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object); } +#define PULSE_APP_NAME(p) (mate_mixer_app_info_get_name (p->priv->app_info)) +#define PULSE_APP_ID(p) (mate_mixer_app_info_get_id (p->priv->app_info)) +#define PULSE_APP_VERSION(p) (mate_mixer_app_info_get_version (p->priv->app_info)) +#define PULSE_APP_ICON(p) (mate_mixer_app_info_get_icon (p->priv->app_info)) + static gboolean pulse_backend_open (MateMixerBackend *backend) { @@ -329,22 +343,22 @@ pulse_backend_open (MateMixerBackend *backend) pulse = PULSE_BACKEND (backend); - if (G_UNLIKELY (pulse->priv->connection != NULL)) { + if G_UNLIKELY (pulse->priv->connection != NULL) { g_warn_if_reached (); return TRUE; } - connection = pulse_connection_new (pulse->priv->app_name, - pulse->priv->app_id, - pulse->priv->app_version, - pulse->priv->app_icon, + connection = pulse_connection_new (PULSE_APP_NAME (pulse), + PULSE_APP_ID (pulse), + PULSE_APP_VERSION (pulse), + PULSE_APP_ICON (pulse), pulse->priv->server_address); /* No connection attempt is made during the construction of the connection, * but it sets up the PulseAudio structures, which might fail in an * unlikely case */ - if (G_UNLIKELY (connection == NULL)) { - change_state (pulse, MATE_MIXER_STATE_FAILED); + if G_UNLIKELY (connection == NULL) { + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED); return FALSE; } @@ -409,16 +423,21 @@ pulse_backend_open (MateMixerBackend *backend) G_CALLBACK (on_connection_ext_stream_info), pulse); - change_state (pulse, MATE_MIXER_STATE_CONNECTING); + PULSE_CHANGE_STATE (backend, MATE_MIXER_STATE_CONNECTING); /* Connect to the PulseAudio server, this might fail either instantly or * asynchronously, for example when remote connection timeouts */ if (pulse_connection_connect (connection, FALSE) == FALSE) { g_object_unref (connection); - change_state (pulse, MATE_MIXER_STATE_FAILED); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED); return FALSE; } + _mate_mixer_backend_set_flags (backend, + MATE_MIXER_BACKEND_HAS_APPLICATION_CONTROLS | + MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM | + MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM); + pulse->priv->connection = connection; return TRUE; } @@ -432,7 +451,10 @@ pulse_backend_close (MateMixerBackend *backend) pulse = PULSE_BACKEND (backend); - connect_source_remove (pulse); + if (pulse->priv->connect_tag != 0) { + g_source_remove (pulse->priv->connect_tag); + pulse->priv->connect_tag = 0; + } if (pulse->priv->connection != NULL) { g_signal_handlers_disconnect_by_data (G_OBJECT (pulse->priv->connection), @@ -441,109 +463,100 @@ pulse_backend_close (MateMixerBackend *backend) g_clear_object (&pulse->priv->connection); } - g_clear_object (&pulse->priv->default_sink); - g_clear_object (&pulse->priv->default_source); - g_hash_table_remove_all (pulse->priv->devices); - g_hash_table_remove_all (pulse->priv->streams); + g_hash_table_remove_all (pulse->priv->sinks); + g_hash_table_remove_all (pulse->priv->sources); g_hash_table_remove_all (pulse->priv->ext_streams); pulse->priv->connected_once = FALSE; - change_state (pulse, MATE_MIXER_STATE_IDLE); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_IDLE); } -static MateMixerState -pulse_backend_get_state (MateMixerBackend *backend) +static void +pulse_backend_set_app_info (MateMixerBackend *backend, MateMixerAppInfo *info) { - g_return_val_if_fail (PULSE_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); + PulseBackend *pulse; + + g_return_if_fail (PULSE_IS_BACKEND (backend)); + g_return_if_fail (info != NULL); + + pulse = PULSE_BACKEND (backend); - return PULSE_BACKEND (backend)->priv->state; + if (pulse->priv->app_info != NULL) + _mate_mixer_app_info_free (pulse->priv->app_info); + + pulse->priv->app_info = _mate_mixer_app_info_copy (info); } static void -pulse_backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data) +pulse_backend_set_server_address (MateMixerBackend *backend, const gchar *address) { - PulseBackend *pulse; - g_return_if_fail (PULSE_IS_BACKEND (backend)); - g_return_if_fail (data != NULL); - pulse = PULSE_BACKEND (backend); + g_free (PULSE_BACKEND (backend)->priv->server_address); - g_free (pulse->priv->app_name); - g_free (pulse->priv->app_id); - g_free (pulse->priv->app_version); - g_free (pulse->priv->app_icon); - g_free (pulse->priv->server_address); - - pulse->priv->app_name = g_strdup (data->app_name); - pulse->priv->app_id = g_strdup (data->app_id); - pulse->priv->app_version = g_strdup (data->app_version); - pulse->priv->app_icon = g_strdup (data->app_icon); - pulse->priv->server_address = g_strdup (data->server_address); + PULSE_BACKEND (backend)->priv->server_address = g_strdup (address); } -static GList * +static const GList * pulse_backend_list_devices (MateMixerBackend *backend) { - GList *list; + PulseBackend *pulse; g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + pulse = PULSE_BACKEND (backend); - return g_list_sort (list, compare_devices); + if (pulse->priv->devices_list == NULL) { + pulse->priv->devices_list = g_hash_table_get_values (pulse->priv->devices); + if (pulse->priv->devices_list != NULL) + g_list_foreach (pulse->priv->devices_list, (GFunc) g_object_ref, NULL); } - return NULL; + return pulse->priv->devices_list; } -static GList * +static const GList * pulse_backend_list_streams (MateMixerBackend *backend) { - GList *list; + PulseBackend *pulse; g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->streams); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return g_list_sort (list, compare_streams); - } - return NULL; -} + pulse = PULSE_BACKEND (backend); -static GList * -pulse_backend_list_cached_streams (MateMixerBackend *backend) -{ - GList *list; + if (pulse->priv->streams_list == NULL) { + GList *sinks; + GList *sources; - g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + sinks = g_hash_table_get_values (pulse->priv->sinks); + if (sinks != NULL) + g_list_foreach (sinks, (GFunc) g_object_ref, NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->ext_streams); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + sources = g_hash_table_get_values (pulse->priv->sources); + if (sources != NULL) + g_list_foreach (sources, (GFunc) g_object_ref, NULL); - return g_list_sort (list, compare_streams); + pulse->priv->streams_list = g_list_concat (sinks, sources); } - return NULL; + return pulse->priv->streams_list; } -static MateMixerStream * -pulse_backend_get_default_input_stream (MateMixerBackend *backend) +static const GList * +pulse_backend_list_stored_controls (MateMixerBackend *backend) { + PulseBackend *pulse; + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - return PULSE_BACKEND (backend)->priv->default_source; + pulse = PULSE_BACKEND (backend); + + if (pulse->priv->ext_streams_list == NULL) { + pulse->priv->ext_streams_list = g_hash_table_get_values (pulse->priv->ext_streams); + if (pulse->priv->ext_streams_list != NULL) + g_list_foreach (pulse->priv->ext_streams_list, (GFunc) g_object_ref, NULL); + } + return pulse->priv->ext_streams_list; } static gboolean @@ -562,29 +575,13 @@ pulse_backend_set_default_input_stream (MateMixerBackend *backend, if (pulse_connection_set_default_source (pulse->priv->connection, name) == FALSE) return FALSE; - if (pulse->priv->default_source != NULL) - g_object_unref (pulse->priv->default_source); - - pulse->priv->default_source = g_object_ref (stream); - /* We might be in the process of setting a default source for which the details * are not yet known, make sure the change does not happen */ - g_object_set_data (G_OBJECT (pulse), - "backend-pending-source", - NULL); - - g_object_notify (G_OBJECT (pulse), "default-input"); + PULSE_SET_PENDING_SOURCE_NULL (pulse); + PULSE_SET_DEFAULT_SOURCE (pulse, stream); return TRUE; } -static MateMixerStream * -pulse_backend_get_default_output_stream (MateMixerBackend *backend) -{ - g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - - return PULSE_BACKEND (backend)->priv->default_sink; -} - static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *stream) @@ -601,18 +598,10 @@ pulse_backend_set_default_output_stream (MateMixerBackend *backend, if (pulse_connection_set_default_sink (pulse->priv->connection, name) == FALSE) return FALSE; - if (pulse->priv->default_sink != NULL) - g_object_unref (pulse->priv->default_sink); - - pulse->priv->default_sink = g_object_ref (stream); - /* We might be in the process of setting a default sink for which the details * are not yet known, make sure the change does not happen */ - g_object_set_data (G_OBJECT (pulse), - "backend-pending-sink", - NULL); - - g_object_notify (G_OBJECT (pulse), "default-output"); + PULSE_SET_PENDING_SINK_NULL (pulse); + PULSE_SET_DEFAULT_SINK (pulse, stream); return TRUE; } @@ -633,41 +622,41 @@ on_connection_state_notify (PulseConnection *connection, * Stream callbacks will unmark available streams and remaining * unavailable streams will be removed when the CONNECTED state * is reached. */ - mark_hanging (pulse); - change_state (pulse, MATE_MIXER_STATE_CONNECTING); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_CONNECTING); - if (pulse->priv->connect_source == NULL && - pulse_connection_connect (connection, TRUE) == FALSE) { - pulse->priv->connect_source = g_timeout_source_new (200); + if G_UNLIKELY (pulse->priv->connect_tag != 0) + break; - g_source_set_callback (pulse->priv->connect_source, + if (pulse_connection_connect (connection, TRUE) == FALSE) { + GSource *source; + + source = g_timeout_source_new (200); + g_source_set_callback (source, (GSourceFunc) connect_source_reconnect, pulse, - (GDestroyNotify) connect_source_remove); + NULL); + pulse->priv->connect_tag = + g_source_attach (source, g_main_context_get_thread_default ()); - g_source_attach (pulse->priv->connect_source, - g_main_context_get_thread_default ()); + g_source_unref (source); } break; } /* First connection attempt has failed */ - change_state (pulse, MATE_MIXER_STATE_FAILED); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED); break; case PULSE_CONNECTION_CONNECTING: case PULSE_CONNECTION_AUTHORIZING: case PULSE_CONNECTION_LOADING: - change_state (pulse, MATE_MIXER_STATE_CONNECTING); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_CONNECTING); break; case PULSE_CONNECTION_CONNECTED: - if (pulse->priv->connected_once == TRUE) - remove_hanging (pulse); - else - pulse->priv->connected_once = TRUE; + pulse->priv->connected_once = TRUE; - change_state (pulse, MATE_MIXER_STATE_READY); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_READY); break; } } @@ -677,18 +666,17 @@ on_connection_server_info (PulseConnection *connection, const pa_server_info *info, PulseBackend *pulse) { - const gchar *name_source = NULL; - const gchar *name_sink = NULL; + MateMixerStream *stream; + const gchar *name_source = NULL; + const gchar *name_sink = NULL; - if (pulse->priv->default_source != NULL) - name_source = mate_mixer_stream_get_name (pulse->priv->default_source); + stream = PULSE_GET_DEFAULT_SOURCE (pulse); + if (stream != NULL) + name_source = mate_mixer_stream_get_name (stream); if (g_strcmp0 (name_source, info->default_source_name) != 0) { - if (pulse->priv->default_source != NULL) - g_clear_object (&pulse->priv->default_source); - if (info->default_source_name != NULL) { - MateMixerStream *stream = g_hash_table_find (pulse->priv->streams, + MateMixerStream *stream = g_hash_table_find (pulse->priv->sources, compare_stream_names, (gpointer) info->default_source_name); @@ -698,20 +686,14 @@ on_connection_server_info (PulseConnection *connection, * When this happens, remember the name of the stream and wait for the * stream info callback. */ if (stream != NULL) { - pulse->priv->default_source = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-source", - NULL); - - g_debug ("Default input stream changed to %s", info->default_source_name); + PULSE_SET_DEFAULT_SOURCE (pulse, stream); + PULSE_SET_PENDING_SOURCE_NULL (pulse); } else { g_debug ("Default input stream changed to unknown stream %s", info->default_source_name); - g_object_set_data_full (G_OBJECT (pulse), - "backend-pending-source", - g_strdup (info->default_source_name), - g_free); + PULSE_SET_PENDING_SOURCE (pulse, info->default_source_name); + PULSE_SET_DEFAULT_SOURCE (pulse, NULL); /* In most cases (for example changing profile) the stream info * arrives by itself, but do not rely on it and request it explicitely. @@ -721,20 +703,16 @@ on_connection_server_info (PulseConnection *connection, info->default_source_name); } } else - g_debug ("Default input stream unset"); - - g_object_notify (G_OBJECT (pulse), "default-input"); + PULSE_SET_DEFAULT_SOURCE (pulse, NULL); } - if (pulse->priv->default_sink != NULL) - name_sink = mate_mixer_stream_get_name (pulse->priv->default_sink); + stream = PULSE_GET_DEFAULT_SINK (pulse); + if (stream != NULL) + name_sink = mate_mixer_stream_get_name (stream); if (g_strcmp0 (name_sink, info->default_sink_name) != 0) { - if (pulse->priv->default_sink != NULL) - g_clear_object (&pulse->priv->default_sink); - if (info->default_sink_name != NULL) { - MateMixerStream *stream = g_hash_table_find (pulse->priv->streams, + MateMixerStream *stream = g_hash_table_find (pulse->priv->sinks, compare_stream_names, (gpointer) info->default_sink_name); @@ -744,21 +722,14 @@ on_connection_server_info (PulseConnection *connection, * When this happens, remember the name of the stream and wait for the * stream info callback. */ if (stream != NULL) { - pulse->priv->default_sink = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-sink", - NULL); - - g_debug ("Default output stream changed to %s", info->default_sink_name); - + PULSE_SET_DEFAULT_SINK (pulse, stream); + PULSE_SET_PENDING_SINK_NULL (pulse); } else { g_debug ("Default output stream changed to unknown stream %s", info->default_sink_name); - g_object_set_data_full (G_OBJECT (pulse), - "backend-pending-sink", - g_strdup (info->default_sink_name), - g_free); + PULSE_SET_PENDING_SINK (pulse, info->default_sink_name); + PULSE_SET_DEFAULT_SINK (pulse, NULL); /* In most cases (for example changing profile) the stream info * arrives by itself, but do not rely on it and request it explicitely. @@ -768,12 +739,10 @@ on_connection_server_info (PulseConnection *connection, info->default_sink_name); } } else - g_debug ("Default output stream unset"); - - g_object_notify (G_OBJECT (pulse), "default-output"); + PULSE_SET_DEFAULT_SINK (pulse, NULL); } - if (pulse->priv->state != MATE_MIXER_STATE_READY) + if (mate_mixer_backend_get_state (MATE_MIXER_BACKEND (pulse)) != MATE_MIXER_STATE_READY) g_debug ("Sound server is %s version %s, running on %s", info->server_name, info->server_version, @@ -790,21 +759,17 @@ on_connection_card_info (PulseConnection *connection, device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->index)); if (device == NULL) { device = pulse_device_new (connection, info); - if (G_UNLIKELY (device == NULL)) - return; - g_hash_table_insert (pulse->priv->devices, GUINT_TO_POINTER (info->index), device); + g_hash_table_insert (pulse->priv->devices, + GUINT_TO_POINTER (info->index), + device); + free_list_devices (pulse); g_signal_emit_by_name (G_OBJECT (pulse), "device-added", mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - } else { + } else pulse_device_update (device, info); - - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (device)); - } } static void @@ -813,29 +778,22 @@ on_connection_card_removed (PulseConnection *connection, PulseBackend *pulse) { PulseDevice *device; + gchar *name; device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (index)); - if (G_UNLIKELY (device == NULL)) + if G_UNLIKELY (device == NULL) return; - remove_device (pulse, device); -} + name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); -/* PulseAudio uses 32-bit integers as indices for sinks, sink inputs, sources and - * source inputs, these indices are not unique among the different kinds of streams, - * but we want to keep all of them in a single hash table. Allow this by using 64-bit - * hash table keys, use the lower 32 bits for the PulseAudio index and some of the - * higher bits to indicate what kind of stream it is. */ -enum { - HASH_BIT_SINK = (1ULL << 63), - HASH_BIT_SINK_INPUT = (1ULL << 62), - HASH_BIT_SOURCE = (1ULL << 61), - HASH_BIT_SOURCE_OUTPUT = (1ULL << 60) -}; -#define HASH_ID_SINK(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK) -#define HASH_ID_SINK_INPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK_INPUT) -#define HASH_ID_SOURCE(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE) -#define HASH_ID_SOURCE_OUTPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE_OUTPUT) + g_hash_table_remove (pulse->priv->devices, GUINT_TO_POINTER (index)); + + free_list_devices (pulse); + g_signal_emit_by_name (G_OBJECT (pulse), + "device-removed", + name); + g_free (name); +} static void on_connection_sink_info (PulseConnection *connection, @@ -844,32 +802,36 @@ on_connection_sink_info (PulseConnection *connection, { PulseDevice *device = NULL; PulseStream *stream; - gint64 index = HASH_ID_SINK (info->index); if (info->card != PA_INVALID_INDEX) - device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card)); + device = g_hash_table_lookup (pulse->priv->devices, + GUINT_TO_POINTER (info->card)); - stream = g_hash_table_lookup (pulse->priv->streams, &index); + stream = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (info->index)); if (stream == NULL) { - stream = pulse_sink_new (connection, info, device); - if (G_UNLIKELY (stream == NULL)) - return; + stream = PULSE_STREAM (pulse_sink_new (connection, info, device)); - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + free_list_streams (pulse); + g_hash_table_insert (pulse->priv->sinks, + GUINT_TO_POINTER (info->index), + stream); - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + if (device != NULL) { + pulse_device_add_stream (device, stream); + } else { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + /* Only emit when not a part of the device, otherwise emitted by + * the main library */ + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + name); + } /* We might be waiting for this sink to set it as the default */ check_pending_sink (pulse, stream); - } else { - pulse_sink_update (stream, info, device); - - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + } else + pulse_sink_update (PULSE_SINK (stream), info); } static void @@ -878,13 +840,38 @@ on_connection_sink_removed (PulseConnection *connection, PulseBackend *pulse) { PulseStream *stream; - gint64 index = HASH_ID_SINK (idx); + PulseDevice *device; - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + stream = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (stream == NULL) return; - remove_stream (pulse, stream); + g_object_ref (stream); + + free_list_streams (pulse); + g_hash_table_remove (pulse->priv->sinks, GUINT_TO_POINTER (idx)); + + device = pulse_stream_get_device (stream); + if (device != NULL) { + pulse_device_remove_stream (device, stream); + } else { + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } + + /* The removed stream might be one of the default streams, this happens + * especially when switching profiles, after which PulseAudio removes the + * old streams and creates new ones with different names */ + if (MATE_MIXER_STREAM (stream) == PULSE_GET_DEFAULT_SINK (pulse)) { + PULSE_SET_DEFAULT_SINK (pulse, NULL); + + /* PulseAudio usually sends a server info update by itself when default + * stream changes, but there is at least one case when it does not - setting + * a card profile to off, so to be sure request an update explicitely */ + pulse_connection_load_server_info (pulse->priv->connection); + } + g_object_unref (stream); } static void @@ -892,40 +879,20 @@ on_connection_sink_input_info (PulseConnection *connection, const pa_sink_input_info *info, PulseBackend *pulse) { - PulseStream *stream; - PulseStream *parent = NULL; - gint64 index; + PulseSink *sink; - if (G_LIKELY (info->sink != PA_INVALID_INDEX)) { - index = HASH_ID_SINK (info->sink); - - parent = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (parent == NULL)) - g_debug ("Unknown parent %d of PulseAudio sink input %s", - info->sink, - info->name); - } - - index = HASH_ID_SINK_INPUT (info->index); - - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (stream == NULL) { - stream = pulse_sink_input_new (connection, info, parent); - if (G_UNLIKELY (stream == NULL)) - return; + if G_UNLIKELY (info->sink == PA_INVALID_INDEX) + return; - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + sink = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (info->sink)); + if G_UNLIKELY (sink == NULL) + return; - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); - } else { - pulse_sink_input_update (stream, info, parent); + pulse_sink_add_input (sink, info); - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + g_hash_table_insert (pulse->priv->sink_inputs, + GUINT_TO_POINTER (info->index), + g_object_ref (sink)); } static void @@ -933,14 +900,13 @@ on_connection_sink_input_removed (PulseConnection *connection, guint idx, PulseBackend *pulse) { - PulseStream *stream; - gint64 index = HASH_ID_SINK_INPUT (idx); + PulseSink *sink; - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + sink = g_hash_table_lookup (pulse->priv->sink_inputs, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (sink == NULL) return; - remove_stream (pulse, stream); + pulse_sink_remove_input (sink, idx); } static void @@ -950,36 +916,40 @@ on_connection_source_info (PulseConnection *connection, { PulseDevice *device = NULL; PulseStream *stream; - gint64 index = HASH_ID_SOURCE (info->index); /* Skip monitor streams */ if (info->monitor_of_sink != PA_INVALID_INDEX) return; if (info->card != PA_INVALID_INDEX) - device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card)); + device = g_hash_table_lookup (pulse->priv->devices, + GUINT_TO_POINTER (info->card)); - stream = g_hash_table_lookup (pulse->priv->streams, &index); + stream = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (info->index)); if (stream == NULL) { - stream = pulse_source_new (connection, info, device); - if (G_UNLIKELY (stream == NULL)) - return; + stream = PULSE_STREAM (pulse_source_new (connection, info, device)); - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + free_list_streams (pulse); + g_hash_table_insert (pulse->priv->sources, + GUINT_TO_POINTER (info->index), + stream); - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + if (device != NULL) { + pulse_device_add_stream (device, stream); + } else { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + /* Only emit when not a part of the device, otherwise emitted by + * the main library */ + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + name); + } /* We might be waiting for this source to set it as the default */ check_pending_source (pulse, stream); - } else { - pulse_source_update (stream, info, device); - - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + } else + pulse_source_update (PULSE_SOURCE (stream), info); } static void @@ -987,14 +957,39 @@ on_connection_source_removed (PulseConnection *connection, guint idx, PulseBackend *pulse) { + PulseDevice *device; PulseStream *stream; - gint64 index = HASH_ID_SOURCE (idx); - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + stream = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (stream == NULL) return; - remove_stream (pulse, stream); + g_object_ref (stream); + + free_list_streams (pulse); + g_hash_table_remove (pulse->priv->sources, GUINT_TO_POINTER (idx)); + + device = pulse_stream_get_device (stream); + if (device != NULL) { + pulse_device_remove_stream (device, stream); + } else { + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } + + /* The removed stream might be one of the default streams, this happens + * especially when switching profiles, after which PulseAudio removes the + * old streams and creates new ones with different names */ + if (MATE_MIXER_STREAM (stream) == PULSE_GET_DEFAULT_SOURCE (pulse)) { + PULSE_SET_DEFAULT_SOURCE (pulse, NULL); + + /* PulseAudio usually sends a server info update by itself when default + * stream changes, but there is at least one case when it does not - setting + * a card profile to off, so to be sure request an update explicitely */ + pulse_connection_load_server_info (pulse->priv->connection); + } + g_object_unref (stream); } static void @@ -1002,39 +997,20 @@ on_connection_source_output_info (PulseConnection *connection, const pa_source_output_info *info, PulseBackend *pulse) { - PulseStream *stream; - PulseStream *parent = NULL; - gint64 index; - - if (G_LIKELY (info->source != PA_INVALID_INDEX)) { - index = HASH_ID_SOURCE (info->source); - - /* Most likely a monitor source that we have skipped */ - parent = g_hash_table_lookup (pulse->priv->streams, &index); - if (parent == NULL) - return; - } + PulseSource *source; - index = HASH_ID_SOURCE_OUTPUT (info->index); - - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (stream == NULL) { - stream = pulse_source_output_new (connection, info, parent); - if (G_UNLIKELY (stream == NULL)) - return; + if G_UNLIKELY (info->source == PA_INVALID_INDEX) + return; - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + source = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (info->source)); + if G_UNLIKELY (source == NULL) + return; - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); - } else { - pulse_source_output_update (stream, info, parent); + pulse_source_add_output (source, info); - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + g_hash_table_insert (pulse->priv->source_outputs, + GUINT_TO_POINTER (info->index), + g_object_ref (source)); } static void @@ -1042,14 +1018,13 @@ on_connection_source_output_removed (PulseConnection *connection, guint idx, PulseBackend *pulse) { - PulseStream *stream; - gint64 index = HASH_ID_SOURCE_OUTPUT (idx); + PulseSource *source; - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + source = g_hash_table_lookup (pulse->priv->source_outputs, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (source == NULL) return; - remove_stream (pulse, stream); + pulse_source_remove_output (source, idx); } static void @@ -1057,63 +1032,75 @@ on_connection_ext_stream_info (PulseConnection *connection, const pa_ext_stream_restore_info *info, PulseBackend *pulse) { - PulseStream *stream; - PulseStream *parent = NULL; + PulseExtStream *ext; + PulseStream *parent = NULL; - if (G_LIKELY (info->device != NULL)) - parent = g_hash_table_find (pulse->priv->streams, compare_stream_names, + if (info->device != NULL) { + parent = g_hash_table_find (pulse->priv->sinks, compare_stream_names, (gpointer) info->device); - stream = g_hash_table_lookup (pulse->priv->ext_streams, info->name); - if (stream == NULL) { - stream = pulse_ext_stream_new (connection, info, parent); - if (G_UNLIKELY (stream == NULL)) - return; + if (parent == NULL) + parent = g_hash_table_find (pulse->priv->sources, compare_stream_names, + (gpointer) info->device); + } + + ext = g_hash_table_lookup (pulse->priv->ext_streams, info->name); + if (ext == NULL) { + ext = pulse_ext_stream_new (connection, info, parent); - g_hash_table_insert (pulse->priv->ext_streams, g_strdup (info->name), stream); + free_list_ext_streams (pulse); + g_hash_table_insert (pulse->priv->ext_streams, + g_strdup (info->name), + ext); g_signal_emit_by_name (G_OBJECT (pulse), - "cached-stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + "stored-control-added", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (ext))); } else { - pulse_ext_stream_update (stream, info, parent); + pulse_ext_stream_update (ext, info, parent); /* The object might be hanging if ext-streams are being loaded, remove * the hanging flag to prevent it from being removed */ - unmark_hanging (pulse, G_OBJECT (stream)); + PULSE_UNSET_HANGING (ext); } } static void on_connection_ext_stream_loading (PulseConnection *connection, PulseBackend *pulse) { - mark_hanging_hash (pulse->priv->ext_streams); + GHashTableIter iter; + gpointer ext; + + g_hash_table_iter_init (&iter, pulse->priv->ext_streams); + + while (g_hash_table_iter_next (&iter, NULL, &ext) == TRUE) + PULSE_SET_HANGING (ext); } static void on_connection_ext_stream_loaded (PulseConnection *connection, PulseBackend *pulse) { GHashTableIter iter; - gpointer value; + gpointer name; + gpointer ext; g_hash_table_iter_init (&iter, pulse->priv->ext_streams); - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) { - guint hanging = - GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging")); + while (g_hash_table_iter_next (&iter, &name, &ext) == TRUE) { + if (PULSE_GET_HANGING (ext) == FALSE) + continue; - if (hanging == 1) { - gchar *name = g_strdup ((const gchar *) value); + free_list_ext_streams (pulse); + g_hash_table_remove (pulse->priv->ext_streams, (gconstpointer) name); - g_hash_table_remove (pulse->priv->ext_streams, (gconstpointer) name); - g_signal_emit_by_name (G_OBJECT (pulse), - "cached-stream-removed", - name); - g_free (name); - } + g_signal_emit_by_name (G_OBJECT (pulse), + "stored-control-removed", + name); } + g_debug ("Ext-streams refreshed"); } +// XXX rename static gboolean connect_source_reconnect (PulseBackend *pulse) { @@ -1121,16 +1108,10 @@ connect_source_reconnect (PulseBackend *pulse) * and wait for the connection state notifications, otherwise this function * will be called again */ if (pulse_connection_connect (pulse->priv->connection, TRUE) == TRUE) { - connect_source_remove (pulse); - return FALSE; + pulse->priv->connect_tag = 0; + return G_SOURCE_REMOVE; } - return TRUE; -} - -static void -connect_source_remove (PulseBackend *pulse) -{ - g_clear_pointer (&pulse->priv->connect_source, g_source_unref); + return G_SOURCE_CONTINUE; } static void @@ -1141,7 +1122,7 @@ check_pending_sink (PulseBackend *pulse, PulseStream *stream) /* See if the currently added sream matches the default input stream * we are waiting for */ - pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-sink"); + pending = PULSE_GET_PENDING_SINK (pulse); if (pending == NULL) return; @@ -1149,14 +1130,10 @@ check_pending_sink (PulseBackend *pulse, PulseStream *stream) if (g_strcmp0 (pending, name) != 0) return; - pulse->priv->default_sink = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-sink", - NULL); + g_debug ("Setting default output stream to pending stream %s", name); - g_debug ("Default output stream changed to pending stream %s", name); - - g_object_notify (G_OBJECT (pulse), "default-output"); + PULSE_SET_PENDING_SINK_NULL (pulse); + PULSE_SET_DEFAULT_SINK (pulse, stream); } static void @@ -1167,7 +1144,7 @@ check_pending_source (PulseBackend *pulse, PulseStream *stream) /* See if the currently added sream matches the default input stream * we are waiting for */ - pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-source"); + pending = PULSE_GET_PENDING_SOURCE (pulse); if (pending == NULL) return; @@ -1175,160 +1152,43 @@ check_pending_source (PulseBackend *pulse, PulseStream *stream) if (g_strcmp0 (pending, name) != 0) return; - pulse->priv->default_source = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-source", - NULL); - - g_debug ("Default input stream changed to pending stream %s", name); + g_debug ("Setting default input stream to pending stream %s", name); - g_object_notify (G_OBJECT (pulse), "default-input"); + PULSE_SET_PENDING_SOURCE_NULL (pulse); + PULSE_SET_DEFAULT_SOURCE (pulse, stream); } static void -mark_hanging (PulseBackend *pulse) +free_list_devices (PulseBackend *pulse) { - /* Mark devices and streams as hanging, ext-streams are handled separately */ - mark_hanging_hash (pulse->priv->devices); - mark_hanging_hash (pulse->priv->streams); -} - -static void -mark_hanging_hash (GHashTable *hash) -{ - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, hash); - - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) - g_object_set_data (G_OBJECT (value), "backend-hanging", GUINT_TO_POINTER (1)); -} - -static void -unmark_hanging (PulseBackend *pulse, GObject *object) -{ - if (pulse->priv->connected_once == FALSE) - return; - if (pulse->priv->state == MATE_MIXER_STATE_READY) + if (pulse->priv->devices_list == NULL) return; - g_object_steal_data (object, "backend-hanging"); -} - -static void -remove_hanging (PulseBackend *pulse) -{ - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, pulse->priv->devices); - - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) { - guint hanging = - GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging")); - - if (hanging == 1) - remove_device (pulse, PULSE_DEVICE (value)); - } - - g_hash_table_iter_init (&iter, pulse->priv->streams); - - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) { - guint hanging = - GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging")); + g_list_free_full (pulse->priv->devices_list, g_object_unref); - if (hanging == 1) - remove_stream (pulse, PULSE_STREAM (value)); - } -} - -static void -remove_device (PulseBackend *pulse, PulseDevice *device) -{ - gchar *name; - - name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - - g_hash_table_remove (pulse->priv->devices, - GUINT_TO_POINTER (pulse_device_get_index (device))); - - g_signal_emit_by_name (G_OBJECT (pulse), "device-removed", name); - g_free (name); + pulse->priv->devices_list = NULL; } static void -remove_stream (PulseBackend *pulse, PulseStream *stream) +free_list_streams (PulseBackend *pulse) { - gchar *name; - guint32 idx; - gint64 index; - gboolean reload = FALSE; - - /* The removed stream might be one of the default streams, this happens - * especially when switching profiles, after which PulseAudio removes the - * old streams and creates new ones with different names */ - if (MATE_MIXER_STREAM (stream) == pulse->priv->default_sink) { - g_clear_object (&pulse->priv->default_sink); - - g_object_notify (G_OBJECT (pulse), "default-output"); - reload = TRUE; - } - else if (MATE_MIXER_STREAM (stream) == pulse->priv->default_source) { - g_clear_object (&pulse->priv->default_source); - - g_object_notify (G_OBJECT (pulse), "default-input"); - reload = TRUE; - } - - idx = pulse_stream_get_index (stream); - - if (PULSE_IS_SINK (stream)) - index = HASH_ID_SINK (idx); - else if (PULSE_IS_SINK_INPUT (stream)) - index = HASH_ID_SINK_INPUT (idx); - else if (PULSE_IS_SOURCE (stream)) - index = HASH_ID_SOURCE (idx); - else - index = HASH_ID_SOURCE_OUTPUT (idx); - - name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); - - g_hash_table_remove (pulse->priv->streams, &index); + if (pulse->priv->streams_list == NULL) + return; - /* PulseAudio usually sends a server info update by itself when default - * stream changes, but there is at least one case when it does not - setting - * a card profile to off, so to be sure request an update explicitely */ - if (reload == TRUE) - pulse_connection_load_server_info (pulse->priv->connection); + g_list_free_full (pulse->priv->streams_list, g_object_unref); - g_signal_emit_by_name (G_OBJECT (pulse), "stream-removed", name); - g_free (name); + pulse->priv->streams_list = NULL; } static void -change_state (PulseBackend *backend, MateMixerState state) +free_list_ext_streams (PulseBackend *pulse) { - if (backend->priv->state == state) + if (pulse->priv->ext_streams_list == NULL) return; - backend->priv->state = state; - - g_object_notify (G_OBJECT (backend), "state"); -} + g_list_free_full (pulse->priv->ext_streams_list, g_object_unref); -static gint -compare_devices (gconstpointer a, gconstpointer b) -{ - return strcmp (mate_mixer_device_get_name (MATE_MIXER_DEVICE (a)), - mate_mixer_device_get_name (MATE_MIXER_DEVICE (b))); -} - -static gint -compare_streams (gconstpointer a, gconstpointer b) -{ - return strcmp (mate_mixer_stream_get_name (MATE_MIXER_STREAM (a)), - mate_mixer_stream_get_name (MATE_MIXER_STREAM (b))); + pulse->priv->ext_streams_list = NULL; } static gboolean @@ -1336,5 +1196,5 @@ compare_stream_names (gpointer key, gpointer value, gpointer user_data) { MateMixerStream *stream = MATE_MIXER_STREAM (value); - return !strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data); + return strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data) == 0; } diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h index bd1face..2c4b8a8 100644 --- a/backends/pulse/pulse-backend.h +++ b/backends/pulse/pulse-backend.h @@ -20,8 +20,10 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> -#include <libmatemixer/matemixer-backend-module.h> +#include "pulse-types.h" #define PULSE_TYPE_BACKEND \ (pulse_backend_get_type ()) @@ -36,13 +38,12 @@ #define PULSE_BACKEND_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_BACKEND, PulseBackendClass)) -typedef struct _PulseBackend PulseBackend; typedef struct _PulseBackendClass PulseBackendClass; typedef struct _PulseBackendPrivate PulseBackendPrivate; struct _PulseBackend { - GObject parent; + MateMixerBackend parent; /*< private >*/ PulseBackendPrivate *priv; @@ -50,7 +51,7 @@ struct _PulseBackend struct _PulseBackendClass { - GObjectClass parent_class; + MateMixerBackendClass parent_class; }; GType pulse_backend_get_type (void) G_GNUC_CONST; diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c deleted file mode 100644 index b99c498..0000000 --- a/backends/pulse/pulse-client-stream.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <string.h> -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> - -#include <pulse/pulseaudio.h> - -#include "pulse-client-stream.h" -#include "pulse-sink.h" -#include "pulse-source.h" -#include "pulse-stream.h" - -struct _PulseClientStreamPrivate -{ - gchar *app_name; - gchar *app_id; - gchar *app_version; - gchar *app_icon; - MateMixerStream *parent; - MateMixerClientStreamFlags flags; - MateMixerClientStreamRole role; -}; - -enum { - PROP_0, - PROP_CLIENT_FLAGS, - PROP_ROLE, - PROP_PARENT, - PROP_APP_NAME, - PROP_APP_ID, - PROP_APP_VERSION, - PROP_APP_ICON -}; - -enum { - REMOVED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0, }; - -static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface); - -static void pulse_client_stream_class_init (PulseClientStreamClass *klass); - -static void pulse_client_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - -static void pulse_client_stream_init (PulseClientStream *client); -static void pulse_client_stream_dispose (GObject *object); -static void pulse_client_stream_finalize (GObject *object); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseClientStream, pulse_client_stream, PULSE_TYPE_STREAM, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_CLIENT_STREAM, - mate_mixer_client_stream_interface_init)) - -static MateMixerClientStreamFlags pulse_client_stream_get_flags (MateMixerClientStream *client); -static MateMixerClientStreamRole pulse_client_stream_get_role (MateMixerClientStream *client); - -static MateMixerStream * pulse_client_stream_get_parent (MateMixerClientStream *client); -static gboolean pulse_client_stream_set_parent (MateMixerClientStream *client, - MateMixerStream *parent); - -static gboolean pulse_client_stream_remove (MateMixerClientStream *client); - -static const gchar * pulse_client_stream_get_app_name (MateMixerClientStream *client); -static const gchar * pulse_client_stream_get_app_id (MateMixerClientStream *client); -static const gchar * pulse_client_stream_get_app_version (MateMixerClientStream *client); -static const gchar * pulse_client_stream_get_app_icon (MateMixerClientStream *client); - -static void -mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface) -{ - iface->get_flags = pulse_client_stream_get_flags; - iface->get_role = pulse_client_stream_get_role; - iface->get_parent = pulse_client_stream_get_parent; - iface->set_parent = pulse_client_stream_set_parent; - iface->remove = pulse_client_stream_remove; - iface->get_app_name = pulse_client_stream_get_app_name; - iface->get_app_id = pulse_client_stream_get_app_id; - iface->get_app_version = pulse_client_stream_get_app_version; - iface->get_app_icon = pulse_client_stream_get_app_icon; -} - -static void -pulse_client_stream_class_init (PulseClientStreamClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = pulse_client_stream_dispose; - object_class->finalize = pulse_client_stream_finalize; - object_class->get_property = pulse_client_stream_get_property; - - signals[REMOVED] = - g_signal_new ("removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseClientStreamClass, removed), - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0, - G_TYPE_NONE); - - g_object_class_override_property (object_class, PROP_CLIENT_FLAGS, "client-flags"); - g_object_class_override_property (object_class, PROP_ROLE, "role"); - g_object_class_override_property (object_class, PROP_PARENT, "parent"); - g_object_class_override_property (object_class, PROP_APP_NAME, "app-name"); - g_object_class_override_property (object_class, PROP_APP_ID, "app-id"); - g_object_class_override_property (object_class, PROP_APP_VERSION, "app-version"); - g_object_class_override_property (object_class, PROP_APP_ICON, "app-icon"); - - g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate)); -} - -static void -pulse_client_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - PulseClientStream *client; - - client = PULSE_CLIENT_STREAM (object); - - switch (param_id) { - case PROP_CLIENT_FLAGS: - g_value_set_flags (value, client->priv->flags); - break; - case PROP_ROLE: - g_value_set_enum (value, client->priv->role); - break; - case PROP_PARENT: - g_value_set_object (value, client->priv->parent); - break; - case PROP_APP_NAME: - g_value_set_string (value, client->priv->app_name); - break; - case PROP_APP_ID: - g_value_set_string (value, client->priv->app_id); - break; - case PROP_APP_VERSION: - g_value_set_string (value, client->priv->app_version); - break; - case PROP_APP_ICON: - g_value_set_string (value, client->priv->app_icon); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -pulse_client_stream_init (PulseClientStream *client) -{ - client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, - PULSE_TYPE_CLIENT_STREAM, - PulseClientStreamPrivate); -} - -static void -pulse_client_stream_dispose (GObject *object) -{ - PulseClientStream *client; - - client = PULSE_CLIENT_STREAM (object); - - g_clear_object (&client->priv->parent); - - G_OBJECT_CLASS (pulse_client_stream_parent_class)->dispose (object); -} - -static void -pulse_client_stream_finalize (GObject *object) -{ - PulseClientStream *client; - - client = PULSE_CLIENT_STREAM (object); - - g_free (client->priv->app_name); - g_free (client->priv->app_id); - g_free (client->priv->app_version); - g_free (client->priv->app_icon); - - G_OBJECT_CLASS (pulse_client_stream_parent_class)->finalize (object); -} - -gboolean -pulse_client_stream_update_flags (PulseClientStream *pclient, - MateMixerClientStreamFlags flags) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (pclient->priv->flags != flags) { - pclient->priv->flags = flags; - - g_object_notify (G_OBJECT (pclient), "client-flags"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_parent (PulseClientStream *pclient, MateMixerStream *parent) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (pclient->priv->parent != parent) { - g_clear_object (&pclient->priv->parent); - - if (G_LIKELY (parent != NULL)) - pclient->priv->parent = g_object_ref (parent); - - g_object_notify (G_OBJECT (pclient), "parent"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_role (PulseClientStream *pclient, - MateMixerClientStreamRole role) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (pclient->priv->role != role) { - pclient->priv->role = role; - - g_object_notify (G_OBJECT (pclient), "role"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_name (PulseClientStream *pclient, const gchar *app_name) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_name, app_name) != 0) { - g_free (pclient->priv->app_name); - pclient->priv->app_name = g_strdup (app_name); - - g_object_notify (G_OBJECT (pclient), "app-name"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_id (PulseClientStream *pclient, const gchar *app_id) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_id, app_id) != 0) { - g_free (pclient->priv->app_id); - pclient->priv->app_id = g_strdup (app_id); - - g_object_notify (G_OBJECT (pclient), "app-id"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_version (PulseClientStream *pclient, const gchar *app_version) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_version, app_version) != 0) { - g_free (pclient->priv->app_version); - pclient->priv->app_version = g_strdup (app_version); - - g_object_notify (G_OBJECT (pclient), "app-version"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_icon (PulseClientStream *pclient, const gchar *app_icon) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_icon, app_icon) != 0) { - g_free (pclient->priv->app_icon); - pclient->priv->app_icon = g_strdup (app_icon); - - g_object_notify (G_OBJECT (pclient), "app-icon"); - } - return TRUE; -} - -static MateMixerClientStreamFlags -pulse_client_stream_get_flags (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_NO_FLAGS); - - return PULSE_CLIENT_STREAM (client)->priv->flags; -} - -static MateMixerClientStreamRole -pulse_client_stream_get_role (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_ROLE_NONE); - - return PULSE_CLIENT_STREAM (client)->priv->role; -} - -static MateMixerStream * -pulse_client_stream_get_parent (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->parent; -} - -static gboolean -pulse_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent) -{ - MateMixerStreamFlags flags; - PulseClientStream *pclient; - PulseClientStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); - g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE); - - pclient = PULSE_CLIENT_STREAM (client); - klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient); - - if (pclient->priv->parent == parent) - return TRUE; - - flags = mate_mixer_stream_get_flags (MATE_MIXER_STREAM (pclient)); - - /* Validate the parent stream */ - if (flags & MATE_MIXER_STREAM_INPUT && !PULSE_IS_SOURCE (parent)) { - g_warning ("Could not change stream parent to %s: not a parent input stream", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent))); - return FALSE; - } else if (flags & MATE_MIXER_STREAM_OUTPUT && !PULSE_IS_SINK (parent)) { - g_warning ("Could not change stream parent to %s: not a parent output stream", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent))); - return FALSE; - } - - /* Set the parent */ - if (klass->set_parent (pclient, PULSE_STREAM (parent)) == FALSE) - return FALSE; - - if (pclient->priv->parent != NULL) - g_object_unref (pclient->priv->parent); - - /* It is allowed for the parent to be NULL when the instance is created, but - * changing the parent requires a valid parent stream */ - pclient->priv->parent = g_object_ref (parent); - - g_object_notify (G_OBJECT (client), "parent"); - return TRUE; -} - -static gboolean -pulse_client_stream_remove (MateMixerClientStream *client) -{ - PulseClientStream *pclient; - PulseClientStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); - - pclient = PULSE_CLIENT_STREAM (client); - klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient); - - if (klass->remove (pclient) == FALSE) - return FALSE; - - // XXX handle this in the backend - g_signal_emit (G_OBJECT (client), - signals[REMOVED], - 0); - - return TRUE; -} - -static const gchar * -pulse_client_stream_get_app_name (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_name; -} - -static const gchar * -pulse_client_stream_get_app_id (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_id; -} - -static const gchar * -pulse_client_stream_get_app_version (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_version; -} - -static const gchar * -pulse_client_stream_get_app_icon (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_icon; -} diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h deleted file mode 100644 index fe24dc3..0000000 --- a/backends/pulse/pulse-client-stream.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef PULSE_CLIENT_STREAM_H -#define PULSE_CLIENT_STREAM_H - -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> - -#include "pulse-stream.h" - -G_BEGIN_DECLS - -#define PULSE_TYPE_CLIENT_STREAM \ - (pulse_client_stream_get_type ()) -#define PULSE_CLIENT_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStream)) -#define PULSE_IS_CLIENT_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_CLIENT_STREAM)) -#define PULSE_CLIENT_STREAM_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) -#define PULSE_IS_CLIENT_STREAM_CLASS(k) \ - (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM)) -#define PULSE_CLIENT_STREAM_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) - -typedef struct _PulseClientStream PulseClientStream; -typedef struct _PulseClientStreamClass PulseClientStreamClass; -typedef struct _PulseClientStreamPrivate PulseClientStreamPrivate; - -struct _PulseClientStream -{ - PulseStream parent; - - /*< private >*/ - PulseClientStreamPrivate *priv; -}; - -struct _PulseClientStreamClass -{ - PulseStreamClass parent_class; - - /*< private >*/ - /* Virtual table */ - gboolean (*set_parent) (PulseClientStream *pclient, - PulseStream *pstream); - - gboolean (*remove) (PulseClientStream *pclient); - - /* Signals */ - void (*removed) (PulseClientStream *pclient); -}; - -GType pulse_client_stream_get_type (void) G_GNUC_CONST; - -gboolean pulse_client_stream_update_flags (PulseClientStream *pclient, - MateMixerClientStreamFlags flags); - -gboolean pulse_client_stream_update_role (PulseClientStream *pclient, - MateMixerClientStreamRole role); - -gboolean pulse_client_stream_update_parent (PulseClientStream *pclient, - MateMixerStream *parent); - -gboolean pulse_client_stream_update_app_name (PulseClientStream *pclient, - const gchar *app_name); -gboolean pulse_client_stream_update_app_id (PulseClientStream *pclient, - const gchar *app_id); -gboolean pulse_client_stream_update_app_version (PulseClientStream *pclient, - const gchar *app_version); -gboolean pulse_client_stream_update_app_icon (PulseClientStream *pclient, - const gchar *app_icon); - -G_END_DECLS - -#endif /* PULSE_CLIENT_STREAM_H */ diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c index cc39caf..7344d2e 100644 --- a/backends/pulse/pulse-connection.c +++ b/backends/pulse/pulse-connection.c @@ -31,14 +31,14 @@ struct _PulseConnectionPrivate { - gchar *server; - guint outstanding; - pa_context *context; - pa_proplist *proplist; - pa_glib_mainloop *mainloop; - gboolean ext_streams_loading; - gboolean ext_streams_dirty; - PulseConnectionState state; + gchar *server; + guint outstanding; + pa_context *context; + pa_proplist *proplist; + pa_glib_mainloop *mainloop; + gboolean ext_streams_loading; + gboolean ext_streams_dirty; + PulseConnectionState state; }; enum { @@ -417,7 +417,7 @@ pulse_connection_new (const gchar *app_name, PulseConnection *connection; mainloop = pa_glib_mainloop_new (g_main_context_get_thread_default ()); - if (G_UNLIKELY (mainloop == NULL)) { + if G_UNLIKELY (mainloop == NULL) { g_warning ("Failed to create PulseAudio main loop"); return NULL; } @@ -468,7 +468,7 @@ pulse_connection_connect (PulseConnection *connection, gboolean wait_for_daemon) context = pa_context_new_with_proplist (mainloop, NULL, connection->priv->proplist); - if (G_UNLIKELY (context == NULL)) { + if G_UNLIKELY (context == NULL) { g_warning ("Failed to create PulseAudio context"); return FALSE; } @@ -774,7 +774,6 @@ pulse_connection_create_monitor (PulseConnection *connection, return pulse_monitor_new (connection->priv->context, connection->priv->proplist, - NULL, index_source, index_sink_input); } @@ -1227,7 +1226,7 @@ load_lists (PulseConnection *connection) GSList *ops = NULL; pa_operation *op; - if (G_UNLIKELY (connection->priv->outstanding > 0)) { + if G_UNLIKELY (connection->priv->outstanding > 0) { g_warn_if_reached (); return FALSE; } @@ -1235,7 +1234,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_card_info_list (connection->priv->context, pulse_card_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1243,7 +1242,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_sink_info_list (connection->priv->context, pulse_sink_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1251,7 +1250,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_sink_input_info_list (connection->priv->context, pulse_sink_input_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1259,7 +1258,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_source_info_list (connection->priv->context, pulse_source_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1267,7 +1266,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_source_output_info_list (connection->priv->context, pulse_source_output_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1303,7 +1302,7 @@ load_list_finished (PulseConnection *connection) * as the final step in the connection process */ connection->priv->outstanding--; - if (G_UNLIKELY (connection->priv->outstanding < 0)) { + if G_UNLIKELY (connection->priv->outstanding < 0) { g_warn_if_reached (); connection->priv->outstanding = 0; } @@ -1311,7 +1310,7 @@ load_list_finished (PulseConnection *connection) if (connection->priv->outstanding == 0) { gboolean ret = pulse_connection_load_server_info (connection); - if (G_UNLIKELY (ret == FALSE)) { + if G_UNLIKELY (ret == FALSE) { pulse_connection_disconnect (connection); return FALSE; } @@ -1640,7 +1639,7 @@ change_state (PulseConnection *connection, PulseConnectionState state) static gboolean process_pulse_operation (PulseConnection *connection, pa_operation *op) { - if (G_UNLIKELY (op == NULL)) { + if G_UNLIKELY (op == NULL) { g_warning ("PulseAudio operation failed: %s", pa_strerror (pa_context_errno (connection->priv->context))); return FALSE; diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h index b9119fd..7dc0bfb 100644 --- a/backends/pulse/pulse-connection.h +++ b/backends/pulse/pulse-connection.h @@ -25,7 +25,7 @@ #include <pulse/ext-stream-restore.h> #include "pulse-enums.h" -#include "pulse-monitor.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,7 +42,6 @@ G_BEGIN_DECLS #define PULSE_CONNECTION_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CONNECTION, PulseConnectionClass)) -typedef struct _PulseConnection PulseConnection; typedef struct _PulseConnectionClass PulseConnectionClass; typedef struct _PulseConnectionPrivate PulseConnectionPrivate; @@ -59,7 +58,6 @@ struct _PulseConnectionClass GObjectClass parent_class; /*< private >*/ - /* Signals */ void (*server_info) (PulseConnection *connection, const pa_server_info *info); @@ -201,7 +199,6 @@ gboolean pulse_connection_kill_source_output (PulseConnection gboolean pulse_connection_write_ext_stream (PulseConnection *connection, const pa_ext_stream_restore_info *info); - gboolean pulse_connection_delete_ext_stream (PulseConnection *connection, const gchar *name); diff --git a/backends/pulse/pulse-device-profile.c b/backends/pulse/pulse-device-profile.c new file mode 100644 index 0000000..5487841 --- /dev/null +++ b/backends/pulse/pulse-device-profile.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-device.h" +#include "pulse-device-profile.h" + +struct _PulseDeviceProfilePrivate +{ + guint priority; +}; + +static void pulse_device_profile_class_init (PulseDeviceProfileClass *klass); +static void pulse_device_profile_init (PulseDeviceProfile *profile); + +G_DEFINE_TYPE (PulseDeviceProfile, pulse_device_profile, MATE_MIXER_TYPE_SWITCH_OPTION) + +static void +pulse_device_profile_class_init (PulseDeviceProfileClass *klass) +{ + g_type_class_add_private (klass, sizeof (PulseDeviceProfilePrivate)); +} + +static void +pulse_device_profile_init (PulseDeviceProfile *profile) +{ + profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (profile, + PULSE_TYPE_DEVICE_PROFILE, + PulseDeviceProfilePrivate); +} + +PulseDeviceProfile * +pulse_device_profile_new (const gchar *name, + const gchar *label, + guint priority) +{ + PulseDeviceProfile *profile; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + + profile = g_object_new (PULSE_TYPE_DEVICE_PROFILE, + "name", name, + "label", label, + NULL); + + profile->priv->priority = priority; + return profile; +} + +const gchar * +pulse_device_profile_get_name (PulseDeviceProfile *profile) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (profile), NULL); + + return mate_mixer_switch_option_get_name (MATE_MIXER_SWITCH_OPTION (profile)); +} + +guint +pulse_device_profile_get_priority (PulseDeviceProfile *profile) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (profile), 0); + + return profile->priv->priority; +} diff --git a/backends/pulse/pulse-device-profile.h b/backends/pulse/pulse-device-profile.h new file mode 100644 index 0000000..0a9c3f4 --- /dev/null +++ b/backends/pulse/pulse-device-profile.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_DEVICE_PROFILE_H +#define PULSE_DEVICE_PROFILE_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_DEVICE_PROFILE \ + (pulse_device_profile_get_type ()) +#define PULSE_DEVICE_PROFILE(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE_PROFILE, PulseDeviceProfile)) +#define PULSE_IS_DEVICE_PROFILE(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE_PROFILE)) +#define PULSE_DEVICE_PROFILE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE_PROFILE, PulseDeviceProfileClass)) +#define PULSE_IS_DEVICE_PROFILE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE_PROFILE)) +#define PULSE_DEVICE_PROFILE_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE_PROFILE, PulseDeviceProfileClass)) + +typedef struct _PulseDeviceProfileClass PulseDeviceProfileClass; +typedef struct _PulseDeviceProfilePrivate PulseDeviceProfilePrivate; + +struct _PulseDeviceProfile +{ + MateMixerSwitchOption parent; + + /*< private >*/ + PulseDeviceProfilePrivate *priv; +}; + +struct _PulseDeviceProfileClass +{ + MateMixerSwitchOptionClass parent; +}; + +GType pulse_device_profile_get_type (void) G_GNUC_CONST; + +PulseDeviceProfile *pulse_device_profile_new (const gchar *name, + const gchar *label, + guint priority); + +const gchar * pulse_device_profile_get_name (PulseDeviceProfile *profile); +guint pulse_device_profile_get_priority (PulseDeviceProfile *profile); + +G_END_DECLS + +#endif /* PULSE_DEVICE_PROFILE_H */ diff --git a/backends/pulse/pulse-device-switch.c b/backends/pulse/pulse-device-switch.c new file mode 100644 index 0000000..7a43d0a --- /dev/null +++ b/backends/pulse/pulse-device-switch.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-device.h" +#include "pulse-device-profile.h" +#include "pulse-device-switch.h" + +struct _PulseDeviceSwitchPrivate +{ + GList *profiles; + PulseDevice *device; +}; + +enum { + PROP_0, + PROP_DEVICE, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void pulse_device_switch_class_init (PulseDeviceSwitchClass *klass); + +static void pulse_device_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_device_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void pulse_device_switch_init (PulseDeviceSwitch *swtch); +static void pulse_device_switch_dispose (GObject *object); + +G_DEFINE_TYPE (PulseDeviceSwitch, pulse_device_switch, MATE_MIXER_TYPE_SWITCH) + +static gboolean pulse_device_switch_set_active_option (MateMixerSwitch *mms, + MateMixerSwitchOption *mmso); + +static const GList *pulse_device_switch_list_options (MateMixerSwitch *mms); + +static gint compare_profiles (gconstpointer a, + gconstpointer b); +static gint compare_profile_name (gconstpointer a, + gconstpointer b); + +static void +pulse_device_switch_class_init (PulseDeviceSwitchClass *klass) +{ + GObjectClass *object_class; + MateMixerSwitchClass *switch_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_device_switch_dispose; + object_class->get_property = pulse_device_switch_get_property; + object_class->set_property = pulse_device_switch_set_property; + + switch_class = MATE_MIXER_SWITCH_CLASS (klass); + switch_class->set_active_option = pulse_device_switch_set_active_option; + switch_class->list_options = pulse_device_switch_list_options; + + properties[PROP_DEVICE] = + g_param_spec_object ("device", + "Device", + "PulseAudio device", + PULSE_TYPE_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (PulseDeviceSwitchPrivate)); +} + +static void +pulse_device_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulseDeviceSwitch *swtch; + + swtch = PULSE_DEVICE_SWITCH (object); + + switch (param_id) { + case PROP_DEVICE: + g_value_set_object (value, swtch->priv->device); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_device_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulseDeviceSwitch *swtch; + + swtch = PULSE_DEVICE_SWITCH (object); + + switch (param_id) { + case PROP_DEVICE: + /* Construct-only object */ + swtch->priv->device = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_device_switch_init (PulseDeviceSwitch *swtch) +{ + swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, + PULSE_TYPE_DEVICE_SWITCH, + PulseDeviceSwitchPrivate); +} + +static void +pulse_device_switch_dispose (GObject *object) +{ + PulseDeviceSwitch *swtch; + + swtch = PULSE_DEVICE_SWITCH (object); + + g_clear_object (&swtch->priv->device); + + G_OBJECT_CLASS (pulse_device_switch_parent_class)->dispose (object); +} + +PulseDeviceSwitch * +pulse_device_switch_new (const gchar *name, const gchar *label, PulseDevice *device) +{ + return g_object_new (PULSE_TYPE_DEVICE_SWITCH, + "name", name, + "label", label, + "role", MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE, + "device", device, + NULL); +} + +PulseDevice * +pulse_device_switch_get_device (PulseDeviceSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (swtch), NULL); + + return swtch->priv->device; +} + +void +pulse_device_switch_add_profile (PulseDeviceSwitch *swtch, PulseDeviceProfile *profile) +{ + g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_DEVICE_PROFILE (profile)); + + swtch->priv->profiles = g_list_insert_sorted (swtch->priv->profiles, + profile, + compare_profiles); +} + +void +pulse_device_switch_set_active_profile (PulseDeviceSwitch *swtch, + PulseDeviceProfile *profile) +{ + g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_DEVICE_PROFILE (profile)); + + _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), + MATE_MIXER_SWITCH_OPTION (profile)); +} + +void +pulse_device_switch_set_active_profile_by_name (PulseDeviceSwitch *swtch, const gchar *name) +{ + GList *item; + + g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch)); + g_return_if_fail (name != NULL); + + item = g_list_find_custom (swtch->priv->profiles, name, compare_profile_name); + if G_UNLIKELY (item == NULL) { + g_debug ("Invalid device switch profile name %s", name); + return; + } + pulse_device_switch_set_active_profile (swtch, PULSE_DEVICE_PROFILE (item->data)); +} + +static gboolean +pulse_device_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso) +{ + PulseDevice *device; + const gchar *device_name; + const gchar *profile_name; + + g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (mms), FALSE); + g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (mmso), FALSE); + + device = pulse_device_switch_get_device (PULSE_DEVICE_SWITCH (mms)); + if G_UNLIKELY (device == NULL) + return FALSE; + + device_name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + profile_name = mate_mixer_switch_option_get_name (mmso); + + return pulse_connection_set_card_profile (pulse_device_get_connection (device), + device_name, + profile_name); +} + +static const GList * +pulse_device_switch_list_options (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (swtch), NULL); + + return PULSE_DEVICE_SWITCH (swtch)->priv->profiles; +} + +static gint +compare_profiles (gconstpointer a, gconstpointer b) +{ + return pulse_device_profile_get_priority (PULSE_DEVICE_PROFILE (b)) - + pulse_device_profile_get_priority (PULSE_DEVICE_PROFILE (a)); +} + +static gint +compare_profile_name (gconstpointer a, gconstpointer b) +{ + PulseDeviceProfile *profile = PULSE_DEVICE_PROFILE (a); + const gchar *name = (const gchar *) b; + + return strcmp (pulse_device_profile_get_name (profile), name); +} diff --git a/backends/pulse/pulse-device-switch.h b/backends/pulse/pulse-device-switch.h new file mode 100644 index 0000000..50f4a68 --- /dev/null +++ b/backends/pulse/pulse-device-switch.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_DEVICE_SWITCH_H +#define PULSE_DEVICE_SWITCH_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_DEVICE_SWITCH \ + (pulse_device_switch_get_type ()) +#define PULSE_DEVICE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitch)) +#define PULSE_IS_DEVICE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE_SWITCH)) +#define PULSE_DEVICE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitchClass)) +#define PULSE_IS_DEVICE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE_SWITCH)) +#define PULSE_DEVICE_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitchClass)) + +typedef struct _PulseDeviceSwitchClass PulseDeviceSwitchClass; +typedef struct _PulseDeviceSwitchPrivate PulseDeviceSwitchPrivate; + +struct _PulseDeviceSwitch +{ + MateMixerSwitch parent; + + /*< private >*/ + PulseDeviceSwitchPrivate *priv; +}; + +struct _PulseDeviceSwitchClass +{ + MateMixerSwitchClass parent_class; +}; + +GType pulse_device_switch_get_type (void) G_GNUC_CONST; + +PulseDeviceSwitch *pulse_device_switch_new (const gchar *name, + const gchar *label, + PulseDevice *device); + +PulseDevice * pulse_device_switch_get_device (PulseDeviceSwitch *swtch); + +void pulse_device_switch_add_profile (PulseDeviceSwitch *swtch, + PulseDeviceProfile *profile); + +void pulse_device_switch_set_active_profile (PulseDeviceSwitch *swtch, + PulseDeviceProfile *profile); + +void pulse_device_switch_set_active_profile_by_name (PulseDeviceSwitch *swtch, + const gchar *name); + +G_END_DECLS + +#endif /* PULSE_DEVICE_SWITCH_H */ diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c index 96e06c8..5403712 100644 --- a/backends/pulse/pulse-device.c +++ b/backends/pulse/pulse-device.c @@ -17,45 +17,40 @@ #include <string.h> #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-device-profile.h> -#include <libmatemixer/matemixer-device-profile-private.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" +#include "pulse-device-profile.h" +#include "pulse-device-switch.h" +#include "pulse-port.h" +#include "pulse-stream.h" struct _PulseDevicePrivate { - guint32 index; - gchar *name; - gchar *description; - gchar *icon; - GHashTable *ports; - GList *ports_list; - GHashTable *profiles; - GList *profiles_list; - PulseConnection *connection; - MateMixerDeviceProfile *profile; + guint32 index; + GHashTable *ports; + GHashTable *streams; + GList *streams_list; + GList *switches_list; + PulseConnection *connection; + PulseDeviceSwitch *pswitch; }; enum { PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_ICON, - PROP_ACTIVE_PROFILE, PROP_INDEX, - PROP_CONNECTION + PROP_CONNECTION, + N_PROPERTIES }; -static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void pulse_device_class_init (PulseDeviceClass *klass); @@ -72,60 +67,25 @@ static void pulse_device_init (PulseDevice *device); static void pulse_device_dispose (GObject *object); static void pulse_device_finalize (GObject *object); -G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, - mate_mixer_device_interface_init)) +G_DEFINE_TYPE (PulseDevice, pulse_device, MATE_MIXER_TYPE_DEVICE) -#if PA_CHECK_VERSION (5, 0, 0) -typedef pa_card_profile_info2 _pa_card_profile_info; -#else -typedef pa_card_profile_info _pa_card_profile_info; -#endif - -static const gchar * pulse_device_get_name (MateMixerDevice *device); -static const gchar * pulse_device_get_description (MateMixerDevice *device); -static const gchar * pulse_device_get_icon (MateMixerDevice *device); +static MateMixerStream *pulse_device_get_stream (MateMixerDevice *mmd, + const gchar *name); -static MateMixerPort * pulse_device_get_port (MateMixerDevice *device, - const gchar *name); -static MateMixerDeviceProfile *pulse_device_get_profile (MateMixerDevice *device, - const gchar *name); +static const GList * pulse_device_list_streams (MateMixerDevice *mmd); +static const GList * pulse_device_list_switches (MateMixerDevice *mmd); -static const GList * pulse_device_list_ports (MateMixerDevice *device); -static const GList * pulse_device_list_profiles (MateMixerDevice *device); +static void pulse_device_load (PulseDevice *device, + const pa_card_info *info); -static MateMixerDeviceProfile *pulse_device_get_active_profile (MateMixerDevice *device); -static gboolean pulse_device_set_active_profile (MateMixerDevice *device, - MateMixerDeviceProfile *profile); - -static void update_port (PulseDevice *device, - pa_card_port_info *p_info); -static void update_profile (PulseDevice *device, - _pa_card_profile_info *p_info); - -static gint compare_ports (gconstpointer a, - gconstpointer b); -static gint compare_profiles (gconstpointer a, - gconstpointer b); - -static void -mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) -{ - iface->get_name = pulse_device_get_name; - iface->get_description = pulse_device_get_description; - iface->get_icon = pulse_device_get_icon; - iface->get_port = pulse_device_get_port; - iface->get_profile = pulse_device_get_profile; - iface->list_ports = pulse_device_list_ports; - iface->list_profiles = pulse_device_list_profiles; - iface->get_active_profile = pulse_device_get_active_profile; - iface->set_active_profile = pulse_device_set_active_profile; -} +static void free_list_streams (PulseDevice *device); +static void free_list_switches (PulseDevice *device); static void pulse_device_class_init (PulseDeviceClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerDeviceClass *device_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = pulse_device_dispose; @@ -133,31 +93,32 @@ pulse_device_class_init (PulseDeviceClass *klass) object_class->get_property = pulse_device_get_property; object_class->set_property = pulse_device_set_property; - g_object_class_install_property (object_class, - PROP_INDEX, - g_param_spec_uint ("index", - "Index", - "Device index", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_CONNECTION, - g_param_spec_object ("connection", - "Connection", - "PulseAudio connection", - PULSE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_ICON, "icon"); - g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); + device_class = MATE_MIXER_DEVICE_CLASS (klass); + device_class->get_stream = pulse_device_get_stream; + device_class->list_streams = pulse_device_list_streams; + device_class->list_switches = pulse_device_list_switches; + + properties[PROP_INDEX] = + g_param_spec_uint ("index", + "Index", + "Index of the device", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_CONNECTION] = + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); g_type_class_add_private (object_class, sizeof (PulseDevicePrivate)); } @@ -173,18 +134,6 @@ pulse_device_get_property (GObject *object, device = PULSE_DEVICE (object); switch (param_id) { - case PROP_NAME: - g_value_set_string (value, device->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, device->priv->description); - break; - case PROP_ICON: - g_value_set_string (value, device->priv->icon); - break; - case PROP_ACTIVE_PROFILE: - g_value_set_object (value, device->priv->profile); - break; case PROP_INDEX: g_value_set_uint (value, device->priv->index); break; @@ -212,7 +161,6 @@ pulse_device_set_property (GObject *object, device->priv->index = g_value_get_uint (value); break; case PROP_CONNECTION: - /* Construct-only object */ device->priv->connection = g_value_dup_object (value); break; default: @@ -233,10 +181,10 @@ pulse_device_init (PulseDevice *device) g_free, g_object_unref); - device->priv->profiles = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); + device->priv->streams = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); } static void @@ -246,20 +194,14 @@ pulse_device_dispose (GObject *object) device = PULSE_DEVICE (object); - if (device->priv->ports_list != NULL) { - g_list_free_full (device->priv->ports_list, g_object_unref); - device->priv->ports_list = NULL; - } g_hash_table_remove_all (device->priv->ports); + g_hash_table_remove_all (device->priv->streams); - if (device->priv->profiles_list != NULL) { - g_list_free_full (device->priv->profiles_list, g_object_unref); - device->priv->profiles_list = NULL; - } - g_hash_table_remove_all (device->priv->profiles); - - g_clear_object (&device->priv->profile); g_clear_object (&device->priv->connection); + g_clear_object (&device->priv->pswitch); + + free_list_streams (device); + free_list_switches (device); G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object); } @@ -271,12 +213,8 @@ pulse_device_finalize (GObject *object) device = PULSE_DEVICE (object); - g_free (device->priv->name); - g_free (device->priv->description); - g_free (device->priv->icon); - - g_hash_table_destroy (device->priv->ports); - g_hash_table_destroy (device->priv->profiles); + g_hash_table_unref (device->priv->ports); + g_hash_table_unref (device->priv->streams); G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object); } @@ -285,123 +223,86 @@ PulseDevice * pulse_device_new (PulseConnection *connection, const pa_card_info *info) { PulseDevice *device; + const gchar *label; + const gchar *icon; g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (info != NULL, NULL); + label = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION); + if G_UNLIKELY (label == NULL) + label = info->name; + + icon = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME); + if G_UNLIKELY (icon == NULL) + icon = "audio-card"; + /* Consider the device index as unchanging parameter */ device = g_object_new (PULSE_TYPE_DEVICE, - "connection", connection, "index", info->index, + "connection", connection, + "name", info->name, + "label", label, + "icon", icon, NULL); - /* Other data may change at any time, so let's make a use of our update function */ + pulse_device_load (device, info); pulse_device_update (device, info); return device; } -gboolean +void pulse_device_update (PulseDevice *device, const pa_card_info *info) { - MateMixerDeviceProfile *profile = NULL; - const gchar *prop; - guint32 i; - - g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (device)); - - /* Name */ - if (g_strcmp0 (device->priv->name, info->name) != 0) { - g_free (device->priv->name); - device->priv->name = g_strdup (info->name); - - g_object_notify (G_OBJECT (device), "name"); - } - - /* Description */ - prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION); - - if (G_UNLIKELY (prop == NULL)) - prop = info->name; - - if (g_strcmp0 (device->priv->description, prop) != 0) { - g_free (device->priv->description); - device->priv->description = g_strdup (prop); - - g_object_notify (G_OBJECT (device), "description"); - } - - /* Icon */ - prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME); - - if (G_UNLIKELY (prop == NULL)) - prop = "audio-card"; - - if (g_strcmp0 (device->priv->icon, prop) != 0) { - g_free (device->priv->icon); - device->priv->icon = g_strdup (prop); - - g_object_notify (G_OBJECT (device), "icon"); - } - -#if PA_CHECK_VERSION (2, 0, 0) - /* List of ports */ - for (i = 0; i < info->n_ports; i++) { - update_port (device, info->ports[i]); - } -#endif + g_return_if_fail (PULSE_IS_DEVICE (device)); + g_return_if_fail (info != NULL); - /* List of profiles */ - for (i = 0; i < info->n_profiles; i++) { #if PA_CHECK_VERSION (5, 0, 0) - pa_card_profile_info2 *p_info = info->profiles2[i]; - - /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only - * differs in the new available flag, we use it not to include profiles - * which are unavailable */ - if (p_info->available == 0) - continue; + if G_LIKELY (info->active_profile2 != NULL) + pulse_device_switch_set_active_profile_by_name (device->priv->pswitch, + info->active_profile2->name); #else - /* The old profile list is an array of structs, not pointers */ - pa_card_profile_info *p_info = &info->profiles[i]; + if G_LIKELY (info->active_profile != NULL) + pulse_device_switch_set_active_profile_by_name (device->priv->pswitch, + info->active_profile->name); #endif - update_profile (device, p_info); - } - - /* Figure out whether the currently active profile has changed */ - profile = NULL; +} -#if PA_CHECK_VERSION (5, 0, 0) - if (G_LIKELY (info->active_profile2 != NULL)) - profile = g_hash_table_lookup (device->priv->profiles, info->active_profile2->name); -#else - if (G_LIKELY (info->active_profile != NULL)) - profile = g_hash_table_lookup (device->priv->profiles, info->active_profile->name); -#endif +void +pulse_device_add_stream (PulseDevice *device, PulseStream *stream) +{ + const gchar *name; - if (profile != device->priv->profile) { - g_clear_object (&device->priv->profile); + g_return_if_fail (PULSE_IS_DEVICE (device)); + g_return_if_fail (PULSE_IS_STREAM (stream)); - if (G_LIKELY (profile != NULL)) - device->priv->profile = g_object_ref (profile); + name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); - g_object_notify (G_OBJECT (device), "active-profile"); - } + free_list_streams (device); - g_object_thaw_notify (G_OBJECT (device)); - return TRUE; + g_hash_table_insert (device->priv->streams, g_strdup (name), stream); + g_signal_emit_by_name (G_OBJECT (device), + "stream-added", + name); } -PulseConnection * -pulse_device_get_connection (PulseDevice *device) +void +pulse_device_remove_stream (PulseDevice *device, PulseStream *stream) { - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); + const gchar *name; - return device->priv->connection; + g_return_if_fail (PULSE_IS_DEVICE (device)); + g_return_if_fail (PULSE_IS_STREAM (stream)); + + name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + + free_list_streams (device); + + g_hash_table_remove (device->priv->streams, name); + g_signal_emit_by_name (G_OBJECT (device), + "stream-removed", + name); } guint32 @@ -412,222 +313,130 @@ pulse_device_get_index (PulseDevice *device) return device->priv->index; } -static const gchar * -pulse_device_get_name (MateMixerDevice *device) -{ - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - return PULSE_DEVICE (device)->priv->name; -} - -static const gchar * -pulse_device_get_description (MateMixerDevice *device) -{ - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - return PULSE_DEVICE (device)->priv->description; -} - -static const gchar * -pulse_device_get_icon (MateMixerDevice *device) +PulseConnection * +pulse_device_get_connection (PulseDevice *device) { g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return PULSE_DEVICE (device)->priv->icon; + return device->priv->connection; } -static MateMixerPort * -pulse_device_get_port (MateMixerDevice *device, const gchar *name) +PulsePort * +pulse_device_get_port (PulseDevice *device, const gchar *name) { g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - g_return_val_if_fail (name != NULL, NULL); - return g_hash_table_lookup (PULSE_DEVICE (device)->priv->ports, name); + return g_hash_table_lookup (device->priv->ports, name); } -static MateMixerDeviceProfile * -pulse_device_get_profile (MateMixerDevice *device, const gchar *name) +static MateMixerStream * +pulse_device_get_stream (MateMixerDevice *mmd, const gchar *name) { - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); g_return_val_if_fail (name != NULL, NULL); - return g_hash_table_lookup (PULSE_DEVICE (device)->priv->profiles, name); + return g_hash_table_lookup (PULSE_DEVICE (mmd)->priv->streams, name); } static const GList * -pulse_device_list_ports (MateMixerDevice *device) +pulse_device_list_streams (MateMixerDevice *mmd) { - PulseDevice *pulse; - - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - pulse = PULSE_DEVICE (device); + PulseDevice *device; - if (pulse->priv->ports_list == NULL) { - GList *list = g_hash_table_get_values (pulse->priv->ports); + g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + device = PULSE_DEVICE (mmd); - pulse->priv->ports_list = g_list_sort (list, compare_ports); - } + if (device->priv->streams_list == NULL) { + device->priv->streams_list = g_hash_table_get_values (device->priv->streams); + if (device->priv->streams_list != NULL) + g_list_foreach (device->priv->streams_list, (GFunc) g_object_ref, NULL); } - - return (const GList *) pulse->priv->ports_list; + return device->priv->streams_list; } static const GList * -pulse_device_list_profiles (MateMixerDevice *device) +pulse_device_list_switches (MateMixerDevice *mmd) { - PulseDevice *pulse; - - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - pulse = PULSE_DEVICE (device); - - if (pulse->priv->profiles_list == NULL) { - GList *list = g_hash_table_get_values (pulse->priv->profiles); - - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - pulse->priv->profiles_list = g_list_sort (list, compare_profiles); - } - } + g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); - return (const GList *) pulse->priv->profiles_list; + return PULSE_DEVICE (mmd)->priv->switches_list; } -static MateMixerDeviceProfile * -pulse_device_get_active_profile (MateMixerDevice *device) +static void +pulse_device_load (PulseDevice *device, const pa_card_info *info) { - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); + guint i; - return PULSE_DEVICE (device)->priv->profile; -} +#if PA_CHECK_VERSION (2, 0, 0) + for (i = 0; i < info->n_ports; i++) { + PulsePort *port; + const gchar *name; + const gchar *icon; -static gboolean -pulse_device_set_active_profile (MateMixerDevice *device, MateMixerDeviceProfile *profile) -{ - PulseDevice *pulse; - const gchar *name; - gboolean ret; + name = info->ports[i]->name; + icon = pa_proplist_gets (info->ports[i]->proplist, "device.icon_name"); - g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); + port = pulse_port_new (name, + info->ports[i]->description, + icon, + info->ports[i]->priority); - pulse = PULSE_DEVICE (device); + g_hash_table_insert (device->priv->ports, g_strdup (name), port); + } +#endif - name = mate_mixer_device_profile_get_name (profile); + /* Create the device profile switch */ + if (info->n_profiles > 0) { + device->priv->pswitch = pulse_device_switch_new ("profile", + _("Profile"), + device); - /* Make sure the profile belongs to the device */ - if (g_hash_table_lookup (pulse->priv->profiles, name) == NULL) { - g_warning ("Profile %s does not belong to device %s", name, pulse->priv->name); - return FALSE; + device->priv->switches_list = g_list_prepend (NULL, g_object_ref (device->priv->pswitch)); } - ret = pulse_connection_set_card_profile (pulse->priv->connection, - pulse->priv->name, - name); - if (ret == TRUE) { - if (pulse->priv->profile != NULL) - g_object_unref (pulse->priv->profile); + for (i = 0; i < info->n_profiles; i++) { + PulseDeviceProfile *profile; + +#if PA_CHECK_VERSION (5, 0, 0) + pa_card_profile_info2 *p_info = info->profiles2[i]; + + /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only + * differs in the new available flag, we use it not to include profiles + * which are unavailable */ + if (p_info->available == 0) + continue; +#else + /* The old profile list is an array of structs, not pointers */ + pa_card_profile_info *p_info = &info->profiles[i]; +#endif - pulse->priv->profile = g_object_ref (profile); + profile = pulse_device_profile_new (p_info->name, + p_info->description, + p_info->priority); - g_object_notify (G_OBJECT (device), "active-profile"); + pulse_device_switch_add_profile (device->priv->pswitch, profile); } - return ret; } static void -update_port (PulseDevice *device, pa_card_port_info *p_info) +free_list_streams (PulseDevice *device) { - MateMixerPort *port; - MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS; - const gchar *icon; - - icon = pa_proplist_gets (p_info->proplist, "device.icon_name"); - - if (p_info->available == PA_PORT_AVAILABLE_YES) - flags |= MATE_MIXER_PORT_AVAILABLE; - - if (p_info->direction & PA_DIRECTION_INPUT) - flags |= MATE_MIXER_PORT_INPUT; - if (p_info->direction & PA_DIRECTION_OUTPUT) - flags |= MATE_MIXER_PORT_OUTPUT; - - port = g_hash_table_lookup (device->priv->ports, p_info->name); - - if (port != NULL) { - /* Update existing port */ - _mate_mixer_port_update_description (port, p_info->description); - _mate_mixer_port_update_icon (port, icon); - _mate_mixer_port_update_priority (port, p_info->priority); - _mate_mixer_port_update_flags (port, flags); - } else { - /* Add previously unknown port to the hash table */ - port = _mate_mixer_port_new (p_info->name, - p_info->description, - icon, - p_info->priority, - flags); - - g_hash_table_insert (device->priv->ports, g_strdup (p_info->name), port); - } + if (device->priv->streams_list == NULL) + return; + + g_list_free_full (device->priv->streams_list, g_object_unref); + + device->priv->streams_list = NULL; } static void -update_profile (PulseDevice *device, _pa_card_profile_info *p_info) +free_list_switches (PulseDevice *device) { - MateMixerDeviceProfile *profile; - - profile = g_hash_table_lookup (device->priv->profiles, p_info->name); - - if (profile != NULL) { - /* Update existing profile */ - _mate_mixer_device_profile_update_description (profile, p_info->description); - _mate_mixer_device_profile_update_priority (profile, p_info->priority); - _mate_mixer_device_profile_update_num_input_streams (profile, p_info->n_sources); - _mate_mixer_device_profile_update_num_output_streams (profile, p_info->n_sinks); - } else { - /* Add previously unknown profile to the hash table */ - profile = _mate_mixer_device_profile_new (p_info->name, - p_info->description, - p_info->priority, - p_info->n_sources, - p_info->n_sinks); - - g_hash_table_insert (device->priv->profiles, g_strdup (p_info->name), profile); - } -} + if (device->priv->switches_list == NULL) + return; -static gint -compare_ports (gconstpointer a, gconstpointer b) -{ - MateMixerPort *p1 = MATE_MIXER_PORT (a); - MateMixerPort *p2 = MATE_MIXER_PORT (b); - - gint ret = (gint) (mate_mixer_port_get_priority (p2) - - mate_mixer_port_get_priority (p1)); - if (ret != 0) - return ret; - else - return strcmp (mate_mixer_port_get_name (p1), - mate_mixer_port_get_name (p2)); -} + g_list_free_full (device->priv->switches_list, g_object_unref); -static gint -compare_profiles (gconstpointer a, gconstpointer b) -{ - MateMixerDeviceProfile *p1 = MATE_MIXER_DEVICE_PROFILE (a); - MateMixerDeviceProfile *p2 = MATE_MIXER_DEVICE_PROFILE (b); - - gint ret = (gint) (mate_mixer_device_profile_get_priority (p2) - - mate_mixer_device_profile_get_priority (p1)); - if (ret != 0) - return ret; - else - return strcmp (mate_mixer_device_profile_get_name (p1), - mate_mixer_device_profile_get_name (p2)); + device->priv->switches_list = NULL; } diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h index 94c331f..863330f 100644 --- a/backends/pulse/pulse-device.h +++ b/backends/pulse/pulse-device.h @@ -20,33 +20,33 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer.h> #include <pulse/pulseaudio.h> -#include "pulse-connection.h" +#include "pulse-types.h" G_BEGIN_DECLS -#define PULSE_TYPE_DEVICE \ +#define PULSE_TYPE_DEVICE \ (pulse_device_get_type ()) -#define PULSE_DEVICE(o) \ +#define PULSE_DEVICE(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE, PulseDevice)) -#define PULSE_IS_DEVICE(o) \ +#define PULSE_IS_DEVICE(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE)) -#define PULSE_DEVICE_CLASS(k) \ +#define PULSE_DEVICE_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE, PulseDeviceClass)) -#define PULSE_IS_DEVICE_CLASS(k) \ +#define PULSE_IS_DEVICE_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE)) -#define PULSE_DEVICE_GET_CLASS(o) \ +#define PULSE_DEVICE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE, PulseDeviceClass)) -typedef struct _PulseDevice PulseDevice; typedef struct _PulseDeviceClass PulseDeviceClass; typedef struct _PulseDevicePrivate PulseDevicePrivate; struct _PulseDevice { - GObject parent; + MateMixerDevice parent; /*< private >*/ PulseDevicePrivate *priv; @@ -54,20 +54,29 @@ struct _PulseDevice struct _PulseDeviceClass { - GObjectClass parent; + MateMixerDeviceClass parent; }; GType pulse_device_get_type (void) G_GNUC_CONST; -PulseDevice *pulse_device_new (PulseConnection *connection, +PulseDevice * pulse_device_new (PulseConnection *connection, const pa_card_info *info); -gboolean pulse_device_update (PulseDevice *device, +void pulse_device_update (PulseDevice *device, const pa_card_info *info); +void pulse_device_add_stream (PulseDevice *device, + PulseStream *stream); + +void pulse_device_remove_stream (PulseDevice *device, + PulseStream *stream); + guint32 pulse_device_get_index (PulseDevice *device); PulseConnection *pulse_device_get_connection (PulseDevice *device); +PulsePort * pulse_device_get_port (PulseDevice *device, + const gchar *name); + G_END_DECLS #endif /* PULSE_DEVICE_H */ diff --git a/backends/pulse/pulse-ext-stream.c b/backends/pulse/pulse-ext-stream.c index b00e967..3e7490a 100644 --- a/backends/pulse/pulse-ext-stream.c +++ b/backends/pulse/pulse-ext-stream.c @@ -19,252 +19,326 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include <pulse/ext-stream-restore.h> #include "pulse-connection.h" -#include "pulse-client-stream.h" #include "pulse-ext-stream.h" #include "pulse-helpers.h" #include "pulse-stream.h" +#include "pulse-stream-control.h" -static void pulse_ext_stream_class_init (PulseExtStreamClass *klass); -static void pulse_ext_stream_init (PulseExtStream *ext); +struct _PulseExtStreamPrivate +{ + MateMixerAppInfo *app_info; + MateMixerDirection direction; +}; -G_DEFINE_TYPE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_CLIENT_STREAM); +enum { + PROP_0, + PROP_DIRECTION +}; -static void pulse_ext_stream_reload (PulseStream *pstream); +static void mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface); -static gboolean pulse_ext_stream_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_ext_stream_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_ext_stream_set_parent (PulseClientStream *pclient, - PulseStream *parent); -static gboolean pulse_ext_stream_remove (PulseClientStream *pclient); +static void pulse_ext_stream_class_init (PulseExtStreamClass *klass); -static void -pulse_ext_stream_class_init (PulseExtStreamClass *klass) -{ - PulseStreamClass *stream_class; - PulseClientStreamClass *client_class; +static void pulse_ext_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_ext_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); - stream_class = PULSE_STREAM_CLASS (klass); +static void pulse_ext_stream_init (PulseExtStream *ext); - stream_class->reload = pulse_ext_stream_reload; - stream_class->set_mute = pulse_ext_stream_set_mute; - stream_class->set_volume = pulse_ext_stream_set_volume; +G_DEFINE_TYPE_WITH_CODE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_STREAM_CONTROL, + G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STORED_CONTROL, + mate_mixer_stored_control_interface_init)) - client_class = PULSE_CLIENT_STREAM_CLASS (klass); +static MateMixerDirection pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc); - client_class->set_parent = pulse_ext_stream_set_parent; - client_class->remove = pulse_ext_stream_remove; -} +static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc); + +static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, + MateMixerStream *mms); + +static gboolean pulse_ext_stream_set_mute (PulseStreamControl *control, + gboolean mute); +static gboolean pulse_ext_stream_set_volume (PulseStreamControl *control, + pa_cvolume *cvolume); + +static void fill_ext_stream_restore_info (PulseStreamControl *control, + pa_ext_stream_restore_info *info); static void -pulse_ext_stream_init (PulseExtStream *ext) +mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface) { + iface->get_direction = pulse_ext_stream_get_direction; } -PulseStream * -pulse_ext_stream_new (PulseConnection *connection, - const pa_ext_stream_restore_info *info, - PulseStream *parent) +static void +pulse_ext_stream_class_init (PulseExtStreamClass *klass) { - PulseStream *ext; + GObjectClass *object_class; + MateMixerStreamControlClass *control_class; + PulseStreamControlClass *pulse_class; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = pulse_ext_stream_get_property; + object_class->set_property = pulse_ext_stream_set_property; - ext = g_object_new (PULSE_TYPE_EXT_STREAM, - "connection", connection, - NULL); + control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); + control_class->get_app_info = pulse_ext_stream_get_app_info; + control_class->set_stream = pulse_ext_stream_set_stream; - /* Consider the stream name as unchanging parameter */ - pulse_stream_update_name (ext, info->name); + pulse_class = PULSE_STREAM_CONTROL_CLASS (klass); + pulse_class->set_mute = pulse_ext_stream_set_mute; + pulse_class->set_volume = pulse_ext_stream_set_volume; - /* Other data may change at any time, so let's make a use of our update function */ - pulse_ext_stream_update (ext, info, parent); + g_object_class_override_property (object_class, PROP_DIRECTION, "direction"); - return ext; + g_type_class_add_private (object_class, sizeof (PulseExtStreamPrivate)); } -gboolean -pulse_ext_stream_update (PulseStream *pstream, - const pa_ext_stream_restore_info *info, - PulseStream *parent) +static void +pulse_ext_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - MateMixerClientStreamRole role = MATE_MIXER_CLIENT_STREAM_ROLE_NONE; - MateMixerStreamFlags flags = MATE_MIXER_STREAM_CLIENT | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_CAN_SET_VOLUME; - MateMixerClientStreamFlags client_flags = - MATE_MIXER_CLIENT_STREAM_CACHED; + PulseExtStream *ext; - PulseClientStream *pclient; - gchar *suffix; + ext = PULSE_EXT_STREAM (object); - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); + switch (param_id) { + case PROP_DIRECTION: + g_value_set_enum (value, ext->priv->direction); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} - pclient = PULSE_CLIENT_STREAM (pstream); +static void +pulse_ext_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulseExtStream *ext; - suffix = strchr (info->name, ':'); - if (suffix != NULL) - suffix++; + ext = PULSE_EXT_STREAM (object); - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); + switch (param_id) { + case PROP_DIRECTION: + ext->priv->direction = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_ext_stream_init (PulseExtStream *ext) +{ + ext->priv = G_TYPE_INSTANCE_GET_PRIVATE (ext, + PULSE_TYPE_EXT_STREAM, + PulseExtStreamPrivate); +} + +PulseExtStream * +pulse_ext_stream_new (PulseConnection *connection, + const pa_ext_stream_restore_info *info, + PulseStream *parent) +{ + PulseExtStream *ext; + gchar *suffix; + MateMixerDirection direction; + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE; + MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; + MateMixerAppInfo *app_info; + + MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); if (g_str_has_prefix (info->name, "sink-input")) - flags |= MATE_MIXER_STREAM_OUTPUT; + direction = MATE_MIXER_DIRECTION_OUTPUT; else if (g_str_has_prefix (info->name, "source-output")) - flags |= MATE_MIXER_STREAM_INPUT; + direction = MATE_MIXER_DIRECTION_INPUT; else - g_debug ("Unknown ext-stream %s", info->name); + direction = MATE_MIXER_DIRECTION_UNKNOWN; + + app_info = _mate_mixer_app_info_new (); + + suffix = strchr (info->name, ':'); + if (suffix != NULL) + suffix++; if (strstr (info->name, "-by-media-role:")) { if (G_LIKELY (suffix != NULL)) - role = pulse_convert_media_role_name (suffix); + media_role = pulse_convert_media_role_name (suffix); } else if (strstr (info->name, "-by-application-name:")) { - client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION; + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; if (G_LIKELY (suffix != NULL)) - pulse_client_stream_update_app_name (pclient, suffix); + _mate_mixer_app_info_set_name (app_info, suffix); } else if (strstr (info->name, "-by-application-id:")) { - client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION; + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; if (G_LIKELY (suffix != NULL)) - pulse_client_stream_update_app_id (pclient, suffix); + _mate_mixer_app_info_set_id (app_info, suffix); } - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); + ext = g_object_new (PULSE_TYPE_EXT_STREAM, + "flags", flags, + "role", role, + "media-role", media_role, + "name", info->name, + "connection", connection, + "direction", direction, + "stream", parent, + NULL); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, &info->volume, 0); + if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) + ext->priv->app_info = app_info; + else + _mate_mixer_app_info_free (app_info); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); + pulse_ext_stream_update (ext, info, parent); + return ext; +} - pulse_client_stream_update_flags (pclient, client_flags); - pulse_client_stream_update_role (pclient, role); +void +pulse_ext_stream_update (PulseExtStream *ext, + const pa_ext_stream_restore_info *info, + PulseStream *parent) +{ + g_return_if_fail (PULSE_IS_EXT_STREAM (ext)); + g_return_if_fail (info != NULL); - if (parent != NULL) - pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent)); - else - pulse_client_stream_update_parent (pclient, NULL); + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (ext)); + + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (ext), + info->mute ? TRUE : FALSE); - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (ext), + &info->channel_map); + + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (ext), + &info->volume, + 0); + + _mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (ext), + MATE_MIXER_STREAM (parent)); + + g_object_thaw_notify (G_OBJECT (ext)); } -static void -pulse_ext_stream_reload (PulseStream *pstream) +static MateMixerDirection +pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc) { - g_return_if_fail (PULSE_IS_EXT_STREAM (pstream)); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_DIRECTION_UNKNOWN); - pulse_connection_load_ext_stream_info (pulse_stream_get_connection (pstream)); + return PULSE_EXT_STREAM (mmsc)->priv->direction; } -static gboolean -pulse_ext_stream_set_mute (PulseStream *pstream, gboolean mute) +static MateMixerAppInfo * +pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc) { - MateMixerStream *parent; - const pa_channel_map *map; - const pa_cvolume *cvolume; - pa_ext_stream_restore_info info; + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), NULL); - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE); + return PULSE_EXT_STREAM (mmsc)->priv->app_info; +} - info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); - info.mute = mute; +static gboolean +pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms) +{ + pa_ext_stream_restore_info info; - map = pulse_stream_get_channel_map (pstream); - info.channel_map = *map; + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); + g_return_val_if_fail (mms == NULL || PULSE_IS_STREAM (mms), FALSE); - cvolume = pulse_stream_get_cvolume (pstream); - info.volume = *cvolume; + fill_ext_stream_restore_info (PULSE_STREAM_CONTROL (mms), &info); - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (parent != NULL) - info.device = mate_mixer_stream_get_name (parent); + if (mms != NULL) + info.device = mate_mixer_stream_get_name (mms); else info.device = NULL; - return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info); + return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (PULSE_STREAM_CONTROL (mmsc)), + &info); } static gboolean -pulse_ext_stream_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +pulse_ext_stream_set_mute (PulseStreamControl *control, gboolean mute) { - MateMixerStream *parent; - const pa_channel_map *map; pa_ext_stream_restore_info info; - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); - - info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); - info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream)); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); - map = pulse_stream_get_channel_map (pstream); - info.channel_map = *map; - - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (parent != NULL) - info.device = mate_mixer_stream_get_name (parent); - else - info.device = NULL; + fill_ext_stream_restore_info (control, &info); - info.volume = *cvolume; + info.mute = mute; - return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info); + return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), + &info); } static gboolean -pulse_ext_stream_set_parent (PulseClientStream *pclient, PulseStream *parent) +pulse_ext_stream_set_volume (PulseStreamControl *control, pa_cvolume *cvolume) { - PulseStream *pstream; - const pa_channel_map *map; - const pa_cvolume *cvolume; pa_ext_stream_restore_info info; - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE); - g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE); - - pstream = PULSE_STREAM (pclient); - - info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); - info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream)); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); - map = pulse_stream_get_channel_map (pstream); - info.channel_map = *map; + fill_ext_stream_restore_info (control, &info); - cvolume = pulse_stream_get_cvolume (pstream); info.volume = *cvolume; - info.device = mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)); - - return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info); + return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), + &info); } -static gboolean -pulse_ext_stream_remove (PulseClientStream *pclient) +static void +fill_ext_stream_restore_info (PulseStreamControl *control, + pa_ext_stream_restore_info *info) { - PulseStream *pstream; - const gchar *name; + MateMixerStream *stream; + MateMixerStreamControl *mmsc; + const pa_channel_map *map; + const pa_cvolume *cvolume; + + mmsc = MATE_MIXER_STREAM_CONTROL (control); - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE); + info->name = mate_mixer_stream_control_get_name (mmsc); + info->mute = mate_mixer_stream_control_get_mute (mmsc); - pstream = PULSE_STREAM (pclient); - name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); + map = pulse_stream_control_get_channel_map (control); + info->channel_map = *map; - return pulse_connection_delete_ext_stream (pulse_stream_get_connection (pstream), name); + cvolume = pulse_stream_control_get_cvolume (control); + info->volume = *cvolume; + + stream = mate_mixer_stream_control_get_stream (mmsc); + if (stream != NULL) + info->device = mate_mixer_stream_get_name (stream); + else + info->device = NULL; } diff --git a/backends/pulse/pulse-ext-stream.h b/backends/pulse/pulse-ext-stream.h index e8dabb6..b667dc7 100644 --- a/backends/pulse/pulse-ext-stream.h +++ b/backends/pulse/pulse-ext-stream.h @@ -24,9 +24,8 @@ #include <pulse/pulseaudio.h> #include <pulse/ext-stream-restore.h> -#include "pulse-client-stream.h" -#include "pulse-connection.h" -#include "pulse-stream.h" +#include "pulse-stream-control.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -43,28 +42,31 @@ G_BEGIN_DECLS #define PULSE_EXT_STREAM_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_EXT_STREAM, PulseExtStreamClass)) -typedef struct _PulseExtStream PulseExtStream; -typedef struct _PulseExtStreamClass PulseExtStreamClass; +typedef struct _PulseExtStreamClass PulseExtStreamClass; +typedef struct _PulseExtStreamPrivate PulseExtStreamPrivate; struct _PulseExtStream { - PulseClientStream parent; + PulseStreamControl parent; + + /*< private >*/ + PulseExtStreamPrivate *priv; }; struct _PulseExtStreamClass { - PulseClientStreamClass parent_class; + PulseStreamControlClass parent_class; }; -GType pulse_ext_stream_get_type (void) G_GNUC_CONST; +GType pulse_ext_stream_get_type (void) G_GNUC_CONST; -PulseStream *pulse_ext_stream_new (PulseConnection *connection, - const pa_ext_stream_restore_info *info, - PulseStream *parent); +PulseExtStream *pulse_ext_stream_new (PulseConnection *connection, + const pa_ext_stream_restore_info *info, + PulseStream *parent); -gboolean pulse_ext_stream_update (PulseStream *pstream, - const pa_ext_stream_restore_info *info, - PulseStream *parent); +void pulse_ext_stream_update (PulseExtStream *ext, + const pa_ext_stream_restore_info *info, + PulseStream *parent); G_END_DECLS diff --git a/backends/pulse/pulse-helpers.c b/backends/pulse/pulse-helpers.c index 577f2c6..73f8cdb 100644 --- a/backends/pulse/pulse-helpers.c +++ b/backends/pulse/pulse-helpers.c @@ -29,6 +29,7 @@ typedef struct { pa_channel_position_t pa_position; } PositionMap; +// XXX optimize static PositionMap const position_map[] = { { MATE_MIXER_CHANNEL_UNKNOWN, PA_CHANNEL_POSITION_INVALID }, { MATE_MIXER_CHANNEL_MONO, PA_CHANNEL_POSITION_MONO }, @@ -76,42 +77,42 @@ pulse_convert_position_to_pulse (MateMixerChannelPosition position) return PA_CHANNEL_POSITION_INVALID; } -MateMixerClientStreamRole +MateMixerStreamControlMediaRole pulse_convert_media_role_name (const gchar *name) { if (!strcmp (name, "video")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_VIDEO; } else if (!strcmp (name, "music")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_MUSIC; } else if (!strcmp (name, "game")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_GAME; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_GAME; } else if (!strcmp (name, "event")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_EVENT; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT; } else if (!strcmp (name, "phone")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_PHONE; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PHONE; } else if (!strcmp (name, "animation")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ANIMATION; } else if (!strcmp (name, "production")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PRODUCTION; } else if (!strcmp (name, "a11y")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_A11Y; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_A11Y; } else if (!strcmp (name, "test")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_TEST; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST; } else if (!strcmp (name, "abstract")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT; } else if (!strcmp (name, "filter")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_FILTER; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER; } - return MATE_MIXER_CLIENT_STREAM_ROLE_NONE; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; } diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h index 7ccd753..667fc3c 100644 --- a/backends/pulse/pulse-helpers.h +++ b/backends/pulse/pulse-helpers.h @@ -19,17 +19,16 @@ #define PULSE_HELPERS_H #include <glib.h> - -#include <libmatemixer/matemixer-enums.h> +#include <libmatemixer/matemixer.h> #include <pulse/pulseaudio.h> G_BEGIN_DECLS -MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position); -pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position); +MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position); +pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position); -MateMixerClientStreamRole pulse_convert_media_role_name (const gchar *name); +MateMixerStreamControlMediaRole pulse_convert_media_role_name (const gchar *name); G_END_DECLS diff --git a/backends/pulse/pulse-monitor.c b/backends/pulse/pulse-monitor.c index 3d5b4a8..915b71b 100644 --- a/backends/pulse/pulse-monitor.c +++ b/backends/pulse/pulse-monitor.c @@ -16,6 +16,7 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> #include <pulse/pulseaudio.h> @@ -27,7 +28,6 @@ struct _PulseMonitorPrivate pa_context *context; pa_proplist *proplist; pa_stream *stream; - gchar *name; guint32 index_source; guint32 index_sink_input; gboolean enabled; @@ -36,7 +36,6 @@ struct _PulseMonitorPrivate enum { PROP_0, PROP_ENABLED, - PROP_NAME, PROP_INDEX_SOURCE, PROP_INDEX_SINK_INPUT, N_PROPERTIES @@ -91,15 +90,6 @@ pulse_monitor_class_init (PulseMonitorClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - properties[PROP_NAME] = - g_param_spec_string ("name", - "Name", - "Name of the monitor", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - properties[PROP_INDEX_SOURCE] = g_param_spec_uint ("index-source", "Index of source", @@ -153,9 +143,6 @@ pulse_monitor_get_property (GObject *object, case PROP_ENABLED: g_value_set_boolean (value, monitor->priv->enabled); break; - case PROP_NAME: - g_value_set_string (value, monitor->priv->name); - break; case PROP_INDEX_SOURCE: g_value_set_uint (value, monitor->priv->index_source); break; @@ -179,9 +166,6 @@ pulse_monitor_set_property (GObject *object, monitor = PULSE_MONITOR (object); switch (param_id) { - case PROP_NAME: - pulse_monitor_set_name (monitor, g_value_get_string (value)); - break; case PROP_INDEX_SOURCE: monitor->priv->index_source = g_value_get_uint (value); break; @@ -218,15 +202,12 @@ pulse_monitor_finalize (GObject *object) pa_context_unref (monitor->priv->context); pa_proplist_free (monitor->priv->proplist); - g_free (monitor->priv->name); - G_OBJECT_CLASS (pulse_monitor_parent_class)->finalize (object); } PulseMonitor * pulse_monitor_new (pa_context *context, pa_proplist *proplist, - const gchar *name, guint32 index_source, guint32 index_sink_input) { @@ -236,7 +217,6 @@ pulse_monitor_new (pa_context *context, g_return_val_if_fail (proplist != NULL, NULL); monitor = g_object_new (PULSE_TYPE_MONITOR, - "name", name, "index-source", index_source, "index-sink-input", index_sink_input, NULL); @@ -280,34 +260,11 @@ pulse_monitor_set_enabled (PulseMonitor *monitor, gboolean enabled) return TRUE; } -const gchar * -pulse_monitor_get_name (PulseMonitor *monitor) -{ - g_return_val_if_fail (PULSE_IS_MONITOR (monitor), NULL); - - return monitor->priv->name; -} - -gboolean -pulse_monitor_set_name (PulseMonitor *monitor, const gchar *name) -{ - g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE); - - if (g_strcmp0 (name, monitor->priv->name) != 0) { - g_free (monitor->priv->name); - monitor->priv->name = g_strdup (name); - - g_object_notify_by_pspec (G_OBJECT (monitor), properties[PROP_NAME]); - } - return TRUE; -} - static gboolean stream_connect (PulseMonitor *monitor) { pa_sample_spec spec; pa_buffer_attr attr; - const gchar *name; gchar *idx; int ret; @@ -320,14 +277,9 @@ stream_connect (PulseMonitor *monitor) spec.format = PA_SAMPLE_FLOAT32; spec.rate = 25; - if (monitor->priv->name != NULL) - name = monitor->priv->name; - else - name = "Peak detect"; - monitor->priv->stream = pa_stream_new_with_proplist (monitor->priv->context, - name, + _("Peak detect"), &spec, NULL, monitor->priv->proplist); diff --git a/backends/pulse/pulse-monitor.h b/backends/pulse/pulse-monitor.h index 41147f5..e371ec3 100644 --- a/backends/pulse/pulse-monitor.h +++ b/backends/pulse/pulse-monitor.h @@ -23,6 +23,8 @@ #include <pulse/pulseaudio.h> +#include "pulse-types.h" + G_BEGIN_DECLS #define PULSE_TYPE_MONITOR \ @@ -38,7 +40,6 @@ G_BEGIN_DECLS #define PULSE_MONITOR_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_MONITOR, PulseMonitorClass)) -typedef struct _PulseMonitor PulseMonitor; typedef struct _PulseMonitorClass PulseMonitorClass; typedef struct _PulseMonitorPrivate PulseMonitorPrivate; @@ -54,7 +55,7 @@ struct _PulseMonitorClass { GObjectClass parent_class; - /* Signals */ + /*< private >*/ void (*value) (PulseMonitor *monitor, gdouble value); }; @@ -63,7 +64,6 @@ GType pulse_monitor_get_type (void) G_GNUC_CONST; PulseMonitor *pulse_monitor_new (pa_context *context, pa_proplist *proplist, - const gchar *name, guint32 index_source, guint32 index_sink_input); @@ -71,10 +71,6 @@ gboolean pulse_monitor_get_enabled (PulseMonitor *monitor); gboolean pulse_monitor_set_enabled (PulseMonitor *monitor, gboolean enabled); -const gchar * pulse_monitor_get_name (PulseMonitor *monitor); -gboolean pulse_monitor_set_name (PulseMonitor *monitor, - const gchar *name); - G_END_DECLS #endif /* PULSE_MONITOR_H */ diff --git a/backends/pulse/pulse-port-switch.c b/backends/pulse/pulse-port-switch.c new file mode 100644 index 0000000..08f1543 --- /dev/null +++ b/backends/pulse/pulse-port-switch.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" +#include "pulse-stream.h" + +struct _PulsePortSwitchPrivate +{ + GList *ports; + PulseStream *stream; +}; + +enum { + PROP_0, + PROP_STREAM, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void pulse_port_switch_class_init (PulsePortSwitchClass *klass); + +static void pulse_port_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_port_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void pulse_port_switch_init (PulsePortSwitch *swtch); +static void pulse_port_switch_dispose (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (PulsePortSwitch, pulse_port_switch, MATE_MIXER_TYPE_SWITCH) + +static gboolean pulse_port_switch_set_active_option (MateMixerSwitch *mms, + MateMixerSwitchOption *mmso); + +static const GList *pulse_port_switch_list_options (MateMixerSwitch *mms); + +static gint compare_ports (gconstpointer a, + gconstpointer b); +static gint compare_port_name (gconstpointer a, + gconstpointer b); + +static void +pulse_port_switch_class_init (PulsePortSwitchClass *klass) +{ + GObjectClass *object_class; + MateMixerSwitchClass *switch_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_port_switch_dispose; + object_class->get_property = pulse_port_switch_get_property; + object_class->set_property = pulse_port_switch_set_property; + + switch_class = MATE_MIXER_SWITCH_CLASS (klass); + switch_class->set_active_option = pulse_port_switch_set_active_option; + switch_class->list_options = pulse_port_switch_list_options; + + properties[PROP_STREAM] = + g_param_spec_object ("stream", + "Stream", + "PulseAudio stream", + PULSE_TYPE_STREAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (PulsePortSwitchPrivate)); +} + +static void +pulse_port_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulsePortSwitch *swtch; + + swtch = PULSE_PORT_SWITCH (object); + + switch (param_id) { + case PROP_STREAM: + g_value_set_object (value, swtch->priv->stream); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_port_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulsePortSwitch *swtch; + + swtch = PULSE_PORT_SWITCH (object); + + switch (param_id) { + case PROP_STREAM: + /* Construct-only object */ + swtch->priv->stream = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_port_switch_init (PulsePortSwitch *swtch) +{ + swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, + PULSE_TYPE_PORT_SWITCH, + PulsePortSwitchPrivate); +} + +static void +pulse_port_switch_dispose (GObject *object) +{ + PulsePortSwitch *swtch; + + swtch = PULSE_PORT_SWITCH (object); + + g_clear_object (&swtch->priv->stream); + + G_OBJECT_CLASS (pulse_port_switch_parent_class)->dispose (object); +} + +PulseStream * +pulse_port_switch_get_stream (PulsePortSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_PORT_SWITCH (swtch), NULL); + + return swtch->priv->stream; +} + +void +pulse_port_switch_add_port (PulsePortSwitch *swtch, PulsePort *port) +{ + g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_PORT (port)); + + swtch->priv->ports = g_list_insert_sorted (swtch->priv->ports, + port, + compare_ports); +} + +void +pulse_port_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port) +{ + g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_PORT (port)); + + _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), + MATE_MIXER_SWITCH_OPTION (port)); +} + +void +pulse_port_switch_set_active_port_by_name (PulsePortSwitch *swtch, const gchar *name) +{ + GList *item; + + g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch)); + g_return_if_fail (name != NULL); + + item = g_list_find_custom (swtch->priv->ports, name, compare_port_name); + if G_UNLIKELY (item == NULL) { + g_debug ("Invalid switch port name %s", name); + return; + } + pulse_port_switch_set_active_port (swtch, PULSE_PORT (item->data)); +} + +static gboolean +pulse_port_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso) +{ + PulsePortSwitchClass *klass; + + g_return_val_if_fail (PULSE_IS_PORT_SWITCH (mms), FALSE); + g_return_val_if_fail (PULSE_IS_PORT (mmso), FALSE); + + klass = PULSE_PORT_SWITCH_GET_CLASS (PULSE_PORT_SWITCH (mms)); + + return klass->set_active_port (PULSE_PORT_SWITCH (mms), + PULSE_PORT (mmso)); +} + +static const GList * +pulse_port_switch_list_options (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_PORT_SWITCH (swtch), NULL); + + return PULSE_PORT_SWITCH (swtch)->priv->ports; +} + +static gint +compare_ports (gconstpointer a, gconstpointer b) +{ + return pulse_port_get_priority (PULSE_PORT (b)) - + pulse_port_get_priority (PULSE_PORT (a)); +} + +static gint +compare_port_name (gconstpointer a, gconstpointer b) +{ + PulsePort *port = PULSE_PORT (a); + const gchar *name = (const gchar *) b; + + return strcmp (pulse_port_get_name (port), name); +} diff --git a/backends/pulse/pulse-port-switch.h b/backends/pulse/pulse-port-switch.h new file mode 100644 index 0000000..6ccef38 --- /dev/null +++ b/backends/pulse/pulse-port-switch.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_PORT_SWITCH_H +#define PULSE_PORT_SWITCH_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_PORT_SWITCH \ + (pulse_port_switch_get_type ()) +#define PULSE_PORT_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_PORT_SWITCH, PulsePortSwitch)) +#define PULSE_IS_PORT_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_PORT_SWITCH)) +#define PULSE_PORT_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_PORT_SWITCH, PulsePortSwitchClass)) +#define PULSE_IS_PORT_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_PORT_SWITCH)) +#define PULSE_PORT_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_PORT_SWITCH, PulsePortSwitchClass)) + +typedef struct _PulsePortSwitchClass PulsePortSwitchClass; +typedef struct _PulsePortSwitchPrivate PulsePortSwitchPrivate; + +struct _PulsePortSwitch +{ + MateMixerSwitch parent; + + /*< private >*/ + PulsePortSwitchPrivate *priv; +}; + +struct _PulsePortSwitchClass +{ + MateMixerSwitchClass parent_class; + + /*< private >*/ + gboolean (*set_active_port) (PulsePortSwitch *swtch, + PulsePort *port); +}; + +GType pulse_port_switch_get_type (void) G_GNUC_CONST; + +PulseStream *pulse_port_switch_get_stream (PulsePortSwitch *swtch); + +void pulse_port_switch_add_port (PulsePortSwitch *swtch, + PulsePort *port); + +void pulse_port_switch_set_active_port (PulsePortSwitch *swtch, + PulsePort *port); + +void pulse_port_switch_set_active_port_by_name (PulsePortSwitch *swtch, + const gchar *name); + +G_END_DECLS + +#endif /* PULSE_PORT_SWITCH_H */ diff --git a/backends/pulse/pulse-port.c b/backends/pulse/pulse-port.c new file mode 100644 index 0000000..f427448 --- /dev/null +++ b/backends/pulse/pulse-port.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-port.h" + +struct _PulsePortPrivate +{ + guint priority; +}; + +static void pulse_port_class_init (PulsePortClass *klass); +static void pulse_port_init (PulsePort *port); + +G_DEFINE_TYPE (PulsePort, pulse_port, MATE_MIXER_TYPE_SWITCH_OPTION) + +static void +pulse_port_class_init (PulsePortClass *klass) +{ + g_type_class_add_private (klass, sizeof (PulsePortPrivate)); +} + +static void +pulse_port_init (PulsePort *port) +{ + port->priv = G_TYPE_INSTANCE_GET_PRIVATE (port, + PULSE_TYPE_PORT, + PulsePortPrivate); +} + +PulsePort * +pulse_port_new (const gchar *name, + const gchar *label, + const gchar *icon, + guint priority) +{ + PulsePort *port; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + + port = g_object_new (PULSE_TYPE_PORT, + "name", name, + "label", label, + "icon", icon, + NULL); + + port->priv->priority = priority; + return port; +} + +const gchar * +pulse_port_get_name (PulsePort *port) +{ + g_return_val_if_fail (PULSE_IS_PORT (port), NULL); + + return mate_mixer_switch_option_get_name (MATE_MIXER_SWITCH_OPTION (port)); +} + +guint +pulse_port_get_priority (PulsePort *port) +{ + g_return_val_if_fail (PULSE_IS_PORT (port), 0); + + return port->priv->priority; +} diff --git a/backends/pulse/pulse-port.h b/backends/pulse/pulse-port.h new file mode 100644 index 0000000..241fa2d --- /dev/null +++ b/backends/pulse/pulse-port.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_PORT_H +#define PULSE_PORT_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_PORT \ + (pulse_port_get_type ()) +#define PULSE_PORT(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_PORT, PulsePort)) +#define PULSE_IS_PORT(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_PORT)) +#define PULSE_PORT_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_PORT, PulsePortClass)) +#define PULSE_IS_PORT_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_PORT)) +#define PULSE_PORT_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_PORT, PulsePortClass)) + +typedef struct _PulsePortClass PulsePortClass; +typedef struct _PulsePortPrivate PulsePortPrivate; + +struct _PulsePort +{ + MateMixerSwitchOption parent; + + /*< private >*/ + PulsePortPrivate *priv; +}; + +struct _PulsePortClass +{ + MateMixerSwitchOptionClass parent; +}; + +GType pulse_port_get_type (void) G_GNUC_CONST; + +PulsePort * pulse_port_new (const gchar *name, + const gchar *label, + const gchar *icon, + guint priority); + +const gchar *pulse_port_get_name (PulsePort *port); +guint pulse_port_get_priority (PulsePort *port); + +G_END_DECLS + +#endif /* PULSE_PORT_H */ diff --git a/backends/pulse/pulse-sink-control.c b/backends/pulse/pulse-sink-control.c new file mode 100644 index 0000000..500cef0 --- /dev/null +++ b/backends/pulse/pulse-sink-control.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-monitor.h" +#include "pulse-stream-control.h" +#include "pulse-sink.h" +#include "pulse-sink-control.h" + +static void pulse_sink_control_class_init (PulseSinkControlClass *klass); +static void pulse_sink_control_init (PulseSinkControl *control); + +G_DEFINE_TYPE (PulseSinkControl, pulse_sink_control, PULSE_TYPE_STREAM_CONTROL); + +static gboolean pulse_sink_control_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_sink_control_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_sink_control_create_monitor (PulseStreamControl *psc); + +static void +pulse_sink_control_class_init (PulseSinkControlClass *klass) +{ + PulseStreamControlClass *control_class; + + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_sink_control_set_mute; + control_class->set_volume = pulse_sink_control_set_volume; + control_class->create_monitor = pulse_sink_control_create_monitor; +} + +static void +pulse_sink_control_init (PulseSinkControl *control) +{ +} + +PulseSinkControl * +pulse_sink_control_new (PulseSink *sink, + const pa_sink_info *info) +{ + PulseSinkControl *control; + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; + MateMixerStreamControlRole role; + guint32 index; + + g_return_val_if_fail (PULSE_IS_SINK (sink), NULL); + g_return_val_if_fail (info != NULL, NULL); + + if (info->active_port != NULL) + role = MATE_MIXER_STREAM_CONTROL_ROLE_PORT; + else + role = MATE_MIXER_STREAM_CONTROL_ROLE_MASTER; + + /* Build the flag list */ + if (info->flags & PA_SINK_DECIBEL_VOLUME) + flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; + + index = pulse_sink_get_index_monitor (sink); + if (index != PA_INVALID_INDEX) + flags |= MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + + control = g_object_new (PULSE_TYPE_SINK_CONTROL, + "name", info->name, + "label", info->description, + "flags", flags, + "role", role, + "stream", sink, + NULL); + + pulse_sink_control_update (control, info); + return control; +} + +void +pulse_sink_control_update (PulseSinkControl *control, const pa_sink_info *info) +{ + g_return_if_fail (PULSE_IS_SINK_CONTROL (control)); + g_return_if_fail (info != NULL); + + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (control)); + + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (control), + info->mute ? TRUE : FALSE); + + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (control), + &info->channel_map); + + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (control), + &info->volume, + info->base_volume); + + g_object_thaw_notify (G_OBJECT (control)); +} + +static gboolean +pulse_sink_control_set_mute (PulseStreamControl *psc, gboolean mute) +{ + g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), FALSE); + + return pulse_connection_set_sink_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + mute); +} + +static gboolean +pulse_sink_control_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) +{ + g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); + + return pulse_connection_set_sink_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + cvolume); +} + +static PulseMonitor * +pulse_sink_control_create_monitor (PulseStreamControl *psc) +{ + PulseSink *sink; + guint32 index; + + g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), NULL); + + sink = PULSE_SINK (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))); + + index = pulse_sink_get_index_monitor (sink); + if G_UNLIKELY (index == PA_INVALID_INDEX) { + g_debug ("Monitor of stream control %s is not available", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc))); + return NULL; + } + + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + index, + PA_INVALID_INDEX); +} diff --git a/backends/pulse/pulse-sink-control.h b/backends/pulse/pulse-sink-control.h new file mode 100644 index 0000000..e9570f4 --- /dev/null +++ b/backends/pulse/pulse-sink-control.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SINK_CONTROL_H +#define PULSE_SINK_CONTROL_H + +#include <glib.h> +#include <glib-object.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream-control.h" +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SINK_CONTROL \ + (pulse_sink_control_get_type ()) +#define PULSE_SINK_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_CONTROL, PulseSinkControl)) +#define PULSE_IS_SINK_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_CONTROL)) +#define PULSE_SINK_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_CONTROL, PulseSinkControlClass)) +#define PULSE_IS_SINK_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_CONTROL)) +#define PULSE_SINK_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_CONTROL, PulseSinkControlClass)) + +typedef struct _PulseSinkControlClass PulseSinkControlClass; +typedef struct _PulseSinkControlPrivate PulseSinkControlPrivate; + +struct _PulseSinkControl +{ + PulseStreamControl parent; +}; + +struct _PulseSinkControlClass +{ + PulseStreamControlClass parent_class; +}; + +GType pulse_sink_control_get_type (void) G_GNUC_CONST; + +PulseSinkControl *pulse_sink_control_new (PulseSink *sink, + const pa_sink_info *info); + +void pulse_sink_control_update (PulseSinkControl *control, + const pa_sink_info *info); + +G_END_DECLS + +#endif /* PULSE_SINK_CONTROL_H */ diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c index 1d5f9c2..eab85b8 100644 --- a/backends/pulse/pulse-sink-input.c +++ b/backends/pulse/pulse-sink-input.c @@ -15,57 +15,41 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include <string.h> #include <glib.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" -#include "pulse-client-stream.h" #include "pulse-helpers.h" #include "pulse-monitor.h" #include "pulse-sink.h" #include "pulse-sink-input.h" #include "pulse-stream.h" +#include "pulse-stream-control.h" static void pulse_sink_input_class_init (PulseSinkInputClass *klass); static void pulse_sink_input_init (PulseSinkInput *input); -G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_CLIENT_STREAM); - -static void pulse_sink_input_reload (PulseStream *pstream); +G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_STREAM_CONTROL); -static gboolean pulse_sink_input_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_sink_input_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_sink_input_set_parent (PulseClientStream *pclient, - PulseStream *parent); -static gboolean pulse_sink_input_remove (PulseClientStream *pclient); -static PulseMonitor *pulse_sink_input_create_monitor (PulseStream *pstream); +static gboolean pulse_sink_input_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_sink_input_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_sink_input_create_monitor (PulseStreamControl *psc); static void pulse_sink_input_class_init (PulseSinkInputClass *klass) { - PulseStreamClass *stream_class; - PulseClientStreamClass *client_class; - - stream_class = PULSE_STREAM_CLASS (klass); + PulseStreamControlClass *control_class; - stream_class->reload = pulse_sink_input_reload; - stream_class->set_mute = pulse_sink_input_set_mute; - stream_class->set_volume = pulse_sink_input_set_volume; - stream_class->create_monitor = pulse_sink_input_create_monitor; - - client_class = PULSE_CLIENT_STREAM_CLASS (klass); - - client_class->set_parent = pulse_sink_input_set_parent; - client_class->remove = pulse_sink_input_remove; + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_sink_input_set_mute; + control_class->set_volume = pulse_sink_input_set_volume; + control_class->create_monitor = pulse_sink_input_create_monitor; } static void @@ -73,230 +57,171 @@ pulse_sink_input_init (PulseSinkInput *input) { } -PulseStream * -pulse_sink_input_new (PulseConnection *connection, - const pa_sink_input_info *info, - PulseStream *parent) +PulseSinkInput * +pulse_sink_input_new (PulseSink *sink, const pa_sink_input_info *info) { - PulseSinkInput *input; + PulseSinkInput *input; + gchar *name; + const gchar *prop; + const gchar *label = NULL; + MateMixerAppInfo *app_info = NULL; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; - /* Consider the sink input index as unchanging parameter */ - input = g_object_new (PULSE_TYPE_SINK_INPUT, - "connection", connection, - "index", info->index, - NULL); + MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; - /* Other data may change at any time, so let's make a use of our update function */ - pulse_sink_input_update (PULSE_STREAM (input), info, parent); - - return PULSE_STREAM (input); -} - -gboolean -pulse_sink_input_update (PulseStream *pstream, - const pa_sink_input_info *info, - PulseStream *parent) -{ - MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_CLIENT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_MONITOR; - PulseClientStream *pclient; - const gchar *prop; - const gchar *description = NULL; - gchar *name; - - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - pclient = PULSE_CLIENT_STREAM (pstream); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); + g_return_val_if_fail (PULSE_IS_SINK (sink), NULL); + g_return_val_if_fail (info != NULL, NULL); /* Many mixer applications query the Pulse client list and use the client * name here, but we use the name only as an identifier, so let's avoid * this unnecessary overhead and use a custom name. * Also make sure to make the name unique by including the PulseAudio index. */ - name = g_strdup_printf ("pulse-stream-client-output-%lu", (gulong) info->index); - - pulse_stream_update_name (pstream, name); - g_free (name); - - prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); - if (prop != NULL) { - MateMixerClientStreamRole role = pulse_convert_media_role_name (prop); - - if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) { - /* The event description seems to provide much better readable - * description for event streams */ - prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION); - - if (G_LIKELY (prop != NULL)) - description = prop; - } - pulse_client_stream_update_role (pclient, role); - } else - pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE); - - if (description == NULL) - description = info->name; - - pulse_stream_update_description (pstream, description); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); - - if (info->client != PA_INVALID_INDEX) - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION); - else - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS); - - if (G_LIKELY (parent != NULL)) { - if (pulse_sink_get_monitor_index (parent) != PA_INVALID_INDEX) - flags |= MATE_MIXER_STREAM_HAS_MONITOR; - - pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent)); - } else - pulse_client_stream_update_parent (pclient, NULL); + name = g_strdup_printf ("pulse-output-control-%lu", (gulong) info->index); #if PA_CHECK_VERSION(1, 0, 0) if (info->has_volume) { flags |= - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; if (info->volume_writable) - flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; } - - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); - pulse_stream_update_channel_map (pstream, &info->channel_map); - - if (info->has_volume) - pulse_stream_update_volume (pstream, &info->volume, 0); - else - pulse_stream_update_volume (pstream, NULL, 0); #else /* Pre-1.0 PulseAudio does not include the has_volume and volume_writable * fields, but does include the volume info, so let's give it a try */ flags |= - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME; + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; +#endif - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); - pulse_stream_update_channel_map (pstream, &info->channel_map); + if (info->client != PA_INVALID_INDEX) { + app_info = _mate_mixer_app_info_new (); - pulse_stream_update_volume (pstream, &info->volume, 0); -#endif + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); - if (prop != NULL) - pulse_client_stream_update_app_name (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_name (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); - if (prop != NULL) - pulse_client_stream_update_app_id (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); + if (prop != NULL) + _mate_mixer_app_info_set_id (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); - if (prop != NULL) - pulse_client_stream_update_app_version (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); + if (prop != NULL) + _mate_mixer_app_info_set_version (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); - if (prop != NULL) - pulse_client_stream_update_app_icon (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_icon (app_info, prop); + } - // XXX needs to fix monitor if parent changes + prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); + if (prop != NULL) { + media_role = pulse_convert_media_role_name (prop); - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} + if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT) { + /* The event description seems to provide much better readable + * description for event streams */ + prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION); + if (prop != NULL) + label = prop; + } + } -static void -pulse_sink_input_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SINK_INPUT (pstream)); + if (label == NULL) + label = info->name; - pulse_connection_load_sink_input_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); -} + input = g_object_new (PULSE_TYPE_SINK_INPUT, + "name", name, + "label", label, + "flags", flags, + "role", role, + "media-role", media_role, + "index", info->index, + "stream", sink, + NULL); + g_free (name); -static gboolean -pulse_sink_input_set_mute (PulseStream *pstream, gboolean mute) -{ - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE); + if (app_info != NULL) + pulse_stream_control_set_app_info (PULSE_STREAM_CONTROL (input), app_info); - return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mute); + pulse_sink_input_update (input, info); + return input; } -static gboolean -pulse_sink_input_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +void +pulse_sink_input_update (PulseSinkInput *input, const pa_sink_input_info *info) { - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + g_return_if_fail (PULSE_IS_SINK_INPUT (input)); + g_return_if_fail (info != NULL); - return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - cvolume); -} + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (input)); -static gboolean -pulse_sink_input_set_parent (PulseClientStream *pclient, PulseStream *parent) -{ - PulseStream *pstream; + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (input), + info->mute ? TRUE : FALSE); - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE); +#if PA_CHECK_VERSION(1, 0, 0) + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input), &info->channel_map); - pstream = PULSE_STREAM (pclient); + if (info->has_volume) + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input), &info->volume, 0); + else + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input), NULL, 0); +#else + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input), &info->channel_map); + pulse_stream_control_set_volume (PULSE_STREAM_CONTROL (input), &info->volume, 0); +#endif - return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - pulse_stream_get_index (parent)); + g_object_thaw_notify (G_OBJECT (input)); } static gboolean -pulse_sink_input_remove (PulseClientStream *pclient) +pulse_sink_input_set_mute (PulseStreamControl *psc, gboolean mute) { - PulseStream *pstream; + g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), FALSE); - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE); + return pulse_connection_set_sink_input_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), + mute); +} - pstream = PULSE_STREAM (pclient); +static gboolean +pulse_sink_input_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) +{ + g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); - return pulse_connection_kill_sink_input (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + return pulse_connection_set_sink_input_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), + cvolume); } static PulseMonitor * -pulse_sink_input_create_monitor (PulseStream *pstream) +pulse_sink_input_create_monitor (PulseStreamControl *psc) { - MateMixerStream *parent; - guint32 index; - - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), NULL); + PulseSink *sink; + guint32 index; - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (G_UNLIKELY (parent == NULL)) { - g_debug ("Not creating monitor for client stream %s as it is not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); - return NULL; - } + g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), NULL); - index = pulse_sink_get_monitor_index (PULSE_STREAM (parent)); + sink = PULSE_SINK (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))); - if (G_UNLIKELY (index == PA_INVALID_INDEX)) { - g_debug ("Not creating monitor for client stream %s as it is not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); + index = pulse_sink_get_index_monitor (sink); + if G_UNLIKELY (index == PA_INVALID_INDEX) { + g_debug ("Monitor of stream control %s is not available", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc))); return NULL; } - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), index, - pulse_stream_get_index (pstream)); + pulse_stream_control_get_index (psc)); } diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h index 1e5004b..127eab6 100644 --- a/backends/pulse/pulse-sink-input.h +++ b/backends/pulse/pulse-sink-input.h @@ -23,9 +23,8 @@ #include <pulse/pulseaudio.h> -#include "pulse-client-stream.h" -#include "pulse-connection.h" -#include "pulse-stream.h" +#include "pulse-stream-control.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,28 +41,25 @@ G_BEGIN_DECLS #define PULSE_SINK_INPUT_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass)) -typedef struct _PulseSinkInput PulseSinkInput; typedef struct _PulseSinkInputClass PulseSinkInputClass; struct _PulseSinkInput { - PulseClientStream parent; + PulseStreamControl parent; }; struct _PulseSinkInputClass { - PulseClientStreamClass parent_class; + PulseStreamControlClass parent_class; }; -GType pulse_sink_input_get_type (void) G_GNUC_CONST; +GType pulse_sink_input_get_type (void) G_GNUC_CONST; -PulseStream *pulse_sink_input_new (PulseConnection *connection, - const pa_sink_input_info *info, - PulseStream *parent); +PulseSinkInput *pulse_sink_input_new (PulseSink *sink, + const pa_sink_input_info *info); -gboolean pulse_sink_input_update (PulseStream *pstream, - const pa_sink_input_info *info, - PulseStream *parent); +void pulse_sink_input_update (PulseSinkInput *input, + const pa_sink_input_info *info); G_END_DECLS diff --git a/backends/pulse/pulse-sink-switch.c b/backends/pulse/pulse-sink-switch.c new file mode 100644 index 0000000..0e08dac --- /dev/null +++ b/backends/pulse/pulse-sink-switch.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" +#include "pulse-sink-switch.h" +#include "pulse-stream.h" + +static void pulse_sink_switch_class_init (PulseSinkSwitchClass *klass); +static void pulse_sink_switch_init (PulseSinkSwitch *swtch); + +G_DEFINE_TYPE (PulseSinkSwitch, pulse_sink_switch, PULSE_TYPE_PORT_SWITCH) + +static gboolean pulse_sink_switch_set_active_port (PulsePortSwitch *swtch, + PulsePort *port); + +static void +pulse_sink_switch_class_init (PulseSinkSwitchClass *klass) +{ + PulsePortSwitchClass *switch_class; + + switch_class = PULSE_PORT_SWITCH_CLASS (klass); + switch_class->set_active_port = pulse_sink_switch_set_active_port; +} + +static void +pulse_sink_switch_init (PulseSinkSwitch *swtch) +{ +} + +PulsePortSwitch * +pulse_sink_switch_new (const gchar *name, const gchar *label, PulseSink *sink) +{ + return g_object_new (PULSE_TYPE_SINK_SWITCH, + "name", name, + "label", label, + "role", MATE_MIXER_SWITCH_ROLE_PORT, + "stream", sink, + NULL); +} + +static gboolean +pulse_sink_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port) +{ + PulseStream *stream; + + g_return_val_if_fail (PULSE_IS_SINK_SWITCH (swtch), FALSE); + g_return_val_if_fail (PULSE_IS_PORT (port), FALSE); + + stream = pulse_port_switch_get_stream (swtch); + + return pulse_connection_set_sink_port (pulse_stream_get_connection (stream), + pulse_stream_get_index (stream), + pulse_port_get_name (port)); +} diff --git a/backends/pulse/pulse-sink-switch.h b/backends/pulse/pulse-sink-switch.h new file mode 100644 index 0000000..71ed4c9 --- /dev/null +++ b/backends/pulse/pulse-sink-switch.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SINK_SWITCH_H +#define PULSE_SINK_SWITCH_H + +#include <glib.h> +#include <glib-object.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SINK_SWITCH \ + (pulse_sink_switch_get_type ()) +#define PULSE_SINK_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitch)) +#define PULSE_IS_SINK_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_SWITCH)) +#define PULSE_SINK_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitchClass)) +#define PULSE_IS_SINK_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_SWITCH)) +#define PULSE_SINK_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitchClass)) + +typedef struct _PulseSinkSwitchClass PulseSinkSwitchClass; +typedef struct _PulseSinkSwitchPrivate PulseSinkSwitchPrivate; + +struct _PulseSinkSwitch +{ + PulsePortSwitch parent; +}; + +struct _PulseSinkSwitchClass +{ + PulsePortSwitchClass parent_class; +}; + +GType pulse_sink_switch_get_type (void) G_GNUC_CONST; + +PulsePortSwitch *pulse_sink_switch_new (const gchar *name, + const gchar *label, + PulseSink *sink); + +G_END_DECLS + +#endif /* PULSE_SINK_SWITCH_H */ diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c index 0f828b1..d2f0280 100644 --- a/backends/pulse/pulse-sink.c +++ b/backends/pulse/pulse-sink.c @@ -16,62 +16,57 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" #include "pulse-monitor.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" #include "pulse-stream.h" #include "pulse-sink.h" +#include "pulse-sink-control.h" +#include "pulse-sink-input.h" +#include "pulse-sink-switch.h" struct _PulseSinkPrivate { - guint32 index_monitor; + guint32 monitor; + GHashTable *inputs; + PulsePortSwitch *pswitch; + GList *streams_list; + GList *switches_list; + PulseSinkControl *control; }; static void pulse_sink_class_init (PulseSinkClass *klass); static void pulse_sink_init (PulseSink *sink); +static void pulse_sink_dispose (GObject *object); +static void pulse_sink_finalize (GObject *object); G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM); -static void pulse_sink_reload (PulseStream *pstream); - -static gboolean pulse_sink_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_sink_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_sink_set_active_port (PulseStream *pstream, - MateMixerPort *port); - -static gboolean pulse_sink_suspend (PulseStream *pstream); -static gboolean pulse_sink_resume (PulseStream *pstream); - -static PulseMonitor *pulse_sink_create_monitor (PulseStream *pstream); - -static void update_ports (PulseStream *pstream, - pa_sink_port_info **ports, - pa_sink_port_info *active); +static const GList *pulse_sink_list_controls (MateMixerStream *mms); +static const GList *pulse_sink_list_switches (MateMixerStream *mms); static void pulse_sink_class_init (PulseSinkClass *klass) { - PulseStreamClass *stream_class; + GObjectClass *object_class; + MateMixerStreamClass *stream_class; - stream_class = PULSE_STREAM_CLASS (klass); + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_sink_dispose; + object_class->finalize = pulse_sink_finalize; - stream_class->reload = pulse_sink_reload; - stream_class->set_mute = pulse_sink_set_mute; - stream_class->set_volume = pulse_sink_set_volume; - stream_class->set_active_port = pulse_sink_set_active_port; - stream_class->suspend = pulse_sink_suspend; - stream_class->resume = pulse_sink_resume; - stream_class->create_monitor = pulse_sink_create_monitor; + stream_class = MATE_MIXER_STREAM_CLASS (klass); + stream_class->list_controls = pulse_sink_list_controls; + stream_class->list_switches = pulse_sink_list_switches; g_type_class_add_private (klass, sizeof (PulseSinkPrivate)); } @@ -83,247 +78,190 @@ pulse_sink_init (PulseSink *sink) PULSE_TYPE_SINK, PulseSinkPrivate); - sink->priv->index_monitor = PA_INVALID_INDEX; + sink->priv->inputs = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + + sink->priv->monitor = PA_INVALID_INDEX; } -PulseStream * -pulse_sink_new (PulseConnection *connection, - const pa_sink_info *info, - PulseDevice *device) +static void +pulse_sink_dispose (GObject *object) { - PulseStream *stream; + PulseSink *sink; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); + sink = PULSE_SINK (object); - /* Consider the sink index as unchanging parameter */ - stream = g_object_new (PULSE_TYPE_SINK, - "connection", connection, - "index", info->index, - NULL); + g_clear_object (&sink->priv->control); + g_clear_object (&sink->priv->pswitch); - /* Other data may change at any time, so let's make a use of our update function */ - pulse_sink_update (stream, info, device); + g_hash_table_remove_all (sink->priv->inputs); - return stream; + G_OBJECT_CLASS (pulse_sink_parent_class)->dispose (object); } -guint32 -pulse_sink_get_monitor_index (PulseStream *pstream) +static void +pulse_sink_finalize (GObject *object) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), PA_INVALID_INDEX); + PulseSink *sink; + + sink = PULSE_SINK (object); - return PULSE_SINK (pstream)->priv->index_monitor; + g_hash_table_unref (sink->priv->inputs); + + G_OBJECT_CLASS (pulse_sink_parent_class)->finalize (object); } -gboolean -pulse_sink_update (PulseStream *pstream, const pa_sink_info *info, PulseDevice *device) +PulseSink * +pulse_sink_new (PulseConnection *connection, + const pa_sink_info *info, + PulseDevice *device) { - MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME | - MATE_MIXER_STREAM_CAN_SUSPEND; PulseSink *sink; - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); - - pulse_stream_update_name (pstream, info->name); - pulse_stream_update_description (pstream, info->description); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); - - /* Stream state */ - switch (info->state) { - case PA_SINK_RUNNING: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING); - break; - case PA_SINK_IDLE: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); - break; - case PA_SINK_SUSPENDED: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); - break; - default: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN); - break; - } - - /* Build the flag list */ - if (info->flags & PA_SINK_DECIBEL_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; - if (info->flags & PA_SINK_FLAT_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; - - sink = PULSE_SINK (pstream); - - if (sink->priv->index_monitor == PA_INVALID_INDEX) - sink->priv->index_monitor = info->monitor_source; + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); - if (sink->priv->index_monitor != PA_INVALID_INDEX) - flags |= MATE_MIXER_STREAM_HAS_MONITOR; + sink = g_object_new (PULSE_TYPE_SINK, + "name", info->name, + "label", info->description, + "device", device, + "direction", MATE_MIXER_DIRECTION_OUTPUT, + "connection", connection, + "index", info->index, + NULL); + + sink->priv->control = pulse_sink_control_new (sink, info); + + if (info->n_ports > 0) { + pa_sink_port_info **ports = info->ports; + + /* Create the port switch */ + sink->priv->pswitch = pulse_sink_switch_new ("port", _("Port"), sink); + + while (*ports != NULL) { + pa_sink_port_info *p = *ports++; + PulsePort *port; + const gchar *icon = NULL; + + /* A port may include an icon but in PulseAudio sink and source ports + * the property is not included, for this reason ports are also read from + * devices where the icons may be present */ + if (device != NULL) { + port = pulse_device_get_port (PULSE_DEVICE (device), p->name); + if (port != NULL) + icon = mate_mixer_switch_option_get_icon (MATE_MIXER_SWITCH_OPTION (port)); + } - /* Flags must be updated before volume */ - pulse_stream_update_flags (pstream, flags); + port = pulse_port_new (p->name, + p->description, + icon, + p->priority); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, &info->volume, info->base_volume); + pulse_port_switch_add_port (sink->priv->pswitch, port); - pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device)); + if (p == info->active_port) + pulse_port_switch_set_active_port (sink->priv->pswitch, port); + } - /* Ports must be updated after device */ - if (info->ports != NULL) { - update_ports (pstream, info->ports, info->active_port); + g_debug ("Created port list for sink %s", info->name); } - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} - -static void -pulse_sink_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SINK (pstream)); + pulse_sink_update (sink, info); - pulse_connection_load_sink_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (sink), + MATE_MIXER_STREAM_CONTROL (sink->priv->control)); + return sink; } -static gboolean -pulse_sink_set_mute (PulseStream *pstream, gboolean mute) +void +pulse_sink_add_input (PulseSink *sink, const pa_sink_input_info *info) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - - return pulse_connection_set_sink_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mute); + PulseSinkInput *input; + + /* This function is used for both creating and refreshing sink inputs */ + input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (info->index)); + if (input == NULL) { + const gchar *name; + + input = pulse_sink_input_new (sink, info); + g_hash_table_insert (sink->priv->inputs, + GINT_TO_POINTER (info->index), + input); + + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input)); + g_signal_emit_by_name (G_OBJECT (sink), + "control-added", + name); + } else + pulse_sink_input_update (input, info); } -static gboolean -pulse_sink_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +void +pulse_sink_remove_input (PulseSink *sink, guint32 index) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + PulseSinkInput *input; + const gchar *name; - return pulse_connection_set_sink_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - cvolume); -} + input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (index)); + if G_UNLIKELY (input == NULL) + return; -static gboolean -pulse_sink_set_active_port (PulseStream *pstream, MateMixerPort *port) -{ - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input)); - return pulse_connection_set_sink_port (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mate_mixer_port_get_name (port)); + g_hash_table_remove (sink->priv->inputs, GINT_TO_POINTER (index)); + g_signal_emit_by_name (G_OBJECT (sink), + "control-removed", + name); } -static gboolean -pulse_sink_suspend (PulseStream *pstream) +void +pulse_sink_update (PulseSink *sink, const pa_sink_info *info) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); + g_return_if_fail (PULSE_IS_SINK (sink)); + g_return_if_fail (info != NULL); - return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - TRUE); + /* The switch doesn't allow being unset, PulseAudio should always include + * the active port name if the are any ports available */ + if (info->active_port != NULL) + pulse_port_switch_set_active_port_by_name (sink->priv->pswitch, + info->active_port->name); + + sink->priv->monitor = info->monitor_source; } -static gboolean -pulse_sink_resume (PulseStream *pstream) +guint32 +pulse_sink_get_index_monitor (PulseSink *sink) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); + g_return_val_if_fail (PULSE_IS_SINK (sink), 0); - return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - FALSE); + return sink->priv->monitor; } -static PulseMonitor * -pulse_sink_create_monitor (PulseStream *pstream) +static const GList * +pulse_sink_list_controls (MateMixerStream *mms) { - guint32 index; + GList *list; - g_return_val_if_fail (PULSE_IS_SINK (pstream), NULL); + g_return_val_if_fail (PULSE_IS_SINK (mms), NULL); - index = pulse_sink_get_monitor_index (pstream); + // XXX + list = g_hash_table_get_values (PULSE_SINK (mms)->priv->inputs); + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); - if (G_UNLIKELY (index == PA_INVALID_INDEX)) { - g_debug ("Not creating monitor for stream %s: not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); - return NULL; - } - - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), - index, - PA_INVALID_INDEX); + return g_list_prepend (list, g_object_ref (PULSE_SINK (mms)->priv->control)); } -static void -update_ports (PulseStream *pstream, - pa_sink_port_info **ports, - pa_sink_port_info *active) +static const GList * +pulse_sink_list_switches (MateMixerStream *mms) { - MateMixerPort *port; - MateMixerDevice *device; - GHashTable *hash; - - hash = pulse_stream_get_ports (pstream); - - while (*ports != NULL) { - MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS; - pa_sink_port_info *info = *ports; - const gchar *icon = NULL; - - device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream)); - if (device != NULL) { - port = mate_mixer_device_get_port (device, info->name); - - if (port != NULL) { - flags = mate_mixer_port_get_flags (port); - icon = mate_mixer_port_get_icon (port); - } - } - -#if PA_CHECK_VERSION(2, 0, 0) - if (info->available == PA_PORT_AVAILABLE_YES) - flags |= MATE_MIXER_PORT_AVAILABLE; - else - flags &= ~MATE_MIXER_PORT_AVAILABLE; -#endif - - port = g_hash_table_lookup (hash, info->name); - - if (port != NULL) { - /* Update existing port */ - _mate_mixer_port_update_description (port, info->description); - _mate_mixer_port_update_icon (port, icon); - _mate_mixer_port_update_priority (port, info->priority); - _mate_mixer_port_update_flags (port, flags); - } else { - /* Add previously unknown port to the hash table */ - port = _mate_mixer_port_new (info->name, - info->description, - icon, - info->priority, - flags); - - g_hash_table_insert (hash, g_strdup (info->name), port); - } - - ports++; - } + g_return_val_if_fail (PULSE_IS_SINK (mms), NULL); - /* Active port */ - if (G_LIKELY (active != NULL)) - port = g_hash_table_lookup (hash, active->name); - else - port = NULL; + // XXX + if (PULSE_SINK (mms)->priv->pswitch != NULL) + return g_list_prepend (NULL, PULSE_SINK (mms)->priv->pswitch); - pulse_stream_update_active_port (pstream, port); + return NULL; } diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h index c0631ca..5eaeaa0 100644 --- a/backends/pulse/pulse-sink.h +++ b/backends/pulse/pulse-sink.h @@ -23,9 +23,8 @@ #include <pulse/pulseaudio.h> -#include "pulse-connection.h" -#include "pulse-device.h" #include "pulse-stream.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,7 +41,6 @@ G_BEGIN_DECLS #define PULSE_SINK_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK, PulseSinkClass)) -typedef struct _PulseSink PulseSink; typedef struct _PulseSinkClass PulseSinkClass; typedef struct _PulseSinkPrivate PulseSinkPrivate; @@ -59,17 +57,21 @@ struct _PulseSinkClass PulseStreamClass parent_class; }; -GType pulse_sink_get_type (void) G_GNUC_CONST; +GType pulse_sink_get_type (void) G_GNUC_CONST; -PulseStream *pulse_sink_new (PulseConnection *connection, - const pa_sink_info *info, - PulseDevice *device); +PulseSink *pulse_sink_new (PulseConnection *connection, + const pa_sink_info *info, + PulseDevice *device); -guint32 pulse_sink_get_monitor_index (PulseStream *pstream); +void pulse_sink_add_input (PulseSink *sink, + const pa_sink_input_info *info); -gboolean pulse_sink_update (PulseStream *pstream, - const pa_sink_info *info, - PulseDevice *device); +void pulse_sink_remove_input (PulseSink *sink, guint32 index); + +void pulse_sink_update (PulseSink *sink, + const pa_sink_info *info); + +guint32 pulse_sink_get_index_monitor (PulseSink *sink); G_END_DECLS diff --git a/backends/pulse/pulse-source-control.c b/backends/pulse/pulse-source-control.c new file mode 100644 index 0000000..3ed1573 --- /dev/null +++ b/backends/pulse/pulse-source-control.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-monitor.h" +#include "pulse-stream-control.h" +#include "pulse-source.h" +#include "pulse-source-control.h" + +static void pulse_source_control_class_init (PulseSourceControlClass *klass); +static void pulse_source_control_init (PulseSourceControl *control); + +G_DEFINE_TYPE (PulseSourceControl, pulse_source_control, PULSE_TYPE_STREAM_CONTROL); + +static gboolean pulse_source_control_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_source_control_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_source_control_create_monitor (PulseStreamControl *psc); + +static void +pulse_source_control_class_init (PulseSourceControlClass *klass) +{ + PulseStreamControlClass *control_class; + + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_source_control_set_mute; + control_class->set_volume = pulse_source_control_set_volume; + control_class->create_monitor = pulse_source_control_create_monitor; +} + +static void +pulse_source_control_init (PulseSourceControl *control) +{ +} + +PulseSourceControl * +pulse_source_control_new (PulseSource *source, + const pa_source_info *info) +{ + PulseSourceControl *control; + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + MateMixerStreamControlRole role; + + g_return_val_if_fail (PULSE_IS_SOURCE (source), NULL); + g_return_val_if_fail (info != NULL, NULL); + + if (info->active_port != NULL) + role = MATE_MIXER_STREAM_CONTROL_ROLE_PORT; + else + role = MATE_MIXER_STREAM_CONTROL_ROLE_MASTER; + + /* Build the flag list */ + if (info->flags & PA_SOURCE_DECIBEL_VOLUME) + flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; + + control = g_object_new (PULSE_TYPE_SOURCE_CONTROL, + "name", info->name, + "label", info->description, + "flags", flags, + "role", role, + "stream", source, + NULL); + + pulse_source_control_update (control, info); + return control; +} + +void +pulse_source_control_update (PulseSourceControl *control, const pa_source_info *info) +{ + g_return_if_fail (PULSE_IS_SOURCE_CONTROL (control)); + g_return_if_fail (info != NULL); + + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (control)); + + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (control), + info->mute ? TRUE : FALSE); + + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (control), + &info->channel_map); + + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (control), + &info->volume, + info->base_volume); + + g_object_thaw_notify (G_OBJECT (control)); +} + +static gboolean +pulse_source_control_set_mute (PulseStreamControl *psc, gboolean mute) +{ + g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), FALSE); + + return pulse_connection_set_source_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + mute); +} + +static gboolean +pulse_source_control_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) +{ + g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); + + return pulse_connection_set_source_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + cvolume); +} + +static PulseMonitor * +pulse_source_control_create_monitor (PulseStreamControl *psc) +{ + guint32 index; + + g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), NULL); + + index = PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc); + if G_UNLIKELY (index == PA_INVALID_INDEX) { + g_debug ("Monitor of stream control %s is not available", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc))); + return NULL; + } + + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + index, + PA_INVALID_INDEX); +} diff --git a/backends/pulse/pulse-source-control.h b/backends/pulse/pulse-source-control.h new file mode 100644 index 0000000..a8d659f --- /dev/null +++ b/backends/pulse/pulse-source-control.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_CONTROL_H +#define PULSE_SOURCE_CONTROL_H + +#include <glib.h> +#include <glib-object.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream-control.h" +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE_CONTROL \ + (pulse_source_control_get_type ()) +#define PULSE_SOURCE_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControl)) +#define PULSE_IS_SOURCE_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_CONTROL)) +#define PULSE_SOURCE_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControlClass)) +#define PULSE_IS_SOURCE_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_CONTROL)) +#define PULSE_SOURCE_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControlClass)) + +typedef struct _PulseSourceControlClass PulseSourceControlClass; +typedef struct _PulseSourceControlPrivate PulseSourceControlPrivate; + +struct _PulseSourceControl +{ + PulseStreamControl parent; +}; + +struct _PulseSourceControlClass +{ + PulseStreamControlClass parent_class; +}; + +GType pulse_source_control_get_type (void) G_GNUC_CONST; + +PulseSourceControl *pulse_source_control_new (PulseSource *source, + const pa_source_info *info); + +void pulse_source_control_update (PulseSourceControl *control, + const pa_source_info *info); + +G_END_DECLS + +#endif /* PULSE_SOURCE_CONTROL_H */ diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c index 6cbd888..69fc3e4 100644 --- a/backends/pulse/pulse-source-output.c +++ b/backends/pulse/pulse-source-output.c @@ -17,56 +17,39 @@ #include <glib.h> #include <glib-object.h> -#include <string.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" -#include "pulse-client-stream.h" #include "pulse-helpers.h" #include "pulse-monitor.h" -#include "pulse-stream.h" #include "pulse-source.h" #include "pulse-source-output.h" +#include "pulse-stream.h" +#include "pulse-stream-control.h" static void pulse_source_output_class_init (PulseSourceOutputClass *klass); static void pulse_source_output_init (PulseSourceOutput *output); -G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_CLIENT_STREAM); - -static void pulse_source_output_reload (PulseStream *pstream); - -static gboolean pulse_source_output_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_source_output_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); +G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM_CONTROL); -static gboolean pulse_source_output_set_parent (PulseClientStream *pclient, - PulseStream *parent); -static gboolean pulse_source_output_remove (PulseClientStream *pclient); - -static PulseMonitor *pulse_source_output_create_monitor (PulseStream *pstream); +static gboolean pulse_source_output_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_source_output_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_source_output_create_monitor (PulseStreamControl *psc); static void pulse_source_output_class_init (PulseSourceOutputClass *klass) { - PulseStreamClass *stream_class; - PulseClientStreamClass *client_class; - - stream_class = PULSE_STREAM_CLASS (klass); - - stream_class->reload = pulse_source_output_reload; - stream_class->set_mute = pulse_source_output_set_mute; - stream_class->set_volume = pulse_source_output_set_volume; - stream_class->create_monitor = pulse_source_output_create_monitor; - - client_class = PULSE_CLIENT_STREAM_CLASS (klass); + PulseStreamControlClass *control_class; - client_class->set_parent = pulse_source_output_set_parent; - client_class->remove = pulse_source_output_remove; + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_source_output_set_mute; + control_class->set_volume = pulse_source_output_set_volume; + control_class->create_monitor = pulse_source_output_create_monitor; } static void @@ -74,210 +57,157 @@ pulse_source_output_init (PulseSourceOutput *output) { } -PulseStream * -pulse_source_output_new (PulseConnection *connection, - const pa_source_output_info *info, - PulseStream *parent) +PulseSourceOutput * +pulse_source_output_new (PulseSource *source, + const pa_source_output_info *info) { PulseSourceOutput *output; + gchar *name; + const gchar *prop; + MateMixerAppInfo *app_info = NULL; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); - - /* Consider the sink input index as unchanging parameter */ - output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT, - "connection", connection, - "index", info->index, - NULL); - - /* Other data may change at any time, so let's make a use of our update function */ - pulse_source_output_update (PULSE_STREAM (output), info, parent); + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; - return PULSE_STREAM (output); -} + MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; -gboolean -pulse_source_output_update (PulseStream *pstream, - const pa_source_output_info *info, - PulseStream *parent) -{ - MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | - MATE_MIXER_STREAM_CLIENT; - PulseClientStream *pclient; - const gchar *prop; - const gchar *description = NULL; - gchar *name; + g_return_val_if_fail (PULSE_IS_SOURCE (source), NULL); + g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); + /* Many mixer applications query the Pulse client list and use the client + * name here, but we use the name only as an identifier, so let's avoid + * this unnecessary overhead and use a custom name. + * Also make sure to make the name unique by including the PulseAudio index. */ + name = g_strdup_printf ("pulse-input-control-%lu", (gulong) info->index); - pclient = PULSE_CLIENT_STREAM (pstream); +#if PA_CHECK_VERSION(1, 0, 0) + if (info->has_volume) { + flags |= + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); + if (info->volume_writable) + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; + } +#else + /* Pre-1.0 PulseAudio does not include the has_volume and volume_writable + * fields, but does include the volume info, so let's give it a try */ + flags |= + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; +#endif - /* Many other mixer applications query the Pulse client list and use the - * client name here, but we use the name only as an identifier, so let's avoid - * this unnecessary overhead and use a custom name. - * Also make sure to make the name unique by including the Pulse index. */ - name = g_strdup_printf ("pulse-stream-client-input-%lu", (gulong) info->index); + if (info->client != PA_INVALID_INDEX) { + app_info = _mate_mixer_app_info_new (); - pulse_stream_update_name (pstream, name); - g_free (name); + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); - if (prop != NULL) - pulse_client_stream_update_app_name (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_name (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); - if (prop != NULL) - pulse_client_stream_update_app_id (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); + if (prop != NULL) + _mate_mixer_app_info_set_id (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); - if (prop != NULL) - pulse_client_stream_update_app_version (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); + if (prop != NULL) + _mate_mixer_app_info_set_version (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); - if (prop != NULL) - pulse_client_stream_update_app_icon (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_icon (app_info, prop); + } prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); - if (prop != NULL) { - MateMixerClientStreamRole role = pulse_convert_media_role_name (prop); + if (prop != NULL) + media_role = pulse_convert_media_role_name (prop); - if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) { - /* The event description seems to provide much better readable - * description for event streams */ - prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION); + output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT, + "name", name, + "label", info->name, + "flags", flags, + "role", role, + "media-role", media_role, + "index", info->index, + "stream", source, + NULL); + g_free (name); - if (G_LIKELY (prop != NULL)) - description = prop; - } - pulse_client_stream_update_role (pclient, role); - } else - pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE); + if (app_info != NULL) + pulse_stream_control_set_app_info (PULSE_STREAM_CONTROL (output), app_info); - if (description == NULL) - description = info->name; + pulse_source_output_update (output, info); + return output; +} - pulse_stream_update_description (pstream, description); +void +pulse_source_output_update (PulseSourceOutput *output, + const pa_source_output_info *info) +{ + g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (output)); + g_return_if_fail (info != NULL); - if (info->client != PA_INVALID_INDEX) - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION); - else - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS); + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (output)); - if (G_LIKELY (parent != NULL)) { - pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent)); - flags |= MATE_MIXER_STREAM_HAS_MONITOR; - } else - pulse_client_stream_update_parent (pclient, NULL); + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (output), + info->mute ? TRUE : FALSE); #if PA_CHECK_VERSION(1, 0, 0) - if (info->has_volume) { - flags |= MATE_MIXER_STREAM_HAS_VOLUME; - - if (info->volume_writable) - flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; - } - - flags |= MATE_MIXER_STREAM_HAS_MUTE; - - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output), + &info->channel_map); if (info->has_volume) - pulse_stream_update_volume (pstream, &info->volume, 0); + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output), + &info->volume, + 0); else - pulse_stream_update_volume (pstream, NULL, 0); + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output), + NULL, + 0); #else - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output), + &info->channel_map); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, NULL, 0); + pulse_stream_control_set_volume (PULSE_STREAM_CONTROL (output), + &info->volume, + 0); #endif - // XXX needs to fix monitor if parent changes - - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} - -static void -pulse_source_output_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream)); - - pulse_connection_load_source_output_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + g_object_thaw_notify (G_OBJECT (output)); } static gboolean -pulse_source_output_set_mute (PulseStream *pstream, gboolean mute) +pulse_source_output_set_mute (PulseStreamControl *psc, gboolean mute) { - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE); + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE); - return pulse_connection_set_source_output_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), + return pulse_connection_set_source_output_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), mute); } static gboolean -pulse_source_output_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +pulse_source_output_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) { - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE); + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE); g_return_val_if_fail (cvolume != NULL, FALSE); - return pulse_connection_set_source_output_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), + return pulse_connection_set_source_output_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), cvolume); } -static gboolean -pulse_source_output_set_parent (PulseClientStream *pclient, PulseStream *parent) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE); - - pstream = PULSE_STREAM (pclient); - - return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - pulse_stream_get_index (parent)); -} - -static gboolean -pulse_source_output_remove (PulseClientStream *pclient) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE); - - pstream = PULSE_STREAM (pclient); - - return pulse_connection_kill_source_output (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); -} - static PulseMonitor * -pulse_source_output_create_monitor (PulseStream *pstream) +pulse_source_output_create_monitor (PulseStreamControl *psc) { - MateMixerStream *parent; - - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), NULL); - - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (G_UNLIKELY (parent == NULL)) { - g_debug ("Not creating monitor for client stream %s: not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); - return NULL; - } + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), NULL); - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), - pulse_stream_get_index (PULSE_STREAM (parent)), + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), PA_INVALID_INDEX); } diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h index 845d439..1037f94 100644 --- a/backends/pulse/pulse-source-output.h +++ b/backends/pulse/pulse-source-output.h @@ -23,48 +23,43 @@ #include <pulse/pulseaudio.h> -#include "pulse-client-stream.h" -#include "pulse-connection.h" -#include "pulse-stream.h" +#include "pulse-stream-control.h" +#include "pulse-types.h" G_BEGIN_DECLS -#define PULSE_TYPE_SOURCE_OUTPUT \ +#define PULSE_TYPE_SOURCE_OUTPUT \ (pulse_source_output_get_type ()) -#define PULSE_SOURCE_OUTPUT(o) \ +#define PULSE_SOURCE_OUTPUT(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutput)) -#define PULSE_IS_SOURCE_OUTPUT(o) \ +#define PULSE_IS_SOURCE_OUTPUT(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_OUTPUT)) -#define PULSE_SOURCE_OUTPUT_CLASS(k) \ +#define PULSE_SOURCE_OUTPUT_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) -#define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \ +#define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT)) -#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \ +#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) -typedef struct _PulseSourceOutput PulseSourceOutput; -typedef struct _PulseSourceOutputClass PulseSourceOutputClass; -typedef struct _PulseSourceOutputPrivate PulseSourceOutputPrivate; +typedef struct _PulseSourceOutputClass PulseSourceOutputClass; struct _PulseSourceOutput { - PulseClientStream parent; + PulseStreamControl parent; }; struct _PulseSourceOutputClass { - PulseClientStreamClass parent_class; + PulseStreamControlClass parent_class; }; -GType pulse_source_output_get_type (void) G_GNUC_CONST; +GType pulse_source_output_get_type (void) G_GNUC_CONST; -PulseStream *pulse_source_output_new (PulseConnection *connection, - const pa_source_output_info *info, - PulseStream *parent); +PulseSourceOutput *pulse_source_output_new (PulseSource *source, + const pa_source_output_info *info); -gboolean pulse_source_output_update (PulseStream *pstream, - const pa_source_output_info *info, - PulseStream *parent); +void pulse_source_output_update (PulseSourceOutput *output, + const pa_source_output_info *info); G_END_DECLS diff --git a/backends/pulse/pulse-source-switch.c b/backends/pulse/pulse-source-switch.c new file mode 100644 index 0000000..178702e --- /dev/null +++ b/backends/pulse/pulse-source-switch.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" +#include "pulse-source-switch.h" +#include "pulse-stream.h" + +static void pulse_source_switch_class_init (PulseSourceSwitchClass *klass); +static void pulse_source_switch_init (PulseSourceSwitch *swtch); + +G_DEFINE_TYPE (PulseSourceSwitch, pulse_source_switch, PULSE_TYPE_PORT_SWITCH) + +static gboolean pulse_source_switch_set_active_port (PulsePortSwitch *swtch, + PulsePort *port); + +static void +pulse_source_switch_class_init (PulseSourceSwitchClass *klass) +{ + PulsePortSwitchClass *switch_class; + + switch_class = PULSE_PORT_SWITCH_CLASS (klass); + switch_class->set_active_port = pulse_source_switch_set_active_port; +} + +static void +pulse_source_switch_init (PulseSourceSwitch *swtch) +{ +} + +PulsePortSwitch * +pulse_source_switch_new (const gchar *name, const gchar *label, PulseSource *source) +{ + return g_object_new (PULSE_TYPE_SOURCE_SWITCH, + "name", name, + "label", label, + "role", MATE_MIXER_SWITCH_ROLE_PORT, + "stream", source, + NULL); +} + +static gboolean +pulse_source_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port) +{ + PulseStream *stream; + + g_return_val_if_fail (PULSE_IS_SOURCE_SWITCH (swtch), FALSE); + g_return_val_if_fail (PULSE_IS_PORT (port), FALSE); + + stream = pulse_port_switch_get_stream (swtch); + + return pulse_connection_set_source_port (pulse_stream_get_connection (stream), + pulse_stream_get_index (stream), + pulse_port_get_name (port)); +} diff --git a/backends/pulse/pulse-source-switch.h b/backends/pulse/pulse-source-switch.h new file mode 100644 index 0000000..408e872 --- /dev/null +++ b/backends/pulse/pulse-source-switch.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_SWITCH_H +#define PULSE_SOURCE_SWITCH_H + +#include <glib.h> +#include <glib-object.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE_SWITCH \ + (pulse_source_switch_get_type ()) +#define PULSE_SOURCE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitch)) +#define PULSE_IS_SOURCE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_SWITCH)) +#define PULSE_SOURCE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitchClass)) +#define PULSE_IS_SOURCE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_SWITCH)) +#define PULSE_SOURCE_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitchClass)) + +typedef struct _PulseSourceSwitchClass PulseSourceSwitchClass; +typedef struct _PulseSourceSwitchPrivate PulseSourceSwitchPrivate; + +struct _PulseSourceSwitch +{ + PulsePortSwitch parent; +}; + +struct _PulseSourceSwitchClass +{ + PulsePortSwitchClass parent_class; +}; + +GType pulse_source_switch_get_type (void) G_GNUC_CONST; + +PulsePortSwitch *pulse_source_switch_new (const gchar *name, + const gchar *label, + PulseSource *source); + +G_END_DECLS + +#endif /* PULSE_SOURCE_SWITCH_H */ diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c index e7dce6f..0d095a7 100644 --- a/backends/pulse/pulse-source.c +++ b/backends/pulse/pulse-source.c @@ -16,253 +16,238 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" #include "pulse-monitor.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" #include "pulse-stream.h" #include "pulse-source.h" +#include "pulse-source-control.h" +#include "pulse-source-output.h" +#include "pulse-source-switch.h" + +struct _PulseSourcePrivate +{ + GHashTable *outputs; + PulsePortSwitch *pswitch; + PulseSourceControl *control; +}; static void pulse_source_class_init (PulseSourceClass *klass); static void pulse_source_init (PulseSource *source); +static void pulse_source_dispose (GObject *object); +static void pulse_source_finalize (GObject *object); G_DEFINE_TYPE (PulseSource, pulse_source, PULSE_TYPE_STREAM); -static void pulse_source_reload (PulseStream *pstream); +static const GList *pulse_source_list_controls (MateMixerStream *mms); +static const GList *pulse_source_list_switches (MateMixerStream *mms); -static gboolean pulse_source_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_source_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_source_set_active_port (PulseStream *pstream, - MateMixerPort *port); +static void +pulse_source_class_init (PulseSourceClass *klass) +{ + GObjectClass *object_class; + MateMixerStreamClass *stream_class; -static PulseMonitor *pulse_source_create_monitor (PulseStream *pstream); + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_source_dispose; + object_class->finalize = pulse_source_finalize; -static void update_ports (PulseStream *pstream, - pa_source_port_info **ports, - pa_source_port_info *active); + stream_class = MATE_MIXER_STREAM_CLASS (klass); + stream_class->list_controls = pulse_source_list_controls; + stream_class->list_switches = pulse_source_list_switches; + + g_type_class_add_private (klass, sizeof (PulseSourcePrivate)); +} static void -pulse_source_class_init (PulseSourceClass *klass) +pulse_source_init (PulseSource *source) +{ + source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source, + PULSE_TYPE_SOURCE, + PulseSourcePrivate); + + source->priv->outputs = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); +} + +static void +pulse_source_dispose (GObject *object) { - PulseStreamClass *stream_class; + PulseSource *source; - stream_class = PULSE_STREAM_CLASS (klass); + source = PULSE_SOURCE (object); - stream_class->reload = pulse_source_reload; - stream_class->set_mute = pulse_source_set_mute; - stream_class->set_volume = pulse_source_set_volume; - stream_class->set_active_port = pulse_source_set_active_port; - stream_class->create_monitor = pulse_source_create_monitor; + g_clear_object (&source->priv->control); + g_clear_object (&source->priv->pswitch); + + g_hash_table_remove_all (source->priv->outputs); + + G_OBJECT_CLASS (pulse_source_parent_class)->dispose (object); } static void -pulse_source_init (PulseSource *source) +pulse_source_finalize (GObject *object) { + PulseSource *source; + + source = PULSE_SOURCE (object); + + g_hash_table_unref (source->priv->outputs); + + G_OBJECT_CLASS (pulse_source_parent_class)->finalize (object); } -PulseStream * +PulseSource * pulse_source_new (PulseConnection *connection, const pa_source_info *info, PulseDevice *device) { - PulseStream *stream; + PulseSource *source; g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (info != NULL, NULL); - /* Consider the sink index as unchanging parameter */ - stream = g_object_new (PULSE_TYPE_SOURCE, + source = g_object_new (PULSE_TYPE_SOURCE, + "name", info->name, + "label", info->description, + "device", device, + "direction", MATE_MIXER_DIRECTION_INPUT, "connection", connection, "index", info->index, NULL); - /* Other data may change at any time, so let's make a use of our update function */ - pulse_source_update (stream, info, device); + source->priv->control = pulse_source_control_new (source, info); - return stream; -} + if (info->n_ports > 0) { + pa_source_port_info **ports = info->ports; -gboolean -pulse_source_update (PulseStream *pstream, - const pa_source_info *info, - PulseDevice *device) -{ - MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_MONITOR | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME | - MATE_MIXER_STREAM_CAN_SUSPEND; - - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); - - pulse_stream_update_name (pstream, info->name); - pulse_stream_update_description (pstream, info->description); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); - - /* Stream state */ - switch (info->state) { - case PA_SOURCE_RUNNING: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING); - break; - case PA_SOURCE_IDLE: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); - break; - case PA_SOURCE_SUSPENDED: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); - break; - default: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN); - break; - } + /* Create the port switch */ + source->priv->pswitch = pulse_source_switch_new ("port", _("Port"), source); + + while (*ports != NULL) { + pa_source_port_info *p = *ports++; + PulsePort *port; + const gchar *icon = NULL; - /* Build the flag list */ - if (info->flags & PA_SINK_DECIBEL_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; - if (info->flags & PA_SINK_FLAT_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; + /* A port may include an icon but in PulseAudio sink and source ports + * the property is not included, for this reason ports are also read from + * devices where the icons may be present */ + if (device != NULL) { + port = pulse_device_get_port (PULSE_DEVICE (device), p->name); + if (port != NULL) + icon = mate_mixer_switch_option_get_icon (MATE_MIXER_SWITCH_OPTION (port)); + } - /* Flags must be updated before volume */ - pulse_stream_update_flags (pstream, flags); + port = pulse_port_new (p->name, + p->description, + icon, + p->priority); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, &info->volume, info->base_volume); + pulse_port_switch_add_port (source->priv->pswitch, port); - pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device)); + if (p == info->active_port) + pulse_port_switch_set_active_port (source->priv->pswitch, port); + } - /* Ports must be updated after device */ - if (info->ports != NULL) { - update_ports (pstream, info->ports, info->active_port); + g_debug ("Created port list for source %s", info->name); } - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} + pulse_source_update (source, info); -static void -pulse_source_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SOURCE (pstream)); - - pulse_connection_load_source_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (source), + MATE_MIXER_STREAM_CONTROL (source->priv->control)); + return source; } -static gboolean -pulse_source_set_mute (PulseStream *pstream, gboolean mute) +void +pulse_source_add_output (PulseSource *source, const pa_source_output_info *info) { - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - - return pulse_connection_set_source_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mute); + PulseSourceOutput *output; + + /* This function is used for both creating and refreshing source outputs */ + output = g_hash_table_lookup (source->priv->outputs, GINT_TO_POINTER (info->index)); + if (output == NULL) { + const gchar *name; + + output = pulse_source_output_new (source, info); + g_hash_table_insert (source->priv->outputs, + GINT_TO_POINTER (info->index), + output); + + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (output)); + g_signal_emit_by_name (G_OBJECT (source), + "control-added", + name); + } else + pulse_source_output_update (output, info); } -static gboolean -pulse_source_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +void +pulse_source_remove_output (PulseSource *source, guint32 index) { - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + PulseSourceOutput *output; + const gchar *name; - return pulse_connection_set_source_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - cvolume); -} + output = g_hash_table_lookup (source->priv->outputs, GINT_TO_POINTER (index)); + if G_UNLIKELY (output == NULL) + return; -static gboolean -pulse_source_set_active_port (PulseStream *pstream, MateMixerPort *port) -{ - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (output)); - return pulse_connection_set_source_port (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mate_mixer_port_get_name (port)); + g_hash_table_remove (source->priv->outputs, GINT_TO_POINTER (index)); + g_signal_emit_by_name (G_OBJECT (source), + "control-removed", + name); } -static PulseMonitor * -pulse_source_create_monitor (PulseStream *pstream) +void +pulse_source_update (PulseSource *source, + const pa_source_info *info) { - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), NULL); - - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - PA_INVALID_INDEX); + g_return_if_fail (PULSE_IS_SOURCE (source)); + g_return_if_fail (info != NULL); + + /* The switch doesn't allow being unset, PulseAudio should always include + * the active port name if the are any ports available */ + if (info->active_port != NULL) + pulse_port_switch_set_active_port_by_name (source->priv->pswitch, + info->active_port->name); } -static void -update_ports (PulseStream *pstream, - pa_source_port_info **ports, - pa_source_port_info *active) +static const GList * +pulse_source_list_controls (MateMixerStream *mms) { - MateMixerPort *port; - MateMixerDevice *device; - GHashTable *hash; - - hash = pulse_stream_get_ports (pstream); + GList *list; - while (*ports != NULL) { - MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS; - pa_source_port_info *info = *ports; - const gchar *icon = NULL; + g_return_val_if_fail (PULSE_IS_SOURCE (mms), NULL); - device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream)); - if (device != NULL) { - port = mate_mixer_device_get_port (device, info->name); - - if (port != NULL) { - flags = mate_mixer_port_get_flags (port); - icon = mate_mixer_port_get_icon (port); - } - } + // XXX + list = g_hash_table_get_values (PULSE_SOURCE (mms)->priv->outputs); + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); -#if PA_CHECK_VERSION(2, 0, 0) - if (info->available == PA_PORT_AVAILABLE_YES) - flags |= MATE_MIXER_PORT_AVAILABLE; - else - flags &= ~MATE_MIXER_PORT_AVAILABLE; -#endif - - port = g_hash_table_lookup (hash, info->name); - - if (port != NULL) { - /* Update existing port */ - _mate_mixer_port_update_description (port, info->description); - _mate_mixer_port_update_icon (port, icon); - _mate_mixer_port_update_priority (port, info->priority); - _mate_mixer_port_update_flags (port, flags); - } else { - /* Add previously unknown port to the hash table */ - port = _mate_mixer_port_new (info->name, - info->description, - icon, - info->priority, - flags); - - g_hash_table_insert (hash, g_strdup (info->name), port); - } + return g_list_prepend (list, g_object_ref (PULSE_SOURCE (mms)->priv->control)); +} - ports++; - } +static const GList * +pulse_source_list_switches (MateMixerStream *mms) +{ + g_return_val_if_fail (PULSE_IS_SOURCE (mms), NULL); - /* Active port */ - if (G_LIKELY (active != NULL)) - port = g_hash_table_lookup (hash, active->name); - else - port = NULL; + // XXX + if (PULSE_SOURCE (mms)->priv->pswitch != NULL) + return g_list_prepend (NULL, PULSE_SOURCE (mms)->priv->pswitch); - pulse_stream_update_active_port (pstream, port); + return NULL; } diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h index 9abf6d8..fdc3d5e 100644 --- a/backends/pulse/pulse-source.h +++ b/backends/pulse/pulse-source.h @@ -23,9 +23,8 @@ #include <pulse/pulseaudio.h> -#include "pulse-connection.h" -#include "pulse-device.h" #include "pulse-stream.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,12 +41,15 @@ G_BEGIN_DECLS #define PULSE_SOURCE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE, PulseSourceClass)) -typedef struct _PulseSource PulseSource; typedef struct _PulseSourceClass PulseSourceClass; +typedef struct _PulseSourcePrivate PulseSourcePrivate; struct _PulseSource { PulseStream parent; + + /*< private >*/ + PulseSourcePrivate *priv; }; struct _PulseSourceClass @@ -55,15 +57,20 @@ struct _PulseSourceClass PulseStreamClass parent_class; }; -GType pulse_source_get_type (void) G_GNUC_CONST; +GType pulse_source_get_type (void) G_GNUC_CONST; + +PulseSource *pulse_source_new (PulseConnection *connection, + const pa_source_info *info, + PulseDevice *device); + +void pulse_source_add_output (PulseSource *source, + const pa_source_output_info *info); -PulseStream *pulse_source_new (PulseConnection *connection, - const pa_source_info *info, - PulseDevice *device); +void pulse_source_remove_output (PulseSource *source, + guint32 index); -gboolean pulse_source_update (PulseStream *pstream, - const pa_source_info *info, - PulseDevice *device); +void pulse_source_update (PulseSource *source, + const pa_source_info *info); G_END_DECLS diff --git a/backends/pulse/pulse-stream-control.c b/backends/pulse/pulse-stream-control.c new file mode 100644 index 0000000..fa17e6b --- /dev/null +++ b/backends/pulse/pulse-stream-control.c @@ -0,0 +1,744 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-helpers.h" +#include "pulse-monitor.h" +#include "pulse-stream-control.h" + +struct _PulseStreamControlPrivate +{ + guint32 index; + guint volume; + pa_cvolume cvolume; + pa_volume_t base_volume; + pa_channel_map channel_map; + PulseConnection *connection; + PulseMonitor *monitor; + MateMixerAppInfo *app_info; +}; + +enum { + PROP_0, + PROP_INDEX, + PROP_CONNECTION, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void pulse_stream_control_class_init (PulseStreamControlClass *klass); + +static void pulse_stream_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_stream_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void pulse_stream_control_init (PulseStreamControl *control); +static void pulse_stream_control_dispose (GObject *object); +static void pulse_stream_control_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (PulseStreamControl, pulse_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL) + +static MateMixerAppInfo * pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc); + +static gboolean pulse_stream_control_set_mute (MateMixerStreamControl *mmsc, + gboolean mute); + +static guint pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc); + +static guint pulse_stream_control_get_volume (MateMixerStreamControl *mmsc); +static gboolean pulse_stream_control_set_volume (MateMixerStreamControl *mmsc, + guint volume); + +static gdouble pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc); +static gboolean pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc, + gdouble decibel); + +static guint pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, + guint channel, + guint volume); + +static gdouble pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc, + guint channel, + gdouble decibel); + +static MateMixerChannelPosition pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position); + +static gboolean pulse_stream_control_set_balance (MateMixerStreamControl *mmsc, + gfloat balance); + +static gboolean pulse_stream_control_set_fade (MateMixerStreamControl *mmsc, + gfloat fade); + +static gboolean pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc); +static gboolean pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc, + gboolean enabled); + +static guint pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc); +static guint pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc); +static guint pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc); +static guint pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc); + +static void on_monitor_value (PulseMonitor *monitor, + gdouble value, + PulseStreamControl *control); + +static void set_balance_fade (PulseStreamControl *control); + +static gboolean set_cvolume (PulseStreamControl *control, + pa_cvolume *cvolume); + +static void +pulse_stream_control_class_init (PulseStreamControlClass *klass) +{ + GObjectClass *object_class; + MateMixerStreamControlClass *control_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_stream_control_dispose; + object_class->finalize = pulse_stream_control_finalize; + object_class->get_property = pulse_stream_control_get_property; + object_class->set_property = pulse_stream_control_set_property; + + control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); + control_class->get_app_info = pulse_stream_control_get_app_info; + control_class->set_mute = pulse_stream_control_set_mute; + control_class->get_num_channels = pulse_stream_control_get_num_channels; + control_class->get_volume = pulse_stream_control_get_volume; + control_class->set_volume = pulse_stream_control_set_volume; + control_class->get_decibel = pulse_stream_control_get_decibel; + control_class->set_decibel = pulse_stream_control_set_decibel; + control_class->get_channel_volume = pulse_stream_control_get_channel_volume; + control_class->set_channel_volume = pulse_stream_control_set_channel_volume; + control_class->get_channel_decibel = pulse_stream_control_get_channel_decibel; + control_class->set_channel_decibel = pulse_stream_control_set_channel_decibel; + control_class->get_channel_position = pulse_stream_control_get_channel_position; + control_class->has_channel_position = pulse_stream_control_has_channel_position; + control_class->set_balance = pulse_stream_control_set_balance; + control_class->set_fade = pulse_stream_control_set_fade; + control_class->get_monitor_enabled = pulse_stream_control_get_monitor_enabled; + control_class->set_monitor_enabled = pulse_stream_control_set_monitor_enabled; + control_class->get_min_volume = pulse_stream_control_get_min_volume; + control_class->get_max_volume = pulse_stream_control_get_max_volume; + control_class->get_normal_volume = pulse_stream_control_get_normal_volume; + control_class->get_base_volume = pulse_stream_control_get_base_volume; + + properties[PROP_INDEX] = + g_param_spec_uint ("index", + "Index", + "Index of the stream control", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_CONNECTION] = + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (PulseStreamControlPrivate)); +} + +static void +pulse_stream_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + switch (param_id) { + case PROP_INDEX: + g_value_set_uint (value, control->priv->index); + break; + case PROP_CONNECTION: + g_value_set_object (value, control->priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_stream_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + switch (param_id) { + case PROP_INDEX: + control->priv->index = g_value_get_uint (value); + break; + case PROP_CONNECTION: + control->priv->connection = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_stream_control_init (PulseStreamControl *control) +{ + control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, + PULSE_TYPE_STREAM_CONTROL, + PulseStreamControlPrivate); + + /* Initialize empty volume and channel map structures, they will be used + * if the stream does not support volume */ + pa_cvolume_init (&control->priv->cvolume); + + pa_channel_map_init (&control->priv->channel_map); +} + +static void +pulse_stream_control_dispose (GObject *object) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + g_clear_object (&control->priv->monitor); + g_clear_object (&control->priv->connection); + + G_OBJECT_CLASS (pulse_stream_control_parent_class)->dispose (object); +} + +static void +pulse_stream_control_finalize (GObject *object) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + if (control->priv->app_info != NULL) + _mate_mixer_app_info_free (control->priv->app_info); + + G_OBJECT_CLASS (pulse_stream_control_parent_class)->finalize (object); +} + +guint32 +pulse_stream_control_get_index (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), 0); + + return control->priv->index; +} + +PulseConnection * +pulse_stream_control_get_connection (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return control->priv->connection; +} + +PulseMonitor * +pulse_stream_control_get_monitor (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return control->priv->monitor; +} + +const pa_cvolume * +pulse_stream_control_get_cvolume (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return &control->priv->cvolume; +} + +const pa_channel_map * +pulse_stream_control_get_channel_map (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return &control->priv->channel_map; +} + +void +pulse_stream_control_set_app_info (PulseStreamControl *control, MateMixerAppInfo *info) +{ + g_return_if_fail (PULSE_IS_STREAM_CONTROL (control)); + + if G_UNLIKELY (control->priv->app_info) + _mate_mixer_app_info_free (control->priv->app_info); + + control->priv->app_info = info; +} + +void +pulse_stream_control_set_channel_map (PulseStreamControl *control, const pa_channel_map *map) +{ + MateMixerStreamControlFlags flags; + + g_return_if_fail (PULSE_IS_STREAM_CONTROL (control)); + g_return_if_fail (map != NULL); + + flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control)); + + if (pa_channel_map_valid (map)) { + if (pa_channel_map_can_balance (map)) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + else + flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + + if (pa_channel_map_can_fade (map)) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE; + else + flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_FADE; + + control->priv->channel_map = *map; + } else { + flags &= ~(MATE_MIXER_STREAM_CONTROL_CAN_BALANCE | MATE_MIXER_STREAM_CONTROL_CAN_FADE); + + /* If the channel map is not valid, create an empty channel map, which + * also won't validate, but at least we know what it is */ + pa_channel_map_init (&control->priv->channel_map); + } + + _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags); +} + +void +pulse_stream_control_set_cvolume (PulseStreamControl *control, + const pa_cvolume *cvolume, + pa_volume_t base_volume) +{ + MateMixerStreamControlFlags flags; + + g_return_if_fail (PULSE_IS_STREAM_CONTROL (control)); + + /* The base volume is not a property */ + control->priv->base_volume = base_volume; + + flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control)); + + g_object_freeze_notify (G_OBJECT (control)); + + if (cvolume != NULL && pa_cvolume_valid (cvolume)) { + /* Decibel volume and volume settability flags must be provided by + * the implementation */ + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE; + + if (pa_cvolume_equal (&control->priv->cvolume, cvolume) == 0) { + control->priv->cvolume = *cvolume; + control->priv->volume = (guint) pa_cvolume_max (&control->priv->cvolume); + + g_object_notify (G_OBJECT (control), "volume"); + } + } else { + flags &= ~(MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL); + + /* If the cvolume is not valid, create an empty cvolume, which also + * won't validate, but at least we know what it is */ + pa_cvolume_init (&control->priv->cvolume); + + if (control->priv->volume != (guint) PA_VOLUME_MUTED) { + control->priv->volume = (guint) PA_VOLUME_MUTED; + + g_object_notify (G_OBJECT (control), "volume"); + } + } + + _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags); + + /* Changing volume may change the balance and fade values as well */ + set_balance_fade (control); + + g_object_thaw_notify (G_OBJECT (control)); +} + +static MateMixerAppInfo * +pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), NULL); + + return PULSE_STREAM_CONTROL (mmsc)->priv->app_info; +} + +static gboolean +pulse_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + return PULSE_STREAM_CONTROL_GET_CLASS (mmsc)->set_mute (PULSE_STREAM_CONTROL (mmsc), mute); +} + +static guint +pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), 0); + + return PULSE_STREAM_CONTROL (mmsc)->priv->channel_map.channels; +} + +static guint +pulse_stream_control_get_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + return PULSE_STREAM_CONTROL (mmsc)->priv->volume; +} + +static gboolean +pulse_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + cvolume = control->priv->cvolume; + + if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) + return FALSE; + + return set_cvolume (control, &cvolume); +} + +static gdouble +pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc) +{ + gdouble value; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY); + + value = pa_sw_volume_to_dB (pulse_stream_control_get_volume (mmsc)); + + /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */ + return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; +} + +static gboolean +pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc, gdouble decibel) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + return pulse_stream_control_set_volume (mmsc, + pa_sw_volume_from_dB (decibel)); +} + +static guint +pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->cvolume.channels) + return (guint) PA_VOLUME_MUTED; + + return (guint) control->priv->cvolume.values[channel]; +} + +static gboolean +pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->cvolume.channels) + return FALSE; + + /* This is safe, because the cvolume is validated by set_cvolume() */ + cvolume = control->priv->cvolume; + cvolume.values[channel] = (pa_volume_t) volume; + + return set_cvolume (control, &cvolume); +} + +static gdouble +pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint channel) +{ + PulseStreamControl *control; + gdouble value; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->cvolume.channels) + return -MATE_MIXER_INFINITY; + + value = pa_sw_volume_to_dB (control->priv->cvolume.values[channel]); + + return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; +} + +static gboolean +pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc, + guint channel, + gdouble decibel) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + return pulse_stream_control_set_channel_volume (mmsc, + channel, + pa_sw_volume_from_dB (decibel)); +} + +static MateMixerChannelPosition +pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->channel_map.channels) + return MATE_MIXER_CHANNEL_UNKNOWN; + + return pulse_convert_position_to_pulse (control->priv->channel_map.map[channel]); +} + +static gboolean +pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position) +{ + PulseStreamControl *control; + pa_channel_position_t p; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + /* Handle invalid position as a special case, otherwise this function would + * return TRUE for e.g. unknown index in a default channel map */ + p = pulse_convert_position_to_pulse (position); + + if (p == PA_CHANNEL_POSITION_INVALID) + return FALSE; + + if (pa_channel_map_has_position (&control->priv->channel_map, p) != 0) + return TRUE; + else + return FALSE; +} + +static gboolean +pulse_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + cvolume = control->priv->cvolume; + + if (pa_cvolume_set_balance (&cvolume, &control->priv->channel_map, balance) == NULL) + return FALSE; + + return set_cvolume (control, &cvolume); +} + +static gboolean +pulse_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + cvolume = control->priv->cvolume; + + if (pa_cvolume_set_fade (&cvolume, &control->priv->channel_map, fade) == NULL) + return FALSE; + + return set_cvolume (control, &cvolume); +} + +static gboolean +pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (control->priv->monitor != NULL) + return pulse_monitor_get_enabled (control->priv->monitor); + + return FALSE; +} + +static gboolean +pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc, gboolean enabled) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (enabled == TRUE) { + if (control->priv->monitor == NULL) { + control->priv->monitor = + PULSE_STREAM_CONTROL_GET_CLASS (control)->create_monitor (control); + + if G_UNLIKELY (control->priv->monitor == NULL) + return FALSE; + + g_signal_connect (G_OBJECT (control->priv->monitor), + "value", + G_CALLBACK (on_monitor_value), + control); + } + } else { + if (control->priv->monitor == NULL) + return FALSE; + } + return pulse_monitor_set_enabled (control->priv->monitor, enabled); +} + +static guint +pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc) +{ + return (guint) PA_VOLUME_MUTED; +} + +static guint +pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + return (guint) PA_VOLUME_UI_MAX; +} + +static guint +pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + return (guint) PA_VOLUME_NORM; +} + +static guint +pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (control->priv->base_volume > 0) + return control->priv->base_volume; + else + return (guint) PA_VOLUME_NORM; +} + +static void +on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStreamControl *control) +{ + g_signal_emit_by_name (G_OBJECT (control), + "monitor-value", + value); +} + +static void +set_balance_fade (PulseStreamControl *control) +{ + gfloat value; + + /* PulseAudio returns the default 0.0f value on error, so skip checking validity + * of the channel map and cvolume */ + value = pa_cvolume_get_balance (&control->priv->cvolume, + &control->priv->channel_map); + + _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), value); + + value = pa_cvolume_get_fade (&control->priv->cvolume, + &control->priv->channel_map); + + _mate_mixer_stream_control_set_fade (MATE_MIXER_STREAM_CONTROL (control), value); +} + +static gboolean +set_cvolume (PulseStreamControl *control, pa_cvolume *cvolume) +{ + PulseStreamControlClass *klass; + + if (pa_cvolume_valid (cvolume) == 0) + return FALSE; + if (pa_cvolume_equal (cvolume, &control->priv->cvolume) != 0) + return TRUE; + + klass = PULSE_STREAM_CONTROL_GET_CLASS (control); + + if (klass->set_volume (control, cvolume) == FALSE) + return FALSE; + + control->priv->cvolume = *cvolume; + control->priv->volume = (guint) pa_cvolume_max (cvolume); + + g_object_notify (G_OBJECT (control), "volume"); + + /* Changing volume may change the balance and fade values as well */ + set_balance_fade (control); + return TRUE; +} diff --git a/backends/pulse/pulse-stream-control.h b/backends/pulse/pulse-stream-control.h new file mode 100644 index 0000000..abc3f98 --- /dev/null +++ b/backends/pulse/pulse-stream-control.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_STREAM_CONTROL_H +#define PULSE_STREAM_CONTROL_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_STREAM_CONTROL \ + (pulse_stream_control_get_type ()) +#define PULSE_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_STREAM_CONTROL, PulseStreamControl)) +#define PULSE_IS_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_STREAM_CONTROL)) +#define PULSE_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_STREAM_CONTROL, PulseStreamControlClass)) +#define PULSE_IS_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM_CONTROL)) +#define PULSE_STREAM_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM_CONTROL, PulseStreamControlClass)) + +#define PULSE_STREAM_CONTROL_GET_CONNECTION(psc) \ + (pulse_stream_get_connection (PULSE_STREAM (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))))) +#define PULSE_STREAM_CONTROL_GET_STREAM_INDEX(psc) \ + (pulse_stream_get_index (PULSE_STREAM (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))))) + +typedef struct _PulseStreamControlClass PulseStreamControlClass; +typedef struct _PulseStreamControlPrivate PulseStreamControlPrivate; + +struct _PulseStreamControl +{ + MateMixerStreamControl parent; + + /*< private >*/ + PulseStreamControlPrivate *priv; +}; + +struct _PulseStreamControlClass +{ + MateMixerStreamControlClass parent_class; + + /*< private >*/ + gboolean (*set_mute) (PulseStreamControl *control, + gboolean mute); + gboolean (*set_volume) (PulseStreamControl *control, + pa_cvolume *volume); + + PulseMonitor *(*create_monitor) (PulseStreamControl *control); +}; + +GType pulse_stream_control_get_type (void) G_GNUC_CONST; + +guint32 pulse_stream_control_get_index (PulseStreamControl *control); + +PulseConnection * pulse_stream_control_get_connection (PulseStreamControl *control); +PulseMonitor * pulse_stream_control_get_monitor (PulseStreamControl *control); + +const pa_cvolume * pulse_stream_control_get_cvolume (PulseStreamControl *control); +const pa_channel_map *pulse_stream_control_get_channel_map (PulseStreamControl *control); + +void pulse_stream_control_set_app_info (PulseStreamControl *stream, + MateMixerAppInfo *info); + +void pulse_stream_control_set_channel_map (PulseStreamControl *control, + const pa_channel_map *map); +void pulse_stream_control_set_cvolume (PulseStreamControl *control, + const pa_cvolume *cvolume, + pa_volume_t base_volume); + +G_END_DECLS + +#endif /* PULSE_STREAM_CONTROL_H */ diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c index fb738ad..752c3e6 100644 --- a/backends/pulse/pulse-stream.c +++ b/backends/pulse/pulse-stream.c @@ -15,62 +15,34 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include <string.h> #include <glib.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> -#include <libmatemixer/matemixer-port.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-device.h" #include "pulse-helpers.h" #include "pulse-monitor.h" +#include "pulse-port.h" #include "pulse-stream.h" struct _PulseStreamPrivate { - guint32 index; - gchar *name; - gchar *description; - MateMixerDevice *device; - MateMixerStreamFlags flags; - MateMixerStreamState state; - gboolean mute; - guint volume; - pa_cvolume cvolume; - pa_volume_t base_volume; - pa_channel_map channel_map; - gfloat balance; - gfloat fade; - GHashTable *ports; - GList *ports_list; - MateMixerPort *port; - PulseConnection *connection; - PulseMonitor *monitor; - gchar *monitor_name; + guint32 index; + PulseConnection *connection; }; enum { PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_DEVICE, - PROP_FLAGS, - PROP_STATE, - PROP_MUTE, - PROP_VOLUME, - PROP_BALANCE, - PROP_FADE, - PROP_ACTIVE_PORT, PROP_INDEX, - PROP_CONNECTION + PROP_CONNECTION, + N_PROPERTIES }; -static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void pulse_stream_class_init (PulseStreamClass *klass); @@ -83,174 +55,45 @@ static void pulse_stream_set_property (GObject *object, const GValue *value, GParamSpec *pspec); -static void pulse_stream_init (PulseStream *pstream); +static void pulse_stream_init (PulseStream *stream); static void pulse_stream_dispose (GObject *object); -static void pulse_stream_finalize (GObject *object); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, - mate_mixer_stream_interface_init)) - -static const gchar * pulse_stream_get_name (MateMixerStream *stream); -static const gchar * pulse_stream_get_description (MateMixerStream *stream); -static MateMixerDevice * pulse_stream_get_device (MateMixerStream *stream); -static MateMixerStreamFlags pulse_stream_get_flags (MateMixerStream *stream); -static MateMixerStreamState pulse_stream_get_state (MateMixerStream *stream); - -static gboolean pulse_stream_get_mute (MateMixerStream *stream); -static gboolean pulse_stream_set_mute (MateMixerStream *stream, - gboolean mute); - -static guint pulse_stream_get_num_channels (MateMixerStream *stream); - -static guint pulse_stream_get_volume (MateMixerStream *stream); -static gboolean pulse_stream_set_volume (MateMixerStream *stream, - guint volume); - -static gdouble pulse_stream_get_decibel (MateMixerStream *stream); -static gboolean pulse_stream_set_decibel (MateMixerStream *stream, - gdouble decibel); - -static guint pulse_stream_get_channel_volume (MateMixerStream *stream, - guint channel); -static gboolean pulse_stream_set_channel_volume (MateMixerStream *stream, - guint channel, - guint volume); - -static gdouble pulse_stream_get_channel_decibel (MateMixerStream *stream, - guint channel); -static gboolean pulse_stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel); - -static MateMixerChannelPosition pulse_stream_get_channel_position (MateMixerStream *stream, - guint channel); -static gboolean pulse_stream_has_channel_position (MateMixerStream *stream, - MateMixerChannelPosition position); - -static gfloat pulse_stream_get_balance (MateMixerStream *stream); -static gboolean pulse_stream_set_balance (MateMixerStream *stream, - gfloat balance); - -static gfloat pulse_stream_get_fade (MateMixerStream *stream); -static gboolean pulse_stream_set_fade (MateMixerStream *stream, - gfloat fade); - -static gboolean pulse_stream_suspend (MateMixerStream *stream); -static gboolean pulse_stream_resume (MateMixerStream *stream); -static gboolean pulse_stream_monitor_start (MateMixerStream *stream); -static void pulse_stream_monitor_stop (MateMixerStream *stream); -static gboolean pulse_stream_monitor_is_running (MateMixerStream *stream); -static gboolean pulse_stream_monitor_set_name (MateMixerStream *stream, - const gchar *name); - -static const GList * pulse_stream_list_ports (MateMixerStream *stream); - -static MateMixerPort * pulse_stream_get_active_port (MateMixerStream *stream); -static gboolean pulse_stream_set_active_port (MateMixerStream *stream, - MateMixerPort *port); - -static guint pulse_stream_get_min_volume (MateMixerStream *stream); -static guint pulse_stream_get_max_volume (MateMixerStream *stream); -static guint pulse_stream_get_normal_volume (MateMixerStream *stream); -static guint pulse_stream_get_base_volume (MateMixerStream *stream); - -static void on_monitor_value (PulseMonitor *monitor, - gdouble value, - PulseStream *pstream); - -static gboolean update_balance_fade (PulseStream *pstream); - -static gboolean set_cvolume (PulseStream *pstream, - pa_cvolume *cvolume); - -static gint compare_ports (gconstpointer a, - gconstpointer b); - -static void -mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) -{ - iface->get_name = pulse_stream_get_name; - iface->get_description = pulse_stream_get_description; - iface->get_device = pulse_stream_get_device; - iface->get_flags = pulse_stream_get_flags; - iface->get_state = pulse_stream_get_state; - iface->get_mute = pulse_stream_get_mute; - iface->set_mute = pulse_stream_set_mute; - iface->get_num_channels = pulse_stream_get_num_channels; - iface->get_volume = pulse_stream_get_volume; - iface->set_volume = pulse_stream_set_volume; - iface->get_decibel = pulse_stream_get_decibel; - iface->set_decibel = pulse_stream_set_decibel; - iface->get_channel_volume = pulse_stream_get_channel_volume; - iface->set_channel_volume = pulse_stream_set_channel_volume; - iface->get_channel_decibel = pulse_stream_get_channel_decibel; - iface->set_channel_decibel = pulse_stream_set_channel_decibel; - iface->get_channel_position = pulse_stream_get_channel_position; - iface->has_channel_position = pulse_stream_has_channel_position; - iface->get_balance = pulse_stream_get_balance; - iface->set_balance = pulse_stream_set_balance; - iface->get_fade = pulse_stream_get_fade; - iface->set_fade = pulse_stream_set_fade; - iface->suspend = pulse_stream_suspend; - iface->resume = pulse_stream_resume; - iface->monitor_start = pulse_stream_monitor_start; - iface->monitor_stop = pulse_stream_monitor_stop; - iface->monitor_is_running = pulse_stream_monitor_is_running; - iface->monitor_set_name = pulse_stream_monitor_set_name; - iface->list_ports = pulse_stream_list_ports; - iface->get_active_port = pulse_stream_get_active_port; - iface->set_active_port = pulse_stream_set_active_port; - iface->get_min_volume = pulse_stream_get_min_volume; - iface->get_max_volume = pulse_stream_get_max_volume; - iface->get_normal_volume = pulse_stream_get_normal_volume; - iface->get_base_volume = pulse_stream_get_base_volume; -} +G_DEFINE_ABSTRACT_TYPE (PulseStream, pulse_stream, MATE_MIXER_TYPE_STREAM) static void pulse_stream_class_init (PulseStreamClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerStreamClass *stream_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = pulse_stream_dispose; - object_class->finalize = pulse_stream_finalize; object_class->get_property = pulse_stream_get_property; object_class->set_property = pulse_stream_set_property; - g_object_class_install_property (object_class, - PROP_INDEX, - g_param_spec_uint ("index", - "Index", - "Stream index", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_CONNECTION, - g_param_spec_object ("connection", - "Connection", - "PulseAudio connection", - PULSE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_DEVICE, "device"); - g_object_class_override_property (object_class, PROP_FLAGS, "flags"); - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_MUTE, "mute"); - g_object_class_override_property (object_class, PROP_VOLUME, "volume"); - g_object_class_override_property (object_class, PROP_BALANCE, "balance"); - g_object_class_override_property (object_class, PROP_FADE, "fade"); - g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); + stream_class = MATE_MIXER_STREAM_CLASS (klass); + + properties[PROP_INDEX] = + g_param_spec_uint ("index", + "Index", + "Index of the stream", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_CONNECTION] = + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); g_type_class_add_private (object_class, sizeof (PulseStreamPrivate)); } @@ -261,46 +104,16 @@ pulse_stream_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - PulseStream *pstream; + PulseStream *stream; - pstream = PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { - case PROP_NAME: - g_value_set_string (value, pstream->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, pstream->priv->description); - break; - case PROP_DEVICE: - g_value_set_object (value, pstream->priv->device); - break; - case PROP_FLAGS: - g_value_set_flags (value, pstream->priv->flags); - break; - case PROP_STATE: - g_value_set_enum (value, pstream->priv->state); - break; - case PROP_MUTE: - g_value_set_boolean (value, pstream->priv->mute); - break; - case PROP_VOLUME: - g_value_set_uint (value, pstream->priv->volume); - break; - case PROP_BALANCE: - g_value_set_float (value, pstream->priv->balance); - break; - case PROP_FADE: - g_value_set_float (value, pstream->priv->fade); - break; - case PROP_ACTIVE_PORT: - g_value_set_object (value, pstream->priv->port); - break; case PROP_INDEX: - g_value_set_uint (value, pstream->priv->index); + g_value_set_uint (value, stream->priv->index); break; case PROP_CONNECTION: - g_value_set_object (value, pstream->priv->connection); + g_value_set_object (value, stream->priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -314,17 +127,16 @@ pulse_stream_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - PulseStream *pstream; + PulseStream *stream; - pstream = PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { case PROP_INDEX: - pstream->priv->index = g_value_get_uint (value); + stream->priv->index = g_value_get_uint (value); break; case PROP_CONNECTION: - /* Construct-only object */ - pstream->priv->connection = g_value_dup_object (value); + stream->priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -333,962 +145,51 @@ pulse_stream_set_property (GObject *object, } static void -pulse_stream_init (PulseStream *pstream) +pulse_stream_init (PulseStream *stream) { - pstream->priv = G_TYPE_INSTANCE_GET_PRIVATE (pstream, - PULSE_TYPE_STREAM, - PulseStreamPrivate); - - pstream->priv->ports = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - - /* Initialize empty volume and channel map structures, they will be used - * if the stream does not support volume */ - pa_cvolume_init (&pstream->priv->cvolume); - - pa_channel_map_init (&pstream->priv->channel_map); + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, + PULSE_TYPE_STREAM, + PulseStreamPrivate); } static void pulse_stream_dispose (GObject *object) { - PulseStream *pstream; + PulseStream *stream; - pstream = PULSE_STREAM (object); + stream = PULSE_STREAM (object); - if (pstream->priv->ports_list != NULL) { - g_list_free_full (pstream->priv->ports_list, g_object_unref); - pstream->priv->ports_list = NULL; - } - g_hash_table_remove_all (pstream->priv->ports); - - g_clear_object (&pstream->priv->port); - g_clear_object (&pstream->priv->device); - g_clear_object (&pstream->priv->monitor); - g_clear_object (&pstream->priv->connection); + g_clear_object (&stream->priv->connection); G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object); } -static void -pulse_stream_finalize (GObject *object) -{ - PulseStream *pstream; - - pstream = PULSE_STREAM (object); - - g_free (pstream->priv->name); - g_free (pstream->priv->description); - g_free (pstream->priv->monitor_name); - - g_hash_table_destroy (pstream->priv->ports); - - G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object); -} - guint32 -pulse_stream_get_index (PulseStream *pstream) +pulse_stream_get_index (PulseStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (pstream), 0); + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - return pstream->priv->index; + return stream->priv->index; } PulseConnection * -pulse_stream_get_connection (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return pstream->priv->connection; -} - -PulseMonitor * -pulse_stream_get_monitor (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return pstream->priv->monitor; -} - -const pa_cvolume * -pulse_stream_get_cvolume (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return &pstream->priv->cvolume; -} - -const pa_channel_map * -pulse_stream_get_channel_map (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return &pstream->priv->channel_map; -} - -GHashTable * -pulse_stream_get_ports (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return pstream->priv->ports; -} - -gboolean -pulse_stream_update_name (PulseStream *pstream, const gchar *name) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - /* Allow the name to be NULL */ - if (g_strcmp0 (name, pstream->priv->name) != 0) { - g_free (pstream->priv->name); - pstream->priv->name = g_strdup (name); - - g_object_notify (G_OBJECT (pstream), "name"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_description (PulseStream *pstream, const gchar *description) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - /* Allow the description to be NULL */ - if (g_strcmp0 (description, pstream->priv->description) != 0) { - g_free (pstream->priv->description); - pstream->priv->description = g_strdup (description); - - g_object_notify (G_OBJECT (pstream), "description"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_device (PulseStream *pstream, MateMixerDevice *device) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->device != device) { - g_clear_object (&pstream->priv->device); - - if (G_LIKELY (device != NULL)) - pstream->priv->device = g_object_ref (device); - - g_object_notify (G_OBJECT (pstream), "device"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_flags (PulseStream *pstream, MateMixerStreamFlags flags) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->flags != flags) { - pstream->priv->flags = flags; - - g_object_notify (G_OBJECT (pstream), "flags"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_state (PulseStream *pstream, MateMixerStreamState state) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->state != state) { - pstream->priv->state = state; - - g_object_notify (G_OBJECT (pstream), "state"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_channel_map (PulseStream *pstream, const pa_channel_map *map) -{ - MateMixerStreamFlags flags; - - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - g_return_val_if_fail (map != NULL, FALSE); - - flags = pstream->priv->flags; - - if (pa_channel_map_valid (map)) { - if (pa_channel_map_can_balance (map)) - flags |= MATE_MIXER_STREAM_CAN_BALANCE; - else - flags &= ~MATE_MIXER_STREAM_CAN_BALANCE; - - if (pa_channel_map_can_fade (map)) - flags |= MATE_MIXER_STREAM_CAN_FADE; - else - flags &= ~MATE_MIXER_STREAM_CAN_FADE; - - pstream->priv->channel_map = *map; - } else { - flags &= ~(MATE_MIXER_STREAM_CAN_BALANCE | MATE_MIXER_STREAM_CAN_FADE); - - /* If the channel map is not valid, create an empty channel map, which - * also won't validate, but at least we know what it is */ - pa_channel_map_init (&pstream->priv->channel_map); - } - - pulse_stream_update_flags (pstream, flags); - return TRUE; -} - -gboolean -pulse_stream_update_volume (PulseStream *pstream, - const pa_cvolume *cvolume, - pa_volume_t base_volume) -{ - MateMixerStreamFlags flags; - - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - /* The base volume is not a property */ - pstream->priv->base_volume = base_volume; - - flags = pstream->priv->flags; - - if (cvolume != NULL && pa_cvolume_valid (cvolume)) { - /* Decibel volume and volume settability flags must be provided by - * the implementation */ - flags |= MATE_MIXER_STREAM_HAS_VOLUME; - - if (pa_cvolume_equal (&pstream->priv->cvolume, cvolume) == 0) { - pstream->priv->cvolume = *cvolume; - pstream->priv->volume = (guint) pa_cvolume_max (&pstream->priv->cvolume); - - g_object_notify (G_OBJECT (pstream), "volume"); - } - } else { - flags &= ~(MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME); - - /* If the cvolume is not valid, create an empty cvolume, which also - * won't validate, but at least we know what it is */ - pa_cvolume_init (&pstream->priv->cvolume); - - if (pstream->priv->volume != (guint) PA_VOLUME_MUTED) { - pstream->priv->volume = (guint) PA_VOLUME_MUTED; - - g_object_notify (G_OBJECT (pstream), "volume"); - } - } - - pulse_stream_update_flags (pstream, flags); - - /* Changing volume may change the balance and fade values as well */ - update_balance_fade (pstream); - return TRUE; -} - -gboolean -pulse_stream_update_mute (PulseStream *pstream, gboolean mute) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->mute != mute) { - pstream->priv->mute = mute; - - g_object_notify (G_OBJECT (pstream), "mute"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_active_port (PulseStream *pstream, MateMixerPort *port) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - if (pstream->priv->port != port) { - if (pstream->priv->port != NULL) - g_clear_object (&pstream->priv->port); - - if (port != NULL) - pstream->priv->port = g_object_ref (port); - - g_object_notify (G_OBJECT (pstream), "active-port"); - return TRUE; - } - return FALSE; -} - -static const gchar * -pulse_stream_get_name (MateMixerStream *stream) +pulse_stream_get_connection (PulseStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - return PULSE_STREAM (stream)->priv->name; + return stream->priv->connection; } -static const gchar * -pulse_stream_get_description (MateMixerStream *stream) +PulseDevice * +pulse_stream_get_device (PulseStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - - return PULSE_STREAM (stream)->priv->description; -} + MateMixerDevice *device; -static MateMixerDevice * -pulse_stream_get_device (MateMixerStream *stream) -{ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - return PULSE_STREAM (stream)->priv->device; -} - -static MateMixerStreamFlags -pulse_stream_get_flags (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); - - return PULSE_STREAM (stream)->priv->flags; -} - -static MateMixerStreamState -pulse_stream_get_state (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); - - return PULSE_STREAM (stream)->priv->state; -} - -static gboolean -pulse_stream_get_mute (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE)) - return FALSE; - - return pstream->priv->mute; -} - -static gboolean -pulse_stream_set_mute (MateMixerStream *stream, gboolean mute) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE)) - return FALSE; - - if (pstream->priv->mute != mute) { - PulseStreamClass *klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->set_mute (pstream, mute) == FALSE) - return FALSE; - - pstream->priv->mute = mute; - - g_object_notify (G_OBJECT (stream), "mute"); - } - return TRUE; -} - -static guint -pulse_stream_get_num_channels (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - - return PULSE_STREAM (stream)->priv->channel_map.channels; -} - -static guint -pulse_stream_get_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - return pstream->priv->volume; -} - -static gboolean -pulse_stream_set_volume (MateMixerStream *stream, guint volume) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - cvolume = pstream->priv->cvolume; - - if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) - return FALSE; - - return set_cvolume (pstream, &cvolume); -} - -static gdouble -pulse_stream_get_decibel (MateMixerStream *stream) -{ - PulseStream *pstream; - gdouble value; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) - return -MATE_MIXER_INFINITY; - - value = pa_sw_volume_to_dB (pulse_stream_get_volume (stream)); - - /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */ - return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; -} - -static gboolean -pulse_stream_set_decibel (MateMixerStream *stream, gdouble decibel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || - !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - return pulse_stream_set_volume (stream, pa_sw_volume_from_dB (decibel)); -} - -static guint -pulse_stream_get_channel_volume (MateMixerStream *stream, guint channel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - if (channel >= pstream->priv->cvolume.channels) - return (guint) PA_VOLUME_MUTED; - - return (guint) pstream->priv->cvolume.values[channel]; -} - -static gboolean -pulse_stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - if (channel >= pstream->priv->cvolume.channels) - return FALSE; - - /* This is safe, because the cvolume is validated by set_cvolume() */ - cvolume = pstream->priv->cvolume; - cvolume.values[channel] = (pa_volume_t) volume; - - return set_cvolume (pstream, &cvolume); -} - -static gdouble -pulse_stream_get_channel_decibel (MateMixerStream *stream, guint channel) -{ - PulseStream *pstream; - gdouble value; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) - return -MATE_MIXER_INFINITY; - - if (channel >= pstream->priv->cvolume.channels) - return -MATE_MIXER_INFINITY; - - value = pa_sw_volume_to_dB (pstream->priv->cvolume.values[channel]); - - return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; -} - -static gboolean -pulse_stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || - !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - return pulse_stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (decibel)); -} - -static MateMixerChannelPosition -pulse_stream_get_channel_position (MateMixerStream *stream, guint channel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN); - - pstream = PULSE_STREAM (stream); - - if (channel >= pstream->priv->channel_map.channels) - return MATE_MIXER_CHANNEL_UNKNOWN; - - return pulse_convert_position_to_pulse (pstream->priv->channel_map.map[channel]); -} - -static gboolean -pulse_stream_has_channel_position (MateMixerStream *stream, - MateMixerChannelPosition position) -{ - PulseStream *pstream; - pa_channel_position_t p; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - /* Handle invalid position as a special case, otherwise this function would - * return TRUE for e.g. unknown index in a default channel map */ - p = pulse_convert_position_to_pulse (position); - - if (p == PA_CHANNEL_POSITION_INVALID) - return FALSE; - - if (pa_channel_map_has_position (&pstream->priv->channel_map, p) != 0) - return TRUE; - else - return FALSE; -} - -static gfloat -pulse_stream_get_balance (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f); - - return PULSE_STREAM (stream)->priv->balance; -} - -static gboolean -pulse_stream_set_balance (MateMixerStream *stream, gfloat balance) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_BALANCE)) - return FALSE; - - cvolume = pstream->priv->cvolume; - - if (pa_cvolume_set_balance (&cvolume, &pstream->priv->channel_map, balance) == NULL) - return FALSE; - - return set_cvolume (pstream, &cvolume); -} - -static gfloat -pulse_stream_get_fade (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f); - - return PULSE_STREAM (stream)->priv->fade; -} - -static gboolean -pulse_stream_set_fade (MateMixerStream *stream, gfloat fade) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_FADE)) - return FALSE; - - cvolume = pstream->priv->cvolume; - - if (pa_cvolume_set_fade (&cvolume, &pstream->priv->channel_map, fade) == NULL) - return FALSE; - - return set_cvolume (pstream, &cvolume); -} - -static gboolean -pulse_stream_suspend (MateMixerStream *stream) -{ - PulseStream *pstream; - PulseStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) - return FALSE; - - if (pstream->priv->state == MATE_MIXER_STREAM_STATE_SUSPENDED) - return FALSE; - - klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->suspend (pstream) == FALSE) - return FALSE; - - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); - return TRUE; -} - -static gboolean -pulse_stream_resume (MateMixerStream *stream) -{ - PulseStream *pstream; - PulseStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) - return FALSE; - - if (pstream->priv->state != MATE_MIXER_STREAM_STATE_SUSPENDED) - return FALSE; - - klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->resume (pstream) == FALSE) - return FALSE; - - /* The state when resumed should be either RUNNING or IDLE, let's assume - * IDLE for now and request an immediate update */ - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); - - klass->reload (pstream); - return TRUE; -} - -static gboolean -pulse_stream_monitor_start (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MONITOR)) - return FALSE; - - if (pstream->priv->monitor == NULL) { - pstream->priv->monitor = PULSE_STREAM_GET_CLASS (pstream)->create_monitor (pstream); - - if (G_UNLIKELY (pstream->priv->monitor == NULL)) - return FALSE; - - pulse_monitor_set_name (pstream->priv->monitor, - pstream->priv->monitor_name); - - g_signal_connect (G_OBJECT (pstream->priv->monitor), - "value", - G_CALLBACK (on_monitor_value), - pstream); - } - - return pulse_monitor_set_enabled (pstream->priv->monitor, TRUE); -} - -static void -pulse_stream_monitor_stop (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_if_fail (PULSE_IS_STREAM (stream)); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->monitor != NULL) - pulse_monitor_set_enabled (pstream->priv->monitor, FALSE); -} - -static gboolean -pulse_stream_monitor_is_running (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->monitor != NULL) - return pulse_monitor_get_enabled (pstream->priv->monitor); - - return FALSE; -} - -static gboolean -pulse_stream_monitor_set_name (MateMixerStream *stream, const gchar *name) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->monitor != NULL) - pulse_monitor_set_name (pstream->priv->monitor, name); - - pstream->priv->monitor_name = g_strdup (name); - return TRUE; -} - -static const GList * -pulse_stream_list_ports (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->ports_list == NULL) { - GList *list = g_hash_table_get_values (pstream->priv->ports); - - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - pstream->priv->ports_list = g_list_sort (list, compare_ports); - } - } - - return (const GList *) pstream->priv->ports_list; -} - -static MateMixerPort * -pulse_stream_get_active_port (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - - return PULSE_STREAM (stream)->priv->port; -} - -static gboolean -pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) -{ - PulseStream *pstream; - PulseStreamClass *klass; - const gchar *name; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - pstream = PULSE_STREAM (stream); - - /* Make sure the port comes from this stream */ - name = mate_mixer_port_get_name (port); - - if (g_hash_table_lookup (pstream->priv->ports, name) == NULL) { - g_warning ("Port %s does not belong to stream %s", - mate_mixer_port_get_name (port), - mate_mixer_stream_get_name (stream)); - return FALSE; - } - - klass = PULSE_STREAM_GET_CLASS (pstream); - - /* Change the port */ - if (klass->set_active_port (pstream, port) == FALSE) - return FALSE; - - if (pstream->priv->port != NULL) - g_object_unref (pstream->priv->port); - - pstream->priv->port = g_object_ref (port); - - g_object_notify (G_OBJECT (stream), "active-port"); - return TRUE; -} - -static guint -pulse_stream_get_min_volume (MateMixerStream *stream) -{ - return (guint) PA_VOLUME_MUTED; -} - -static guint -pulse_stream_get_max_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - return (guint) PA_VOLUME_UI_MAX; -} - -static guint -pulse_stream_get_normal_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - return (guint) PA_VOLUME_NORM; -} - -static guint -pulse_stream_get_base_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - if (pstream->priv->base_volume > 0) - return pstream->priv->base_volume; - else - return (guint) PA_VOLUME_NORM; -} - -static void -on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStream *pstream) -{ - g_signal_emit_by_name (G_OBJECT (pstream), - "monitor-value", - value); -} - -static gboolean -update_balance_fade (PulseStream *pstream) -{ - gfloat fade; - gfloat balance; - gboolean changed = FALSE; - - /* The PulseAudio return the default 0.0f values on errors, so skip checking - * validity of the channel map and volume */ - balance = pa_cvolume_get_balance (&pstream->priv->cvolume, - &pstream->priv->channel_map); - - if (pstream->priv->balance != balance) { - pstream->priv->balance = balance; - - g_object_notify (G_OBJECT (pstream), "balance"); - changed = TRUE; - } - - fade = pa_cvolume_get_fade (&pstream->priv->cvolume, - &pstream->priv->channel_map); - - if (pstream->priv->fade != fade) { - pstream->priv->fade = fade; - - g_object_notify (G_OBJECT (pstream), "fade"); - changed = TRUE; - } - - return changed; -} - -static gboolean -set_cvolume (PulseStream *pstream, pa_cvolume *cvolume) -{ - PulseStreamClass *klass; - - if (pa_cvolume_valid (cvolume) == 0) - return FALSE; - if (pa_cvolume_equal (cvolume, &pstream->priv->cvolume) != 0) - return TRUE; - - klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->set_volume (pstream, cvolume) == FALSE) - return FALSE; - - pstream->priv->cvolume = *cvolume; - pstream->priv->volume = (guint) pa_cvolume_max (cvolume); - - g_object_notify (G_OBJECT (pstream), "volume"); - - /* Changing volume may change the balance and fade values as well */ - update_balance_fade (pstream); - return TRUE; -} - -static gint -compare_ports (gconstpointer a, gconstpointer b) -{ - MateMixerPort *p1 = MATE_MIXER_PORT (a); - MateMixerPort *p2 = MATE_MIXER_PORT (b); + device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (stream)); + if (device != NULL) + return PULSE_DEVICE (device); - gint ret = (gint) (mate_mixer_port_get_priority (p2) - - mate_mixer_port_get_priority (p1)); - if (ret != 0) - return ret; - else - return strcmp (mate_mixer_port_get_name (p1), - mate_mixer_port_get_name (p2)); + return NULL; } diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h index e4c6a00..eafe457 100644 --- a/backends/pulse/pulse-stream.h +++ b/backends/pulse/pulse-stream.h @@ -20,15 +20,11 @@ #include <glib.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> #include <pulse/pulseaudio.h> -#include "pulse-connection.h" -#include "pulse-monitor.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -45,13 +41,12 @@ G_BEGIN_DECLS #define PULSE_STREAM_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM, PulseStreamClass)) -typedef struct _PulseStream PulseStream; typedef struct _PulseStreamClass PulseStreamClass; typedef struct _PulseStreamPrivate PulseStreamPrivate; struct _PulseStream { - GObject parent; + MateMixerStream parent; /*< private >*/ PulseStreamPrivate *priv; @@ -59,58 +54,15 @@ struct _PulseStream struct _PulseStreamClass { - GObjectClass parent_class; - - /*< private >*/ - /* Virtual table */ - void (*reload) (PulseStream *stream); - - gboolean (*set_mute) (PulseStream *stream, - gboolean mute); - gboolean (*set_volume) (PulseStream *stream, - pa_cvolume *volume); - - gboolean (*set_active_port) (PulseStream *stream, - MateMixerPort *port); - - gboolean (*suspend) (PulseStream *stream); - gboolean (*resume) (PulseStream *stream); - - PulseMonitor *(*create_monitor) (PulseStream *stream); + MateMixerStreamClass parent_class; }; -GType pulse_stream_get_type (void) G_GNUC_CONST; - -guint32 pulse_stream_get_index (PulseStream *pstream); -PulseConnection * pulse_stream_get_connection (PulseStream *pstream); -PulseMonitor * pulse_stream_get_monitor (PulseStream *pstream); -GHashTable * pulse_stream_get_ports (PulseStream *pstream); - -const pa_cvolume * pulse_stream_get_cvolume (PulseStream *pstream); -const pa_channel_map *pulse_stream_get_channel_map (PulseStream *pstream); - -gboolean pulse_stream_update_name (PulseStream *pstream, - const gchar *name); -gboolean pulse_stream_update_description (PulseStream *pstream, - const gchar *description); -gboolean pulse_stream_update_device (PulseStream *pstream, - MateMixerDevice *device); -gboolean pulse_stream_update_flags (PulseStream *pstream, - MateMixerStreamFlags flags); -gboolean pulse_stream_update_state (PulseStream *pstream, - MateMixerStreamState state); - -gboolean pulse_stream_update_channel_map (PulseStream *pstream, - const pa_channel_map *map); -gboolean pulse_stream_update_volume (PulseStream *pstream, - const pa_cvolume *volume, - pa_volume_t base_volume); +GType pulse_stream_get_type (void) G_GNUC_CONST; -gboolean pulse_stream_update_mute (PulseStream *pstream, - gboolean mute); +guint32 pulse_stream_get_index (PulseStream *stream); +PulseConnection *pulse_stream_get_connection (PulseStream *stream); -gboolean pulse_stream_update_active_port (PulseStream *pstream, - MateMixerPort *port); +PulseDevice * pulse_stream_get_device (PulseStream *stream); G_END_DECLS diff --git a/backends/pulse/pulse-types.h b/backends/pulse/pulse-types.h new file mode 100644 index 0000000..c664268 --- /dev/null +++ b/backends/pulse/pulse-types.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_TYPES_H +#define PULSE_TYPES_H + +G_BEGIN_DECLS + +typedef struct _PulseBackend PulseBackend; +typedef struct _PulseConnection PulseConnection; +typedef struct _PulseDevice PulseDevice; +typedef struct _PulseDeviceProfile PulseDeviceProfile; +typedef struct _PulseDeviceSwitch PulseDeviceSwitch; +typedef struct _PulseExtStream PulseExtStream; +typedef struct _PulseMonitor PulseMonitor; +typedef struct _PulsePort PulsePort; +typedef struct _PulsePortSwitch PulsePortSwitch; +typedef struct _PulseSink PulseSink; +typedef struct _PulseSinkControl PulseSinkControl; +typedef struct _PulseSinkInput PulseSinkInput; +typedef struct _PulseSinkSwitch PulseSinkSwitch; +typedef struct _PulseSource PulseSource; +typedef struct _PulseSourceControl PulseSourceControl; +typedef struct _PulseSourceOutput PulseSourceOutput; +typedef struct _PulseSourceSwitch PulseSourceSwitch; +typedef struct _PulseStream PulseStream; +typedef struct _PulseStreamControl PulseStreamControl; + +G_END_DECLS + +#endif /* PULSE_TYPES_H */ |