// 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(); int charge_now(); 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")); } int Battery::charge_now() { return stoi(attr("charge_now")); } 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 value (buf, len); if (value.back() == '\n') value.pop_back(); return value; } 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); auto batdisplay = false; try { switch (battery.status()) { case Charging: out << "↑"; break; case Discharging: out << "↓"; break; default: out << " "; } batdisplay = true; } catch (const system_error& ex) { if (ex.code().value() != ENOENT) throw ex; } try { int capacity = battery.capacity(); out << capacity << "%"; batdisplay = true; } catch (const system_error& ex) { if (ex.code().value() != ENOENT) throw ex; try { int charge_now = battery.charge_now(); out << charge_now; batdisplay = true; } catch (const system_error& ex) { if (ex.code().value() != ENOENT) throw ex; } } if (batdisplay) out << " "; } 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)); } }