GCC Code Coverage Report


Directory: ./
File: panels/sound/cc-volume-slider.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 134 0.0%
Functions: 0 17 0.0%
Branches: 0 72 0.0%

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