From 8b37e81374928856d8fd859b95a62c8bf4211901 Mon Sep 17 00:00:00 2001
From: Tom Englund <tomenglund26@gmail.com>
Date: Fri, 9 Aug 2024 19:33:20 +0200
Subject: [PATCH] cursormgr: add a new setting to sync gsettings (#7253)

cursor:sync_gsettings_theme is set to default true and if enabled it
will now sync xcursor theme loading with gsettings if it can, meaning
CSD clients will now also change to the appropiate theme upon start and
hyprctl setcursor THEME SIZE .
---
 CMakeLists.txt                  |  1 +
 meson.build                     |  2 ++
 src/config/ConfigManager.cpp    |  1 +
 src/managers/XCursorManager.cpp | 59 +++++++++++++++++++++++++++++++++
 src/managers/XCursorManager.hpp |  1 +
 src/meson.build                 |  1 +
 6 files changed, 65 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index cfbd431f5..d8c45bbe1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -117,6 +117,7 @@ pkg_check_modules(
   libliftoff
   libudev
   gbm
+  gio-2.0
   hyprlang>=0.3.2
   hyprcursor>=0.1.7
   hyprutils>=0.2.1)
diff --git a/meson.build b/meson.build
index e8cd25b4c..6a9b7ac58 100644
--- a/meson.build
+++ b/meson.build
@@ -34,6 +34,8 @@ xcb_render_dep = dependency('xcb-render', required: get_option('xwayland'))
 xcb_res_dep = dependency('xcb-res', required: get_option('xwayland'))
 xcb_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland'))
 
+gio_dep = dependency('gio-2.0', required:true)
+
 cmake = import('cmake')
 udis = cmake.subproject('udis86')
 udis86 = udis.dependency('libudis86')
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 1a823f3e4..fb93032a4 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -539,6 +539,7 @@ CConfigManager::CConfigManager() {
     m_pConfig->addConfigValue("cursor:zoom_factor", {1.f});
     m_pConfig->addConfigValue("cursor:zoom_rigid", Hyprlang::INT{0});
     m_pConfig->addConfigValue("cursor:enable_hyprcursor", Hyprlang::INT{1});
+    m_pConfig->addConfigValue("cursor:sync_gsettings_theme", Hyprlang::INT{1});
     m_pConfig->addConfigValue("cursor:hide_on_key_press", Hyprlang::INT{0});
     m_pConfig->addConfigValue("cursor:hide_on_touch", Hyprlang::INT{1});
     m_pConfig->addConfigValue("cursor:allow_dumb_copy", Hyprlang::INT{0});
diff --git a/src/managers/XCursorManager.cpp b/src/managers/XCursorManager.cpp
index f2a7ab53b..1108bbb20 100644
--- a/src/managers/XCursorManager.cpp
+++ b/src/managers/XCursorManager.cpp
@@ -1,6 +1,9 @@
 #include <cstring>
 #include <dirent.h>
 #include <filesystem>
+#include <gio/gio.h>
+#include <gio/gsettingsschema.h>
+#include "config/ConfigValue.hpp"
 #include "helpers/CursorShapes.hpp"
 #include "debug/Log.hpp"
 #include "XCursorManager.hpp"
@@ -146,6 +149,10 @@ void CXCursorManager::loadTheme(std::string const& name, int size) {
 
         cursors.emplace_back(cursor);
     }
+
+    static auto SYNCGSETTINGS = CConfigValue<Hyprlang::INT>("cursor:sync_gsettings_theme");
+    if (*SYNCGSETTINGS)
+        syncGsettings();
 }
 
 SP<SXCursors> CXCursorManager::getShape(std::string const& shape, int size) {
@@ -542,3 +549,55 @@ std::vector<SP<SXCursors>> CXCursorManager::loadAllFromDir(std::string const& pa
 
     return newCursors;
 }
+
+void CXCursorManager::syncGsettings() {
+    auto checkParamExists = [](std::string const& paramName, std::string const& category) {
+        auto* gSettingsSchemaSource = g_settings_schema_source_get_default();
+
+        if (!gSettingsSchemaSource) {
+            Debug::log(WARN, "GSettings default schema source does not exist, cant sync GSettings");
+            return false;
+        }
+
+        auto* gSettingsSchema = g_settings_schema_source_lookup(gSettingsSchemaSource, category.c_str(), true);
+        bool  hasParam        = false;
+
+        if (gSettingsSchema != NULL) {
+            hasParam = gSettingsSchema && g_settings_schema_has_key(gSettingsSchema, paramName.c_str());
+            g_settings_schema_unref(gSettingsSchema);
+        }
+
+        return hasParam;
+    };
+
+    using SettingValue = std::variant<std::string, int>;
+    auto setValue      = [&checkParamExists](std::string const& paramName, const SettingValue& paramValue, std::string const& category) {
+        if (!checkParamExists(paramName, category)) {
+            Debug::log(WARN, "GSettings parameter doesnt exist {} in {}", paramName, category);
+            return;
+        }
+
+        auto* gsettings = g_settings_new(category.c_str());
+
+        if (!gsettings) {
+            Debug::log(WARN, "GSettings failed to allocate new settings with category {}", category);
+            return;
+        }
+
+        std::visit(
+            [&](auto&& value) {
+                using T = std::decay_t<decltype(value)>;
+                if constexpr (std::is_same_v<T, std::string>)
+                    g_settings_set_string(gsettings, paramName.c_str(), value.c_str());
+                else if constexpr (std::is_same_v<T, int>)
+                    g_settings_set_int(gsettings, paramName.c_str(), value);
+            },
+            paramValue);
+
+        g_settings_sync();
+        g_object_unref(gsettings);
+    };
+
+    setValue("cursor-theme", themeName, "org.gnome.desktop.interface");
+    setValue("cursor-size", lastLoadSize, "org.gnome.desktop.interface");
+}
diff --git a/src/managers/XCursorManager.hpp b/src/managers/XCursorManager.hpp
index 206370550..464c1ec3f 100644
--- a/src/managers/XCursorManager.hpp
+++ b/src/managers/XCursorManager.hpp
@@ -38,6 +38,7 @@ class CXCursorManager {
     std::string                     getLegacyShapeName(std::string const& shape);
     std::vector<SP<SXCursors>>      loadStandardCursors(std::string const& name, int size);
     std::vector<SP<SXCursors>>      loadAllFromDir(std::string const& path, int size);
+    void                            syncGsettings();
 
     int                             lastLoadSize = 0;
     std::string                     themeName    = "";
diff --git a/src/meson.build b/src/meson.build
index 098d82985..475ecc24b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -28,6 +28,7 @@ executable('Hyprland', src,
     xcb_xfixes_dep,
     backtrace_dep,
     epoll_dep,
+    gio_dep,
     udis86,
 
     dependency('pixman-1'),