From 24f54f579f28ac4d48bcd3782a3ecc507dffe806 Mon Sep 17 00:00:00 2001 From: Ben <66143154+bdebiase@users.noreply.github.com> Date: Fri, 5 Jul 2024 23:00:39 -0400 Subject: [PATCH] service/upower: add upower service --- CMakeLists.txt | 2 + src/services/CMakeLists.txt | 4 + src/services/upower/CMakeLists.txt | 39 ++++ src/services/upower/core.cpp | 165 ++++++++++++++++ src/services/upower/core.hpp | 77 ++++++++ src/services/upower/device.cpp | 134 +++++++++++++ src/services/upower/device.hpp | 187 ++++++++++++++++++ src/services/upower/module.md | 7 + .../upower/org.freedesktop.UPower.Device.xml | 16 ++ .../upower/org.freedesktop.UPower.xml | 7 + 10 files changed, 638 insertions(+) create mode 100644 src/services/upower/CMakeLists.txt create mode 100644 src/services/upower/core.cpp create mode 100644 src/services/upower/core.hpp create mode 100644 src/services/upower/device.cpp create mode 100644 src/services/upower/device.hpp create mode 100644 src/services/upower/module.md create mode 100644 src/services/upower/org.freedesktop.UPower.Device.xml create mode 100644 src/services/upower/org.freedesktop.UPower.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 4865490..9ec58d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option(SERVICE_PIPEWIRE "PipeWire service" ON) option(SERVICE_MPRIS "Mpris service" ON) option(SERVICE_PAM "Pam service" ON) option(SERVICE_GREETD "Greet service" ON) +option(SERVICE_UPOWER "UPower service" ON) message(STATUS "Quickshell configuration") message(STATUS " Jemalloc: ${USE_JEMALLOC}") @@ -43,6 +44,7 @@ message(STATUS " PipeWire: ${SERVICE_PIPEWIRE}") message(STATUS " Mpris: ${SERVICE_MPRIS}") message(STATUS " Pam: ${SERVICE_PAM}") message(STATUS " Greetd: ${SERVICE_GREETD}") +message(STATUS " UPower: ${SERVICE_UPOWER}") message(STATUS " Hyprland: ${HYPRLAND}") if (HYPRLAND) message(STATUS " IPC: ${HYPRLAND_IPC}") diff --git a/src/services/CMakeLists.txt b/src/services/CMakeLists.txt index 8005f07..089f5fd 100644 --- a/src/services/CMakeLists.txt +++ b/src/services/CMakeLists.txt @@ -17,3 +17,7 @@ endif() if (SERVICE_GREETD) add_subdirectory(greetd) endif() + +if (SERVICE_UPOWER) + add_subdirectory(upower) +endif() diff --git a/src/services/upower/CMakeLists.txt b/src/services/upower/CMakeLists.txt new file mode 100644 index 0000000..59987c4 --- /dev/null +++ b/src/services/upower/CMakeLists.txt @@ -0,0 +1,39 @@ +set_source_files_properties(org.freedesktop.UPower.xml PROPERTIES + CLASSNAME DBusUPowerService + NO_NAMESPACE TRUE +) + +qt_add_dbus_interface(DBUS_INTERFACES + org.freedesktop.UPower.xml + dbus_service +) + +set_source_files_properties(org.freedesktop.UPower.Device.xml PROPERTIES + CLASSNAME DBusUPowerDevice + NO_NAMESPACE TRUE +) + +qt_add_dbus_interface(DBUS_INTERFACES + org.freedesktop.UPower.Device.xml + dbus_device +) + +qt_add_library(quickshell-service-upower STATIC + core.cpp + device.cpp + ${DBUS_INTERFACES} +) + +# dbus headers +target_include_directories(quickshell-service-upower PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + +qt_add_qml_module(quickshell-service-upower + URI Quickshell.Services.UPower + VERSION 0.1 +) + +target_link_libraries(quickshell-service-upower PRIVATE ${QT_DEPS} quickshell-dbus) +target_link_libraries(quickshell PRIVATE quickshell-service-upowerplugin) + +qs_pch(quickshell-service-upower) +qs_pch(quickshell-service-upowerplugin) diff --git a/src/services/upower/core.cpp b/src/services/upower/core.cpp new file mode 100644 index 0000000..de952e0 --- /dev/null +++ b/src/services/upower/core.cpp @@ -0,0 +1,165 @@ +#include "core.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../core/model.hpp" +#include "../../dbus/properties.hpp" +#include "dbus_service.h" +#include "device.hpp" + +namespace qs::service::upower { + +Q_LOGGING_CATEGORY(logUPower, "quickshell.service.upower", QtWarningMsg); + +UPower::UPower() { + qCDebug(logUPower) << "Starting UPower"; + + auto bus = QDBusConnection::systemBus(); + + if (!bus.isConnected()) { + qCWarning(logUPower) << "Could not connect to DBus. UPower service will not work."; + return; + } + + this->service = + new DBusUPowerService("org.freedesktop.UPower", "/org/freedesktop/UPower", bus, this); + + if (!this->service->isValid()) { + qCWarning(logUPower) << "Cannot connect to the UPower service."; + return; + } + + QObject::connect( + &this->pOnBattery, + &dbus::AbstractDBusProperty::changed, + this, + &UPower::onBatteryChanged + ); + + this->serviceProperties.setInterface(this->service); + this->serviceProperties.updateAllViaGetAll(); + + this->registerExisting(); +} + +void UPower::registerExisting() { + this->registerDevice("/org/freedesktop/UPower/devices/DisplayDevice"); + + auto pending = this->service->EnumerateDevices(); + auto* call = new QDBusPendingCallWatcher(pending, this); + + auto responseCallback = [this](QDBusPendingCallWatcher* call) { + const QDBusPendingReply> reply = *call; + + if (reply.isError()) { + qCWarning(logUPower) << "Failed to enumerate devices:" << reply.error().message(); + } else { + for (const QDBusObjectPath& devicePath: reply.value()) { + this->registerDevice(devicePath.path()); + } + } + + delete call; + }; + + QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); +} + +void UPower::onDeviceReady() { + auto* device = qobject_cast(this->sender()); + + if (device->path() == "/org/freedesktop/UPower/devices/DisplayDevice") { + this->mDisplayDevice = device; + emit this->displayDeviceChanged(); + qCDebug(logUPower) << "Display UPowerDevice" << device->path() << "ready"; + return; + } + + this->readyDevices.insertObject(device); + qCDebug(logUPower) << "UPowerDevice" << device->path() << "ready"; +} + +void UPower::onDeviceDestroyed(QObject* object) { + auto* device = static_cast(object); // NOLINT + + this->mDevices.remove(device->path()); + + if (device == this->mDisplayDevice) { + this->mDisplayDevice = nullptr; + emit this->displayDeviceChanged(); + qCDebug(logUPower) << "Display UPowerDevice" << device->path() << "destroyed"; + return; + } + + this->readyDevices.removeObject(device); + qCDebug(logUPower) << "UPowerDevice" << device->path() << "destroyed"; +} + +void UPower::registerDevice(const QString& path) { + if (this->mDevices.contains(path)) { + qCDebug(logUPower) << "Skipping duplicate registration of UPowerDevice" << path; + return; + } + + auto* device = new UPowerDevice(path, this); + if (!device->isValid()) { + qCWarning(logUPower) << "Ignoring invalid UPowerDevice registration of" << path; + delete device; + return; + } + + this->mDevices.insert(path, device); + QObject::connect(device, &UPowerDevice::ready, this, &UPower::onDeviceReady); + QObject::connect(device, &QObject::destroyed, this, &UPower::onDeviceDestroyed); + + qCDebug(logUPower) << "Registered UPowerDevice" << path; +} + +UPowerDevice* UPower::displayDevice() { return this->mDisplayDevice; } + +ObjectModel* UPower::devices() { return &this->readyDevices; } + +bool UPower::onBattery() const { return this->pOnBattery.get(); } + +UPower* UPower::instance() { + static UPower* instance = new UPower(); // NOLINT + return instance; +} + +UPowerQml::UPowerQml(QObject* parent): QObject(parent) { + QObject::connect( + UPower::instance(), + &UPower::displayDeviceChanged, + this, + &UPowerQml::displayDeviceChanged + ); + QObject::connect( + UPower::instance(), + &UPower::onBatteryChanged, + this, + &UPowerQml::onBatteryChanged + ); +} + +UPowerDevice* UPowerQml::displayDevice() { // NOLINT + return UPower::instance()->displayDevice(); +} + +ObjectModel* UPowerQml::devices() { // NOLINT + return UPower::instance()->devices(); +} + +bool UPowerQml::onBattery() { return UPower::instance()->onBattery(); } + +} // namespace qs::service::upower diff --git a/src/services/upower/core.hpp b/src/services/upower/core.hpp new file mode 100644 index 0000000..3b2f860 --- /dev/null +++ b/src/services/upower/core.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "../../core/model.hpp" +#include "../../dbus/properties.hpp" +#include "dbus_service.h" +#include "device.hpp" + +namespace qs::service::upower { + +class UPower: public QObject { + Q_OBJECT; + +public: + [[nodiscard]] UPowerDevice* displayDevice(); + [[nodiscard]] ObjectModel* devices(); + [[nodiscard]] bool onBattery() const; + + static UPower* instance(); + +signals: + void displayDeviceChanged(); + void onBatteryChanged(); + +private slots: + void onDeviceReady(); + void onDeviceDestroyed(QObject* object); + +private: + explicit UPower(); + + void registerExisting(); + void registerDevice(const QString& path); + + UPowerDevice* mDisplayDevice = nullptr; + QHash mDevices; + ObjectModel readyDevices {this}; + + dbus::DBusPropertyGroup serviceProperties; + dbus::DBusProperty pOnBattery {this->serviceProperties, "OnBattery"}; + + DBusUPowerService* service = nullptr; +}; + +class UPowerQml: public QObject { + Q_OBJECT; + QML_NAMED_ELEMENT(UPower); + QML_SINGLETON; + /// UPower's DisplayDevice for your system. Can be `null`. + /// + /// This is an aggregate device and not a physical one, meaning you will not find it in `devices`. + /// It is typically the device that is used for displaying information in desktop environments. + Q_PROPERTY(UPowerDevice* displayDevice READ displayDevice NOTIFY displayDeviceChanged); + /// All connected UPower devices. + Q_PROPERTY(ObjectModel* devices READ devices CONSTANT); + /// If the system is currently running on battery power, or discharging. + Q_PROPERTY(bool onBattery READ onBattery NOTIFY onBatteryChanged); + +public: + explicit UPowerQml(QObject* parent = nullptr); + + [[nodiscard]] UPowerDevice* displayDevice(); + [[nodiscard]] ObjectModel* devices(); + [[nodiscard]] static bool onBattery(); + +signals: + void displayDeviceChanged(); + void onBatteryChanged(); +}; + +} // namespace qs::service::upower diff --git a/src/services/upower/device.cpp b/src/services/upower/device.cpp new file mode 100644 index 0000000..fb7b606 --- /dev/null +++ b/src/services/upower/device.cpp @@ -0,0 +1,134 @@ +#include "device.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../dbus/properties.hpp" +#include "dbus_device.h" + +using namespace qs::dbus; + +namespace qs::service::upower { + +Q_LOGGING_CATEGORY(logUPowerDevice, "quickshell.service.upower.device", QtWarningMsg); + +QString UPowerDeviceState::toString(UPowerDeviceState::Enum status) { + switch (status) { + case UPowerDeviceState::Unknown: return "Unknown"; + case UPowerDeviceState::Charging: return "Charging"; + case UPowerDeviceState::Discharging: return "Discharging"; + case UPowerDeviceState::Empty: return "Empty"; + case UPowerDeviceState::FullyCharged: return "Fully Charged"; + case UPowerDeviceState::PendingCharge: return "Pending Charge"; + case UPowerDeviceState::PendingDischarge: return "Pending Discharge"; + default: return "Invalid Status"; + } +} + +QString UPowerDeviceType::toString(UPowerDeviceType::Enum type) { + switch (type) { + case UPowerDeviceType::Unknown: return "Unknown"; + case UPowerDeviceType::LinePower: return "Line Power"; + case UPowerDeviceType::Battery: return "Battery"; + case UPowerDeviceType::Ups: return "Ups"; + case UPowerDeviceType::Monitor: return "Monitor"; + case UPowerDeviceType::Mouse: return "Mouse"; + case UPowerDeviceType::Keyboard: return "Keyboard"; + case UPowerDeviceType::Pda: return "Pda"; + case UPowerDeviceType::Phone: return "Phone"; + case UPowerDeviceType::MediaPlayer: return "Media Player"; + case UPowerDeviceType::Tablet: return "Tablet"; + case UPowerDeviceType::Computer: return "Computer"; + case UPowerDeviceType::GamingInput: return "Gaming Input"; + case UPowerDeviceType::Pen: return "Pen"; + case UPowerDeviceType::Touchpad: return "Touchpad"; + case UPowerDeviceType::Modem: return "Modem"; + case UPowerDeviceType::Network: return "Network"; + case UPowerDeviceType::Headset: return "Headset"; + case UPowerDeviceType::Speakers: return "Speakers"; + case UPowerDeviceType::Headphones: return "Headphones"; + case UPowerDeviceType::Video: return "Video"; + case UPowerDeviceType::OtherAudio: return "Other Audio"; + case UPowerDeviceType::RemoteControl: return "Remote Control"; + case UPowerDeviceType::Printer: return "Printer"; + case UPowerDeviceType::Scanner: return "Scanner"; + case UPowerDeviceType::Camera: return "Camera"; + case UPowerDeviceType::Wearable: return "Wearable"; + case UPowerDeviceType::Toy: return "Toy"; + case UPowerDeviceType::BluetoothGeneric: return "Bluetooth Generic"; + default: return "Invalid Type"; + } +} + +UPowerDevice::UPowerDevice(const QString& path, QObject* parent): QObject(parent) { + this->device = + new DBusUPowerDevice("org.freedesktop.UPower", path, QDBusConnection::systemBus(), this); + + if (!this->device->isValid()) { + qCWarning(logUPowerDevice) << "Cannot create UPowerDevice for" << path; + return; + } + + // clang-format off + QObject::connect(&this->pType, &AbstractDBusProperty::changed, this, &UPowerDevice::typeChanged); + QObject::connect(&this->pPowerSupply, &AbstractDBusProperty::changed, this, &UPowerDevice::powerSupplyChanged); + QObject::connect(&this->pEnergy, &AbstractDBusProperty::changed, this, &UPowerDevice::energyChanged); + QObject::connect(&this->pEnergyCapacity, &AbstractDBusProperty::changed, this, &UPowerDevice::energyCapacityChanged); + QObject::connect(&this->pChangeRate, &AbstractDBusProperty::changed, this, &UPowerDevice::changeRateChanged); + QObject::connect(&this->pTimeToEmpty, &AbstractDBusProperty::changed, this, &UPowerDevice::timeToEmptyChanged); + QObject::connect(&this->pTimeToFull, &AbstractDBusProperty::changed, this, &UPowerDevice::timeToFullChanged); + QObject::connect(&this->pPercentage, &AbstractDBusProperty::changed, this, &UPowerDevice::percentageChanged); + QObject::connect(&this->pIsPresent, &AbstractDBusProperty::changed, this, &UPowerDevice::isPresentChanged); + QObject::connect(&this->pState, &AbstractDBusProperty::changed, this, &UPowerDevice::stateChanged); + QObject::connect(&this->pHealthPercentage, &AbstractDBusProperty::changed, this, &UPowerDevice::healthPercentageChanged); + QObject::connect(&this->pHealthPercentage, &AbstractDBusProperty::changed, this, &UPowerDevice::healthSupportedChanged); + QObject::connect(&this->pIconName, &AbstractDBusProperty::changed, this, &UPowerDevice::iconNameChanged); + QObject::connect(&this->pType, &AbstractDBusProperty::changed, this, &UPowerDevice::isLaptopBatteryChanged); + QObject::connect(&this->pNativePath, &AbstractDBusProperty::changed, this, &UPowerDevice::nativePathChanged); + + QObject::connect(&this->deviceProperties, &DBusPropertyGroup::getAllFinished, this, &UPowerDevice::ready); + // clang-format on + + this->deviceProperties.setInterface(this->device); + this->deviceProperties.updateAllViaGetAll(); +} + +bool UPowerDevice::isValid() const { return this->device->isValid(); } +QString UPowerDevice::address() const { return this->device->service(); } +QString UPowerDevice::path() const { return this->device->path(); } + +UPowerDeviceType::Enum UPowerDevice::type() const { + return static_cast(this->pType.get()); +} + +bool UPowerDevice::powerSupply() const { return this->pPowerSupply.get(); } +qreal UPowerDevice::energy() const { return this->pEnergy.get(); } +qreal UPowerDevice::energyCapacity() const { return this->pEnergyCapacity.get(); } +qreal UPowerDevice::changeRate() const { return this->pChangeRate.get(); } +qlonglong UPowerDevice::timeToEmpty() const { return this->pTimeToEmpty.get(); } +qlonglong UPowerDevice::timeToFull() const { return this->pTimeToFull.get(); } +qreal UPowerDevice::percentage() const { return this->pPercentage.get(); } +bool UPowerDevice::isPresent() const { return this->pIsPresent.get(); } + +UPowerDeviceState::Enum UPowerDevice::state() const { + return static_cast(this->pState.get()); +} + +qreal UPowerDevice::healthPercentage() const { return this->pHealthPercentage.get(); } + +bool UPowerDevice::healthSupported() const { return this->healthPercentage() != 0; } + +QString UPowerDevice::iconName() const { return this->pIconName.get(); } + +bool UPowerDevice::isLaptopBattery() const { + return this->pType.get() == UPowerDeviceType::Battery && this->pPowerSupply.get(); +} + +QString UPowerDevice::nativePath() const { return this->pNativePath.get(); } + +} // namespace qs::service::upower diff --git a/src/services/upower/device.hpp b/src/services/upower/device.hpp new file mode 100644 index 0000000..47a71a5 --- /dev/null +++ b/src/services/upower/device.hpp @@ -0,0 +1,187 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../../core/doc.hpp" +#include "../../dbus/properties.hpp" +#include "dbus_device.h" + +namespace qs::service::upower { + +class UPowerDeviceState: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum { + Unknown = 0, + Charging = 1, + Discharging = 2, + Empty = 3, + FullyCharged = 4, + /// The device is waiting to be charged after it was plugged in. + PendingCharge = 5, + /// The device is waiting to be discharged after being unplugged. + PendingDischarge = 6, + }; + Q_ENUM(Enum); + + Q_INVOKABLE static QString toString(UPowerDeviceState::Enum status); +}; + +class UPowerDeviceType: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum { + Unknown = 0, + LinePower = 1, + Battery = 2, + Ups = 3, + Monitor = 4, + Mouse = 5, + Keyboard = 6, + Pda = 7, + Phone = 8, + MediaPlayer = 9, + Tablet = 10, + Computer = 11, + GamingInput = 12, + Pen = 13, + Touchpad = 14, + Modem = 15, + Network = 16, + Headset = 17, + Speakers = 18, + Headphones = 19, + Video = 20, + OtherAudio = 21, + RemoteControl = 22, + Printer = 23, + Scanner = 24, + Camera = 25, + Wearable = 26, + Toy = 27, + BluetoothGeneric = 28, + }; + Q_ENUM(Enum); + + Q_INVOKABLE static QString toString(UPowerDeviceType::Enum type); +}; + +///! A device exposed through the UPower system service. +class UPowerDevice: public QObject { + Q_OBJECT; + // clang-format off + /// The type of device. + Q_PROPERTY(UPowerDeviceType::Enum type READ type NOTIFY typeChanged); + /// If the device is a power supply for your computer and can provide charge. + Q_PROPERTY(bool powerSupply READ powerSupply NOTIFY powerSupplyChanged); + /// Current energy level of the device in watt-hours. + Q_PROPERTY(qreal energy READ energy NOTIFY energyChanged); + /// Maximum energy capacity of the device in watt-hours + Q_PROPERTY(qreal energyCapacity READ energyCapacity NOTIFY energyCapacityChanged); + /// Rate of energy change in watts (positive when charging, negative when discharging). + Q_PROPERTY(qreal changeRate READ changeRate NOTIFY changeRateChanged); + /// Estimated time until the device is fully discharged, in seconds. + /// + /// Will be set to `0` if charging. + Q_PROPERTY(qreal timeToEmpty READ timeToEmpty NOTIFY timeToEmptyChanged); + /// Estimated time until the device is fully charged, in seconds. + /// + /// Will be set to `0` if discharging. + Q_PROPERTY(qreal timeToFull READ timeToFull NOTIFY timeToFullChanged); + /// Current charge level as a percentage. + /// + /// This would be equivalent to `energy / energyCapacity`. + Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged); + /// If the power source is present in the bay or slot, useful for hot-removable batteries. + /// + /// If the device `type` is not `Battery`, then the property will be invalid. + Q_PROPERTY(bool isPresent READ isPresent NOTIFY isPresentChanged); + /// Current state of the device. + Q_PROPERTY(UPowerDeviceState::Enum state READ state NOTIFY stateChanged); + /// Health of the device as a percentage of its original health. + Q_PROPERTY(qreal healthPercentage READ healthPercentage NOTIFY healthPercentageChanged); + Q_PROPERTY(bool healthSupported READ healthSupported NOTIFY healthSupportedChanged); + /// Name of the icon representing the current state of the device, or an empty string if not provided. + Q_PROPERTY(QString iconName READ iconName NOTIFY iconNameChanged); + /// If the device is a laptop battery or not. Use this to check if your device is a valid battery. + /// + /// This will be equivalent to `type == Battery && powerSupply == true`. + Q_PROPERTY(bool isLaptopBattery READ isLaptopBattery NOTIFY isLaptopBatteryChanged); + /// Native path of the device specific to your OS. + Q_PROPERTY(QString nativePath READ nativePath NOTIFY nativePathChanged); + // clang-format on + QML_ELEMENT; + QML_UNCREATABLE("UPowerDevices can only be acquired from UPower"); + +public: + explicit UPowerDevice(const QString& path, QObject* parent = nullptr); + + [[nodiscard]] bool isValid() const; + [[nodiscard]] QString address() const; + [[nodiscard]] QString path() const; + + [[nodiscard]] UPowerDeviceType::Enum type() const; + [[nodiscard]] bool powerSupply() const; + [[nodiscard]] qreal energy() const; + [[nodiscard]] qreal energyCapacity() const; + [[nodiscard]] qreal changeRate() const; + [[nodiscard]] qlonglong timeToEmpty() const; + [[nodiscard]] qlonglong timeToFull() const; + [[nodiscard]] qreal percentage() const; + [[nodiscard]] bool isPresent() const; + [[nodiscard]] UPowerDeviceState::Enum state() const; + [[nodiscard]] qreal healthPercentage() const; + [[nodiscard]] bool healthSupported() const; + [[nodiscard]] QString iconName() const; + [[nodiscard]] bool isLaptopBattery() const; + [[nodiscard]] QString nativePath() const; + +signals: + QSDOC_HIDE void ready(); + + void typeChanged(); + void powerSupplyChanged(); + void energyChanged(); + void energyCapacityChanged(); + void changeRateChanged(); + void timeToEmptyChanged(); + void timeToFullChanged(); + void percentageChanged(); + void isPresentChanged(); + void stateChanged(); + void healthPercentageChanged(); + void healthSupportedChanged(); + void iconNameChanged(); + void isLaptopBatteryChanged(); + void nativePathChanged(); + +private: + dbus::DBusPropertyGroup deviceProperties; + dbus::DBusProperty pType {this->deviceProperties, "Type"}; + dbus::DBusProperty pPowerSupply {this->deviceProperties, "PowerSupply"}; + dbus::DBusProperty pEnergy {this->deviceProperties, "Energy"}; + dbus::DBusProperty pEnergyCapacity {this->deviceProperties, "EnergyFull"}; + dbus::DBusProperty pChangeRate {this->deviceProperties, "EnergyRate"}; + dbus::DBusProperty pTimeToEmpty {this->deviceProperties, "TimeToEmpty"}; + dbus::DBusProperty pTimeToFull {this->deviceProperties, "TimeToFull"}; + dbus::DBusProperty pPercentage {this->deviceProperties, "Percentage"}; + dbus::DBusProperty pIsPresent {this->deviceProperties, "IsPresent"}; + dbus::DBusProperty pState {this->deviceProperties, "State"}; + dbus::DBusProperty pHealthPercentage {this->deviceProperties, "Capacity"}; + dbus::DBusProperty pIconName {this->deviceProperties, "IconName"}; + dbus::DBusProperty pNativePath {this->deviceProperties, "NativePath"}; + + DBusUPowerDevice* device = nullptr; +}; + +} // namespace qs::service::upower diff --git a/src/services/upower/module.md b/src/services/upower/module.md new file mode 100644 index 0000000..99c7ece --- /dev/null +++ b/src/services/upower/module.md @@ -0,0 +1,7 @@ +name = "Quickshell.Services.UPower" +description = "UPower Service" +headers = [ + "core.hpp", + "device.hpp", +] +----- diff --git a/src/services/upower/org.freedesktop.UPower.Device.xml b/src/services/upower/org.freedesktop.UPower.Device.xml new file mode 100644 index 0000000..ef98a30 --- /dev/null +++ b/src/services/upower/org.freedesktop.UPower.Device.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/services/upower/org.freedesktop.UPower.xml b/src/services/upower/org.freedesktop.UPower.xml new file mode 100644 index 0000000..a7522ee --- /dev/null +++ b/src/services/upower/org.freedesktop.UPower.xml @@ -0,0 +1,7 @@ + + + + + + +