[libadwaita/wip/exalm/dark: 2/9] Add AdwSettings




commit e2894309b30511444764025e4770a61c034cd397
Author: Alexander Mikhaylenko <exalm7659 gmail com>
Date:   Thu Aug 26 14:10:29 2021 +0500

    Add AdwSettings

 src/adw-enums-private.c.in |   1 +
 src/adw-settings-private.h |  37 ++++
 src/adw-settings.c         | 484 +++++++++++++++++++++++++++++++++++++++++++++
 src/meson.build            |   2 +
 4 files changed, 524 insertions(+)
---
diff --git a/src/adw-enums-private.c.in b/src/adw-enums-private.c.in
index 097ec60e..4aa8b6db 100644
--- a/src/adw-enums-private.c.in
+++ b/src/adw-enums-private.c.in
@@ -3,6 +3,7 @@
 #include "config.h"
 #include "adw-enums-private.h"
 #include "adw-animation-private.h"
+#include "adw-settings-private.h"
 
 /*** END file-header ***/
 
diff --git a/src/adw-settings-private.h b/src/adw-settings-private.h
new file mode 100644
index 00000000..7533c93e
--- /dev/null
+++ b/src/adw-settings-private.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+#include "adw-enums-private.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+  ADW_SYSTEM_COLOR_SCHEME_DEFAULT,
+  ADW_SYSTEM_COLOR_SCHEME_PREFER_DARK,
+  ADW_SYSTEM_COLOR_SCHEME_PREFER_LIGHT,
+} AdwSystemColorScheme;
+
+#define ADW_TYPE_SETTINGS (adw_settings_get_type())
+
+G_DECLARE_FINAL_TYPE (AdwSettings, adw_settings, ADW, SETTINGS, GObject)
+
+AdwSettings *adw_settings_get_default (void);
+
+gboolean             adw_settings_has_color_scheme (AdwSettings *self);
+AdwSystemColorScheme adw_settings_get_color_scheme (AdwSettings *self);
+
+gboolean adw_settings_get_high_contrast (AdwSettings *self);
+
+G_END_DECLS
diff --git a/src/adw-settings.c b/src/adw-settings.c
new file mode 100644
index 00000000..c8b3014b
--- /dev/null
+++ b/src/adw-settings.c
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#include "config.h"
+
+#include "adw-settings-private.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#define PORTAL_BUS_NAME "org.freedesktop.portal.Desktop"
+#define PORTAL_OBJECT_PATH "/org/freedesktop/portal/desktop"
+#define PORTAL_SETTINGS_INTERFACE "org.freedesktop.portal.Settings"
+
+#define PORTAL_ERROR_NOT_FOUND "org.freedesktop.portal.Error.NotFound"
+
+struct _AdwSettings
+{
+  GObject parent_instance;
+
+  GDBusProxy *settings_portal;
+  GSettings *interface_settings;
+  GSettings *a11y_settings;
+
+  AdwSystemColorScheme color_scheme;
+  gboolean high_contrast;
+
+  gboolean has_high_contrast;
+  gboolean has_color_scheme;
+  gboolean color_scheme_use_fdo_setting;
+};
+
+G_DEFINE_TYPE (AdwSettings, adw_settings, G_TYPE_OBJECT);
+
+enum {
+  PROP_0,
+  PROP_COLOR_SCHEME,
+  PROP_HIGH_CONTRAST,
+  LAST_PROP,
+};
+
+static GParamSpec *props[LAST_PROP];
+
+static AdwSettings *default_instance;
+
+static void
+set_color_scheme (AdwSettings          *self,
+                  AdwSystemColorScheme  color_scheme)
+{
+  if (color_scheme == self->color_scheme)
+    return;
+
+  self->color_scheme = color_scheme;
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_COLOR_SCHEME]);
+}
+
+static void
+set_high_contrast (AdwSettings *self,
+                   gboolean     high_contrast)
+{
+  if (high_contrast == self->high_contrast)
+    return;
+
+  self->high_contrast = high_contrast;
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_HIGH_CONTRAST]);
+}
+
+/* Settings portal */
+
+static gboolean
+get_disable_portal (void)
+{
+  const char *disable_portal = g_getenv ("ADW_DISABLE_PORTAL");
+
+  return disable_portal && disable_portal[0] == '1';
+}
+
+static gboolean
+read_portal_setting (AdwSettings  *self,
+                     const char   *schema,
+                     const char   *name,
+                     const char   *type,
+                     GVariant    **out)
+{
+  g_autoptr (GError) error = NULL;
+  g_autoptr (GVariant) ret = NULL;
+  g_autoptr (GVariant) child = NULL;
+  g_autoptr (GVariant) child2 = NULL;
+  g_autoptr (GVariantType) out_type = NULL;
+
+  ret = g_dbus_proxy_call_sync (self->settings_portal,
+                                "Read",
+                                g_variant_new ("(ss)", schema, name),
+                                G_DBUS_CALL_FLAGS_NONE,
+                                G_MAXINT,
+                                NULL,
+                                &error);
+  if (error) {
+    if (error->domain == G_DBUS_ERROR &&
+        error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) {
+      g_debug ("Portal not found: %s", error->message);
+
+      return FALSE;
+    }
+
+    if (g_dbus_error_is_remote_error (error)) {
+      g_autofree char *remote_error = g_dbus_error_get_remote_error (error);
+
+      if (!g_strcmp0 (remote_error, PORTAL_ERROR_NOT_FOUND)) {
+        g_debug ("Setting %s.%s of type %s not found", schema, name, type);
+
+        return FALSE;
+      }
+    }
+
+    g_critical ("Couldn't read the %s setting: %s", name, error->message);
+
+    return FALSE;
+  }
+
+  g_variant_get (ret, "(v)", &child);
+  g_variant_get (child, "v", &child2);
+
+  out_type = g_variant_type_new (type);
+  if (!g_variant_type_equal (g_variant_get_type (child2), out_type)) {
+    g_critical ("Invalid type for %s.%s: expected %s, got %s",
+                schema, name, type, g_variant_get_type_string (child2));
+
+    return FALSE;
+  }
+
+  *out = g_steal_pointer (&child2);
+
+  return TRUE;
+}
+
+static AdwSystemColorScheme
+get_fdo_color_scheme (GVariant *variant)
+{
+  guint32 color_scheme = g_variant_get_uint32 (variant);
+
+  if (color_scheme > ADW_SYSTEM_COLOR_SCHEME_PREFER_LIGHT) {
+    g_warning ("Invalid color scheme: %u", color_scheme);
+
+    color_scheme = ADW_SYSTEM_COLOR_SCHEME_DEFAULT;
+  }
+
+  return color_scheme;
+}
+
+static AdwSystemColorScheme
+get_gnome_color_scheme (GVariant *variant)
+{
+  const char *str = g_variant_get_string (variant, NULL);
+
+  if (!g_strcmp0 (str, "default"))
+    return ADW_SYSTEM_COLOR_SCHEME_DEFAULT;
+
+  if (!g_strcmp0 (str, "prefer-dark"))
+    return ADW_SYSTEM_COLOR_SCHEME_PREFER_DARK;
+
+  if (!g_strcmp0 (str, "prefer-light"))
+    return ADW_SYSTEM_COLOR_SCHEME_PREFER_LIGHT;
+
+  g_warning ("Invalid color scheme: %s", str);
+
+  return ADW_SYSTEM_COLOR_SCHEME_DEFAULT;
+}
+
+static void
+settings_portal_changed_cb (GDBusProxy  *proxy,
+                            const char  *sender_name,
+                            const char  *signal_name,
+                            GVariant    *parameters,
+                            AdwSettings *self)
+{
+  const char *namespace;
+  const char *name;
+  g_autoptr (GVariant) value = NULL;
+
+  if (g_strcmp0 (signal_name, "SettingChanged"))
+    return;
+
+  g_variant_get (parameters, "(&s&sv)", &namespace, &name, &value);
+
+  if (!g_strcmp0 (namespace, "org.freedesktop.appearance") &&
+      !g_strcmp0 (name, "color-scheme") &&
+      self->color_scheme_use_fdo_setting) {
+    set_color_scheme (self, get_fdo_color_scheme (value));
+
+    return;
+  }
+
+  if (!g_strcmp0 (namespace, "org.gnome.desktop.interface") &&
+      !g_strcmp0 (name, "color-scheme") &&
+      !self->color_scheme_use_fdo_setting) {
+    set_color_scheme (self, get_gnome_color_scheme (value));
+
+    return;
+  }
+
+  if (!g_strcmp0 (namespace, "org.gnome.desktop.interface.a11y") &&
+      !g_strcmp0 (name, "high-contrast")) {
+    set_high_contrast (self, g_variant_get_boolean (value));
+
+    return;
+  }
+}
+
+static void
+init_portal (AdwSettings *self)
+{
+  g_autoptr (GError) error = NULL;
+  g_autoptr (GVariant) color_scheme_variant = NULL;
+  g_autoptr (GVariant) high_contrast_variant = NULL;
+
+  if (get_disable_portal ())
+    return;
+
+  self->settings_portal = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                                         G_DBUS_PROXY_FLAGS_NONE,
+                                                         NULL,
+                                                         PORTAL_BUS_NAME,
+                                                         PORTAL_OBJECT_PATH,
+                                                         PORTAL_SETTINGS_INTERFACE,
+                                                         NULL,
+                                                         &error);
+  if (error) {
+    g_debug ("Settings portal not found: %s", error->message);
+
+    return;
+  }
+
+  if (read_portal_setting (self, "org.freedesktop.appearance",
+                           "color-scheme", "u", &color_scheme_variant)) {
+    self->has_color_scheme = TRUE;
+    self->color_scheme_use_fdo_setting = TRUE;
+    self->color_scheme = get_fdo_color_scheme (color_scheme_variant);
+  }
+
+  if (!self->has_color_scheme &&
+      read_portal_setting (self, "org.gnome.desktop.interface",
+                           "color-scheme", "s", &color_scheme_variant)) {
+    self->has_color_scheme = TRUE;
+    self->color_scheme = get_gnome_color_scheme (color_scheme_variant);
+  }
+
+  if (read_portal_setting (self, "org.gnome.desktop.interface.a11y",
+                           "high-contrast", "b", &high_contrast_variant)) {
+    self->has_high_contrast = TRUE;
+    self->high_contrast = g_variant_get_boolean (high_contrast_variant);
+  }
+
+  if (!self->has_color_scheme && !self->has_high_contrast)
+    return;
+
+  g_signal_connect (self->settings_portal, "g-signal",
+                    G_CALLBACK (settings_portal_changed_cb), self);
+}
+
+/* GSettings */
+
+static gboolean
+is_running_in_flatpak (void)
+{
+  return g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS);
+}
+
+static void
+gsettings_color_scheme_changed_cb (AdwSettings *self)
+{
+  set_color_scheme (self, g_settings_get_enum (self->interface_settings, "color-scheme"));
+}
+
+static void
+gsettings_high_contrast_changed_cb (AdwSettings *self)
+{
+  set_high_contrast (self, g_settings_get_boolean (self->a11y_settings, "high-contrast"));
+}
+
+static void
+init_gsettings (AdwSettings *self)
+{
+  GSettingsSchema *schema;
+
+  /* While we can access gsettings in flatpak, we can't do anything useful with
+   * them as they aren't propagated from the system. */
+  if (is_running_in_flatpak ())
+    return;
+
+  self->interface_settings = g_settings_new ("org.gnome.desktop.interface");
+  g_object_get (self->interface_settings, "settings-schema", &schema, NULL);
+
+  if (!self->has_color_scheme &&
+      g_settings_schema_has_key (schema, "color-scheme")) {
+    self->has_color_scheme = TRUE;
+    self->color_scheme = g_settings_get_enum (self->interface_settings, "color-scheme");
+
+    g_signal_connect_swapped (self->interface_settings,
+                              "changed::color-scheme",
+                              G_CALLBACK (gsettings_color_scheme_changed_cb),
+                              self);
+  }
+
+  self->a11y_settings = g_settings_new ("org.gnome.desktop.a11y.interface");
+  g_object_get (self->a11y_settings, "settings-schema", &schema, NULL);
+
+  if (!self->has_high_contrast &&
+      g_settings_schema_has_key (schema, "high-contrast")) {
+    self->has_high_contrast = TRUE;
+    self->high_contrast = g_settings_get_boolean (self->a11y_settings, "high-contrast");
+
+    g_signal_connect_swapped (self->a11y_settings,
+                              "changed::high-contrast",
+                              G_CALLBACK (gsettings_high_contrast_changed_cb),
+                              self);
+  }
+}
+
+/* Legacy */
+
+static gboolean
+is_theme_high_contrast (GdkDisplay *display)
+{
+  g_auto (GValue) value = G_VALUE_INIT;
+  const char *theme_name;
+
+  g_value_init (&value, G_TYPE_STRING);
+  if (!gdk_display_get_setting (display, "gtk-theme-name", &value))
+    return FALSE;
+
+  theme_name = g_value_get_string (&value);
+
+  return !g_strcmp0 (theme_name, "HighContrast") ||
+         !g_strcmp0 (theme_name, "HighContrastInverse");
+}
+
+static void
+display_setting_changed_cb (AdwSettings *self,
+                            const char  *setting,
+                            GdkDisplay  *display)
+{
+  if (!g_strcmp0 (setting, "gtk-theme-name"))
+    set_high_contrast (self, is_theme_high_contrast (display));
+}
+
+static void
+init_legacy (AdwSettings *self)
+{
+  GdkDisplay *display = gdk_display_get_default ();
+
+  if (!display)
+    return;
+
+  self->has_high_contrast = TRUE;
+  self->high_contrast = is_theme_high_contrast (display);
+
+  g_signal_connect_swapped (display,
+                            "setting-changed",
+                            G_CALLBACK (display_setting_changed_cb),
+                            self);
+}
+
+static void
+adw_settings_constructed (GObject *object)
+{
+  AdwSettings *self = ADW_SETTINGS (object);
+
+  G_OBJECT_CLASS (adw_settings_parent_class)->constructed (object);
+
+  init_portal (self);
+
+  if (!self->has_color_scheme || !self->has_high_contrast)
+    init_gsettings (self);
+
+  if (!self->has_high_contrast)
+    init_legacy (self);
+}
+
+static void
+adw_settings_dispose (GObject *object)
+{
+  AdwSettings *self = ADW_SETTINGS (object);
+
+  g_clear_object (&self->settings_portal);
+  g_clear_object (&self->interface_settings);
+  g_clear_object (&self->a11y_settings);
+
+  G_OBJECT_CLASS (adw_settings_parent_class)->dispose (object);
+}
+
+static void
+adw_settings_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  AdwSettings *self = ADW_SETTINGS (object);
+
+  switch (prop_id) {
+  case PROP_COLOR_SCHEME:
+    g_value_set_enum (value, adw_settings_get_color_scheme (self));
+    break;
+
+  case PROP_HIGH_CONTRAST:
+    g_value_set_boolean (value, adw_settings_get_high_contrast (self));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+adw_settings_class_init (AdwSettingsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = adw_settings_constructed;
+  object_class->dispose = adw_settings_dispose;
+  object_class->get_property = adw_settings_get_property;
+
+  props[PROP_COLOR_SCHEME] =
+    g_param_spec_enum ("color-scheme",
+                       "Color Scheme",
+                       "Color Scheme",
+                       ADW_TYPE_SYSTEM_COLOR_SCHEME,
+                       ADW_SYSTEM_COLOR_SCHEME_DEFAULT,
+                       G_PARAM_READABLE);
+
+  props[PROP_HIGH_CONTRAST] =
+    g_param_spec_boolean ("high-contrast",
+                          "High Contrast",
+                          "High Contrast",
+                          FALSE,
+                          G_PARAM_READABLE);
+
+  g_object_class_install_properties (object_class, LAST_PROP, props);
+}
+
+static void
+adw_settings_init (AdwSettings *self)
+{
+}
+
+AdwSettings *
+adw_settings_get_default (void)
+{
+  if (!default_instance)
+    default_instance = g_object_new (ADW_TYPE_SETTINGS, NULL);
+
+  return default_instance;
+}
+
+gboolean
+adw_settings_has_color_scheme (AdwSettings *self)
+{
+  g_return_val_if_fail (ADW_IS_SETTINGS (self), FALSE);
+
+  return self->has_color_scheme;
+}
+
+AdwSystemColorScheme
+adw_settings_get_color_scheme (AdwSettings *self)
+{
+  g_return_val_if_fail (ADW_IS_SETTINGS (self), ADW_SYSTEM_COLOR_SCHEME_DEFAULT);
+
+  return self->color_scheme;
+}
+
+gboolean
+adw_settings_get_high_contrast (AdwSettings *self)
+{
+  g_return_val_if_fail (ADW_IS_SETTINGS (self), FALSE);
+
+  return self->high_contrast;
+}
diff --git a/src/meson.build b/src/meson.build
index 9feba8bf..3b44f905 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -22,6 +22,7 @@ adw_public_enum_headers = [
 
 adw_private_enum_headers = [
   'adw-animation-private.h',
+  'adw-settings-private.h',
 ]
 
 version_data = configuration_data()
@@ -153,6 +154,7 @@ src_sources = [
   'adw-preferences-page.c',
   'adw-preferences-row.c',
   'adw-preferences-window.c',
+  'adw-settings.c',
   'adw-shadow-helper.c',
   'adw-split-button.c',
   'adw-squeezer.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]