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