mirror of
https://github.com/perk11/runwhenidle.git
synced 2026-01-06 20:33:57 -05:00
Make commands that fail, fail faster and move first idle check before first sleep, but after an initial 300ms sleep
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
runwhenidle
|
||||
package-build/
|
||||
*.o
|
||||
11
Makefile
11
Makefile
@@ -4,6 +4,8 @@ CC=gcc
|
||||
ifeq ($(PREFIX),)
|
||||
PREFIX := /usr
|
||||
endif
|
||||
SOURCES = sleep_utils.c main.c
|
||||
OBJECTS = $(SOURCES:.c=.o)
|
||||
|
||||
all: executable
|
||||
|
||||
@@ -13,15 +15,18 @@ release: executable
|
||||
debug: CCFLAGS += -DDEBUG -ggdb
|
||||
debug: executable
|
||||
|
||||
executable:
|
||||
$(CC) main.c -o $(TARGET_EXEC) $(CCFLAGS) $(LDFLAGS) $(LDLIBS)
|
||||
%.o: %.c
|
||||
$(CC) $(CCFLAGS) -c $< -o $@ $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
executable: $(OBJECTS)
|
||||
$(CC) $(CCFLAGS) $(OBJECTS) -o $(TARGET_EXEC) $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
install: release
|
||||
install -d $(DESTDIR)$(PREFIX)/bin/
|
||||
install -m 755 $(TARGET_EXEC) $(DESTDIR)$(PREFIX)/bin/
|
||||
|
||||
clean:
|
||||
rm -f runwhenidle
|
||||
rm -f $(OBJECTS) $(TARGET_EXEC)
|
||||
|
||||
debian-package:
|
||||
docker build --build-arg HOST_UID=`id -u` --tag runwhenidle-ubuntu2204-build distro-packages/ubuntu22.04
|
||||
|
||||
35
main.c
35
main.c
@@ -7,6 +7,8 @@
|
||||
#include <X11/extensions/scrnsaver.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "sleep_utils.h"
|
||||
|
||||
int verbose;
|
||||
int quiet;
|
||||
|
||||
@@ -68,6 +70,17 @@ pid_t run_shell_command(const char *shell_command_to_run, pid_t pid) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
void exit_if_pid_has_finished(pid_t pid) {
|
||||
int status;
|
||||
if (waitpid(pid, &status, WNOHANG + WUNTRACED) == pid && WIFEXITED(status)) {
|
||||
int exit_code = WEXITSTATUS(status);
|
||||
if (verbose) {
|
||||
fprintf(stderr, "PID %i has finished with exit code %u\n", pid, exit_code);
|
||||
}
|
||||
exit(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
pid_t pid;
|
||||
int user_idle_timeout_ms = 300000;
|
||||
@@ -120,14 +133,23 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
pid = run_shell_command(argv[optind], pid);
|
||||
|
||||
// Let command run for 300ms to give it a chance to error-out or provide initial output.
|
||||
// 300ms is chosen to avoid giving user a noticeable delay while giving most quick commands a chance to finish.
|
||||
sleep_for_milliseconds(300);
|
||||
|
||||
int polling_interval_seconds = 1;
|
||||
int sleep_time_seconds = polling_interval_seconds;
|
||||
int command_paused = 0;
|
||||
|
||||
// Monitor user activity
|
||||
while (1) {
|
||||
sleep(sleep_time_seconds);
|
||||
XScreenSaverQueryInfo(dpy, DefaultRootWindow(dpy), info);
|
||||
|
||||
// Checking this after querying the screensaver timer so that the command is still running while
|
||||
// we're querying the screensaver and has a chance to do some work and finish,
|
||||
// but before potentially pausing the command to avoid trying to pause it if it completed.
|
||||
exit_if_pid_has_finished(pid);
|
||||
|
||||
if (info->idle > user_idle_timeout_ms) {
|
||||
// User is inactive
|
||||
if (command_paused) {
|
||||
@@ -159,15 +181,6 @@ int main(int argc, char *argv[]) {
|
||||
sleep_time_seconds);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the command has finished
|
||||
int status;
|
||||
if (waitpid(pid, &status, WNOHANG + WUNTRACED) == pid && WIFEXITED(status)) {
|
||||
int exit_code = WEXITSTATUS(status);
|
||||
if (verbose) {
|
||||
fprintf(stderr, "Command has finished with exit code %u\n", exit_code);
|
||||
}
|
||||
return exit_code;
|
||||
}
|
||||
sleep(sleep_time_seconds);
|
||||
}
|
||||
}
|
||||
|
||||
20
sleep_utils.c
Normal file
20
sleep_utils.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
int sleep_for_milliseconds(long milliseconds)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
if (milliseconds < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ts.tv_sec = milliseconds / 1000;
|
||||
ts.tv_nsec = (milliseconds % 1000) * 1000000;
|
||||
|
||||
//We don't care about sleeping for exact amount in case sleep is interrupted by a signal,
|
||||
//which is why NULL is used for the second argument
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
15
sleep_utils.h
Normal file
15
sleep_utils.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef SLEEP_UTILS_H
|
||||
#define SLEEP_UTILS_H
|
||||
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
/**
|
||||
* Sleeps for the specified number of milliseconds unless interrupted by a signal.
|
||||
*
|
||||
* @param milliseconds The number of milliseconds to sleep.
|
||||
* @return 0 on success, -1 on failure or being interrupted by a signal (sets errno to EINVAL if milliseconds is negative).
|
||||
*/
|
||||
int sleep_for_milliseconds(long milliseconds);
|
||||
|
||||
#endif /* SLEEP_UTILS_H */
|
||||
Reference in New Issue
Block a user