Merge branch 'registry-settings-root-key' into 'main'

gregistrysettingsbackend: Allow a different root key path

See merge request GNOME/glib!3306
This commit is contained in:
Philip Withnall 2023-04-14 17:00:01 +00:00
commit da35056247
6 changed files with 334 additions and 122 deletions

View File

@ -110,3 +110,13 @@ G_WIN32_REGISTRY_KEY_GET_CLASS
G_TYPE_WIN32_REGISTRY_SUBKEY_ITER G_TYPE_WIN32_REGISTRY_SUBKEY_ITER
G_TYPE_WIN32_REGISTRY_VALUE_ITER G_TYPE_WIN32_REGISTRY_VALUE_ITER
</SECTION> </SECTION>
<SECTION>
<FILE>gregistrysettingsbackend</FILE>
<TITLE>GRegistrySettingsBackend</TITLE>
GRegistrySettingsBackend
g_registry_settings_backend_new
<SUBSECTION Private>
g_registry_settings_backend_get_type
</SECTION>

View File

@ -1355,7 +1355,7 @@ _g_io_modules_ensure_loaded (void)
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
g_type_ensure (_g_win32_volume_monitor_get_type ()); g_type_ensure (_g_win32_volume_monitor_get_type ());
g_type_ensure (g_win32_file_monitor_get_type ()); g_type_ensure (g_win32_file_monitor_get_type ());
g_type_ensure (g_registry_backend_get_type ()); g_type_ensure (g_registry_settings_backend_get_type ());
#endif #endif
#ifdef HAVE_COCOA #ifdef HAVE_COCOA
g_type_ensure (g_nextstep_settings_backend_get_type ()); g_type_ensure (g_nextstep_settings_backend_get_type ());

View File

