about summary refs log tree commit diff
path: root/modules/workstation/windowing/sway/status.cpp
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2020-04-17 12:37:56 +0000
committerAlyssa Ross <hi@alyssa.is>2020-04-17 12:53:18 +0000
commitdb03ffc0e69656dc688b4d41cdaf64b9ac2ff99a (patch)
tree7a81d38d738773abc96903e114702dd8cdd5ac21 /modules/workstation/windowing/sway/status.cpp
parent0dfa02be057e7875e21fef874fa8236b894bf3b5 (diff)
downloadnixlib-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/sway/status.cpp')
-rw-r--r--modules/workstation/windowing/sway/status.cpp157
1 files changed, 157 insertions, 0 deletions
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));
+	}
+}