mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 08:22:16 +01:00 
			
		
		
		
	Add SPDX license (but not copyright) headers to all files which follow a certain pattern in their existing non-machine-readable header comment. This commit was entirely generated using the command: ``` git ls-files gio/*.[ch] | xargs perl -0777 -pi -e 's/\n \*\n \* This library is free software; you can redistribute it and\/or\n \* modify it under the terms of the GNU Lesser General Public/\n \*\n \* SPDX-License-Identifier: LGPL-2.1-or-later\n \*\n \* This library is free software; you can redistribute it and\/or\n \* modify it under the terms of the GNU Lesser General Public/igs' ``` Signed-off-by: Philip Withnall <pwithnall@endlessos.org> Helps: #1415
		
			
				
	
	
		
			519 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			519 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright © 2009, 2010 Codethink Limited
 | |
|  *
 | |
|  * SPDX-License-Identifier: LGPL-2.1-or-later
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * 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, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * Author: Ryan Lortie <desrt@desrt.ca>
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include "gdelayedsettingsbackend.h"
 | |
| #include "gsettingsbackendinternal.h"
 | |
| 
 | |
| #include <string.h>
 | |
| 
 | |
| 
 | |
| struct _GDelayedSettingsBackendPrivate
 | |
| {
 | |
|   GSettingsBackend *backend;
 | |
|   GMutex lock;
 | |
|   GTree *delayed;
 | |
| 
 | |
|   GMainContext *owner_context;
 | |
|   gpointer owner;
 | |
| };
 | |
| 
 | |
| G_DEFINE_TYPE_WITH_PRIVATE (GDelayedSettingsBackend,
 | |
|                             g_delayed_settings_backend,
 | |
|                             G_TYPE_SETTINGS_BACKEND)
 | |
| 
 | |
| static gboolean
 | |
| invoke_notify_unapplied (gpointer data)
 | |