@ -19,13 +19,11 @@
* Author: Sam Thursfield <ssssam@gmail.com> * Author: Sam Thursfield <ssssam@gmail.com>
*/ */
/* GRegistryBackend implementation notes: /* GRegistrySettingsBackend implementation notes:
* *
* - All settings are stored under the path: * - All settings are stored under the registry path given at construction
* HKEY_CURRENT_USER\Software\GSettings\ * time. Permissions and system-wide defaults are not implemented and will
* This means all settings are per-user. Permissions and system-wide * probably always be out of scope of the Windows port of GLib.
* defaults are not implemented and will probably always be out of scope of
* the Windows port of GLib.
* *
* - The registry type system is limited. Most GVariant types are stored as * - The registry type system is limited. Most GVariant types are stored as
* literals via g_variant_print/parse(). Strings are stored without the * literals via g_variant_print/parse(). Strings are stored without the
@ -93,8 +91,11 @@
#include "gregistrysettingsbackend.h" #include "gregistrysettingsbackend.h"
#include "gsettingsbackend.h" #include "gsettingsbackend.h"
#include "gsettingsbackendinternal.h"
#include "giomodule-priv.h" #include "giomodule-priv.h"
#include <glibintl.h>
#include <windows.h> #include <windows.h>
//#define TRACE //#define TRACE
@ -154,17 +155,22 @@ typedef struct
HANDLE message_sent_event, message_received_event; HANDLE message_sent_event, message_received_event;
} WatchThreadState; } WatchThreadState;
#define G_TYPE_REGISTRY_BACKEND (g_registry_backend_get_type ()) #define G_TYPE_REGISTRY_SETTINGS_BACKEND (g_registry_settings_backend_get_type ())
#define G_REGISTRY_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ #define G_REGISTRY_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_REGISTRY_BACKEND, GRegistryBackend)) G_TYPE_REGISTRY_SETTINGS_BACKEND, GRegistrySettingsBackend))
#define G_IS_REGISTRY_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ #define G_IS_REGISTRY_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_REGISTRY_BACKEND)) G_TYPE_REGISTRY_SETTINGS_BACKEND))
typedef GSettingsBackendClass GRegistryBackendClass; typedef enum {
PROP_REGISTRY_KEY = 1,
} GRegistrySettingsBackendProperty;
typedef GSettingsBackendClass GRegistrySettingsBackendClass;
typedef struct { typedef struct {
GSettingsBackend parent_instance; GSettingsBackend parent_instance;
HKEY base_key;
gchar *base_path; gchar *base_path;
gunichar2 *base_pathw; gunichar2 *base_pathw;
@ -174,10 +180,10 @@ typedef struct {
GNode *cache_root; GNode *cache_root;
WatchThreadState *watch; WatchThreadState *watch;
} GRegistryBackend; } GRegistrySettingsBackend;
G_DEFINE_TYPE_WITH_CODE (GRegistryBackend, G_DEFINE_TYPE_WITH_CODE (GRegistrySettingsBackend,
g_registry_backend, g_registry_settings_backend,
G_TYPE_SETTINGS_BACKEND, G_TYPE_SETTINGS_BACKEND,
_g_io_modules_ensure_extension_points_registered (); _g_io_modules_ensure_extension_points_registered ();
g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
@ -319,6 +325,59 @@ handle_read_error (LONG result,
path_name, value_name); path_name, value_name);
} }
typedef struct {
HKEY handle;
const char *name;
} HandleNamePair;
static const HandleNamePair predefined_key_names[] = {
{ HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT" },
{ HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG" },
{ HKEY_CURRENT_USER, "HKEY_CURRENT_USER" },
{ HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE" },
{ HKEY_USERS, "HKEY_USERS" }
};
static const gchar*
predefined_key_to_string (HKEY key)
{
for (gsize i = 0; i < G_N_ELEMENTS (predefined_key_names); i++)
{
if (predefined_key_names[i].handle == key)
return predefined_key_names[i].name;
}
g_warning ("gregistrysettingsbackend: unexpected root key (%p)", key);
return "";
}
static const gchar*
split_registry_path (const gchar *full_path,
HKEY *root_key)
{
g_assert (full_path != NULL);
for (gsize i = 0; i < G_N_ELEMENTS (predefined_key_names); i++)
{
const gchar *root_name = predefined_key_names[i].name;
if (g_str_has_prefix (full_path, root_name))
{
const gchar *rest = full_path + strlen (root_name);
if (*rest == '\\')
{
if (root_key != NULL)
*root_key = predefined_key_names[i].handle;
return ++rest;
}
}
}
return NULL;
}
/*************************************************************************** /***************************************************************************
* Cache of registry values * Cache of registry values
***************************************************************************/ ***************************************************************************/
@ -742,7 +801,7 @@ registry_cache_update_node (GNode *cache_node,
} }
} }
default: default:
g_warning ("gregistrybackend: registry_cache_update_node: Unhandled value type"); g_warning ("gregistrysettingsbackend: registry_cache_update_node: Unhandled value type");
return FALSE; return FALSE;
} }
} }
@ -838,12 +897,12 @@ registry_read (HKEY hpath,
} }
static GVariant * static GVariant *
g_registry_backend_read (GSettingsBackend *backend, g_registry_settings_backend_read (GSettingsBackend *backend,
const gchar *key_name, const gchar *key_name,
const GVariantType *expected_type, const GVariantType *expected_type,
gboolean default_value) gboolean default_value)
{ {
GRegistryBackend *self = G_REGISTRY_BACKEND (backend); GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (backend);
GNode *cache_node; GNode *cache_node;
RegistryValue registry_value; RegistryValue registry_value;
GVariant *gsettings_value = NULL; GVariant *gsettings_value = NULL;
@ -924,16 +983,16 @@ g_registry_backend_read (GSettingsBackend *backend,
typedef struct typedef struct
{ {
GRegistryBackend *self; GRegistrySettingsBackend *self;
HKEY hroot; HKEY hroot;
} RegistryWrite; } RegistryWrite;
static gboolean static gboolean
g_registry_backend_write_one (const char *key_name, g_registry_settings_backend_write_one (const char *key_name,
GVariant *variant, GVariant *variant,
gpointer user_data) gpointer user_data)
{ {
GRegistryBackend *self; GRegistrySettingsBackend *self;
RegistryWrite *action; RegistryWrite *action;
RegistryValue value; RegistryValue value;
HKEY hroot; HKEY hroot;
@ -952,7 +1011,7 @@ g_registry_backend_write_one (const char *key_name,
type_string = g_variant_get_type_string (variant); type_string = g_variant_get_type_string (variant);
action = user_data; action = user_data;
self = G_REGISTRY_BACKEND (action->self); self = G_REGISTRY_SETTINGS_BACKEND (action->self);
hroot = action->hroot; hroot = action->hroot;
value.type = REG_NONE; value.type = REG_NONE;
@ -1030,7 +1089,7 @@ g_registry_backend_write_one (const char *key_name,
result = RegCreateKeyExW (hroot, path_namew, 0, NULL, 0, KEY_WRITE, NULL, &hpath, NULL); result = RegCreateKeyExW (hroot, path_namew, 0, NULL, 0, KEY_WRITE, NULL, &hpath, NULL);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
{ {
g_message_win32_error (result, "gregistrybackend: opening key %s failed", g_message_win32_error (result, "gregistrysettingsbackend: opening key %s failed",
path_name + 1); path_name + 1);
registry_value_free (value); registry_value_free (value);
g_free (path_namew); g_free (path_namew);
@ -1065,7 +1124,8 @@ g_registry_backend_write_one (const char *key_name,
result = RegSetValueExW (hpath, value_namew, 0, value.type, value_data, value_data_size); result = RegSetValueExW (hpath, value_namew, 0, value.type, value_data, value_data_size);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
g_message_win32_error (result, "gregistrybackend: setting value %s\\%s\\%s failed.\n", g_message_win32_error (result, "gregistrysettingsbackend: setting value %s\\%s\\%s\\%s failed.\n",
predefined_key_to_string (self->base_key),
self->base_path, path_name, value_name); self->base_path, path_name, value_name);
/* If the write fails then it will seem like the value has changed until the /* If the write fails then it will seem like the value has changed until the
@ -1086,27 +1146,29 @@ g_registry_backend_write_one (const char *key_name,
* we can't do that. */ * we can't do that. */
static gboolean static gboolean
g_registry_backend_write (GSettingsBackend *backend, g_registry_settings_backend_write (GSettingsBackend *backend,
const gchar *key_name, const gchar *key_name,
GVariant *value, GVariant *value,
gpointer origin_tag) gpointer origin_tag)
{ {
GRegistryBackend *self = G_REGISTRY_BACKEND (backend); GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (backend);
LONG result; LONG result;
HKEY hroot; HKEY hroot;
RegistryWrite action; RegistryWrite action;
result = RegCreateKeyExW (HKEY_CURRENT_USER, self->base_pathw, 0, NULL, 0, result = RegCreateKeyExW (self->base_key, self->base_pathw, 0, NULL, 0,
KEY_WRITE, NULL, &hroot, NULL); KEY_WRITE, NULL, &hroot, NULL);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
{ {
trace ("Error opening/creating key %s.\n", self->base_path); trace ("Error opening/creating key %s\\%s.\n",
predefined_key_to_string (self->base_key),
self->base_path);
return FALSE; return FALSE;
} }
action.self = self; action.self = self;
action.hroot = hroot; action.hroot = hroot;
g_registry_backend_write_one (key_name, value, &action); g_registry_settings_backend_write_one (key_name, value, &action);
g_settings_backend_changed (backend, key_name, origin_tag); g_settings_backend_changed (backend, key_name, origin_tag);
RegCloseKey (hroot); RegCloseKey (hroot);
@ -1115,26 +1177,28 @@ g_registry_backend_write (GSettingsBackend *backend,
} }
static gboolean static gboolean
g_registry_backend_write_tree (GSettingsBackend *backend, g_registry_settings_backend_write_tree (GSettingsBackend *backend,
GTree *values, GTree *values,
gpointer origin_tag) gpointer origin_tag)
{ {
GRegistryBackend *self = G_REGISTRY_BACKEND (backend); GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (backend);
LONG result; LONG result;
HKEY hroot; HKEY hroot;
RegistryWrite action; RegistryWrite action;
result = RegCreateKeyExW (HKEY_CURRENT_USER, self->base_pathw, 0, NULL, 0, result = RegCreateKeyExW (self->base_key, self->base_pathw, 0, NULL, 0,
KEY_WRITE, NULL, &hroot, NULL); KEY_WRITE, NULL, &hroot, NULL);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
{ {
trace ("Error opening/creating key %s.\n", self->base_path); trace ("Error opening/creating key %s\\%s.\n",
predefined_key_to_string (self->base_key),
self->base_path);
return FALSE; return FALSE;
} }
action.self = self; action.self = self;
action.hroot = hroot; action.hroot = hroot;
g_tree_foreach (values, (GTraverseFunc)g_registry_backend_write_one, g_tree_foreach (values, (GTraverseFunc)g_registry_settings_backend_write_one,
&action); &action);
g_settings_backend_changed_tree (backend, values, origin_tag); g_settings_backend_changed_tree (backend, values, origin_tag);
@ -1144,11 +1208,11 @@ g_registry_backend_write_tree (GSettingsBackend *backend,
} }
static void static void
g_registry_backend_reset (GSettingsBackend *backend, g_registry_settings_backend_reset (GSettingsBackend *backend,
const gchar *key_name, const gchar *key_name,
gpointer origin_tag) gpointer origin_tag)
{ {
GRegistryBackend *self = G_REGISTRY_BACKEND (backend); GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (backend);
gchar *path_name; gchar *path_name;
gunichar2 *path_namew; gunichar2 *path_namew;
gchar *value_name = NULL; gchar *value_name = NULL;
@ -1168,12 +1232,14 @@ g_registry_backend_reset (GSettingsBackend *backend,
path_name = parse_key (key_name, self->base_path, &value_name); path_name = parse_key (key_name, self->base_path, &value_name);
path_namew = g_utf8_to_utf16 (path_name, -1, NULL, NULL, NULL); path_namew = g_utf8_to_utf16 (path_name, -1, NULL, NULL, NULL);
result = RegOpenKeyExW (HKEY_CURRENT_USER, path_namew, 0, KEY_SET_VALUE, &hpath); result = RegOpenKeyExW (self->base_key, path_namew, 0, KEY_SET_VALUE, &hpath);
g_free (path_namew); g_free (path_namew);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
{ {
g_message_win32_error (result, "Registry: resetting key '%s'", path_name); g_message_win32_error (result, "Registry: resetting key '%s\\%s'",
predefined_key_to_string (self->base_key),
path_name);
g_free (path_name); g_free (path_name);
return; return;
} }
@ -1186,7 +1252,9 @@ g_registry_backend_reset (GSettingsBackend *backend,
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
{ {
g_message_win32_error (result, "Registry: resetting key '%s'", path_name); g_message_win32_error (result, "Registry: resetting key '%s\\%s'",
predefined_key_to_string (self->base_key),
path_name);
g_free (path_name); g_free (path_name);
return; return;
} }
@ -1197,10 +1265,10 @@ g_registry_backend_reset (GSettingsBackend *backend,
} }
static gboolean static gboolean
g_registry_backend_get_writable (GSettingsBackend *backend, g_registry_settings_backend_get_writable (GSettingsBackend *backend,
const gchar *key_name) const gchar *key_name)
{ {
GRegistryBackend *self = G_REGISTRY_BACKEND (backend); GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (backend);
gchar *path_name; gchar *path_name;
gunichar2 *path_namew; gunichar2 *path_namew;
gchar *value_name = NULL; gchar *value_name = NULL;
@ -1214,13 +1282,14 @@ g_registry_backend_get_writable (GSettingsBackend *backend,
* of a problem since at the end of the day we have to create it anyway * of a problem since at the end of the day we have to create it anyway
* to read or to write from it * to read or to write from it
*/ */
result = RegCreateKeyExW (HKEY_CURRENT_USER, path_namew, 0, NULL, 0, result = RegCreateKeyExW (self->base_key, path_namew, 0, NULL, 0,
KEY_WRITE, NULL, &hpath, NULL); KEY_WRITE, NULL, &hpath, NULL);
g_free (path_namew); g_free (path_namew);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
{ {
trace ("Error opening/creating key to check writability: %s.\n", trace ("Error opening/creating key to check writability: %s\\%s.\n",
predefined_key_to_string (self->base_key),
path_name); path_name);
g_free (path_name); g_free (path_name);
@ -1291,7 +1360,7 @@ registry_cache_destroy_tree (GNode *node,
/* One of these is sent down the pipe when something happens in the registry. */ /* One of these is sent down the pipe when something happens in the registry. */
typedef struct typedef struct
{ {
GRegistryBackend *self; GRegistrySettingsBackend *self;
gchar *prefix; /* prefix is a gsettings path, all items are subkeys of this. */ gchar *prefix; /* prefix is a gsettings path, all items are subkeys of this. */
GPtrArray *items; /* each item is a subkey below prefix that has changed. */ GPtrArray *items; /* each item is a subkey below prefix that has changed. */
} RegistryEvent; } RegistryEvent;
@ -1350,7 +1419,7 @@ registry_cache_remove_deleted (GNode *node,
/* Update cache from registry, and optionally report on the changes. /* Update cache from registry, and optionally report on the changes.
* *
* This function is sometimes called from the watch thread, with no locking. It * This function is sometimes called from the watch thread, with no locking. It
* does call g_registry_backend functions, but this is okay because they only * does call g_registry_settings_backend functions, but this is okay because they only
* access self->base which is constant. * access self->base which is constant.
* *
* When looking at this code bear in mind the terminology: in the registry, keys * When looking at this code bear in mind the terminology: in the registry, keys
@ -1362,7 +1431,7 @@ registry_cache_remove_deleted (GNode *node,
* there are notifications that are watching them. * there are notifications that are watching them.
*/ */
static void static void
registry_cache_update (GRegistryBackend *self, registry_cache_update (GRegistrySettingsBackend *self,
HKEY hpath, HKEY hpath,
const gchar *prefix, const gchar *prefix,
const gchar *partial_key_name, const gchar *partial_key_name,
@ -1439,7 +1508,7 @@ registry_cache_update (GRegistryBackend *self,
} }
if (result != ERROR_NO_MORE_ITEMS) if (result != ERROR_NO_MORE_ITEMS)
g_message_win32_error (result, "gregistrybackend: error enumerating subkeys for cache."); g_message_win32_error (result, "gregistrysettingsbackend: error enumerating subkeys for cache.");
/* Enumerate each value at 'path' and check if it has changed */ /* Enumerate each value at 'path' and check if it has changed */
i = 0; i = 0;
@ -1512,7 +1581,7 @@ registry_cache_update (GRegistryBackend *self,
} }
if (result != ERROR_NO_MORE_ITEMS) if (result != ERROR_NO_MORE_ITEMS)
g_message_win32_error (result, "gregistrybackend: error enumerating values for cache"); g_message_win32_error (result, "gregistrysettingsbackend: error enumerating values for cache");
/* Any nodes now left unreadable must have been deleted, remove them from cache */ /* Any nodes now left unreadable must have been deleted, remove them from cache */
g_node_children_foreach (cache_node, G_TRAVERSE_ALL, g_node_children_foreach (cache_node, G_TRAVERSE_ALL,
@ -1583,7 +1652,7 @@ _free_watch (WatchThreadState *self,
if (cache_node != NULL) if (cache_node != NULL)
{ {
//registry_cache_dump (G_REGISTRY_BACKEND (self->owner)->cache_root, NULL); //registry_cache_dump (G_REGISTRY_SETTINGS_BACKEND (self->owner)->cache_root, NULL);
registry_cache_unref_tree (cache_node); registry_cache_unref_tree (cache_node);
} }
@ -1803,14 +1872,14 @@ watch_thread_function (LPVOID parameter)
* likely to block (only when changing notification subscriptions). * likely to block (only when changing notification subscriptions).
*/ */
event = g_slice_new (RegistryEvent); event = g_slice_new (RegistryEvent);
event->self = G_REGISTRY_BACKEND (g_object_ref (self->owner)); event->self = G_REGISTRY_SETTINGS_BACKEND (g_object_ref (self->owner));
event->prefix = g_strdup (prefix); event->prefix = g_strdup (prefix);
event->items = g_ptr_array_new_with_free_func (g_free); event->items = g_ptr_array_new_with_free_func (g_free);
EnterCriticalSection (G_REGISTRY_BACKEND (self->owner)->cache_lock); EnterCriticalSection (G_REGISTRY_SETTINGS_BACKEND (self->owner)->cache_lock);
registry_cache_update (G_REGISTRY_BACKEND (self->owner), hpath, registry_cache_update (G_REGISTRY_SETTINGS_BACKEND (self->owner), hpath,
prefix, NULL, cache_node, 0, event); prefix, NULL, cache_node, 0, event);
LeaveCriticalSection (G_REGISTRY_BACKEND (self->owner)->cache_lock); LeaveCriticalSection (G_REGISTRY_SETTINGS_BACKEND (self->owner)->cache_lock);
if (event->items->len > 0) if (event->items->len > 0)
g_idle_add ((GSourceFunc) watch_handler, event); g_idle_add ((GSourceFunc) watch_handler, event);
@ -1833,7 +1902,7 @@ watch_thread_function (LPVOID parameter)
} }
static gboolean static gboolean
watch_start (GRegistryBackend *self) watch_start (GRegistrySettingsBackend *self)
{ {
WatchThreadState *watch; WatchThreadState *watch;
@ -1850,7 +1919,7 @@ watch_start (GRegistryBackend *self)
watch->message_received_event = CreateEvent (NULL, FALSE, FALSE, NULL); watch->message_received_event = CreateEvent (NULL, FALSE, FALSE, NULL);
if (watch->message_sent_event == NULL || watch->message_received_event == NULL) if (watch->message_sent_event == NULL || watch->message_received_event == NULL)
{ {
g_message_win32_error (GetLastError (), "gregistrybackend: Failed to create sync objects."); g_message_win32_error (GetLastError (), "gregistrysettingsbackend: Failed to create sync objects.");
goto fail; goto fail;
} }
@ -1858,7 +1927,7 @@ watch_start (GRegistryBackend *self)
watch->thread = CreateThread (NULL, 1024, watch_thread_function, watch, 0, NULL); watch->thread = CreateThread (NULL, 1024, watch_thread_function, watch, 0, NULL);
if (watch->thread == NULL) if (watch->thread == NULL)
{ {
g_message_win32_error (GetLastError (), "gregistrybackend: Failed to create notify watch thread."); g_message_win32_error (GetLastError (), "gregistrysettingsbackend: Failed to create notify watch thread.");
goto fail; goto fail;
} }
@ -1880,7 +1949,7 @@ fail:
/* This function assumes you hold the message lock! */ /* This function assumes you hold the message lock! */
static void static void
watch_stop_unlocked (GRegistryBackend *self) watch_stop_unlocked (GRegistrySettingsBackend *self)
{ {
WatchThreadState *watch = self->watch; WatchThreadState *watch = self->watch;
DWORD result; DWORD result;
@ -1897,7 +1966,7 @@ watch_stop_unlocked (GRegistryBackend *self)
result = WaitForSingleObject (watch->message_received_event, INFINITE); result = WaitForSingleObject (watch->message_received_event, INFINITE);
if (result != WAIT_OBJECT_0) if (result != WAIT_OBJECT_0)
{ {
g_warning ("gregistrybackend: unable to stop watch thread."); g_warning ("gregistrysettingsbackend: unable to stop watch thread.");
return; return;
} }
@ -1914,7 +1983,7 @@ watch_stop_unlocked (GRegistryBackend *self)
} }
static gboolean static gboolean
watch_add_notify (GRegistryBackend *self, watch_add_notify (GRegistrySettingsBackend *self,
HANDLE event, HANDLE event,
HKEY hpath, HKEY hpath,
gchar *gsettings_prefix) gchar *gsettings_prefix)
@ -1987,7 +2056,7 @@ watch_add_notify (GRegistryBackend *self,
} }
static void static void
watch_remove_notify (GRegistryBackend *self, watch_remove_notify (GRegistrySettingsBackend *self,
const gchar *key_name) const gchar *key_name)
{ {
WatchThreadState *watch = self->watch; WatchThreadState *watch = self->watch;
@ -2025,10 +2094,10 @@ watch_remove_notify (GRegistryBackend *self,
* key. Our job is easier because keys and values are separate. * key. Our job is easier because keys and values are separate.
*/ */
static void static void
g_registry_backend_subscribe (GSettingsBackend *backend, g_registry_settings_backend_subscribe (GSettingsBackend *backend,
const char *key_name) const char *key_name)
{ {
GRegistryBackend *self = G_REGISTRY_BACKEND (backend); GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (backend);
gchar *path_name; gchar *path_name;
gunichar2 *path_namew; gunichar2 *path_namew;
gchar *value_name = NULL; gchar *value_name = NULL;
@ -2062,13 +2131,13 @@ g_registry_backend_subscribe (GSettingsBackend *backend,
/* Give the caller the benefit of the doubt if the key doesn't exist and create it. The caller /* Give the caller the benefit of the doubt if the key doesn't exist and create it. The caller
* is almost certainly a new g_settings with this path as base path. */ * is almost certainly a new g_settings with this path as base path. */
result = RegCreateKeyExW (HKEY_CURRENT_USER, path_namew, 0, NULL, 0, KEY_READ, NULL, &hpath, result = RegCreateKeyExW (self->base_key, path_namew, 0, NULL, 0, KEY_READ, NULL, &hpath,
NULL); NULL);
g_free (path_namew); g_free (path_namew);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
{ {
g_message_win32_error (result, "gregistrybackend: Unable to subscribe to key %s.", key_name); g_message_win32_error (result, "gregistrysettingsbackend: Unable to subscribe to key %s.", key_name);
g_atomic_int_inc (&self->watch->watches_remaining); g_atomic_int_inc (&self->watch->watches_remaining);
return; return;
} }
@ -2076,7 +2145,7 @@ g_registry_backend_subscribe (GSettingsBackend *backend,
event = CreateEvent (NULL, FALSE, FALSE, NULL); event = CreateEvent (NULL, FALSE, FALSE, NULL);
if (event == NULL) if (event == NULL)
{ {
g_message_win32_error (result, "gregistrybackend: CreateEvent failed."); g_message_win32_error (result, "gregistrysettingsbackend: CreateEvent failed.");
g_atomic_int_inc (&self->watch->watches_remaining); g_atomic_int_inc (&self->watch->watches_remaining);
RegCloseKey (hpath); RegCloseKey (hpath);
return; return;
@ -2093,12 +2162,12 @@ g_registry_backend_subscribe (GSettingsBackend *backend,
} }
static void static void
g_registry_backend_unsubscribe (GSettingsBackend *backend, g_registry_settings_backend_unsubscribe (GSettingsBackend *backend,
const char *key_name) const char *key_name)
{ {
trace ("unsubscribe: %s.\n", key_name); trace ("unsubscribe: %s.\n", key_name);
watch_remove_notify (G_REGISTRY_BACKEND (backend), key_name); watch_remove_notify (G_REGISTRY_SETTINGS_BACKEND (backend), key_name);
} }
/******************************************************************************** /********************************************************************************
@ -2106,9 +2175,9 @@ g_registry_backend_unsubscribe (GSettingsBackend *backend,
********************************************************************************/ ********************************************************************************/
static void static void
g_registry_backend_finalize (GObject *object) g_registry_settings_backend_finalize (GObject *object)
{ {
GRegistryBackend *self = G_REGISTRY_BACKEND (object); GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (object);
RegistryCacheItem *item; RegistryCacheItem *item;
item = self->cache_root->data; item = self->cache_root->data;
@ -2131,30 +2200,137 @@ g_registry_backend_finalize (GObject *object)
} }
static void static void
g_registry_backend_class_init (GRegistryBackendClass *class) g_registry_settings_backend_constructed (GObject *object)
{
GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (object);
if (self->base_key == NULL || self->base_path == NULL || self->base_pathw == NULL)
{
self->base_key = HKEY_CURRENT_USER;
self->base_path = g_strdup ("Software\\GSettings");
self->base_pathw = g_utf8_to_utf16 (self->base_path, -1, NULL, NULL, NULL);
}
}
static void
g_registry_settings_backend_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (object);
switch ((GRegistrySettingsBackendProperty) prop_id)
{
case PROP_REGISTRY_KEY:
g_value_take_string (value,
g_strdup_printf ("%s\\%s",
predefined_key_to_string (self->base_key),
self->base_path));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_registry_settings_backend_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GRegistrySettingsBackend *self = G_REGISTRY_SETTINGS_BACKEND (object);
const gchar *reg_path;
switch ((GRegistrySettingsBackendProperty) prop_id)
{
case PROP_REGISTRY_KEY:
/* Construct only. */
g_assert (self->base_key == NULL);
g_assert (self->base_path == NULL);
g_assert (self->base_pathw == NULL);
reg_path = g_value_get_string (value);
if (reg_path != NULL)
{
HKEY base_key;
const gchar *base_path = split_registry_path (reg_path, &base_key);
if (base_path != NULL)
{
gunichar2 *base_pathw = g_utf8_to_utf16 (base_path, -1, NULL, NULL, NULL);
if (base_pathw != NULL)
{
self->base_key = g_steal_pointer (&base_key);
self->base_path = g_strdup (base_path);
self->base_pathw = g_steal_pointer (&base_pathw);
}
else
g_warning ("gregistrysettingsbackend: invalid base registry path '%s'", reg_path);
}
else
g_warning ("gregistrysettingsbackend: base registry path '%s' does not start with a valid root key", reg_path);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_registry_settings_backend_class_init (GRegistrySettingsBackendClass *class)
{ {
GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class); GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class); GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = g_registry_backend_finalize; object_class->finalize = g_registry_settings_backend_finalize;
object_class->constructed = g_registry_settings_backend_constructed;
object_class->get_property = g_registry_settings_backend_get_property;
object_class->set_property = g_registry_settings_backend_set_property;
backend_class->read = g_registry_backend_read; backend_class->read = g_registry_settings_backend_read;
backend_class->write = g_registry_backend_write; backend_class->write = g_registry_settings_backend_write;
backend_class->write_tree = g_registry_backend_write_tree; backend_class->write_tree = g_registry_settings_backend_write_tree;
backend_class->reset = g_registry_backend_reset; backend_class->reset = g_registry_settings_backend_reset;
backend_class->get_writable = g_registry_backend_get_writable; backend_class->get_writable = g_registry_settings_backend_get_writable;
backend_class->subscribe = g_registry_backend_subscribe; backend_class->subscribe = g_registry_settings_backend_subscribe;
backend_class->unsubscribe = g_registry_backend_unsubscribe; backend_class->unsubscribe = g_registry_settings_backend_unsubscribe;
/**
* GRegistrySettingsBackend:registry-key:
*
* The location where settings are stored in the registry. Must
* start with one of the following:
* - `HKEY_CLASSES_ROOT`
* - `HKEY_CURRENT_CONFIG`
* - `HKEY_CURRENT_USER`
* - `HKEY_LOCAL_MACHINE`
* - `HKEY_USERS`
*
* Defaults to `HKEY_CURRENT_USER\Software\GSettings`.
*
* Since: 2.78
*/
g_object_class_install_property (object_class,
PROP_REGISTRY_KEY,
g_param_spec_string ("registry-key",
P_("Root registry key"),
P_("The path to the registry key where settings are stored"),
"HKEY_CURRENT_USER\\Software\\GSettings",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
} }
static void static void
g_registry_backend_init (GRegistryBackend *self) g_registry_settings_backend_init (GRegistrySettingsBackend *self)
{ {
RegistryCacheItem *item; RegistryCacheItem *item;
self->base_path = g_strdup_printf ("Software\\GSettings");
self->base_pathw = g_utf8_to_utf16 (self->base_path, -1, NULL, NULL, NULL);
item = g_slice_new (RegistryCacheItem); item = g_slice_new (RegistryCacheItem);
item->value.type = REG_NONE; item->value.type = REG_NONE;
item->value.ptr = NULL; item->value.ptr = NULL;
@ -2167,3 +2343,25 @@ g_registry_backend_init (GRegistryBackend *self)
self->watch = NULL; self->watch = NULL;
} }
/**
* g_registry_settings_backend_new:
* @registry_key: (nullable): the path to the registry key where
* settings are stored, or %NULL.
*
* If @registry_key is %NULL then the default path
* `HKEY_CURRENT_USER\Software\GSettings` is used.
*
* Returns: (transfer full): a registry-backed #GSettingsBackend
*
* Since: 2.78
**/
GSettingsBackend *
g_registry_settings_backend_new (const gchar *registry_key)
{
g_return_val_if_fail (registry_key == NULL || split_registry_path (registry_key, NULL), NULL);
return G_SETTINGS_BACKEND (g_object_new (G_TYPE_REGISTRY_SETTINGS_BACKEND,
"registry-key", registry_key,
NULL));
}

View File

@ -24,7 +24,9 @@
#include <glib-object.h> #include <glib-object.h>
GType g_registry_backend_get_type (void); #include <gio/gsettingsbackend.h>
GIO_AVAILABLE_IN_2_78
GSettingsBackend * g_registry_settings_backend_new (const gchar *registry_key);
#endif /* __G_REGISTRY_SETTINGS_BACKEND_H__ */ #endif /* __G_REGISTRY_SETTINGS_BACKEND_H__ */

View File

@ -95,4 +95,8 @@ GType g_keyfile_settings_backend_get_type (void);
GType g_nextstep_settings_backend_get_type (void); GType g_nextstep_settings_backend_get_type (void);
#endif #endif
#ifdef G_OS_WIN32
GType g_registry_settings_backend_get_type (void);
#endif
#endif /* __G_SETTINGS_BACKEND_INTERNAL_H__ */ #endif /* __G_SETTINGS_BACKEND_INTERNAL_H__ */

View File

@ -276,10 +276,6 @@ settings_sources = files(
'gsettings.c', 'gsettings.c',
) )
if host_system == 'windows'
settings_sources += files('gregistrysettingsbackend.c')
endif
application_headers = files( application_headers = files(
'gapplication.h', 'gapplication.h',
'gapplicationcommandline.h', 'gapplicationcommandline.h',
@ -444,6 +440,7 @@ else
win32_sources += files( win32_sources += files(
'gmemorymonitorwin32.c', 'gmemorymonitorwin32.c',
'gregistrysettingsbackend.c',
'gwin32registrykey.c', 'gwin32registrykey.c',
'gwin32mount.c', 'gwin32mount.c',
'gwin32volumemonitor.c', 'gwin32volumemonitor.c',
@ -467,6 +464,7 @@ else
win32_sources += [gio_win_res] win32_sources += [gio_win_res]
gio_win32_include_headers = files( gio_win32_include_headers = files(
'gregistrysettingsbackend.h',
'gwin32inputstream.h', 'gwin32inputstream.h',
'gwin32outputstream.h', 'gwin32outputstream.h',
) )