diff --git a/LICENSE b/LICENSE index 247aa29..d2c57e8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2024 Johannes Findeisen +Copyright 2025 Johannes Findeisen Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..560bef9 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Xtea + +Xtea 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. + +## Building checkpw + +``` +git clone https://git.xw3.org/hanez/Xtea.git +cd Xtea +make +``` + +## Installation + +``` +sudo make install +``` + +Xtea is installed to /usr/bin/. + +## Uninstall + +``` +sudo make uninstall +``` + +## Usage + +``` +Xtea -h +Usage: Xtea [-d] [-v] [-h] [arguments...] + +Options: + -d Run in daemon mode. Restart the program after inactivity. + -v Show the program version and exit. + -h Show this help message and exit. + +Arguments: + Time (in seconds) to wait after inactivity before running the program. + Program to execute after the specified delay. + [arguments...] Optional arguments to pass to the program. + +Examples: + Xtea 5 ls -ls Execute 'ls -l' after 5 seconds of inactivity. + Xtea -d 10 echo 'Hello' Run 'echo Hello' in daemon mode after 10 seconds of inactivity. +``` diff --git a/Xtea.c b/Xtea.c index 8e3e301..f22fe23 100644 --- a/Xtea.c +++ b/Xtea.c @@ -1,40 +1,83 @@ /** - * Xtimer is a program that executes a given command or program after a given + * Xtea 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 +#include #include -#include // For INT_MAX and INT_MIN #include -#include #include +#include + +#define VERSION "0.1.0" + +void print_help(const char *program_name) +{ + printf("Usage: %s [-d] [-v] [-h] [arguments...]\n", program_name); + printf("\n"); + printf("Options:\n"); + printf(" -d Run in daemon mode. Restart the program after inactivity.\n"); + printf(" -v Show the program version and exit.\n"); + printf(" -h Show this help message and exit.\n"); + printf("\n"); + printf("Arguments:\n"); + printf(" Time (in seconds) to wait after inactivity before running the program.\n"); + printf(" Program to execute after the specified delay.\n"); + printf(" [arguments...] Optional arguments to pass to the program.\n"); + printf("\n"); + printf("Examples:\n"); + printf(" %s 5 ls -ls Execute 'ls -l' after 5 seconds of inactivity.\n", program_name); + printf(" %s -d 10 echo 'Hello' Run 'echo Hello' in daemon mode after 10 seconds of inactivity.\n", program_name); +} int main(int argc, char *argv[]) { - if (argc < 3) { - fprintf(stderr, "Usage: %s [arguments...]\n", - argv[0]); + if (argc < 2) { + print_help(argv[0]); + return EXIT_FAILURE; + } + + // Check for version or help flags + if (strcmp(argv[1], "-v") == 0) { + printf("%s: %s\n", argv[0], VERSION); + return EXIT_SUCCESS; + } + + if (strcmp(argv[1], "-h") == 0) { + print_help(argv[0]); + return EXIT_SUCCESS; + } + + // Check for daemon mode + int daemon_mode = 0; + int arg_offset = 1; + + if (strcmp(argv[1], "-d") == 0) { + daemon_mode = 1; + arg_offset = 2; // Adjust argument offset for parsing other parameters + } + + if (argc < arg_offset + 2) { + print_help(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 + long wait_time = strtol(argv[arg_offset], &endptr, 10); 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); + 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) + // Safely cast the wait_time to an int int wait_time_seconds = (int)wait_time; // Initialize X11 display @@ -47,8 +90,7 @@ INT_MAX); // 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"); + fprintf(stderr, "Error: X Input Extension is not supported on this display.\n"); XCloseDisplay(display); return EXIT_FAILURE; } @@ -73,49 +115,61 @@ INT_MAX); return EXIT_FAILURE; } - // Timer logic with input monitoring - time_t start_time, current_time; - start_time = time(NULL); + do { + // Timer logic with input monitoring + time_t start_time, current_time; + start_time = time(NULL); - while (1) { - current_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); + // 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 && + 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 } - // Sleep briefly to reduce CPU usage - usleep(10000); // 10 milliseconds - } + // Execute the program with optional arguments + pid_t pid = fork(); + if (pid == 0) { + execvp(argv[arg_offset + 1], &argv[arg_offset + 1]); + + // If execvp returns, an error occurred + perror("Error executing program"); + exit(EXIT_FAILURE); + } else if (pid < 0) { + perror("Error forking to execute program"); + } else { + // Wait for the child process to finish + waitpid(pid, NULL, 0); + } + } while (daemon_mode); // 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; + return EXIT_SUCCESS; }