From 74881d96a00b7707b4020adf3d8eae86dda29940 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Fri, 6 Oct 2023 23:55:58 +0200 Subject: [PATCH] WIP: Use color scheme based on OS system dark/light mode preferences Signed-off-by: Christian Parpart --- .clang-format | 2 +- src/contour/CMakeLists.txt | 7 ++-- src/contour/ContourGuiApp.cpp | 62 +++++++++++++++++++++++++++++++++++ src/contour/ContourGuiApp.h | 31 +++++++++++++++++- src/contour/contour.yml | 10 +++++- 5 files changed, 107 insertions(+), 5 deletions(-) diff --git a/.clang-format b/.clang-format index bd35115950..0e63501492 100644 --- a/.clang-format +++ b/.clang-format @@ -95,7 +95,7 @@ IncludeCategories: Priority: 52 - Regex: '^ #include +#include +#include #include #include #include @@ -43,6 +45,20 @@ namespace CLI = crispy::cli; namespace contour { +namespace +{ + ColorPreference getColorPreferenceFromFDO(unsigned value) + { + switch (value) + { + case 2: return ColorPreference::Light; + case 1: return ColorPreference::Dark; + case 0: [[fallthrough]]; + default: return ColorPreference::Default; + } + } +} // namespace + ContourGuiApp::ContourGuiApp(): _sessionManager(*this) { link("contour.terminal", bind(&ContourGuiApp::terminalGuiAction, this)); @@ -347,6 +363,36 @@ int ContourGuiApp::terminalGuiAction() // NB: We use QApplication over QGuiApplication because we want to use SystemTrayIcon. QApplication app(qtArgsCount, (char**) qtArgsPtr.data()); + { + QString const name = "org.freedesktop.portal.Desktop"; + QString const path = "/org/freedesktop/portal/desktop"; + QString const interface = "org.freedesktop.portal.Settings"; + + QDBusConnection::sessionBus().connect(QStringLiteral("org.freedesktop.portal.Desktop"), + QStringLiteral("/org/freedesktop/portal/desktop"), + QStringLiteral("org.freedesktop.portal.Settings"), + QStringLiteral("SettingChanged"), + this, + SLOT(slotSettingChanged(QString, QString, QDBusVariant))); + + auto fdoSettingsApi = QDBusInterface(name, path, interface, QDBusConnection::sessionBus()); + QDBusMessage const reply = fdoSettingsApi.call("Read", "org.freedesktop.appearance", "color-scheme"); + if (reply.type() == QDBusMessage::ReplyMessage) + { + assert(reply.arguments().count() == 1); + const uint result = reply.arguments() + .first() + .value() + .variant() + .value() + .variant() + .toUInt(); + + _colorPreference = getColorPreferenceFromFDO(result); + fmt::print("color theme mode: {}\n", _colorPreference); // TODO: remove debug print + } + } + #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // Enforce OpenGL over any other. As much as I'd love to provide other backends, too. // We currently only support OpenGL. @@ -409,6 +455,22 @@ int ContourGuiApp::terminalGuiAction() return rv; } +void ContourGuiApp::slotSettingChanged(QString nameSpace, QString key, QDBusVariant value) // NOLINT +{ + if (nameSpace == QLatin1String("org.freedesktop.appearance") && key == QLatin1String("color-scheme")) + { + const uint result = value.variant().toUInt(); + auto newValue = getColorPreferenceFromFDO(result); + + if (_colorPreference != newValue) + { + _colorPreference = newValue; + fmt::print("color preferenced changed to {} mode\n", _colorPreference); // TODO: remove debug print + // TODO: notify all terminal sessions about color preference change + } + } +} + void ContourGuiApp::ensureTermInfoFile() { if (!vtpty::Process::isFlatpak()) diff --git a/src/contour/ContourGuiApp.h b/src/contour/ContourGuiApp.h index e29d43ea47..dc3c229ddd 100644 --- a/src/contour/ContourGuiApp.h +++ b/src/contour/ContourGuiApp.h @@ -7,10 +7,10 @@ #include +#include #include #include -#include #include #include #include @@ -25,6 +25,13 @@ namespace config class TerminalSession; +enum class ColorPreference +{ + Default, + Dark, + Light, +}; + /// Extends ContourApp with terminal GUI capability. class ContourGuiApp: public QObject, public ContourApp { @@ -69,6 +76,9 @@ class ContourGuiApp: public QObject, public ContourApp [[nodiscard]] static QUrl resolveResource(std::string_view path); + private slots: + void slotSettingChanged(QString nameSpace, QString key, QDBusVariant value); + private: static void ensureTermInfoFile(); bool loadConfig(std::string const& target); @@ -82,7 +92,26 @@ class ContourGuiApp: public QObject, public ContourApp char const** _argv = nullptr; std::optional _exitStatus; + ColorPreference _colorPreference = ColorPreference::Default; + std::unique_ptr _qmlEngine; }; } // namespace contour + + +template <> +struct fmt::formatter: fmt::formatter +{ + auto format(contour::ColorPreference value, format_context& ctx) -> format_context::iterator + { + string_view name; + switch (value) + { + case contour::ColorPreference::Default: name = "Default"; break; + case contour::ColorPreference::Dark: name = "Dark"; break; + case contour::ColorPreference::Light: name = "Light"; break; + } + return formatter::format(name, ctx); + } +}; diff --git a/src/contour/contour.yml b/src/contour/contour.yml index 9df37283e8..b15327e7c7 100644 --- a/src/contour/contour.yml +++ b/src/contour/contour.yml @@ -452,7 +452,15 @@ profiles: blur: false # Specifies a colorscheme to use (alternatively the colors can be inlined). - colors: "default" + # + # This can be either the name to a single colorscheme to always use, + # or a map with two keys (dark and light) to determine the color scheme to use for each. + # The dark color scheme is used when the system is configured to prefer dark mode and light theme otherwise. + # + # Default: "default" + colors: + light: onelight + dark: onedark # Hyperlinks (via OSC-8) can be stylized and colorized on hover. hyperlink_decoration: