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