nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright © 2009, 2010 Codethink Limited |
||
3 | * |
||
4 | * This library is free software; you can redistribute it and/or |
||
5 | * modify it under the terms of the GNU Lesser General Public |
||
6 | * License as published by the Free Software Foundation; either |
||
7 | * version 2 of the licence, or (at your option) any later version. |
||
8 | * |
||
9 | * This library is distributed in the hope that it will be useful, |
||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
12 | * Lesser General Public License for more details. |
||
13 | * |
||
14 | * You should have received a copy of the GNU Lesser General Public |
||
15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
16 | * |
||
17 | * Author: Ryan Lortie <desrt@desrt.ca> |
||
18 | */ |
||
19 | |||
20 | #include "config.h" |
||
21 | |||
22 | #include "gdelayedsettingsbackend.h" |
||
23 | #include "gsettingsbackendinternal.h" |
||
24 | |||
25 | #include <string.h> |
||
26 | |||
27 | |||
28 | struct _GDelayedSettingsBackendPrivate |
||
29 | { |
||
30 | GSettingsBackend *backend; |
||
31 | GMutex lock; |
||
32 | GTree *delayed; |
||
33 | |||
34 | GMainContext *owner_context; |
||
35 | gpointer owner; |
||
36 | }; |
||
37 | |||
38 | G_DEFINE_TYPE_WITH_PRIVATE (GDelayedSettingsBackend, |
||
39 | g_delayed_settings_backend, |
||
40 | G_TYPE_SETTINGS_BACKEND) |
||
41 | |||
42 | static gboolean |
||
43 | invoke_notify_unapplied (gpointer data) |
||
44 | { |
||
45 | g_object_notify (data, "has-unapplied"); |
||
46 | g_object_unref (data); |
||
47 | |||
48 | return FALSE; |
||
49 | } |
||
50 | |||
51 | static void |
||
52 | g_delayed_settings_backend_notify_unapplied (GDelayedSettingsBackend *delayed) |
||
53 | { |
||
54 | GMainContext *target_context; |
||
55 | GObject *target; |
||
56 | |||
57 | g_mutex_lock (&delayed->priv->lock); |
||
58 | if (delayed->priv->owner) |
||
59 | { |
||
60 | target_context = delayed->priv->owner_context; |
||
61 | target = g_object_ref (delayed->priv->owner); |
||
62 | } |
||
63 | else |
||
64 | { |
||
65 | target_context = NULL; |
||
66 | target = NULL; |
||
67 | } |
||
68 | g_mutex_unlock (&delayed->priv->lock); |
||
69 | |||
70 | if (target != NULL) |
||
71 | g_main_context_invoke (target_context, invoke_notify_unapplied, target); |
||
72 | } |
||
73 | |||
74 | |||
75 | static GVariant * |
||
76 | g_delayed_settings_backend_read (GSettingsBackend *backend, |
||
77 | const gchar *key, |
||
78 | const GVariantType *expected_type, |
||
79 | gboolean default_value) |
||
80 | { |
||
81 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
82 | gpointer result = NULL; |
||
83 | |||
84 | if (!default_value) |
||
85 | { |
||
86 | g_mutex_lock (&delayed->priv->lock); |
||
87 | if (g_tree_lookup_extended (delayed->priv->delayed, key, NULL, &result)) |
||
88 | { |
||
89 | /* NULL in the tree means we should consult the default value */ |
||
90 | if (result != NULL) |
||
91 | g_variant_ref (result); |
||
92 | else |
||
93 | default_value = TRUE; |
||
94 | } |
||
95 | g_mutex_unlock (&delayed->priv->lock); |
||
96 | } |
||
97 | |||
98 | if (result == NULL) |
||
99 | result = g_settings_backend_read (delayed->priv->backend, key, |
||
100 | expected_type, default_value); |
||
101 | |||
102 | return result; |
||
103 | } |
||
104 | |||
105 | static GVariant * |
||
106 | g_delayed_settings_backend_read_user_value (GSettingsBackend *backend, |
||
107 | const gchar *key, |
||
108 | const GVariantType *expected_type) |
||
109 | { |
||
110 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
111 | gboolean value_found = FALSE; |
||
112 | gpointer result = NULL; |
||
113 | |||
114 | /* If we find an explicit NULL in our changeset then we want to return |
||
115 | * NULL (because the user value has been reset). |
||
116 | * |
||
117 | * Otherwise, chain up. |
||
118 | */ |
||
119 | g_mutex_lock (&delayed->priv->lock); |
||
120 | value_found = g_tree_lookup_extended (delayed->priv->delayed, key, NULL, &result); |
||
121 | if (result) |
||
122 | g_variant_ref (result); |
||
123 | g_mutex_unlock (&delayed->priv->lock); |
||
124 | |||
125 | if (value_found) |
||
126 | return result; |
||
127 | |||
128 | return g_settings_backend_read_user_value (delayed->priv->backend, key, expected_type); |
||
129 | } |
||
130 | |||
131 | static gboolean |
||
132 | g_delayed_settings_backend_write (GSettingsBackend *backend, |
||
133 | const gchar *key, |
||
134 | GVariant *value, |
||
135 | gpointer origin_tag) |
||
136 | { |
||
137 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
138 | gboolean was_empty; |
||
139 | |||
140 | g_mutex_lock (&delayed->priv->lock); |
||
141 | was_empty = g_tree_nnodes (delayed->priv->delayed) == 0; |
||
142 | g_tree_insert (delayed->priv->delayed, g_strdup (key), |
||
143 | g_variant_ref_sink (value)); |
||
144 | g_mutex_unlock (&delayed->priv->lock); |
||
145 | |||
146 | g_settings_backend_changed (backend, key, origin_tag); |
||
147 | |||
148 | if (was_empty) |
||
149 | g_delayed_settings_backend_notify_unapplied (delayed); |
||
150 | |||
151 | return TRUE; |
||
152 | } |
||
153 | |||
154 | static gboolean |
||
155 | add_to_tree (gpointer key, |
||
156 | gpointer value, |
||
157 | gpointer user_data) |
||
158 | { |
||
159 | g_tree_insert (user_data, g_strdup (key), g_variant_ref (value)); |
||
160 | return FALSE; |
||
161 | } |
||
162 | |||
163 | static gboolean |
||
164 | g_delayed_settings_backend_write_tree (GSettingsBackend *backend, |
||
165 | GTree *tree, |
||
166 | gpointer origin_tag) |
||
167 | { |
||
168 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
169 | gboolean was_empty; |
||
170 | |||
171 | g_mutex_lock (&delayed->priv->lock); |
||
172 | was_empty = g_tree_nnodes (delayed->priv->delayed) == 0; |
||
173 | |||
174 | g_tree_foreach (tree, add_to_tree, delayed->priv->delayed); |
||
175 | g_mutex_unlock (&delayed->priv->lock); |
||
176 | |||
177 | g_settings_backend_changed_tree (backend, tree, origin_tag); |
||
178 | |||
179 | if (was_empty) |
||
180 | g_delayed_settings_backend_notify_unapplied (delayed); |
||
181 | |||
182 | return TRUE; |
||
183 | } |
||
184 | |||
185 | static gboolean |
||
186 | g_delayed_settings_backend_get_writable (GSettingsBackend *backend, |
||
187 | const gchar *name) |
||
188 | { |
||
189 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
190 | |||
191 | return g_settings_backend_get_writable (delayed->priv->backend, name); |
||
192 | } |
||
193 | |||
194 | static void |
||
195 | g_delayed_settings_backend_reset (GSettingsBackend *backend, |
||
196 | const gchar *key, |
||
197 | gpointer origin_tag) |
||
198 | { |
||
199 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
200 | gboolean was_empty; |
||
201 | |||
202 | g_mutex_lock (&delayed->priv->lock); |
||
203 | was_empty = g_tree_nnodes (delayed->priv->delayed) == 0; |
||
204 | g_tree_insert (delayed->priv->delayed, g_strdup (key), NULL); |
||
205 | g_mutex_unlock (&delayed->priv->lock); |
||
206 | |||
207 | if (was_empty) |
||
208 | g_delayed_settings_backend_notify_unapplied (delayed); |
||
209 | } |
||
210 | |||
211 | static void |
||
212 | g_delayed_settings_backend_subscribe (GSettingsBackend *backend, |
||
213 | const char *name) |
||
214 | { |
||
215 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
216 | |||
217 | g_settings_backend_subscribe (delayed->priv->backend, name); |
||
218 | } |
||
219 | |||
220 | static void |
||
221 | g_delayed_settings_backend_unsubscribe (GSettingsBackend *backend, |
||
222 | const char *name) |
||
223 | { |
||
224 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
225 | |||
226 | g_settings_backend_unsubscribe (delayed->priv->backend, name); |
||
227 | } |
||
228 | |||
229 | static GPermission * |
||
230 | g_delayed_settings_backend_get_permission (GSettingsBackend *backend, |
||
231 | const gchar *path) |
||
232 | { |
||
233 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend); |
||
234 | |||
235 | return g_settings_backend_get_permission (delayed->priv->backend, path); |
||
236 | } |
||
237 | |||
238 | |||
239 | /* method calls */ |
||
240 | gboolean |
||
241 | g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend *delayed) |
||
242 | { |
||
243 | /* we don't need to lock for this... */ |
||
244 | |||
245 | return g_tree_nnodes (delayed->priv->delayed) > 0; |
||
246 | } |
||
247 | |||
248 | void |
||
249 | g_delayed_settings_backend_apply (GDelayedSettingsBackend *delayed) |
||
250 | { |
||
251 | if (g_tree_nnodes (delayed->priv->delayed) > 0) |
||
252 | { |
||
253 | gboolean success; |
||
254 | GTree *tmp; |
||
255 | |||
256 | g_mutex_lock (&delayed->priv->lock); |
||
257 | tmp = delayed->priv->delayed; |
||
258 | delayed->priv->delayed = g_settings_backend_create_tree (); |
||
259 | success = g_settings_backend_write_tree (delayed->priv->backend, |
||
260 | tmp, delayed->priv); |
||
261 | g_mutex_unlock (&delayed->priv->lock); |
||
262 | |||
263 | if (!success) |
||
264 | g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed), |
||
265 | tmp, NULL); |
||
266 | |||
267 | g_tree_unref (tmp); |
||
268 | |||
269 | g_delayed_settings_backend_notify_unapplied (delayed); |
||
270 | } |
||
271 | } |
||
272 | |||
273 | void |
||
274 | g_delayed_settings_backend_revert (GDelayedSettingsBackend *delayed) |
||
275 | { |
||
276 | if (g_tree_nnodes (delayed->priv->delayed) > 0) |
||
277 | { |
||
278 | GTree *tmp; |
||
279 | |||
280 | g_mutex_lock (&delayed->priv->lock); |
||
281 | tmp = delayed->priv->delayed; |
||
282 | delayed->priv->delayed = g_settings_backend_create_tree (); |
||
283 | g_mutex_unlock (&delayed->priv->lock); |
||
284 | g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed), tmp, NULL); |
||
285 | g_tree_unref (tmp); |
||
286 | |||
287 | g_delayed_settings_backend_notify_unapplied (delayed); |
||
288 | } |
||
289 | } |
||
290 | |||
291 | /* change notification */ |
||
292 | static void |
||
293 | delayed_backend_changed (GObject *target, |
||
294 | GSettingsBackend *backend, |
||
295 | const gchar *key, |
||
296 | gpointer origin_tag) |
||
297 | { |
||
298 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); |
||
299 | |||
300 | if (origin_tag != delayed->priv) |
||
301 | g_settings_backend_changed (G_SETTINGS_BACKEND (delayed), |
||
302 | key, origin_tag); |
||
303 | } |
||
304 | |||
305 | static void |
||
306 | delayed_backend_keys_changed (GObject *target, |
||
307 | GSettingsBackend *backend, |
||
308 | const gchar *path, |
||
309 | gpointer origin_tag, |
||
310 | const gchar * const *items) |
||
311 | { |
||
312 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); |
||
313 | |||
314 | if (origin_tag != delayed->priv) |
||
315 | g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed), |
||
316 | path, items, origin_tag); |
||
317 | } |
||
318 | |||
319 | static void |
||
320 | delayed_backend_path_changed (GObject *target, |
||
321 | GSettingsBackend *backend, |
||
322 | const gchar *path, |
||
323 | gpointer origin_tag) |
||
324 | { |
||
325 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); |
||
326 | |||
327 | if (origin_tag != delayed->priv) |
||
328 | g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed), |
||
329 | path, origin_tag); |
||
330 | } |
||
331 | |||
332 | static void |
||
333 | delayed_backend_writable_changed (GObject *target, |
||
334 | GSettingsBackend *backend, |
||
335 | const gchar *key) |
||
336 | { |
||
337 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); |
||
338 | gboolean last_one = FALSE; |
||
339 | |||
340 | g_mutex_lock (&delayed->priv->lock); |
||
341 | |||
342 | if (g_tree_lookup (delayed->priv->delayed, key) != NULL && |
||
343 | !g_settings_backend_get_writable (delayed->priv->backend, key)) |
||
344 | { |
||
345 | /* drop the key from our changeset if it just became read-only. |
||
346 | * no need to signal since the writable change below implies it. |
||
347 | * |
||
348 | * note that the item in the tree may very well be set to NULL in |
||
349 | * the case that the user stored a reset. we intentionally don't |
||
350 | * drop the key in this case since a reset will always succeed |
||
351 | * (even against a non-writable key). |
||
352 | */ |
||
353 | g_tree_remove (delayed->priv->delayed, key); |
||
354 | |||
355 | /* if that was the only key... */ |
||
356 | last_one = g_tree_nnodes (delayed->priv->delayed) == 0; |
||
357 | } |
||
358 | |||
359 | g_mutex_unlock (&delayed->priv->lock); |
||
360 | |||
361 | if (last_one) |
||
362 | g_delayed_settings_backend_notify_unapplied (delayed); |
||
363 | |||
364 | g_settings_backend_writable_changed (G_SETTINGS_BACKEND (delayed), key); |
||
365 | } |
||
366 | |||
367 | /* slow method until we get foreach-with-remove in GTree |
||
368 | */ |
||
369 | typedef struct |
||
370 | { |
||
371 | const gchar *path; |
||
372 | const gchar **keys; |
||
373 | gsize index; |
||
374 | } CheckPrefixState; |
||
375 | |||
376 | static gboolean |
||
377 | check_prefix (gpointer key, |
||
378 | gpointer value, |
||
379 | gpointer data) |
||
380 | { |
||
381 | CheckPrefixState *state = data; |
||
382 | |||
383 | if (g_str_has_prefix (key, state->path)) |
||
384 | state->keys[state->index++] = key; |
||
385 | |||
386 | return FALSE; |
||
387 | } |
||
388 | |||
389 | static void |
||
390 | delayed_backend_path_writable_changed (GObject *target, |
||
391 | GSettingsBackend *backend, |
||
392 | const gchar *path) |
||
393 | { |
||
394 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); |
||
395 | gboolean last_one = FALSE; |
||
396 | gsize n_keys; |
||
397 | |||
398 | g_mutex_lock (&delayed->priv->lock); |
||
399 | |||
400 | n_keys = g_tree_nnodes (delayed->priv->delayed); |
||
401 | |||
402 | if (n_keys > 0) |
||
403 | { |
||
404 | CheckPrefixState state = { path, g_new (const gchar *, n_keys) }; |
||
405 | gsize i; |
||
406 | |||
407 | /* collect a list of possibly-affected keys (ie: matching the path) */ |
||
408 | g_tree_foreach (delayed->priv->delayed, check_prefix, &state); |
||
409 | |||
410 | /* drop the keys that have been affected. |
||
411 | * |
||
412 | * don't drop 'reset' keys (see above) */ |
||
413 | for (i = 0; i < state.index; i++) |
||
414 | if (g_tree_lookup (delayed->priv->delayed, state.keys[i]) != NULL && |
||
415 | !g_settings_backend_get_writable (delayed->priv->backend, |
||
416 | state.keys[i])) |
||
417 | g_tree_remove (delayed->priv->delayed, state.keys[i]); |
||
418 | |||
419 | g_free (state.keys); |
||
420 | |||
421 | last_one = g_tree_nnodes (delayed->priv->delayed) == 0; |
||
422 | } |
||
423 | |||
424 | g_mutex_unlock (&delayed->priv->lock); |
||
425 | |||
426 | if (last_one) |
||
427 | g_delayed_settings_backend_notify_unapplied (delayed); |
||
428 | |||
429 | g_settings_backend_path_writable_changed (G_SETTINGS_BACKEND (delayed), |
||
430 | path); |
||
431 | } |
||
432 | |||
433 | static void |
||
434 | g_delayed_settings_backend_finalize (GObject *object) |
||
435 | { |
||
436 | GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object); |
||
437 | |||
438 | g_mutex_clear (&delayed->priv->lock); |
||
439 | g_object_unref (delayed->priv->backend); |
||
440 | g_tree_unref (delayed->priv->delayed); |
||
441 | |||
442 | /* if our owner is still alive, why are we finalizing? */ |
||
443 | g_assert (delayed->priv->owner == NULL); |
||
444 | |||
445 | G_OBJECT_CLASS (g_delayed_settings_backend_parent_class) |
||
446 | ->finalize (object); |
||
447 | } |
||
448 | |||
449 | static void |
||
450 | g_delayed_settings_backend_class_init (GDelayedSettingsBackendClass *class) |
||
451 | { |
||
452 | GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class); |
||
453 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
||
454 | |||
455 | backend_class->read = g_delayed_settings_backend_read; |
||
456 | backend_class->read_user_value = g_delayed_settings_backend_read_user_value; |
||
457 | backend_class->write = g_delayed_settings_backend_write; |
||
458 | backend_class->write_tree = g_delayed_settings_backend_write_tree; |
||
459 | backend_class->reset = g_delayed_settings_backend_reset; |
||
460 | backend_class->get_writable = g_delayed_settings_backend_get_writable; |
||
461 | backend_class->subscribe = g_delayed_settings_backend_subscribe; |
||
462 | backend_class->unsubscribe = g_delayed_settings_backend_unsubscribe; |
||
463 | backend_class->get_permission = g_delayed_settings_backend_get_permission; |
||
464 | |||
465 | object_class->finalize = g_delayed_settings_backend_finalize; |
||
466 | } |
||
467 | |||
468 | static void |
||
469 | g_delayed_settings_backend_init (GDelayedSettingsBackend *delayed) |
||
470 | { |
||
471 | delayed->priv = g_delayed_settings_backend_get_instance_private (delayed); |
||
472 | delayed->priv->delayed = g_settings_backend_create_tree (); |
||
473 | g_mutex_init (&delayed->priv->lock); |
||
474 | } |
||
475 | |||
476 | static void |
||
477 | g_delayed_settings_backend_disown (gpointer data, |
||
478 | GObject *where_the_object_was) |
||
479 | { |
||
480 | GDelayedSettingsBackend *delayed = data; |
||
481 | |||
482 | g_mutex_lock (&delayed->priv->lock); |
||
483 | delayed->priv->owner_context = NULL; |
||
484 | delayed->priv->owner = NULL; |
||
485 | g_mutex_unlock (&delayed->priv->lock); |
||
486 | } |
||
487 | |||
488 | GDelayedSettingsBackend * |
||
489 | g_delayed_settings_backend_new (GSettingsBackend *backend, |
||
490 | gpointer owner, |
||
491 | GMainContext *owner_context) |
||
492 | { |
||
493 | static GSettingsListenerVTable vtable = { |
||
494 | delayed_backend_changed, |
||
495 | delayed_backend_path_changed, |
||
496 | delayed_backend_keys_changed, |
||
497 | delayed_backend_writable_changed, |
||
498 | delayed_backend_path_writable_changed |
||
499 | }; |
||
500 | GDelayedSettingsBackend *delayed; |
||
501 | |||
502 | delayed = g_object_new (G_TYPE_DELAYED_SETTINGS_BACKEND, NULL); |
||
503 | delayed->priv->backend = g_object_ref (backend); |
||
504 | delayed->priv->owner_context = owner_context; |
||
505 | delayed->priv->owner = owner; |
||
506 | |||
507 | g_object_weak_ref (owner, g_delayed_settings_backend_disown, delayed); |
||
508 | |||
509 | g_settings_backend_watch (delayed->priv->backend, |
||
510 | &vtable, G_OBJECT (delayed), NULL); |
||
511 | |||
512 | return delayed; |
||
513 | } |