feat(upower): added support for auxiliary batteries

This commit is contained in:
Lemmy
2026-05-09 00:25:43 -04:00
parent f8eeff3e8a
commit 02e0ed0b03
15 changed files with 331 additions and 75 deletions
+1 -1
View File
@@ -953,7 +953,7 @@
},
"device": {
"label": "Device",
"description": "Audio stream to control"
"description": "Device to monitor or control"
},
"display": {
"label": "Display",
+3 -1
View File
@@ -525,6 +525,7 @@ void Application::initServices() {
m_upowerService->setChangeCallback([this]() {
onUpowerStateChangedForHooks();
m_bar.refresh();
m_settingsWindow.onExternalOptionsChanged();
});
} catch (const std::exception& e) {
kLog.warn("upower disabled: {}", e.what());
@@ -754,7 +755,8 @@ void Application::initUi() {
m_renderContext.setTextFontFamily(m_configService.config().shell.fontFamily);
m_wallpaper.initialize(m_wayland, &m_configService, &m_renderContext, &m_sharedTextureCache);
m_backdrop.initialize(m_wayland, &m_configService, &m_sharedTextureCache, &m_glShared);
m_settingsWindow.initialize(m_wayland, &m_configService, &m_renderContext, &m_dependencyService);
m_settingsWindow.initialize(m_wayland, &m_configService, &m_renderContext, &m_dependencyService,
m_upowerService.get());
m_settingsWindow.setOpenDesktopWidgetEditor([this]() { m_desktopWidgetsController.toggleEdit(); });
m_lockScreen.initialize(m_wayland, &m_renderContext, &m_configService, &m_sharedTextureCache);
m_lockScreen.setSessionHooks([this]() { m_hookManager.fire(HookKind::SessionLocked); },
+185 -56
View File
@@ -3,11 +3,14 @@
#include "core/log.h"
#include "dbus/system_bus.h"
#include "i18n/i18n.h"
#include "util/string_utils.h"
#include <algorithm>
#include <map>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#include <string_view>
#include <utility>
#include <vector>
namespace {
@@ -19,6 +22,8 @@ namespace {
static constexpr auto k_propertiesInterface = "org.freedesktop.DBus.Properties";
// UPower device types
constexpr std::uint32_t k_deviceTypeUnknown = 0;
constexpr std::uint32_t k_deviceTypeLinePower = 1;
constexpr std::uint32_t k_deviceTypeBattery = 2;
// UPower battery states
@@ -63,6 +68,40 @@ namespace {
}
}
bool isBatteryCapableDeviceType(std::uint32_t type) {
return type != k_deviceTypeUnknown && type != k_deviceTypeLinePower;
}
bool isAutoSelector(std::string_view selector) {
const std::string normalized = StringUtils::toLower(StringUtils::trim(selector));
return normalized.empty() || normalized == "auto";
}
bool hasSelectorSuffix(std::string_view value, std::string_view selector) {
if (value.empty() || selector.empty() || value.size() < selector.size()) {
return false;
}
const std::size_t start = value.size() - selector.size();
if (value.substr(start) != selector) {
return false;
}
if (start == 0) {
return true;
}
const char before = value[start - 1];
return before == '/' || before == '_' || before == '-' || before == ':' || before == '.';
}
bool selectorMatchesField(const std::string& value, std::string_view selector) {
return std::string_view(value) == selector || hasSelectorSuffix(value, selector);
}
bool selectorMatchesDevice(const UPowerDeviceInfo& info, std::string_view selector) {
return selectorMatchesField(info.path, selector) || selectorMatchesField(info.nativePath, selector) ||
selectorMatchesField(info.model, selector) || selectorMatchesField(info.serial, selector) ||
selectorMatchesField(info.vendor, selector);
}
BatteryState decodeBatteryState(std::uint32_t raw) {
switch (raw) {
case k_stateCharging:
@@ -89,107 +128,197 @@ namespace {
UPowerService::UPowerService(SystemBus& bus) : m_bus(bus) {
m_upowerProxy = sdbus::createProxy(m_bus.connection(), k_upowerBusName, k_upowerObjectPath);
// Listen for devices being added/removed so we can rebind the battery proxy
m_upowerProxy->uponSignal("DeviceAdded").onInterface(k_upowerInterface).call([this](const sdbus::ObjectPath&) {
scanDevices();
refresh();
});
m_upowerProxy->uponSignal("DeviceRemoved").onInterface(k_upowerInterface).call([this](const sdbus::ObjectPath& path) {
if (m_deviceProxy != nullptr) {
// If our tracked device was removed, clear it and re-scan
try {
if (m_deviceProxy->getObjectPath() == path) {
m_deviceProxy.reset();
scanDevices();
m_upowerProxy->uponSignal("PropertiesChanged")
.onInterface(k_propertiesInterface)
.call([this](const std::string& interfaceName, const std::map<std::string, sdbus::Variant>& /*changed*/,
const std::vector<std::string>& /*invalidated*/) {
if (interfaceName == k_upowerInterface) {
refresh();
}
} catch (const sdbus::Error&) {
m_deviceProxy.reset();
scanDevices();
}
}
refresh();
});
m_upowerProxy->uponSignal("DeviceAdded").onInterface(k_upowerInterface).call([this](const sdbus::ObjectPath&) {
rescanDevices();
});
scanDevices();
refresh();
m_upowerProxy->uponSignal("DeviceRemoved").onInterface(k_upowerInterface).call([this](const sdbus::ObjectPath&) {
rescanDevices();
});
rescanDevices();
if (m_state.isPresent) {
kLog.info("battery {:.0f}% state={} ({})", m_state.percentage, static_cast<int>(m_state.state),
m_state.onBattery ? "on battery" : "on AC");
} else {
kLog.info("connected (no battery present)");
kLog.info("connected (no system battery present)");
}
}
void UPowerService::setChangeCallback(ChangeCallback callback) { m_changeCallback = std::move(callback); }
void UPowerService::refresh() { emitChangedIfNeeded(readState()); }
void UPowerService::refresh() { refreshDeviceStates(); }
void UPowerService::scanDevices() {
if (m_deviceProxy != nullptr) {
return; // already have a battery bound
std::vector<UPowerDeviceInfo> UPowerService::batteryDevices() const {
std::vector<UPowerDeviceInfo> devices;
devices.reserve(m_devices.size());
for (const auto& device : m_devices) {
if (device.info.isPresent && isBatteryCapableDeviceType(device.info.type)) {
devices.push_back(device.info);
}
}
return devices;
}
UPowerState UPowerService::stateForDevice(std::string_view selector) const {
if (isAutoSelector(selector)) {
return m_state;
}
if (const auto* device = findDevice(selector); device != nullptr) {
return device->state;
}
UPowerState missing;
missing.onBattery = getPropertyOr<bool>(*m_upowerProxy, k_upowerInterface, "OnBattery", false);
return missing;
}
void UPowerService::rescanDevices() {
std::vector<sdbus::ObjectPath> paths;
try {
m_upowerProxy->callMethod("EnumerateDevices").onInterface(k_upowerInterface).storeResultsTo(paths);
} catch (const sdbus::Error& e) {
kLog.warn("EnumerateDevices failed: {}", e.what());
emitChangedIfNeeded(false);
return;
}
std::vector<TrackedDevice> nextDevices;
nextDevices.reserve(paths.size());
for (const auto& path : paths) {
try {
auto probe = sdbus::createProxy(m_bus.connection(), k_upowerBusName, path);
const auto type = getPropertyOr<std::uint32_t>(*probe, k_deviceInterface, "Type", 0);
const auto present = getPropertyOr<bool>(*probe, k_deviceInterface, "IsPresent", false);
if (type == k_deviceTypeBattery && present) {
bindBatteryDevice(path);
return;
auto proxy = sdbus::createProxy(m_bus.connection(), k_upowerBusName, path);
auto info = readDeviceInfo(std::string(path), *proxy);
if (!isBatteryCapableDeviceType(info.type)) {
continue;
}
proxy->uponSignal("PropertiesChanged")
.onInterface(k_propertiesInterface)
.call([this](const std::string& interfaceName, const std::map<std::string, sdbus::Variant>& /*changed*/,
const std::vector<std::string>& /*invalidated*/) {
if (interfaceName == k_deviceInterface) {
refresh();
}
});
nextDevices.push_back(TrackedDevice{std::move(info), std::move(proxy)});
} catch (const sdbus::Error&) {
continue;
}
}
std::sort(nextDevices.begin(), nextDevices.end(),
[](const TrackedDevice& lhs, const TrackedDevice& rhs) { return lhs.info.path < rhs.info.path; });
bool devicesChanged = m_devices.size() != nextDevices.size();
if (!devicesChanged) {
for (std::size_t i = 0; i < m_devices.size(); ++i) {
if (m_devices[i].info != nextDevices[i].info) {
devicesChanged = true;
break;
}
}
}
m_devices = std::move(nextDevices);
if (devicesChanged) {
kLog.debug("tracking {} UPower battery-capable device(s)", m_devices.size());
}
emitChangedIfNeeded(devicesChanged);
}
void UPowerService::bindBatteryDevice(const sdbus::ObjectPath& path) {
m_deviceProxy = sdbus::createProxy(m_bus.connection(), k_upowerBusName, path);
m_deviceProxy->uponSignal("PropertiesChanged")
.onInterface(k_propertiesInterface)
.call([this](const std::string& interfaceName, const std::map<std::string, sdbus::Variant>& /*changed*/,
const std::vector<std::string>& /*invalidated*/) {
if (interfaceName == k_deviceInterface) {
refresh();
}
});
kLog.debug("bound battery device {}", std::string(path));
}
UPowerState UPowerService::readState() const {
UPowerState UPowerService::readDefaultState() const {
UPowerState next;
next.onBattery = getPropertyOr<bool>(*m_upowerProxy, k_upowerInterface, "OnBattery", false);
if (m_deviceProxy == nullptr) {
const auto* device = defaultSystemBattery();
if (device == nullptr) {
return next;
}
next.percentage = getPropertyOr<double>(*m_deviceProxy, k_deviceInterface, "Percentage", 0.0);
next.isPresent = getPropertyOr<bool>(*m_deviceProxy, k_deviceInterface, "IsPresent", false);
const auto rawState = getPropertyOr<std::uint32_t>(*m_deviceProxy, k_deviceInterface, "State", 0);
next = device->state;
next.onBattery = getPropertyOr<bool>(*m_upowerProxy, k_upowerInterface, "OnBattery", false);
return next;
}
UPowerState UPowerService::readDeviceState(sdbus::IProxy& proxy) const {
UPowerState next;
next.onBattery = getPropertyOr<bool>(*m_upowerProxy, k_upowerInterface, "OnBattery", false);
next.percentage = getPropertyOr<double>(proxy, k_deviceInterface, "Percentage", 0.0);
next.isPresent = getPropertyOr<bool>(proxy, k_deviceInterface, "IsPresent", false);
const auto rawState = getPropertyOr<std::uint32_t>(proxy, k_deviceInterface, "State", 0);
next.state = decodeBatteryState(rawState);
next.timeToEmpty = getPropertyOr<std::int64_t>(*m_deviceProxy, k_deviceInterface, "TimeToEmpty", 0);
next.timeToFull = getPropertyOr<std::int64_t>(*m_deviceProxy, k_deviceInterface, "TimeToFull", 0);
next.timeToEmpty = getPropertyOr<std::int64_t>(proxy, k_deviceInterface, "TimeToEmpty", 0);
next.timeToFull = getPropertyOr<std::int64_t>(proxy, k_deviceInterface, "TimeToFull", 0);
return next;
}
void UPowerService::emitChangedIfNeeded(const UPowerState& next) {
if (next == m_state) {
UPowerDeviceInfo UPowerService::readDeviceInfo(std::string path, sdbus::IProxy& proxy) const {
UPowerDeviceInfo info;
info.path = std::move(path);
info.nativePath = getPropertyOr<std::string>(proxy, k_deviceInterface, "NativePath", "");
info.vendor = getPropertyOr<std::string>(proxy, k_deviceInterface, "Vendor", "");
info.model = getPropertyOr<std::string>(proxy, k_deviceInterface, "Model", "");
info.serial = getPropertyOr<std::string>(proxy, k_deviceInterface, "Serial", "");
info.type = getPropertyOr<std::uint32_t>(proxy, k_deviceInterface, "Type", 0);
info.powerSupply = getPropertyOr<bool>(proxy, k_deviceInterface, "PowerSupply", false);
info.state = readDeviceState(proxy);
info.isPresent = info.state.isPresent;
return info;
}
const UPowerDeviceInfo* UPowerService::defaultSystemBattery() const noexcept {
for (const auto& device : m_devices) {
if (device.info.type == k_deviceTypeBattery && device.info.powerSupply && device.info.isPresent) {
return &device.info;
}
}
return nullptr;
}
const UPowerDeviceInfo* UPowerService::findDevice(std::string_view selector) const {
const std::string trimmed = StringUtils::trim(selector);
if (trimmed.empty()) {
return nullptr;
}
for (const auto& device : m_devices) {
if (isBatteryCapableDeviceType(device.info.type) && selectorMatchesDevice(device.info, trimmed)) {
return &device.info;
}
}
return nullptr;
}
void UPowerService::refreshDeviceStates() {
bool devicesChanged = false;
for (auto& device : m_devices) {
auto next = readDeviceInfo(device.info.path, *device.proxy);
if (next != device.info) {
device.info = std::move(next);
devicesChanged = true;
}
}
emitChangedIfNeeded(devicesChanged);
}
void UPowerService::emitChangedIfNeeded(bool devicesChanged) {
const UPowerState next = readDefaultState();
if (!devicesChanged && next == m_state) {
return;
}
+32 -6
View File
@@ -4,12 +4,13 @@
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
class SystemBus;
namespace sdbus {
class IProxy;
class ObjectPath;
} // namespace sdbus
enum class BatteryState : std::uint8_t {
@@ -35,6 +36,20 @@ struct UPowerState {
bool operator==(const UPowerState&) const = default;
};
struct UPowerDeviceInfo {
std::string path;
std::string nativePath;
std::string vendor;
std::string model;
std::string serial;
std::uint32_t type = 0;
bool powerSupply = false;
bool isPresent = false;
UPowerState state;
bool operator==(const UPowerDeviceInfo&) const = default;
};
class UPowerService {
public:
using ChangeCallback = std::function<void()>;
@@ -45,16 +60,27 @@ public:
void refresh();
[[nodiscard]] const UPowerState& state() const noexcept { return m_state; }
[[nodiscard]] UPowerState stateForDevice(std::string_view selector) const;
[[nodiscard]] std::vector<UPowerDeviceInfo> batteryDevices() const;
private:
[[nodiscard]] UPowerState readState() const;
void emitChangedIfNeeded(const UPowerState& next);
void bindBatteryDevice(const sdbus::ObjectPath& path);
void scanDevices();
struct TrackedDevice {
UPowerDeviceInfo info;
std::unique_ptr<sdbus::IProxy> proxy;
};
[[nodiscard]] UPowerState readDefaultState() const;
[[nodiscard]] UPowerState readDeviceState(sdbus::IProxy& proxy) const;
[[nodiscard]] UPowerDeviceInfo readDeviceInfo(std::string path, sdbus::IProxy& proxy) const;
[[nodiscard]] const UPowerDeviceInfo* defaultSystemBattery() const noexcept;
[[nodiscard]] const UPowerDeviceInfo* findDevice(std::string_view selector) const;
void emitChangedIfNeeded(bool devicesChanged);
void rescanDevices();
void refreshDeviceStates();
SystemBus& m_bus;
std::unique_ptr<sdbus::IProxy> m_upowerProxy;
std::unique_ptr<sdbus::IProxy> m_deviceProxy;
std::vector<TrackedDevice> m_devices;
UPowerState m_state;
ChangeCallback m_changeCallback;
};
+2 -1
View File
@@ -131,7 +131,8 @@ std::unique_ptr<Widget> WidgetFactory::create(const std::string& name, wl_output
}
if (type == "battery") {
auto widget = std::make_unique<BatteryWidget>(m_upower);
const std::string deviceSelector = wc != nullptr ? wc->getString("device", "auto") : std::string("auto");
auto widget = std::make_unique<BatteryWidget>(m_upower, deviceSelector);
widget->setContentScale(contentScale);
return widget;
}
+4 -2
View File
@@ -8,6 +8,7 @@
#include <cmath>
#include <string>
#include <utility>
namespace {
@@ -38,7 +39,8 @@ namespace {
} // namespace
BatteryWidget::BatteryWidget(UPowerService* upower) : m_upower(upower) {}
BatteryWidget::BatteryWidget(UPowerService* upower, std::string deviceSelector)
: m_upower(upower), m_deviceSelector(std::move(deviceSelector)) {}
void BatteryWidget::create() {
auto container = std::make_unique<Node>();
@@ -90,7 +92,7 @@ void BatteryWidget::syncState(Renderer& renderer) {
return;
}
const auto& s = m_upower->state();
const auto s = m_upower->stateForDevice(m_deviceSelector);
if (s.percentage == m_lastPct && s.state == m_lastState && s.isPresent == m_lastPresent &&
m_isVertical == m_lastVertical) {
+4 -1
View File
@@ -3,12 +3,14 @@
#include "dbus/upower/upower_service.h"
#include "shell/bar/widget.h"
#include <string>
class Glyph;
class Label;
class BatteryWidget : public Widget {
public:
explicit BatteryWidget(UPowerService* upower);
BatteryWidget(UPowerService* upower, std::string deviceSelector = "auto");
void create() override;
@@ -18,6 +20,7 @@ private:
void syncState(Renderer& renderer);
UPowerService* m_upower = nullptr;
std::string m_deviceSelector = "auto";
Glyph* m_glyph = nullptr;
Label* m_label = nullptr;
double m_lastPct = -1.0;
+34 -5
View File
@@ -664,6 +664,29 @@ namespace settings {
return options;
}
SelectSetting batteryDeviceSelectSetting(const BarWidgetEditorContext& ctx, std::string selectedValue) {
if (selectedValue.empty()) {
selectedValue = "auto";
}
std::vector<SelectOption> options = ctx.batteryDeviceOptions;
if (options.empty()) {
options.push_back(SelectOption{.value = "auto", .label = i18n::tr("common.states.auto")});
}
const auto hasSelected = std::any_of(options.begin(), options.end(), [&selectedValue](const SelectOption& opt) {
return opt.value == selectedValue;
});
if (!selectedValue.empty() && !hasSelected) {
options.push_back(SelectOption{
.value = selectedValue,
.label = i18n::tr("settings.controls.select.unknown-value", "value", selectedValue),
});
}
return SelectSetting{std::move(options), std::move(selectedValue)};
}
void addRawWidgetSettings(Flex& panel, std::string_view widgetName, const std::vector<WidgetSettingSpec>& specs,
std::size_t& visibleSpecs, const BarWidgetEditorContext& ctx) {
if (!ctx.showAdvanced) {
@@ -987,12 +1010,18 @@ namespace settings {
ctx.makeListBlock(*panel, entry, ListSetting{.items = settingValueAsStringList(value)});
break;
case WidgetSettingValueType::Select: {
std::vector<SelectOption> options;
options.reserve(spec.options.size());
for (const auto& option : spec.options) {
options.push_back(SelectOption{std::string(option.value), i18n::tr(option.labelKey)});
SelectSetting selectSetting;
const std::string selectedValue = settingValueAsString(value);
if (widgetType == "battery" && spec.key == "device") {
selectSetting = batteryDeviceSelectSetting(ctx, selectedValue);
} else {
std::vector<SelectOption> options;
options.reserve(spec.options.size());
for (const auto& option : spec.options) {
options.push_back(SelectOption{std::string(option.value), i18n::tr(option.labelKey)});
}
selectSetting = SelectSetting{std::move(options), selectedValue};
}
SelectSetting selectSetting{std::move(options), settingValueAsString(value)};
selectSetting.segmented = spec.segmented;
ctx.makeRow(*panel, entry, ctx.makeSelect(std::move(selectSetting), path));
break;
+1
View File
@@ -21,6 +21,7 @@ namespace settings {
float scale = 1.0f;
bool showAdvanced = false;
bool showOverriddenOnly = false;
std::vector<SelectOption> batteryDeviceOptions;
std::string& openWidgetPickerPath;
std::string& editingWidgetName;
std::string& pendingDeleteWidgetName;
+1
View File
@@ -1223,6 +1223,7 @@ namespace settings {
.scale = scale,
.showAdvanced = ctx.showAdvanced,
.showOverriddenOnly = ctx.showOverriddenOnly,
.batteryDeviceOptions = ctx.batteryDeviceOptions,
.openWidgetPickerPath = ctx.openWidgetPickerPath,
.editingWidgetName = ctx.editingWidgetName,
.pendingDeleteWidgetName = ctx.pendingDeleteWidgetName,
+1
View File
@@ -27,6 +27,7 @@ namespace settings {
const BarMonitorOverride* selectedMonitorOverride = nullptr;
bool showAdvanced = false;
bool showOverriddenOnly = false;
std::vector<SelectOption> batteryDeviceOptions;
std::string& openWidgetPickerPath;
std::string& openSearchPickerPath;
+50 -1
View File
@@ -5,6 +5,7 @@
#include "core/deferred_call.h"
#include "core/log.h"
#include "core/ui_phase.h"
#include "dbus/upower/upower_service.h"
#include "i18n/i18n.h"
#include "render/render_context.h"
#include "shell/settings/settings_content.h"
@@ -29,6 +30,7 @@
#include "ui/dialogs/file_dialog.h"
#include "ui/palette.h"
#include "ui/style.h"
#include "util/string_utils.h"
#include "wayland/toplevel_surface.h"
#include "wayland/wayland_connection.h"
@@ -157,16 +159,61 @@ namespace {
return {};
}
std::string upowerDeviceLabel(const UPowerDeviceInfo& device) {
const std::string nativeName =
!device.nativePath.empty() ? StringUtils::pathTail(device.nativePath) : StringUtils::pathTail(device.path);
std::string label;
if (!device.vendor.empty() && !device.model.empty()) {
label = device.vendor + " " + device.model;
} else if (!device.model.empty()) {
label = device.model;
} else if (!device.vendor.empty()) {
label = device.vendor;
} else {
label = nativeName;
}
if (!nativeName.empty() && label != nativeName) {
label += " (" + nativeName + ")";
}
return label;
}
std::vector<settings::SelectOption> upowerBatteryDeviceOptions(UPowerService* upower) {
std::vector<settings::SelectOption> options;
options.push_back(settings::SelectOption{.value = "auto", .label = i18n::tr("common.states.auto")});
if (upower == nullptr) {
return options;
}
const auto devices = upower->batteryDevices();
options.reserve(devices.size() + 1);
for (const auto& device : devices) {
std::string description = device.path;
if (!device.nativePath.empty() && device.nativePath != device.path) {
description = device.nativePath + " - " + device.path;
}
options.push_back(settings::SelectOption{
.value = device.path,
.label = upowerDeviceLabel(device),
.description = std::move(description),
});
}
return options;
}
} // namespace
SettingsWindow::~SettingsWindow() = default;
void SettingsWindow::initialize(WaylandConnection& wayland, ConfigService* config, RenderContext* renderContext,
DependencyService* dependencies) {
DependencyService* dependencies, UPowerService* upower) {
m_wayland = &wayland;
m_config = config;
m_renderContext = renderContext;
m_dependencies = dependencies;
m_upower = upower;
m_showAdvanced = m_config != nullptr ? m_config->config().shell.settingsShowAdvanced : false;
}
@@ -1084,6 +1131,7 @@ void SettingsWindow::rebuildSettingsContent() {
},
});
const auto batteryDeviceOptions = upowerBatteryDeviceOptions(m_upower);
settings::addSettingsContentSections(
*m_contentContainer, m_settingsRegistry,
settings::SettingsContentContext{
@@ -1096,6 +1144,7 @@ void SettingsWindow::rebuildSettingsContent() {
.selectedMonitorOverride = selectedMonitorOverride,
.showAdvanced = m_showAdvanced,
.showOverriddenOnly = m_showOverriddenOnly,
.batteryDeviceOptions = batteryDeviceOptions,
.openWidgetPickerPath = m_openWidgetPickerPath,
.openSearchPickerPath = m_openSearchPickerPath,
.editingWidgetName = m_editingWidgetName,
+3 -1
View File
@@ -21,6 +21,7 @@ class ConfigService;
class DependencyService;
class Flex;
class RenderContext;
class UPowerService;
class WaylandConnection;
struct KeyboardEvent;
struct PointerEvent;
@@ -33,7 +34,7 @@ public:
~SettingsWindow();
void initialize(WaylandConnection& wayland, ConfigService* config, RenderContext* renderContext,
DependencyService* dependencies);
DependencyService* dependencies, UPowerService* upower);
void open();
void openToBarWidget(std::string barName, std::string widgetName);
@@ -84,6 +85,7 @@ private:
ConfigService* m_config = nullptr;
RenderContext* m_renderContext = nullptr;
DependencyService* m_dependencies = nullptr;
UPowerService* m_upower = nullptr;
std::unique_ptr<ToplevelSurface> m_surface;
std::unique_ptr<Node> m_sceneRoot;
@@ -337,6 +337,8 @@ namespace settings {
add(boolSpec("show_when_idle", false));
add(colorRoleSpec("low_color", "primary"));
add(colorRoleSpec("high_color", "primary"));
} else if (type == "battery") {
add(selectSpec("device", "auto", {{"auto", "common.states.auto"}}));
} else if (type == "bluetooth") {
add(boolSpec("show_label", false));
} else if (type == "brightness") {
+8
View File
@@ -26,6 +26,14 @@ namespace StringUtils {
return out;
}
[[nodiscard]] inline std::string pathTail(std::string_view path) {
const auto slash = path.find_last_of('/');
if (slash == std::string_view::npos || slash + 1 >= path.size()) {
return std::string(path);
}
return std::string(path.substr(slash + 1));
}
inline void toLowerInPlace(std::string& s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
}