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