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"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <libpeas.h>
10 : : #include <libvalent-core.h>
11 : :
12 : : #include "valent-mixer.h"
13 : : #include "valent-mixer-adapter.h"
14 : : #include "valent-mixer-stream.h"
15 : :
16 : :
17 : : /**
18 : : * ValentMixer:
19 : : *
20 : : * A class for monitoring and controlling the system volume.
21 : : *
22 : : * `ValentMixer` is an abstraction of volume mixers, intended for use by
23 : : * [class@Valent.DevicePlugin] implementations.
24 : : *
25 : : * Plugins can implement [class@Valent.MixerAdapter] to provide an interface to
26 : : * monitor and control audio streams.
27 : : *
28 : : * Since: 1.0
29 : : */
30 : :
31 : : struct _ValentMixer
32 : : {
33 : : ValentComponent parent_instance;
34 : :
35 : : ValentMixerAdapter *default_adapter;
36 : : GPtrArray *streams;
37 : : };
38 : :
39 : : static void g_list_model_iface_init (GListModelInterface *iface);
40 : :
41 [ + + + - ]: 279 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentMixer, valent_mixer, VALENT_TYPE_COMPONENT,
42 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
43 : :
44 : : enum {
45 : : PROP_0,
46 : : PROP_DEFAULT_INPUT,
47 : : PROP_DEFAULT_OUTPUT,
48 : : N_PROPERTIES
49 : : };
50 : :
51 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
52 : :
53 : : static ValentMixer *default_mixer = NULL;
54 : :
55 : : #if 0
56 : : /* Translate the position */
57 : : for (unsigned int i = 0; i < self->adapters->len; i++)
58 : : {
59 : : GListModel *adapter = g_ptr_array_index (self->adapters, i);
60 : :
61 : : if (adapter == list)
62 : : break;
63 : :
64 : : offset += g_list_model_get_n_items (adapter);
65 : : }
66 : : #endif
67 : :
68 : :
69 : : /*
70 : : * ValentMixerAdapter Callbacks
71 : : */
72 : : static void
73 : 11 : on_default_input_changed (ValentMixerAdapter *adapter,
74 : : GParamSpec *pspec,
75 : : ValentMixer *self)
76 : : {
77 [ + - ]: 11 : if (self->default_adapter != adapter)
78 : : return;
79 : :
80 : 11 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEFAULT_INPUT]);
81 : : }
82 : :
83 : : static void
84 : 14 : on_default_output_changed (ValentMixerAdapter *adapter,
85 : : GParamSpec *pspec,
86 : : ValentMixer *self)
87 : : {
88 [ + - ]: 14 : if (self->default_adapter != adapter)
89 : : return;
90 : :
91 : 14 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEFAULT_OUTPUT]);
92 : : }
93 : :
94 : : static void
95 : 36 : on_items_changed (GListModel *list,
96 : : unsigned int position,
97 : : unsigned int removed,
98 : : unsigned int added,
99 : : ValentMixer *self)
100 : : {
101 [ + - ]: 36 : g_assert (VALENT_IS_MIXER_ADAPTER (list));
102 [ + - ]: 36 : g_assert (VALENT_IS_MIXER (self));
103 : :
104 : : /* Remove items */
105 [ + + ]: 41 : while (removed--)
106 : 5 : g_ptr_array_remove_index (self->streams, position);
107 : :
108 : : /* Add items */
109 [ + + ]: 59 : for (unsigned int i = 0; i < added; i++)
110 : : {
111 : 23 : g_autoptr (ValentMixerStream) stream = NULL;
112 : :
113 : 23 : stream = g_list_model_get_item (list, position + i);
114 : 23 : g_ptr_array_insert (self->streams, position + i, g_steal_pointer (&stream));
115 : : }
116 : :
117 : 36 : g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
118 : 36 : }
119 : :
120 : : /*
121 : : * ValentComponent
122 : : */
123 : : static void
124 : 8 : valent_mixer_bind_preferred (ValentComponent *component,
125 : : GObject *extension)
126 : : {
127 : 8 : ValentMixer *self = VALENT_MIXER (component);
128 : 8 : ValentMixerAdapter *adapter = VALENT_MIXER_ADAPTER (extension);
129 : :
130 : 8 : VALENT_ENTRY;
131 : :
132 [ + - ]: 8 : g_assert (VALENT_IS_MIXER (self));
133 [ + - - + ]: 8 : g_assert (adapter == NULL || VALENT_IS_MIXER_ADAPTER (adapter));
134 : :
135 [ - + ]: 8 : if (self->default_adapter != NULL)
136 : : {
137 : 0 : GListModel *list = G_LIST_MODEL (self->default_adapter);
138 : :
139 : 0 : g_signal_handlers_disconnect_by_func (self->default_adapter,
140 : : self,
141 : : on_items_changed);
142 : 0 : g_signal_handlers_disconnect_by_func (self->default_adapter,
143 : : self,
144 : : on_default_input_changed);
145 : 0 : g_signal_handlers_disconnect_by_func (self->default_adapter,
146 : : self,
147 : : on_default_output_changed);
148 : 0 : on_items_changed (list, 0, g_list_model_get_n_items (list), 0, self);
149 : 0 : self->default_adapter = NULL;
150 : : }
151 : :
152 [ + - ]: 8 : if (adapter != NULL)
153 : : {
154 : 8 : GListModel *list = G_LIST_MODEL (adapter);
155 : :
156 : 8 : g_signal_connect_object (adapter,
157 : : "notify::default-input",
158 : : G_CALLBACK (on_default_input_changed),
159 : : self, 0);
160 : 8 : g_signal_connect_object (adapter,
161 : : "notify::default-output",
162 : : G_CALLBACK (on_default_output_changed),
163 : : self, 0);
164 : 8 : g_signal_connect_object (adapter,
165 : : "items-changed",
166 : : G_CALLBACK (on_items_changed),
167 : : self, 0);
168 : 8 : on_items_changed (list, 0, 0, g_list_model_get_n_items (list), self);
169 : 8 : g_object_notify (G_OBJECT (self), "default-input");
170 : 8 : g_object_notify (G_OBJECT (self), "default-output");
171 : 8 : self->default_adapter = adapter;
172 : : }
173 : :
174 : 8 : VALENT_EXIT;
175 : : }
176 : :
177 : : /*
178 : : * GListModel
179 : : */
180 : : static gpointer
181 : 11 : valent_mixer_get_item (GListModel *list,
182 : : unsigned int position)
183 : : {
184 : 11 : ValentMixer *self = VALENT_MIXER (list);
185 : :
186 [ + - ]: 11 : g_assert (VALENT_IS_MIXER (self));
187 : :
188 [ + - ]: 11 : if G_UNLIKELY (position >= self->streams->len)
189 : : return NULL;
190 : :
191 : 11 : return g_object_ref (g_ptr_array_index (self->streams, position));
192 : : }
193 : :
194 : : static GType
195 : 0 : valent_mixer_get_item_type (GListModel *list)
196 : : {
197 : 0 : return VALENT_TYPE_MIXER_STREAM;
198 : : }
199 : :
200 : : static unsigned int
201 : 4 : valent_mixer_get_n_items (GListModel *list)
202 : : {
203 : 4 : ValentMixer *self = VALENT_MIXER (list);
204 : :
205 [ + - ]: 4 : g_assert (VALENT_IS_MIXER (self));
206 : :
207 : 4 : return self->streams->len;
208 : : }
209 : :
210 : : static void
211 : 4 : g_list_model_iface_init (GListModelInterface *iface)
212 : : {
213 : 4 : iface->get_item = valent_mixer_get_item;
214 : 4 : iface->get_item_type = valent_mixer_get_item_type;
215 : 4 : iface->get_n_items = valent_mixer_get_n_items;
216 : 4 : }
217 : :
218 : : /*
219 : : * GObject
220 : : */
221 : : static void
222 : 8 : valent_mixer_finalize (GObject *object)
223 : : {
224 : 8 : ValentMixer *self = VALENT_MIXER (object);
225 : :
226 [ + - ]: 8 : g_clear_pointer (&self->streams, g_ptr_array_unref);
227 : :
228 : 8 : G_OBJECT_CLASS (valent_mixer_parent_class)->finalize (object);
229 : 8 : }
230 : :
231 : : static void
232 : 2 : valent_mixer_get_property (GObject *object,
233 : : guint prop_id,
234 : : GValue *value,
235 : : GParamSpec *pspec)
236 : : {
237 : 2 : ValentMixer *self = VALENT_MIXER (object);
238 : :
239 [ + + - ]: 2 : switch (prop_id)
240 : : {
241 : 1 : case PROP_DEFAULT_INPUT:
242 : 1 : g_value_set_object (value, valent_mixer_get_default_input (self));
243 : 1 : break;
244 : :
245 : 1 : case PROP_DEFAULT_OUTPUT:
246 : 1 : g_value_set_object (value, valent_mixer_get_default_output (self));
247 : 1 : break;
248 : :
249 : 0 : default:
250 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 : : }
252 : 2 : }
253 : :
254 : : static void
255 : 2 : valent_mixer_set_property (GObject *object,
256 : : guint prop_id,
257 : : const GValue *value,
258 : : GParamSpec *pspec)
259 : : {
260 : 2 : ValentMixer *self = VALENT_MIXER (object);
261 : :
262 [ + + - ]: 2 : switch (prop_id)
263 : : {
264 : 1 : case PROP_DEFAULT_INPUT:
265 : 1 : valent_mixer_set_default_input (self, g_value_get_object (value));
266 : 1 : break;
267 : :
268 : 1 : case PROP_DEFAULT_OUTPUT:
269 : 1 : valent_mixer_set_default_output (self, g_value_get_object (value));
270 : 1 : break;
271 : :
272 : 0 : default:
273 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
274 : : }
275 : 2 : }
276 : :
277 : : static void
278 : 4 : valent_mixer_class_init (ValentMixerClass *klass)
279 : : {
280 : 4 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
281 : 4 : ValentComponentClass *component_class = VALENT_COMPONENT_CLASS (klass);
282 : :
283 : 4 : object_class->finalize = valent_mixer_finalize;
284 : 4 : object_class->get_property = valent_mixer_get_property;
285 : 4 : object_class->set_property = valent_mixer_set_property;
286 : :
287 : 4 : component_class->bind_preferred = valent_mixer_bind_preferred;
288 : :
289 : : /**
290 : : * ValentMixer:default-input: (getter get_default_input) (setter set_default_input)
291 : : *
292 : : * The active input stream.
293 : : *
294 : : * Since: 1.0
295 : : */
296 : 8 : properties [PROP_DEFAULT_INPUT] =
297 : 4 : g_param_spec_object ("default-input", NULL, NULL,
298 : : VALENT_TYPE_MIXER_STREAM,
299 : : (G_PARAM_READWRITE |
300 : : G_PARAM_EXPLICIT_NOTIFY |
301 : : G_PARAM_STATIC_STRINGS));
302 : :
303 : : /**
304 : : * ValentMixer:default-output: (getter get_default_output) (setter set_default_output)
305 : : *
306 : : * The active output stream.
307 : : *
308 : : * Since: 1.0
309 : : */
310 : 8 : properties [PROP_DEFAULT_OUTPUT] =
311 : 4 : g_param_spec_object ("default-output", NULL, NULL,
312 : : VALENT_TYPE_MIXER_STREAM,
313 : : (G_PARAM_READWRITE |
314 : : G_PARAM_EXPLICIT_NOTIFY |
315 : : G_PARAM_STATIC_STRINGS));
316 : :
317 : 4 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
318 : 4 : }
319 : :
320 : : static void
321 : 8 : valent_mixer_init (ValentMixer *self)
322 : : {
323 : 8 : self->streams = g_ptr_array_new_with_free_func (g_object_unref);
324 : 8 : }
325 : :
326 : : /**
327 : : * valent_mixer_get_default:
328 : : *
329 : : * Get the default [class@Valent.Mixer].
330 : : *
331 : : * Returns: (transfer none) (not nullable): a `ValentMixer`
332 : : *
333 : : * Since: 1.0
334 : : */
335 : : ValentMixer *
336 : 25 : valent_mixer_get_default (void)
337 : : {
338 [ + + ]: 25 : if (default_mixer == NULL)
339 : : {
340 : 8 : default_mixer = g_object_new (VALENT_TYPE_MIXER,
341 : : "plugin-domain", "mixer",
342 : : "plugin-type", VALENT_TYPE_MIXER_ADAPTER,
343 : : NULL);
344 : :
345 : 8 : g_object_add_weak_pointer (G_OBJECT (default_mixer),
346 : : (gpointer)&default_mixer);
347 : : }
348 : :
349 : 25 : return default_mixer;
350 : : }
351 : :
352 : : /**
353 : : * valent_mixer_get_default_input: (get-property default-input)
354 : : * @mixer: a `ValentMixer`
355 : : *
356 : : * Get the default input stream for the primary [class@Valent.MixerAdapter].
357 : : *
358 : : * Returns: (transfer none) (nullable): a `ValentMixerStream`
359 : : *
360 : : * Since: 1.0
361 : : */
362 : : ValentMixerStream *
363 : 9 : valent_mixer_get_default_input (ValentMixer *mixer)
364 : : {
365 : 9 : ValentMixerStream *ret = NULL;
366 : :
367 : 9 : VALENT_ENTRY;
368 : :
369 [ + - ]: 9 : g_return_val_if_fail (VALENT_IS_MIXER (mixer), NULL);
370 : :
371 [ - + ]: 9 : if G_LIKELY (mixer->default_adapter != NULL)
372 : 9 : ret = valent_mixer_adapter_get_default_input (mixer->default_adapter);
373 : :
374 : 9 : VALENT_RETURN (ret);
375 : : }
376 : :
377 : : /**
378 : : * valent_mixer_set_default_input: (set-property default-input)
379 : : * @mixer: a `ValentMixer`
380 : : * @stream: a `ValentMixerStream`
381 : : *
382 : : * Set the default input stream for the primary [class@Valent.MixerAdapter].
383 : : *
384 : : * Since: 1.0
385 : : */
386 : : void
387 : 2 : valent_mixer_set_default_input (ValentMixer *mixer,
388 : : ValentMixerStream *stream)
389 : : {
390 : 2 : VALENT_ENTRY;
391 : :
392 [ + - ]: 2 : g_return_if_fail (VALENT_IS_MIXER (mixer));
393 [ - + ]: 2 : g_return_if_fail (VALENT_IS_MIXER_STREAM (stream));
394 : :
395 [ + - ]: 2 : if G_LIKELY (mixer->default_adapter != NULL)
396 : 2 : valent_mixer_adapter_set_default_input (mixer->default_adapter, stream);
397 : :
398 : 2 : VALENT_EXIT;
399 : : }
400 : :
401 : : /**
402 : : * valent_mixer_get_default_output: (get-property default-output)
403 : : * @mixer: a `ValentMixer`
404 : : *
405 : : * Get the default output stream for the primary [class@Valent.MixerAdapter].
406 : : *
407 : : * Returns: (transfer none) (nullable): a `ValentMixerStream`
408 : : *
409 : : * Since: 1.0
410 : : */
411 : : ValentMixerStream *
412 : 19 : valent_mixer_get_default_output (ValentMixer *mixer)
413 : : {
414 : 19 : ValentMixerStream *ret = NULL;
415 : :
416 : 19 : VALENT_ENTRY;
417 : :
418 [ + - ]: 19 : g_return_val_if_fail (VALENT_IS_MIXER (mixer), NULL);
419 : :
420 [ - + ]: 19 : if G_LIKELY (mixer->default_adapter != NULL)
421 : 19 : ret = valent_mixer_adapter_get_default_output (mixer->default_adapter);
422 : :
423 : 19 : VALENT_RETURN (ret);
424 : : }
425 : :
426 : : /**
427 : : * valent_mixer_set_default_output: (set-property default-output)
428 : : * @mixer: a `ValentMixer`
429 : : * @stream: a `ValentMixerStream`
430 : : *
431 : : * Set the default output stream for the primary [class@Valent.MixerAdapter].
432 : : *
433 : : * Since: 1.0
434 : : */
435 : : void
436 : 4 : valent_mixer_set_default_output (ValentMixer *mixer,
437 : : ValentMixerStream *stream)
438 : : {
439 : 4 : VALENT_ENTRY;
440 : :
441 [ + - ]: 4 : g_return_if_fail (VALENT_IS_MIXER (mixer));
442 [ - + ]: 4 : g_return_if_fail (VALENT_IS_MIXER_STREAM (stream));
443 : :
444 [ + - ]: 4 : if G_LIKELY (mixer->default_adapter != NULL)
445 : 4 : valent_mixer_adapter_set_default_output (mixer->default_adapter, stream);
446 : :
447 : 4 : VALENT_EXIT;
448 : : }
449 : :
|