service/upower: add upower service

This commit is contained in:
Ben 2024-07-05 23:00:39 -04:00
parent 497c9c4e50
commit 24f54f579f
10 changed files with 638 additions and 0 deletions

View file

@ -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}")

View file

@ -17,3 +17,7 @@ endif()
if (SERVICE_GREETD)
add_subdirectory(greetd)
endif()
if (SERVICE_UPOWER)
add_subdirectory(upower)
endif()

View file

@ -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)

View file

@ -0,0 +1,165 @@
#include "core.hpp"
#include <qcontainerfwd.h>
#include <qdbusconnection.h>
#include <qdbusconnectioninterface.h>
#include <qdbusextratypes.h>
#include <qdbuspendingcall.h>
#include <qdbuspendingreply.h>
#include <qdbusservicewatcher.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qobject.h>
#include <qqmllist.h>
#include <qtmetamacros.h>
#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<QList<QDBusObjectPath>> 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<UPowerDevice*>(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<UPowerDevice*>(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<UPowerDevice>* 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<UPowerDevice>* UPowerQml::devices() { // NOLINT
return UPower::instance()->devices();
}
bool UPowerQml::onBattery() { return UPower::instance()->onBattery(); }
} // namespace qs::service::upower

View file

@ -0,0 +1,77 @@
#pragma once
#include <qdbusservicewatcher.h>
#include <qhash.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qqmllist.h>
#include <qtmetamacros.h>
#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<UPowerDevice>* 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<QString, UPowerDevice*> mDevices;
ObjectModel<UPowerDevice> readyDevices {this};
dbus::DBusPropertyGroup serviceProperties;
dbus::DBusProperty<bool> 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<UPowerDevice>* 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<UPowerDevice>* devices();
[[nodiscard]] static bool onBattery();
signals:
void displayDeviceChanged();
void onBatteryChanged();
};
} // namespace qs::service::upower

View file

@ -0,0 +1,134 @@
#include "device.hpp"
#include <qcontainerfwd.h>
#include <qdbusconnection.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qobject.h>
#include <qstring.h>
#include <qtypes.h>
#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<UPowerDeviceType::Enum>(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<UPowerDeviceState::Enum>(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

View file

@ -0,0 +1,187 @@
#pragma once
#include <qcontainerfwd.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#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<quint32> pType {this->deviceProperties, "Type"};
dbus::DBusProperty<bool> pPowerSupply {this->deviceProperties, "PowerSupply"};
dbus::DBusProperty<qreal> pEnergy {this->deviceProperties, "Energy"};
dbus::DBusProperty<qreal> pEnergyCapacity {this->deviceProperties, "EnergyFull"};
dbus::DBusProperty<qreal> pChangeRate {this->deviceProperties, "EnergyRate"};
dbus::DBusProperty<qlonglong> pTimeToEmpty {this->deviceProperties, "TimeToEmpty"};
dbus::DBusProperty<qlonglong> pTimeToFull {this->deviceProperties, "TimeToFull"};
dbus::DBusProperty<qreal> pPercentage {this->deviceProperties, "Percentage"};
dbus::DBusProperty<bool> pIsPresent {this->deviceProperties, "IsPresent"};
dbus::DBusProperty<quint32> pState {this->deviceProperties, "State"};
dbus::DBusProperty<qreal> pHealthPercentage {this->deviceProperties, "Capacity"};
dbus::DBusProperty<QString> pIconName {this->deviceProperties, "IconName"};
dbus::DBusProperty<QString> pNativePath {this->deviceProperties, "NativePath"};
DBusUPowerDevice* device = nullptr;
};
} // namespace qs::service::upower

View file

@ -0,0 +1,7 @@
name = "Quickshell.Services.UPower"
description = "UPower Service"
headers = [
"core.hpp",
"device.hpp",
]
-----

View file

@ -0,0 +1,16 @@
<node>
<interface name="org.freedesktop.UPower.Device">
<property name="Type" type="u" access="read"/>
<property name="PowerSupply" type="b" access="read"/>
<property name="Energy" type="d" access="read"/>
<property name="EnergyFull" type="d" access="read"/>
<property name="EnergyRate" type="d" access="read"/>
<property name="TimeToEmpty" type="x" access="read"/>
<property name="TimeToFull" type="x" access="read"/>
<property name="Percentage" type="d" access="read"/>
<property name="IsPresent" type="b" access="read"/>
<property name="State" type="u" access="read"/>
<property name="Capacity" type = "d" access="read"/>
<property name="IconName" type="s" access="read"/>
</interface>
</node>

View file

@ -0,0 +1,7 @@
<node>
<interface name="org.freedesktop.UPower">
<method name="EnumerateDevices">
<arg direction="out" type="ao" name="devices"/>
</method>
</interface>
</node>