* 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/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
#include <string.h>
-#include "gioalias.h"
struct _GDelayedSettingsBackendPrivate
{
GSettingsBackend *backend;
+ GMutex lock;
GTree *delayed;
+
+ GMainContext *owner_context;
gpointer owner;
};
-G_DEFINE_TYPE (GDelayedSettingsBackend,
- g_delayed_settings_backend,
- G_TYPE_SETTINGS_BACKEND)
+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)
+ const GVariantType *expected_type,
+ gboolean default_value)
{
GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
- GVariant *result;
+ gpointer result = NULL;
- if ((result = g_tree_lookup (delayed->priv->delayed, key)))
- return g_variant_ref (result);
+ 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);
+ }
- return g_settings_backend_read (delayed->priv->backend,
- key, expected_type);
+ 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,
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 && delayed->priv->owner)
- g_object_notify (delayed->priv->owner, "has-unapplied");
+ if (was_empty)
+ g_delayed_settings_backend_notify_unapplied (delayed);
return TRUE;
}
}
static gboolean
-g_delayed_settings_backend_write_keys (GSettingsBackend *backend,
+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 && delayed->priv->owner)
- g_object_notify (delayed->priv->owner, "has-unapplied");
+ if (was_empty)
+ g_delayed_settings_backend_notify_unapplied (delayed);
return TRUE;
}
const gchar *key,
gpointer origin_tag)
{
- /* deal with this... */
-}
+ GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
+ gboolean was_empty;
-static void
-g_delayed_settings_backend_reset_path (GSettingsBackend *backend,
- const gchar *path,
- gpointer origin_tag)
-{
- /* deal with this... */
+ 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);
+
+ if (was_empty)
+ g_delayed_settings_backend_notify_unapplied (delayed);
}
static void
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;
}
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_keys (delayed->priv->backend,
+ 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),
g_tree_unref (tmp);
- if (delayed->priv->owner)
- g_object_notify (delayed->priv->owner, "has-unapplied");
+ g_delayed_settings_backend_notify_unapplied (delayed);
}
}
{
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);
- if (delayed->priv->owner)
- g_object_notify (delayed->priv->owner, "has-unapplied");
+ g_delayed_settings_backend_notify_unapplied (delayed);
}
}
/* change notification */
static void
-delayed_backend_changed (GSettingsBackend *backend,
+delayed_backend_changed (GObject *target,
+ GSettingsBackend *backend,
const gchar *key,
- gpointer origin_tag,
- gpointer user_data)
+ gpointer origin_tag)
{
- GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+ GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
if (origin_tag != delayed->priv)
g_settings_backend_changed (G_SETTINGS_BACKEND (delayed),
}
static void
-delayed_backend_keys_changed (GSettingsBackend *backend,
+delayed_backend_keys_changed (GObject *target,
+ GSettingsBackend *backend,
const gchar *path,
- const gchar * const *items,
gpointer origin_tag,
- gpointer user_data)
+ const gchar * const *items)
{
- GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+ GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
if (origin_tag != delayed->priv)
g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed),
}
static void
-delayed_backend_path_changed (GSettingsBackend *backend,
- const gchar *path,
- gpointer origin_tag,
- gpointer user_data)
+delayed_backend_path_changed (GObject *target,
+ GSettingsBackend *backend,
+ const gchar *path,
+ gpointer origin_tag)
{
- GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+ GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
if (origin_tag != delayed->priv)
g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed),
}
static void
-delayed_backend_writable_changed (GSettingsBackend *backend,
- const gchar *key,
- gpointer user_data)
+delayed_backend_writable_changed (GObject *target,
+ GSettingsBackend *backend,
+ const gchar *key)
{
- GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+ GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
+ gboolean last_one = FALSE;
- if (g_tree_lookup (delayed->priv->delayed, key) &&
+ 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 this since the writable change implies it.
+ * 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);
}
}
static void
-delayed_backend_path_writable_changed (GSettingsBackend *backend,
- const gchar *path,
- gpointer user_data)
+delayed_backend_path_writable_changed (GObject *target,
+ GSettingsBackend *backend,
+ const gchar *path)
{
- GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
- CheckPrefixState state = { path, };
- gsize i;
+ GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target);
+ gboolean last_one = FALSE;
+ gsize n_keys;
- /* collect a list of possibly-affected keys (ie: matching the path) */
- state.keys = g_new (const gchar *, g_tree_nnodes (delayed->priv->delayed));
- g_tree_foreach (delayed->priv->delayed, check_prefix, &state);
+ g_mutex_lock (&delayed->priv->lock);
- /* drop the keys that have been affected */
- for (i = 0; i < state.index; i++)
- if (!g_settings_backend_get_writable (delayed->priv->backend,
- state.keys[i]))
- g_tree_remove (delayed->priv->delayed, state.keys[i]);
+ n_keys = g_tree_nnodes (delayed->priv->delayed);
+
+ if (n_keys > 0)
+ {
+ CheckPrefixState state = { path, g_new (const gchar *, n_keys) };
+ gsize i;
- g_free (state.keys);
+ /* 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);
{
GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
- g_settings_backend_unwatch (delayed->priv->backend, delayed);
+ 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
GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
- g_type_class_add_private (class, sizeof (GDelayedSettingsBackendPrivate));
-
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_keys = g_delayed_settings_backend_write_keys;
+ backend_class->write_tree = g_delayed_settings_backend_write_tree;
backend_class->reset = g_delayed_settings_backend_reset;
- backend_class->reset_path = g_delayed_settings_backend_reset_path;
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_TYPE_INSTANCE_GET_PRIVATE (delayed, G_TYPE_DELAYED_SETTINGS_BACKEND,
- GDelayedSettingsBackendPrivate);
-
+ 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)
+ 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,
- delayed_backend_changed,
- delayed_backend_path_changed,
- delayed_backend_keys_changed,
- delayed_backend_writable_changed,
- delayed_backend_path_writable_changed,
- delayed);
+ &vtable, G_OBJECT (delayed), NULL);
return delayed;
}