1770 lines
71 KiB
Diff
1770 lines
71 KiB
Diff
From dffcd6f60cb6984b28d673d013b436de4ee54f4e Mon Sep 17 00:00:00 2001
|
|
From: Bastien Nocera <hadess@hadess.net>
|
|
Date: Mon, 22 Jun 2009 15:40:19 +0100
|
|
Subject: [PATCH 1/4] Update gnome-volume-control code from master
|
|
|
|
And make slight changes to gsd-media-keys-manager.c to match
|
|
the new API.
|
|
---
|
|
plugins/media-keys/cut-n-paste/gvc-channel-map.c | 175 +++++++++++--
|
|
plugins/media-keys/cut-n-paste/gvc-channel-map.h | 24 ++-
|
|
plugins/media-keys/cut-n-paste/gvc-mixer-control.c | 276 ++++++++++++++++----
|
|
plugins/media-keys/cut-n-paste/gvc-mixer-control.h | 1 +
|
|
.../media-keys/cut-n-paste/gvc-mixer-event-role.c | 29 +-
|
|
.../media-keys/cut-n-paste/gvc-mixer-event-role.h | 5 +-
|
|
.../media-keys/cut-n-paste/gvc-mixer-sink-input.c | 40 ++--
|
|
plugins/media-keys/cut-n-paste/gvc-mixer-sink.c | 53 ++--
|
|
.../cut-n-paste/gvc-mixer-source-output.c | 6 +-
|
|
plugins/media-keys/cut-n-paste/gvc-mixer-source.c | 48 ++--
|
|
plugins/media-keys/cut-n-paste/gvc-mixer-stream.c | 225 ++++++++++++++---
|
|
plugins/media-keys/cut-n-paste/gvc-mixer-stream.h | 23 ++-
|
|
plugins/media-keys/gsd-media-keys-manager.c | 10 +-
|
|
13 files changed, 708 insertions(+), 207 deletions(-)
|
|
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.c b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
|
|
index e7c9b22..32750ef 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
|
|
@@ -33,15 +33,26 @@
|
|
|
|
#define GVC_CHANNEL_MAP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapPrivate))
|
|
|
|
+#ifndef PA_CHECK_VERSION
|
|
+#define PA_CHECK_VERSION(major,minor,micro) \
|
|
+ ((PA_MAJOR > (major)) || \
|
|
+ (PA_MAJOR == (major) && PA_MINOR > (minor)) || \
|
|
+ (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
|
|
+#endif
|
|
+
|
|
+
|
|
struct GvcChannelMapPrivate
|
|
{
|
|
- guint num_channels;
|
|
- pa_channel_position_t positions[PA_CHANNELS_MAX];
|
|
- gdouble gains[PA_CHANNELS_MAX];
|
|
+ pa_channel_map pa_map;
|
|
+ pa_cvolume pa_volume;
|
|
+ gdouble extern_volume[NUM_TYPES]; /* volume, balance, fade, lfe */
|
|
+ gboolean can_balance;
|
|
+ gboolean can_fade;
|
|
+ gboolean has_lfe;
|
|
};
|
|
|
|
enum {
|
|
- GAINS_CHANGED,
|
|
+ VOLUME_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
@@ -53,25 +64,135 @@ static void gvc_channel_map_finalize (GObject *object);
|
|
|
|
G_DEFINE_TYPE (GvcChannelMap, gvc_channel_map, G_TYPE_OBJECT)
|
|
|
|
+/* FIXME remove when we depend on a newer PA */
|
|
+static int
|
|
+gvc_pa_channel_map_has_position (const pa_channel_map *map, pa_channel_position_t p) {
|
|
+ unsigned c;
|
|
+
|
|
+ g_return_val_if_fail(pa_channel_map_valid(map), 0);
|
|
+ g_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
|
|
+
|
|
+ for (c = 0; c < map->channels; c++)
|
|
+ if (map->map[c] == p)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#if !PA_CHECK_VERSION(0,9,16)
|
|
+/* The PulseAudio master increase version only when tagged, so let's avoid clashing with pa_ namespace */
|
|
+#define pa_cvolume_get_position gvc_cvolume_get_position
|
|
+static pa_volume_t
|
|
+gvc_cvolume_get_position (pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) {
|
|
+ unsigned c;
|
|
+ pa_volume_t v = PA_VOLUME_MUTED;
|
|
+
|
|
+ g_assert(cv);
|
|
+ g_assert(map);
|
|
+
|
|
+ g_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED);
|
|
+ g_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED);
|
|
+
|
|
+ for (c = 0; c < map->channels; c++)
|
|
+ if (map->map[c] == t)
|
|
+ if (cv->values[c] > v)
|
|
+ v = cv->values[c];
|
|
+
|
|
+ return v;
|
|
+}
|
|
+#endif
|
|
+
|
|
guint
|
|
gvc_channel_map_get_num_channels (GvcChannelMap *map)
|
|
{
|
|
g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), 0);
|
|
- return map->priv->num_channels;
|
|
+
|
|
+ if (!pa_channel_map_valid(&map->priv->pa_map))
|
|
+ return 0;
|
|
+
|
|
+ return map->priv->pa_map.channels;
|
|
+}
|
|
+
|
|
+const gdouble *
|
|
+gvc_channel_map_get_volume (GvcChannelMap *map)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
|
|
+
|
|
+ if (!pa_channel_map_valid(&map->priv->pa_map))
|
|
+ return NULL;
|
|
+
|
|
+ map->priv->extern_volume[VOLUME] = (gdouble) pa_cvolume_max (&map->priv->pa_volume);
|
|
+ if (gvc_channel_map_can_balance (map))
|
|
+ map->priv->extern_volume[BALANCE] = (gdouble) pa_cvolume_get_balance (&map->priv->pa_volume, &map->priv->pa_map);
|
|
+ else
|
|
+ map->priv->extern_volume[BALANCE] = 0;
|
|
+ if (gvc_channel_map_can_fade (map))
|
|
+ map->priv->extern_volume[FADE] = (gdouble) pa_cvolume_get_fade (&map->priv->pa_volume, &map->priv->pa_map);
|
|
+ else
|
|
+ map->priv->extern_volume[FADE] = 0;
|
|
+ if (gvc_channel_map_has_lfe (map))
|
|
+ map->priv->extern_volume[LFE] = (gdouble) pa_cvolume_get_position (&map->priv->pa_volume, &map->priv->pa_map, PA_CHANNEL_POSITION_LFE);
|
|
+ else
|
|
+ map->priv->extern_volume[LFE] = 0;
|
|
+
|
|
+ return map->priv->extern_volume;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gvc_channel_map_can_balance (GvcChannelMap *map)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
|
|
+
|
|
+ return map->priv->can_balance;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gvc_channel_map_can_fade (GvcChannelMap *map)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
|
|
+
|
|
+ return map->priv->can_fade;
|
|
+}
|
|
+
|
|
+const char *
|
|
+gvc_channel_map_get_mapping (GvcChannelMap *map)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
|
|
+
|
|
+ if (!pa_channel_map_valid(&map->priv->pa_map))
|
|
+ return NULL;
|
|
+
|
|
+ return pa_channel_map_to_pretty_name (&map->priv->pa_map);
|
|
}
|
|
|
|
-gdouble *
|
|
-gvc_channel_map_get_gains (GvcChannelMap *map)
|
|
+gboolean
|
|
+gvc_channel_map_has_lfe (GvcChannelMap *map)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
|
|
+
|
|
+ return map->priv->has_lfe;
|
|
+}
|
|
+
|
|
+const pa_channel_map *
|
|
+gvc_channel_map_get_pa_channel_map (GvcChannelMap *map)
|
|
{
|
|
g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
|
|
- return map->priv->gains;
|
|
+
|
|
+ if (!pa_channel_map_valid(&map->priv->pa_map))
|
|
+ return NULL;
|
|
+
|
|
+ return &map->priv->pa_map;
|
|
}
|
|
|
|
-pa_channel_position_t *
|
|
-gvc_channel_map_get_positions (GvcChannelMap *map)
|
|
+const pa_cvolume *
|
|
+gvc_channel_map_get_cvolume (GvcChannelMap *map)
|
|
{
|
|
g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
|
|
- return map->priv->positions;
|
|
+
|
|
+ if (!pa_channel_map_valid(&map->priv->pa_map))
|
|
+ return NULL;
|
|
+
|
|
+ return &map->priv->pa_volume;
|
|
}
|
|
|
|
static void
|
|
@@ -81,11 +202,11 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
|
|
|
|
gobject_class->finalize = gvc_channel_map_finalize;
|
|
|
|
- signals [GAINS_CHANGED] =
|
|
- g_signal_new ("gains-changed",
|
|
+ signals [VOLUME_CHANGED] =
|
|
+ g_signal_new ("volume-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
- G_STRUCT_OFFSET (GvcChannelMapClass, gains_changed),
|
|
+ G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
@@ -94,10 +215,19 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
|
|
}
|
|
|
|
void
|
|
-gvc_channel_map_gains_changed (GvcChannelMap *map)
|
|
+gvc_channel_map_volume_changed (GvcChannelMap *map,
|
|
+ const pa_cvolume *cv)
|
|
{
|
|
g_return_if_fail (GVC_IS_CHANNEL_MAP (map));
|
|
- g_signal_emit (map, signals[GAINS_CHANGED], 0);
|
|
+ g_return_if_fail (cv != NULL);
|
|
+ g_return_if_fail (pa_cvolume_compatible_with_channel_map(cv, &map->priv->pa_map));
|
|
+
|
|
+ if (pa_cvolume_equal(cv, &map->priv->pa_volume))
|
|
+ return;
|
|
+
|
|
+ map->priv->pa_volume = *cv;
|
|
+
|
|
+ g_signal_emit (map, signals[VOLUME_CHANGED], 0);
|
|
}
|
|
|
|
static void
|
|
@@ -133,13 +263,14 @@ static void
|
|
set_from_pa_map (GvcChannelMap *map,
|
|
const pa_channel_map *pa_map)
|
|
{
|
|
- guint i;
|
|
+ g_assert (pa_channel_map_valid(pa_map));
|
|
+
|
|
+ map->priv->can_balance = pa_channel_map_can_balance (pa_map);
|
|
+ map->priv->can_fade = pa_channel_map_can_fade (pa_map);
|
|
+ map->priv->has_lfe = gvc_pa_channel_map_has_position (pa_map, PA_CHANNEL_POSITION_LFE);
|
|
|
|
- map->priv->num_channels = pa_map->channels;
|
|
- for (i = 0; i < pa_map->channels; i++) {
|
|
- map->priv->positions[i] = pa_map->map[i];
|
|
- map->priv->gains[i] = 1.0;
|
|
- }
|
|
+ map->priv->pa_map = *pa_map;
|
|
+ pa_cvolume_set(&map->priv->pa_volume, pa_map->channels, PA_VOLUME_NORM);
|
|
}
|
|
|
|
GvcChannelMap *
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.h b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
|
|
index 963904f..b35c9cb 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.h
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
|
|
@@ -44,19 +44,35 @@ typedef struct
|
|
typedef struct
|
|
{
|
|
GObjectClass parent_class;
|
|
- void (*gains_changed) (GvcChannelMap *channel_map);
|
|
+ void (*volume_changed) (GvcChannelMap *channel_map);
|
|
} GvcChannelMapClass;
|
|
|
|
+enum {
|
|
+ VOLUME,
|
|
+ BALANCE,
|
|
+ FADE,
|
|
+ LFE,
|
|
+};
|
|
+
|
|
+#define NUM_TYPES LFE + 1
|
|
+
|
|
GType gvc_channel_map_get_type (void);
|
|
|
|
GvcChannelMap * gvc_channel_map_new (void);
|
|
GvcChannelMap * gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *map);
|
|
guint gvc_channel_map_get_num_channels (GvcChannelMap *map);
|
|
-pa_channel_position_t * gvc_channel_map_get_positions (GvcChannelMap *map);
|
|
-gdouble * gvc_channel_map_get_gains (GvcChannelMap *map);
|
|
+const gdouble * gvc_channel_map_get_volume (GvcChannelMap *map);
|
|
+gboolean gvc_channel_map_can_balance (GvcChannelMap *map);
|
|
+gboolean gvc_channel_map_can_fade (GvcChannelMap *map);
|
|
+gboolean gvc_channel_map_has_lfe (GvcChannelMap *map);
|
|
|
|
-void gvc_channel_map_gains_changed (GvcChannelMap *map);
|
|
+void gvc_channel_map_volume_changed (GvcChannelMap *map,
|
|
+ const pa_cvolume *cv);
|
|
+const char * gvc_channel_map_get_mapping (GvcChannelMap *map);
|
|
|
|
+/* private */
|
|
+const pa_cvolume * gvc_channel_map_get_cvolume (GvcChannelMap *map);
|
|
+const pa_channel_map * gvc_channel_map_get_pa_channel_map (GvcChannelMap *map);
|
|
G_END_DECLS
|
|
|
|
#endif /* __GVC_CHANNEL_MAP_H */
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
|
|
index e7dd18b..92b0286 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
|
|
@@ -48,6 +48,7 @@ struct GvcMixerControlPrivate
|
|
pa_mainloop_api *pa_api;
|
|
pa_context *pa_context;
|
|
int n_outstanding;
|
|
+ guint reconnect_id;
|
|
|
|
gboolean default_sink_is_set;
|
|
guint default_sink_id;
|
|
@@ -65,9 +66,12 @@ struct GvcMixerControlPrivate
|
|
GHashTable *sink_inputs; /* routable output streams */
|
|
GHashTable *source_outputs; /* routable input streams */
|
|
GHashTable *clients;
|
|
+
|
|
+ GvcMixerStream *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
|
|
};
|
|
|
|
enum {
|
|
+ CONNECTING,
|
|
READY,
|
|
STREAM_ADDED,
|
|
STREAM_REMOVED,
|
|
@@ -104,6 +108,42 @@ gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
|
|
return stream;
|
|
}
|
|
|
|
+static void
|
|
+gvc_mixer_control_stream_restore_cb (pa_context *c,
|
|
+ const pa_ext_stream_restore_info *info,
|
|
+ int eol,
|
|
+ void *userdata)
|
|
+{
|
|
+ pa_operation *o;
|
|
+ GvcMixerControl *control = (GvcMixerControl *) userdata;
|
|
+ pa_ext_stream_restore_info new_info;
|
|
+
|
|
+ if (eol || control->priv->new_default_stream == NULL)
|
|
+ return;
|
|
+
|
|
+ new_info.name = info->name;
|
|
+ new_info.channel_map = info->channel_map;
|
|
+ new_info.volume = info->volume;
|
|
+ new_info.mute = info->mute;
|
|
+
|
|
+ new_info.device = gvc_mixer_stream_get_name (control->priv->new_default_stream);
|
|
+
|
|
+ o = pa_ext_stream_restore_write (control->priv->pa_context,
|
|
+ PA_UPDATE_REPLACE,
|
|
+ &new_info, 1,
|
|
+ TRUE, NULL, NULL);
|
|
+
|
|
+ if (o == NULL) {
|
|
+ g_warning ("pa_ext_stream_restore_write() failed: %s",
|
|
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_debug ("Changed default device for %s to %s", info->name, info->device);
|
|
+
|
|
+ pa_operation_unref (o);
|
|
+}
|
|
+
|
|
gboolean
|
|
gvc_mixer_control_set_default_sink (GvcMixerControl *control,
|
|
GvcMixerStream *stream)
|
|
@@ -118,7 +158,23 @@ gvc_mixer_control_set_default_sink (GvcMixerControl *control,
|
|
NULL,
|
|
NULL);
|
|
if (o == NULL) {
|
|
- g_warning ("pa_context_set_default_sink() failed");
|
|
+ g_warning ("pa_context_set_default_sink() failed: %s",
|
|
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ pa_operation_unref (o);
|
|
+
|
|
+ control->priv->new_default_stream = stream;
|
|
+ g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_stream);
|
|
+
|
|
+ o = pa_ext_stream_restore_read (control->priv->pa_context,
|
|
+ gvc_mixer_control_stream_restore_cb,
|
|
+ control);
|
|
+
|
|
+ if (o == NULL) {
|
|
+ g_warning ("pa_ext_stream_restore_read() failed: %s",
|
|
+ pa_strerror (pa_context_errno (control->priv->pa_context)));
|
|
return FALSE;
|
|
}
|
|
|
|
@@ -220,6 +276,13 @@ gvc_stream_collate (GvcMixerStream *a,
|
|
namea = gvc_mixer_stream_get_name (a);
|
|
nameb = gvc_mixer_stream_get_name (b);
|
|
|
|
+ if (nameb == NULL && namea == NULL)
|
|
+ return 0;
|
|
+ if (nameb == NULL)
|
|
+ return 1;
|
|
+ if (namea == NULL)
|
|
+ return -1;
|
|
+
|
|
return g_utf8_collate (namea, nameb);
|
|
}
|
|
|
|
@@ -320,12 +383,14 @@ _set_default_source (GvcMixerControl *control,
|
|
{
|
|
guint new_id;
|
|
|
|
- new_id = 0;
|
|
-
|
|
- if (stream != NULL) {
|
|
- new_id = gvc_mixer_stream_get_id (stream);
|
|
+ if (stream == NULL) {
|
|
+ control->priv->default_source_id = 0;
|
|
+ control->priv->default_source_is_set = FALSE;
|
|
+ return;
|
|
}
|
|
|
|
+ new_id = gvc_mixer_stream_get_id (stream);
|
|
+
|
|
if (control->priv->default_source_id != new_id) {
|
|
control->priv->default_source_id = new_id;
|
|
control->priv->default_source_is_set = TRUE;
|
|
@@ -342,12 +407,14 @@ _set_default_sink (GvcMixerControl *control,
|
|
{
|
|
guint new_id;
|
|
|
|
- new_id = 0;
|
|
-
|
|
- if (stream != NULL) {
|
|
- new_id = gvc_mixer_stream_get_id (stream);
|
|
+ if (stream == NULL) {
|
|
+ control->priv->default_sink_id = 0;
|
|
+ control->priv->default_sink_is_set = FALSE;
|
|
+ return;
|
|
}
|
|
|
|
+ new_id = gvc_mixer_stream_get_id (stream);
|
|
+
|
|
if (control->priv->default_sink_id != new_id) {
|
|
control->priv->default_sink_id = new_id;
|
|
control->priv->default_sink_is_set = TRUE;
|
|
@@ -495,6 +562,7 @@ update_sink (GvcMixerControl *control,
|
|
GvcMixerStream *stream;
|
|
gboolean is_new;
|
|
pa_volume_t max_volume;
|
|
+ GvcChannelMap *map;
|
|
char map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];
|
|
|
|
pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
|
|
@@ -506,22 +574,21 @@ update_sink (GvcMixerControl *control,
|
|
map_buff);
|
|
#endif
|
|
|
|
- /* for now completely ignore virtual streams */
|
|
- if (!(info->flags & PA_SINK_HARDWARE)) {
|
|
- return;
|
|
- }
|
|
-
|
|
+ map = NULL;
|
|
is_new = FALSE;
|
|
stream = g_hash_table_lookup (control->priv->sinks,
|
|
GUINT_TO_POINTER (info->index));
|
|
if (stream == NULL) {
|
|
- GvcChannelMap *map;
|
|
map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
|
|
stream = gvc_mixer_sink_new (control->priv->pa_context,
|
|
info->index,
|
|
map);
|
|
g_object_unref (map);
|
|
is_new = TRUE;
|
|
+ } else if (gvc_mixer_stream_is_running (stream)) {
|
|
+ /* Ignore events if volume changes are outstanding */
|
|
+ g_debug ("Ignoring event, volume changes are outstanding");
|
|
+ return;
|
|
}
|
|
|
|
max_volume = pa_cvolume_max (&info->volume);
|
|
@@ -531,11 +598,6 @@ update_sink (GvcMixerControl *control,
|
|
gvc_mixer_stream_set_volume (stream, (guint)max_volume);
|
|
gvc_mixer_stream_set_is_muted (stream, info->mute);
|
|
gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME));
|
|
- if (!!(info->flags & PA_SINK_DECIBEL_VOLUME)) {
|
|
- gdouble db;
|
|
- db = pa_sw_volume_to_dB (max_volume);
|
|
- gvc_mixer_stream_set_decibel (stream, db);
|
|
- }
|
|
|
|
if (is_new) {
|
|
g_hash_table_insert (control->priv->sinks,
|
|
@@ -549,6 +611,10 @@ update_sink (GvcMixerControl *control,
|
|
&& strcmp (control->priv->default_sink_name, info->name) == 0) {
|
|
_set_default_sink (control, stream);
|
|
}
|
|
+
|
|
+ if (map == NULL)
|
|
+ map = gvc_mixer_stream_get_channel_map (stream);
|
|
+ gvc_channel_map_volume_changed (map, &info->volume);
|
|
}
|
|
|
|
static void
|
|
@@ -566,8 +632,8 @@ update_source (GvcMixerControl *control,
|
|
info->description);
|
|
#endif
|
|
|
|
- /* for now completely ignore virtual streams */
|
|
- if (!(info->flags & PA_SOURCE_HARDWARE)) {
|
|
+ /* completely ignore monitors, they're not real sources */
|
|
+ if (info->monitor_of_sink != PA_INVALID_INDEX) {
|
|
return;
|
|
}
|
|
|
|
@@ -583,6 +649,10 @@ update_source (GvcMixerControl *control,
|
|
map);
|
|
g_object_unref (map);
|
|
is_new = TRUE;
|
|
+ } else if (gvc_mixer_stream_is_running (stream)) {
|
|
+ /* Ignore events if volume changes are outstanding */
|
|
+ g_debug ("Ignoring event, volume changes are outstanding");
|
|
+ return;
|
|
}
|
|
|
|
max_volume = pa_cvolume_max (&info->volume);
|
|
@@ -593,11 +663,7 @@ update_source (GvcMixerControl *control,
|
|
gvc_mixer_stream_set_volume (stream, (guint)max_volume);
|
|
gvc_mixer_stream_set_is_muted (stream, info->mute);
|
|
gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME));
|
|
- if (!!(info->flags & PA_SINK_DECIBEL_VOLUME)) {
|
|
- gdouble db;
|
|
- db = pa_sw_volume_to_dB (max_volume);
|
|
- gvc_mixer_stream_set_decibel (stream, db);
|
|
- }
|
|
+ gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
|
|
|
|
if (is_new) {
|
|
g_hash_table_insert (control->priv->sources,
|
|
@@ -662,6 +728,34 @@ set_icon_name_from_proplist (GvcMixerStream *stream,
|
|
}
|
|
|
|
static void
|
|
+set_is_event_stream_from_proplist (GvcMixerStream *stream,
|
|
+ pa_proplist *l)
|
|
+{
|
|
+ const char *t;
|
|
+ gboolean is_event_stream;
|
|
+
|
|
+ is_event_stream = FALSE;
|
|
+
|
|
+ if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
|
|
+ if (g_str_equal (t, "event"))
|
|
+ is_event_stream = TRUE;
|
|
+ }
|
|
+
|
|
+ gvc_mixer_stream_set_is_event_stream (stream, is_event_stream);
|
|
+}
|
|
+
|
|
+static void
|
|
+set_application_id_from_proplist (GvcMixerStream *stream,
|
|
+ pa_proplist *l)
|
|
+{
|
|
+ const char *t;
|
|
+
|
|
+ if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) {
|
|
+ gvc_mixer_stream_set_application_id (stream, t);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
update_sink_input (GvcMixerControl *control,
|
|
const pa_sink_input_info *info)
|
|
{
|
|
@@ -690,6 +784,10 @@ update_sink_input (GvcMixerControl *control,
|
|
map);
|
|
g_object_unref (map);
|
|
is_new = TRUE;
|
|
+ } else if (gvc_mixer_stream_is_running (stream)) {
|
|
+ /* Ignore events if volume changes are outstanding */
|
|
+ g_debug ("Ignoring event, volume changes are outstanding");
|
|
+ return;
|
|
}
|
|
|
|
max_volume = pa_cvolume_max (&info->volume);
|
|
@@ -699,9 +797,12 @@ update_sink_input (GvcMixerControl *control,
|
|
gvc_mixer_stream_set_name (stream, name);
|
|
gvc_mixer_stream_set_description (stream, info->name);
|
|
|
|
+ set_application_id_from_proplist (stream, info->proplist);
|
|
+ set_is_event_stream_from_proplist (stream, info->proplist);
|
|
set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
|
|
gvc_mixer_stream_set_volume (stream, (guint)max_volume);
|
|
gvc_mixer_stream_set_is_muted (stream, info->mute);
|
|
+ gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX);
|
|
|
|
if (is_new) {
|
|
g_hash_table_insert (control->priv->sink_inputs,
|
|
@@ -745,7 +846,9 @@ update_source_output (GvcMixerControl *control,
|
|
|
|
gvc_mixer_stream_set_name (stream, name);
|
|
gvc_mixer_stream_set_description (stream, info->name);
|
|
- set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
|
|
+ set_application_id_from_proplist (stream, info->proplist);
|
|
+ set_is_event_stream_from_proplist (stream, info->proplist);
|
|
+ set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
|
|
|
|
if (is_new) {
|
|
g_hash_table_insert (control->priv->source_outputs,
|
|
@@ -938,8 +1041,16 @@ update_event_role_stream (GvcMixerControl *control,
|
|
is_new = FALSE;
|
|
|
|
if (!control->priv->event_sink_input_is_set) {
|
|
+ pa_channel_map pa_map;
|
|
+ GvcChannelMap *map;
|
|
+
|
|
+ pa_map.channels = 1;
|
|
+ pa_map.map[0] = PA_CHANNEL_POSITION_MONO;
|
|
+ map = gvc_channel_map_new_from_pa_channel_map (&pa_map);
|
|
+
|
|
stream = gvc_mixer_event_role_new (control->priv->pa_context,
|
|
- info->device);
|
|
+ info->device,
|
|
+ map);
|
|
control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
|
|
control->priv->event_sink_input_is_set = TRUE;
|
|
|
|
@@ -1346,6 +1457,78 @@ gvc_mixer_control_ready (GvcMixerControl *control)
|
|
}
|
|
|
|
static void
|
|
+gvc_mixer_new_pa_context (GvcMixerControl *self)
|
|
+{
|
|
+ pa_proplist *proplist;
|
|
+
|
|
+ g_return_if_fail (self);
|
|
+ g_return_if_fail (!self->priv->pa_context);
|
|
+
|
|
+ /* FIXME: read these from an object property */
|
|
+ proplist = pa_proplist_new ();
|
|
+ pa_proplist_sets (proplist,
|
|
+ PA_PROP_APPLICATION_NAME,
|
|
+ _("GNOME Volume Control"));
|
|
+ pa_proplist_sets (proplist,
|
|
+ PA_PROP_APPLICATION_ID,
|
|
+ "org.gnome.VolumeControl");
|
|
+ pa_proplist_sets (proplist,
|
|
+ PA_PROP_APPLICATION_ICON_NAME,
|
|
+ "multimedia-volume-control");
|
|
+ pa_proplist_sets (proplist,
|
|
+ PA_PROP_APPLICATION_VERSION,
|
|
+ PACKAGE_VERSION);
|
|
+
|
|
+ self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
|
|
+
|
|
+ pa_proplist_free (proplist);
|
|
+ g_assert (self->priv->pa_context);
|
|
+}
|
|
+
|
|
+static void
|
|
+remove_all_streams (GvcMixerControl *control, GHashTable *hash_table)
|
|
+{
|
|
+ GHashTableIter iter;
|
|
+ gpointer key, value;
|
|
+
|
|
+ g_hash_table_iter_init (&iter, hash_table);
|
|
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
+ remove_stream (control, value);
|
|
+ g_hash_table_iter_remove (&iter);
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+idle_reconnect (gpointer data)
|
|
+{
|
|
+ GvcMixerControl *control = GVC_MIXER_CONTROL (data);
|
|
+ GHashTableIter iter;
|
|
+ gpointer key, value;
|
|
+
|
|
+ g_return_val_if_fail (control, FALSE);
|
|
+
|
|
+ if (control->priv->pa_context) {
|
|
+ pa_context_unref (control->priv->pa_context);
|
|
+ control->priv->pa_context = NULL;
|
|
+ gvc_mixer_new_pa_context (control);
|
|
+ }
|
|
+
|
|
+ remove_all_streams (control, control->priv->sinks);
|
|
+ remove_all_streams (control, control->priv->sources);
|
|
+ remove_all_streams (control, control->priv->sink_inputs);
|
|
+ remove_all_streams (control, control->priv->source_outputs);
|
|
+
|
|
+ g_hash_table_iter_init (&iter, control->priv->clients);
|
|
+ while (g_hash_table_iter_next (&iter, &key, &value))
|
|
+ g_hash_table_iter_remove (&iter);
|
|
+
|
|
+ gvc_mixer_control_open (control); /* cannot fail */
|
|
+
|
|
+ control->priv->reconnect_id = 0;
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static void
|
|
_pa_context_state_cb (pa_context *context,
|
|
void *userdata)
|
|
{
|
|
@@ -1363,7 +1546,9 @@ _pa_context_state_cb (pa_context *context,
|
|
break;
|
|
|
|
case PA_CONTEXT_FAILED:
|
|
- g_warning ("Connection failed");
|
|
+ g_warning ("Connection failed, reconnecting...");
|
|
+ if (control->priv->reconnect_id == 0)
|
|
+ control->priv->reconnect_id = g_timeout_add_seconds (5, idle_reconnect, control);
|
|
break;
|
|
|
|
case PA_CONTEXT_TERMINATED:
|
|
@@ -1386,7 +1571,8 @@ gvc_mixer_control_open (GvcMixerControl *control)
|
|
_pa_context_state_cb,
|
|
control);
|
|
|
|
- res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) 0, NULL);
|
|
+ g_signal_emit (G_OBJECT (control), signals[CONNECTING], 0);
|
|
+ res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL);
|
|
if (res < 0) {
|
|
g_warning ("Failed to connect context: %s",
|
|
pa_strerror (pa_context_errno (control->priv->pa_context)));
|
|
@@ -1465,30 +1651,12 @@ gvc_mixer_control_constructor (GType type,
|
|
{
|
|
GObject *object;
|
|
GvcMixerControl *self;
|
|
- pa_proplist *proplist;
|
|
|
|
object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
|
|
|
|
self = GVC_MIXER_CONTROL (object);
|
|
|
|
- /* FIXME: read these from an object property */
|
|
- proplist = pa_proplist_new ();
|
|
- pa_proplist_sets (proplist,
|
|
- PA_PROP_APPLICATION_NAME,
|
|
- _("GNOME Volume Control"));
|
|
- pa_proplist_sets (proplist,
|
|
- PA_PROP_APPLICATION_ID,
|
|
- "org.gnome.VolumeControl");
|
|
- pa_proplist_sets (proplist,
|
|
- PA_PROP_APPLICATION_ICON_NAME,
|
|
- "multimedia-volume-control");
|
|
- pa_proplist_sets (proplist,
|
|
- PA_PROP_APPLICATION_VERSION,
|
|
- PACKAGE_VERSION);
|
|
-
|
|
- self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
|
|
- g_assert (self->priv->pa_context);
|
|
- pa_proplist_free (proplist);
|
|
+ gvc_mixer_new_pa_context (self);
|
|
|
|
return object;
|
|
}
|
|
@@ -1502,6 +1670,14 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
|
|
object_class->dispose = gvc_mixer_control_dispose;
|
|
object_class->finalize = gvc_mixer_control_finalize;
|
|
|
|
+ signals [CONNECTING] =
|
|
+ g_signal_new ("connecting",
|
|
+ G_TYPE_FROM_CLASS (klass),
|
|
+ G_SIGNAL_RUN_LAST,
|
|
+ G_STRUCT_OFFSET (GvcMixerControlClass, connecting),
|
|
+ NULL, NULL,
|
|
+ g_cclosure_marshal_VOID__VOID,
|
|
+ G_TYPE_NONE, 0);
|
|
signals [READY] =
|
|
g_signal_new ("ready",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-control.h b/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
|
|
index 33e745a..3de9e62 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
|
|
@@ -46,6 +46,7 @@ typedef struct
|
|
{
|
|
GObjectClass parent_class;
|
|
|
|
+ void (*connecting) (GvcMixerControl *control);
|
|
void (*ready) (GvcMixerControl *control);
|
|
void (*stream_added) (GvcMixerControl *control,
|
|
guint id);
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
|
|
index b5469ca..69e38ce 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
|
|
@@ -53,21 +53,22 @@ G_DEFINE_TYPE (GvcMixerEventRole, gvc_mixer_event_role, GVC_TYPE_MIXER_STREAM)
|
|
|
|
static gboolean
|
|
update_settings (GvcMixerEventRole *role,
|
|
- guint volume,
|
|
- gboolean is_muted)
|
|
+ gboolean is_muted,
|
|
+ gpointer *op)
|
|
{
|
|
pa_operation *o;
|
|
guint index;
|
|
+ GvcChannelMap *map;
|
|
pa_context *context;
|
|
pa_ext_stream_restore_info info;
|
|
|
|
index = gvc_mixer_stream_get_index (GVC_MIXER_STREAM (role));
|
|
|
|
- pa_cvolume_set (&info.volume, 1, (pa_volume_t)volume);
|
|
+ map = gvc_mixer_stream_get_channel_map (GVC_MIXER_STREAM(role));
|
|
|
|
+ info.volume = *gvc_channel_map_get_cvolume(map);
|
|
info.name = "sink-input-by-media-role:event";
|
|
- info.channel_map.channels = 1;
|
|
- info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
|
|
+ info.channel_map = *gvc_channel_map_get_pa_channel_map(map);
|
|
info.device = role->priv->device;
|
|
info.mute = is_muted;
|
|
|
|
@@ -86,18 +87,17 @@ update_settings (GvcMixerEventRole *role,
|
|
return FALSE;
|
|
}
|
|
|
|
- pa_operation_unref(o);
|
|
+ if (op != NULL)
|
|
+ *op = o;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
-gvc_mixer_event_role_change_volume (GvcMixerStream *stream,
|
|
- guint volume)
|
|
+gvc_mixer_event_role_push_volume (GvcMixerStream *stream, gpointer *op)
|
|
{
|
|
return update_settings (GVC_MIXER_EVENT_ROLE (stream),
|
|
- volume,
|
|
- gvc_mixer_stream_get_is_muted (stream));
|
|
+ gvc_mixer_stream_get_is_muted (stream), op);
|
|
}
|
|
|
|
static gboolean
|
|
@@ -105,8 +105,7 @@ gvc_mixer_event_role_change_is_muted (GvcMixerStream *stream,
|
|
gboolean is_muted)
|
|
{
|
|
return update_settings (GVC_MIXER_EVENT_ROLE (stream),
|
|
- gvc_mixer_stream_get_volume (stream),
|
|
- is_muted);
|
|
+ is_muted, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
@@ -184,7 +183,7 @@ gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass)
|
|
object_class->set_property = gvc_mixer_event_role_set_property;
|
|
object_class->get_property = gvc_mixer_event_role_get_property;
|
|
|
|
- stream_class->change_volume = gvc_mixer_event_role_change_volume;
|
|
+ stream_class->push_volume = gvc_mixer_event_role_push_volume;
|
|
stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted;
|
|
|
|
g_object_class_install_property (object_class,
|
|
@@ -224,7 +223,8 @@ gvc_mixer_event_role_finalize (GObject *object)
|
|
|
|
GvcMixerStream *
|
|
gvc_mixer_event_role_new (pa_context *context,
|
|
- const char *device)
|
|
+ const char *device,
|
|
+ GvcChannelMap *channel_map)
|
|
{
|
|
GObject *object;
|
|
|
|
@@ -232,6 +232,7 @@ gvc_mixer_event_role_new (pa_context *context,
|
|
"pa-context", context,
|
|
"index", 0,
|
|
"device", device,
|
|
+ "channel-map", channel_map,
|
|
NULL);
|
|
|
|
return GVC_MIXER_STREAM (object);
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
|
|
index 280c597..ab4c509 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
|
|
@@ -48,8 +48,9 @@ typedef struct
|
|
|
|
GType gvc_mixer_event_role_get_type (void);
|
|
|
|
-GvcMixerStream * gvc_mixer_event_role_new (pa_context *context,
|
|
- const char *device);
|
|
+GvcMixerStream * gvc_mixer_event_role_new (pa_context *context,
|
|
+ const char *device,
|
|
+ GvcChannelMap *channel_map);
|
|
|
|
G_END_DECLS
|
|
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c b/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
|
|
index b2c7172..35551bb 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
|
|
@@ -40,44 +40,33 @@ struct GvcMixerSinkInputPrivate
|
|
|
|
static void gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass);
|
|
static void gvc_mixer_sink_input_init (GvcMixerSinkInput *mixer_sink_input);
|
|
-static void gvc_mixer_sink_input_finalize (GObject *object);
|
|
+static void gvc_mixer_sink_input_finalize (GObject *object);
|
|
+static void gvc_mixer_sink_input_dispose (GObject *object);
|
|
|
|
G_DEFINE_TYPE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM)
|
|
|
|
static gboolean
|
|
-gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
|
|
- guint volume)
|
|
+gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op)
|
|
{
|
|
pa_operation *o;
|
|
guint index;
|
|
GvcChannelMap *map;
|
|
pa_context *context;
|
|
- pa_cvolume cv;
|
|
- guint i;
|
|
+ const pa_cvolume *cv;
|
|
guint num_channels;
|
|
- gdouble *gains;
|
|
|
|
index = gvc_mixer_stream_get_index (stream);
|
|
|
|
map = gvc_mixer_stream_get_channel_map (stream);
|
|
num_channels = gvc_channel_map_get_num_channels (map);
|
|
- gains = gvc_channel_map_get_gains (map);
|
|
|
|
- /* set all values to nominal level */
|
|
- pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
|
|
-
|
|
- /* apply channel gain mapping */
|
|
- for (i = 0; i < num_channels; i++) {
|
|
- pa_volume_t v;
|
|
- v = (double) volume * gains[i];
|
|
- cv.values[i] = v;
|
|
- }
|
|
+ cv = gvc_channel_map_get_cvolume(map);
|
|
|
|
context = gvc_mixer_stream_get_pa_context (stream);
|
|
|
|
o = pa_context_set_sink_input_volume (context,
|
|
index,
|
|
- &cv,
|
|
+ cv,
|
|
NULL,
|
|
NULL);
|
|
|
|
@@ -86,7 +75,7 @@ gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
|
|
return FALSE;
|
|
}
|
|
|
|
- pa_operation_unref(o);
|
|
+ *op = o;
|
|
|
|
return TRUE;
|
|
}
|
|
@@ -140,9 +129,10 @@ gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass)
|
|
GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
|
|
|
|
object_class->constructor = gvc_mixer_sink_input_constructor;
|
|
+ object_class->dispose = gvc_mixer_sink_input_dispose;
|
|
object_class->finalize = gvc_mixer_sink_input_finalize;
|
|
|
|
- stream_class->change_volume = gvc_mixer_sink_input_change_volume;
|
|
+ stream_class->push_volume = gvc_mixer_sink_input_push_volume;
|
|
stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted;
|
|
|
|
g_type_class_add_private (klass, sizeof (GvcMixerSinkInputPrivate));
|
|
@@ -152,7 +142,19 @@ static void
|
|
gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input)
|
|
{
|
|
sink_input->priv = GVC_MIXER_SINK_INPUT_GET_PRIVATE (sink_input);
|
|
+}
|
|
+
|
|
+static void
|
|
+gvc_mixer_sink_input_dispose (GObject *object)
|
|
+{
|
|
+ GvcMixerSinkInput *mixer_sink_input;
|
|
+
|
|
+ g_return_if_fail (object != NULL);
|
|
+ g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object));
|
|
+
|
|
+ mixer_sink_input = GVC_MIXER_SINK_INPUT (object);
|
|
|
|
+ G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c b/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
|
|
index 76eb3d7..06e5af6 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
|
|
@@ -40,57 +40,41 @@ struct GvcMixerSinkPrivate
|
|
|
|
static void gvc_mixer_sink_class_init (GvcMixerSinkClass *klass);
|
|
static void gvc_mixer_sink_init (GvcMixerSink *mixer_sink);
|
|
-static void gvc_mixer_sink_finalize (GObject *object);
|
|
+static void gvc_mixer_sink_finalize (GObject *object);
|
|
+static void gvc_mixer_sink_dispose (GObject *object);
|
|
|
|
G_DEFINE_TYPE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM)
|
|
|
|
static gboolean
|
|
-gvc_mixer_sink_change_volume (GvcMixerStream *stream,
|
|
- guint volume)
|
|
+gvc_mixer_sink_push_volume (GvcMixerStream *stream, gpointer *op)
|
|
{
|
|
pa_operation *o;
|
|
guint index;
|
|
GvcChannelMap *map;
|
|
pa_context *context;
|
|
- pa_cvolume cv;
|
|
- guint i;
|
|
- guint num_channels;
|
|
- gdouble *gains;
|
|
+ const pa_cvolume *cv;
|
|
|
|
index = gvc_mixer_stream_get_index (stream);
|
|
|
|
-
|
|
map = gvc_mixer_stream_get_channel_map (stream);
|
|
- num_channels = gvc_channel_map_get_num_channels (map);
|
|
- gains = gvc_channel_map_get_gains (map);
|
|
-
|
|
- g_debug ("Changing volume for sink: n=%d vol=%u", num_channels, (guint)volume);
|
|
|
|
- /* set all values to nominal level */
|
|
- pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
|
|
-
|
|
- /* apply channel gain mapping */
|
|
- for (i = 0; i < num_channels; i++) {
|
|
- pa_volume_t v;
|
|
- v = (double) volume * gains[i];
|
|
- g_debug ("Channel %d v=%u", i, v);
|
|
- cv.values[i] = v;
|
|
- }
|
|
+ /* set the volume */
|
|
+ cv = gvc_channel_map_get_cvolume(map);
|
|
|
|
context = gvc_mixer_stream_get_pa_context (stream);
|
|
|
|
o = pa_context_set_sink_volume_by_index (context,
|
|
index,
|
|
- &cv,
|
|
+ cv,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (o == NULL) {
|
|
- g_warning ("pa_context_set_sink_volume_by_index() failed");
|
|
+ g_warning ("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
|
|
return FALSE;
|
|
}
|
|
|
|
- pa_operation_unref(o);
|
|
+ *op = o;
|
|
|
|
return TRUE;
|
|
}
|
|
@@ -113,7 +97,7 @@ gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
|
|
NULL);
|
|
|
|
if (o == NULL) {
|
|
- g_warning ("pa_context_set_sink_mute_by_index() failed");
|
|
+ g_warning ("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
|
|
return FALSE;
|
|
}
|
|
|
|
@@ -127,7 +111,7 @@ gvc_mixer_sink_constructor (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_params)
|
|
{
|
|
- GObject *object;
|
|
+ GObject *object;
|
|
GvcMixerSink *self;
|
|
|
|
object = G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->constructor (type, n_construct_properties, construct_params);
|
|
@@ -144,9 +128,10 @@ gvc_mixer_sink_class_init (GvcMixerSinkClass *klass)
|
|
GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
|
|
|
|
object_class->constructor = gvc_mixer_sink_constructor;
|
|
+ object_class->dispose = gvc_mixer_sink_dispose;
|
|
object_class->finalize = gvc_mixer_sink_finalize;
|
|
|
|
- stream_class->change_volume = gvc_mixer_sink_change_volume;
|
|
+ stream_class->push_volume = gvc_mixer_sink_push_volume;
|
|
stream_class->change_is_muted = gvc_mixer_sink_change_is_muted;
|
|
|
|
g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate));
|
|
@@ -156,7 +141,19 @@ static void
|
|
gvc_mixer_sink_init (GvcMixerSink *sink)
|
|
{
|
|
sink->priv = GVC_MIXER_SINK_GET_PRIVATE (sink);
|
|
+}
|
|
+
|
|
+static void
|
|
+gvc_mixer_sink_dispose (GObject *object)
|
|
+{
|
|
+ GvcMixerSink *mixer_sink;
|
|
+
|
|
+ g_return_if_fail (object != NULL);
|
|
+ g_return_if_fail (GVC_IS_MIXER_SINK (object));
|
|
+
|
|
+ mixer_sink = GVC_MIXER_SINK (object);
|
|
|
|
+ G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c b/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
|
|
index b71ad23..b4cc34d 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
|
|
@@ -45,10 +45,10 @@ static void gvc_mixer_source_output_finalize (GObject *object);
|
|
G_DEFINE_TYPE (GvcMixerSourceOutput, gvc_mixer_source_output, GVC_TYPE_MIXER_STREAM)
|
|
|
|
static gboolean
|
|
-gvc_mixer_source_output_change_volume (GvcMixerStream *stream,
|
|
- guint volume)
|
|
+gvc_mixer_source_output_push_volume (GvcMixerStream *stream, gpointer *op)
|
|
{
|
|
/* FIXME: */
|
|
+ *op = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
@@ -84,7 +84,7 @@ gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass)
|
|
object_class->constructor = gvc_mixer_source_output_constructor;
|
|
object_class->finalize = gvc_mixer_source_output_finalize;
|
|
|
|
- stream_class->change_volume = gvc_mixer_source_output_change_volume;
|
|
+ stream_class->push_volume = gvc_mixer_source_output_push_volume;
|
|
stream_class->change_is_muted = gvc_mixer_source_output_change_is_muted;
|
|
|
|
g_type_class_add_private (klass, sizeof (GvcMixerSourceOutputPrivate));
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-source.c b/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
|
|
index de1b09e..ae02d85 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
|
|
@@ -41,53 +41,40 @@ struct GvcMixerSourcePrivate
|
|
static void gvc_mixer_source_class_init (GvcMixerSourceClass *klass);
|
|
static void gvc_mixer_source_init (GvcMixerSource *mixer_source);
|
|
static void gvc_mixer_source_finalize (GObject *object);
|
|
+static void gvc_mixer_source_dispose (GObject *object);
|
|
|
|
G_DEFINE_TYPE (GvcMixerSource, gvc_mixer_source, GVC_TYPE_MIXER_STREAM)
|
|
|
|
static gboolean
|
|
-gvc_mixer_source_change_volume (GvcMixerStream *stream,
|
|
- guint volume)
|
|
+gvc_mixer_source_push_volume (GvcMixerStream *stream, gpointer *op)
|
|
{
|
|
pa_operation *o;
|
|
guint index;
|
|
GvcChannelMap *map;
|
|
pa_context *context;
|
|
- pa_cvolume cv;
|
|
- guint num_channels;
|
|
- guint i;
|
|
- gdouble *gains;
|
|
+ const pa_cvolume *cv;
|
|
|
|
index = gvc_mixer_stream_get_index (stream);
|
|
|
|
map = gvc_mixer_stream_get_channel_map (stream);
|
|
- num_channels = gvc_channel_map_get_num_channels (map);
|
|
- gains = gvc_channel_map_get_gains (map);
|
|
|
|
- /* set all values to nominal level */
|
|
- pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
|
|
-
|
|
-
|
|
- /* apply channel gain mapping */
|
|
- for (i = 0; i < num_channels; i++) {
|
|
- pa_volume_t v;
|
|
- v = (double) volume * gains[i];
|
|
- cv.values[i] = v;
|
|
- }
|
|
+ /* set the volume */
|
|
+ cv = gvc_channel_map_get_cvolume (map);
|
|
|
|
context = gvc_mixer_stream_get_pa_context (stream);
|
|
|
|
o = pa_context_set_source_volume_by_index (context,
|
|
index,
|
|
- &cv,
|
|
+ cv,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (o == NULL) {
|
|
- g_warning ("pa_context_set_source_volume_by_index() failed");
|
|
+ g_warning ("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
|
|
return FALSE;
|
|
}
|
|
|
|
- pa_operation_unref(o);
|
|
+ *op = o;
|
|
|
|
return TRUE;
|
|
}
|
|
@@ -110,7 +97,7 @@ gvc_mixer_source_change_is_muted (GvcMixerStream *stream,
|
|
NULL);
|
|
|
|
if (o == NULL) {
|
|
- g_warning ("pa_context_set_source_mute_by_index() failed");
|
|
+ g_warning ("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
|
|
return FALSE;
|
|
}
|
|
|
|
@@ -124,7 +111,7 @@ gvc_mixer_source_constructor (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_params)
|
|
{
|
|
- GObject *object;
|
|
+ GObject *object;
|
|
GvcMixerSource *self;
|
|
|
|
object = G_OBJECT_CLASS (gvc_mixer_source_parent_class)->constructor (type, n_construct_properties, construct_params);
|
|
@@ -141,9 +128,10 @@ gvc_mixer_source_class_init (GvcMixerSourceClass *klass)
|
|
GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
|
|
|
|
object_class->constructor = gvc_mixer_source_constructor;
|
|
+ object_class->dispose = gvc_mixer_source_dispose;
|
|
object_class->finalize = gvc_mixer_source_finalize;
|
|
|
|
- stream_class->change_volume = gvc_mixer_source_change_volume;
|
|
+ stream_class->push_volume = gvc_mixer_source_push_volume;
|
|
stream_class->change_is_muted = gvc_mixer_source_change_is_muted;
|
|
|
|
g_type_class_add_private (klass, sizeof (GvcMixerSourcePrivate));
|
|
@@ -153,7 +141,19 @@ static void
|
|
gvc_mixer_source_init (GvcMixerSource *source)
|
|
{
|
|
source->priv = GVC_MIXER_SOURCE_GET_PRIVATE (source);
|
|
+}
|
|
+
|
|
+static void
|
|
+gvc_mixer_source_dispose (GObject *object)
|
|
+{
|
|
+ GvcMixerSource *mixer_source;
|
|
+
|
|
+ g_return_if_fail (object != NULL);
|
|
+ g_return_if_fail (GVC_IS_MIXER_SOURCE (object));
|
|
+
|
|
+ mixer_source = GVC_MIXER_SOURCE (object);
|
|
|
|
+ G_OBJECT_CLASS (gvc_mixer_source_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
|
|
index 69d8598..e5cfb19 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
|
|
@@ -41,13 +41,16 @@ struct GvcMixerStreamPrivate
|
|
guint id;
|
|
guint index;
|
|
GvcChannelMap *channel_map;
|
|
- guint volume;
|
|
- gdouble decibel;
|
|
char *name;
|
|
char *description;
|
|
+ char *application_id;
|
|
char *icon_name;
|
|
gboolean is_muted;
|
|
gboolean can_decibel;
|
|
+ gboolean is_event_stream;
|
|
+ gboolean is_virtual;
|
|
+ pa_volume_t base_volume;
|
|
+ pa_operation *change_volume_op;
|
|
};
|
|
|
|
enum
|
|
@@ -59,11 +62,14 @@ enum
|
|
PROP_INDEX,
|
|
PROP_NAME,
|
|
PROP_DESCRIPTION,
|
|
+ PROP_APPLICATION_ID,
|
|
PROP_ICON_NAME,
|
|
PROP_VOLUME,
|
|
PROP_DECIBEL,
|
|
PROP_IS_MUTED,
|
|
- PROP_CAN_DECIBEL
|
|
+ PROP_CAN_DECIBEL,
|
|
+ PROP_IS_EVENT_STREAM,
|
|
+ PROP_IS_VIRTUAL,
|
|
};
|
|
|
|
static void gvc_mixer_stream_class_init (GvcMixerStreamClass *klass);
|
|
@@ -114,28 +120,36 @@ gvc_mixer_stream_get_channel_map (GvcMixerStream *stream)
|
|
return stream->priv->channel_map;
|
|
}
|
|
|
|
-guint
|
|
+pa_volume_t
|
|
gvc_mixer_stream_get_volume (GvcMixerStream *stream)
|
|
{
|
|
g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
|
|
- return stream->priv->volume;
|
|
+
|
|
+ return (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME];
|
|
}
|
|
|
|
gdouble
|
|
gvc_mixer_stream_get_decibel (GvcMixerStream *stream)
|
|
{
|
|
g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
|
|
- return stream->priv->decibel;
|
|
+
|
|
+ return pa_sw_volume_to_dB(
|
|
+ (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]);
|
|
}
|
|
|
|
gboolean
|
|
gvc_mixer_stream_set_volume (GvcMixerStream *stream,
|
|
- pa_volume_t volume)
|
|
+ pa_volume_t volume)
|
|
{
|
|
+ pa_cvolume cv;
|
|
+
|
|
g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
|
|
- if (volume != stream->priv->volume) {
|
|
- stream->priv->volume = volume;
|
|
+ cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
|
|
+ pa_cvolume_scale(&cv, volume);
|
|
+
|
|
+ if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
|
|
+ gvc_channel_map_volume_changed(stream->priv->channel_map, &cv);
|
|
g_object_notify (G_OBJECT (stream), "volume");
|
|
}
|
|
|
|
@@ -146,11 +160,16 @@ gboolean
|
|
gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
|
|
gdouble db)
|
|
{
|
|
+ pa_cvolume cv;
|
|
+
|
|
g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
|
|
- if (db != stream->priv->decibel) {
|
|
- stream->priv->decibel = db;
|
|
- g_object_notify (G_OBJECT (stream), "decibel");
|
|
+ cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
|
|
+ pa_cvolume_scale(&cv, pa_sw_volume_from_dB(db));
|
|
+
|
|
+ if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
|
|
+ gvc_channel_map_volume_changed(stream->priv->channel_map, &cv);
|
|
+ g_object_notify (G_OBJECT (stream), "volume");
|
|
}
|
|
|
|
return TRUE;
|
|
@@ -238,12 +257,73 @@ gvc_mixer_stream_set_description (GvcMixerStream *stream,
|
|
return TRUE;
|
|
}
|
|
|
|
+gboolean
|
|
+gvc_mixer_stream_is_event_stream (GvcMixerStream *stream)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
+
|
|
+ return stream->priv->is_event_stream;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
|
|
+ gboolean is_event_stream)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
+
|
|
+ stream->priv->is_event_stream = is_event_stream;
|
|
+ g_object_notify (G_OBJECT (stream), "is-event-stream");
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gvc_mixer_stream_is_virtual (GvcMixerStream *stream)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
+
|
|
+ return stream->priv->is_virtual;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream,
|
|
+ gboolean is_virtual)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
+
|
|
+ stream->priv->is_virtual = is_virtual;
|
|
+ g_object_notify (G_OBJECT (stream), "is-virtual");
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+const char *
|
|
+gvc_mixer_stream_get_application_id (GvcMixerStream *stream)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
|
|
+ return stream->priv->application_id;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
|
|
+ const char *application_id)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
+
|
|
+ g_free (stream->priv->application_id);
|
|
+ stream->priv->application_id = g_strdup (application_id);
|
|
+ g_object_notify (G_OBJECT (stream), "application-id");
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
static void
|
|
-on_channel_map_gains_changed (GvcChannelMap *channel_map,
|
|
- GvcMixerStream *stream)
|
|
+on_channel_map_volume_changed (GvcChannelMap *channel_map,
|
|
+ GvcMixerStream *stream)
|
|
{
|
|
- g_debug ("Gains changed");
|
|
- gvc_mixer_stream_change_volume (stream, stream->priv->volume);
|
|
+ gvc_mixer_stream_push_volume (stream);
|
|
+
|
|
+ g_object_notify (G_OBJECT (stream), "volume");
|
|
}
|
|
|
|
static gboolean
|
|
@@ -258,7 +338,7 @@ gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
|
|
|
|
if (stream->priv->channel_map != NULL) {
|
|
g_signal_handlers_disconnect_by_func (stream->priv->channel_map,
|
|
- on_channel_map_gains_changed,
|
|
+ on_channel_map_volume_changed,
|
|
stream);
|
|
g_object_unref (stream->priv->channel_map);
|
|
}
|
|
@@ -267,8 +347,8 @@ gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
|
|
|
|
if (stream->priv->channel_map != NULL) {
|
|
g_signal_connect (stream->priv->channel_map,
|
|
- "gains-changed",
|
|
- G_CALLBACK (on_channel_map_gains_changed),
|
|
+ "volume-changed",
|
|
+ G_CALLBACK (on_channel_map_volume_changed),
|
|
stream);
|
|
|
|
g_object_notify (G_OBJECT (stream), "channel-map");
|
|
@@ -297,6 +377,25 @@ gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
|
|
return TRUE;
|
|
}
|
|
|
|
+pa_volume_t
|
|
+gvc_mixer_stream_get_base_volume (GvcMixerStream *stream)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
|
|
+
|
|
+ return stream->priv->base_volume;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
|
|
+ pa_volume_t base_volume)
|
|
+{
|
|
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
+
|
|
+ stream->priv->base_volume = base_volume;
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
static void
|
|
gvc_mixer_stream_set_property (GObject *object,
|
|
guint prop_id,
|
|
@@ -324,6 +423,9 @@ gvc_mixer_stream_set_property (GObject *object,
|
|
case PROP_DESCRIPTION:
|
|
gvc_mixer_stream_set_description (self, g_value_get_string (value));
|
|
break;
|
|
+ case PROP_APPLICATION_ID:
|
|
+ gvc_mixer_stream_set_application_id (self, g_value_get_string (value));
|
|
+ break;
|
|
case PROP_ICON_NAME:
|
|
gvc_mixer_stream_set_icon_name (self, g_value_get_string (value));
|
|
break;
|
|
@@ -336,6 +438,12 @@ gvc_mixer_stream_set_property (GObject *object,
|
|
case PROP_IS_MUTED:
|
|
gvc_mixer_stream_set_is_muted (self, g_value_get_boolean (value));
|
|
break;
|
|
+ case PROP_IS_EVENT_STREAM:
|
|
+ gvc_mixer_stream_set_is_event_stream (self, g_value_get_boolean (value));
|
|
+ break;
|
|
+ case PROP_IS_VIRTUAL:
|
|
+ gvc_mixer_stream_set_is_virtual (self, g_value_get_boolean (value));
|
|
+ break;
|
|
case PROP_CAN_DECIBEL:
|
|
gvc_mixer_stream_set_can_decibel (self, g_value_get_boolean (value));
|
|
break;
|
|
@@ -372,18 +480,29 @@ gvc_mixer_stream_get_property (GObject *object,
|
|
case PROP_DESCRIPTION:
|
|
g_value_set_string (value, self->priv->description);
|
|
break;
|
|
+ case PROP_APPLICATION_ID:
|
|
+ g_value_set_string (value, self->priv->application_id);
|
|
+ break;
|
|
case PROP_ICON_NAME:
|
|
g_value_set_string (value, self->priv->icon_name);
|
|
break;
|
|
case PROP_VOLUME:
|
|
- g_value_set_ulong (value, self->priv->volume);
|
|
+ g_value_set_ulong (value,
|
|
+ pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map)));
|
|
break;
|
|
case PROP_DECIBEL:
|
|
- g_value_set_double (value, self->priv->decibel);
|
|
+ g_value_set_double (value,
|
|
+ pa_sw_volume_to_dB(pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map))));
|
|
break;
|
|
case PROP_IS_MUTED:
|
|
g_value_set_boolean (value, self->priv->is_muted);
|
|
break;
|
|
+ case PROP_IS_EVENT_STREAM:
|
|
+ g_value_set_boolean (value, self->priv->is_event_stream);
|
|
+ break;
|
|
+ case PROP_IS_VIRTUAL:
|
|
+ g_value_set_boolean (value, self->priv->is_virtual);
|
|
+ break;
|
|
case PROP_CAN_DECIBEL:
|
|
g_value_set_boolean (value, self->priv->can_decibel);
|
|
break;
|
|
@@ -411,8 +530,7 @@ gvc_mixer_stream_constructor (GType type,
|
|
}
|
|
|
|
static gboolean
|
|
-gvc_mixer_stream_real_change_volume (GvcMixerStream *stream,
|
|
- guint volume)
|
|
+gvc_mixer_stream_real_push_volume (GvcMixerStream *stream, gpointer *op)
|
|
{
|
|
return FALSE;
|
|
}
|
|
@@ -425,12 +543,17 @@ gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream,
|
|
}
|
|
|
|
gboolean
|
|
-gvc_mixer_stream_change_volume (GvcMixerStream *stream,
|
|
- guint volume)
|
|
+gvc_mixer_stream_push_volume (GvcMixerStream *stream)
|
|
{
|
|
+ pa_operation *op;
|
|
gboolean ret;
|
|
g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
|
|
- ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_volume (stream, volume);
|
|
+ ret = GVC_MIXER_STREAM_GET_CLASS (stream)->push_volume (stream, (gpointer *) &op);
|
|
+ if (ret) {
|
|
+ if (stream->priv->change_volume_op != NULL)
|
|
+ pa_operation_unref (stream->priv->change_volume_op);
|
|
+ stream->priv->change_volume_op = op;
|
|
+ }
|
|
return ret;
|
|
}
|
|
|
|
@@ -444,6 +567,21 @@ gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
|
|
return ret;
|
|
}
|
|
|
|
+gboolean
|
|
+gvc_mixer_stream_is_running (GvcMixerStream *stream)
|
|
+{
|
|
+ if (stream->priv->change_volume_op == NULL)
|
|
+ return FALSE;
|
|
+
|
|
+ if ((pa_operation_get_state(stream->priv->change_volume_op) == PA_OPERATION_RUNNING))
|
|
+ return TRUE;
|
|
+
|
|
+ pa_operation_unref(stream->priv->change_volume_op);
|
|
+ stream->priv->change_volume_op = NULL;
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
static void
|
|
gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
|
|
{
|
|
@@ -454,7 +592,7 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
|
|
gobject_class->set_property = gvc_mixer_stream_set_property;
|
|
gobject_class->get_property = gvc_mixer_stream_get_property;
|
|
|
|
- klass->change_volume = gvc_mixer_stream_real_change_volume;
|
|
+ klass->push_volume = gvc_mixer_stream_real_push_volume;
|
|
klass->change_is_muted = gvc_mixer_stream_real_change_is_muted;
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
@@ -490,7 +628,7 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
|
|
"Volume",
|
|
"The volume for this stream",
|
|
0, G_MAXULONG, 0,
|
|
- G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
|
+ G_PARAM_READWRITE));
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_DECIBEL,
|
|
g_param_spec_double ("decibel",
|
|
@@ -514,6 +652,13 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
|
|
NULL,
|
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
|
g_object_class_install_property (gobject_class,
|
|
+ PROP_APPLICATION_ID,
|
|
+ g_param_spec_string ("application-id",
|
|
+ "Application identifier",
|
|
+ "Application identifier for this stream",
|
|
+ NULL,
|
|
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
|
+ g_object_class_install_property (gobject_class,
|
|
PROP_ICON_NAME,
|
|
g_param_spec_string ("icon-name",
|
|
"Icon Name",
|
|
@@ -534,7 +679,20 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
|
|
"Whether stream volume can be converted to decibel units",
|
|
FALSE,
|
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
|
-
|
|
+ g_object_class_install_property (gobject_class,
|
|
+ PROP_IS_EVENT_STREAM,
|
|
+ g_param_spec_boolean ("is-event-stream",
|
|
+ "is event stream",
|
|
+ "Whether stream's role is to play an event",
|
|
+ FALSE,
|
|
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
|
+ g_object_class_install_property (gobject_class,
|
|
+ PROP_IS_VIRTUAL,
|
|
+ g_param_spec_boolean ("is-virtual",
|
|
+ "is virtual stream",
|
|
+ "Whether the stream is virtual",
|
|
+ FALSE,
|
|
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
|
g_type_class_add_private (klass, sizeof (GvcMixerStreamPrivate));
|
|
}
|
|
|
|
@@ -542,7 +700,6 @@ static void
|
|
gvc_mixer_stream_init (GvcMixerStream *stream)
|
|
{
|
|
stream->priv = GVC_MIXER_STREAM_GET_PRIVATE (stream);
|
|
-
|
|
}
|
|
|
|
static void
|
|
@@ -563,8 +720,16 @@ gvc_mixer_stream_finalize (GObject *object)
|
|
g_free (mixer_stream->priv->description);
|
|
mixer_stream->priv->description = NULL;
|
|
|
|
+ g_free (mixer_stream->priv->application_id);
|
|
+ mixer_stream->priv->application_id = NULL;
|
|
+
|
|
g_free (mixer_stream->priv->icon_name);
|
|
mixer_stream->priv->icon_name = NULL;
|
|
|
|
+ if (mixer_stream->priv->change_volume_op) {
|
|
+ pa_operation_unref(mixer_stream->priv->change_volume_op);
|
|
+ mixer_stream->priv->change_volume_op = NULL;
|
|
+ }
|
|
+
|
|
G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->finalize (object);
|
|
}
|
|
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
|
|
index 3dee03b..4171ca3 100644
|
|
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
|
|
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
|
|
@@ -48,8 +48,7 @@ typedef struct
|
|
GObjectClass parent_class;
|
|
|
|
/* vtable */
|
|
- gboolean (*change_volume) (GvcMixerStream *stream,
|
|
- guint volume);
|
|
+ gboolean (*push_volume) (GvcMixerStream *stream, gpointer *operation);
|
|
gboolean (*change_is_muted) (GvcMixerStream *stream,
|
|
gboolean is_muted);
|
|
} GvcMixerStreamClass;
|
|
@@ -61,22 +60,26 @@ guint gvc_mixer_stream_get_index (GvcMixerStream *stream);
|
|
guint gvc_mixer_stream_get_id (GvcMixerStream *stream);
|
|
GvcChannelMap * gvc_mixer_stream_get_channel_map (GvcMixerStream *stream);
|
|
|
|
-guint gvc_mixer_stream_get_volume (GvcMixerStream *stream);
|
|
+pa_volume_t gvc_mixer_stream_get_volume (GvcMixerStream *stream);
|
|
gdouble gvc_mixer_stream_get_decibel (GvcMixerStream *stream);
|
|
-gboolean gvc_mixer_stream_change_volume (GvcMixerStream *stream,
|
|
- guint volume);
|
|
+gboolean gvc_mixer_stream_push_volume (GvcMixerStream *stream);
|
|
+pa_volume_t gvc_mixer_stream_get_base_volume (GvcMixerStream *stream);
|
|
|
|
gboolean gvc_mixer_stream_get_is_muted (GvcMixerStream *stream);
|
|
gboolean gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream);
|
|
gboolean gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
|
|
gboolean is_muted);
|
|
+gboolean gvc_mixer_stream_is_running (GvcMixerStream *stream);
|
|
const char * gvc_mixer_stream_get_name (GvcMixerStream *stream);
|
|
const char * gvc_mixer_stream_get_icon_name (GvcMixerStream *stream);
|
|
const char * gvc_mixer_stream_get_description (GvcMixerStream *stream);
|
|
+const char * gvc_mixer_stream_get_application_id (GvcMixerStream *stream);
|
|
+gboolean gvc_mixer_stream_is_event_stream (GvcMixerStream *stream);
|
|
+gboolean gvc_mixer_stream_is_virtual (GvcMixerStream *stream);
|
|
|
|
/* private */
|
|
gboolean gvc_mixer_stream_set_volume (GvcMixerStream *stream,
|
|
- guint volume);
|
|
+ pa_volume_t volume);
|
|
gboolean gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
|
|
gdouble db);
|
|
gboolean gvc_mixer_stream_set_is_muted (GvcMixerStream *stream,
|
|
@@ -89,6 +92,14 @@ gboolean gvc_mixer_stream_set_description (GvcMixerStream *stream,
|
|
const char *description);
|
|
gboolean gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
|
|
const char *name);
|
|
+gboolean gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
|
|
+ gboolean is_event_stream);
|
|
+gboolean gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream,
|
|
+ gboolean is_event_stream);
|
|
+gboolean gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
|
|
+ const char *application_id);
|
|
+gboolean gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
|
|
+ pa_volume_t base_volume);
|
|
|
|
G_END_DECLS
|
|
|
|
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
|
|
index d35048c..3eeea71 100644
|
|
--- a/plugins/media-keys/gsd-media-keys-manager.c
|
|
+++ b/plugins/media-keys/gsd-media-keys-manager.c
|
|
@@ -687,17 +687,17 @@ do_sound_action (GsdMediaKeysManager *manager,
|
|
if (!muted && (vol <= norm_vol_step)) {
|
|
manager->priv->num_expected_update_signals = 2;
|
|
gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
|
|
- gvc_mixer_stream_change_volume (manager->priv->stream, 0);
|
|
+ gvc_mixer_stream_set_volume (manager->priv->stream, 0);
|
|
} else if (!muted) {
|
|
manager->priv->num_expected_update_signals = 1;
|
|
- gvc_mixer_stream_change_volume (manager->priv->stream, vol - norm_vol_step);
|
|
+ gvc_mixer_stream_set_volume (manager->priv->stream, vol - norm_vol_step);
|
|
}
|
|
break;
|
|
case VOLUME_UP_KEY:
|
|
if (muted) {
|
|
if (vol == 0) {
|
|
manager->priv->num_expected_update_signals = 2;
|
|
- gvc_mixer_stream_change_volume (manager->priv->stream, vol + norm_vol_step);
|
|
+ gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
|
|
gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
|
|
} else {
|
|
manager->priv->num_expected_update_signals = 1;
|
|
@@ -707,9 +707,9 @@ do_sound_action (GsdMediaKeysManager *manager,
|
|
if (vol < MAX_VOLUME) {
|
|
manager->priv->num_expected_update_signals = 1;
|
|
if (vol + norm_vol_step >= MAX_VOLUME) {
|
|
- gvc_mixer_stream_change_volume (manager->priv->stream, MAX_VOLUME);
|
|
+ gvc_mixer_stream_set_volume (manager->priv->stream, MAX_VOLUME);
|
|
} else {
|
|
- gvc_mixer_stream_change_volume (manager->priv->stream, vol + norm_vol_step);
|
|
+ gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
|
|
}
|
|
}
|
|
}
|
|
--
|
|
1.6.2.5
|
|
|