Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright (C) 2018 Canonical Ltd. |
3 |
|
|
* |
4 |
|
|
* This program is free software; you can redistribute it and/or |
5 |
|
|
* modify it under the terms of the GNU General Public License as |
6 |
|
|
* published by the Free Software Foundation; either version 2 of the |
7 |
|
|
* License, or (at your option) any later version. |
8 |
|
|
* |
9 |
|
|
* This program is distributed in the hope that it will be useful, but |
10 |
|
|
* 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 General Public License |
15 |
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 |
|
|
*/ |
17 |
|
|
|
18 |
|
|
#include <glib/gi18n.h> |
19 |
|
|
#include <math.h> |
20 |
|
|
#include <pulse/pulseaudio.h> |
21 |
|
|
#include <gvc-mixer-control.h> |
22 |
|
|
|
23 |
|
|
#include "cc-sound-resources.h" |
24 |
|
|
#include "cc-volume-slider.h" |
25 |
|
|
|
26 |
|
|
struct _CcVolumeSlider |
27 |
|
|
{ |
28 |
|
|
GtkWidget parent_instance; |
29 |
|
|
|
30 |
|
|
GtkButton *mute_button; |
31 |
|
|
GtkAdjustment *volume_adjustment; |
32 |
|
|
GtkScale *volume_scale; |
33 |
|
|
|
34 |
|
|
gboolean is_amplified; |
35 |
|
|
GvcMixerControl *mixer_control; |
36 |
|
|
GvcMixerStream *stream; |
37 |
|
|
CcStreamType type; |
38 |
|
|
guint notify_volume_handler_id; |
39 |
|
|
guint notify_is_muted_handler_id; |
40 |
|
|
}; |
41 |
|
|
|
42 |
|
✗ |
G_DEFINE_TYPE (CcVolumeSlider, cc_volume_slider, GTK_TYPE_WIDGET) |
43 |
|
|
|
44 |
|
|
static void notify_is_muted_cb (CcVolumeSlider *self); |
45 |
|
|
|
46 |
|
|
static void |
47 |
|
✗ |
update_mute_button_tooltip (CcVolumeSlider *self, |
48 |
|
|
gdouble volume) |
49 |
|
|
{ |
50 |
|
|
const gchar *tooltip; |
51 |
|
|
|
52 |
|
✗ |
tooltip = (volume == 0.0) ? _("Unmute") : _("Mute"); |
53 |
|
✗ |
gtk_widget_set_tooltip_text (GTK_WIDGET (self->mute_button), tooltip); |
54 |
|
✗ |
} |
55 |
|
|
|
56 |
|
|
static void |
57 |
|
✗ |
update_volume_icon (CcVolumeSlider *self) |
58 |
|
|
{ |
59 |
|
✗ |
const gchar *icon_name = NULL; |
60 |
|
|
gdouble volume, fraction; |
61 |
|
|
|
62 |
|
✗ |
volume = gtk_adjustment_get_value (self->volume_adjustment); |
63 |
|
✗ |
fraction = (100.0 * volume) / gtk_adjustment_get_upper (self->volume_adjustment); |
64 |
|
|
|
65 |
|
✗ |
update_mute_button_tooltip (self, volume); |
66 |
|
|
|
67 |
|
✗ |
switch (self->type) |
68 |
|
|
{ |
69 |
|
✗ |
case CC_STREAM_TYPE_INPUT: |
70 |
|
✗ |
if (fraction == 0.0) |
71 |
|
✗ |
icon_name = "microphone-sensitivity-muted-symbolic"; |
72 |
|
✗ |
else if (fraction < 30.0) |
73 |
|
✗ |
icon_name = "microphone-sensitivity-low-symbolic"; |
74 |
|
✗ |
else if (fraction > 30.0 && fraction < 70.0) |
75 |
|
✗ |
icon_name = "microphone-sensitivity-medium-symbolic"; |
76 |
|
|
else |
77 |
|
✗ |
icon_name = "microphone-sensitivity-high-symbolic"; |
78 |
|
✗ |
break; |
79 |
|
|
|
80 |
|
✗ |
case CC_STREAM_TYPE_OUTPUT: |
81 |
|
✗ |
if (fraction == 0.0) |
82 |
|
✗ |
icon_name = "audio-volume-muted-symbolic"; |
83 |
|
✗ |
else if (fraction < 30.0) |
84 |
|
✗ |
icon_name = "audio-volume-low-symbolic"; |
85 |
|
✗ |
else if (fraction > 30.0 && fraction < 70.0) |
86 |
|
✗ |
icon_name = "audio-volume-medium-symbolic"; |
87 |
|
|
else |
88 |
|
✗ |
icon_name = "audio-volume-high-symbolic"; |
89 |
|
✗ |
break; |
90 |
|
|
|
91 |
|
✗ |
default: |
92 |
|
✗ |
g_assert_not_reached (); |
93 |
|
|
break; |
94 |
|
|
} |
95 |
|
|
|
96 |
|
✗ |
gtk_button_set_icon_name (GTK_BUTTON (self->mute_button), icon_name); |
97 |
|
✗ |
} |
98 |
|
|
|
99 |
|
|
static void |
100 |
|
✗ |
volume_changed_cb (CcVolumeSlider *self) |
101 |
|
|
{ |
102 |
|
|
gdouble volume, rounded; |
103 |
|
|
|
104 |
|
✗ |
if (self->stream == NULL) |
105 |
|
✗ |
return; |
106 |
|
|
|
107 |
|
✗ |
volume = gtk_adjustment_get_value (self->volume_adjustment); |
108 |
|
✗ |
rounded = round (volume); |
109 |
|
|
|
110 |
|
|
// If the stream is muted, unmute it |
111 |
|
✗ |
if (gvc_mixer_stream_get_is_muted (self->stream)) |
112 |
|
✗ |
gvc_mixer_stream_change_is_muted (self->stream, FALSE); |
113 |
|
|
|
114 |
|
✗ |
if (gvc_mixer_stream_set_volume (self->stream, (pa_volume_t) rounded)) |
115 |
|
✗ |
gvc_mixer_stream_push_volume (self->stream); |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
static void |
119 |
|
✗ |
notify_volume_cb (CcVolumeSlider *self) |
120 |
|
|
{ |
121 |
|
✗ |
g_signal_handlers_block_by_func (self->volume_adjustment, volume_changed_cb, self); |
122 |
|
✗ |
gtk_adjustment_set_value (self->volume_adjustment, gvc_mixer_stream_get_volume (self->stream)); |
123 |
|
✗ |
g_signal_handlers_unblock_by_func (self->volume_adjustment, volume_changed_cb, self); |
124 |
|
|
|
125 |
|
✗ |
update_volume_icon (self); |
126 |
|
✗ |
} |
127 |
|
|
|
128 |
|
|
static void |
129 |
|
✗ |
update_ranges (CcVolumeSlider *self) |
130 |
|
|
{ |
131 |
|
|
gdouble vol_max_norm; |
132 |
|
|
|
133 |
|
✗ |
if (self->mixer_control == NULL) |
134 |
|
✗ |
return; |
135 |
|
|
|
136 |
|
✗ |
vol_max_norm = gvc_mixer_control_get_vol_max_norm (self->mixer_control); |
137 |
|
|
|
138 |
|
✗ |
gtk_scale_clear_marks (self->volume_scale); |
139 |
|
✗ |
if (self->is_amplified) |
140 |
|
|
{ |
141 |
|
✗ |
gtk_adjustment_set_upper (self->volume_adjustment, gvc_mixer_control_get_vol_max_amplified (self->mixer_control)); |
142 |
|
✗ |
gtk_scale_add_mark (self->volume_scale, |
143 |
|
|
vol_max_norm, |
144 |
|
|
GTK_POS_BOTTOM, |
145 |
|
✗ |
C_("volume", "100%")); |
146 |
|
|
} |
147 |
|
|
else |
148 |
|
|
{ |
149 |
|
✗ |
gtk_adjustment_set_upper (self->volume_adjustment, vol_max_norm); |
150 |
|
|
} |
151 |
|
✗ |
gtk_adjustment_set_page_increment (self->volume_adjustment, vol_max_norm / 10.0); |
152 |
|
✗ |
gtk_adjustment_set_step_increment (self->volume_adjustment, vol_max_norm / 100.0); |
153 |
|
|
|
154 |
|
✗ |
if (self->stream) |
155 |
|
|
{ |
156 |
|
✗ |
notify_volume_cb (self); |
157 |
|
✗ |
notify_is_muted_cb (self); |
158 |
|
|
} |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
static void |
162 |
|
✗ |
mute_cb (GtkWidget *widget, |
163 |
|
|
const char *action_name, |
164 |
|
|
GVariant *parameter) |
165 |
|
|
{ |
166 |
|
✗ |
CcVolumeSlider *self = CC_VOLUME_SLIDER (widget); |
167 |
|
|
|
168 |
|
✗ |
if (self->stream == NULL) |
169 |
|
✗ |
return; |
170 |
|
|
|
171 |
|
✗ |
if (!gvc_mixer_stream_get_is_muted (self->stream) && |
172 |
|
✗ |
gvc_mixer_stream_get_volume (self->stream) == 0.0) |
173 |
|
✗ |
{ |
174 |
|
✗ |
gdouble default_volume = gvc_mixer_control_get_vol_max_norm (self->mixer_control) * 0.25; |
175 |
|
|
|
176 |
|
✗ |
if (gvc_mixer_stream_set_volume (self->stream, (pa_volume_t) default_volume)) |
177 |
|
✗ |
gvc_mixer_stream_push_volume (self->stream); |
178 |
|
|
} |
179 |
|
|
else |
180 |
|
|
{ |
181 |
|
✗ |
gvc_mixer_stream_change_is_muted (self->stream, !gvc_mixer_stream_get_is_muted (self->stream)); |
182 |
|
|
} |
183 |
|
|
} |
184 |
|
|
|
185 |
|
|
static void |
186 |
|
✗ |
notify_is_muted_cb (CcVolumeSlider *self) |
187 |
|
|
{ |
188 |
|
✗ |
if (gvc_mixer_stream_get_is_muted (self->stream)) |
189 |
|
|
{ |
190 |
|
✗ |
g_signal_handlers_block_by_func (self->volume_adjustment, volume_changed_cb, self); |
191 |
|
✗ |
gtk_adjustment_set_value (self->volume_adjustment, 0.0); |
192 |
|
✗ |
g_signal_handlers_unblock_by_func (self->volume_adjustment, volume_changed_cb, self); |
193 |
|
|
|
194 |
|
✗ |
update_volume_icon (self); |
195 |
|
|
} |
196 |
|
|
else |
197 |
|
|
{ |
198 |
|
✗ |
notify_volume_cb (self); |
199 |
|
|
} |
200 |
|
✗ |
} |
201 |
|
|
|
202 |
|
|
static void |
203 |
|
✗ |
cc_volume_slider_dispose (GObject *object) |
204 |
|
|
{ |
205 |
|
✗ |
CcVolumeSlider *self = CC_VOLUME_SLIDER (object); |
206 |
|
|
|
207 |
|
✗ |
g_clear_pointer ((GtkWidget **) &self->volume_scale, gtk_widget_unparent); |
208 |
|
✗ |
g_clear_pointer ((GtkWidget **) &self->mute_button, gtk_widget_unparent); |
209 |
|
|
|
210 |
|
✗ |
g_clear_object (&self->mixer_control); |
211 |
|
✗ |
g_clear_object (&self->stream); |
212 |
|
|
|
213 |
|
✗ |
G_OBJECT_CLASS (cc_volume_slider_parent_class)->dispose (object); |
214 |
|
✗ |
} |
215 |
|
|
|
216 |
|
|
void |
217 |
|
✗ |
cc_volume_slider_class_init (CcVolumeSliderClass *klass) |
218 |
|
|
{ |
219 |
|
✗ |
GObjectClass *object_class = G_OBJECT_CLASS (klass); |
220 |
|
✗ |
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
221 |
|
|
|
222 |
|
✗ |
object_class->dispose = cc_volume_slider_dispose; |
223 |
|
|
|
224 |
|
✗ |
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/sound/cc-volume-slider.ui"); |
225 |
|
|
|
226 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcVolumeSlider, mute_button); |
227 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcVolumeSlider, volume_adjustment); |
228 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcVolumeSlider, volume_scale); |
229 |
|
|
|
230 |
|
✗ |
gtk_widget_class_bind_template_callback (widget_class, volume_changed_cb); |
231 |
|
|
|
232 |
|
✗ |
gtk_widget_class_install_action (widget_class, "volume-slider.mute", NULL, mute_cb); |
233 |
|
|
|
234 |
|
✗ |
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); |
235 |
|
✗ |
} |
236 |
|
|
|
237 |
|
|
void |
238 |
|
✗ |
cc_volume_slider_init (CcVolumeSlider *self) |
239 |
|
|
{ |
240 |
|
|
GtkLayoutManager *box_layout; |
241 |
|
|
|
242 |
|
✗ |
g_resources_register (cc_sound_get_resource ()); |
243 |
|
|
|
244 |
|
✗ |
gtk_widget_init_template (GTK_WIDGET (self)); |
245 |
|
|
|
246 |
|
✗ |
box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (self)); |
247 |
|
✗ |
gtk_box_layout_set_spacing (GTK_BOX_LAYOUT (box_layout), 6); |
248 |
|
✗ |
} |
249 |
|
|
|
250 |
|
|
void |
251 |
|
✗ |
cc_volume_slider_set_mixer_control (CcVolumeSlider *self, |
252 |
|
|
GvcMixerControl *mixer_control) |
253 |
|
|
{ |
254 |
|
✗ |
g_return_if_fail (CC_IS_VOLUME_SLIDER (self)); |
255 |
|
|
|
256 |
|
✗ |
g_set_object (&self->mixer_control, mixer_control); |
257 |
|
|
|
258 |
|
✗ |
update_ranges (self); |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
void |
262 |
|
✗ |
cc_volume_slider_set_stream (CcVolumeSlider *self, |
263 |
|
|
GvcMixerStream *stream, |
264 |
|
|
CcStreamType type) |
265 |
|
|
{ |
266 |
|
✗ |
g_return_if_fail (CC_IS_VOLUME_SLIDER (self)); |
267 |
|
|
|
268 |
|
✗ |
if (self->stream != NULL) |
269 |
|
|
{ |
270 |
|
✗ |
g_signal_handler_disconnect (self->stream, self->notify_volume_handler_id); |
271 |
|
✗ |
self->notify_volume_handler_id = 0; |
272 |
|
✗ |
g_signal_handler_disconnect (self->stream, self->notify_is_muted_handler_id); |
273 |
|
✗ |
self->notify_is_muted_handler_id = 0; |
274 |
|
|
} |
275 |
|
✗ |
g_clear_object (&self->stream); |
276 |
|
|
|
277 |
|
✗ |
self->type = type; |
278 |
|
|
|
279 |
|
✗ |
if (stream != NULL) |
280 |
|
|
{ |
281 |
|
✗ |
self->stream = g_object_ref (stream); |
282 |
|
|
|
283 |
|
✗ |
self->notify_volume_handler_id = g_signal_connect_object (stream, |
284 |
|
|
"notify::volume", |
285 |
|
|
G_CALLBACK (notify_volume_cb), |
286 |
|
|
self, G_CONNECT_SWAPPED); |
287 |
|
✗ |
self->notify_is_muted_handler_id = g_signal_connect_object (stream, |
288 |
|
|
"notify::is-muted", |
289 |
|
|
G_CALLBACK (notify_is_muted_cb), |
290 |
|
|
self, G_CONNECT_SWAPPED); |
291 |
|
✗ |
notify_volume_cb (self); |
292 |
|
✗ |
notify_is_muted_cb (self); |
293 |
|
|
} |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
GvcMixerStream * |
297 |
|
✗ |
cc_volume_slider_get_stream (CcVolumeSlider *self) |
298 |
|
|
{ |
299 |
|
✗ |
g_return_val_if_fail (CC_IS_VOLUME_SLIDER (self), NULL); |
300 |
|
|
|
301 |
|
✗ |
return self->stream; |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
void |
305 |
|
✗ |
cc_volume_slider_set_is_amplified (CcVolumeSlider *self, |
306 |
|
|
gboolean is_amplified) |
307 |
|
|
{ |
308 |
|
✗ |
g_return_if_fail (CC_IS_VOLUME_SLIDER (self)); |
309 |
|
|
|
310 |
|
✗ |
self->is_amplified = is_amplified; |
311 |
|
|
|
312 |
|
✗ |
update_ranges (self); |
313 |
|
|
} |
314 |
|
|
|