diff options
author | Alyssa Ross <hi@alyssa.is> | 2020-04-17 12:37:56 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2020-04-17 12:53:18 +0000 |
commit | db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a (patch) | |
tree | 7a81d38d738773abc96903e114702dd8cdd5ac21 /modules/workstation/windowing | |
parent | 0dfa02be057e7875e21fef874fa8236b894bf3b5 (diff) | |
download | nixlib-db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a.tar nixlib-db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a.tar.gz nixlib-db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a.tar.bz2 nixlib-db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a.tar.lz nixlib-db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a.tar.xz nixlib-db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a.tar.zst nixlib-db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a.zip |
modules/sway: rewrite status_command in C++
The execline version was nice, but I think execline is not really suitable for fairly complicated scripts that run once a second. I frequently saw it as one of the most active processes on the system, and it was probably really bad for power consumption! Before rewriting in C++, I tried C, but the string handling was extremely annoying, and Rust, but it can't do time stuff in the standard library. I didn't want to have to pull in a library just for this, so C++ it was. My first C++ program, in fact!
Diffstat (limited to 'modules/workstation/windowing')
-rw-r--r-- | modules/workstation/windowing/sway/default.nix | 8 | ||||
-rw-r--r-- | modules/workstation/windowing/sway/status.cpp | 157 | ||||
-rw-r--r-- | modules/workstation/windowing/sway/status_command.in | 39 |
3 files changed, 160 insertions, 44 deletions
diff --git a/modules/workstation/windowing/sway/default.nix b/modules/workstation/windowing/sway/default.nix index 665763f94615..5901639d1baa 100644 --- a/modules/workstation/windowing/sway/default.nix +++ b/modules/workstation/windowing/sway/default.nix @@ -29,11 +29,9 @@ inherit (cfg) extraConfig; }; - status_command = pkgs.substituteAll { - src = ./status_command.in; - isExecutable = true; - inherit (pkgs) execline; - }; + status_command = pkgs.runCommandCC "status" {} '' + c++ -std=c++17 -o $out ${./status.cpp} + ''; choose_workspace = pkgs.substituteAll { src = ./choose_workspace.sh.in; diff --git a/modules/workstation/windowing/sway/status.cpp b/modules/workstation/windowing/sway/status.cpp new file mode 100644 index 000000000000..8f593d2a93cb --- /dev/null +++ b/modules/workstation/windowing/sway/status.cpp @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// Copyright 2020 Alyssa Ross +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +#include <chrono> +#include <fcntl.h> +#include <filesystem> +#include <iostream> +#include <thread> +#include <unistd.h> + +namespace fs = std::filesystem; + +using fs::directory_iterator; +using std::chrono::seconds; +using std::generic_category; +using std::put_time; +using std::stoi; +using std::string; +using std::stringstream; +using std::system_error; +using std::this_thread::sleep_for; +using std::time; + +enum BatteryStatus { + Unknown, + Charging, + Discharging, + NotCharging, + Full, +}; + +class Battery { +public: + Battery(string name); + + string name() { return m_name; } + fs::path path(); + BatteryStatus status(); + int capacity(); + +private: + string m_name; + string attr(string name); +}; + +Battery::Battery(string name) +{ + m_name = name; +} + +fs::path Battery::path() +{ + static fs::path base = "/sys/class/power_supply"; + return base / name(); +} + +BatteryStatus Battery::status() +{ + auto status = attr("status"); + + if (status == "Charging") + return Charging; + if (status == "Discharging") + return Discharging; + if (status == "Not charging") + return NotCharging; + if (status == "Full") + return Full; + + return Unknown; +} + +int Battery::capacity() +{ + return stoi(attr("capacity")); +} + +string Battery::attr(string name) +{ + // Use read() to make sure this is done in a single read syscall, + // because sysfs doesn't like multiple reads. + int fd = open((path() / name).c_str(), O_RDONLY); + if (fd == -1) + throw system_error(errno, generic_category()); + char buf[13]; + int len = read(fd, &buf, 13); + if (len == -1) + throw system_error(errno, generic_category()); + close(fd); + string capacity (buf, len); + if (capacity.back() == '\n') + capacity.pop_back(); + return capacity; +} + +int main(void) +{ + while (true) { + // Buffer output so it can be done all at once in a single write(), + // because of <https://github.com/swaywm/sway/issues/3857>. + stringstream out; + + for (const auto& entry : directory_iterator("/sys/class/power_supply")) { + auto name = entry.path().filename().string(); + if (name.find("BAT") != 0) + continue; + + Battery battery (name); + BatteryStatus status; + int capacity; + + try { + status = battery.status(); + capacity = battery.capacity(); + } catch (const system_error& ex) { + if (ex.code().value() == ENOENT) + continue; + + throw ex; + } + + switch (status) { + case Charging: + out << "↑"; + case Discharging: + out << "↓"; + default: + out << " "; + } + + out << capacity << "% "; + } + + auto t = time(nullptr); + out << put_time(localtime(&t), "%F %T") << "\n"; + + auto buf = out.str(); + if (write(STDOUT_FILENO, buf.c_str(), buf.length()) == -1) + perror("write"); + + sleep_for(seconds(1)); + } +} diff --git a/modules/workstation/windowing/sway/status_command.in b/modules/workstation/windowing/sway/status_command.in deleted file mode 100644 index 19b87b8e0777..000000000000 --- a/modules/workstation/windowing/sway/status_command.in +++ /dev/null @@ -1,39 +0,0 @@ -#! @execline@/bin/execlineb - -loopwhilex - -elglob -0 bats /sys/class/power_supply/BAT* - -# We need to capture the whole status line, and then echo it all at -# once, to work around https://github.com/swaywm/sway/issues/3857. -backtick -i -n line { - foreground { - forx bat { $bats } - importas -i -u bat bat - - foreground { - backtick -i -n status { cat ${bat}/status } - importas -i -u status status - - ifelse { test $status = Charging } - { printf "↑" } - - if { test $status = Discharging } - printf "↓" - } - - foreground { - redirfd -r 0 ${bat}/capacity - tr -d "\n" - } - - foreground { printf "%% " } - } - - foreground { date "+%Y-%m-%d %H:%M:%S" } -} -importas -i -u line line - -if { echo $line } - -sleep 1 |