Files
desk-reminder/desk_reminder.c

195 lines
5.7 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/ioctl.h>
#include <openssl/sha.h>
#include <time.h>
#include <pthread.h>
#include <ctype.h>
// Interval in seconds between reminders (default 20 minutes),
// can be overridden with -i (supports optional s/m suffix).
static unsigned long long interval_seconds = 20ULL * 60ULL;
// Shared timestamp of when the "standing" event last occurred.
static unsigned long long last_time_standing = 0;
// Map a subset of key codes (digits and Enter) to characters.
static char keycode_to_char(unsigned short code)
{
switch (code) {
case KEY_0: return '0';
case KEY_1: return '1';
case KEY_2: return '2';
case KEY_3: return '3';
case KEY_4: return '4';
case KEY_5: return '5';
case KEY_6: return '6';
case KEY_7: return '7';
case KEY_8: return '8';
case KEY_9: return '9';
default: return '\0';
}
}
// Thread that once per second prints how long it has been since
// last_time_standing was updated.
static void *time_watcher_thread(void *arg)
{
(void)arg;
for (;;) {
sleep(1);
unsigned long long now = (unsigned long long)time(NULL);
if (last_time_standing != 0) {
unsigned long long delta = now - last_time_standing;
printf("Seconds since last_time_standing: %llu (interval=%llus)\n",
delta, interval_seconds);
fflush(stdout);
// If we've exceeded the configured interval, lock the screen
if (delta >= interval_seconds) {
// Use xinput to disable all keyboard-like devices until user stands again.
// This pipeline lists input devices, filters keyboards (excluding XTEST),
// extracts their IDs, and disables each one.
system("xinput --list --short | "
"grep -i 'keyboard' | grep -v 'XTEST' | "
"sed -E 's/.*id=([0-9]+).*/\\1/' | "
"xargs -r -n1 xinput disable");
system("i3lock -n -i /usr/local/share/lockscreens/standing_gopher.png");
}
}
}
return NULL;
}
int main(int argc, char **argv) {
// Replace with the specific event node for your keyboard
// Use 'lsusb' and 'cat /proc/bus/input/devices' to find the right one
const char *dev = "/dev/input/by-id/usb-IC_Reader_IC_Reader_08FF20171101-event-kbd";
struct input_event ev;
int fd;
// Optional: -i <interval>[s|m] to override interval_seconds
int opt;
while ((opt = getopt(argc, argv, "i:")) != -1) {
switch (opt) {
case 'i': {
char *end = NULL;
unsigned long long v = strtoull(optarg, &end, 10);
if (!optarg[0] || end == optarg || v == 0) {
fprintf(stderr, "Invalid interval '%s' for -i (must start with positive integer)\n", optarg);
return EXIT_FAILURE;
}
char unit = 'm'; // default unit: minutes
if (*end != '\0') {
// Allow a single unit character 's' or 'm'
if (end[1] != '\0') {
fprintf(stderr, "Invalid interval suffix in '%s' (use optional 's' or 'm')\n", optarg);
return EXIT_FAILURE;
}
unit = (char)tolower((unsigned char)*end);
if (unit != 's' && unit != 'm') {
fprintf(stderr, "Unknown interval unit '%c' in '%s' (use 's' or 'm')\n", *end, optarg);
return EXIT_FAILURE;
}
}
if (unit == 's') {
interval_seconds = v;
} else { // 'm'
interval_seconds = v * 60ULL;
}
break;
}
default:
fprintf(stderr, "Usage: %s [-i interval{s|m}]\n", argv[0]);
return EXIT_FAILURE;
}
}
fd = open(dev, O_RDONLY);
if (fd == -1) {
perror("Cannot open device");
return EXIT_FAILURE;
}
// Grab the device: This blocks all other apps from seeing these keystrokes
if (ioctl(fd, EVIOCGRAB, 1) == -1) {
perror("Could not grab device");
close(fd);
return EXIT_FAILURE;
}
printf("Input blocked from %s. Press Ctrl+C in this terminal to stop.\n", dev);
// Buffer to accumulate the characters the reader "types"
char buf_str[128];
size_t buf_len = 0;
// Initialize last_time_standing and start the watcher thread
last_time_standing = (unsigned long long)time(NULL);
pthread_t watcher;
if (pthread_create(&watcher, NULL, time_watcher_thread, NULL) != 0) {
perror("Failed to create watcher thread");
ioctl(fd, EVIOCGRAB, 0);
close(fd);
return EXIT_FAILURE;
}
pthread_detach(watcher);
// Continuous loop to capture events
while (1) {
ssize_t n = read(fd, &ev, sizeof(ev));
if (n == (ssize_t)-1) {
perror("Read error");
break;
} else if (n != sizeof(ev)) {
continue;
}
// Process only key events (type 1)
if (ev.type == EV_KEY) {
if (ev.value == 1) { // key press
if (ev.code == KEY_ENTER || ev.code == KEY_KPENTER) {
// End of the "typed" sequence: print the accumulated string
if (buf_len > 0) {
buf_str[buf_len] = '\0';
long long val = strtoll(buf_str, NULL, 10);
// Get the SHA-256 hash of the value
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256((unsigned char*)buf_str, buf_len, hash);
printf("SHA-256: ");
unsigned long long hash_val = 0;
for (int i = 0; i < 8; i++) {
hash_val = (hash_val << 8) | hash[i];
}
printf(" (as number: %llu)", hash_val);
printf("\n");
printf("Typed: %s (as number: %lld)\n", buf_str, val);
buf_len = 0;
if (hash_val == 4798061567229363866L) {
last_time_standing = (unsigned long long)time(NULL);
printf("Desk reminder timer reset at %llu seconds since epoch.\n", last_time_standing);
system("pkill i3lock");
system("xinput --list --short | "
"grep -i 'keyboard' | grep -v 'XTEST' | "
"sed -E 's/.*id=([0-9]+).*/\\1/' | "
"xargs -r -n1 xinput enable");
}
}
} else {
char c = keycode_to_char(ev.code);
if (c != '\0' && buf_len + 1 < sizeof(buf_str))
buf_str[buf_len++] = c;
}
}
}
}
// Release the grab before exiting
ioctl(fd, EVIOCGRAB, 0);
close(fd);
return 0;
}