[libhandy/wip/exalm/dark: 2/6] Add HdySettings




commit 1f7c803340d39fecbd07aeb93326165aad08c10c
Author: Alexander Mikhaylenko <exalm7659 gmail com>
Date:   Thu Aug 26 15:48:36 2021 +0500

    Add HdySettings

 doc/meson.build            |   1 +
 src/hdy-enums-private.c.in |   1 +
 src/hdy-settings-private.h |  37 ++++
 src/hdy-settings.c         | 484 +++++++++++++++++++++++++++++++++++++++++++++
 src/meson.build            |   2 +
 5 files changed, 525 insertions(+)
---
diff --git a/doc/meson.build b/doc/meson.build
index e8dcf1a6..6a15b230 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -19,6 +19,7 @@ private_headers = [
     'hdy-keypad-button-private.h',
     'hdy-preferences-group-private.h',
     'hdy-preferences-page-private.h',
+    'hdy-settings-private.h',
     'hdy-shadow-helper-private.h',
     'hdy-stackable-box-private.h',
     'hdy-swipe-tracker-private.h',
diff --git a/src/hdy-enums-private.c.in b/src/hdy-enums-private.c.in
index 2cf8f8b3..2f27d812 100644
--- a/src/hdy-enums-private.c.in
+++ b/src/hdy-enums-private.c.in
@@ -3,6 +3,7 @@
 #include "config.h"
 #include "hdy-enums-private.h"
 #include "hdy-stackable-box-private.h"
+#include "hdy-settings-private.h"
 
 /*** END file-header ***/
 
diff --git a/src/hdy-settings-private.h b/src/hdy-settings-private.h
new file mode 100644
index 00000000..643fa963
--- /dev/null
+++ b/src/hdy-settings-private.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#pragma once
+
+#if !defined(_HANDY_INSIDE) && !defined(HANDY_COMPILATION)
+#error "Only <handy.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+#include "hdy-enums-private.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+  HDY_SYSTEM_COLOR_SCHEME_DEFAULT,
+  HDY_SYSTEM_COLOR_SCHEME_PREFER_DARK,
+  HDY_SYSTEM_COLOR_SCHEME_PREFER_LIGHT,
+} HdySystemColorScheme;
+
+#define HDY_TYPE_SETTINGS (hdy_settings_get_type())
+
+G_DECLARE_FINAL_TYPE (HdySettings, hdy_settings, HDY, SETTINGS, GObject)
+
+HdySettings *hdy_settings_get_default (void);
+
+gboolean             hdy_settings_has_color_scheme (HdySettings *self);
+HdySystemColorScheme hdy_settings_get_color_scheme (HdySettings *self);
+
+gboolean hdy_settings_get_high_contrast (HdySettings *self);
+
+G_END_DECLS
diff --git a/src/hdy-settings.c b/src/hdy-settings.c
new file mode 100644
index 00000000..2d69c204
--- /dev/null
+++ b/src/hdy-settings.c
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#include "config.h"
+
+#include "hdy-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 _HdySettings
+{
+  GObject parent_instance;
+
+  GDBusProxy *settings_portal;
+  GSettings *settings;
+
+  HdySystemColorScheme color_scheme;
+  gboolean high_contrast;
+
+  gboolean has_high_contrast;
+  gboolean has_color_scheme;
+  gboolean color_scheme_use_fdo_setting;
+};
+
+G_DEFINE_TYPE (HdySettings, hdy_settings, G_TYPE_OBJECT);
+
+enum {
+  PROP_0,
+  PROP_COLOR_SCHEME,
+  PROP_HIGH_CONTRAST,
+  LAST_PROP,
+};
+
+static GParamSpec *props[LAST_PROP];
+
+static HdySettings *default_instance;
+
+static void
+set_color_scheme (HdySettings          *self,
+                  HdySystemColorScheme  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 (HdySettings *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 ("HDY_DISABLE_PORTAL");
+
+  return disable_portal && disable_portal[0] == '1';
+}
+
+static gboolean
+read_portal_setting (HdySettings  *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 HdySystemColorScheme
+get_fdo_color_scheme (GVariant *variant)
+{
+  guint32 color_scheme = g_variant_get_uint32 (variant);
+
+  if (color_scheme > HDY_SYSTEM_COLOR_SCHEME_PREFER_LIGHT) {
+    g_warning ("Invalid color scheme: %u", color_scheme);
+
+    color_scheme = HDY_SYSTEM_COLOR_SCHEME_DEFAULT;
+  }
+
+  return color_scheme;
+}
+
+static HdySystemColorScheme
+get_gnome_color_scheme (GVariant *variant)
+{
+  const char *str = g_variant_get_string (variant, NULL);
+
+  if (!g_strcmp0 (str, "default"))
+    return HDY_SYSTEM_COLOR_SCHEME_DEFAULT;
+
+  if (!g_strcmp0 (str, "prefer-dark"))
+    return HDY_SYSTEM_COLOR_SCHEME_PREFER_DARK;
+
+  if (!g_strcmp0 (str, "prefer-light"))
+    return HDY_SYSTEM_COLOR_SCHEME_PREFER_LIGHT;
+
+  g_warning ("Invalid color scheme: %s", str);
+
+  return HDY_SYSTEM_COLOR_SCHEME_DEFAULT;
+}
+
+static void
+settings_portal_changed_cb (GDBusProxy  *proxy,
+                            const char  *sender_name,
+                            const char  *signal_name,
+                            GVariant    *parameters,
+                            HdySettings *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) {
+    self->color_scheme = get_gnome_color_scheme (value);
+
+    return;
+  }
+
+  if (!g_strcmp0 (namespace, "org.gnome.desktop.interface") &&
+      !g_strcmp0 (name, "high-contrast")) {
+    set_high_contrast (self, g_variant_get_boolean (value));
+
+    return;
+  }
+}
+
+static void
+init_portal (HdySettings *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",
+                           "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 (HdySettings *self)
+{
+  set_color_scheme (self, g_settings_get_enum (self->settings, "color-scheme"));
+}
+
+static void
+gsettings_high_contrast_changed_cb (HdySettings *self)
+{
+  set_high_contrast (self, g_settings_get_boolean (self->settings, "high-contrast"));
+}
+
+static void
+init_gsettings (HdySettings *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->settings = g_settings_new ("org.gnome.desktop.interface");
+
+  g_object_get (self->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->settings, "color-scheme");
+
+    g_signal_connect_swapped (self->settings,
+                              "changed::color-scheme",
+                              G_CALLBACK (gsettings_color_scheme_changed_cb),
+                              self);
+  }
+
+  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->settings, "high-contrast");
+
+    g_signal_connect_swapped (self->settings,
+                              "changed::high-contrast",
+                              G_CALLBACK (gsettings_high_contrast_changed_cb),
+                              self);
+  }
+}
+
+/* Legacy */
+
+static gboolean
+is_theme_high_contrast (void)
+{
+  const char *icon_theme_name;
+
+  /* We're using icon theme here as we control gtk-theme-name and it
+   * interferes with the notifications. */
+  g_object_get (gtk_settings_get_default (),
+                "gtk-icon-theme-name", &icon_theme_name,
+                NULL);
+
+  return !g_strcmp0 (icon_theme_name, "HighContrast") ||
+         !g_strcmp0 (icon_theme_name, "HighContrastInverse");
+}
+
+static void
+theme_name_changed_cb (HdySettings *self)
+{
+  set_high_contrast (self, is_theme_high_contrast ());
+}
+
+static void
+init_legacy (HdySettings *self)
+{
+  GdkDisplay *display = gdk_display_get_default ();
+  GdkScreen *screen;
+
+  if (!display)
+    return;
+
+  screen = gdk_display_get_default_screen (display);
+
+  if (!screen)
+    return;
+
+  self->has_high_contrast = TRUE;
+  self->high_contrast = is_theme_high_contrast ();
+
+  g_signal_connect_swapped (gtk_settings_get_default (),
+                            "notify::gtk-icon-theme-name",
+                            G_CALLBACK (theme_name_changed_cb),
+                            self);
+}
+
+static void
+hdy_settings_constructed (GObject *object)
+{
+  HdySettings *self = HDY_SETTINGS (object);
+
+  G_OBJECT_CLASS (hdy_settings_parent_class)->constructed (object);
+
+  g_debug ("Trying to initialize portal");
+
+  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
+hdy_settings_dispose (GObject *object)
+{
+  HdySettings *self = HDY_SETTINGS (object);
+
+  g_clear_object (&self->settings_portal);
+  g_clear_object (&self->settings);
+
+  G_OBJECT_CLASS (hdy_settings_parent_class)->dispose (object);
+}
+
+static void
+hdy_settings_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  HdySettings *self = HDY_SETTINGS (object);
+
+  switch (prop_id) {
+  case PROP_COLOR_SCHEME:
+    g_value_set_enum (value, hdy_settings_get_color_scheme (self));
+    break;
+
+  case PROP_HIGH_CONTRAST:
+    g_value_set_boolean (value, hdy_settings_get_high_contrast (self));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+hdy_settings_class_init (HdySettingsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = hdy_settings_constructed;
+  object_class->dispose = hdy_settings_dispose;
+  object_class->get_property = hdy_settings_get_property;
+
+  props[PROP_COLOR_SCHEME] =
+    g_param_spec_enum ("color-scheme",
+                       "Color Scheme",
+                       "Color Scheme",
+                       HDY_TYPE_SYSTEM_COLOR_SCHEME,
+                       HDY_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
+hdy_settings_init (HdySettings *self)
+{
+}
+
+HdySettings *
+hdy_settings_get_default (void)
+{
+  if (!default_instance)
+    default_instance = g_object_new (HDY_TYPE_SETTINGS, NULL);
+
+  return default_instance;
+}
+
+gboolean
+hdy_settings_has_color_scheme (HdySettings *self)
+{
+  g_return_val_if_fail (HDY_IS_SETTINGS (self), FALSE);
+
+  return self->has_color_scheme;
+}
+
+HdySystemColorScheme
+hdy_settings_get_color_scheme (HdySettings *self)
+{
+  g_return_val_if_fail (HDY_IS_SETTINGS (self), HDY_SYSTEM_COLOR_SCHEME_DEFAULT);
+
+  return self->color_scheme;
+}
+
+gboolean
+hdy_settings_get_high_contrast (HdySettings *self)
+{
+  g_return_val_if_fail (HDY_IS_SETTINGS (self), FALSE);
+
+  return self->high_contrast;
+}
diff --git a/src/meson.build b/src/meson.build
index c201f34e..cfea194c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -21,6 +21,7 @@ hdy_public_enum_headers = [
 
 hdy_private_enum_headers = [
   'hdy-stackable-box-private.h',
+  'hdy-settings-private.h',
 ]
 
 version_data = configuration_data()
@@ -151,6 +152,7 @@ src_sources = [
   'hdy-preferences-row.c',
   'hdy-preferences-window.c',
   'hdy-search-bar.c',
+  'hdy-settings.c',
   'hdy-shadow-helper.c',
   'hdy-squeezer.c',
   'hdy-stackable-box.c',


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