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-component"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <libpeas.h>
10 : : #include <libtracker-sparql/tracker-sparql.h>
11 : :
12 : : #include "valent-component.h"
13 : : #include "valent-component-private.h"
14 : : #include "valent-debug.h"
15 : : #include "valent-extension.h"
16 : : #include "valent-global.h"
17 : : #include "valent-object.h"
18 : :
19 : :
20 : : /**
21 : : * ValentComponent:
22 : : *
23 : : * An abstract base class for components.
24 : : *
25 : : * `ValentComponent` is a base class for session and system components, such as
26 : : * the clipboard or volume control. Each component is typically used in a
27 : : * singleton pattern, backed by one or more extensions.
28 : : *
29 : : * Since: 1.0
30 : : */
31 : :
32 : : typedef struct
33 : : {
34 : : PeasEngine *engine;
35 : : ValentContext *context;
36 : : char *plugin_domain;
37 : : char *plugin_priority;
38 : : GType plugin_type;
39 : : GHashTable *plugins;
40 : : GObject *primary_adapter;
41 : : } ValentComponentPrivate;
42 : :
43 [ + + + - ]: 2016 : G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ValentComponent, valent_component, VALENT_TYPE_OBJECT);
44 : :
45 : :
46 : : /**
47 : : * ValentComponentClass:
48 : : * @bind_extension: the virtual function pointer for bind_extension()
49 : : * @unbind_extension: the virtual function pointer for unbind_extension()
50 : : *
51 : : * The virtual function table for `ValentComponent`.
52 : : */
53 : :
54 : : typedef enum {
55 : : PROP_PLUGIN_DOMAIN = 1,
56 : : PROP_PLUGIN_TYPE,
57 : : PROP_PRIMARY_ADAPTER,
58 : : } ValentComponentProperty;
59 : :
60 : : static GParamSpec *properties[PROP_PRIMARY_ADAPTER + 1] = { NULL, };
61 : :
62 : :
63 : : static void
64 : 53 : component_plugin_free (gpointer data)
65 : : {
66 : 53 : ValentPlugin *plugin = data;
67 : :
68 [ + + ]: 53 : if (plugin->extension != NULL)
69 : : {
70 [ + - ]: 46 : ValentComponentClass *klass = VALENT_COMPONENT_GET_CLASS (plugin->parent);
71 : :
72 [ + - ]: 46 : if (klass->unbind_extension != NULL)
73 : 46 : klass->unbind_extension (plugin->parent, plugin->extension);
74 : : }
75 : :
76 : 53 : g_clear_pointer (&plugin, valent_plugin_free);
77 : 53 : }
78 : :
79 : :
80 : : static int64_t
81 : 74 : _peas_plugin_info_get_priority (PeasPluginInfo *info,
82 : : const char *key)
83 : : {
84 : 74 : const char *priority_str = NULL;
85 : 74 : int64_t priority = 0;
86 : :
87 [ + - ]: 74 : if (info != NULL && key != NULL)
88 : 74 : priority_str = peas_plugin_info_get_external_data (info, key);
89 : :
90 [ + + ]: 74 : if (priority_str != NULL)
91 : 59 : priority = g_ascii_strtoll (priority_str, NULL, 10);
92 : :
93 : 74 : return priority;
94 : : }
95 : :
96 : : static void
97 : 80 : valent_component_update_preferred (ValentComponent *self)
98 : : {
99 : 80 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
100 : 80 : GHashTableIter iter;
101 : 80 : PeasPluginInfo *info;
102 : 80 : ValentPlugin *plugin;
103 : 80 : GObject *extension = NULL;
104 : 80 : int64_t extension_priority = 0;
105 : :
106 : 80 : VALENT_ENTRY;
107 : :
108 [ + - ]: 80 : g_assert (VALENT_IS_COMPONENT (self));
109 : :
110 : 80 : g_hash_table_iter_init (&iter, priv->plugins);
111 : :
112 [ + + ]: 180 : while (g_hash_table_iter_next (&iter, (void **)&info, (void **)&plugin))
113 : : {
114 : 100 : ValentPluginState state;
115 : 100 : int64_t priority;
116 : :
117 [ + + ]: 100 : if (plugin->extension == NULL)
118 : 13 : continue;
119 : :
120 : 87 : state = valent_extension_plugin_state_check (VALENT_EXTENSION (plugin->extension), NULL);
121 : :
122 [ + + ]: 87 : if (state != VALENT_PLUGIN_STATE_ACTIVE)
123 : 13 : continue;
124 : :
125 : 74 : priority = _peas_plugin_info_get_priority (info, priv->plugin_priority);
126 : :
127 [ + + ]: 74 : if (extension == NULL || priority < extension_priority)
128 : : {
129 : 71 : extension = plugin->extension;
130 : 71 : extension_priority = priority;
131 : : }
132 : : }
133 : :
134 [ + + ]: 80 : if (priv->primary_adapter != extension)
135 : : {
136 : 79 : VALENT_NOTE ("%s(): %s: %s", G_STRFUNC, G_OBJECT_TYPE_NAME (self),
137 : : extension ? G_OBJECT_TYPE_NAME (extension) : "No Adapter");
138 : 79 : priv->primary_adapter = extension;
139 : 79 : VALENT_COMPONENT_GET_CLASS (self)->bind_preferred (self, priv->primary_adapter);
140 : 79 : g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRIMARY_ADAPTER]);
141 : : }
142 : :
143 : 80 : VALENT_EXIT;
144 : : }
145 : :
146 : :
147 : : /*
148 : : * GSettings Handlers
149 : : */
150 : : static void
151 : 18 : on_plugin_state_changed (ValentExtension *extension,
152 : : GParamSpec *pspec,
153 : : ValentComponent *self)
154 : : {
155 : 18 : ValentPluginState state = VALENT_PLUGIN_STATE_ACTIVE;
156 : 36 : g_autoptr (GError) error = NULL;
157 : :
158 [ + - ]: 18 : g_assert (VALENT_IS_EXTENSION (extension));
159 [ - + ]: 18 : g_assert (VALENT_IS_COMPONENT (self));
160 : :
161 : 18 : state = valent_extension_plugin_state_check (extension, &error);
162 : :
163 [ - + ]: 18 : if (state == VALENT_PLUGIN_STATE_ERROR)
164 : 0 : g_warning ("%s(): %s", G_OBJECT_TYPE_NAME (extension), error->message);
165 : :
166 [ - + ]: 18 : valent_component_update_preferred (self);
167 : 18 : }
168 : :
169 : : static void
170 : 9 : g_async_initable_init_async_cb (GObject *object,
171 : : GAsyncResult *result,
172 : : gpointer user_data)
173 : : {
174 : 9 : GAsyncInitable *initable = G_ASYNC_INITABLE (object);
175 : 18 : g_autoptr (GError) error = NULL;
176 : :
177 : 9 : VALENT_ENTRY;
178 : :
179 [ - + - - ]: 9 : if (!g_async_initable_init_finish (initable, result, &error) &&
180 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
181 : : {
182 : 0 : g_warning ("%s initialization failed: %s",
183 : : G_OBJECT_TYPE_NAME (initable),
184 : : error->message);
185 : : }
186 : :
187 [ - + ]: 9 : VALENT_EXIT;
188 : : }
189 : :
190 : : static void
191 : 62 : valent_component_enable_plugin (ValentComponent *self,
192 : : ValentPlugin *plugin)
193 : : {
194 : 62 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
195 : 124 : g_autofree char *urn = NULL;
196 : 62 : const char *domain = NULL;
197 : 62 : const char *module = NULL;
198 : :
199 : 62 : VALENT_ENTRY;
200 : :
201 [ + - ]: 62 : g_assert (VALENT_IS_COMPONENT (self));
202 [ - + ]: 62 : g_assert (plugin != NULL);
203 : :
204 : 62 : domain = valent_context_get_domain (priv->context);
205 : 62 : module = peas_plugin_info_get_module_name (plugin->info);
206 : 62 : urn = tracker_sparql_escape_uri_printf ("urn:valent:%s:%s", domain, module);
207 : 62 : plugin->extension = peas_engine_create_extension (priv->engine,
208 : : plugin->info,
209 : : priv->plugin_type,
210 : : "iri", urn,
211 : : "object", self,
212 : : NULL);
213 [ - + ]: 62 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
214 : :
215 : 62 : VALENT_COMPONENT_GET_CLASS (self)->bind_extension (self, plugin->extension);
216 : 62 : valent_component_update_preferred (self);
217 : :
218 : : /* If the extension state changes, update the preferred adapter */
219 : 62 : g_signal_connect_object (plugin->extension,
220 : : "notify::plugin-state",
221 : : G_CALLBACK (on_plugin_state_changed),
222 : : self, 0);
223 : :
224 : : /* If the extension requires initialization, use a chained cancellable in case
225 : : * the plugin is unloaded or the component is destroyed. */
226 [ + - + - : 62 : if (G_IS_ASYNC_INITABLE (plugin->extension))
+ - + + ]
227 : : {
228 : 9 : GAsyncInitable *initable = G_ASYNC_INITABLE (plugin->extension);
229 : 71 : g_autoptr (GCancellable) destroy = NULL;
230 : :
231 : 9 : plugin->cancellable = g_cancellable_new ();
232 : 9 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (self),
233 : : plugin->cancellable);
234 : :
235 [ + - ]: 9 : g_async_initable_init_async (initable,
236 : : G_PRIORITY_DEFAULT,
237 : : destroy,
238 : : g_async_initable_init_async_cb,
239 : : NULL);
240 : : }
241 [ + - + - : 53 : else if (G_IS_INITABLE (plugin->extension))
+ - - + ]
242 : : {
243 : 0 : GInitable *initable = G_INITABLE (plugin->extension);
244 : 0 : g_autoptr (GCancellable) destroy = NULL;
245 [ # # ]: 0 : g_autoptr (GError) error = NULL;
246 : :
247 : 0 : plugin->cancellable = g_cancellable_new ();
248 : 0 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (self),
249 : : plugin->cancellable);
250 : :
251 [ # # # # ]: 0 : if (!g_initable_init (initable, destroy, &error) &&
252 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
253 : : {
254 : 0 : g_warning ("%s initialization failed: %s",
255 : : G_OBJECT_TYPE_NAME (initable),
256 : : error->message);
257 : : }
258 : : }
259 : :
260 : 62 : VALENT_EXIT;
261 : : }
262 : :
263 : : static void
264 : 0 : valent_component_disable_plugin (ValentComponent *self,
265 : : ValentPlugin *plugin)
266 : : {
267 : 0 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
268 : 0 : g_autoptr (GObject) extension = NULL;
269 : :
270 [ # # ]: 0 : g_assert (VALENT_IS_COMPONENT (self));
271 [ # # ]: 0 : g_assert (plugin != NULL);
272 : :
273 : : /* Ensure any in-progress initialization is cancelled */
274 : 0 : g_cancellable_cancel (plugin->cancellable);
275 [ # # ]: 0 : g_clear_object (&plugin->cancellable);
276 : :
277 : : /* Steal the object and reset the preferred adapter */
278 [ # # ]: 0 : extension = g_steal_pointer (&plugin->extension);
279 [ # # # # ]: 0 : g_return_if_fail (G_IS_OBJECT (extension));
280 : :
281 [ # # ]: 0 : if (priv->primary_adapter == extension)
282 : 0 : valent_component_update_preferred (self);
283 : :
284 [ # # ]: 0 : VALENT_COMPONENT_GET_CLASS (self)->unbind_extension (self, extension);
285 : : }
286 : :
287 : : static void
288 : 0 : on_plugin_enabled_changed (ValentPlugin *plugin)
289 : : {
290 [ # # ]: 0 : g_assert (plugin != NULL);
291 [ # # ]: 0 : g_assert (VALENT_IS_COMPONENT (plugin->parent));
292 : :
293 [ # # ]: 0 : if (valent_plugin_get_enabled (plugin))
294 : 0 : valent_component_enable_plugin (plugin->parent, plugin);
295 : : else
296 : 0 : valent_component_disable_plugin (plugin->parent, plugin);
297 : 0 : }
298 : :
299 : : /*
300 : : * PeasEngine Callbacks
301 : : */
302 : : static void
303 : 269 : on_load_plugin (PeasEngine *engine,
304 : : PeasPluginInfo *info,
305 : : ValentComponent *self)
306 : : {
307 : 269 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
308 : 269 : ValentPlugin *plugin;
309 : :
310 : 269 : VALENT_ENTRY;
311 : :
312 [ + - ]: 269 : g_assert (PEAS_IS_ENGINE (engine));
313 [ - + ]: 269 : g_assert (info != NULL);
314 [ - + ]: 269 : g_assert (VALENT_IS_COMPONENT (self));
315 : :
316 : : /* We're only interested in one GType */
317 [ + + ]: 269 : if (!peas_engine_provides_extension (engine, info, priv->plugin_type))
318 : 69 : VALENT_EXIT;
319 : :
320 : 69 : VALENT_NOTE ("%s: %s",
321 : : g_type_name (priv->plugin_type),
322 : : peas_plugin_info_get_module_name (info));
323 : :
324 : 69 : plugin = valent_plugin_new (self, priv->context, info,
325 : : G_CALLBACK (on_plugin_enabled_changed));
326 : 69 : g_hash_table_insert (priv->plugins, info, plugin);
327 : :
328 [ + + ]: 69 : if (valent_plugin_get_enabled (plugin))
329 : 62 : valent_component_enable_plugin (self, plugin);
330 : :
331 : 269 : VALENT_EXIT;
332 : : }
333 : :
334 : : static void
335 : 0 : on_unload_plugin (PeasEngine *engine,
336 : : PeasPluginInfo *info,
337 : : ValentComponent *self)
338 : : {
339 : 0 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
340 : :
341 : 0 : VALENT_ENTRY;
342 : :
343 [ # # ]: 0 : g_assert (PEAS_IS_ENGINE (engine));
344 [ # # ]: 0 : g_assert (info != NULL);
345 [ # # ]: 0 : g_assert (VALENT_IS_COMPONENT (self));
346 : :
347 : : /* We're only interested in one GType */
348 [ # # ]: 0 : if (!peas_engine_provides_extension (engine, info, priv->plugin_type))
349 : 0 : VALENT_EXIT;
350 : :
351 : 0 : VALENT_NOTE ("%s: %s",
352 : : g_type_name (priv->plugin_type),
353 : : peas_plugin_info_get_module_name (info));
354 : :
355 : 0 : g_hash_table_remove (priv->plugins, info);
356 : :
357 : 0 : VALENT_EXIT;
358 : : }
359 : :
360 : : /* LCOV_EXCL_START */
361 : : static void
362 : : valent_component_real_bind_preferred (ValentComponent *component,
363 : : GObject *extension)
364 : : {
365 : : g_assert (VALENT_IS_COMPONENT (component));
366 : : g_assert (extension == NULL || G_IS_OBJECT (extension));
367 : : }
368 : :
369 : : static void
370 : : valent_component_real_bind_extension (ValentComponent *component,
371 : : GObject *extension)
372 : : {
373 : : g_assert (VALENT_IS_COMPONENT (component));
374 : : g_assert (G_IS_OBJECT (extension));
375 : : }
376 : :
377 : : static void
378 : : valent_component_real_unbind_extension (ValentComponent *component,
379 : : GObject *extension)
380 : : {
381 : : g_assert (VALENT_IS_COMPONENT (component));
382 : : g_assert (G_IS_OBJECT (extension));
383 : : }
384 : : /* LCOV_EXCL_STOP */
385 : :
386 : : /*
387 : : * ValentObject
388 : : */
389 : : static void
390 : 44 : valent_component_destroy (ValentObject *object)
391 : : {
392 : 44 : ValentComponent *self = VALENT_COMPONENT (object);
393 : 44 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
394 : :
395 : 44 : g_signal_handlers_disconnect_by_func (priv->engine, on_load_plugin, self);
396 : 44 : g_signal_handlers_disconnect_by_func (priv->engine, on_unload_plugin, self);
397 : 44 : g_hash_table_remove_all (priv->plugins);
398 : :
399 : 44 : VALENT_OBJECT_CLASS (valent_component_parent_class)->destroy (object);
400 : 44 : }
401 : :
402 : : /*
403 : : * GObject
404 : : */
405 : : static void
406 : 58 : valent_component_constructed (GObject *object)
407 : : {
408 : 58 : ValentComponent *self = VALENT_COMPONENT (object);
409 : 58 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
410 : 58 : unsigned int n_plugins = 0;
411 : :
412 [ + - ]: 58 : g_assert (priv->plugin_domain != NULL);
413 [ - + ]: 58 : g_assert (priv->plugin_type != G_TYPE_NONE);
414 : :
415 : 58 : priv->context = valent_context_new (NULL, priv->plugin_domain, NULL);
416 : :
417 : : /* Infer the priority key */
418 [ + - ]: 58 : if (g_type_name (priv->plugin_type) != NULL)
419 : : {
420 : 58 : const char *type_name = g_type_name (priv->plugin_type);
421 : :
422 [ + - + - : 58 : if (g_str_has_prefix (type_name, "Valent"))
+ - ]
423 : 58 : priv->plugin_priority = g_strdup_printf ("X-%sPriority", &type_name[6]);
424 : : else
425 : 0 : priv->plugin_priority = g_strdup_printf ("X-%sPriority", type_name);
426 : : }
427 : :
428 : : /* Setup PeasEngine */
429 : 58 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (priv->engine));
430 : :
431 [ + + ]: 327 : for (unsigned int i = 0; i < n_plugins; i++)
432 : : {
433 : 269 : g_autoptr (PeasPluginInfo) info = NULL;
434 : :
435 : 269 : info = g_list_model_get_item (G_LIST_MODEL (priv->engine), i);
436 : :
437 [ + - ]: 269 : if (peas_plugin_info_is_loaded (info))
438 : 269 : on_load_plugin (priv->engine, info, self);
439 : : }
440 : :
441 : 58 : g_signal_connect_object (priv->engine,
442 : : "load-plugin",
443 : : G_CALLBACK (on_load_plugin),
444 : : self,
445 : : G_CONNECT_AFTER);
446 : :
447 : 58 : g_signal_connect_object (priv->engine,
448 : : "unload-plugin",
449 : : G_CALLBACK (on_unload_plugin),
450 : : self,
451 : : 0);
452 : :
453 : 58 : G_OBJECT_CLASS (valent_component_parent_class)->constructed (object);
454 : 58 : }
455 : :
456 : : static void
457 : 44 : valent_component_finalize (GObject *object)
458 : : {
459 : 44 : ValentComponent *self = VALENT_COMPONENT (object);
460 : 44 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
461 : :
462 [ + - ]: 44 : g_clear_pointer (&priv->plugin_domain, g_free);
463 [ + - ]: 44 : g_clear_pointer (&priv->plugin_priority, g_free);
464 [ + - ]: 44 : g_clear_pointer (&priv->plugins, g_hash_table_unref);
465 [ + - ]: 44 : g_clear_object (&priv->context);
466 : :
467 : 44 : G_OBJECT_CLASS (valent_component_parent_class)->finalize (object);
468 : 44 : }
469 : :
470 : : static void
471 : 4 : valent_component_get_property (GObject *object,
472 : : guint prop_id,
473 : : GValue *value,
474 : : GParamSpec *pspec)
475 : : {
476 : 4 : ValentComponent *self = VALENT_COMPONENT (object);
477 : 4 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
478 : :
479 [ - - + - ]: 4 : switch ((ValentComponentProperty)prop_id)
480 : : {
481 : 0 : case PROP_PLUGIN_DOMAIN:
482 : 0 : g_value_set_string (value, priv->plugin_domain);
483 : 0 : break;
484 : :
485 : 0 : case PROP_PLUGIN_TYPE:
486 : 0 : g_value_set_gtype (value, priv->plugin_type);
487 : 0 : break;
488 : :
489 : 4 : case PROP_PRIMARY_ADAPTER:
490 : 4 : g_value_set_object (value, priv->primary_adapter);
491 : 4 : break;
492 : :
493 : 0 : default:
494 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
495 : : }
496 : 4 : }
497 : :
498 : : static void
499 : 116 : valent_component_set_property (GObject *object,
500 : : guint prop_id,
501 : : const GValue *value,
502 : : GParamSpec *pspec)
503 : : {
504 : 116 : ValentComponent *self = VALENT_COMPONENT (object);
505 : 116 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
506 : :
507 [ + + - - ]: 116 : switch ((ValentComponentProperty)prop_id)
508 : : {
509 : 58 : case PROP_PLUGIN_DOMAIN:
510 : 58 : priv->plugin_domain = g_value_dup_string (value);
511 : 58 : break;
512 : :
513 : 58 : case PROP_PLUGIN_TYPE:
514 : 58 : priv->plugin_type = g_value_get_gtype (value);
515 : 58 : break;
516 : :
517 : 0 : case PROP_PRIMARY_ADAPTER:
518 : 0 : valent_component_set_primary_adapter (self, g_value_get_object (value));
519 : 0 : break;
520 : :
521 : 0 : default:
522 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
523 : : }
524 : 116 : }
525 : :
526 : : static void
527 : 26 : valent_component_class_init (ValentComponentClass *klass)
528 : : {
529 : 26 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
530 : 26 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
531 : :
532 : 26 : object_class->constructed = valent_component_constructed;
533 : 26 : object_class->finalize = valent_component_finalize;
534 : 26 : object_class->get_property = valent_component_get_property;
535 : 26 : object_class->set_property = valent_component_set_property;
536 : :
537 : 26 : vobject_class->destroy = valent_component_destroy;
538 : :
539 : 26 : klass->bind_extension = valent_component_real_bind_extension;
540 : 26 : klass->unbind_extension = valent_component_real_unbind_extension;
541 : 26 : klass->bind_preferred = valent_component_real_bind_preferred;
542 : :
543 : : /**
544 : : * ValentComponent:plugin-context:
545 : : *
546 : : * The domain of the component.
547 : : *
548 : : * This is a `GSettings` safe string such as "contacts" or "media", used to
549 : : * structure settings and files of components and their extensions.
550 : : *
551 : : * Since: 1.0
552 : : */
553 : 52 : properties [PROP_PLUGIN_DOMAIN] =
554 : 26 : g_param_spec_string ("plugin-domain", NULL, NULL,
555 : : NULL,
556 : : (G_PARAM_READWRITE |
557 : : G_PARAM_CONSTRUCT_ONLY |
558 : : G_PARAM_EXPLICIT_NOTIFY |
559 : : G_PARAM_STATIC_STRINGS));
560 : :
561 : : /**
562 : : * ValentComponent:plugin-type:
563 : : *
564 : : * The extension point [alias@GObject.Type].
565 : : *
566 : : * Since: 1.0
567 : : */
568 : 52 : properties [PROP_PLUGIN_TYPE] =
569 : 26 : g_param_spec_gtype ("plugin-type", NULL, NULL,
570 : : G_TYPE_NONE,
571 : : (G_PARAM_READWRITE |
572 : : G_PARAM_CONSTRUCT_ONLY |
573 : : G_PARAM_EXPLICIT_NOTIFY |
574 : : G_PARAM_STATIC_STRINGS));
575 : :
576 : : /**
577 : : * ValentComponent:primary-adapter: (getter get_primary_adapter) (setter set_primary_adapter)
578 : : *
579 : : * The [class@Valent.Extension] serving as the primary adapter for the host.
580 : : *
581 : : * The default value for extensions is `0`; the lower the value the higher the
582 : : * priority. Plugins may use this adapter as though it represents the local
583 : : * device, such as a PipeWire adapter for [class@Valent.MixerAdapter].
584 : : *
585 : : * Since: 1.0
586 : : */
587 : 52 : properties [PROP_PRIMARY_ADAPTER] =
588 : 26 : g_param_spec_object ("primary-adapter", NULL, NULL,
589 : : VALENT_TYPE_EXTENSION,
590 : : (G_PARAM_READWRITE |
591 : : G_PARAM_EXPLICIT_NOTIFY |
592 : : G_PARAM_STATIC_STRINGS));
593 : :
594 : 26 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
595 : 26 : }
596 : :
597 : : static void
598 : 58 : valent_component_init (ValentComponent *self)
599 : : {
600 : 58 : ValentComponentPrivate *priv = valent_component_get_instance_private (self);
601 : :
602 : 58 : priv->engine = valent_get_plugin_engine ();
603 : 58 : priv->plugins = g_hash_table_new_full (NULL, NULL, NULL, component_plugin_free);
604 : 58 : }
605 : :
606 : : /**
607 : : * valent_component_get_primary_adapter: (get-property primary-adapter)
608 : : * @component: a `ValentComponent`
609 : : *
610 : : * Returns: (transfer none) (nullable): the primary adapter
611 : : *
612 : : * Since: 1.0
613 : : */
614 : : ValentExtension *
615 : 27 : valent_component_get_primary_adapter (ValentComponent *component)
616 : : {
617 : 27 : ValentComponentPrivate *priv = valent_component_get_instance_private (component);
618 : :
619 [ + - ]: 27 : g_return_val_if_fail (VALENT_IS_COMPONENT (component), NULL);
620 : :
621 : 27 : return (ValentExtension *)priv->primary_adapter;
622 : : }
623 : :
624 : : /**
625 : : * valent_component_set_primary_adapter: (set-property primary-adapter)
626 : : * @component: a `ValentComponent`
627 : : * @extension: (nullable): a `ValentExtension`
628 : : *
629 : : * Set the primary adapter to @extension.
630 : : *
631 : : * If @extension is %NULL, the component will automatically select the best
632 : : * available choice.
633 : : *
634 : : * Since: 1.0
635 : : */
636 : : void
637 : 0 : valent_component_set_primary_adapter (ValentComponent *component,
638 : : ValentExtension *extension)
639 : : {
640 : 0 : ValentComponentPrivate *priv = valent_component_get_instance_private (component);
641 : :
642 [ # # ]: 0 : g_return_if_fail (VALENT_IS_COMPONENT (component));
643 [ # # # # ]: 0 : g_return_if_fail (extension == NULL || VALENT_IS_EXTENSION (extension));
644 : :
645 [ # # ]: 0 : if (g_set_object (&priv->primary_adapter, (GObject *)extension))
646 : : {
647 [ # # ]: 0 : if (priv->primary_adapter == NULL)
648 : : {
649 : 0 : valent_component_update_preferred (component);
650 : : }
651 : : else
652 : : {
653 : 0 : g_object_notify_by_pspec (G_OBJECT (component),
654 : : properties[PROP_PRIMARY_ADAPTER]);
655 : : }
656 : : }
657 : : }
658 : :
|