| {
 | |
|   g_object_notify (data, "has-unapplied");
 | |
|   g_object_unref (data);
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_notify_unapplied (GDelayedSettingsBackend *delayed)
 | |
| {
 | |
|   GMainContext *target_context;
 | |
|   GObject *target;
 | |
| 
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
|   if (delayed->priv->owner)
 | |
|     {
 | |
|       target_context = delayed->priv->owner_context;
 | |
|       target = g_object_ref (delayed->priv->owner);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       target_context = NULL;
 | |
|       target = NULL;
 | |
|     }
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|   if (target != NULL)
 | |
|     g_main_context_invoke (target_context, invoke_notify_unapplied, target);
 | |
| }
 | |
| 
 | |
| 
 | |
| static GVariant *
 | |
| g_delayed_settings_backend_read (GSettingsBackend   *backend,
 | |
|                                  const gchar        *key,
 | |
|                                  const GVariantType *expected_type,
 | |
|                                  gboolean            default_value)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
|   gpointer result = NULL;
 | |
| 
 | |
|   if (!default_value)
 | |
|     {
 | |
|       g_mutex_lock (&delayed->priv->lock);
 | |
|       if (g_tree_lookup_extended (delayed->priv->delayed, key, NULL, &result))
 | |
|         {
 | |
|           /* NULL in the tree means we should consult the default value */
 | |
|           if (result != NULL)
 | |
|             g_variant_ref (result);
 | |
|           else
 | |
|             default_value = TRUE;
 | |
|         }
 | |
|       g_mutex_unlock (&delayed->priv->lock);
 | |
|     }
 | |
| 
 | |
|   if (result == NULL)
 | |
|     result = g_settings_backend_read (delayed->priv->backend, key,
 | |
|                                       expected_type, default_value);
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static GVariant *
 | |
| g_delayed_settings_backend_read_user_value (GSettingsBackend   *backend,
 | |
|                                             const gchar        *key,
 | |
|                                             const GVariantType *expected_type)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
|   gboolean value_found = FALSE;
 | |
|   gpointer result = NULL;
 | |
| 
 | |
|   /* If we find an explicit NULL in our changeset then we want to return
 | |
|    * NULL (because the user value has been reset).
 | |
|    *
 | |
|    * Otherwise, chain up.
 | |
|    */
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
|   value_found = g_tree_lookup_extended (delayed->priv->delayed, key, NULL, &result);
 | |
|   if (result)
 | |
|     g_variant_ref (result);
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|   if (value_found)
 | |
|     return result;
 | |
| 
 | |
|   return g_settings_backend_read_user_value (delayed->priv->backend, key, expected_type);
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| g_delayed_settings_backend_write (GSettingsBackend *backend,
 | |
|                                   const gchar      *key,
 | |
|                                   GVariant         *value,
 | |
|                                   gpointer          origin_tag)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
|   gboolean was_empty;
 | |
| 
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
|   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
 | |
|   g_tree_insert (delayed->priv->delayed, g_strdup (key),
 | |
|                  g_variant_ref_sink (value));
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|   g_settings_backend_changed (backend, key, origin_tag);
 | |
| 
 | |
|   if (was_empty)
 | |
|     g_delayed_settings_backend_notify_unapplied (delayed);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| add_to_tree (gpointer key,
 | |
|              gpointer value,
 | |
|              gpointer user_data)
 | |
| {
 | |
|   /* A value may be %NULL if its key has been reset */
 | |
|   g_tree_insert (user_data, g_strdup (key), (value != NULL) ? g_variant_ref (value) : NULL);
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| g_delayed_settings_backend_write_tree (GSettingsBackend *backend,
 | |
|                                        GTree            *tree,
 | |
|                                        gpointer          origin_tag)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
|   gboolean was_empty;
 | |
| 
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
|   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
 | |
| 
 | |
|   g_tree_foreach (tree, add_to_tree, delayed->priv->delayed);
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|   g_settings_backend_changed_tree (backend, tree, origin_tag);
 | |
| 
 | |
|   if (was_empty)
 | |
|     g_delayed_settings_backend_notify_unapplied (delayed);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| g_delayed_settings_backend_get_writable (GSettingsBackend *backend,
 | |
|                                          const gchar      *name)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
| 
 | |
|   return g_settings_backend_get_writable (delayed->priv->backend, name);
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_reset (GSettingsBackend *backend,
 | |
|                                   const gchar      *key,
 | |
|                                   gpointer          origin_tag)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
|   gboolean was_empty;
 | |
| 
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
|   was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
 | |
|   g_tree_insert (delayed->priv->delayed, g_strdup (key), NULL);
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|   g_settings_backend_changed (backend, key, origin_tag);
 | |
| 
 | |
|   if (was_empty)
 | |
|     g_delayed_settings_backend_notify_unapplied (delayed);
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_subscribe (GSettingsBackend *backend,
 | |
|                                       const char       *name)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
| 
 | |
|   g_settings_backend_subscribe (delayed->priv->backend, name);
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_unsubscribe (GSettingsBackend *backend,
 | |
|                                         const char       *name)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
| 
 | |
|   g_settings_backend_unsubscribe (delayed->priv->backend, name);
 | |
| }
 | |
| 
 | |
| static GPermission *
 | |
| g_delayed_settings_backend_get_permission (GSettingsBackend *backend,
 | |
|                                            const gchar      *path)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
 | |
| 
 | |
|   return g_settings_backend_get_permission (delayed->priv->backend, path);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* method calls */
 | |
| gboolean
 | |
| g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend *delayed)
 | |
| {
 | |
|   /* we don't need to lock for this... */
 | |
| 
 | |
|   return g_tree_nnodes (delayed->priv->delayed) > 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| g_delayed_settings_backend_apply (GDelayedSettingsBackend *delayed)
 | |
| {
 | |
|   if (g_tree_nnodes (delayed->priv->delayed) > 0)
 | |
|     {
 | |
|       gboolean success;
 | |
|       GTree *tmp;
 | |
| 
 | |
|       g_mutex_lock (&delayed->priv->lock);
 | |
|       tmp = delayed->priv->delayed;
 | |
|       delayed->priv->delayed = g_settings_backend_create_tree ();
 | |
|       success = g_settings_backend_write_tree (delayed->priv->backend,
 | |
|                                                tmp, delayed->priv);
 | |
|       g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|       if (!success)
 | |
|         g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed),
 | |
|                                          tmp, NULL);
 | |
| 
 | |
|       g_tree_unref (tmp);
 | |
| 
 | |
|       g_delayed_settings_backend_notify_unapplied (delayed);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| g_delayed_settings_backend_revert (GDelayedSettingsBackend *delayed)
 | |
| {
 | |
|   if (g_tree_nnodes (delayed->priv->delayed) > 0)
 | |
|     {
 | |
|       GTree *tmp;
 | |
| 
 | |
|       g_mutex_lock (&delayed->priv->lock);
 | |
|       tmp = delayed->priv->delayed;
 | |
|       delayed->priv->delayed = g_settings_backend_create_tree ();
 | |
|       g_mutex_unlock (&delayed->priv->lock);
 | |
|       g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed), tmp, NULL);
 | |
|       g_tree_unref (tmp);
 | |
| 
 | |
|       g_delayed_settings_backend_notify_unapplied (delayed);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* change notification */
 | |
| static void
 | |
| delayed_backend_changed (GObject          *target,
 | |
|                          GSettingsBackend *backend,
 | |
|                          const gchar      *key,
 | |
|                          gpointer          origin_tag)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 | |
| 
 | |
|   if (origin_tag != delayed->priv)
 | |
|     g_settings_backend_changed (G_SETTINGS_BACKEND (delayed),
 | |
|                                 key, origin_tag);
 | |
| }
 | |
