// 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));
}
}