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 |
|
|
|