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