GCC Code Coverage Report


Directory: ./
File: panels/sound/cc-volume-levels-window.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 83 0.0%
Functions: 0 13 0.0%
Branches: 0 45 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2018 Canonical Ltd.
3 * Copyright (C) 2023 Marco Melorio
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <pulse/pulseaudio.h>
20 #include <gvc-mixer-sink.h>
21 #include <gvc-mixer-source.h>
22
23 #include "cc-stream-row.h"
24 #include "cc-volume-levels-window.h"
25
26 struct _CcVolumeLevelsWindow
27 {
28 AdwDialog parent_instance;
29
30 GtkListBox *listbox;
31 GtkSizeGroup *label_size_group;
32
33 GvcMixerControl *mixer_control;
34 GListStore *stream_list;
35 };
36
37 G_DEFINE_TYPE (CcVolumeLevelsWindow, cc_volume_levels_window, ADW_TYPE_DIALOG)
38
39 static gint
40 sort_stream (gconstpointer a,
41 gconstpointer b,
42 gpointer user_data)
43 {
44 CcVolumeLevelsWindow *self = user_data;
45 GvcMixerStream *stream_a, *stream_b, *event_sink;
46 g_autofree gchar *name_a = NULL;
47 g_autofree gchar *name_b = NULL;
48
49 stream_a = GVC_MIXER_STREAM (a);
50 stream_b = GVC_MIXER_STREAM (b);
51
52 /* Put the system sound events control at the top */
53 event_sink = gvc_mixer_control_get_event_sink_input (self->mixer_control);
54 if (stream_a == event_sink)
55 return -1;
56 else if (stream_b == event_sink)
57 return 1;
58
59 name_a = g_utf8_casefold (gvc_mixer_stream_get_name (stream_a), -1);
60 name_b = g_utf8_casefold (gvc_mixer_stream_get_name (stream_b), -1);
61
62 return g_strcmp0 (name_a, name_b);
63 }
64
65 static gboolean
66 filter_stream (gpointer item,
67 gpointer user_data)
68 {
69 GvcMixerStream *stream = item;
70 const gchar *app_id = gvc_mixer_stream_get_application_id (stream);
71
72 /* Filter out master volume controls */
73 if (g_strcmp0 (app_id, "org.gnome.VolumeControl") == 0 ||
74 g_strcmp0 (app_id, "org.PulseAudio.pavucontrol") == 0)
75 {
76 return FALSE;
77 }
78
79 /* Filter out streams that aren't volume controls */
80 if (GVC_IS_MIXER_SOURCE (stream) ||
81 GVC_IS_MIXER_SINK (stream) ||
82 gvc_mixer_stream_is_virtual (stream) ||
83 gvc_mixer_stream_is_event_stream (stream))
84 {
85 return FALSE;
86 }
87
88 return TRUE;
89 }
90
91 static GtkWidget *
92 create_stream_row (gpointer item,
93 gpointer user_data)
94 {
95 CcVolumeLevelsWindow *self = user_data;
96 GvcMixerStream *stream = item;
97 guint id;
98 CcStreamRow *row;
99
100 id = gvc_mixer_stream_get_id (stream);
101 row = cc_stream_row_new (self->label_size_group, stream, id, CC_STREAM_TYPE_OUTPUT, self->mixer_control);
102
103 return GTK_WIDGET (row);
104 }
105
106 static void
107 stream_added_cb (CcVolumeLevelsWindow *self,
108 guint id)
109 {
110 GvcMixerStream *stream = gvc_mixer_control_lookup_stream_id (self->mixer_control, id);
111
112 if (stream == NULL)
113 return;
114
115 g_list_store_append (self->stream_list, G_OBJECT (stream));
116 }
117
118 static void
119 stream_removed_cb (CcVolumeLevelsWindow *self,
120 guint id)
121 {
122 guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self->stream_list));
123
124 for (guint i = 0; i < n_items; i++)
125 {
126 g_autoptr(GObject) item;
127 guint stream_id;
128
129 item = g_list_model_get_item (G_LIST_MODEL (self->stream_list), i);
130 stream_id = gvc_mixer_stream_get_id (GVC_MIXER_STREAM (item));
131
132 if (id == stream_id)
133 {
134 g_list_store_remove (self->stream_list, i);
135 return;
136 }
137 }
138 }
139
140 static void
141 add_stream (gpointer data,
142 gpointer user_data)
143 {
144 CcVolumeLevelsWindow *self = user_data;
145 GvcMixerStream *stream = data;
146
147 g_list_store_append (self->stream_list, G_OBJECT (stream));
148 }
149
150 static void
151 cc_volume_levels_window_dispose (GObject *object)
152 {
153 CcVolumeLevelsWindow *self = CC_VOLUME_LEVELS_WINDOW (object);
154
155 g_clear_object (&self->mixer_control);
156
157 G_OBJECT_CLASS (cc_volume_levels_window_parent_class)->dispose (object);
158 }
159
160 void
161 cc_volume_levels_window_class_init (CcVolumeLevelsWindowClass *klass)
162 {
163 GObjectClass *object_class = G_OBJECT_CLASS (klass);
164 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
165
166 object_class->dispose = cc_volume_levels_window_dispose;
167
168 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/sound/cc-volume-levels-window.ui");
169
170 gtk_widget_class_bind_template_child (widget_class, CcVolumeLevelsWindow, listbox);
171 gtk_widget_class_bind_template_child (widget_class, CcVolumeLevelsWindow, label_size_group);
172 }
173
174 void
175 cc_volume_levels_window_init (CcVolumeLevelsWindow *self)
176 {
177 GtkFilter *filter;
178 GtkFilterListModel *filter_model;
179 GtkSorter *sorter;
180 GtkSortListModel *sort_model;
181
182 gtk_widget_init_template (GTK_WIDGET (self));
183
184 self->stream_list = g_list_store_new (GVC_TYPE_MIXER_STREAM);
185
186 filter = GTK_FILTER (gtk_custom_filter_new (filter_stream, NULL, NULL));
187 filter_model = gtk_filter_list_model_new (G_LIST_MODEL (self->stream_list), filter);
188
189 sorter = GTK_SORTER (gtk_custom_sorter_new (sort_stream, self, NULL));
190 sort_model = gtk_sort_list_model_new (G_LIST_MODEL (filter_model), sorter);
191
192 gtk_list_box_bind_model (self->listbox,
193 G_LIST_MODEL (sort_model),
194 create_stream_row,
195 self, NULL);
196 }
197
198 CcVolumeLevelsWindow *
199 cc_volume_levels_window_new (GvcMixerControl *mixer_control)
200 {
201 CcVolumeLevelsWindow *self;
202 g_autoptr(GSList) streams = NULL;
203
204 self = g_object_new (CC_TYPE_VOLUME_LEVELS_WINDOW, NULL);
205
206 self->mixer_control = g_object_ref (mixer_control);
207
208 streams = gvc_mixer_control_get_streams (self->mixer_control);
209 g_slist_foreach (streams, add_stream, self);
210
211 g_signal_connect_object (self->mixer_control,
212 "stream-added",
213 G_CALLBACK (stream_added_cb),
214 self, G_CONNECT_SWAPPED);
215 g_signal_connect_object (self->mixer_control,
216 "stream-removed",
217 G_CALLBACK (stream_removed_cb),
218 self, G_CONNECT_SWAPPED);
219
220 return self;
221 }
222