summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authornoname <[email protected]>2025-02-27 15:08:15 +1100
committernoname <[email protected]>2025-03-03 22:06:48 +1100
commitbfceeb515a928de6ad901a1435b8e5bbc26eb64d (patch)
treec8513f519ec15345c3dd95dca9be873da43248f6 /lib
parent4b283b0045c0752c36c6e8306fc137f2c9f244a4 (diff)
test
Diffstat (limited to 'lib')
-rw-r--r--lib/sway/cli.vala50
-rw-r--r--lib/sway/config.vala.in7
-rw-r--r--lib/sway/default.nix15
-rw-r--r--lib/sway/device.vala458
l---------lib/sway/gir.py1
-rw-r--r--lib/sway/ifaces.vala63
-rw-r--r--lib/sway/meson.build114
-rw-r--r--lib/sway/meson_options.txt11
-rw-r--r--lib/sway/upower.vala80
-rw-r--r--lib/sway/version1
10 files changed, 800 insertions, 0 deletions
diff --git a/lib/sway/cli.vala b/lib/sway/cli.vala
new file mode 100644
index 0000000..c51264b
--- /dev/null
+++ b/lib/sway/cli.vala
@@ -0,0 +1,50 @@
+static bool help;
+static bool version;
+static bool monitor;
+
+const OptionEntry[] options = {
+ { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null },
+ { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null },
+ { "monitor", 'm', OptionFlags.NONE, OptionArg.NONE, ref monitor, null, null },
+ { null },
+};
+
+int main(string[] argv) {
+ try {
+ var opts = new OptionContext();
+ opts.add_main_entries(options, null);
+ opts.set_help_enabled(false);
+ opts.set_ignore_unknown_options(false);
+ opts.parse(ref argv);
+ } catch (OptionError err) {
+ printerr (err.message);
+ return 1;
+ }
+
+ if (help) {
+ print("Usage:\n");
+ print(" %s [flags]\n\n", argv[0]);
+ print("Flags:\n");
+ print(" -h, --help Print this help and exit\n");
+ print(" -v, --version Print version number and exit\n");
+ print(" -m, --monitor Monitor property changes\n");
+ return 0;
+ }
+
+ if (version) {
+ print(AstalBattery.VERSION);
+ return 0;
+ }
+
+ var battery = AstalBattery.get_default();
+ print("%s\n", Json.gobject_to_data(battery, null));
+
+ if (monitor) {
+ battery.notify.connect(() => {
+ print("%s\n", Json.gobject_to_data(battery, null));
+ });
+ new GLib.MainLoop(null, false).run();
+ }
+
+ return 0;
+}
diff --git a/lib/sway/config.vala.in b/lib/sway/config.vala.in
new file mode 100644
index 0000000..404e60a
--- /dev/null
+++ b/lib/sway/config.vala.in
@@ -0,0 +1,7 @@
+[CCode (gir_namespace = "AstalBattery", gir_version = "@API_VERSION@")]
+namespace AstalBattery {
+ public const int MAJOR_VERSION = @MAJOR_VERSION@;
+ public const int MINOR_VERSION = @MINOR_VERSION@;
+ public const int MICRO_VERSION = @MICRO_VERSION@;
+ public const string VERSION = "@VERSION@";
+}
diff --git a/lib/sway/default.nix b/lib/sway/default.nix
new file mode 100644
index 0000000..17bf67a
--- /dev/null
+++ b/lib/sway/default.nix
@@ -0,0 +1,15 @@
+{
+ mkAstalPkg,
+ pkgs,
+ ...
+}:
+mkAstalPkg {
+ pname = "astal-battery";
+ src = ./.;
+ packages = [pkgs.json-glib];
+
+ libname = "battery";
+ authors = "Aylur";
+ gir-suffix = "Battery";
+ description = "DBus proxy for upowerd devices";
+}
diff --git a/lib/sway/device.vala b/lib/sway/device.vala
new file mode 100644
index 0000000..db69574
--- /dev/null
+++ b/lib/sway/device.vala
@@ -0,0 +1,458 @@
+namespace AstalBattery {
+ /** Get the DisplayDevice. */
+ public Device get_default() {
+ return Device.get_default();
+ }
+}
+
+/**
+ * Client for a UPower [[https://upower.freedesktop.org/docs/Device.html|device]].
+ */
+public class AstalBattery.Device : Object {
+ private static Device display_device;
+
+ /** Get the DisplayDevice. */
+ public static Device? get_default() {
+ if (display_device != null) {
+ return display_device;
+ }
+
+ try {
+ display_device = new Device((ObjectPath)"/org/freedesktop/UPower/devices/DisplayDevice");
+ return display_device;
+ } catch (Error error) {
+ critical(error.message);
+ }
+
+ return null;
+ }
+
+ private IUPowerDevice proxy;
+
+ public Device(ObjectPath path) throws Error {
+ proxy = Bus.get_proxy_sync(BusType.SYSTEM, "org.freedesktop.UPower", path);
+ proxy.g_properties_changed.connect(sync);
+ sync();
+ }
+
+ /**
+ * If it is [[email protected]], you will need to verify that the
+ * property power-supply has the value `true` before considering it as a laptop battery.
+ * Otherwise it will likely be the battery for a device of an unknown type.
+ */
+ public Type device_type { get; private set; }
+
+ /**
+ * Native path of the power source. This is the sysfs path,
+ * for example /sys/devices/LNXSYSTM:00/device:00/PNP0C0A:00/power_supply/BAT0.
+ * It is blank if the device is being driven by a user space driver.
+ */
+ public string native_path { owned get; private set; }
+
+ /** Name of the vendor of the battery. */
+ public string vendor { owned get; private set; }
+
+ /** Name of the model of this battery. */
+ public string model { owned get; private set; }
+
+ /** Unique serial number of the battery. */
+ public string serial { owned get; private set; }
+
+ /**
+ * The point in time (seconds since the Epoch)
+ * that data was read from the power source.
+ */
+ public uint64 update_time { get; private set; }
+
+ /**
+ * If the power device is used to supply the system.
+ * This would be set `true` for laptop batteries and UPS devices,
+ * but set to `false` for wireless mice or PDAs.
+ */
+ public bool power_supply { get; private set; }
+
+ /** If the power device has history. */
+ // TODO: public bool has_history { get; private set; }
+
+ /** If the power device has statistics. */
+ // TODO: public bool has_statistics { get; private set; }
+
+ /**
+ * Whether power is currently being provided through line power.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]_POWER].
+ */
+ public bool online { get; private set; }
+
+ /**
+ * Amount of energy (measured in Wh) currently available in the power source.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public double energy { get; private set; }
+
+ /**
+ * Amount of energy (measured in Wh) in the power source when it's considered to be empty.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public double energy_empty { get; private set; }
+
+ /**
+ * Amount of energy (measured in Wh) in the power source when it's considered full.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public double energy_full { get; private set; }
+
+ /**
+ * Amount of energy (measured in Wh) the power source is designed to hold when it's considered full.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public double energy_full_design { get; private set; }
+
+ /**
+ * Amount of energy being drained from the source, measured in W.
+ * If positive, the source is being discharged, if negative it's being charged.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public double energy_rate { get; private set; }
+
+ /** Voltage in the Cell or being recorded by the meter. */
+ public double voltage { get; private set; }
+
+ /**
+ * The number of charge cycles as defined by the TCO certification,
+ * or -1 if that value is unknown or not applicable.
+ */
+ public int charge_cycles { get; private set; }
+
+ /** Luminosity being recorded by the meter. */
+ public double luminosity { get; private set; }
+
+ /**
+ * Number of seconds until the power source is considered empty. Is set to 0 if unknown.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public int64 time_to_empty { get; private set; }
+
+ /**
+ * Number of seconds until the power source is considered full. Is set to 0 if unknown.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public int64 time_to_full { get; private set;}
+
+ /**
+ * The amount of energy left in the power source expressed as a percentage between 0 and 1.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ * The percentage will be an approximation if [[email protected]:battery_level]
+ * is set to something other than None.
+ */
+ public double percentage { get; private set; }
+
+ /**
+ * The temperature of the device in degrees Celsius.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public double temperature { get; private set; }
+
+ /**
+ * If the power source is present in the bay.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public bool is_present { get; private set; }
+
+ /**
+ * The battery power state.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public State state { get; private set; }
+
+ /**
+ * If the power source is rechargeable.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public bool is_rechargable { get; private set; }
+
+ /**
+ * The capacity of the power source expressed as a percentage between 0 and 1.
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public double capacity { get; private set; }
+
+ /**
+ * Technology used in the battery:
+ *
+ * This property is only valid if [[email protected]:device_type] is [[email protected]].
+ */
+ public Technology technology { get; private set; }
+
+ /** Warning level of the battery. */
+ public WarningLevel warning_level { get; private set; }
+
+ /**
+ * The level of the battery for devices which do not report a percentage
+ * but rather a coarse battery level. If the value is None.
+ * then the device does not support coarse battery reporting,
+ * and the [[email protected]:percentage] should be used instead.
+ */
+ public BatteryLevel battery_level { get; private set; }
+
+ /**
+ * An icon name representing this Device.
+ *
+ * NOTE: [[email protected]:battery_icon_name] might be a better fit
+ * as it is calculated from percentage.
+ */
+ public string icon_name { owned get; private set; }
+
+ /**
+ * Indicates if [[email protected]:state] is charging or fully charged.
+ */
+ public bool charging { get; private set; }
+
+ /**
+ * Indicates if [[email protected]:device_type] is not line power or unknown.
+ */
+ public bool is_battery { get; private set; }
+
+ /**
+ * An icon name in the form of "battery-level-$percentage-$state-symbolic".
+ */
+ public string battery_icon_name { get; private set; }
+
+ /**
+ * A string representation of this device's [[email protected]:device_type].
+ */
+ public string device_type_name { get; private set; }
+
+ /**
+ * An icon name that can be used to represent this device's [[email protected]:device_type].
+ */
+ public string device_type_icon { get; private set; }
+
+ // TODO: get_history
+ // TODO: get_statistics
+
+ private void sync() {
+ device_type = (Type)proxy.Type;
+ native_path = proxy.native_path;
+ vendor = proxy.vendor;
+ model = proxy.model;
+ serial = proxy.serial;
+ update_time = proxy.update_time;
+ power_supply = proxy.power_supply;
+ // TODO: has_history = proxy.has_history;
+ // TODO: has_statistics = proxy.has_statistics;
+ online = proxy.online;
+ energy = proxy.energy;
+ energy_empty = proxy.energy_empty;
+ energy_full = proxy.energy_full;
+ energy_full_design = proxy.energy_full_design;
+ energy_rate = proxy.energy_rate;
+ voltage = proxy.voltage;
+ charge_cycles = proxy.charge_cycles;
+ luminosity = proxy.luminosity;
+ time_to_empty = proxy.time_to_empty;
+ time_to_full = proxy.time_to_full;
+ percentage = proxy.percentage / 100;
+ temperature = proxy.temperature;
+ is_present = proxy.is_present;
+ state = (State)proxy.state;
+ is_rechargable = proxy.is_rechargable;
+ capacity = proxy.capacity / 100;
+ technology = (Technology)proxy.technology;
+ warning_level = (WarningLevel)proxy.warning_level;
+ battery_level = (BatteryLevel)proxy.battery_level;
+ icon_name = proxy.icon_name;
+
+ charging = state == State.FULLY_CHARGED || state == State.CHARGING;
+ is_battery = device_type != Type.UNKNOWN && device_type != Type.LINE_POWER;
+
+ if (!is_battery) {
+ battery_icon_name = "battery-missing-symbolic";
+ } else if (percentage >= 0.95 && charging) {
+ battery_icon_name = "battery-level-100-charged-symbolic";
+ } else {
+ var state = charging ? "-charging" : "";
+ var level = (int)Math.round(percentage * 10)*10;
+ battery_icon_name = @"battery-level-$level$state-symbolic";
+ }
+
+ device_type_name = device_type.get_name();
+ device_type_icon = device_type.get_icon_name();
+ }
+}
+
+[CCode (type_signature = "u")]
+public enum AstalBattery.State {
+ UNKNOWN,
+ CHARGING,
+ DISCHARGING,
+ EMPTY,
+ FULLY_CHARGED,
+ PENDING_CHARGE,
+ PENDING_DISCHARGE,
+}
+
+[CCode (type_signature = "u")]
+public enum AstalBattery.Technology {
+ UNKNOWN,
+ LITHIUM_ION,
+ LITHIUM_POLYMER,
+ LITHIUM_IRON_PHOSPHATE,
+ LEAD_ACID,
+ NICKEL_CADMIUM,
+ NICKEL_METAL_HYDRIDE,
+}
+
+[CCode (type_signature = "u")]
+public enum AstalBattery.WarningLevel {
+ UNKNOWN,
+ NONE,
+ DISCHARGING,
+ LOW,
+ CRITICIAL,
+ ACTION,
+}
+
+[CCode (type_signature = "u")]
+public enum AstalBattery.BatteryLevel {
+ UNKNOWN,
+ NONE,
+ LOW,
+ CRITICIAL,
+ NORMAL,
+ HIGH,
+ FULL,
+}
+
+[CCode (type_signature = "u")]
+public enum AstalBattery.Type {
+ UNKNOWN,
+ LINE_POWER,
+ BATTERY,
+ UPS,
+ MONITOR,
+ MOUSE,
+ KEYBOARD,
+ PDA,
+ PHONE,
+ MEDIA_PLAYER,
+ TABLET,
+ COMPUTER,
+ GAMING_INPUT,
+ PEN,
+ TOUCHPAD,
+ MODEM,
+ NETWORK,
+ HEADSET,
+ SPEAKERS,
+ HEADPHONES,
+ VIDEO,
+ OTHER_AUDIO,
+ REMOVE_CONTROL,
+ PRINTER,
+ SCANNER,
+ CAMERA,
+ WEARABLE,
+ TOY,
+ BLUETOOTH_GENERIC;
+
+ // TODO: add more icon names
+ internal string? get_icon_name () {
+ switch (this) {
+ case UPS:
+ return "uninterruptible-power-supply";
+ case MOUSE:
+ return "input-mouse";
+ case KEYBOARD:
+ return "input-keyboard";
+ case PDA:
+ case PHONE:
+ return "phone";
+ case MEDIA_PLAYER:
+ return "multimedia-player";
+ case TABLET:
+ case PEN:
+ return "input-tablet";
+ case GAMING_INPUT:
+ return "input-gaming";
+ default:
+ return null;
+ }
+ }
+
+ internal unowned string? get_name () {
+ switch (this) {
+ case LINE_POWER:
+ return "Plugged In";
+ case BATTERY:
+ return "Battery";
+ case UPS:
+ return "UPS";
+ case MONITOR:
+ return "Display";
+ case MOUSE:
+ return "Mouse";
+ case KEYBOARD:
+ return "Keyboard";
+ case PDA:
+ return "PDA";
+ case PHONE:
+ return "Phone";
+ case MEDIA_PLAYER:
+ return "Media Player";
+ case TABLET:
+ return "Tablet";
+ case COMPUTER:
+ return "Computer";
+ case GAMING_INPUT:
+ return "Controller";
+ case PEN:
+ return "Pen";
+ case TOUCHPAD:
+ return "Touchpad";
+ case MODEM:
+ return "Modem";
+ case NETWORK:
+ return "Network";
+ case HEADSET:
+ return "Headset";
+ case SPEAKERS:
+ return "Speakers";
+ case HEADPHONES:
+ return "Headphones";
+ case VIDEO:
+ return "Video";
+ case OTHER_AUDIO:
+ return "Other Audio";
+ case REMOVE_CONTROL:
+ return "Remove Control";
+ case PRINTER:
+ return "Printer";
+ case SCANNER:
+ return "Scanner";
+ case CAMERA:
+ return "Camera";
+ case WEARABLE:
+ return "Wearable";
+ case TOY:
+ return "Toy";
+ case BLUETOOTH_GENERIC:
+ return "Bluetooth Generic";
+ default:
+ return "Unknown";
+ }
+ }
+}
diff --git a/lib/sway/gir.py b/lib/sway/gir.py
new file mode 120000
index 0000000..b5b4f1d
--- /dev/null
+++ b/lib/sway/gir.py
@@ -0,0 +1 @@
+../gir.py \ No newline at end of file
diff --git a/lib/sway/ifaces.vala b/lib/sway/ifaces.vala
new file mode 100644
index 0000000..36d35fe
--- /dev/null
+++ b/lib/sway/ifaces.vala
@@ -0,0 +1,63 @@
+[DBus (name = "org.freedesktop.UPower")]
+private interface AstalBattery.IUPower : DBusProxy {
+ public abstract ObjectPath[] enumerate_devices() throws Error;
+ public abstract ObjectPath get_display_device() throws Error;
+ public abstract string get_critical_action() throws Error;
+
+ public signal void device_added(ObjectPath object_path);
+ public signal void device_removed(ObjectPath object_path);
+
+ public abstract string daemon_version { owned get; }
+ public abstract bool on_battery { get; }
+ public abstract bool lid_is_closed { get; }
+ public abstract bool lid_is_present { get; }
+}
+
+[DBus (name = "org.freedesktop.UPower.Device")]
+private interface AstalBattery.IUPowerDevice : DBusProxy {
+ // public abstract HistoryDataPoint[] get_history (string type, uint32 timespan, uint32 resolution) throws GLib.Error;
+ // public abstract StatisticsDataPoint[] get_statistics (string type) throws GLib.Error;
+ // public abstract void refresh () throws GLib.Error;
+
+ public abstract uint Type { get; }
+ public abstract string native_path { owned get; }
+ public abstract string vendor { owned get; }
+ public abstract string model { owned get; }
+ public abstract string serial { owned get; }
+ public abstract uint64 update_time { get; }
+ public abstract bool power_supply { get; }
+ public abstract bool has_history { get; }
+ public abstract bool has_statistics { get; }
+ public abstract bool online { get; }
+ public abstract double energy { get; }
+ public abstract double energy_empty { get; }
+ public abstract double energy_full { get; }
+ public abstract double energy_full_design { get; }
+ public abstract double energy_rate { get; }
+ public abstract double voltage { get; }
+ public abstract int32 charge_cycles { get; }
+ public abstract double luminosity { get; }
+ public abstract int64 time_to_empty { get; }
+ public abstract int64 time_to_full { get; }
+ public abstract double percentage { get; }
+ public abstract double temperature { get; }
+ public abstract bool is_present { get; }
+ public abstract uint state { get; }
+ public abstract bool is_rechargable { get; }
+ public abstract double capacity { get; }
+ public abstract uint technology { get; }
+ public abstract uint32 warning_level { get; }
+ public abstract uint32 battery_level { get; }
+ public abstract string icon_name { owned get; }
+}
+
+// private struct AstalBattery.HistoryDataPoint {
+// uint32 time;
+// double value;
+// uint32 state;
+// }
+//
+// private struct AstalBattery.StatisticsDataPoint {
+// double value;
+// double accuracy;
+// }
diff --git a/lib/sway/meson.build b/lib/sway/meson.build
new file mode 100644
index 0000000..ec8e3d0
--- /dev/null
+++ b/lib/sway/meson.build
@@ -0,0 +1,114 @@
+project(
+ 'astal-battery',
+ 'vala',
+ 'c',
+ version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(),
+ meson_version: '>= 0.62.0',
+ default_options: [
+ 'warning_level=2',
+ 'werror=false',
+ 'c_std=gnu11',
+ ],
+)
+
+assert(
+ get_option('lib') or get_option('cli'),
+ 'Either lib or cli option must be set to true.',
+)
+
+version_split = meson.project_version().split('.')
+api_version = version_split[0] + '.' + version_split[1]
+gir = 'AstalBattery-' + api_version + '.gir'
+typelib = 'AstalBattery-' + api_version + '.typelib'
+
+config = configure_file(
+ input: 'config.vala.in',
+ output: 'config.vala',
+ configuration: {
+ 'API_VERSION': api_version,
+ 'VERSION': meson.project_version(),
+ 'MAJOR_VERSION': version_split[0],
+ 'MINOR_VERSION': version_split[1],
+ 'MICRO_VERSION': version_split[2],
+ },
+)
+
+pkgconfig_deps = [
+ dependency('glib-2.0'),
+ dependency('gio-2.0'),
+ dependency('gobject-2.0'),
+ dependency('json-glib-1.0'),
+]
+
+deps = pkgconfig_deps + meson.get_compiler('c').find_library('m')
+
+sources = [config] + files(
+ 'device.vala',
+ 'ifaces.vala',
+ 'upower.vala',
+)
+
+if get_option('lib')
+ lib = library(
+ meson.project_name(),
+ sources,
+ dependencies: deps,
+ vala_args: ['--vapi-comments'],
+ vala_header: meson.project_name() + '.h',
+ vala_vapi: meson.project_name() + '-' + api_version + '.vapi',
+ version: meson.project_version(),
+ install: true,
+ install_dir: [true, true, true],
+ )
+
+ pkgs = []
+ foreach dep : pkgconfig_deps
+ pkgs += ['--pkg=' + dep.name()]
+ endforeach
+
+ gir_tgt = custom_target(
+ gir,
+ command: [find_program('python3'), files('gir.py'), meson.project_name(), gir]
+ + pkgs
+ + sources,
+ input: sources,
+ depends: lib,
+ output: gir,
+ install: true,
+ install_dir: get_option('datadir') / 'gir-1.0',
+ )
+
+ custom_target(
+ typelib,
+ command: [
+ find_program('g-ir-compiler'),
+ '--output', '@OUTPUT@',
+ '--shared-library', get_option('prefix') / get_option('libdir') / '@PLAINNAME@',
+ meson.current_build_dir() / gir,
+ ],
+ input: lib,
+ output: typelib,
+ depends: [lib, gir_tgt],
+ install: true,
+ install_dir: get_option('libdir') / 'girepository-1.0',
+ )
+
+ import('pkgconfig').generate(
+ lib,
+ name: meson.project_name(),
+ filebase: meson.project_name() + '-' + api_version,
+ version: meson.project_version(),
+ subdirs: meson.project_name(),
+ requires: pkgconfig_deps,
+ install_dir: get_option('libdir') / 'pkgconfig',
+ )
+endif
+
+if get_option('cli')
+ executable(
+ meson.project_name(),
+ ['cli.vala', sources],
+ dependencies: deps,
+ install: true,
+ )
+endif
diff --git a/lib/sway/meson_options.txt b/lib/sway/meson_options.txt
new file mode 100644
index 0000000..f110242
--- /dev/null
+++ b/lib/sway/meson_options.txt
@@ -0,0 +1,11 @@
+option(
+ 'lib',
+ type: 'boolean',
+ value: true,
+)
+
+option(
+ 'cli',
+ type: 'boolean',
+ value: true,
+)
diff --git a/lib/sway/upower.vala b/lib/sway/upower.vala
new file mode 100644
index 0000000..ea5946a
--- /dev/null
+++ b/lib/sway/upower.vala
@@ -0,0 +1,80 @@
+/**
+ * Client for the UPower [[https://upower.freedesktop.org/docs/UPower.html|dbus interface]].
+ */
+public class AstalBattery.UPower : Object {
+ private IUPower proxy;
+ private HashTable<string, Device> _devices =
+ new HashTable<string, Device>(str_hash, str_equal);
+
+ /** List of UPower devices. */
+ public List<weak Device> devices {
+ owned get { return _devices.get_values(); }
+ }
+
+ /** Emitted when a new device is connected. */
+ public signal void device_added(Device device);
+
+ /** Emitted a new device is disconnected. */
+ public signal void device_removed(Device device);
+
+ /** A composite device that represents the battery status. */
+ public Device display_device { owned get { return Device.get_default(); }}
+
+ public string daemon_version { owned get { return proxy.daemon_version; } }
+
+ /** Indicates whether the system is running on battery power. */
+ public bool on_battery { get { return proxy.on_battery; } }
+
+ /** Indicates if the laptop lid is closed where the display cannot be seen. */
+ public bool lid_is_closed { get { return proxy.lid_is_closed; } }
+
+ /** Indicates if the system has a lid device. */
+ public bool lid_is_present { get { return proxy.lid_is_closed; } }
+
+ /**
+ * When the system's power supply is critical (critically low batteries or UPS),
+ * the system will take this action.
+ */
+ public string critical_action {
+ owned get {
+ try {
+ return proxy.get_critical_action();
+ } catch (Error error) {
+ critical(error.message);
+ return "";
+ }
+ }
+ }
+
+ construct {
+ try {
+ proxy = Bus.get_proxy_sync(
+ BusType.SYSTEM,
+ "org.freedesktop.UPower",
+ "/org/freedesktop/UPower"
+ );
+
+ foreach (var path in proxy.enumerate_devices())
+ _devices.set(path, new Device(path));
+
+ proxy.device_added.connect((path) => {
+ try {
+ var d = new Device(path);
+ _devices.set(path, d);
+ device_added(d);
+ notify_property("devices");
+ } catch (Error err) {
+ critical(err.message);
+ }
+ });
+
+ proxy.device_removed.connect((path) => {
+ device_removed(_devices.get(path));
+ _devices.remove(path);
+ notify_property("devices");
+ });
+ } catch (Error error) {
+ critical(error.message);
+ }
+ }
+}
diff --git a/lib/sway/version b/lib/sway/version
new file mode 100644
index 0000000..6e8bf73
--- /dev/null
+++ b/lib/sway/version
@@ -0,0 +1 @@
+0.1.0