From a4ce7800845c3fd24783680718e75cf1fe6aaaf8 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Sun, 13 Sep 2020 20:26:46 +0300 Subject: [PATCH] Implement some daemons --- .gitignore | 1 + Cargo.lock | 351 +++++++++ Cargo.nix | 1570 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 + README.md | 23 + battery/Cargo.toml | 11 + battery/src/main.rs | 122 ++++ bluetooth/Cargo.toml | 11 + bluetooth/src/main.rs | 43 ++ brightness/Cargo.toml | 11 + brightness/src/main.rs | 35 + common/Cargo.toml | 12 + common/src/lib.rs | 179 +++++ default.nix | 3 + flake.lock | 26 + flake.nix | 57 ++ pulseaudio/Cargo.toml | 11 + pulseaudio/src/main.rs | 84 +++ shell.nix | 3 + 19 files changed, 2562 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.nix create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 battery/Cargo.toml create mode 100644 battery/src/main.rs create mode 100644 bluetooth/Cargo.toml create mode 100644 bluetooth/src/main.rs create mode 100644 brightness/Cargo.toml create mode 100644 brightness/src/main.rs create mode 100644 common/Cargo.toml create mode 100644 common/src/lib.rs create mode 100644 default.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 pulseaudio/Cargo.toml create mode 100644 pulseaudio/src/main.rs create mode 100644 shell.nix diff --git a/.gitignore b/.gitignore index b25e81f..8e219d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ */target +target/ result diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b784b64 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,351 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backlight" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dd1f0fb2c34052222f8937bd7ab412c664e8436e2c775d1b64836974889f21" + +[[package]] +name = "battery" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8775be4956c98c9ac7c11cc383d632636935d3a688dabddb71ac83ba00a3a72f" +dependencies = [ + "cfg-if", + "core-foundation", + "lazycell", + "libc", + "mach", + "num-traits", + "uom", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blurz" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1bc2df4d0f1d373b324672d9966aca3f5ba1f03d69edad6240144774539ea59" +dependencies = [ + "dbus", + "hex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "configparser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe1d7dcda7d1da79e444bdfba1465f2f849a58b07774e1df473ee77030cb47a7" + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "dbus" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819" +dependencies = [ + "libc", + "libdbus-sys", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16160d212ae91abe9f3324c3fb233929ba322dde63585d15cda3336f8c529ed1" +dependencies = [ + "gdk-pixbuf-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "798f97101eea8180da363d0e80e07ec7ec6d1809306601c0100c1de5bc8b4f52" +dependencies = [ + "bitflags", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "gio-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a303bbf7a5e75ab3b627117ff10e495d1b9e97e1d68966285ac2b1f6270091bc" +dependencies = [ + "bitflags", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "glib" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b0452824cc63066940f01adc721804919f0b76cdba3cfab977b00b87f16d4a" +dependencies = [ + "bitflags", + "glib-sys", + "gobject-sys", + "lazy_static", + "libc", +] + +[[package]] +name = "glib-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9693049613ff52b93013cc3d2590366d8e530366d288438724b73f6c7dc4be8" +dependencies = [ + "bitflags", + "libc", + "pkg-config", +] + +[[package]] +name = "gobject-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d507c87a71b1143c66ed21a969be9b99a76df234b342d733e787e6c9c7d7c2" +dependencies = [ + "bitflags", + "glib-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" + +[[package]] +name = "libdbus-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libnotify" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10506a4f8bc6f8f7ccc6fde3a8290378d7aed3d1a26dca606a73e2ffe140cc2d" +dependencies = [ + "gdk-pixbuf", + "gdk-pixbuf-sys", + "glib", + "glib-sys", + "gobject-sys", + "libnotify-sys", +] + +[[package]] +name = "libnotify-sys" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a716b9b7d24ed10f1eb431e1527fa13c9a4bf2d4fa68bb3e54da1d0747383c" +dependencies = [ + "bitflags", + "gdk-pixbuf-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "libpulse-binding" +version = "2.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1795efad67f34ae95740ded62e3c346e707b0caeb0f6088d75958e97316b5538" +dependencies = [ + "libc", + "libpulse-sys", + "winapi", +] + +[[package]] +name = "libpulse-sys" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f53857071d564f864755a6945d316cc8fe62c36ed20bd4231c8ab6b62141786" +dependencies = [ + "libc", + "pkg-config", + "winapi", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "pkg-config" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" + +[[package]] +name = "simple-osd-battery" +version = "0.1.0" +dependencies = [ + "battery", + "simple-osd-common", +] + +[[package]] +name = "simple-osd-bluetooth" +version = "0.1.0" +dependencies = [ + "blurz", + "simple-osd-common", +] + +[[package]] +name = "simple-osd-brightness" +version = "0.1.0" +dependencies = [ + "backlight", + "simple-osd-common", +] + +[[package]] +name = "simple-osd-common" +version = "0.1.0" +dependencies = [ + "configparser", + "libnotify", + "xdg", +] + +[[package]] +name = "simple-osd-pulseaudio" +version = "0.1.0" +dependencies = [ + "libpulse-binding", + "simple-osd-common", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "uom" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb593f5252356bfb829112f8fca2d0982d48588d2d6bb5a92553b0dfc4c9aba" +dependencies = [ + "num-traits", + "typenum", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" diff --git a/Cargo.nix b/Cargo.nix new file mode 100644 index 0000000..ed3bb47 --- /dev/null +++ b/Cargo.nix @@ -0,0 +1,1570 @@ + +# This file was @generated by crate2nix 0.8.0 with the command: +# "generate" +# See https://github.com/kolloch/crate2nix for more info. + +{ nixpkgs ? +, pkgs ? import nixpkgs { config = {}; } +, lib ? pkgs.lib +, stdenv ? pkgs.stdenv +, buildRustCrate ? pkgs.buildRustCrate + # This is used as the `crateOverrides` argument for `buildRustCrate`. +, defaultCrateOverrides ? pkgs.defaultCrateOverrides + # The features to enable for the root_crate or the workspace_members. +, rootFeatures ? [ "default" ] + # If true, throw errors instead of issueing deprecation warnings. +, strictDeprecation ? false + # Whether to perform release builds: longer compile times, faster binaries. +, release ? true +}: + +rec { + # + # "public" attributes that we attempt to keep stable with new versions of crate2nix. + # + + + # Refer your crate build derivation by name here. + # You can override the features with + # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. + workspaceMembers = { + "simple-osd-battery" = rec { + packageId = "simple-osd-battery"; + build = internal.buildRustCrateWithFeatures { + packageId = "simple-osd-battery"; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; + "simple-osd-bluetooth" = rec { + packageId = "simple-osd-bluetooth"; + build = internal.buildRustCrateWithFeatures { + packageId = "simple-osd-bluetooth"; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; + "simple-osd-brightness" = rec { + packageId = "simple-osd-brightness"; + build = internal.buildRustCrateWithFeatures { + packageId = "simple-osd-brightness"; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; + "simple-osd-common" = rec { + packageId = "simple-osd-common"; + build = internal.buildRustCrateWithFeatures { + packageId = "simple-osd-common"; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; + "simple-osd-pulseaudio" = rec { + packageId = "simple-osd-pulseaudio"; + build = internal.buildRustCrateWithFeatures { + packageId = "simple-osd-pulseaudio"; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; + }; + workspace_members = + internal.deprecationWarning + "workspace_members is deprecated in crate2nix 0.4. Please use workspaceMembers instead." + lib.mapAttrs (n: v: v.build) workspaceMembers; + + # + # "internal" ("private") attributes that may change in every new version of crate2nix. + # + + internal = rec { + # Build and dependency information for crates. + # Many of the fields are passed one-to-one to buildRustCrate. + # + # Noteworthy: + # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate. + # but with additional information which is used during dependency/feature resolution. + # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging. + # * `devDependencies` as of now not used by `buildRustCrate` but used to + # inject test dependencies into the build + + crates = { + "autocfg" = rec { + crateName = "autocfg"; + version = "1.0.1"; + edition = "2015"; + sha256 = "0jj6i9zn4gjl03kjvziqdji6rwx8ykz8zk2ngpc331z2g3fk3c6d"; + authors = [ + "Josh Stone " + ]; + + }; + "backlight" = rec { + crateName = "backlight"; + version = "0.1.1"; + edition = "2015"; + sha256 = "08czi1s6k0v43dfpfb3f8gl69ihjnixbsdw95wi54h63n87izpd1"; + authors = [ + "Jerko Steiner " + ]; + + }; + "battery" = rec { + crateName = "battery"; + version = "0.7.6"; + edition = "2018"; + sha256 = "0bx7lc0bm0xcf7dvvnl8lv9kasb36bb87hqwq73rm369ar4vwxc7"; + authors = [ + "svartalf " + ]; + dependencies = [ + { + name = "cfg-if"; + packageId = "cfg-if"; + } + { + name = "core-foundation"; + packageId = "core-foundation"; + target = { target, features }: ((target."os" == "macos") || (target."os" == "ios")); + } + { + name = "lazycell"; + packageId = "lazycell"; + target = { target, features }: (target."os" == "linux"); + } + { + name = "libc"; + packageId = "libc"; + target = { target, features }: ((target."os" == "dragonfly") || (target."os" == "freebsd")); + } + { + name = "libc"; + packageId = "libc"; + target = { target, features }: ((target."os" == "macos") || (target."os" == "ios")); + } + { + name = "mach"; + packageId = "mach"; + target = { target, features }: ((target."os" == "macos") || (target."os" == "ios")); + } + { + name = "num-traits"; + packageId = "num-traits"; + usesDefaultFeatures = false; + } + { + name = "uom"; + packageId = "uom"; + features = [ "autoconvert" "f32" "si" ]; + } + { + name = "winapi"; + packageId = "winapi"; + target = { target, features }: (target."os" == "windows"); + features = [ "impl-default" "devguid" "winbase" "ioapiset" "ntdef" "setupapi" "handleapi" "errhandlingapi" "winerror" ]; + } + ]; + + }; + "bitflags" = rec { + crateName = "bitflags"; + version = "1.2.1"; + edition = "2015"; + sha256 = "14qnd5nq8p2almk79m4m8ydqhd413yaxsyjp5xd19g3mikzf47fg"; + authors = [ + "The Rust Project Developers" + ]; + features = { + }; + resolvedDefaultFeatures = [ "default" ]; + }; + "blurz" = rec { + crateName = "blurz"; + version = "0.4.0"; + edition = "2015"; + sha256 = "0nga752pfi014kbdm7nn0cgvmxd3mik9jbb74jrp7lzis3s2vg5i"; + authors = [ + "Attila Dusnoki " + ]; + dependencies = [ + { + name = "dbus"; + packageId = "dbus"; + } + { + name = "hex"; + packageId = "hex"; + } + ]; + + }; + "cfg-if" = rec { + crateName = "cfg-if"; + version = "0.1.10"; + edition = "2018"; + sha256 = "08h80ihs74jcyp24cd75wwabygbbdgl05k6p5dmq8akbr78vv1a7"; + authors = [ + "Alex Crichton " + ]; + features = { + "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; + }; + }; + "configparser" = rec { + crateName = "configparser"; + version = "1.0.0"; + edition = "2018"; + sha256 = "19s7rcq71rry8zgy2x3pn1c9m11gbx3a3yxx8kj7knnilz6ps7gy"; + authors = [ + "QEDK " + ]; + + }; + "core-foundation" = rec { + crateName = "core-foundation"; + version = "0.7.0"; + edition = "2015"; + sha256 = "0wbias8f0m5kyn2pcksi0h58fdslams6nmf16w78fgn42dx4rljp"; + authors = [ + "The Servo Project Developers" + ]; + dependencies = [ + { + name = "core-foundation-sys"; + packageId = "core-foundation-sys"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + features = { + "mac_os_10_7_support" = [ "core-foundation-sys/mac_os_10_7_support" ]; + "mac_os_10_8_features" = [ "core-foundation-sys/mac_os_10_8_features" ]; + "with-chrono" = [ "chrono" ]; + "with-uuid" = [ "uuid" ]; + }; + }; + "core-foundation-sys" = rec { + crateName = "core-foundation-sys"; + version = "0.7.0"; + edition = "2015"; + sha256 = "1b5qfnnmg49sawwfsb0c0wbj81bqi7h7lh68pmhbidf0jjs1m9xk"; + authors = [ + "The Servo Project Developers" + ]; + features = { + }; + }; + "dbus" = rec { + crateName = "dbus"; + version = "0.6.5"; + edition = "2015"; + sha256 = "068qyxvaam34sjmhjgxz6iikklvylxly7gp6n00yksqydzrz1da8"; + authors = [ + "David Henningsson " + ]; + dependencies = [ + { + name = "libc"; + packageId = "libc"; + } + { + name = "libdbus-sys"; + packageId = "libdbus-sys"; + } + ]; + features = { + }; + }; + "gdk-pixbuf" = rec { + crateName = "gdk-pixbuf"; + version = "0.3.0"; + edition = "2015"; + sha256 = "1lcyaa66ycx3rlamsn33vqnk5fi974izphr46fgvw6p958hhs5hn"; + libName = "gdk_pixbuf"; + authors = [ + "The Gtk-rs Project Developers" + ]; + dependencies = [ + { + name = "gdk-pixbuf-sys"; + packageId = "gdk-pixbuf-sys"; + } + { + name = "glib"; + packageId = "glib"; + } + { + name = "glib-sys"; + packageId = "glib-sys"; + } + { + name = "gobject-sys"; + packageId = "gobject-sys"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + features = { + "dox" = [ "glib/dox" "gdk-pixbuf-sys/dox" ]; + "embed-lgpl-docs" = [ "gtk-rs-lgpl-docs" ]; + "purge-lgpl-docs" = [ "gtk-rs-lgpl-docs" ]; + "v2_28" = [ "gdk-pixbuf-sys/v2_28" ]; + "v2_30" = [ "v2_28" "gdk-pixbuf-sys/v2_30" ]; + "v2_32" = [ "v2_30" "gdk-pixbuf-sys/v2_32" ]; + "v2_36" = [ "v2_32" "gdk-pixbuf-sys/v2_36" ]; + }; + }; + "gdk-pixbuf-sys" = rec { + crateName = "gdk-pixbuf-sys"; + version = "0.5.0"; + edition = "2015"; + sha256 = "0ljgifyfa78c23002rih14c6vv67gvh803ix6vd810ga3q89g3vr"; + libName = "gdk_pixbuf_sys"; + authors = [ + "The Gtk-rs Project Developers" + ]; + dependencies = [ + { + name = "bitflags"; + packageId = "bitflags"; + } + { + name = "gio-sys"; + packageId = "gio-sys"; + } + { + name = "glib-sys"; + packageId = "glib-sys"; + } + { + name = "gobject-sys"; + packageId = "gobject-sys"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + buildDependencies = [ + { + name = "pkg-config"; + packageId = "pkg-config"; + } + ]; + features = { + "v2_30" = [ "v2_28" ]; + "v2_32" = [ "v2_30" ]; + "v2_36" = [ "v2_32" ]; + }; + }; + "gio-sys" = rec { + crateName = "gio-sys"; + version = "0.5.0"; + edition = "2015"; + sha256 = "1g4i00kzdcf2b8l6d2fnw6brw6sx947g2zqi4yvb6np7lpvvn0x3"; + libName = "gio_sys"; + authors = [ + "The Gtk-rs Project Developers" + ]; + dependencies = [ + { + name = "bitflags"; + packageId = "bitflags"; + } + { + name = "glib-sys"; + packageId = "glib-sys"; + } + { + name = "gobject-sys"; + packageId = "gobject-sys"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + buildDependencies = [ + { + name = "pkg-config"; + packageId = "pkg-config"; + } + ]; + features = { + "v2_36" = [ "v2_34" ]; + "v2_38" = [ "v2_36" ]; + "v2_40" = [ "v2_38" ]; + "v2_42" = [ "v2_40" ]; + "v2_44" = [ "v2_42" ]; + "v2_46" = [ "v2_44" ]; + "v2_48" = [ "v2_46" ]; + "v2_50" = [ "v2_48" ]; + "v2_52" = [ "v2_50" ]; + "v2_54" = [ "v2_52" ]; + }; + }; + "glib" = rec { + crateName = "glib"; + version = "0.4.1"; + edition = "2015"; + sha256 = "0jkdy63hpc3pp7x3rfndfq5rz48431rdq6ph81lhcqyc4hl4bc5r"; + authors = [ + "The Gtk-rs Project Developers" + ]; + dependencies = [ + { + name = "bitflags"; + packageId = "bitflags"; + } + { + name = "glib-sys"; + packageId = "glib-sys"; + } + { + name = "gobject-sys"; + packageId = "gobject-sys"; + } + { + name = "lazy_static"; + packageId = "lazy_static"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + features = { + "dox" = [ "glib-sys/dox" "gobject-sys/dox" ]; + "v2_34" = [ "glib-sys/v2_34" "gobject-sys/v2_34" ]; + "v2_38" = [ "v2_34" "glib-sys/v2_38" "gobject-sys/v2_38" ]; + "v2_40" = [ "v2_38" "glib-sys/v2_40" ]; + "v2_44" = [ "v2_40" "glib-sys/v2_44" "gobject-sys/v2_44" ]; + "v2_46" = [ "v2_44" "glib-sys/v2_46" "gobject-sys/v2_46" ]; + "v2_48" = [ "v2_46" "glib-sys/v2_48" ]; + "v2_50" = [ "v2_48" "glib-sys/v2_50" ]; + "v2_52" = [ "v2_50" "glib-sys/v2_52" ]; + "v2_54" = [ "v2_52" "glib-sys/v2_54" "gobject-sys/v2_54" ]; + }; + }; + "glib-sys" = rec { + crateName = "glib-sys"; + version = "0.5.0"; + edition = "2015"; + sha256 = "1s2bvk3zcwsbf8w88a3d6qqfbn360dcx5hrw069jpx9zc54k0sfr"; + libName = "glib_sys"; + authors = [ + "The Gtk-rs Project Developers" + ]; + dependencies = [ + { + name = "bitflags"; + packageId = "bitflags"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + buildDependencies = [ + { + name = "pkg-config"; + packageId = "pkg-config"; + } + ]; + features = { + "v2_36" = [ "v2_34" ]; + "v2_38" = [ "v2_36" ]; + "v2_40" = [ "v2_38" ]; + "v2_44" = [ "v2_40" ]; + "v2_46" = [ "v2_44" ]; + "v2_48" = [ "v2_46" ]; + "v2_50" = [ "v2_48" ]; + "v2_52" = [ "v2_50" ]; + "v2_54" = [ "v2_52" ]; + }; + }; + "gobject-sys" = rec { + crateName = "gobject-sys"; + version = "0.5.0"; + edition = "2015"; + sha256 = "1hnpqz4yd1z76gbl5crly9nsg6cvprlsj8gdcqy19cbigb40gmb0"; + libName = "gobject_sys"; + authors = [ + "The Gtk-rs Project Developers" + ]; + dependencies = [ + { + name = "bitflags"; + packageId = "bitflags"; + } + { + name = "glib-sys"; + packageId = "glib-sys"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + buildDependencies = [ + { + name = "pkg-config"; + packageId = "pkg-config"; + } + ]; + features = { + "v2_36" = [ "v2_34" ]; + "v2_38" = [ "v2_36" ]; + "v2_42" = [ "v2_38" ]; + "v2_44" = [ "v2_42" ]; + "v2_46" = [ "v2_44" ]; + "v2_54" = [ "v2_46" ]; + }; + }; + "hex" = rec { + crateName = "hex"; + version = "0.3.2"; + edition = "2015"; + sha256 = "0xsdcjiik5j750j67zk42qdnmm4ahirk3gmkmcqgq7qls2jjcl40"; + authors = [ + "KokaKiwi " + ]; + features = { + }; + }; + "lazy_static" = rec { + crateName = "lazy_static"; + version = "1.4.0"; + edition = "2015"; + sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2"; + authors = [ + "Marvin Löbel " + ]; + features = { + "spin_no_std" = [ "spin" ]; + }; + }; + "lazycell" = rec { + crateName = "lazycell"; + version = "1.3.0"; + edition = "2015"; + sha256 = "0m8gw7dn30i0zjjpjdyf6pc16c34nl71lpv461mix50x3p70h3c3"; + authors = [ + "Alex Crichton " + "Nikita Pekin " + ]; + features = { + "nightly-testing" = [ "clippy" "nightly" ]; + }; + }; + "libc" = rec { + crateName = "libc"; + version = "0.2.77"; + edition = "2015"; + sha256 = "0dc2z75prvi9vgg7djzy4nkb61vish01p5knis50hq15xh86pygj"; + authors = [ + "The Rust Project Developers" + ]; + features = { + "default" = [ "std" ]; + "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ]; + "use_std" = [ "std" ]; + }; + resolvedDefaultFeatures = [ "default" "std" ]; + }; + "libdbus-sys" = rec { + crateName = "libdbus-sys"; + version = "0.2.1"; + edition = "2015"; + sha256 = "1w06ycq2mw8zfp9j73macgdl8d2881bnxbzdyyxys90ljyya64nw"; + authors = [ + "David Henningsson " + ]; + buildDependencies = [ + { + name = "pkg-config"; + packageId = "pkg-config"; + } + ]; + + }; + "libnotify" = rec { + crateName = "libnotify"; + version = "1.0.3"; + edition = "2015"; + sha256 = "0bfc83hzzqkkd9hclvd2s79sxmvq0clsiqzxqv6ggy66id7nll0h"; + authors = [ + "Mika Attila " + "Julian Ospald " + ]; + dependencies = [ + { + name = "gdk-pixbuf"; + packageId = "gdk-pixbuf"; + } + { + name = "gdk-pixbuf-sys"; + packageId = "gdk-pixbuf-sys"; + } + { + name = "glib"; + packageId = "glib"; + } + { + name = "glib-sys"; + packageId = "glib-sys"; + } + { + name = "gobject-sys"; + packageId = "gobject-sys"; + } + { + name = "libnotify-sys"; + packageId = "libnotify-sys"; + } + ]; + + }; + "libnotify-sys" = rec { + crateName = "libnotify-sys"; + version = "1.0.2"; + edition = "2015"; + sha256 = "0g1q8w3ivnjl7sxniynly95rlg51gx9f2cdl3q7x2knjnywid9x0"; + libName = "libnotify_sys"; + authors = [ + "Mika Attila " + "Julian Ospald " + ]; + dependencies = [ + { + name = "bitflags"; + packageId = "bitflags"; + } + { + name = "gdk-pixbuf-sys"; + packageId = "gdk-pixbuf-sys"; + } + { + name = "glib-sys"; + packageId = "glib-sys"; + } + { + name = "gobject-sys"; + packageId = "gobject-sys"; + } + { + name = "libc"; + packageId = "libc"; + } + ]; + buildDependencies = [ + { + name = "pkg-config"; + packageId = "pkg-config"; + } + ]; + + }; + "libpulse-binding" = rec { + crateName = "libpulse-binding"; + version = "2.16.2"; + edition = "2018"; + sha256 = "0f2mdcqrg3lmfn6hixmhmq67nw3f6hy2xmny81byjjpkcynyz58p"; + authors = [ + "Lyndon Brown " + ]; + dependencies = [ + { + name = "libc"; + packageId = "libc"; + } + { + name = "libpulse-sys"; + packageId = "libpulse-sys"; + usesDefaultFeatures = false; + } + { + name = "winapi"; + packageId = "winapi"; + usesDefaultFeatures = false; + target = { target, features }: target."windows"; + features = [ "winsock2" ]; + } + ]; + features = { + "default" = [ "pa_latest_common" ]; + "dox" = [ "libpulse-sys/dox" ]; + "pa_latest" = [ "pa_v13" ]; + "pa_latest_common" = [ "pa_v13" ]; + "pa_v12" = [ "pa_v8" "libpulse-sys/pa_v12" ]; + "pa_v13" = [ "pa_v12" "libpulse-sys/pa_v13" ]; + "pa_v5" = [ "libpulse-sys/pa_v5" ]; + "pa_v6" = [ "pa_v5" "libpulse-sys/pa_v6" ]; + "pa_v8" = [ "pa_v6" "libpulse-sys/pa_v8" ]; + }; + resolvedDefaultFeatures = [ "default" "pa_latest_common" "pa_v12" "pa_v13" "pa_v5" "pa_v6" "pa_v8" ]; + }; + "libpulse-sys" = rec { + crateName = "libpulse-sys"; + version = "1.13.2"; + edition = "2018"; + sha256 = "11hp2ii6pay8651bs87d6qnfd3yc2v9lasasfmjghr6mf5q8alsz"; + authors = [ + "Lyndon Brown " + ]; + dependencies = [ + { + name = "libc"; + packageId = "libc"; + } + { + name = "winapi"; + packageId = "winapi"; + usesDefaultFeatures = false; + target = { target, features }: target."windows"; + features = [ "winsock2" ]; + } + ]; + buildDependencies = [ + { + name = "pkg-config"; + packageId = "pkg-config"; + } + ]; + features = { + "default" = [ "pa_latest_common" ]; + "pa_latest" = [ "pa_v13" ]; + "pa_latest_common" = [ "pa_v13" ]; + "pa_v12" = [ "pa_v8" ]; + "pa_v13" = [ "pa_v12" ]; + "pa_v6" = [ "pa_v5" ]; + "pa_v8" = [ "pa_v6" ]; + }; + resolvedDefaultFeatures = [ "pa_v12" "pa_v13" "pa_v5" "pa_v6" "pa_v8" ]; + }; + "mach" = rec { + crateName = "mach"; + version = "0.3.2"; + edition = "2015"; + sha256 = "1yksa8lwzqh150gr4417rls1wk20asy9vhp8kq5g9n7z58xyh8xq"; + authors = [ + "Nick Fitzgerald " + "David Cuddeback " + "Gonzalo Brito Gadeschi " + ]; + dependencies = [ + { + name = "libc"; + packageId = "libc"; + usesDefaultFeatures = false; + target = { target, features }: ((target."os" == "macos") || (target."os" == "ios")); + } + ]; + features = { + "rustc-dep-of-std" = [ "rustc-std-workspace-core" "libc/rustc-dep-of-std" ]; + }; + resolvedDefaultFeatures = [ "default" ]; + }; + "num-traits" = rec { + crateName = "num-traits"; + version = "0.2.12"; + edition = "2015"; + sha256 = "04fnzwlnn6fcy09jjbi9l7bj5dvg657x5c2sjgwfb3pl0z67n9mc"; + authors = [ + "The Rust Project Developers" + ]; + buildDependencies = [ + { + name = "autocfg"; + packageId = "autocfg"; + } + ]; + features = { + "default" = [ "std" ]; + }; + resolvedDefaultFeatures = [ "std" ]; + }; + "pkg-config" = rec { + crateName = "pkg-config"; + version = "0.3.18"; + edition = "2015"; + sha256 = "0cxc4yd9qb40944a2svgci41bws68f1hqvyljhrldwbadda94r6k"; + authors = [ + "Alex Crichton " + ]; + + }; + "simple-osd-battery" = rec { + crateName = "simple-osd-battery"; + version = "0.1.0"; + edition = "2018"; + crateBin = [ + { name = "simple-osd-battery"; path = "src/main.rs"; } + ]; + src = (builtins.filterSource sourceFilter ./battery); + authors = [ + "Alexander Bantyev " + ]; + dependencies = [ + { + name = "battery"; + packageId = "battery"; + } + { + name = "simple-osd-common"; + packageId = "simple-osd-common"; + } + ]; + + }; + "simple-osd-bluetooth" = rec { + crateName = "simple-osd-bluetooth"; + version = "0.1.0"; + edition = "2018"; + crateBin = [ + { name = "simple-osd-bluetooth"; path = "src/main.rs"; } + ]; + src = (builtins.filterSource sourceFilter ./bluetooth); + authors = [ + "Alexander Bantyev " + ]; + dependencies = [ + { + name = "blurz"; + packageId = "blurz"; + } + { + name = "simple-osd-common"; + packageId = "simple-osd-common"; + } + ]; + + }; + "simple-osd-brightness" = rec { + crateName = "simple-osd-brightness"; + version = "0.1.0"; + edition = "2018"; + crateBin = [ + { name = "simple-osd-brightness"; path = "src/main.rs"; } + ]; + src = (builtins.filterSource sourceFilter ./brightness); + authors = [ + "Alexander Bantyev " + ]; + dependencies = [ + { + name = "backlight"; + packageId = "backlight"; + } + { + name = "simple-osd-common"; + packageId = "simple-osd-common"; + } + ]; + + }; + "simple-osd-common" = rec { + crateName = "simple-osd-common"; + version = "0.1.0"; + edition = "2018"; + src = (builtins.filterSource sourceFilter ./common); + authors = [ + "Alexander Bantyev " + ]; + dependencies = [ + { + name = "configparser"; + packageId = "configparser"; + } + { + name = "libnotify"; + packageId = "libnotify"; + } + { + name = "xdg"; + packageId = "xdg"; + } + ]; + + }; + "simple-osd-pulseaudio" = rec { + crateName = "simple-osd-pulseaudio"; + version = "0.1.0"; + edition = "2018"; + crateBin = [ + { name = "simple-osd-pulseaudio"; path = "src/main.rs"; } + ]; + src = (builtins.filterSource sourceFilter ./pulseaudio); + authors = [ + "Alexander Bantyev " + ]; + dependencies = [ + { + name = "libpulse-binding"; + packageId = "libpulse-binding"; + } + { + name = "simple-osd-common"; + packageId = "simple-osd-common"; + } + ]; + + }; + "typenum" = rec { + crateName = "typenum"; + version = "1.12.0"; + edition = "2015"; + sha256 = "0cvbksljz61ian21fnn0h51kphl0pwpzb932bv4s0rwy1wh8lg1p"; + build = "build/main.rs"; + authors = [ + "Paho Lurie-Gregg " + "Andre Bogus " + ]; + features = { + }; + }; + "uom" = rec { + crateName = "uom"; + version = "0.29.0"; + edition = "2015"; + sha256 = "1fls9ky0sfsmj9dbpmnji22x90h95p58ybqi56wbymi34psr7dcb"; + authors = [ + "Mike Boutin " + ]; + dependencies = [ + { + name = "num-traits"; + packageId = "num-traits"; + usesDefaultFeatures = false; + } + { + name = "typenum"; + packageId = "typenum"; + } + ]; + features = { + "bigint" = [ "bigint-support" ]; + "bigint-support" = [ "num-bigint" "num-rational/bigint" ]; + "bigrational" = [ "bigint-support" ]; + "biguint" = [ "bigint-support" ]; + "default" = [ "autoconvert" "f32" "f64" "si" "std" ]; + "i128" = [ "rational-support" ]; + "i16" = [ "rational-support" ]; + "i32" = [ "rational-support" ]; + "i64" = [ "rational-support" ]; + "i8" = [ "rational-support" ]; + "isize" = [ "rational-support" ]; + "rational" = [ "rational-support" ]; + "rational-support" = [ "num-rational" ]; + "rational32" = [ "rational-support" ]; + "rational64" = [ "rational-support" ]; + "std" = [ "num-traits/std" ]; + "u128" = [ "rational-support" ]; + "u16" = [ "rational-support" ]; + "u32" = [ "rational-support" ]; + "u64" = [ "rational-support" ]; + "u8" = [ "rational-support" ]; + "use_serde" = [ "serde" ]; + "usize" = [ "rational-support" ]; + }; + resolvedDefaultFeatures = [ "autoconvert" "default" "f32" "f64" "si" "std" ]; + }; + "winapi" = rec { + crateName = "winapi"; + version = "0.3.9"; + edition = "2015"; + sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw"; + authors = [ + "Peter Atashian " + ]; + dependencies = [ + { + name = "winapi-i686-pc-windows-gnu"; + packageId = "winapi-i686-pc-windows-gnu"; + target = { target, features }: (stdenv.hostPlatform.config == "i686-pc-windows-gnu"); + } + { + name = "winapi-x86_64-pc-windows-gnu"; + packageId = "winapi-x86_64-pc-windows-gnu"; + target = { target, features }: (stdenv.hostPlatform.config == "x86_64-pc-windows-gnu"); + } + ]; + features = { + "debug" = [ "impl-debug" ]; + }; + resolvedDefaultFeatures = [ "devguid" "errhandlingapi" "handleapi" "impl-default" "ioapiset" "ntdef" "setupapi" "winbase" "winerror" "winsock2" ]; + }; + "winapi-i686-pc-windows-gnu" = rec { + crateName = "winapi-i686-pc-windows-gnu"; + version = "0.4.0"; + edition = "2015"; + sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc"; + authors = [ + "Peter Atashian " + ]; + + }; + "winapi-x86_64-pc-windows-gnu" = rec { + crateName = "winapi-x86_64-pc-windows-gnu"; + version = "0.4.0"; + edition = "2015"; + sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki"; + authors = [ + "Peter Atashian " + ]; + + }; + "xdg" = rec { + crateName = "xdg"; + version = "2.2.0"; + edition = "2015"; + sha256 = "0mws8a0fr3cqk5nh7aq9lmkmhzghvasqy4mhw6nnza06l4d6i2fh"; + authors = [ + "Ben Longbons " + "whitequark " + ]; + + }; + }; + + # +# crate2nix/default.nix (excerpt start) +# + + /* Target (platform) data for conditional dependencies. + This corresponds roughly to what buildRustCrate is setting. + */ + defaultTarget = { + unix = true; + windows = false; + fuchsia = true; + test = false; + + # This doesn't appear to be officially documented anywhere yet. + # See https://github.com/rust-lang-nursery/rust-forge/issues/101. + os = if stdenv.hostPlatform.isDarwin + then "macos" + else stdenv.hostPlatform.parsed.kernel.name; + arch = stdenv.hostPlatform.parsed.cpu.name; + family = "unix"; + env = "gnu"; + endian = + if stdenv.hostPlatform.parsed.cpu.significantByte.name == "littleEndian" + then "little" else "big"; + pointer_width = toString stdenv.hostPlatform.parsed.cpu.bits; + vendor = stdenv.hostPlatform.parsed.vendor.name; + debug_assertions = false; + }; + + /* Filters common temp files and build files. */ + # TODO(pkolloch): Substitute with gitignore filter + sourceFilter = name: type: + let + baseName = builtins.baseNameOf (builtins.toString name); + in + ! ( + # Filter out git + baseName == ".gitignore" + || (type == "directory" && baseName == ".git") + + # Filter out build results + || ( + type == "directory" && ( + baseName == "target" + || baseName == "_site" + || baseName == ".sass-cache" + || baseName == ".jekyll-metadata" + || baseName == "build-artifacts" + ) + ) + + # Filter out nix-build result symlinks + || ( + type == "symlink" && lib.hasPrefix "result" baseName + ) + + # Filter out IDE config + || ( + type == "directory" && ( + baseName == ".idea" || baseName == ".vscode" + ) + ) || lib.hasSuffix ".iml" baseName + + # Filter out nix build files + || baseName == "Cargo.nix" + + # Filter out editor backup / swap files. + || lib.hasSuffix "~" baseName + || builtins.match "^\\.sw[a-z]$$" baseName != null + || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null + || lib.hasSuffix ".tmp" baseName + || lib.hasSuffix ".bak" baseName + || baseName == "tests.nix" + ); + + /* Returns a crate which depends on successful test execution + of crate given as the second argument. + + testCrateFlags: list of flags to pass to the test exectuable + testInputs: list of packages that should be available during test execution + */ + crateWithTest = { crate, testCrate, testCrateFlags, testInputs }: + assert builtins.typeOf testCrateFlags == "list"; + assert builtins.typeOf testInputs == "list"; + let + # override the `crate` so that it will build and execute tests instead of + # building the actual lib and bin targets We just have to pass `--test` + # to rustc and it will do the right thing. We execute the tests and copy + # their log and the test executables to $out for later inspection. + test = let + drv = testCrate.override ( + _: { + buildTests = true; + } + ); + in + pkgs.runCommand "run-tests-${testCrate.name}" { + inherit testCrateFlags; + buildInputs = testInputs; + } '' + set -ex + cd ${crate.src} + for file in ${drv}/tests/*; do + $file $testCrateFlags 2>&1 | tee -a $out + done + ''; + in + crate.overrideAttrs ( + old: { + checkPhase = '' + test -e ${test} + ''; + passthru = (old.passthru or {}) // { + inherit test; + }; + } + ); + + /* A restricted overridable version of builtRustCratesWithFeatures. */ + buildRustCrateWithFeatures = + { packageId + , features ? rootFeatures + , crateOverrides ? defaultCrateOverrides + , buildRustCrateFunc ? ( + if crateOverrides == pkgs.defaultCrateOverrides + then buildRustCrate + else buildRustCrate.override { + defaultCrateOverrides = crateOverrides; + } + ) + , runTests ? false + , testCrateFlags ? [] + , testInputs ? [] + }: + lib.makeOverridable + ( + { features, crateOverrides, runTests, testCrateFlags, testInputs }: + let + builtRustCrates = builtRustCratesWithFeatures { + inherit packageId features buildRustCrateFunc; + runTests = false; + }; + builtTestRustCrates = builtRustCratesWithFeatures { + inherit packageId features buildRustCrateFunc; + runTests = true; + }; + drv = builtRustCrates.${packageId}; + testDrv = builtTestRustCrates.${packageId}; + in + if runTests then + crateWithTest { + crate = drv; + testCrate = testDrv; + inherit testCrateFlags testInputs; + } + else drv + ) + { inherit features crateOverrides runTests testCrateFlags testInputs; }; + + /* Returns an attr set with packageId mapped to the result of buildRustCrateFunc + for the corresponding crate. + */ + builtRustCratesWithFeatures = + { packageId + , features + , crateConfigs ? crates + , buildRustCrateFunc + , runTests + , target ? defaultTarget + } @ args: + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isList features); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + rootPackageId = packageId; + mergedFeatures = mergePackageFeatures ( + args // { + inherit rootPackageId; + target = target // { test = runTests; }; + } + ); + + buildByPackageId = packageId: buildByPackageIdImpl packageId; + + # Memoize built packages so that reappearing packages are only built once. + builtByPackageId = + lib.mapAttrs (packageId: value: buildByPackageId packageId) crateConfigs; + + buildByPackageIdImpl = packageId: + let + features = mergedFeatures."${packageId}" or []; + crateConfig' = crateConfigs."${packageId}"; + crateConfig = + builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ]; + devDependencies = + lib.optionals + (runTests && packageId == rootPackageId) + (crateConfig'.devDependencies or []); + dependencies = + dependencyDerivations { + inherit builtByPackageId features target; + dependencies = + (crateConfig.dependencies or []) + ++ devDependencies; + }; + buildDependencies = + dependencyDerivations { + inherit builtByPackageId features target; + dependencies = crateConfig.buildDependencies or []; + }; + + filterEnabledDependenciesForThis = dependencies: filterEnabledDependencies { + inherit dependencies features target; + }; + + dependenciesWithRenames = + lib.filter (d: d ? "rename") ( + filterEnabledDependenciesForThis + ( + (crateConfig.buildDependencies or []) + ++ (crateConfig.dependencies or []) + ++ devDependencies + ) + ); + + crateRenames = + builtins.listToAttrs + (map (d: { name = d.name; value = d.rename; }) dependenciesWithRenames); + in + buildRustCrateFunc ( + crateConfig // { + src = crateConfig.src or ( + pkgs.fetchurl { + name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz"; + url = "https://crates.io/api/v1/crates/${crateConfig.crateName}/${crateConfig.version}/download"; + sha256 = crateConfig.sha256; + } + ); + inherit features dependencies buildDependencies crateRenames release; + } + ); + in + builtByPackageId; + + /* Returns the actual derivations for the given dependencies. */ + dependencyDerivations = + { builtByPackageId + , features + , dependencies + , target + }: + assert (builtins.isAttrs builtByPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencies); + assert (builtins.isAttrs target); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies features target; + }; + depDerivation = dependency: builtByPackageId.${dependency.packageId}; + in + map depDerivation enabledDependencies; + + /* Returns a sanitized version of val with all values substituted that cannot + be serialized as JSON. + */ + sanitizeForJson = val: + if builtins.isAttrs val + then lib.mapAttrs (n: v: sanitizeForJson v) val + else if builtins.isList val + then builtins.map sanitizeForJson val + else if builtins.isFunction val + then "function" + else val; + + /* Returns various tools to debug a crate. */ + debugCrate = { packageId, target ? defaultTarget }: + assert (builtins.isString packageId); + let + debug = rec { + # The built tree as passed to buildRustCrate. + buildTree = buildRustCrateWithFeatures { + buildRustCrateFunc = lib.id; + inherit packageId; + }; + sanitizedBuildTree = sanitizeForJson buildTree; + dependencyTree = sanitizeForJson ( + buildRustCrateWithFeatures { + buildRustCrateFunc = crate: { + "01_crateName" = crate.crateName or false; + "02_features" = crate.features or []; + "03_dependencies" = crate.dependencies or []; + }; + inherit packageId; + } + ); + mergedPackageFeatures = mergePackageFeatures { + features = rootFeatures; + inherit packageId target; + }; + diffedDefaultPackageFeatures = diffDefaultPackageFeatures { + inherit packageId target; + }; + }; + in + { internal = debug; }; + + /* Returns differences between cargo default features and crate2nix default + features. + + This is useful for verifying the feature resolution in crate2nix. + */ + diffDefaultPackageFeatures = + { crateConfigs ? crates + , packageId + , target + }: + assert (builtins.isAttrs crateConfigs); + let + prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; }); + mergedFeatures = + prefixValues + "crate2nix" + (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; }); + configs = prefixValues "cargo" crateConfigs; + combined = lib.foldAttrs (a: b: a // b) {} [ mergedFeatures configs ]; + onlyInCargo = + builtins.attrNames + (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined); + onlyInCrate2Nix = + builtins.attrNames + (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined); + differentFeatures = lib.filterAttrs + ( + n: v: + (v ? "crate2nix") + && (v ? "cargo") + && (v.crate2nix.features or []) != (v."cargo".resolved_default_features or []) + ) + combined; + in + builtins.toJSON { + inherit onlyInCargo onlyInCrate2Nix differentFeatures; + }; + + /* Returns an attrset mapping packageId to the list of enabled features. + + If multiple paths to a dependency enable different features, the + corresponding feature sets are merged. Features in rust are additive. + */ + mergePackageFeatures = + { crateConfigs ? crates + , packageId + , rootPackageId ? packageId + , features ? rootFeatures + , dependencyPath ? [ crates.${packageId}.crateName ] + , featuresByPackageId ? {} + , target + # Adds devDependencies to the crate with rootPackageId. + , runTests ? false + , ... + } @ args: + assert (builtins.isAttrs crateConfigs); + assert (builtins.isString packageId); + assert (builtins.isString rootPackageId); + assert (builtins.isList features); + assert (builtins.isList dependencyPath); + assert (builtins.isAttrs featuresByPackageId); + assert (builtins.isAttrs target); + assert (builtins.isBool runTests); + let + crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}"); + expandedFeatures = expandFeatures (crateConfig.features or {}) features; + + depWithResolvedFeatures = dependency: + let + packageId = dependency.packageId; + features = dependencyFeatures expandedFeatures dependency; + in + { inherit packageId features; }; + + resolveDependencies = cache: path: dependencies: + assert (builtins.isAttrs cache); + assert (builtins.isList dependencies); + let + enabledDependencies = filterEnabledDependencies { + inherit dependencies target; + features = expandedFeatures; + }; + directDependencies = map depWithResolvedFeatures enabledDependencies; + foldOverCache = op: lib.foldl op cache directDependencies; + in + foldOverCache + ( + cache: { packageId, features }: + let + cacheFeatures = cache.${packageId} or []; + combinedFeatures = sortedUnique (cacheFeatures ++ features); + in + if cache ? ${packageId} && cache.${packageId} == combinedFeatures + then cache + else mergePackageFeatures { + features = combinedFeatures; + featuresByPackageId = cache; + inherit crateConfigs packageId target runTests rootPackageId; + } + ); + + cacheWithSelf = + let + cacheFeatures = featuresByPackageId.${packageId} or []; + combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures); + in + featuresByPackageId // { + "${packageId}" = combinedFeatures; + }; + + cacheWithDependencies = + resolveDependencies cacheWithSelf "dep" ( + crateConfig.dependencies or [] + ++ lib.optionals + (runTests && packageId == rootPackageId) + (crateConfig.devDependencies or []) + ); + + cacheWithAll = + resolveDependencies + cacheWithDependencies "build" + (crateConfig.buildDependencies or []); + in + cacheWithAll; + + /* Returns the enabled dependencies given the enabled features. */ + filterEnabledDependencies = { dependencies, features, target }: + assert (builtins.isList dependencies); + assert (builtins.isList features); + assert (builtins.isAttrs target); + + lib.filter + ( + dep: + let + targetFunc = dep.target or (features: true); + in + targetFunc { inherit features target; } + && ( + !(dep.optional or false) + || builtins.any (doesFeatureEnableDependency dep) features + ) + ) + dependencies; + + /* Returns whether the given feature should enable the given dependency. */ + doesFeatureEnableDependency = { name, rename ? null, ... }: feature: + let + prefix = "${name}/"; + len = builtins.stringLength prefix; + startsWithPrefix = builtins.substring 0 len feature == prefix; + in + (rename == null && feature == name) + || (rename != null && rename == feature) + || startsWithPrefix; + + /* Returns the expanded features for the given inputFeatures by applying the + rules in featureMap. + + featureMap is an attribute set which maps feature names to lists of further + feature names to enable in case this feature is selected. + */ + expandFeatures = featureMap: inputFeatures: + assert (builtins.isAttrs featureMap); + assert (builtins.isList inputFeatures); + let + expandFeature = feature: + assert (builtins.isString feature); + [ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [])); + outFeatures = builtins.concatMap expandFeature inputFeatures; + in + sortedUnique outFeatures; + + /* + Returns the actual features for the given dependency. + + features: The features of the crate that refers this dependency. + */ + dependencyFeatures = features: dependency: + assert (builtins.isList features); + assert (builtins.isAttrs dependency); + let + defaultOrNil = if dependency.usesDefaultFeatures or true + then [ "default" ] + else []; + explicitFeatures = dependency.features or []; + additionalDependencyFeatures = + let + dependencyPrefix = (dependency.rename or dependency.name) + "/"; + dependencyFeatures = + builtins.filter (f: lib.hasPrefix dependencyPrefix f) features; + in + builtins.map (lib.removePrefix dependencyPrefix) dependencyFeatures; + in + defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures; + + /* Sorts and removes duplicates from a list of strings. */ + sortedUnique = features: + assert (builtins.isList features); + assert (builtins.all builtins.isString features); + let + outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) {} features; + outFeaturesUnique = builtins.attrNames outFeaturesSet; + in + builtins.sort (a: b: a < b) outFeaturesUnique; + + deprecationWarning = message: value: + if strictDeprecation + then builtins.throw "strictDeprecation enabled, aborting: ${message}" + else builtins.trace message value; + + # + # crate2nix/default.nix (excerpt end) + # + + }; +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4560d4c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +members = [ + "common", + + "battery", + "bluetooth", + "brightness", + "pulseaudio" +] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ff4f696 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Simple OSD daemons + +A collection of simple yet configurable OSD daemons that use Freedesktop notifications to display various information about your system. + +## Installation + +### Nix + +These commands will allow you to try the daemons without installing anything globally; Likely, you want to somehow install this on your system after this. How you do it depends on your setup and preferences. + +`nix shell git+https://code.balsoft.ru/balsoft/simple-osd-daemons` to get all daemons. Add `#$DAEMON` to only get `$DAEMON`. If you don't use nix 3 yet, try `nix-shell https://code.balsoft.ru/balsoft/simple-osd-daemons/archive/master.tar.gz -A defaultPackage.x86_64-linux` + +### Cargo + +`cargo build` + +## Usage + +Run the daemons you need. At this moment, none of them accept any arguments. + +### Configuration + +`simple-osd-daemons` follows XDG Basedir specification: configuration will be written to `$XDG_CONFIG_HOME/simple-osd/`, typically `~/.config/simple-osd/`. Each daemon has a separate configuration file in INI format, and there is also a `common` configuration file. On startup, the daemons will create their configuration files and populate them with default values if they don't exist. diff --git a/battery/Cargo.toml b/battery/Cargo.toml new file mode 100644 index 0000000..4eab083 --- /dev/null +++ b/battery/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "simple-osd-battery" +version = "0.1.0" +authors = ["Alexander Bantyev "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +battery = "0.7.6" +simple-osd-common = { version = "0.1", path = "../common" } \ No newline at end of file diff --git a/battery/src/main.rs b/battery/src/main.rs new file mode 100644 index 0000000..412902e --- /dev/null +++ b/battery/src/main.rs @@ -0,0 +1,122 @@ +extern crate battery; +extern crate simple_osd_common as osd; + +use std::io; +use std::thread; +use std::time::Duration; + +use osd::notify::{OSD, OSDContents, OSDProgressText, Urgency}; +use osd::config::Config; + +use battery::units::Time; + +#[derive(Debug)] +enum Threshold { + Percentage(i32), + Minutes(i32) +} + +#[derive(Debug, Eq, PartialEq)] +enum State { + Low, + Critical, + Charging, + Normal +} + +fn parse_threshold(thresh: String) -> Option { + let mut s = thresh.clone(); + + let last = s.pop(); + + match last { + Some('%') => s.parse().map(Threshold::Percentage).ok(), + Some('m') => s.parse().map(Threshold::Minutes).ok(), + _ => None + } +} + +fn main() -> battery::Result<()> { + let mut config = Config::new("battery"); + + let mut low_threshold_str = config.get_default("threshold", "low", String::from("30m")); + let mut critical_threshold_str = config.get_default("threshold", "critical", String::from("10m")); + + let low_threshold = parse_threshold(low_threshold_str).expect("Low threshold is incorrect: must be either a percentage or minutes"); + let critical_threshold = parse_threshold(critical_threshold_str).expect("Critical threshold is incorrect: must be either a percentage or minutes"); + + let refresh_interval = config.get_default("default", "refresh interval", 30); + + println!("{:?}, {:?}", low_threshold, critical_threshold); + + let mut osd = OSD::new(); + osd.icon = Some(String::from("battery")); + + let manager = battery::Manager::new()?; + let mut battery = match manager.batteries()?.next() { + Some(Ok(battery)) => battery, + Some(Err(e)) => { + eprintln!("Unable to access battery information"); + return Err(e); + } + None => { + eprintln!("Unable to find any batteries"); + return Err(io::Error::from(io::ErrorKind::NotFound).into()); + } + }; + + let mut state: State; + let mut last_state: State = State::Normal; + + loop { + state = match battery.state() { + battery::State::Charging => State::Charging, + battery::State::Full => State::Normal, + _ => { + let soc = (battery.state_of_charge().value * 100.) as i32; + let tte = battery.time_to_empty().map(|q| q.value).unwrap_or(0.) as i32 / 60; + println!("{:?}, {:?}", soc, tte); + let low = match low_threshold { + Threshold::Percentage(p) => if soc <= p { State::Low } else { State::Normal }, + Threshold::Minutes(m) => if tte <= m { State::Low } else { State::Normal } + }; + match critical_threshold { + Threshold::Percentage(p) => if soc <= p { State::Critical } else { low }, + Threshold::Minutes(m) => if tte <= m { State::Critical } else { low } + } + } + }; + + if state != last_state { + match state { + State::Charging => { + battery.time_to_full().map(|ttf| { + osd.title = Some(format!("Charging, {:?} until full", ttf)); + osd.urgency = Urgency::Low; + osd.update(); + }); + } + State::Low => { + battery.time_to_empty().map(|tte| { + osd.title = Some(format!("Low battery, {:?} remaining", tte)); + osd.urgency = Urgency::Normal; + osd.update(); + }); + }, + State::Normal | State::Critical => { } + } + } + + if state == State::Critical { + battery.time_to_empty().map(|tte| { + osd.title = Some(format!("Critically low battery, {:?} remaining", tte)); + osd.urgency = Urgency::Critical; + osd.update(); + }); + } + + thread::sleep(Duration::from_secs(refresh_interval)); + manager.refresh(&mut battery)?; + last_state = state; + } +} diff --git a/bluetooth/Cargo.toml b/bluetooth/Cargo.toml new file mode 100644 index 0000000..1f3987c --- /dev/null +++ b/bluetooth/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "simple-osd-bluetooth" +version = "0.1.0" +authors = ["Alexander Bantyev "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +simple-osd-common = { version = "0.1", path = "../common" } +blurz = "0.4.0" \ No newline at end of file diff --git a/bluetooth/src/main.rs b/bluetooth/src/main.rs new file mode 100644 index 0000000..3894046 --- /dev/null +++ b/bluetooth/src/main.rs @@ -0,0 +1,43 @@ +extern crate blurz; +extern crate simple_osd_common as osd; + +use blurz::bluetooth_session::BluetoothSession; +use blurz::bluetooth_adapter::BluetoothAdapter; + +use osd::config::Config; +use osd::notify::{OSD, Urgency}; + +fn main() { + + let mut config = Config::new("bluetooth"); + + let refresh_interval = config.get_default("default", "refresh interval", 15); + + let path: Option = config.get("session", "path"); + + let session = BluetoothSession::create_session(path.as_deref()).unwrap(); + + let adapter = BluetoothAdapter::init(&session).unwrap(); + + let mut osd = OSD::new(); + osd.icon = Some(String::from("bluetooth")); + osd.urgency = Urgency::Low; + + let mut device_name: String; + let mut last_device_name: String = String::new(); + + loop { + let device = adapter.get_first_device().unwrap(); + + device_name = device.get_name().unwrap_or(String::from("")); + + if device_name != last_device_name { + osd.title = Some(format!("Bluetooth: connected to {}", device_name)); + osd.update(); + } + + last_device_name = device_name; + + std::thread::sleep(std::time::Duration::from_secs(refresh_interval)) + } +} diff --git a/brightness/Cargo.toml b/brightness/Cargo.toml new file mode 100644 index 0000000..dce5aa0 --- /dev/null +++ b/brightness/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "simple-osd-brightness" +version = "0.1.0" +authors = ["Alexander Bantyev "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +simple-osd-common = { version = "0.1", path = "../common" } +backlight = "0.1.1" \ No newline at end of file diff --git a/brightness/src/main.rs b/brightness/src/main.rs new file mode 100644 index 0000000..a7d2239 --- /dev/null +++ b/brightness/src/main.rs @@ -0,0 +1,35 @@ +extern crate backlight; +extern crate simple_osd_common as osd; + +use osd::config::Config; +use osd::notify::{OSD, OSDContents, OSDProgressText}; + +use backlight::Brightness; + +fn main() { + let mut config = Config::new("brightness"); + + let refresh_interval = config.get_default("default", "refresh interval", 1); + + let brightness = Brightness::default(); + + let m = brightness.get_max_brightness().unwrap() as f32; + + let mut osd = OSD::new(); + osd.title = Some(String::from("Screen brightness")); + + let mut b : f32; + + let mut last_b : f32 = 0.; + + loop { + b = brightness.get_brightness().unwrap() as f32; + + if b != last_b { + osd.contents = OSDContents::Progress(b/m, OSDProgressText::Percentage); + osd.update(); + } + + std::thread::sleep(std::time::Duration::from_secs(refresh_interval)) + } +} diff --git a/common/Cargo.toml b/common/Cargo.toml new file mode 100644 index 0000000..d371cf6 --- /dev/null +++ b/common/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "simple-osd-common" +version = "0.1.0" +authors = ["Alexander Bantyev "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libnotify = "1.0.3" +configparser = "1.0.0" +xdg = "2.1" diff --git a/common/src/lib.rs b/common/src/lib.rs new file mode 100644 index 0000000..34d6173 --- /dev/null +++ b/common/src/lib.rs @@ -0,0 +1,179 @@ +extern crate libnotify; +extern crate xdg; +extern crate configparser; + +pub static APPNAME: &str = "simple-osd"; + +pub mod config { + use configparser::ini::Ini; + use xdg::BaseDirectories; + use std::fs::{File, metadata}; + + pub struct Config { + config_path: Option, + config: Ini + } + + impl Config { + + pub fn new(name: &'static str) -> Config { + let mut config = Ini::new(); + + let xdg_dirs = BaseDirectories::with_prefix(crate::APPNAME).unwrap(); + + let config_path_option = xdg_dirs.place_config_file(name).ok(); + + if let Some(config_path_buf) = config_path_option.clone() { + if metadata(config_path_buf.clone()).map(|m| m.is_file()).unwrap_or(false) { + config.load(config_path_buf.to_str().unwrap()); + } else { + File::create(config_path_buf); + } + } + + let config_path = config_path_option.map(|p| p.to_str().unwrap().to_string()); + + Config { config, config_path } + } + + pub fn get(&mut self, section: &str, key: &str) -> Option + where + T: std::str::FromStr, + ::Err: std::fmt::Debug + { + self.config.get(section, key).map(|s: String| { s.parse().unwrap() }).or_else(|| { + self.config.set(section, key, None); + self.config_path.as_ref().map(|path| self.config.write(path.as_str())); + None + }) + } + + pub fn get_default(&mut self, section: &str, key: &str, default: T) -> T + where + T: std::str::FromStr, + T: std::fmt::Display, + ::Err: std::fmt::Debug + { + let val: Option = self.get(section, key); + + val.unwrap_or_else(|| { + self.config.set(section, key, Some(format!("{}", default))); + self.config_path.as_ref().map(|path| self.config.write(path.as_str())); + default + }) + } + } +} +pub mod notify { + use libnotify::Notification; + pub use libnotify::Urgency; + use crate::config::Config; + + fn init_if_not_already() { + if ! libnotify::is_initted() { + println!("Initializing libnotify"); + libnotify::init(crate::APPNAME).unwrap() + } + } + + pub enum OSDProgressText { + Percentage, + Text(Option) + } + + pub enum OSDContents { + Simple(Option), + Progress(f32, OSDProgressText) + } + + pub struct OSD { + pub title: Option, + + pub icon: Option, + + pub contents: OSDContents, + + pub urgency: Urgency, + + // Progress bar stuff + length: i32, + + full: String, + empty: String, + + start: String, + end: String, + + // Internal notification + notification: Notification + } + + impl OSD { + pub fn new() -> OSD { + init_if_not_already(); + + let mut config = Config::new("common"); + + let length = config.get_default("progressbar", "length", 20); + + let full = config.get_default("progressbar", "full", String::from("█")); + let empty = config.get_default("progressbar", "empty", String::from("░")); + + let start = config.get_default("progressbar", "start", String::new()); + let end = config.get_default("progressbar", "end", String::new()); + + let notification = Notification::new("", None, None); + + return OSD { + title: None, icon: None, + contents: OSDContents::Simple(None), + urgency: Urgency::Normal, + length, full, empty, start, end, + notification + }; + } + + fn get_full_text(&self) -> Option { + match &self.contents { + OSDContents::Simple(text) => text.clone(), + OSDContents::Progress(value, text) => { + let mut s = String::new(); + + s.push_str(self.start.as_str()); + + for _ in 0..(value * self.length as f32) as i32 { + s.push_str(self.full.as_str()) + } + + for _ in (value * self.length as f32) as i32..self.length { + s.push_str(self.empty.as_str()) + } + + s.push_str(self.end.as_str()); + + s.push_str(" "); + + match text { + OSDProgressText::Percentage => { + s.push_str(((value * 100.) as i32).to_string().as_str()); + + s.push_str("%"); + }, + OSDProgressText::Text(text) => { + text.as_ref().map(|text| s.push_str(text.as_str())); + } + } + + + Some(s) + } + } + } + + pub fn update(&mut self) { + self.notification.update(self.title.as_deref().unwrap_or(""), self.get_full_text().as_deref(), self.icon.as_deref()).unwrap(); + self.notification.set_urgency(self.urgency); + self.notification.show().unwrap(); + } + } +} diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..b22e926 --- /dev/null +++ b/default.nix @@ -0,0 +1,3 @@ +(import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) { + src = builtins.fetchGit ./.; +}).defaultNix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9dc94ab --- /dev/null +++ b/flake.lock @@ -0,0 +1,26 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1599773960, + "narHash": "sha256-5bL52aaUOOyOBjgKh9/6jQlFbeE+WfVX7dpvjohmD+w=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5916b9637048446755629c84ae6f13361f623d13", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e2cd3f6 --- /dev/null +++ b/flake.nix @@ -0,0 +1,57 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + }; + + description = "A collection of simple on-screen-display daemons"; + + outputs = { self, nixpkgs }: + let forAllSystems = f: builtins.mapAttrs (_: f) nixpkgs.legacyPackages; + in { + packages = forAllSystems (pkgs: + let + commonDeps = with pkgs; [ libnotify gdk_pixbuf glib ]; + + project = import ./Cargo.nix { + inherit nixpkgs pkgs; + defaultCrateOverrides = pkgs.defaultCrateOverrides // { + simple-osd-battery = oa: { + buildInputs = commonDeps; + }; + simple-osd-bluetooth = oa: { + buildInputs = commonDeps ++ [ pkgs.dbus_tools.lib pkgs.dbus_tools.dev ]; + nativeBuildInputs = [ pkgs.pkg-config ]; + }; + simple-osd-brightness = oa: { + buildInputs = commonDeps; + }; + # See https://github.com/kolloch/crate2nix/issues/149 + libpulse-binding = oa: { + preBuild = "sed s/pulse::libpulse.so.0/pulse/ -i target/*link*"; + }; + simple-osd-pulseaudio = oa: { + buildInputs = commonDeps ++ [ pkgs.libpulseaudio ]; + nativeBuildInputs = [ pkgs.pkg-config ]; + preBuild = "sed s/pulse::libpulse.so.0/pulse/ -i target/*link*"; + }; + }; + }; + membersList = builtins.attrValues (builtins.mapAttrs (name: member: { name = pkgs.lib.removePrefix "simple-osd-" name; value = member.build; }) project.workspaceMembers); + in builtins.listToAttrs membersList); + apps = builtins.mapAttrs (_: + builtins.mapAttrs (_: pkg: { + type = "app"; + program = "${pkg}/bin/${pkg.pname}"; + })) self.packages; + + defaultPackage = builtins.mapAttrs (system: pkgs: + pkgs.buildEnv { + name = "simple-osd-daemons"; + paths = builtins.attrValues self.packages.${system}; + }) nixpkgs.legacyPackages; + + devShell = builtins.mapAttrs (system: pkgs: pkgs.mkShell { + inputsFrom = builtins.attrValues self.packages.${system}; + }) nixpkgs.legacyPackages; + }; +} diff --git a/pulseaudio/Cargo.toml b/pulseaudio/Cargo.toml new file mode 100644 index 0000000..77402cd --- /dev/null +++ b/pulseaudio/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "simple-osd-pulseaudio" +version = "0.1.0" +authors = ["Alexander Bantyev "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libpulse-binding = "2.16.2" +simple-osd-common = { version = "0.1", path = "../common" } diff --git a/pulseaudio/src/main.rs b/pulseaudio/src/main.rs new file mode 100644 index 0000000..94b0f12 --- /dev/null +++ b/pulseaudio/src/main.rs @@ -0,0 +1,84 @@ +extern crate libpulse_binding as pulse; + +extern crate simple_osd_common as osd; + +use pulse::mainloop::standard::Mainloop; +use pulse::context::Context; +use osd::config::Config; +use osd::notify::{OSD, OSDContents, OSDProgressText}; + +use pulse::context::subscribe::{subscription_masks, Operation, Facility}; + +use pulse::callbacks::ListResult; +use pulse::context::introspect::SinkInfo; + +fn main() { + let mut mainloop = Mainloop::new().expect("Failed to create mainloop"); + + let mut config = Config::new("pulseaudio"); + + let mut context = Context::new( + &mainloop, osd::APPNAME + ).expect("Failed to create new context"); + + context.connect(config.get::("default", "server").as_deref(), 0, None) + .expect("Failed to connect context"); + + // Wait for context to be ready + loop { + mainloop.iterate(false); + match context.get_state() { + pulse::context::State::Ready => { break; }, + pulse::context::State::Failed | + pulse::context::State::Unconnected | + pulse::context::State::Terminated => { + eprintln!("Context state failed/terminated, quitting..."); + return; + }, + _ => {} + } + } + + eprintln!("connected"); + + context.subscribe(subscription_masks::SINK, |success| { + if ! success { + eprintln!("failed to subscribe to events"); + return; + } + }); + + let introspector = context.introspect(); + + + // Explanation for the unsafe: + // Both subscribe_callback and sink_info_handler shall not outlive mainloop, but the borrow checker can't know that. + // Thus, it moves osd into the subscribe_callback and then tries to move it into the sink_info_handler, but that's impossible. + // In reality, both closures will be destroyed when mainloop quits, and osd's lifetime is the same as mainloop's. + unsafe { + let osd: *mut OSD = &mut OSD::new(); + (*osd).icon = Some(String::from("multimedia-volume-control")); + + let sink_info_handler = move |results: ListResult<&SinkInfo>| { + if let ListResult::Item(i) = results { + let volume = i.volume.avg(); + let sink_name = i.description.as_deref().unwrap_or("Unnamed sink"); + let muted_message = if i.mute { " [MUTED]" } else { "" }; + (*osd).title = Some(format!("Volume on {}{}", sink_name, muted_message)); + (*osd).contents = OSDContents::Progress(volume.0 as f32 / 65536., OSDProgressText::Percentage); + (*osd).update(); + } + }; + + let subscribe_callback = move |facility, operation, index| { + if facility == Some(Facility::Sink) && operation == Some(Operation::Changed) { + introspector.get_sink_info_by_index(index, sink_info_handler); + } + }; + + context.set_subscribe_callback(Some(Box::new(subscribe_callback))); + + // We need to run mainloop here for reasons I don't understand. It crashes otherwise. + mainloop.run().unwrap(); + } +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..db84e3d --- /dev/null +++ b/shell.nix @@ -0,0 +1,3 @@ +(import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) { + src = builtins.fetchGit ./.; +}).shellNix