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 : : #include <gio/gio.h>
5 : : #include <glib.h>
6 : : #include <glib-object.h>
7 : :
8 : : #include "valent-device.h"
9 : : #include "valent-device-impl.h"
10 : :
11 : :
12 : : struct _ValentDeviceImpl
13 : : {
14 : : GDBusInterfaceSkeleton parent_instance;
15 : :
16 : : ValentDevice *device;
17 : : GHashTable *cache;
18 : : GHashTable *pending;
19 : : unsigned int flush_id;
20 : : };
21 : :
22 [ + + + - ]: 4 : G_DEFINE_FINAL_TYPE (ValentDeviceImpl, valent_device_impl, G_TYPE_DBUS_INTERFACE_SKELETON);
23 : :
24 : : enum {
25 : : PROP_0,
26 : : PROP_DEVICE,
27 : : N_PROPERTIES,
28 : : };
29 : :
30 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
31 : :
32 : :
33 : : /*
34 : : * ca.andyholmes.Valent.Device Interface
35 : : */
36 : : static const GDBusPropertyInfo * const iface_properties[] = {
37 : : &((const GDBusPropertyInfo){
38 : : -1,
39 : : "IconName",
40 : : "s",
41 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
42 : : NULL
43 : : }),
44 : : &((const GDBusPropertyInfo){
45 : : -1,
46 : : "Id",
47 : : "s",
48 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
49 : : NULL
50 : : }),
51 : : &((const GDBusPropertyInfo){
52 : : -1,
53 : : "Name",
54 : : "s",
55 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
56 : : NULL
57 : : }),
58 : : &((const GDBusPropertyInfo){
59 : : -1,
60 : : "State",
61 : : "u",
62 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
63 : : NULL
64 : : }),
65 : : NULL,
66 : : };
67 : :
68 : : static const GDBusInterfaceInfo iface_info = {
69 : : -1,
70 : : "ca.andyholmes.Valent.Device",
71 : : NULL,
72 : : NULL,
73 : : (GDBusPropertyInfo **)&iface_properties,
74 : : NULL
75 : : };
76 : :
77 : :
78 : : /*
79 : : * Helper Functions
80 : : */
81 : : static gboolean
82 : 1 : flush_idle (gpointer data)
83 : : {
84 : 1 : g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (data));
85 : :
86 : 1 : return G_SOURCE_REMOVE;
87 : : }
88 : :
89 : : static void
90 : 1 : on_property_changed (GObject *object,
91 : : GParamSpec *pspec,
92 : : ValentDeviceImpl *self)
93 : : {
94 : 1 : GVariant *value = NULL;
95 : 1 : const char *name;
96 : :
97 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_IMPL (self));
98 : :
99 : : /* Retrieve the property */
100 : 1 : name = g_param_spec_get_name (pspec);
101 : :
102 [ - + ]: 1 : if (g_str_equal (name, "state"))
103 : : {
104 : 0 : name = "State";
105 : 0 : value = g_variant_new_uint32 (valent_device_get_state (self->device));
106 : : }
107 [ - + ]: 1 : else if (g_str_equal (name, "name"))
108 : : {
109 : 0 : name = "Name";
110 : 0 : value = g_variant_new_string (valent_device_get_name (self->device));
111 : : }
112 [ + - ]: 1 : else if (g_str_equal (name, "icon-name"))
113 : : {
114 : 1 : name = "IconName";
115 : 1 : value = g_variant_new_string (valent_device_get_icon_name (self->device));
116 : : }
117 [ # # ]: 0 : else if (g_str_equal (name, "id"))
118 : : {
119 : 0 : name = "Id";
120 : 0 : value = g_variant_new_string (valent_device_get_id (self->device));
121 : : }
122 : :
123 : 2 : g_hash_table_replace (self->cache,
124 : 1 : g_strdup (name),
125 [ - + ]: 1 : g_variant_ref_sink (value));
126 : 2 : g_hash_table_replace (self->pending,
127 : 1 : g_strdup (name),
128 [ - + ]: 1 : g_variant_ref_sink (value));
129 : :
130 [ + - ]: 1 : if (self->flush_id == 0)
131 : 1 : self->flush_id = g_idle_add (flush_idle, self);
132 : 1 : }
133 : :
134 : :
135 : : /*
136 : : * GDBusInterfaceVTable
137 : : */
138 : : static void
139 : 0 : valent_device_impl_method_call (GDBusConnection *connection,
140 : : const char *sender,
141 : : const char *object_path,
142 : : const char *interface_name,
143 : : const char *method_name,
144 : : GVariant *parameters,
145 : : GDBusMethodInvocation *invocation,
146 : : void *user_data)
147 : : {
148 : 0 : g_dbus_method_invocation_return_error (invocation,
149 : : G_DBUS_ERROR,
150 : : G_DBUS_ERROR_UNKNOWN_METHOD,
151 : : "Unknown method %s on %s",
152 : : method_name,
153 : : interface_name);
154 : 0 : }
155 : :
156 : : static GVariant *
157 : 0 : valent_device_impl_property_get (GDBusConnection *connection,
158 : : const char *sender,
159 : : const char *object_path,
160 : : const char *interface_name,
161 : : const char *property_name,
162 : : GError **error,
163 : : void *user_data)
164 : : {
165 : 0 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (user_data);
166 : 0 : GVariant *value;
167 : :
168 [ # # ]: 0 : if ((value = g_hash_table_lookup (self->cache, property_name)) != NULL)
169 : 0 : return g_variant_ref (value);
170 : :
171 : 0 : g_set_error (error,
172 : : G_DBUS_ERROR,
173 : : G_DBUS_ERROR_FAILED,
174 : : "Failed to read %s property on %s",
175 : : property_name,
176 : : interface_name);
177 : :
178 : 0 : return NULL;
179 : : }
180 : :
181 : : static gboolean
182 : 0 : valent_device_impl_property_set (GDBusConnection *connection,
183 : : const char *sender,
184 : : const char *object_path,
185 : : const char *interface_name,
186 : : const char *property_name,
187 : : GVariant *value,
188 : : GError **error,
189 : : void *user_data)
190 : : {
191 : 0 : g_set_error (error,
192 : : G_DBUS_ERROR,
193 : : G_DBUS_ERROR_PROPERTY_READ_ONLY,
194 : : "Read-only property %s on %s",
195 : : property_name,
196 : : interface_name);
197 : :
198 : 0 : return FALSE;
199 : : }
200 : :
201 : : static const GDBusInterfaceVTable iface_vtable = {
202 : : valent_device_impl_method_call,
203 : : valent_device_impl_property_get,
204 : : valent_device_impl_property_set,
205 : : };
206 : :
207 : :
208 : : /*
209 : : * GDBusInterfaceSkeleton
210 : : */
211 : : static void
212 : 1 : valent_device_impl_flush (GDBusInterfaceSkeleton *skeleton)
213 : : {
214 : 1 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (skeleton);
215 : 2 : g_autolist (GDBusConnection) connections = NULL;
216 : 1 : g_autoptr (GVariant) parameters = NULL;
217 : 1 : const char *object_path;
218 : 1 : GVariantBuilder changed_properties;
219 : 1 : GVariantBuilder invalidated_properties;
220 : 1 : GHashTableIter pending_properties;
221 : 1 : gpointer key, value;
222 : :
223 : : /* Sort the pending property changes into "changed" and "invalidated" */
224 : 1 : g_hash_table_iter_init (&pending_properties, self->pending);
225 : 1 : g_variant_builder_init (&changed_properties, G_VARIANT_TYPE_VARDICT);
226 : 1 : g_variant_builder_init (&invalidated_properties, G_VARIANT_TYPE_STRING_ARRAY);
227 : :
228 [ + + ]: 2 : while (g_hash_table_iter_next (&pending_properties, &key, &value))
229 : : {
230 [ + - ]: 1 : if (value)
231 : 1 : g_variant_builder_add (&changed_properties, "{sv}", key, value);
232 : : else
233 : 0 : g_variant_builder_add (&invalidated_properties, "s", key);
234 : :
235 : 1 : g_hash_table_iter_remove (&pending_properties);
236 : : }
237 : :
238 : 1 : parameters = g_variant_new ("(s@a{sv}@as)",
239 : : iface_info.name,
240 : : g_variant_builder_end (&changed_properties),
241 : : g_variant_builder_end (&invalidated_properties));
242 : 1 : g_variant_ref_sink (parameters);
243 : :
244 : : /* Emit "PropertiesChanged" on each connection */
245 : 1 : connections = g_dbus_interface_skeleton_get_connections (skeleton);
246 : 1 : object_path = g_dbus_interface_skeleton_get_object_path (skeleton);
247 : :
248 [ + + ]: 2 : for (const GList *iter = connections; iter; iter = iter->next)
249 : : {
250 : 1 : g_autoptr (GError) error = NULL;
251 : :
252 : 1 : g_dbus_connection_emit_signal (G_DBUS_CONNECTION (iter->data),
253 : : NULL,
254 : : object_path,
255 : : "org.freedesktop.DBus.Properties",
256 : : "PropertiesChanged",
257 : : parameters,
258 : : &error);
259 : :
260 [ - + ]: 1 : if (error != NULL)
261 : 0 : g_debug ("%s(): %s", G_STRFUNC, error->message);
262 : : }
263 : :
264 [ + - + - ]: 1 : g_clear_handle_id (&self->flush_id, g_source_remove);
265 : 1 : }
266 : :
267 : : static GVariant *
268 : 2 : valent_device_impl_get_properties (GDBusInterfaceSkeleton *skeleton)
269 : : {
270 : 2 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (skeleton);
271 : 2 : GVariantBuilder builder;
272 : 2 : GHashTableIter iter;
273 : 2 : gpointer key, value;
274 : :
275 : 2 : g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
276 : 2 : g_hash_table_iter_init (&iter, self->cache);
277 : :
278 [ + + ]: 10 : while (g_hash_table_iter_next (&iter, &key, &value))
279 : 8 : g_variant_builder_add (&builder, "{sv}", key, value);
280 : :
281 : 2 : return g_variant_builder_end (&builder);
282 : : }
283 : :
284 : : static GDBusInterfaceInfo *
285 : 5 : valent_device_impl_get_info (GDBusInterfaceSkeleton *skeleton)
286 : : {
287 : 5 : return (GDBusInterfaceInfo *)&iface_info;
288 : : }
289 : :
290 : : static GDBusInterfaceVTable *
291 : 1 : valent_device_impl_get_vtable (GDBusInterfaceSkeleton *skeleton)
292 : : {
293 : 1 : return (GDBusInterfaceVTable *)&iface_vtable;
294 : : }
295 : :
296 : :
297 : : /*
298 : : * GObject
299 : : */
300 : : static void
301 : 1 : valent_device_impl_constructed (GObject *object)
302 : : {
303 : 1 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (object);
304 : 1 : GVariant *value = NULL;
305 : :
306 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE (self->device));
307 : :
308 : 1 : value = g_variant_new_string (valent_device_get_icon_name (self->device));
309 : 2 : g_hash_table_insert (self->cache,
310 : 1 : g_strdup ("IconName"),
311 : 1 : g_variant_ref_sink (value));
312 : 1 : value = g_variant_new_string (valent_device_get_id (self->device));
313 : 2 : g_hash_table_insert (self->cache,
314 : 1 : g_strdup ("Id"),
315 : 1 : g_variant_ref_sink (value));
316 : 1 : value = g_variant_new_string (valent_device_get_name (self->device));
317 : 1 : g_hash_table_insert (self->cache,
318 : : g_strdup ("Name"),
319 : 1 : g_variant_ref_sink (value));
320 : 1 : value = g_variant_new_uint32 (valent_device_get_state (self->device));
321 : 2 : g_hash_table_insert (self->cache,
322 : 1 : g_strdup ("State"),
323 : 1 : g_variant_ref_sink (value));
324 : :
325 : 1 : g_signal_connect_object (self->device,
326 : : "notify",
327 : : G_CALLBACK (on_property_changed),
328 : : self, 0);
329 : :
330 : 1 : G_OBJECT_CLASS (valent_device_impl_parent_class)->constructed (object);
331 : 1 : }
332 : :
333 : : static void
334 : 1 : valent_device_impl_dispose (GObject *object)
335 : : {
336 : 1 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (object);
337 : :
338 : 1 : g_signal_handlers_disconnect_by_data (self->device, self);
339 [ - + ]: 1 : g_clear_handle_id (&self->flush_id, g_source_remove);
340 : :
341 : 1 : G_OBJECT_CLASS (valent_device_impl_parent_class)->dispose (object);
342 : 1 : }
343 : :
344 : : static void
345 : 1 : valent_device_impl_finalize (GObject *object)
346 : : {
347 : 1 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (object);
348 : :
349 [ + - ]: 1 : g_clear_pointer (&self->cache, g_hash_table_unref);
350 [ + - ]: 1 : g_clear_pointer (&self->pending, g_hash_table_unref);
351 : :
352 : 1 : G_OBJECT_CLASS (valent_device_impl_parent_class)->finalize (object);
353 : 1 : }
354 : :
355 : : static void
356 : 0 : valent_device_impl_get_property (GObject *object,
357 : : guint prop_id,
358 : : GValue *value,
359 : : GParamSpec *pspec)
360 : : {
361 : 0 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (object);
362 : :
363 [ # # ]: 0 : switch (prop_id)
364 : : {
365 : 0 : case PROP_DEVICE:
366 : 0 : g_value_set_object (value, self->device);
367 : 0 : break;
368 : :
369 : 0 : default:
370 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371 : : }
372 : 0 : }
373 : :
374 : : static void
375 : 1 : valent_device_impl_set_property (GObject *object,
376 : : guint prop_id,
377 : : const GValue *value,
378 : : GParamSpec *pspec)
379 : : {
380 : 1 : ValentDeviceImpl *self = VALENT_DEVICE_IMPL (object);
381 : :
382 [ + - ]: 1 : switch (prop_id)
383 : : {
384 : 1 : case PROP_DEVICE:
385 : 1 : self->device = g_value_get_object (value);
386 : 1 : break;
387 : :
388 : 0 : default:
389 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
390 : : }
391 : 1 : }
392 : :
393 : : void
394 : 1 : valent_device_impl_class_init (ValentDeviceImplClass *klass)
395 : : {
396 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
397 : 1 : GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass);
398 : :
399 : 1 : object_class->constructed = valent_device_impl_constructed;
400 : 1 : object_class->dispose = valent_device_impl_dispose;
401 : 1 : object_class->finalize = valent_device_impl_finalize;
402 : 1 : object_class->get_property = valent_device_impl_get_property;
403 : 1 : object_class->set_property = valent_device_impl_set_property;
404 : :
405 : 1 : skeleton_class->get_info = valent_device_impl_get_info;
406 : 1 : skeleton_class->get_vtable = valent_device_impl_get_vtable;
407 : 1 : skeleton_class->get_properties = valent_device_impl_get_properties;
408 : 1 : skeleton_class->flush = valent_device_impl_flush;
409 : :
410 : 2 : properties[PROP_DEVICE] =
411 : 1 : g_param_spec_object ("device", NULL, NULL,
412 : : VALENT_TYPE_DEVICE,
413 : : (G_PARAM_READWRITE |
414 : : G_PARAM_CONSTRUCT_ONLY |
415 : : G_PARAM_EXPLICIT_NOTIFY |
416 : : G_PARAM_STATIC_STRINGS));
417 : :
418 : 1 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
419 : 1 : }
420 : :
421 : : static void
422 : 1 : valent_device_impl_init (ValentDeviceImpl *self)
423 : : {
424 : 1 : self->cache = g_hash_table_new_full (g_str_hash,
425 : : g_str_equal,
426 : : g_free,
427 : : (GDestroyNotify)g_variant_unref);
428 : 1 : self->pending = g_hash_table_new_full (g_str_hash,
429 : : g_str_equal,
430 : : g_free,
431 : : (GDestroyNotify)g_variant_unref);
432 : 1 : }
433 : :
434 : : /**
435 : : * valent_device_impl_new:
436 : : * @device: a `ValentDevice`
437 : : *
438 : : * Create a new `ValentDeviceImpl`.
439 : : *
440 : : * Returns: (transfer full): a `GDBusInterfaceSkeleton`
441 : : */
442 : : GDBusInterfaceSkeleton *
443 : 1 : valent_device_impl_new (ValentDevice *device)
444 : : {
445 : 1 : return g_object_new (VALENT_TYPE_DEVICE_IMPL,
446 : : "device", device,
447 : : NULL);
448 : : }
449 : :
|