1 /*
2  * Copyright © 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 #define G_SETTINGS_ENABLE_BACKEND
23 #include <gio/gsettingsbackend.h>
24 #include "../engine/dconf-engine.h"
25 #include <gio/gio.h>
26 
27 #include <string.h>
28 
29 typedef GSettingsBackendClass DConfSettingsBackendClass;
30 
31 typedef struct
32 {
33   GSettingsBackend backend;
34   DConfEngine     *engine;
35 } DConfSettingsBackend;
36 
37 static GType dconf_settings_backend_get_type (void);
G_DEFINE_TYPE(DConfSettingsBackend,dconf_settings_backend,G_TYPE_SETTINGS_BACKEND)38 G_DEFINE_TYPE (DConfSettingsBackend, dconf_settings_backend, G_TYPE_SETTINGS_BACKEND)
39 
40 static GVariant *
41 dconf_settings_backend_read (GSettingsBackend   *backend,
42                              const gchar        *key,
43                              const GVariantType *expected_type,
44                              gboolean            default_value)
45 {
46   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
47 
48   return dconf_engine_read (dcsb->engine,
49                             default_value ? DCONF_READ_DEFAULT_VALUE : 0,
50                             NULL, key);
51 }
52 
53 static GVariant *
dconf_settings_backend_read_user_value(GSettingsBackend * backend,const gchar * key,const GVariantType * expected_type)54 dconf_settings_backend_read_user_value (GSettingsBackend   *backend,
55                                         const gchar        *key,
56                                         const GVariantType *expected_type)
57 {
58   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
59 
60   return dconf_engine_read (dcsb->engine, DCONF_READ_USER_VALUE, NULL, key);
61 }
62 
63 static gboolean
dconf_settings_backend_write(GSettingsBackend * backend,const gchar * key,GVariant * value,gpointer origin_tag)64 dconf_settings_backend_write (GSettingsBackend *backend,
65                               const gchar      *key,
66                               GVariant         *value,
67                               gpointer          origin_tag)
68 {
69   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
70   DConfChangeset *change;
71   gboolean success;
72 
73   change = dconf_changeset_new ();
74   dconf_changeset_set (change, key, value);
75 
76   success = dconf_engine_change_fast (dcsb->engine, change, origin_tag, NULL);
77   dconf_changeset_unref (change);
78 
79   return success;
80 }
81 
82 static gboolean
dconf_settings_backend_add_to_changeset(gpointer key,gpointer value,gpointer data)83 dconf_settings_backend_add_to_changeset (gpointer key,
84                                          gpointer value,
85                                          gpointer data)
86 {
87   dconf_changeset_set (data, key, value);
88 
89   return FALSE;
90 }
91 
92 static gboolean
dconf_settings_backend_write_tree(GSettingsBackend * backend,GTree * tree,gpointer origin_tag)93 dconf_settings_backend_write_tree (GSettingsBackend *backend,
94                                    GTree            *tree,
95                                    gpointer          origin_tag)
96 {
97   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
98   DConfChangeset *change;
99   gboolean success;
100 
101   if (g_tree_nnodes (tree) == 0)
102     return TRUE;
103 
104   change = dconf_changeset_new ();
105   g_tree_foreach (tree, dconf_settings_backend_add_to_changeset, change);
106   success = dconf_engine_change_fast (dcsb->engine, change, origin_tag, NULL);
107   dconf_changeset_unref (change);
108 
109   return success;
110 }
111 
112 static void
dconf_settings_backend_reset(GSettingsBackend * backend,const gchar * key,gpointer origin_tag)113 dconf_settings_backend_reset (GSettingsBackend *backend,
114                               const gchar      *key,
115                               gpointer          origin_tag)
116 {
117   dconf_settings_backend_write (backend, key, NULL, origin_tag);
118 }
119 
120 static gboolean
dconf_settings_backend_get_writable(GSettingsBackend * backend,const gchar * name)121 dconf_settings_backend_get_writable (GSettingsBackend *backend,
122                                      const gchar      *name)
123 {
124   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
125 
126   return dconf_engine_is_writable (dcsb->engine, name);
127 }
128 
129 static void
dconf_settings_backend_subscribe(GSettingsBackend * backend,const gchar * name)130 dconf_settings_backend_subscribe (GSettingsBackend *backend,
131                                   const gchar      *name)
132 {
133   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
134 
135   dconf_engine_watch_fast (dcsb->engine, name);
136 }
137 
138 static void
dconf_settings_backend_unsubscribe(GSettingsBackend * backend,const gchar * name)139 dconf_settings_backend_unsubscribe (GSettingsBackend *backend,
140                                     const gchar      *name)
141 {
142   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
143 
144   dconf_engine_unwatch_fast (dcsb->engine, name);
145 }
146 
147 static void
dconf_settings_backend_sync(GSettingsBackend * backend)148 dconf_settings_backend_sync (GSettingsBackend *backend)
149 {
150   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
151 
152   dconf_engine_sync (dcsb->engine);
153 }
154 
155 static void
dconf_settings_backend_free_weak_ref(gpointer data)156 dconf_settings_backend_free_weak_ref (gpointer data)
157 {
158   GWeakRef *weak_ref = data;
159 
160   g_weak_ref_clear (weak_ref);
161   g_slice_free (GWeakRef, weak_ref);
162 }
163 
164 static void
dconf_settings_backend_init(DConfSettingsBackend * dcsb)165 dconf_settings_backend_init (DConfSettingsBackend *dcsb)
166 {
167   GWeakRef *weak_ref;
168 
169   weak_ref = g_slice_new (GWeakRef);
170   g_weak_ref_init (weak_ref, dcsb);
171   dcsb->engine = dconf_engine_new (NULL, weak_ref, dconf_settings_backend_free_weak_ref);
172 }
173 
174 static void
dconf_settings_backend_finalize(GObject * object)175 dconf_settings_backend_finalize (GObject *object)
176 {
177   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) object;
178 
179   dconf_engine_unref (dcsb->engine);
180 
181   G_OBJECT_CLASS (dconf_settings_backend_parent_class)
182     ->finalize (object);
183 }
184 
185 static void
dconf_settings_backend_class_init(GSettingsBackendClass * class)186 dconf_settings_backend_class_init (GSettingsBackendClass *class)
187 {
188   GObjectClass *object_class = G_OBJECT_CLASS (class);
189 
190   object_class->finalize = dconf_settings_backend_finalize;
191 
192   class->read = dconf_settings_backend_read;
193   class->read_user_value = dconf_settings_backend_read_user_value;
194   class->write = dconf_settings_backend_write;
195   class->write_tree = dconf_settings_backend_write_tree;
196   class->reset = dconf_settings_backend_reset;
197   class->get_writable = dconf_settings_backend_get_writable;
198   class->subscribe = dconf_settings_backend_subscribe;
199   class->unsubscribe = dconf_settings_backend_unsubscribe;
200   class->sync = dconf_settings_backend_sync;
201 }
202 
203 void
g_io_module_load(GIOModule * module)204 g_io_module_load (GIOModule *module)
205 {
206   g_type_module_use (G_TYPE_MODULE (module));
207   g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
208                                   dconf_settings_backend_get_type (),
209                                   "dconf", 100);
210 }
211 
212 void
g_io_module_unload(GIOModule * module)213 g_io_module_unload (GIOModule *module)
214 {
215   g_assert_not_reached ();
216 }
217 
218 gchar **
g_io_module_query(void)219 g_io_module_query (void)
220 {
221   return g_strsplit (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, "!", 0);
222 }
223 
224 void
dconf_engine_change_notify(DConfEngine * engine,const gchar * prefix,const gchar * const * changes,const gchar * tag,gboolean is_writability,gpointer origin_tag,gpointer user_data)225 dconf_engine_change_notify (DConfEngine         *engine,
226                             const gchar         *prefix,
227                             const gchar * const *changes,
228                             const gchar         *tag,
229                             gboolean             is_writability,
230                             gpointer             origin_tag,
231                             gpointer             user_data)
232 {
233   GWeakRef *weak_ref = user_data;
234   DConfSettingsBackend *dcsb;
235   g_debug ("change_notify: %s", prefix);
236 
237   dcsb = g_weak_ref_get (weak_ref);
238 
239   if (dcsb == NULL)
240     return;
241 
242   if (changes[0] == NULL)
243     return;
244 
245   if (is_writability)
246     {
247       /* We know that the engine does it this way... */
248       g_assert (changes[0][0] == '\0' && changes[1] == NULL);
249 
250       if (g_str_has_suffix (prefix, "/"))
251         g_settings_backend_path_writable_changed (G_SETTINGS_BACKEND (dcsb), prefix);
252       else
253         g_settings_backend_writable_changed (G_SETTINGS_BACKEND (dcsb), prefix);
254     }
255 
256   /* We send the normal change notification even in the event that this
257    * was a writability notification because adding/removing a lock could
258    * impact the value that gets read.
259    */
260   if (changes[1] == NULL)
261     {
262       if (g_str_has_suffix (prefix, "/"))
263         g_settings_backend_path_changed (G_SETTINGS_BACKEND (dcsb), prefix, origin_tag);
264       else
265         g_settings_backend_changed (G_SETTINGS_BACKEND (dcsb), prefix, origin_tag);
266     }
267   else
268     g_settings_backend_keys_changed (G_SETTINGS_BACKEND (dcsb), prefix, changes, origin_tag);
269 }
270