* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
* Matthias Clasen <mclasen@redhat.com>
#include <glibintl.h>
-G_DEFINE_ABSTRACT_TYPE (GSettingsBackend, g_settings_backend, G_TYPE_OBJECT)
-
typedef struct _GSettingsBackendClosure GSettingsBackendClosure;
typedef struct _GSettingsBackendWatch GSettingsBackendWatch;
struct _GSettingsBackendPrivate
{
GSettingsBackendWatch *watches;
- GStaticMutex lock;
+ GMutex lock;
};
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GSettingsBackend, g_settings_backend, G_TYPE_OBJECT)
+
/* For g_settings_backend_sync_default(), we only want to actually do
* the sync if the backend already exists. This avoids us creating an
* entire GSettingsBackend in order to call a do-nothing sync()
* g_settings_backend_create_tree() is a convenience function to create
* suitable trees.
*
- * <note><para>
- * The #GSettingsBackend API is exported to allow third-party
+ * The GSettingsBackend API is exported to allow third-party
* implementations, but does not carry the same stability guarantees
* as the public GIO API. For this reason, you have to define the
- * C preprocessor symbol #G_SETTINGS_ENABLE_BACKEND before including
- * <filename>gio/gsettingsbackend.h</filename>
- * </para></note>
+ * C preprocessor symbol %G_SETTINGS_ENABLE_BACKEND before including
+ * `gio/gsettingsbackend.h`.
**/
static gboolean
struct _GSettingsBackendClosure
{
- void (*function) (GObject *target,
- GSettingsBackend *backend,
- const gchar *name,
- gpointer data1,
- gpointer data2);
-
- GSettingsBackend *backend;
- GObject *target;
- gchar *name;
- gpointer data1;
- GBoxedFreeFunc data1_free;
- gpointer data2;
+ void (*function) (GObject *target,
+ GSettingsBackend *backend,
+ const gchar *name,
+ gpointer origin_tag,
+ gchar **names);
+
+ GMainContext *context;
+ GObject *target;
+ GSettingsBackend *backend;
+ gchar *name;
+ gpointer origin_tag;
+ gchar **names;
};
static void
GSettingsBackendWatch **ptr;
/* search and remove */
- g_static_mutex_lock (&backend->priv->lock);
+ g_mutex_lock (&backend->priv->lock);
for (ptr = &backend->priv->watches; *ptr; ptr = &(*ptr)->next)
if ((*ptr)->target == where_the_object_was)
{
*ptr = tmp->next;
g_slice_free (GSettingsBackendWatch, tmp);
- g_static_mutex_unlock (&backend->priv->lock);
+ g_mutex_unlock (&backend->priv->lock);
return;
}
* g_settings_backend_watch:
* @backend: a #GSettingsBackend
* @target: the GObject (typically GSettings instance) to call back to
- * @context: a #GMainContext, or %NULL
+ * @context: (allow-none): a #GMainContext, or %NULL
* ...: callbacks...
*
* Registers a new watch on a #GSettingsBackend.
* that appears as an argument to some of the callbacks, you *must* have
* @context as %NULL. Otherwise, you are subject to cross-thread
* dispatching and whatever owned @origin_tag at the time that the event
- * occured may no longer own it. This is a problem if you consider that
+ * occurred may no longer own it. This is a problem if you consider that
* you may now be the new owner of that address and mistakenly think
* that the event in question originated from yourself.
*
g_object_weak_ref (target, g_settings_backend_watch_weak_notify, backend);
/* linked list prepend */
- g_static_mutex_lock (&backend->priv->lock);
+ g_mutex_lock (&backend->priv->lock);
watch->next = backend->priv->watches;
backend->priv->watches = watch;
- g_static_mutex_unlock (&backend->priv->lock);
+ g_mutex_unlock (&backend->priv->lock);
}
void
GSettingsBackendClosure *closure = user_data;
closure->function (closure->target, closure->backend, closure->name,
- closure->data1, closure->data2);
+ closure->origin_tag, closure->names);
- closure->data1_free (closure->data1);
g_object_unref (closure->backend);
g_object_unref (closure->target);
+ g_strfreev (closure->names);
g_free (closure->name);
g_slice_free (GSettingsBackendClosure, closure);
return FALSE;
}
-static gpointer
-pointer_id (gpointer a)
-{
- return a;
-}
-
-static void
-pointer_ignore (gpointer a)
-{
-}
-
static void
-g_settings_backend_dispatch_signal (GSettingsBackend *backend,
- gsize function_offset,
- const gchar *name,
- gpointer data1,
- GBoxedCopyFunc data1_copy,
- GBoxedFreeFunc data1_free,
- gpointer data2)
+g_settings_backend_dispatch_signal (GSettingsBackend *backend,
+ gsize function_offset,
+ const gchar *name,
+ gpointer origin_tag,
+ const gchar * const *names)
{
- GSettingsBackendWatch *suffix, *watch, *next;
-
- if (data1_copy == NULL)
- data1_copy = pointer_id;
-
- if (data1_free == NULL)
- data1_free = pointer_ignore;
+ GSettingsBackendWatch *watch;
+ GSList *closures = NULL;
/* We're in a little bit of a tricky situation here. We need to hold
* a lock while traversing the list, but we don't want to hold the
* lock while calling back into user code.
*
- * Since we're not holding the lock while we call user code, we can't
- * render the list immutable. We can, however, store a pointer to a
- * given suffix of the list and render that suffix immutable.
- *
- * Adds will never modify the suffix since adds always come in the
- * form of prepends. We can also prevent removes from modifying the
- * suffix since removes only happen in response to the last reference
- * count dropping -- so just add a reference to everything in the
- * suffix.
+ * We work around this by creating a bunch of GSettingsBackendClosure
+ * objects while holding the lock and dispatching them after. We
+ * never touch the list without holding the lock.
*/
- g_static_mutex_lock (&backend->priv->lock);
- suffix = backend->priv->watches;
- for (watch = suffix; watch; watch = watch->next)
- g_object_ref (watch->target);
- g_static_mutex_unlock (&backend->priv->lock);
-
- /* The suffix is now immutable, so this is safe. */
- for (watch = suffix; watch; watch = next)
+ g_mutex_lock (&backend->priv->lock);
+ for (watch = backend->priv->watches; watch; watch = watch->next)
{
GSettingsBackendClosure *closure;
closure = g_slice_new (GSettingsBackendClosure);
+ closure->context = watch->context;
closure->backend = g_object_ref (backend);
- closure->target = watch->target; /* we took our ref above */
+ closure->target = g_object_ref (watch->target);
closure->function = G_STRUCT_MEMBER (void *, watch->vtable,
function_offset);
closure->name = g_strdup (name);
- closure->data1 = data1_copy (data1);
- closure->data1_free = data1_free;
- closure->data2 = data2;
+ closure->origin_tag = origin_tag;
+ closure->names = g_strdupv ((gchar **) names);
- /* we do this here because 'watch' may not live to the end of this
- * iteration of the loop (since we may unref the target below).
- */
- next = watch->next;
+ closures = g_slist_prepend (closures, closure);
+ }
+ g_mutex_unlock (&backend->priv->lock);
- if (watch->context)
- g_main_context_invoke (watch->context,
+ while (closures)
+ {
+ GSettingsBackendClosure *closure = closures->data;
+
+ if (closure->context)
+ g_main_context_invoke (closure->context,
g_settings_backend_invoke_closure,
closure);
else
g_settings_backend_invoke_closure (closure);
+
+ closures = g_slist_delete_link (closures, closures);
}
}
* dispatching the signal later.
*
* The implementation may call this function at any other time it likes
- * in response to other events (such as changes occuring outside of the
+ * in response to other events (such as changes occurring outside of the
* program). These calls may originate from a mainloop or may originate
* in response to any other action (including from calls to
* g_settings_backend_write()).
g_settings_backend_dispatch_signal (backend,
G_STRUCT_OFFSET (GSettingsListenerVTable,
changed),
- key, origin_tag, NULL, NULL, NULL);
+ key, origin_tag, NULL);
}
/**
g_settings_backend_dispatch_signal (backend,
G_STRUCT_OFFSET (GSettingsListenerVTable,
keys_changed),
- path, (gpointer) items,
- (GBoxedCopyFunc) g_strdupv,
- (GBoxedFreeFunc) g_strfreev,
- origin_tag);
+ path, origin_tag, items);
}
/**
g_settings_backend_dispatch_signal (backend,
G_STRUCT_OFFSET (GSettingsListenerVTable,
path_changed),
- path, origin_tag, NULL, NULL, NULL);
+ path, origin_tag, NULL);
}
/**
g_settings_backend_dispatch_signal (backend,
G_STRUCT_OFFSET (GSettingsListenerVTable,
writable_changed),
- key, NULL, NULL, NULL, NULL);
+ key, NULL, NULL);
}
/**
g_settings_backend_dispatch_signal (backend,
G_STRUCT_OFFSET (GSettingsListenerVTable,
path_writable_changed),
- path, NULL, NULL, NULL, NULL);
+ path, NULL, NULL);
}
typedef struct
GTree *tree,
gpointer origin_tag)
{
- GSettingsBackendWatch *watch;
const gchar **keys;
gchar *path;
}
#endif
- for (watch = backend->priv->watches; watch; watch = watch->next)
- watch->vtable->keys_changed (watch->target, backend,
- path, keys, origin_tag);
-
+ g_settings_backend_keys_changed (backend, path, keys, origin_tag);
g_free (path);
g_free (keys);
}
* @key: the key to read
* @expected_type: a #GVariantType
* @default_value: if the default value should be returned
- * @returns: the value that was read, or %NULL
*
* Reads a key. This call will never block.
*
* If @default_value is %TRUE then this gets the default value from the
* backend (ie: the one that the backend would contain if
* g_settings_reset() were called).
+ *
+ * Returns: the value that was read, or %NULL
*/
GVariant *
g_settings_backend_read (GSettingsBackend *backend,
value = G_SETTINGS_BACKEND_GET_CLASS (backend)
->read (backend, key, expected_type, default_value);
+ if (value != NULL)
+ value = g_variant_take_ref (value);
+
+ if G_UNLIKELY (value && !g_variant_is_of_type (value, expected_type))
+ {
+ g_variant_unref (value);
+ value = NULL;
+ }
+
+ return value;
+}
+
+/*< private >
+ * g_settings_backend_read_user_value:
+ * @backend: a #GSettingsBackend implementation
+ * @key: the key to read
+ * @expected_type: a #GVariantType
+ *
+ * Reads the 'user value' of a key.
+ *
+ * This is the value of the key that the user has control over and has
+ * set for themselves. Put another way: if the user did not set the
+ * value for themselves, then this will return %NULL (even if the
+ * sysadmin has provided a default value).
+ *
+ * Returns: the value that was read, or %NULL
+ */
+GVariant *
+g_settings_backend_read_user_value (GSettingsBackend *backend,
+ const gchar *key,
+ const GVariantType *expected_type)
+{
+ GVariant *value;
+
+ value = G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->read_user_value (backend, key, expected_type);
+
+ if (value != NULL)
+ value = g_variant_take_ref (value);
+
if G_UNLIKELY (value && !g_variant_is_of_type (value, expected_type))
{
g_variant_unref (value);
* @key: the name of the key
* @value: a #GVariant value to write to this key
* @origin_tag: the origin tag
- * @returns: %TRUE if the write succeeded, %FALSE if the key was not writable
*
* Writes exactly one key.
*
* to emit a second "changed" signal (either during this call, or later)
* to indicate that the affected keys have suddenly "changed back" to their
* old values.
+ *
+ * Returns: %TRUE if the write succeeded, %FALSE if the key was not writable
*/
gboolean
g_settings_backend_write (GSettingsBackend *backend,
GVariant *value,
gpointer origin_tag)
{
- return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ gboolean success;
+
+ g_variant_ref_sink (value);
+ success = G_SETTINGS_BACKEND_GET_CLASS (backend)
->write (backend, key, value, origin_tag);
+ g_variant_unref (value);
+
+ return success;
}
/*< private >
- * g_settings_backend_write_keys:
+ * g_settings_backend_write_tree:
* @backend: a #GSettingsBackend implementation
- * @values: a #GTree containing key-value pairs to write
+ * @tree: a #GTree containing key-value pairs to write
* @origin_tag: the origin tag
*
* Writes one or more keys. This call will never block.
* g_settings_backend_get_writable:
* @backend: a #GSettingsBackend implementation
* @key: the name of a key
- * @returns: %TRUE if the key is writable
*
* Finds out if a key is available for writing to. This is the
* interface through which 'lockdown' is implemented. Locked down
*
* You should not write to locked-down keys, but if you do, the
* implementation will deal with it.
+ *
+ * Returns: %TRUE if the key is writable
*/
gboolean
g_settings_backend_get_writable (GSettingsBackend *backend,
{
GSettingsBackend *backend = G_SETTINGS_BACKEND (object);
- g_static_mutex_unlock (&backend->priv->lock);
+ g_mutex_clear (&backend->priv->lock);
G_OBJECT_CLASS (g_settings_backend_parent_class)
->finalize (object);
{
}
+static GVariant *
+g_settings_backend_real_read_user_value (GSettingsBackend *backend,
+ const gchar *key,
+ const GVariantType *expected_type)
+{
+ return g_settings_backend_read (backend, key, expected_type, FALSE);
+}
+
static void
g_settings_backend_init (GSettingsBackend *backend)
{
- backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend,
- G_TYPE_SETTINGS_BACKEND,
- GSettingsBackendPrivate);
- g_static_mutex_init (&backend->priv->lock);
+ backend->priv = g_settings_backend_get_instance_private (backend);
+ g_mutex_init (&backend->priv->lock);
}
static void
class->subscribe = ignore_subscription;
class->unsubscribe = ignore_subscription;
- gobject_class->finalize = g_settings_backend_finalize;
+ class->read_user_value = g_settings_backend_real_read_user_value;
- g_type_class_add_private (class, sizeof (GSettingsBackendPrivate));
+ gobject_class->finalize = g_settings_backend_finalize;
}
static void
/*< private >
* g_settings_backend_create_tree:
- * @returns: a new #GTree
*
* This is a convenience function for creating a tree that is compatible
* with g_settings_backend_write(). It merely calls g_tree_new_full()
* with strcmp(), g_free() and g_variant_unref().
+ *
+ * Returns: a new #GTree
*/
GTree *
g_settings_backend_create_tree (void)
g_free, g_settings_backend_variant_unref0);
}
+static gboolean
+g_settings_backend_verify (gpointer impl)
+{
+ GSettingsBackend *backend = impl;
+
+ if (strcmp (G_OBJECT_TYPE_NAME (backend), "GMemorySettingsBackend") == 0 &&
+ g_strcmp0 (g_getenv ("GSETTINGS_BACKEND"), "memory") != 0)
+ {
+ g_message ("Using the 'memory' GSettings backend. Your settings "
+ "will not be saved or shared with other applications.");
+ }
+
+ g_settings_has_backend = TRUE;
+ return TRUE;
+}
+
/**
* g_settings_backend_get_default:
- * @returns: (transfer full): the default #GSettingsBackend
*
* Returns the default #GSettingsBackend. It is possible to override
- * the default by setting the <envar>GSETTINGS_BACKEND</envar>
- * environment variable to the name of a settings backend.
+ * the default by setting the `GSETTINGS_BACKEND` environment variable
+ * to the name of a settings backend.
*
* The user gets a reference to the backend.
*
+ * Returns: (transfer full): the default #GSettingsBackend
+ *
* Since: 2.28
*/
GSettingsBackend *
g_settings_backend_get_default (void)
{
- static gsize backend;
-
- if (g_once_init_enter (&backend))
- {
- GSettingsBackend *instance;
- GIOExtensionPoint *point;
- GIOExtension *extension;
- GType extension_type;
- GList *extensions;
- const gchar *env;
-
- _g_io_modules_ensure_loaded ();
-
- point = g_io_extension_point_lookup (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME);
- extension = NULL;
-
- if ((env = getenv ("GSETTINGS_BACKEND")))
- {
- extension = g_io_extension_point_get_extension_by_name (point, env);
-
- if (extension == NULL)
- g_warning ("Can't find GSettings backend '%s' given in "
- "GSETTINGS_BACKEND environment variable", env);
- }
-
- if (extension == NULL)
- {
- extensions = g_io_extension_point_get_extensions (point);
-
- if (extensions == NULL)
- g_error ("No GSettingsBackend implementations exist.");
-
- extension = extensions->data;
-
- if (strcmp (g_io_extension_get_name (extension), "memory") == 0)
- g_message ("Using the 'memory' GSettings backend. Your settings "
- "will not be saved or shared with other applications.");
- }
-
- extension_type = g_io_extension_get_type (extension);
- instance = g_object_new (extension_type, NULL);
- g_settings_has_backend = TRUE;
-
- g_once_init_leave (&backend, (gsize) instance);
- }
+ GSettingsBackend *backend;
- return g_object_ref ((void *) backend);
+ backend = _g_io_module_get_default (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
+ "GSETTINGS_BACKEND",
+ g_settings_backend_verify);
+ return g_object_ref (backend);
}
/*< private >
* g_settings_backend_get_permission:
* @backend: a #GSettingsBackend
* @path: a path
- * @returns: a non-%NULL #GPermission. Free with g_object_unref()
*
* Gets the permission object associated with writing to keys below
* @path on @backend.
*
* If this is not implemented in the backend, then a %TRUE
* #GSimplePermission is returned.
+ *
+ * Returns: a non-%NULL #GPermission. Free with g_object_unref()
*/
GPermission *
g_settings_backend_get_permission (GSettingsBackend *backend,