1 /*
2 * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <glib.h>
19 #include <glib-object.h>
20 #include <libmatemixer/matemixer.h>
21 #include <libmatemixer/matemixer-private.h>
22
23 #include <pulse/pulseaudio.h>
24
25 #include "pulse-connection.h"
26 #include "pulse-helpers.h"
27 #include "pulse-monitor.h"
28 #include "pulse-source.h"
29 #include "pulse-source-output.h"
30 #include "pulse-stream.h"
31 #include "pulse-stream-control.h"
32
33 G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM_CONTROL);
34
35 static guint pulse_source_output_get_max_volume (MateMixerStreamControl *mmsc);
36
37 static gboolean pulse_source_output_set_mute (PulseStreamControl *psc,
38 gboolean mute);
39 static gboolean pulse_source_output_set_volume (PulseStreamControl *psc,
40 pa_cvolume *cvolume);
41 static PulseMonitor *pulse_source_output_create_monitor (PulseStreamControl *psc);
42
43 static void
pulse_source_output_class_init(PulseSourceOutputClass * klass)44 pulse_source_output_class_init (PulseSourceOutputClass *klass)
45 {
46 MateMixerStreamControlClass *mmsc_class;
47 PulseStreamControlClass *control_class;
48
49 mmsc_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
50 mmsc_class->get_max_volume = pulse_source_output_get_max_volume;
51
52 control_class = PULSE_STREAM_CONTROL_CLASS (klass);
53 control_class->set_mute = pulse_source_output_set_mute;
54 control_class->set_volume = pulse_source_output_set_volume;
55 control_class->create_monitor = pulse_source_output_create_monitor;
56 }
57
58 static void
pulse_source_output_init(PulseSourceOutput * output)59 pulse_source_output_init (PulseSourceOutput *output)
60 {
61 }
62
63 PulseSourceOutput *
pulse_source_output_new(PulseConnection * connection,const pa_source_output_info * info,PulseSource * parent)64 pulse_source_output_new (PulseConnection *connection,
65 const pa_source_output_info *info,
66 PulseSource *parent)
67 {
68 PulseSourceOutput *output;
69 gchar *name;
70 const gchar *prop;
71 MateMixerAppInfo *app_info = NULL;
72
73 MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
74 MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE |
75 MATE_MIXER_STREAM_CONTROL_HAS_MONITOR;
76 MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
77
78 MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN;
79
80 g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
81 g_return_val_if_fail (info != NULL, NULL);
82 g_return_val_if_fail (PULSE_IS_SOURCE (parent), NULL);
83
84 /* Many mixer applications query the Pulse client list and use the client
85 * name here, but we use the name only as an identifier, so let's avoid
86 * this unnecessary overhead and use a custom name.
87 * Also make sure to make the name unique by including the PulseAudio index. */
88 name = g_strdup_printf ("pulse-input-control-%lu", (gulong) info->index);
89
90 if (info->has_volume) {
91 flags |=
92 MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
93 MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
94
95 if (info->volume_writable)
96 flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
97 }
98
99 if (info->client != PA_INVALID_INDEX) {
100 app_info = _mate_mixer_app_info_new ();
101
102 role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION;
103
104 prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
105 if (prop != NULL)
106 _mate_mixer_app_info_set_name (app_info, prop);
107
108 prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
109 if (prop != NULL)
110 _mate_mixer_app_info_set_id (app_info, prop);
111
112 prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
113 if (prop != NULL)
114 _mate_mixer_app_info_set_version (app_info, prop);
115
116 prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
117 if (prop != NULL)
118 _mate_mixer_app_info_set_icon (app_info, prop);
119 }
120
121 prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
122 if (prop != NULL)
123 media_role = pulse_convert_media_role_name (prop);
124
125 output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT,
126 "name", name,
127 "label", info->name,
128 "flags", flags,
129 "role", role,
130 "media-role", media_role,
131 "stream", parent,
132 "connection", connection,
133 "index", info->index,
134 NULL);
135 g_free (name);
136
137 if (app_info != NULL) {
138 /* Takes ownership of app_info */
139 pulse_stream_control_set_app_info (PULSE_STREAM_CONTROL (output),
140 app_info,
141 TRUE);
142 }
143
144 pulse_source_output_update (output, info);
145 return output;
146 }
147
148 void
pulse_source_output_update(PulseSourceOutput * output,const pa_source_output_info * info)149 pulse_source_output_update (PulseSourceOutput *output,
150 const pa_source_output_info *info)
151 {
152 g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (output));
153 g_return_if_fail (info != NULL);
154
155 /* Let all the information update before emitting notify signals */
156 g_object_freeze_notify (G_OBJECT (output));
157
158 _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (output),
159 info->mute ? TRUE : FALSE);
160
161 pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output),
162 &info->channel_map);
163 if (info->has_volume)
164 pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
165 &info->volume,
166 0);
167 else
168 pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
169 NULL,
170 0);
171
172 g_object_thaw_notify (G_OBJECT (output));
173 }
174
175 static guint
pulse_source_output_get_max_volume(MateMixerStreamControl * mmsc)176 pulse_source_output_get_max_volume (MateMixerStreamControl *mmsc)
177 {
178 g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (mmsc), (guint) PA_VOLUME_MUTED);
179
180 /* Do not extend the volume to PA_VOLUME_UI_MAX as PulseStreamControl does */
181 return (guint) PA_VOLUME_NORM;
182 }
183
184 static gboolean
pulse_source_output_set_mute(PulseStreamControl * psc,gboolean mute)185 pulse_source_output_set_mute (PulseStreamControl *psc, gboolean mute)
186 {
187 g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE);
188
189 return pulse_connection_set_source_output_mute (pulse_stream_control_get_connection (psc),
190 pulse_stream_control_get_index (psc),
191 mute);
192 }
193
194 static gboolean
pulse_source_output_set_volume(PulseStreamControl * psc,pa_cvolume * cvolume)195 pulse_source_output_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume)
196 {
197 g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE);
198 g_return_val_if_fail (cvolume != NULL, FALSE);
199
200 return pulse_connection_set_source_output_volume (pulse_stream_control_get_connection (psc),
201 pulse_stream_control_get_index (psc),
202 cvolume);
203 }
204
205 static PulseMonitor *
pulse_source_output_create_monitor(PulseStreamControl * psc)206 pulse_source_output_create_monitor (PulseStreamControl *psc)
207 {
208 g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), NULL);
209
210 return pulse_connection_create_monitor (pulse_stream_control_get_connection (psc),
211 pulse_stream_control_get_stream_index (psc),
212 PA_INVALID_INDEX);
213 }
214