Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-mixer-remote"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <adwaita.h>
9 : : #include <glib/gi18n-lib.h>
10 : : #include <gtk/gtk.h>
11 : : #include <pango/pango.h>
12 : : #include <valent.h>
13 : :
14 : : #include "valent-mixer-remote.h"
15 : : #include "valent-ui-utils-private.h"
16 : :
17 : : struct _ValentMixerRemote
18 : : {
19 : : AdwBreakpointBin parent_instance;
20 : :
21 : : ValentMixerAdapter *adapter;
22 : :
23 : : /* template */
24 : : GtkListBox *stream_list;
25 : : GListModel *model;
26 : : GtkFilter *filter;
27 : : GtkStringSorter *sorter;
28 : : GtkWidget *group;
29 : : };
30 : :
31 [ + + + - ]: 36 : G_DEFINE_FINAL_TYPE (ValentMixerRemote, valent_mixer_remote, ADW_TYPE_BREAKPOINT_BIN)
32 : :
33 : : typedef enum {
34 : : PROP_MIXER = 1,
35 : : } ValentMixerRemoteProperty;
36 : :
37 : : static GParamSpec *properties[PROP_MIXER + 1] = { NULL, };
38 : :
39 : : static gboolean
40 : 0 : valent_mixer_output_filter (gpointer item,
41 : : gpointer user_data)
42 : : {
43 : 0 : ValentMixerStream *stream = VALENT_MIXER_STREAM (item);
44 : :
45 [ # # ]: 0 : if (valent_mixer_stream_get_direction (stream) == VALENT_MIXER_OUTPUT)
46 : 0 : return TRUE;
47 : :
48 : : return FALSE;
49 : : }
50 : :
51 : : static gboolean
52 : 0 : on_change_value (GtkRange *range,
53 : : GtkScrollType scroll,
54 : : double value,
55 : : ValentMixerStream *stream)
56 : : {
57 : 0 : GtkAdjustment *adjustment;
58 : 0 : double lower, upper, page_size;
59 : :
60 [ # # ]: 0 : g_assert (VALENT_IS_MIXER_STREAM (stream));
61 : :
62 : 0 : adjustment = gtk_range_get_adjustment (range);
63 : 0 : lower = gtk_adjustment_get_lower (adjustment);
64 : 0 : upper = gtk_adjustment_get_upper (adjustment);
65 : 0 : page_size = gtk_adjustment_get_page_size (adjustment);
66 [ # # ]: 0 : value = CLAMP (value, lower, (upper - page_size));
67 : :
68 : 0 : gtk_adjustment_set_value (adjustment, value);
69 : 0 : valent_mixer_stream_set_level (stream, (unsigned int)round (value));
70 : :
71 : 0 : return GDK_EVENT_STOP;
72 : : }
73 : :
74 : : static void
75 : 0 : update_icon_cb (ValentMixerStream *stream,
76 : : GParamSpec *pspec,
77 : : GtkButton *button)
78 : : {
79 : 0 : unsigned int level = 0;
80 : :
81 [ # # ]: 0 : if (valent_mixer_stream_get_muted (stream))
82 : : {
83 : 0 : gtk_button_set_icon_name (button, "audio-volume-muted-symbolic");
84 : 0 : return;
85 : : }
86 : :
87 : 0 : level = valent_mixer_stream_get_level (stream);
88 [ # # ]: 0 : if (level >= 70)
89 : 0 : gtk_button_set_icon_name (button, "audio-volume-high-symbolic");
90 [ # # ]: 0 : else if (level >= 30)
91 : 0 : gtk_button_set_icon_name (button, "audio-volume-medium-symbolic");
92 [ # # ]: 0 : else if (level > 0)
93 : 0 : gtk_button_set_icon_name (button, "audio-volume-low-symbolic");
94 : : else
95 : 0 : gtk_button_set_icon_name (button, "audio-volume-muted-symbolic");
96 : : }
97 : :
98 : : static void
99 : 0 : on_stream_selected (GtkCheckButton *button,
100 : : GParamSpec *pspec,
101 : : ValentMixerRemote *self)
102 : : {
103 [ # # ]: 0 : if (gtk_check_button_get_active (button))
104 : : {
105 : 0 : ValentMixerStream *stream = NULL;
106 : :
107 : 0 : stream = g_object_get_data (G_OBJECT (button), "valent-mixer-stream");
108 : 0 : valent_mixer_adapter_set_default_output (self->adapter, stream);
109 : : }
110 : 0 : }
111 : :
112 : : /*
113 : : * ValentMixerRemote
114 : : */
115 : : static GtkWidget *
116 : 0 : stream_list_create (gpointer item,
117 : : gpointer user_data)
118 : : {
119 : 0 : ValentMixerRemote *self = VALENT_MIXER_REMOTE (user_data);
120 : 0 : ValentMixerStream *stream = VALENT_MIXER_STREAM (item);
121 : 0 : ValentMixerStream *default_output = NULL;
122 : 0 : GtkAdjustment *adjustment;
123 : 0 : GtkWidget *row;
124 : 0 : GtkWidget *grid;
125 : 0 : GtkWidget *radio;
126 : 0 : GtkWidget *button;
127 : 0 : GtkWidget *label;
128 : 0 : GtkWidget *slider;
129 : :
130 : 0 : default_output = valent_mixer_adapter_get_default_output (self->adapter);
131 : :
132 : 0 : grid = g_object_new (GTK_TYPE_GRID,
133 : : "row-spacing", 8,
134 : : NULL);
135 : :
136 : 0 : radio = g_object_new (GTK_TYPE_CHECK_BUTTON,
137 : : "active", (default_output == stream),
138 : : "group", self->group,
139 : : "valign", GTK_ALIGN_CENTER,
140 : : NULL);
141 : 0 : g_signal_connect_object (radio,
142 : : "toggled",
143 : : G_CALLBACK (on_stream_selected),
144 : : self,
145 : : G_CONNECT_DEFAULT);
146 : 0 : g_object_set_data (G_OBJECT (radio), "valent-mixer-stream", stream);
147 : 0 : gtk_grid_attach (GTK_GRID (grid), radio, 0, 0, 2, 1);
148 : :
149 [ # # ]: 0 : if (self->group == NULL)
150 : 0 : self->group = radio;
151 : :
152 : 0 : label = g_object_new (GTK_TYPE_LABEL,
153 : : "label", valent_mixer_stream_get_description (stream),
154 : : "ellipsize", PANGO_ELLIPSIZE_END,
155 : : "xalign", 0.0,
156 : : NULL);
157 : 0 : g_object_bind_property (stream, "description",
158 : : label, "label",
159 : : G_BINDING_DEFAULT);
160 : 0 : gtk_check_button_set_child (GTK_CHECK_BUTTON (radio), label);
161 : :
162 : 0 : button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
163 : : "active", valent_mixer_stream_get_muted (stream),
164 : : "icon-name", "audio-volume-medium-symbolic",
165 : : NULL);
166 : 0 : gtk_widget_add_css_class (button, "circular");
167 : 0 : gtk_widget_add_css_class (button, "flat");
168 : 0 : g_signal_connect_object (stream,
169 : : "notify::muted",
170 : : G_CALLBACK (update_icon_cb),
171 : : button,
172 : : G_CONNECT_DEFAULT);
173 : 0 : g_object_bind_property (stream, "muted",
174 : : button, "active",
175 : : G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
176 : 0 : gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 1, 1);
177 : :
178 : 0 : adjustment = gtk_adjustment_new (valent_mixer_stream_get_level (stream),
179 : : 0.0, 110.0,
180 : : 1.0, // step increment
181 : : 2.0, // page increment
182 : : 10.0); // page size
183 : 0 : g_object_bind_property (stream, "level",
184 : : adjustment, "value",
185 : : G_BINDING_DEFAULT);
186 : 0 : g_signal_connect_object (stream,
187 : : "notify::level",
188 : : G_CALLBACK (update_icon_cb),
189 : : button,
190 : : G_CONNECT_DEFAULT);
191 : 0 : slider = g_object_new (GTK_TYPE_SCALE,
192 : : "adjustment", adjustment,
193 : : "hexpand", TRUE,
194 : : NULL);
195 : 0 : g_signal_connect_object (slider,
196 : : "change-value",
197 : : G_CALLBACK (on_change_value),
198 : : stream,
199 : : G_CONNECT_DEFAULT);
200 : 0 : gtk_grid_attach (GTK_GRID (grid), slider, 1, 1, 1, 1);
201 : :
202 : 0 : row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
203 : : "activatable", FALSE,
204 : : "selectable", FALSE,
205 : : "child", grid,
206 : : NULL);
207 : 0 : gtk_widget_add_css_class (row, "valent-mixer-row");
208 : 0 : gtk_accessible_update_relation (GTK_ACCESSIBLE (row),
209 : : GTK_ACCESSIBLE_RELATION_DESCRIBED_BY, label, NULL,
210 : : -1);
211 : :
212 : 0 : return row;
213 : : }
214 : :
215 : : static void
216 : 1 : valent_mixer_remote_set_mixer (ValentMixerRemote *self,
217 : : ValentMixerAdapter *mixer)
218 : : {
219 [ + - ]: 1 : g_assert (VALENT_IS_MIXER_REMOTE (self));
220 [ - + - - ]: 1 : g_assert (mixer == NULL || VALENT_IS_MIXER_ADAPTER (mixer));
221 : :
222 [ - + ]: 1 : if (g_set_object (&self->adapter, mixer))
223 : : {
224 : 0 : self->group = NULL;
225 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIXER]);
226 : : }
227 : 1 : }
228 : :
229 : : /*
230 : : * GObject
231 : : */
232 : : static void
233 : 1 : valent_mixer_remote_dispose (GObject *object)
234 : : {
235 : 1 : ValentMixerRemote *self = VALENT_MIXER_REMOTE (object);
236 : :
237 [ - + ]: 1 : if (self->adapter != NULL)
238 : : {
239 : 0 : g_signal_handlers_disconnect_by_data (self->adapter, self);
240 [ # # ]: 0 : g_clear_object (&self->adapter);
241 : : }
242 : :
243 : 1 : G_OBJECT_CLASS (valent_mixer_remote_parent_class)->dispose (object);
244 : 1 : }
245 : :
246 : : static void
247 : 1 : valent_mixer_remote_get_property (GObject *object,
248 : : guint prop_id,
249 : : GValue *value,
250 : : GParamSpec *pspec)
251 : : {
252 : 1 : ValentMixerRemote *self = VALENT_MIXER_REMOTE (object);
253 : :
254 [ + - ]: 1 : switch ((ValentMixerRemoteProperty)prop_id)
255 : : {
256 : 1 : case PROP_MIXER:
257 : 1 : g_value_set_object (value, self->adapter);
258 : 1 : break;
259 : :
260 : 0 : default:
261 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
262 : : }
263 : 1 : }
264 : :
265 : : static void
266 : 1 : valent_mixer_remote_set_property (GObject *object,
267 : : guint prop_id,
268 : : const GValue *value,
269 : : GParamSpec *pspec)
270 : : {
271 : 1 : ValentMixerRemote *self = VALENT_MIXER_REMOTE (object);
272 : :
273 [ + - ]: 1 : switch ((ValentMixerRemoteProperty)prop_id)
274 : : {
275 : 1 : case PROP_MIXER:
276 : 1 : valent_mixer_remote_set_mixer (self, g_value_get_object (value));
277 : 1 : break;
278 : :
279 : 0 : default:
280 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
281 : : }
282 : 1 : }
283 : :
284 : : static void
285 : 1 : valent_mixer_remote_class_init (ValentMixerRemoteClass *klass)
286 : : {
287 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
288 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
289 : :
290 : 1 : object_class->dispose = valent_mixer_remote_dispose;
291 : 1 : object_class->get_property = valent_mixer_remote_get_property;
292 : 1 : object_class->set_property = valent_mixer_remote_set_property;
293 : :
294 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-mixer-remote.ui");
295 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMixerRemote, stream_list);
296 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMixerRemote, filter);
297 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMixerRemote, model);
298 : :
299 : 2 : properties [PROP_MIXER] =
300 : 1 : g_param_spec_object ("mixer", NULL, NULL,
301 : : VALENT_TYPE_MIXER_ADAPTER,
302 : : (G_PARAM_READWRITE |
303 : : G_PARAM_EXPLICIT_NOTIFY |
304 : : G_PARAM_STATIC_STRINGS));
305 : :
306 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
307 : 1 : }
308 : :
309 : : static void
310 : 1 : valent_mixer_remote_init (ValentMixerRemote *self)
311 : : {
312 : 1 : gtk_widget_init_template (GTK_WIDGET (self));
313 : :
314 : 1 : gtk_custom_filter_set_filter_func (GTK_CUSTOM_FILTER (self->filter),
315 : : valent_mixer_output_filter,
316 : : self,
317 : : NULL);
318 : 1 : gtk_list_box_bind_model (self->stream_list,
319 : 1 : G_LIST_MODEL (self->model),
320 : : stream_list_create,
321 : : self,
322 : : NULL);
323 : 1 : }
324 : :
|