From 704f23576a00e4bec41006ac97e955d7a82b8012 Mon Sep 17 00:00:00 2001
From: Caleb Connolly <caleb.connolly@linaro.org>
Date: Fri, 12 Apr 2024 22:39:22 +0100
Subject: [PATCH] iskey: new utility

iskey tells you if any of a given list of keys are pressed on any input
device in the system.

e.g.

$ iskey KEY_VOLUMEDOWN

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
---
 README.md         |   3 ++
 iskey/README.md   |   7 +++
 iskey/iskey.c     | 128 ++++++++++++++++++++++++++++++++++++++++++++++
 iskey/meson.build |   7 +++
 4 files changed, 145 insertions(+)
 create mode 100644 iskey/README.md
 create mode 100644 iskey/iskey.c
 create mode 100644 iskey/meson.build

diff --git a/README.md b/README.md
index ade831e..92cad17 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ BuffyBox is a suite of graphical applications for the terminal.
 
 **[squeek2lvgl]** – Converter for transforming [Squeekboard] layouts into [LVGL]-compatible C code
 
+**[iskey]** - A utility for checking for key presses in bash scripts.
+
 **[shared]** – Internal code that is shared by some or all applications in the suite but not meant to be used externally 
 
 ## Contributing
@@ -44,6 +46,7 @@ For the license of bundled images and fonts, see [shared/cursor] and [shared/fon
 [LVGL]: https://github.com/lvgl/lvgl
 [shared]: ./shared
 [squeek2lvgl]: ./squeek2lvgl
+[iskey]: ./iskey
 [Squeekboard]: https://gitlab.gnome.org/World/Phosh/squeekboard
 [shared/cursor]: ./shared/cursor
 [shared/fonts]: ./shared/fonts
diff --git a/iskey/README.md b/iskey/README.md
new file mode 100644
index 0000000..ff7bf6f
--- /dev/null
+++ b/iskey/README.md
@@ -0,0 +1,7 @@
+# iskey
+
+A tiny utility for determining if a key is pressed.
+
+iskey is intended to be used in the context of a constrained environment like
+the initramfs, it allows for easily scripting things like "is the user holding
+volume down" which are generally non-trivial to check for in a shell script.
diff --git a/iskey/iskey.c b/iskey/iskey.c
new file mode 100644
index 0000000..e92f52d
--- /dev/null
+++ b/iskey/iskey.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <dirent.h> 
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libevdev/libevdev.h>
+
+#define MAX_KEYS 32
+
+static int codes[MAX_KEYS] = { 0 };
+static unsigned int types[MAX_KEYS] = { 0 };
+static int debug = 0;
+static int print_key = 0;
+
+#define log(fmt, ...) \
+	do { \
+		if (debug) \
+			fprintf(stderr, fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define INPUT_DIR "/dev/input/"
+
+int usage(const char *name)
+{
+	fprintf(stderr, "Usage: %s [-d] [-s] key1...\n", name);
+	fprintf(stderr, "  -d: debug mode\n");
+	fprintf(stderr, "  -s: print the name of the key\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "  Check if any input device has the specified keys pressed\n"
+			"  and exit with code 0 if so, otherwise exit with code 1\n");
+	return 1;
+}
+
+/* Check if any of the input devices have a non-zero value for any of the keys.
+ * if so then return 0, otherwise return -1.
+ */
+int check_for_keys(struct libevdev *dev)
+{
+	int i;
+
+	for (i = 0; i < MAX_KEYS; i++) {
+		if (codes[i] == 0)
+			break;
+		if (!libevdev_has_event_code(dev, types[i], codes[i]))
+			continue;
+		if (libevdev_get_event_value(dev, types[i], codes[i])) {
+			if (print_key)
+				printf("%s\n", libevdev_event_code_get_name(types[i], codes[i]));
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+int main(int argc, char *argv[])
+{
+	struct libevdev *dev;
+	struct dirent *dir;
+	DIR *d;
+	int i = 0, opt, fd;
+	char path[64] = { 0 };
+
+	/* getopt */
+	while ((opt = getopt(argc, argv, "d")) != -1) {
+		switch (opt) {
+		case 'd':
+			debug = 1;
+			break;
+		case 's':
+			print_key = 1;
+			break;
+		default:
+			return usage(argv[0]);
+		}
+	}
+
+	for (; optind < argc && i < MAX_KEYS; optind++) {
+		codes[i] = libevdev_event_code_from_code_name(argv[optind]);
+		if (codes[i] == -1) {
+			fprintf(stderr, "Unknown key %s\n", argv[optind]);
+			return 1;
+		}
+		types[i++] = libevdev_event_type_from_code_name(argv[optind]);
+		log("Checking for %s %s (%d)\n", libevdev_event_type_get_name(types[i-1]), argv[optind], codes[i-1]);
+	}
+
+	d = opendir(INPUT_DIR);
+	if (!d) {
+		perror("couldn't open /dev/input/");
+		return 1;
+	}
+
+	/* Walk through the entries in /dev/input */
+	while ((dir = readdir(d)) != NULL) {
+		memset(path, 0, sizeof(path));
+		i = snprintf(path, sizeof(path), "%s%s", INPUT_DIR, dir->d_name);
+		if (i < 0 || i >= sizeof(path)) {
+			printf("Path '%s' too long\n", dir->d_name);
+			return 1;
+		}
+
+		if (dir->d_type != DT_CHR || strncmp("event", dir->d_name, 5) != 0)
+			continue;
+
+		fd = open(path, O_RDONLY|O_NONBLOCK);
+		if (fd < 0) {
+			log("couldn't open device %s\n", dir->d_name);
+			continue;
+		}
+		fd = libevdev_new_from_fd(fd, &dev);
+		if (fd < 0) {
+			log("couldn't init libevdev for %s: %s\n", dir->d_name, strerror(-fd));
+			continue;
+		}
+		log("Checking device %s\n", libevdev_get_name(dev));
+		if (!check_for_keys(dev)) {
+			close(fd);
+			return 0;
+		}
+		close(fd);
+	}
+	closedir(d);
+
+	return 1;
+}
diff --git a/iskey/meson.build b/iskey/meson.build
new file mode 100644
index 0000000..aa317d4
--- /dev/null
+++ b/iskey/meson.build
@@ -0,0 +1,7 @@
+project('iskey', 'c')
+
+libevdev_dep = dependency('libevdev')
+
+iskey_exe = executable('iskey',
+			'iskey.c',
+			dependencies: libevdev_dep)
-- 
GitLab