/** * 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 - 2025 * License: Apache-2.0 (see LICENSE) */ #include #include #include #include // For INT_MAX and INT_MIN #include #include #include int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s [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; }