Initial commit.

This commit is contained in:
Johannes Findeisen 2025-01-13 06:07:48 +01:00
commit 09fbae8eab
3 changed files with 140 additions and 0 deletions

13
LICENSE Normal file
View file

@ -0,0 +1,13 @@
Copyright 2024 Johannes Findeisen
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

6
Makefile Normal file
View file

@ -0,0 +1,6 @@
all:
$(CC) -Wall -o xtimer xtimer.c -lX11 -lXi
clean:
rm -f xtimer

121
xtimer.c Normal file
View file

@ -0,0 +1,121 @@
/**
* xtimer is a program that executes a givin 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 <unistd.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include <time.h>
#include <limits.h> // For INT_MAX and INT_MIN
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;
}