From db03ffc0e69656dc688b4d41cdaf64b9ac2ff99a Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 17 Apr 2020 12:37:56 +0000 Subject: 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! --- modules/workstation/windowing/sway/status.cpp | 157 ++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 modules/workstation/windowing/sway/status.cpp (limited to 'modules/workstation/windowing/sway/status.cpp') 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 . + +#include +#include +#include +#include +#include +#include + +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 . + 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)); + } +} -- cgit 1.4.1