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