| 
 | |
| static void
 | |
| delayed_backend_keys_changed (GObject             *target,
 | |
|                               GSettingsBackend    *backend,
 | |
|                               const gchar         *path,
 | |
|                               gpointer             origin_tag,
 | |
|                               const gchar * const *items)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 | |
| 
 | |
|   if (origin_tag != delayed->priv)
 | |
|     g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed),
 | |
|                                      path, items, origin_tag);
 | |
| }
 | |
| 
 | |
| static void
 | |
| delayed_backend_path_changed (GObject          *target,
 | |
|                               GSettingsBackend *backend,
 | |
|                               const gchar      *path,
 | |
|                               gpointer          origin_tag)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 | |
| 
 | |
|   if (origin_tag != delayed->priv)
 | |
|     g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed),
 | |
|                                      path, origin_tag);
 | |
| }
 | |
| 
 | |
| static void
 | |
| delayed_backend_writable_changed (GObject          *target,
 | |
|                                   GSettingsBackend *backend,
 | |
|                                   const gchar      *key)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 | |
|   gboolean last_one = FALSE;
 | |
| 
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
| 
 | |
|   if (g_tree_lookup (delayed->priv->delayed, key) != NULL &&
 | |
|       !g_settings_backend_get_writable (delayed->priv->backend, key))
 | |
|     {
 | |
|       /* drop the key from our changeset if it just became read-only.
 | |
|        * no need to signal since the writable change below implies it.
 | |
|        *
 | |
|        * note that the item in the tree may very well be set to NULL in
 | |
|        * the case that the user stored a reset.  we intentionally don't
 | |
|        * drop the key in this case since a reset will always succeed
 | |
|        * (even against a non-writable key).
 | |
|        */
 | |
|       g_tree_remove (delayed->priv->delayed, key);
 | |
| 
 | |
|       /* if that was the only key... */
 | |
|       last_one = g_tree_nnodes (delayed->priv->delayed) == 0;
 | |
|     }
 | |
| 
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|   if (last_one)
 | |
|     g_delayed_settings_backend_notify_unapplied (delayed);
 | |
| 
 | |
|   g_settings_backend_writable_changed (G_SETTINGS_BACKEND (delayed), key);
 | |
| }
 | |
| 
 | |
| /* slow method until we get foreach-with-remove in GTree
 | |
|  */
 | |
| typedef struct
 | |
| {
 | |
|   const gchar *path;
 | |
|   const gchar **keys;
 | |
|   gsize index;
 | |
| } CheckPrefixState;
 | |
| 
 | |
| static gboolean
 | |
| check_prefix (gpointer key,
 | |
|               gpointer value,
 | |
|               gpointer data)
 | |
