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