diff options
Diffstat (limited to 'lib/powerprofiles')
-rw-r--r-- | lib/powerprofiles/cli.vala | 80 | ||||
-rw-r--r-- | lib/powerprofiles/config.vala.in | 6 | ||||
-rw-r--r-- | lib/powerprofiles/meson.build | 93 | ||||
-rw-r--r-- | lib/powerprofiles/meson_options.txt | 11 | ||||
-rw-r--r-- | lib/powerprofiles/power-profiles.vala | 205 | ||||
-rw-r--r-- | lib/powerprofiles/version | 1 |
6 files changed, 396 insertions, 0 deletions
diff --git a/lib/powerprofiles/cli.vala b/lib/powerprofiles/cli.vala new file mode 100644 index 0000000..7be01d2 --- /dev/null +++ b/lib/powerprofiles/cli.vala @@ -0,0 +1,80 @@ +static bool help; +static bool version; +static bool daemonize; +static bool list; +static string set; + +const OptionEntry[] options = { + { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null }, + { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null }, + { "daemonize", 'd', OptionFlags.NONE, OptionArg.NONE, ref daemonize, null, null }, + { "list", 'l', OptionFlags.NONE, OptionArg.NONE, ref list, null, null }, + { "set", 's', OptionFlags.NONE, OptionArg.STRING, ref set, 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(" -d, --daemonize Monitor for changes\n"); + print(" -l, --list List available profiles\n"); + return 0; + } + + if (version) { + print(AstalPowerProfiles.VERSION); + return 0; + } + + var profiles = AstalPowerProfiles.get_default(); + if (set != null) { + profiles.active_profile = set; + } + + else if (list) { + foreach (var p in profiles.profiles) { + print("%s\n", p.profile); + } + return 0; + } + + if (daemonize) { + var loop = new MainLoop(); + + stdout.printf("%s\n", profiles.to_json_string()); + stdout.flush(); + + profiles.notify.connect(() => { + stdout.printf("%s\n", profiles.to_json_string()); + stdout.flush(); + }); + + profiles.profile_released.connect(() => { + stdout.printf("%s\n", profiles.to_json_string()); + stdout.flush(); + }); + + loop.run(); + } + + if (set == null && !daemonize) { + stdout.printf("%s\n", profiles.to_json_string()); + } + + return 0; +} diff --git a/lib/powerprofiles/config.vala.in b/lib/powerprofiles/config.vala.in new file mode 100644 index 0000000..79034f1 --- /dev/null +++ b/lib/powerprofiles/config.vala.in @@ -0,0 +1,6 @@ +namespace AstalPowerProfiles { + 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/powerprofiles/meson.build b/lib/powerprofiles/meson.build new file mode 100644 index 0000000..d0fe78f --- /dev/null +++ b/lib/powerprofiles/meson.build @@ -0,0 +1,93 @@ +project( + 'astal-power-profiles', + '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 = 'AstalPowerProfiles-' + api_version + '.gir' +typelib = 'AstalPowerProfiles-' + api_version + '.typelib' + +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('gobject-2.0'), + dependency('gio-2.0'), + dependency('json-glib-1.0'), +] + +sources = [ + config, + 'power-profiles.vala', +] + +if get_option('lib') + lib = library( + meson.project_name(), + sources, + dependencies: deps, + vala_header: meson.project_name() + '.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/lib/powerprofiles/meson_options.txt b/lib/powerprofiles/meson_options.txt new file mode 100644 index 0000000..f110242 --- /dev/null +++ b/lib/powerprofiles/meson_options.txt @@ -0,0 +1,11 @@ +option( + 'lib', + type: 'boolean', + value: true, +) + +option( + 'cli', + type: 'boolean', + value: true, +) diff --git a/lib/powerprofiles/power-profiles.vala b/lib/powerprofiles/power-profiles.vala new file mode 100644 index 0000000..ab98505 --- /dev/null +++ b/lib/powerprofiles/power-profiles.vala @@ -0,0 +1,205 @@ +namespace AstalPowerProfiles { +[DBus (name = "org.freedesktop.UPower.PowerProfiles")] +private interface IPowerProfiles : DBusProxy { + public abstract string[] actions { owned get; } + public abstract string active_profile { owned get; set; } + 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 void 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; +} + +public PowerProfiles get_default() { + return PowerProfiles.get_default(); +} + +public class PowerProfiles : Object { + private static PowerProfiles instance; + public static PowerProfiles get_default() { + if (instance == null) + instance = new PowerProfiles(); + + return instance; + } + + private IPowerProfiles proxy; + + construct { + try { + proxy = Bus.get_proxy_sync( + GLib.BusType.SYSTEM, + "org.freedesktop.UPower.PowerProfiles", + "/org/freedesktop/UPower/PowerProfiles" + ); + + proxy.profile_released.connect((cookie) => profile_released(cookie)); + proxy.g_properties_changed.connect((props) => { + var map = (HashTable<string, Variant>)props; + foreach (var key in map.get_keys()) { + notify_property(kebab_case(key)); + if (key == "ActiveProfile") + notify_property("icon-name"); + } + }); + } catch (Error error){ + critical(error.message); + } + } + + public string active_profile { + owned get { return proxy.active_profile; } + set { proxy.active_profile = value; } + } + + public string icon_name { + owned get { return @"power-profile-$active_profile-symbolic"; } + } + + public string[] actions { + owned get { return proxy.actions.copy(); } + } + + public Hold[] active_profile_holds { + owned get { + Hold[] holds = new Hold[proxy.active_profile_holds.length]; + for (var i = 0; i < proxy.active_profile_holds.length; ++i) { + var hold = proxy.active_profile_holds[i]; + holds[i] = Hold() { + application_id = hold.get("ApplicationId").get_string(), + profile = hold.get("Profile").get_string(), + reason = hold.get("Reason").get_string() + }; + } + return holds; + } + } + + public string performance_degraded { + owned get { return proxy.performance_degraded; } + } + + public string performance_inhibited { + owned get { return proxy.performance_degraded; } + } + + public Profile[] profiles { + owned get { + Profile[] profs = new Profile[proxy.profiles.length]; + for (var i = 0; i < proxy.profiles.length; ++i) { + var prof = proxy.profiles[i]; + profs[i] = Profile() { + profile = prof.get("Profile").get_string(), + cpu_driver = prof.get("CpuDriver").get_string(), + platform_driver = prof.get("PlatformDriver").get_string(), + driver = prof.get("Driver").get_string() + }; + } + return profs; + } + } + + public string version { + owned get { return proxy.version; } + } + + public signal void profile_released (uint cookie); + + public int hold_profile(string profile, string reason, string application_id) { + try { + return (int)proxy.hold_profile(profile, reason, application_id); + } catch (Error error) { + critical(error.message); + return -1; + } + } + + public void release_profile(uint cookie) { + try { + proxy.release_profile(cookie); + } catch (Error error) { + critical(error.message); + } + } + + public string to_json_string() { + var acts = new Json.Builder().begin_array(); + foreach (var action in actions) { + acts.add_string_value(action); + } + + var active_holds = new Json.Builder().begin_array(); + foreach (var action in active_profile_holds) { + active_holds.add_value(new Json.Builder() + .begin_object() + .set_member_name("application_id").add_string_value(action.application_id) + .set_member_name("profile").add_string_value(action.profile) + .set_member_name("reason").add_string_value(action.reason) + .end_object() + .get_root()); + } + + var profs = new Json.Builder().begin_array(); + foreach (var prof in profiles) { + profs.add_value(new Json.Builder() + .begin_object() + .set_member_name("profie").add_string_value(prof.profile) + .set_member_name("driver").add_string_value(prof.driver) + .set_member_name("cpu_driver").add_string_value(prof.cpu_driver) + .set_member_name("platform_driver").add_string_value(prof.platform_driver) + .end_object() + .get_root()); + } + + return Json.to_string(new Json.Builder() + .begin_object() + .set_member_name("active_profile").add_string_value(active_profile) + .set_member_name("icon_name").add_string_value(icon_name) + .set_member_name("performance_degraded").add_string_value(performance_degraded) + .set_member_name("performance_inhibited").add_string_value(performance_inhibited) + .set_member_name("actions").add_value(acts.end_array().get_root()) + .set_member_name("active_profile_holds").add_value(active_holds.end_array().get_root()) + .set_member_name("profiles").add_value(profs.end_array().get_root()) + .end_object() + .get_root(), false); + } +} + +public struct Profile { + public string profile; + public string cpu_driver; + public string platform_driver; + public string driver; +} + +public struct Hold { + public string application_id; + public string profile; + public string reason; +} + +private string kebab_case(string pascal_case) { + StringBuilder kebab_case = new StringBuilder(); + + for (int i = 0; i < pascal_case.length; i++) { + char c = pascal_case[i]; + + if (c >= 'A' && c <= 'Z') { + if (i != 0) { + kebab_case.append_c('-'); + } + + kebab_case.append_c((char)(c + 32)); + } else { + kebab_case.append_c(c); + } + } + + return kebab_case.str; +} +} diff --git a/lib/powerprofiles/version b/lib/powerprofiles/version new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/lib/powerprofiles/version @@ -0,0 +1 @@ +0.1.0 |