| {
 | |
|   CheckPrefixState *state = data;
 | |
| 
 | |
|   if (g_str_has_prefix (key, state->path))
 | |
|     state->keys[state->index++] = key;
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| delayed_backend_path_writable_changed (GObject          *target,
 | |
|                                        GSettingsBackend *backend,
 | |
|                                        const gchar      *path)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
 | |
|   gboolean last_one = FALSE;
 | |
|   gsize n_keys;
 | |
| 
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
| 
 | |
|   n_keys = g_tree_nnodes (delayed->priv->delayed);
 | |
| 
 | |
|   if (n_keys > 0)
 | |
|     {
 | |
|       CheckPrefixState state = { path, g_new (const gchar *, n_keys), 0 };
 | |
|       gsize i;
 | |
| 
 | |
|       /* collect a list of possibly-affected keys (ie: matching the path) */
 | |
|       g_tree_foreach (delayed->priv->delayed, check_prefix, &state);
 | |
| 
 | |
|       /* drop the keys that have been affected.
 | |
|        *
 | |
|        * don't drop 'reset' keys (see above) */
 | |
|       for (i = 0; i < state.index; i++)
 | |
|         if (g_tree_lookup (delayed->priv->delayed, state.keys[i]) != NULL &&
 | |
|             !g_settings_backend_get_writable (delayed->priv->backend,
 | |
|                                               state.keys[i]))
 | |
|           g_tree_remove (delayed->priv->delayed, state.keys[i]);
 | |
| 
 | |
|       g_free (state.keys);
 | |
| 
 | |
|       last_one = g_tree_nnodes (delayed->priv->delayed) == 0;
 | |
|     }
 | |
| 
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| 
 | |
|   if (last_one)
 | |
|     g_delayed_settings_backend_notify_unapplied (delayed);
 | |
| 
 | |
|   g_settings_backend_path_writable_changed (G_SETTINGS_BACKEND (delayed),
 | |
|                                             path);
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_finalize (GObject *object)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
 | |
| 
 | |
|   g_mutex_clear (&delayed->priv->lock);
 | |
|   g_object_unref (delayed->priv->backend);
 | |
|   g_tree_unref (delayed->priv->delayed);
 | |
| 
 | |
|   /* if our owner is still alive, why are we finalizing? */
 | |
|   g_assert (delayed->priv->owner == NULL);
 | |
| 
 | |
|   G_OBJECT_CLASS (g_delayed_settings_backend_parent_class)
 | |
|     ->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_class_init (GDelayedSettingsBackendClass *class)
 | |
| {
 | |
|   GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class);
 | |
|   GObjectClass *object_class = G_OBJECT_CLASS (class);
 | |
| 
 | |
|   backend_class->read = g_delayed_settings_backend_read;
 | |
|   backend_class->read_user_value = g_delayed_settings_backend_read_user_value;
 | |
|   backend_class->write = g_delayed_settings_backend_write;
 | |
|   backend_class->write_tree = g_delayed_settings_backend_write_tree;
 | |
|   backend_class->reset = g_delayed_settings_backend_reset;
 | |
|   backend_class->get_writable = g_delayed_settings_backend_get_writable;
 | |
|   backend_class->subscribe = g_delayed_settings_backend_subscribe;
 | |
|   backend_class->unsubscribe = g_delayed_settings_backend_unsubscribe;
 | |
|   backend_class->get_permission = g_delayed_settings_backend_get_permission;
 | |
| 
 | |
|   object_class->finalize = g_delayed_settings_backend_finalize;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_init (GDelayedSettingsBackend *delayed)
 | |
| {
 | |
|   delayed->priv = g_delayed_settings_backend_get_instance_private (delayed);
 | |
|   delayed->priv->delayed = g_settings_backend_create_tree ();
 | |
|   g_mutex_init (&delayed->priv->lock);
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_delayed_settings_backend_disown (gpointer  data,
 | |
|                                    GObject  *where_the_object_was)
 | |
| {
 | |
|   GDelayedSettingsBackend *delayed = data;
 | |
| 
 | |
|   g_mutex_lock (&delayed->priv->lock);
 | |
|   delayed->priv->owner_context = NULL;
 | |
|   delayed->priv->owner = NULL;
 | |
|   g_mutex_unlock (&delayed->priv->lock);
 | |
| }
 | |
| 
 | |
| GDelayedSettingsBackend *
 | |
| g_delayed_settings_backend_new (GSettingsBackend *backend,
 | |
|                                 gpointer          owner,
 | |
|                                 GMainContext     *owner_context)
 | |
| {
 | |
|   static GSettingsListenerVTable vtable = {
 | |
|     delayed_backend_changed,
 | |
|     delayed_backend_path_changed,
 | |
|     delayed_backend_keys_changed,
 | |
|     delayed_backend_writable_changed,
 | |
|     delayed_backend_path_writable_changed
 | |
|   };
 | |
|   GDelayedSettingsBackend *delayed;
 | |
| 
 | |
|   delayed = g_object_new (G_TYPE_DELAYED_SETTINGS_BACKEND, NULL);
 | |
|   delayed->priv->backend = g_object_ref (backend);
 | |
|   delayed->priv->owner_context = owner_context;
 | |
|   delayed->priv->owner = owner;
 | |
| 
 | |
|   g_object_weak_ref (owner, g_delayed_settings_backend_disown, delayed);
 | |
| 
 | |
|   g_settings_backend_watch (delayed->priv->backend,
 | |
|                             &vtable, G_OBJECT (delayed), NULL);
 | |
| 
 | |
|   return delayed;
 | |
| }
 |