Waybar/src/util/portal.cpp

107 lines
3.7 KiB
C++
Raw Normal View History

search for dark or light mode stylesheet summary: ------- This commit adds xdg-desktop-portal support to waybar. If a portal supporting `org.freedesktop.portal.Settings` exists, then it will be queried for the current colorscheme. This colorscheme will then be used to prefer a `style-light.css` or `style-dark.css` over the basic `style.css`. technical details: ----------------- Appearance is provided by several libraries, such as libhandy (mobile) and libadwaita. However, waybar links to neither of these libraries. As the amount of code required to communicate with xdg-desktop portal as a client is rather minimal, I believe doing so is better than linking to an additional library. The Gio library for communicating with dbus is rather messy, Instead of the `Portal` class containing a `Gio::Dbus::Proxy`, it extends it which simplifies signal handling. `Portal` then exposes its own signal, which can be listened to by waybar to update CSS. For a reference implementation, please see another one of my projects: https://github.com/4e554c4c/darkman.nvim/blob/main/portal.go test plan: --------- If no desktop portal which provides `Settings` exists, then waybar continues with the log line ``` [2023-09-06 14:14:37.754] [info] Unable to receive desktop appearance: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface “org.freedesktop.portal.Settings” on object at path /org/freedesktop/portal/desktop ``` Furthermore, if `style-light.css` or `style-dark.css` do not exist, then `style.css` will still be searched for. Waybar has been tested with both light and dark startup. E.g. if the appearance is dark on startup the log lines ``` [2023-09-06 14:27:45.379] [info] Discovered appearance 'dark' [2023-09-06 14:27:45.379] [debug] Try expanding: $XDG_CONFIG_HOME/waybar/style-dark.css [2023-09-06 14:27:45.379] [debug] Found config file: $XDG_CONFIG_HOME/waybar/style-dark.css [2023-09-06 14:27:45.379] [info] Using CSS file /home/pounce/.config/waybar/style-dark.css ``` will be observed. If the color then changes to light during the operation of waybar, it will change css files: ``` [2023-09-06 14:28:17.173] [info] Received new appearance 'dark' [2023-09-06 14:28:17.173] [debug] Try expanding: $XDG_CONFIG_HOME/waybar/style-light.css [2023-09-06 14:28:17.173] [debug] Found config file: $XDG_CONFIG_HOME/waybar/style-light.css [2023-09-06 14:28:17.173] [info] Using CSS file /home/pounce/.config/waybar/style-light.css ``` Finally, tested resetting waybar and toggling style (works, and style is only changed once). fixes: Alexays/Waybar#1973
2023-09-06 15:19:56 +00:00
#include "util/portal.hpp"
#include <giomm/dbusproxy.h>
#include <glibmm/variant.h>
#include <spdlog/spdlog.h>
#include <iostream>
#include <string>
#include "fmt/format.h"
namespace waybar {
static constexpr const char* PORTAL_BUS_NAME = "org.freedesktop.portal.Desktop";
static constexpr const char* PORTAL_OBJ_PATH = "/org/freedesktop/portal/desktop";
static constexpr const char* PORTAL_INTERFACE = "org.freedesktop.portal.Settings";
static constexpr const char* PORTAL_NAMESPACE = "org.freedesktop.appearance";
static constexpr const char* PORTAL_KEY = "color-scheme";
} // namespace waybar
using namespace Gio;
auto fmt::formatter<waybar::Appearance>::format(waybar::Appearance c, format_context& ctx) const {
string_view name;
switch (c) {
case waybar::Appearance::LIGHT:
name = "light";
break;
case waybar::Appearance::DARK:
name = "dark";
break;
default:
name = "unknown";
break;
}
return formatter<string_view>::format(name, ctx);
}
waybar::Portal::Portal()
: DBus::Proxy(DBus::Connection::get_sync(DBus::BusType::BUS_TYPE_SESSION), PORTAL_BUS_NAME,
PORTAL_OBJ_PATH, PORTAL_INTERFACE),
currentMode(Appearance::UNKNOWN) {
refreshAppearance();
};
void waybar::Portal::refreshAppearance() {
auto params = Glib::Variant<std::tuple<Glib::ustring, Glib::ustring>>::create(
{PORTAL_NAMESPACE, PORTAL_KEY});
Glib::VariantBase response;
try {
response = call_sync(std::string(PORTAL_INTERFACE) + ".Read", params);
} catch (const Glib::Error& e) {
spdlog::info("Unable to receive desktop appearance: {}", std::string(e.what()));
return;
}
// unfortunately, the response is triple-nested, with type (v<v<uint32_t>>),
// so we have cast thrice. This is a variation from the freedesktop standard
// (it should only be doubly nested) but all implementations appear to do so.
//
// xdg-desktop-portal 1.17 will fix this issue with a new `ReadOne` method,
// but this version is not yet released.
// TODO(xdg-desktop-portal v1.17): switch to ReadOne
auto container = Glib::VariantBase::cast_dynamic<Glib::VariantContainerBase>(response);
Glib::VariantBase modev;
container.get_child(modev, 0);
auto mode =
Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::Variant<Glib::Variant<uint32_t>>>>(modev)
.get()
.get()
.get();
auto newMode = Appearance(mode);
if (newMode == currentMode) {
return;
}
spdlog::info("Discovered appearance '{}'", newMode);
currentMode = newMode;
m_signal_appearance_changed.emit(currentMode);
}
waybar::Appearance waybar::Portal::getAppearance() { return currentMode; };
void waybar::Portal::on_signal(const Glib::ustring& sender_name, const Glib::ustring& signal_name,
const Glib::VariantContainerBase& parameters) {
spdlog::debug("Received signal {}", (std::string)signal_name);
if (signal_name != "SettingChanged" || parameters.get_n_children() != 3) {
return;
}
Glib::VariantBase nspcv, keyv, valuev;
parameters.get_child(nspcv, 0);
parameters.get_child(keyv, 1);
parameters.get_child(valuev, 2);
auto nspc = Glib::VariantBase::cast_dynamic<Glib::Variant<std::string>>(nspcv).get();
auto key = Glib::VariantBase::cast_dynamic<Glib::Variant<std::string>>(keyv).get();
if (nspc != PORTAL_NAMESPACE || key != PORTAL_KEY) {
return;
}
auto value =
Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::Variant<uint32_t>>>(valuev).get().get();
auto newMode = Appearance(value);
if (newMode == currentMode) {
return;
}
spdlog::info("Received new appearance '{}'", newMode);
currentMode = newMode;
m_signal_appearance_changed.emit(currentMode);
}