diff options
-rw-r--r-- | backends/pulse/pulse-ext-stream.c | 522 | ||||
-rw-r--r-- | backends/pulse/pulse-ext-stream.h | 4 | ||||
-rw-r--r-- | docs/reference/libmatemixer-sections.txt | 10 | ||||
-rw-r--r-- | libmatemixer/matemixer-stored-control.c | 108 | ||||
-rw-r--r-- | libmatemixer/matemixer-stored-control.h | 41 |
5 files changed, 553 insertions, 132 deletions
diff --git a/backends/pulse/pulse-ext-stream.c b/backends/pulse/pulse-ext-stream.c index 5d2ec75..5d7863a 100644 --- a/backends/pulse/pulse-ext-stream.c +++ b/backends/pulse/pulse-ext-stream.c @@ -33,16 +33,20 @@ struct _PulseExtStreamPrivate { - MateMixerAppInfo *app_info; - MateMixerDirection direction; + guint volume; + pa_cvolume cvolume; + pa_channel_map channel_map; + MateMixerAppInfo *app_info; + PulseConnection *connection; }; enum { PROP_0, - PROP_DIRECTION + PROP_CONNECTION, + N_PROPERTIES }; -static void mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface); +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void pulse_ext_stream_class_init (PulseExtStreamClass *klass); @@ -56,52 +60,95 @@ static void pulse_ext_stream_set_property (GObject *object, GParamSpec *pspec); static void pulse_ext_stream_init (PulseExtStream *ext); +static void pulse_ext_stream_dispose (GObject *object); +static void pulse_ext_stream_finalize (GObject *object); -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)) +G_DEFINE_TYPE (PulseExtStream, pulse_ext_stream, MATE_MIXER_TYPE_STORED_CONTROL) -static MateMixerDirection pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc); +static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc); -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_stream (MateMixerStreamControl *mmsc, - MateMixerStream *mms); +static gboolean pulse_ext_stream_set_mute (MateMixerStreamControl *mmsc, + gboolean mute); -static gboolean pulse_ext_stream_set_mute (PulseStreamControl *control, - gboolean mute); -static gboolean pulse_ext_stream_set_volume (PulseStreamControl *control, - pa_cvolume *cvolume); +static guint pulse_ext_stream_get_num_channels (MateMixerStreamControl *mmsc); -static void fill_ext_stream_restore_info (PulseStreamControl *control, - pa_ext_stream_restore_info *info); +static guint pulse_ext_stream_get_volume (MateMixerStreamControl *mmsc); +static gboolean pulse_ext_stream_set_volume (MateMixerStreamControl *mmsc, + guint volume); -static void -mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface) -{ - iface->get_direction = pulse_ext_stream_get_direction; -} +static guint pulse_ext_stream_get_channel_volume (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_ext_stream_set_channel_volume (MateMixerStreamControl *mmsc, + guint channel, + guint volume); + +static MateMixerChannelPosition pulse_ext_stream_get_channel_position (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_ext_stream_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position); + +static gboolean pulse_ext_stream_set_balance (MateMixerStreamControl *mmsc, + gfloat balance); + +static gboolean pulse_ext_stream_set_fade (MateMixerStreamControl *mmsc, + gfloat fade); + +static guint pulse_ext_stream_get_min_volume (MateMixerStreamControl *mmsc); +static guint pulse_ext_stream_get_max_volume (MateMixerStreamControl *mmsc); +static guint pulse_ext_stream_get_normal_volume (MateMixerStreamControl *mmsc); +static guint pulse_ext_stream_get_base_volume (MateMixerStreamControl *mmsc); + +static void fill_ext_stream_restore_info (PulseExtStream *ext, + pa_ext_stream_restore_info *info); + +static gboolean write_cvolume (PulseExtStream *ext, + const pa_cvolume *cvolume); +static void store_cvolume (PulseExtStream *ext, + const pa_cvolume *cvolume); static void pulse_ext_stream_class_init (PulseExtStreamClass *klass) { GObjectClass *object_class; MateMixerStreamControlClass *control_class; - PulseStreamControlClass *pulse_class; object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_ext_stream_dispose; + object_class->finalize = pulse_ext_stream_finalize; object_class->get_property = pulse_ext_stream_get_property; object_class->set_property = pulse_ext_stream_set_property; 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; - - 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; - - g_object_class_override_property (object_class, PROP_DIRECTION, "direction"); + control_class->get_app_info = pulse_ext_stream_get_app_info; + control_class->set_stream = pulse_ext_stream_set_stream; + control_class->set_mute = pulse_ext_stream_set_mute; + control_class->get_num_channels = pulse_ext_stream_get_num_channels; + control_class->get_volume = pulse_ext_stream_get_volume; + control_class->set_volume = pulse_ext_stream_set_volume; + control_class->get_channel_volume = pulse_ext_stream_get_channel_volume; + control_class->set_channel_volume = pulse_ext_stream_set_channel_volume; + control_class->get_channel_position = pulse_ext_stream_get_channel_position; + control_class->has_channel_position = pulse_ext_stream_has_channel_position; + control_class->set_balance = pulse_ext_stream_set_balance; + control_class->set_fade = pulse_ext_stream_set_fade; + control_class->get_min_volume = pulse_ext_stream_get_min_volume; + control_class->get_max_volume = pulse_ext_stream_get_max_volume; + control_class->get_normal_volume = pulse_ext_stream_get_normal_volume; + control_class->get_base_volume = pulse_ext_stream_get_base_volume; + + 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 (PulseExtStreamPrivate)); } @@ -117,8 +164,8 @@ pulse_ext_stream_get_property (GObject *object, ext = PULSE_EXT_STREAM (object); switch (param_id) { - case PROP_DIRECTION: - g_value_set_enum (value, ext->priv->direction); + case PROP_CONNECTION: + g_value_set_object (value, ext->priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -137,8 +184,9 @@ pulse_ext_stream_set_property (GObject *object, ext = PULSE_EXT_STREAM (object); switch (param_id) { - case PROP_DIRECTION: - ext->priv->direction = g_value_get_enum (value); + case PROP_CONNECTION: + /* Construct-only object */ + ext->priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -154,26 +202,59 @@ pulse_ext_stream_init (PulseExtStream *ext) PulseExtStreamPrivate); } +static void +pulse_ext_stream_dispose (GObject *object) +{ + PulseExtStream *ext; + + ext = PULSE_EXT_STREAM (object); + + g_clear_object (&ext->priv->connection); + + G_OBJECT_CLASS (pulse_ext_stream_parent_class)->dispose (object); +} + +static void +pulse_ext_stream_finalize (GObject *object) +{ + PulseExtStream *ext; + + ext = PULSE_EXT_STREAM (object); + + if (ext->priv->app_info != NULL) + _mate_mixer_app_info_free (ext->priv->app_info); + + G_OBJECT_CLASS (pulse_ext_stream_parent_class)->finalize (object); +} + 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; + PulseExtStream *ext; + gchar *suffix; + MateMixerAppInfo *app_info = NULL; + MateMixerDirection direction; + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MOVABLE | + MATE_MIXER_STREAM_CONTROL_STORED; + MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; + 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); + /* The name of an ext-stream is in one of the following formats: + * sink-input-by-media-role: ... + * sink-input-by-application-name: ... + * sink-input-by-application-id: ... + * sink-input-by-media-name: ... + * source-output-by-media-role: ... + * source-output-by-application-name: ... + * source-output-by-application-id: ... + * source-output-by-media-name: ... + */ if (g_str_has_prefix (info->name, "sink-input")) direction = MATE_MIXER_DIRECTION_OUTPUT; else if (g_str_has_prefix (info->name, "source-output")) @@ -181,26 +262,36 @@ pulse_ext_stream_new (PulseConnection *connection, else 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)) + if G_LIKELY (suffix != NULL) media_role = pulse_convert_media_role_name (suffix); } else if (strstr (info->name, "-by-application-name:")) { role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; - if (G_LIKELY (suffix != NULL)) + /* Make sure an application ext-stream always has a MateMixerAppInfo + * structure available, even in the case no application info is + * available */ + if (app_info == NULL) + app_info = _mate_mixer_app_info_new (); + + if G_LIKELY (suffix != NULL) _mate_mixer_app_info_set_name (app_info, suffix); } else if (strstr (info->name, "-by-application-id:")) { role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; - if (G_LIKELY (suffix != NULL)) + /* Make sure an application ext-stream always has a MateMixerAppInfo + * structure available, even in the case no application info is + * available */ + if (app_info == NULL) + app_info = _mate_mixer_app_info_new (); + + if G_LIKELY (suffix != NULL) _mate_mixer_app_info_set_id (app_info, suffix); } @@ -214,12 +305,12 @@ pulse_ext_stream_new (PulseConnection *connection, "stream", parent, NULL); - if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) - ext->priv->app_info = app_info; - else - _mate_mixer_app_info_free (app_info); + // XXX property? + ext->priv->app_info = app_info; + /* Store values which are expected to be changed */ pulse_ext_stream_update (ext, info, parent); + return ext; } @@ -228,6 +319,9 @@ pulse_ext_stream_update (PulseExtStream *ext, const pa_ext_stream_restore_info *info, PulseStream *parent) { + MateMixerStreamControlFlags flags; + gboolean volume_changed = FALSE; + g_return_if_fail (PULSE_IS_EXT_STREAM (ext)); g_return_if_fail (info != NULL); @@ -237,27 +331,54 @@ pulse_ext_stream_update (PulseExtStream *ext, _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (ext), info->mute ? TRUE : FALSE); - pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (ext), - &info->channel_map); + flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (ext)); + + if (pa_channel_map_valid (&info->channel_map) != 0) { + if (pa_channel_map_can_balance (&info->channel_map) != 0) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + else + flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + + if (pa_channel_map_can_fade (&info->channel_map) != 0) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE; + else + flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_FADE; + + ext->priv->channel_map = info->channel_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 (&ext->priv->channel_map); + } - pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (ext), - &info->volume, - 0); + if (pa_cvolume_valid (&info->volume) != 0) { + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; + if (pa_cvolume_equal (&ext->priv->cvolume, &info->volume) == 0) + volume_changed = TRUE; + } else { + flags &= ~(MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE); + + if (ext->priv->volume != (guint) PA_VOLUME_MUTED) + volume_changed = TRUE; + } + + if (volume_changed == TRUE) + store_cvolume (ext, &info->volume); + + _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (ext), flags); + + /* Also set initially, but may change at any time */ _mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (ext), MATE_MIXER_STREAM (parent)); g_object_thaw_notify (G_OBJECT (ext)); } -static MateMixerDirection -pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc) -{ - g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_DIRECTION_UNKNOWN); - - return PULSE_EXT_STREAM (mmsc)->priv->direction; -} - static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc) { @@ -269,82 +390,277 @@ pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc) static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms) { + PulseExtStream *ext; pa_ext_stream_restore_info info; g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); g_return_val_if_fail (mms == NULL || PULSE_IS_STREAM (mms), FALSE); - fill_ext_stream_restore_info (PULSE_STREAM_CONTROL (mmsc), &info); + ext = PULSE_EXT_STREAM (mmsc); + fill_ext_stream_restore_info (ext, &info); if (mms != NULL) info.device = mate_mixer_stream_get_name (mms); else info.device = NULL; - return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (PULSE_STREAM_CONTROL (mmsc)), - &info); + return pulse_connection_write_ext_stream (ext->priv->connection, &info); } static gboolean -pulse_ext_stream_set_mute (PulseStreamControl *control, gboolean mute) +pulse_ext_stream_set_mute (MateMixerStreamControl *mmsc, gboolean mute) { + PulseExtStream *ext; pa_ext_stream_restore_info info; - g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); - fill_ext_stream_restore_info (control, &info); + ext = PULSE_EXT_STREAM (mmsc); + fill_ext_stream_restore_info (ext, &info); info.mute = mute; - return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), - &info); + return pulse_connection_write_ext_stream (ext->priv->connection, &info); +} + +static guint +pulse_ext_stream_get_num_channels (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), 0); + + return PULSE_EXT_STREAM (mmsc)->priv->channel_map.channels; +} + +static guint +pulse_ext_stream_get_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED); + + return PULSE_EXT_STREAM (mmsc)->priv->volume; } static gboolean -pulse_ext_stream_set_volume (PulseStreamControl *control, pa_cvolume *cvolume) +pulse_ext_stream_set_volume (MateMixerStreamControl *mmsc, guint volume) { - pa_ext_stream_restore_info info; + PulseExtStream *ext; + pa_cvolume cvolume; - g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); - fill_ext_stream_restore_info (control, &info); + ext = PULSE_EXT_STREAM (mmsc); + cvolume = ext->priv->cvolume; - info.volume = *cvolume; + /* Modify a temporary cvolume structure as the change may be irreversible */ + if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) + return FALSE; + + return write_cvolume (ext, &cvolume); +} + +static guint +pulse_ext_stream_get_channel_volume (MateMixerStreamControl *mmsc, guint channel) +{ + PulseExtStream *ext; + + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED); + + ext = PULSE_EXT_STREAM (mmsc); + + if (channel >= ext->priv->cvolume.channels) + return (guint) PA_VOLUME_MUTED; + + return (guint) ext->priv->cvolume.values[channel]; +} + +static gboolean +pulse_ext_stream_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume) +{ + PulseExtStream *ext; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); + + ext = PULSE_EXT_STREAM (mmsc); - return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), - &info); + if (channel >= ext->priv->cvolume.channels) + return FALSE; + + /* Modify a temporary cvolume structure as the change may be irreversible */ + cvolume = ext->priv->cvolume; + cvolume.values[channel] = (pa_volume_t) volume; + + return write_cvolume (ext, &cvolume); +} + +static MateMixerChannelPosition +pulse_ext_stream_get_channel_position (MateMixerStreamControl *mmsc, guint channel) +{ + PulseExtStream *ext; + + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_CHANNEL_UNKNOWN); + + ext = PULSE_EXT_STREAM (mmsc); + + if (channel >= ext->priv->channel_map.channels) + return MATE_MIXER_CHANNEL_UNKNOWN; + + return pulse_convert_position_from_pulse (ext->priv->channel_map.map[channel]); +} + +static gboolean +pulse_ext_stream_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position) +{ + PulseExtStream *ext; + pa_channel_position_t p; + + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); + + ext = PULSE_EXT_STREAM (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 (&ext->priv->channel_map, p) != 0) + return TRUE; + else + return FALSE; +} + +static gboolean +pulse_ext_stream_set_balance (MateMixerStreamControl *mmsc, gfloat balance) +{ + PulseExtStream *ext; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); + + ext = PULSE_EXT_STREAM (mmsc); + cvolume = ext->priv->cvolume; + + /* Modify a temporary cvolume structure as the change may be irreversible */ + if (pa_cvolume_set_balance (&cvolume, &ext->priv->channel_map, balance) == NULL) + return FALSE; + + return write_cvolume (ext, &cvolume); +} + +static gboolean +pulse_ext_stream_set_fade (MateMixerStreamControl *mmsc, gfloat fade) +{ + PulseExtStream *ext; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); + + ext = PULSE_EXT_STREAM (mmsc); + cvolume = ext->priv->cvolume; + + /* Modify a temporary cvolume structure as the change may be irreversible */ + if (pa_cvolume_set_fade (&cvolume, &ext->priv->channel_map, fade) == NULL) + return FALSE; + + return write_cvolume (ext, &cvolume); +} + +static guint +pulse_ext_stream_get_min_volume (MateMixerStreamControl *mmsc) +{ + return (guint) PA_VOLUME_MUTED; +} + +static guint +pulse_ext_stream_get_max_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED); + + return (guint) PA_VOLUME_UI_MAX; +} + +static guint +pulse_ext_stream_get_normal_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED); + + return (guint) PA_VOLUME_NORM; +} + +static guint +pulse_ext_stream_get_base_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED); + + /* Base volume is not supported/used in ext-streams */ + return (guint) PA_VOLUME_NORM; } static void -fill_ext_stream_restore_info (PulseStreamControl *control, +fill_ext_stream_restore_info (PulseExtStream *ext, pa_ext_stream_restore_info *info) { - MateMixerStream *stream; + MateMixerStream *mms; MateMixerStreamControl *mmsc; - const pa_channel_map *map; - const pa_cvolume *cvolume; - mmsc = MATE_MIXER_STREAM_CONTROL (control); + mmsc = MATE_MIXER_STREAM_CONTROL (ext); info->name = mate_mixer_stream_control_get_name (mmsc); info->mute = mate_mixer_stream_control_get_mute (mmsc); + info->volume = ext->priv->cvolume; + info->channel_map = ext->priv->channel_map; - map = pulse_stream_control_get_channel_map (control); - if G_LIKELY (map != NULL) - info->channel_map = *map; + mms = mate_mixer_stream_control_get_stream (mmsc); + if (mms != NULL) + info->device = mate_mixer_stream_get_name (mms); else - pa_channel_map_init (&info->channel_map); + info->device = NULL; +} - cvolume = pulse_stream_control_get_cvolume (control); - if G_LIKELY (cvolume != NULL) - info->volume = *cvolume; - else - pa_cvolume_init (&info->volume); +static gboolean +write_cvolume (PulseExtStream *ext, const pa_cvolume *cvolume) +{ + pa_ext_stream_restore_info info; - stream = mate_mixer_stream_control_get_stream (mmsc); - if (stream != NULL) - info->device = mate_mixer_stream_get_name (stream); - else - info->device = NULL; + /* Make sure to only store a valid and modified volume */ + if (pa_cvolume_valid (cvolume) == 0) + return FALSE; + if (pa_cvolume_equal (cvolume, &ext->priv->cvolume) != 0) + return TRUE; + + fill_ext_stream_restore_info (ext, &info); + info.volume = *cvolume; + + if (pulse_connection_write_ext_stream (ext->priv->connection, &info) == FALSE) + return FALSE; + + store_cvolume (ext, cvolume); + return TRUE; +} + +static void +store_cvolume (PulseExtStream *ext, const pa_cvolume *cvolume) +{ + gfloat value; + + /* Avoid validating whether the volume has changed, it should be done by + * the caller */ + ext->priv->cvolume = *cvolume; + ext->priv->volume = (guint) pa_cvolume_max (cvolume); + + g_object_notify (G_OBJECT (ext), "volume"); + + /* PulseAudio returns the default 0.0f value on error, so skip checking validity + * of the channel map and cvolume */ + value = pa_cvolume_get_balance (&ext->priv->cvolume, + &ext->priv->channel_map); + + _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (ext), value); + + value = pa_cvolume_get_fade (&ext->priv->cvolume, + &ext->priv->channel_map); + + _mate_mixer_stream_control_set_fade (MATE_MIXER_STREAM_CONTROL (ext), value); } diff --git a/backends/pulse/pulse-ext-stream.h b/backends/pulse/pulse-ext-stream.h index b667dc7..af3d75f 100644 --- a/backends/pulse/pulse-ext-stream.h +++ b/backends/pulse/pulse-ext-stream.h @@ -47,7 +47,7 @@ typedef struct _PulseExtStreamPrivate PulseExtStreamPrivate; struct _PulseExtStream { - PulseStreamControl parent; + MateMixerStoredControl parent; /*< private >*/ PulseExtStreamPrivate *priv; @@ -55,7 +55,7 @@ struct _PulseExtStream struct _PulseExtStreamClass { - PulseStreamControlClass parent_class; + MateMixerStoredControlClass parent_class; }; GType pulse_ext_stream_get_type (void) G_GNUC_CONST; diff --git a/docs/reference/libmatemixer-sections.txt b/docs/reference/libmatemixer-sections.txt index d3545f6..911d43d 100644 --- a/docs/reference/libmatemixer-sections.txt +++ b/docs/reference/libmatemixer-sections.txt @@ -91,14 +91,18 @@ mate_mixer_device_get_type <SECTION> <FILE>matemixer-stored-control</FILE> <TITLE>MateMixerStoredControl</TITLE> -MateMixerStoredControlInterface +MateMixerStoredControl +MateMixerStoredControlClass mate_mixer_stored_control_get_direction <SUBSECTION Standard> -MATE_MIXER_IS_STORED_CONTROL MATE_MIXER_STORED_CONTROL -MATE_MIXER_STORED_CONTROL_GET_INTERFACE +MATE_MIXER_STORED_CONTROL_CLASS +MATE_MIXER_STORED_CONTROL_GET_CLASS +MATE_MIXER_IS_STORED_CONTROL +MATE_MIXER_IS_STORED_CONTROL_CLASS MATE_MIXER_TYPE_STORED_CONTROL <SUBSECTION Private> +MateMixerStoredControlPrivate mate_mixer_stored_control_get_type </SECTION> diff --git a/libmatemixer/matemixer-stored-control.c b/libmatemixer/matemixer-stored-control.c index eb2a448..d150490 100644 --- a/libmatemixer/matemixer-stored-control.c +++ b/libmatemixer/matemixer-stored-control.c @@ -23,20 +23,104 @@ #include "matemixer-stream-control.h" #include "matemixer-stored-control.h" -G_DEFINE_INTERFACE (MateMixerStoredControl, mate_mixer_stored_control, MATE_MIXER_TYPE_STREAM_CONTROL) +struct _MateMixerStoredControlPrivate +{ + MateMixerDirection direction; +}; + +enum { + PROP_0, + PROP_DIRECTION, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void mate_mixer_stored_control_class_init (MateMixerStoredControlClass *klass); + +static void mate_mixer_stored_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_stored_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_stored_control_init (MateMixerStoredControl *control); + +G_DEFINE_ABSTRACT_TYPE (MateMixerStoredControl, mate_mixer_stored_control, MATE_MIXER_TYPE_STREAM_CONTROL) + +static void +mate_mixer_stored_control_class_init (MateMixerStoredControlClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = mate_mixer_stored_control_get_property; + object_class->set_property = mate_mixer_stored_control_set_property; + + properties[PROP_DIRECTION] = + g_param_spec_enum ("direction", + "Direction", + "Direction of the stored control", + MATE_MIXER_TYPE_DIRECTION, + MATE_MIXER_DIRECTION_UNKNOWN, + 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 (MateMixerStoredControlPrivate)); +} + +static void +mate_mixer_stored_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerStoredControl *control; + + control = MATE_MIXER_STORED_CONTROL (object); + + switch (param_id) { + case PROP_DIRECTION: + g_value_set_enum (value, control->priv->direction); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_stored_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerStoredControl *control; + + control = MATE_MIXER_STORED_CONTROL (object); + + switch (param_id) { + case PROP_DIRECTION: + control->priv->direction = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} static void -mate_mixer_stored_control_default_init (MateMixerStoredControlInterface *iface) +mate_mixer_stored_control_init (MateMixerStoredControl *control) { - g_object_interface_install_property (iface, - g_param_spec_enum ("direction", - "Direction", - "Direction of the stored control", - MATE_MIXER_TYPE_DIRECTION, - MATE_MIXER_DIRECTION_UNKNOWN, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); + control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, + MATE_MIXER_TYPE_STORED_CONTROL, + MateMixerStoredControlPrivate); } /** @@ -48,5 +132,5 @@ mate_mixer_stored_control_get_direction (MateMixerStoredControl *control) { g_return_val_if_fail (MATE_MIXER_IS_STORED_CONTROL (control), MATE_MIXER_DIRECTION_UNKNOWN); - return MATE_MIXER_STORED_CONTROL_GET_INTERFACE (control)->get_direction (control); + return control->priv->direction; } diff --git a/libmatemixer/matemixer-stored-control.h b/libmatemixer/matemixer-stored-control.h index 09731ef..d21209e 100644 --- a/libmatemixer/matemixer-stored-control.h +++ b/libmatemixer/matemixer-stored-control.h @@ -22,33 +22,50 @@ #include <glib-object.h> #include <libmatemixer/matemixer-enums.h> +#include <libmatemixer/matemixer-stream-control.h> #include <libmatemixer/matemixer-types.h> G_BEGIN_DECLS -#define MATE_MIXER_TYPE_STORED_CONTROL \ +#define MATE_MIXER_TYPE_STORED_CONTROL \ (mate_mixer_stored_control_get_type ()) -#define MATE_MIXER_STORED_CONTROL(o) \ +#define MATE_MIXER_STORED_CONTROL(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControl)) -#define MATE_MIXER_IS_STORED_CONTROL(o) \ +#define MATE_MIXER_IS_STORED_CONTROL(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_STORED_CONTROL)) -#define MATE_MIXER_STORED_CONTROL_GET_INTERFACE(o) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControlInterface)) +#define MATE_MIXER_STORED_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControlClass)) +#define MATE_MIXER_IS_STORED_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_STORED_CONTROL)) +#define MATE_MIXER_STORED_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControlClass)) -typedef struct _MateMixerStoredControlInterface MateMixerStoredControlInterface; +typedef struct _MateMixerStoredControlClass MateMixerStoredControlClass; +typedef struct _MateMixerStoredControlPrivate MateMixerStoredControlPrivate; /** - * MateMixerStoredControlInterface: - * @parent_iface: The parent interface. + * MateMixerStoredControl: * - * The interface structure for #MateMixerStoredControl. + * The #MateMixerStoredControl structure contains only private data and should only + * be accessed using the provided API. */ -struct _MateMixerStoredControlInterface +struct _MateMixerStoredControl { - GTypeInterface parent_iface; + MateMixerStreamControl object; /*< private >*/ - MateMixerDirection (*get_direction) (MateMixerStoredControl *control); + MateMixerStoredControlPrivate *priv; +}; + +/** + * MateMixerStoredControlClass: + * @parent_class: The parent class. + * + * The class structure for #MateMixerStoredControl. + */ +struct _MateMixerStoredControlClass +{ + MateMixerStreamControlClass parent_class; }; GType mate_mixer_stored_control_get_type (void) G_GNUC_CONST; |