diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cli.vala | 50 | ||||
-rw-r--r-- | src/config.vala.in | 6 | ||||
-rw-r--r-- | src/device.vala | 148 | ||||
-rw-r--r-- | src/ifaces.vala | 90 | ||||
-rw-r--r-- | src/kbd.vala | 64 | ||||
-rw-r--r-- | src/meson.build | 80 | ||||
-rw-r--r-- | src/profiles.vala | 65 | ||||
-rw-r--r-- | src/upower.vala | 58 | ||||
-rw-r--r-- | src/utils.vala | 36 |
9 files changed, 597 insertions, 0 deletions
diff --git a/src/cli.vala b/src/cli.vala new file mode 100644 index 0000000..85e70ae --- /dev/null +++ b/src/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", AstalBattery.to_json(battery)); + + if (monitor) { + battery.notify.connect(() => { + print("%s\n", AstalBattery.to_json(battery)); + }); + new GLib.MainLoop(null, false).run(); + } + + return 0; +} diff --git a/src/config.vala.in b/src/config.vala.in new file mode 100644 index 0000000..6e7f77e --- /dev/null +++ b/src/config.vala.in @@ -0,0 +1,6 @@ +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/src/device.vala b/src/device.vala new file mode 100644 index 0000000..2dc03eb --- /dev/null +++ b/src/device.vala @@ -0,0 +1,148 @@ +namespace AstalBattery { +public Device get_default() { + return Device.get_default(); +} + +public class Device : Object { + private static Device display_device; + public static Device? get_default() { + if (display_device != null) + return display_device; + + try { + display_device = new Device( + "/org/freedesktop/UPower/devices/DisplayDevice"); + + return display_device; + } catch (Error error) { + critical(error.message); + } + return null; + } + + private Properties props; + private IUPowerDevice proxy; + + public DeviceType device_type { + get { + Value value = Value(Type.UINT); + proxy.get_property("name", ref value); + return value.get_uint(); + } + } + + public string native_path { owned get { return proxy.native_path; } } + public string vendor { owned get { return proxy.vendor; } } + public string model { owned get { return proxy.model; } } + public string serial { owned get { return proxy.serial; } } + public uint64 update_time { get { return proxy.update_time; } } + public bool power_supply { get { return proxy.power_supply; } } + public bool has_history { get { return proxy.has_history; } } + public bool has_statistics { get { return proxy.has_statistics; } } + public bool online { get { return proxy.online; } } + public double energy { get { return proxy.energy; } } + public double energy_empty { get { return proxy.energy_empty; } } + public double energy_full { get { return proxy.energy_full; } } + public double energy_full_design { get { return proxy.energy_full_design; } } + public double energy_rate { get { return proxy.energy_rate; } } + public double voltage { get { return proxy.voltage; } } + public int charge_cycles { get { return proxy.charge_cycles; } } + public double luminosity { get { return proxy.luminosity; } } + public int64 time_to_empty { get { return proxy.time_to_empty; } } + public int64 time_to_full { get { return proxy.time_to_full; }} + public double percentage { get { return proxy.percentage / 100; } } + public double temperature { get { return proxy.temperature; } } + public bool is_present { get { return proxy.is_present; } } + public DeviceState state { get { return proxy.state; } } + public bool is_rechargable { get { return proxy.is_rechargable; } } + public double capacitiy { get { return proxy.capacitiy; } } + public DeviceTechnology technology { get { return proxy.technology; } } + public WarningLevel warning_level { get { return proxy.warning_level; } } + public BatteryLevel battery_level { get { return proxy.battery_level; } } + public string icon_name { owned get { return proxy.icon_name; } } + + public Device(string path) throws Error { + proxy = Bus.get_proxy_sync(BusType.SYSTEM, "org.freedesktop.UPower", path); + props = Bus.get_proxy_sync(BusType.SYSTEM, "org.freedesktop.UPower", path); + + props.properties_changed.connect((iface, vardict) => { + foreach (var key in vardict.get_keys()) { + var prop = pascal_to_kebab_case(key); + if (get_class().find_property(prop) != null) + notify_property(prop); + } + }); + } +} + +public enum DeviceType { + UNKNOWN = 0, + 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, +} + +public enum DeviceState { + UNKNOWN = 0, + CHARGING, + DISCHARGING, + EMPTY, + FULLY_CHARGED, + PENDING_CHARGE, + PENDING_DISCHARGE, +} + +public enum DeviceTechnology { + UNKNOWN = 0, + LITHIUM_ION, + LITHIUM_POLYMER, + LITHIUM_IRON_PHOSPHATE, + LEAD_ACID, + NICKEL_CADMIUM, + NICKEL_METAL_HYDRIDE, +} + +public enum WarningLevel { + UNKNOWN = 0, + NONE, + DISCHARGING, + LOW, + CRITICIAL, + ACTION, +} + +public enum BatteryLevel { + UNKNOWN = 0, + NONE, + LOW, + CRITICIAL, + NORMAL, + HIGH, + FULL, +} +} diff --git a/src/ifaces.vala b/src/ifaces.vala new file mode 100644 index 0000000..9c769d1 --- /dev/null +++ b/src/ifaces.vala @@ -0,0 +1,90 @@ +namespace AstalBattery { +[DBus (name = "org.freedesktop.DBus.Properties")] +public interface Properties : Object { + public signal void properties_changed ( + string iface_name, + HashTable<string, Variant> props, + string[] invalidated_props + ); +} + +[DBus (name = "org.freedesktop.UPower")] +interface IUPower : Object { + public abstract string[] enumerate_devices() throws Error; + public abstract string get_display_device() throws Error; + public abstract string get_critical_action() throws Error; + + public signal void device_added(string object_path); + public signal void device_removed(string 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 lis_is_present { get; } +} + +[DBus (name = "org.freedesktop.UPower.KbdBacklight")] +interface IUPowerKdbBacklight : Object { + public abstract int get_brightness() throws Error; + public abstract int get_max_brightness() throws Error; + public abstract void set_brightness(int value) throws Error; + + public signal void brightness_changed(int value); + public signal void brightness_changed_with_source(int value, string source); +} + +[DBus (name = "org.freedesktop.UPower.Device")] +public interface IUPowerDevice : Object { + // public abstract void refresh() throws Error; + // public abstract void get_history(); + // public abstract void get_statistics(); + + // incompatible with gobject + // 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 int 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 capacitiy { get; } + public abstract uint technology { get; } + public abstract uint warning_level { get; } + public abstract uint battery_level { get; } + public abstract string icon_name { owned get; } +} + +[DBus (name = "org.freedesktop.UPower.PowerProfiles")] +public interface IPowerProfiles : Object { + public abstract string[] actions { owned get; } + public abstract string active_profile { owned get; } + public abstract HashTable<string, Variant>[] active_profile_holds { owned get; } + public abstract string performance_degraded { owned get; } + public abstract string performance_inhibited { owned get; } + public abstract HashTable<string, Variant>[] profiles { owned get; } + public abstract string version { owned get; } + + public signal uint profile_released (uint cookie); + + public abstract uint hold_profile(string profile, string reason, string application_id) throws Error; + public abstract void release_profile(uint cookie) throws Error; +} +} diff --git a/src/kbd.vala b/src/kbd.vala new file mode 100644 index 0000000..13d722e --- /dev/null +++ b/src/kbd.vala @@ -0,0 +1,64 @@ +namespace AstalBattery { +public KbdBacklight get_keyboard() { + return KbdBacklight.get_default(); +} + +public class KbdBacklight : Object { + private IUPowerKdbBacklight proxy; + + private static KbdBacklight instance; + public static KbdBacklight? get_default() { + if (instance != null) + return instance; + + try { + instance = new KbdBacklight(); + return instance; + } catch (Error error) { + critical(error.message); + return null; + } + } + + public int brightness { + get { + try { + return proxy.get_brightness(); + } catch (Error error) { + critical(error.message); + return 0; + } + } + set { + try { + proxy.set_brightness(value); + } catch (Error error) { + critical(error.message); + } + } + } + + public int max_brightness { + get { + try { + return proxy.get_max_brightness(); + } catch (Error error) { + critical(error.message); + return 0; + } + } + } + + public KbdBacklight() throws Error { + proxy = Bus.get_proxy_sync( + BusType.SYSTEM, + "org.freedesktop.UPower", + "/org/freedesktop/UPower/KbdBacklight" + ); + + proxy.brightness_changed.connect(() => { + notify_property("brightness"); + }); + } +} +} diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..9de2aad --- /dev/null +++ b/src/meson.build @@ -0,0 +1,80 @@ +version_split = meson.project_version().split('.') +api_version = version_split[0] + '.' + version_split[1] +gir = 'AstalBattery-' + api_version + '.gir' +typelib = 'AstalBattery-' + api_version + '.typelib' +so = 'lib' + meson.project_name() + '.so.' + meson.project_version() + +config = configure_file( + input: 'config.vala.in', + output: 'config.vala', + configuration: { + 'VERSION': meson.project_version(), + 'MAJOR_VERSION': version_split[0], + 'MINOR_VERSION': version_split[1], + 'MICRO_VERSION': version_split[2], + }, +) + +deps = [ + dependency('glib-2.0'), + dependency('gio-2.0'), + dependency('gobject-2.0'), +] + +sources = [ + config, + 'ifaces.vala', + 'device.vala', + 'upower.vala', + 'kbd.vala', + 'profiles.vala', + 'utils.vala', +] + +if get_option('lib') + lib = library( + meson.project_name(), + sources, + dependencies: deps, + vala_header: meson.project_name() + '-' + api_version + '.h', + vala_vapi: meson.project_name() + '-' + api_version + '.vapi', + vala_gir: gir, + version: meson.project_version(), + install: true, + install_dir: [true, true, true, true], + ) + + import('pkgconfig').generate( + lib, + name: meson.project_name(), + filebase: meson.project_name() + '-' + api_version, + version: meson.project_version(), + subdirs: meson.project_name(), + requires: deps, + install_dir: get_option('libdir') / 'pkgconfig', + ) + + 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, + install: true, + install_dir: get_option('libdir') / 'girepository-1.0', + ) +endif + +if get_option('cli') + executable( + meson.project_name(), + ['cli.vala', sources], + dependencies: deps, + install: true, + ) +endif diff --git a/src/profiles.vala b/src/profiles.vala new file mode 100644 index 0000000..4b43923 --- /dev/null +++ b/src/profiles.vala @@ -0,0 +1,65 @@ +namespace AstalBattery { +public PowerProfiles get_power_profiles() { + return PowerProfiles.get_default(); +} + +public class PowerProfiles : Object { + private static PowerProfiles instance; + public static PowerProfiles get_default() { + if (instance != null) + return instance; + + instance = new PowerProfiles(); + return instance; + } + + private IPowerProfiles proxy; + private Properties props; + + public string[] actions { owned get { return proxy.actions; } } + public string active_profile { owned get { return proxy.active_profile; } } + public HashTable<string, Variant>[] active_profile_holds { owned get { return proxy.active_profile_holds; } } + public string performance_degraded { owned get { return proxy.performance_degraded; } } + public string performance_inhibited { owned get { return proxy.performance_inhibited; } } + public HashTable<string, Variant>[] profiles { owned get { return proxy.profiles; } } + public string version { owned get { return proxy.version; } } + + public signal uint profile_released (uint cookie); + + public uint hold_profile(string profile, string reason, string application_id) throws Error { + return proxy.hold_profile(profile, reason, application_id); + } + + public void release_profile(uint cookie) throws Error { + proxy.release_profile(cookie); + } + + construct { + try { + proxy = Bus.get_proxy_sync( + BusType.SYSTEM, + "org.freedesktop.UPower.PowerProfiles", + "/org/freedesktop/UPower/PowerProfiles" + ); + + props = Bus.get_proxy_sync( + BusType.SYSTEM, + "org.freedesktop.UPower.PowerProfiles", + "/org/freedesktop/UPower/PowerProfiles" + ); + + props.properties_changed.connect((iface, vardict) => { + foreach (var key in vardict.get_keys()) { + var prop = pascal_to_kebab_case(key); + if (get_class().find_property(prop) != null) + notify_property(prop); + } + }); + + proxy.profile_released.connect((cookie) => profile_released(cookie)); + } catch (Error error) { + critical(error.message); + } + } +} +} diff --git a/src/upower.vala b/src/upower.vala new file mode 100644 index 0000000..9c18ffd --- /dev/null +++ b/src/upower.vala @@ -0,0 +1,58 @@ +namespace AstalBattery { +public class UPower : Object { + private IUPower proxy; + private HashTable<string, Device> _devices = + new HashTable<string, Device>(str_hash, str_equal); + + public List<weak Device> devices { + owned get { return _devices.get_values(); } + } + + public signal void device_added(Device device); + public signal void device_removed(Device device); + + public Device display_device { owned get { return Device.get_default(); }} + + public string daemon_version { owned get { return proxy.daemon_version; } } + public bool on_battery { get { return proxy.on_battery; } } + public bool lid_is_closed { get { return proxy.lid_is_closed; } } + public bool lis_is_present { get { return proxy.lid_is_closed; } } + + 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) => { + _devices.set(path, new Device(path)); + notify_property("devices"); + }); + + 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/src/utils.vala b/src/utils.vala new file mode 100644 index 0000000..de22f18 --- /dev/null +++ b/src/utils.vala @@ -0,0 +1,36 @@ +namespace AstalBattery{ +internal string pascal_to_kebab_case(string pascal) { + StringBuilder kebab = new StringBuilder(); + + for (int i = 0; i < pascal.length; i++) { + char c = pascal[i]; + if (c.isupper()) { + if (i != 0) + kebab.append_c('-'); + + kebab.append_c(c.tolower()); + } else { + kebab.append_c(c); + } + } + + return kebab.str; +} + +internal string to_json(AstalBattery.Device device) { + string s = "unknown"; + if (device.state == AstalBattery.DeviceState.CHARGING) + s = "charging"; + if (device.state == AstalBattery.DeviceState.DISCHARGING) + s = "discharging"; + if (device.state == AstalBattery.DeviceState.FULLY_CHARGED) + s = "fully_charged"; + + var p = device.percentage; + var i = device.icon_name; + var r = device.state == AstalBattery.DeviceState.CHARGING + ? device.time_to_full : device.time_to_empty; + + return "{ \"percentage\": %f, \"state\": \"%s\", \"icon_name\": \"%s\", \"time_remaining\": %f }".printf(p, s, i, r); +} +} |