about summary refs log tree commit diff
path: root/modules/workstation/windowing
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
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')
-rw-r--r--modules/workstation/windowing/sway/default.nix8
-rw-r--r--modules/workstation/windowing/sway/status.cpp157
-rw-r--r--modules/workstation/windowing/sway/status_command.in39
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