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