Xtea/Xtea.c
2025-01-13 07:11:05 +01:00

121 lines
3.7 KiB
C

/**
* Xtimer is a program that executes a given command or program after a given
* time if no X11 keybord or mouse input happend in the given time.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* License: Apache-2.0 (see LICENSE)
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h> // For INT_MAX and INT_MIN
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage: %s <seconds> <program> [arguments...]\n",
argv[0]);
return EXIT_FAILURE;
}
// Validate the first parameter (wait time)
char *endptr;
long wait_time = strtol(argv[1], &endptr, 10);
// Check for valid integer input
if (*endptr != '\0' || wait_time <= 0 || wait_time > INT_MAX) {
fprintf(stderr,
"Error: First argument must be a positive integer within the range 1 to %d.\n",
INT_MAX);
return EXIT_FAILURE;
}
// Safely cast the wait_time to an int (it's safe after the above check)
int wait_time_seconds = (int)wait_time;
// Initialize X11 display
Display *display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "Error: Unable to open X display.\n");
return EXIT_FAILURE;
}
// Check for XInput2 support
int opcode, event, error;
if (!XQueryExtension(display, "XInputExtension", &opcode, &event, &error)) {
fprintf(stderr,
"Error: X Input Extension is not supported on this display.\n");
XCloseDisplay(display);
return EXIT_FAILURE;
}
// Select events to monitor (keyboard, mouse clicks, and motion)
Window root = DefaultRootWindow(display);
XIEventMask evmask;
unsigned char mask[(XI_LASTEVENT + 7) / 8] = {0};
evmask.deviceid = XIAllDevices;
evmask.mask_len = sizeof(mask);
evmask.mask = mask;
XISetMask(mask, XI_KeyPress);
XISetMask(mask, XI_KeyRelease);
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_ButtonRelease);
XISetMask(mask, XI_Motion); // Listen for mouse motion events
if (XISelectEvents(display, root, &evmask, 1) != Success) {
fprintf(stderr, "Error: Unable to select input events.\n");
XCloseDisplay(display);
return EXIT_FAILURE;
}
// Timer logic with input monitoring
time_t start_time, current_time;
start_time = time(NULL);
while (1) {
current_time = time(NULL);
// Check if the timer has expired
if (current_time - start_time >= wait_time_seconds) {
break;
}
// Check for input events
while (XPending(display) > 0) {
XEvent event;
XNextEvent(display, &event);
if (event.xcookie.type == GenericEvent &&
event.xcookie.extension == opcode && // Use the opcode obtained earlier
XGetEventData(display, &event.xcookie)) {
XIDeviceEvent *xi_event = (XIDeviceEvent *)event.xcookie.data;
if (xi_event->evtype == XI_KeyPress ||
xi_event->evtype == XI_ButtonPress ||
xi_event->evtype == XI_Motion) {
// Reset the timer on input or mouse movement
start_time = time(NULL);
}
XFreeEventData(display, &event.xcookie);
}
}
// Sleep briefly to reduce CPU usage
usleep(10000); // 10 milliseconds
}
// Close the X display
XCloseDisplay(display);
// Execute the program with optional arguments
execvp(argv[2], &argv[2]);
// If execvp returns, an error occurred
perror("Error executing program");
return EXIT_FAILURE;
}