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