Ich suche nach einer Möglichkeit, bestimmte Skripts nach einer Zeit der Inaktivität und nach Ablauf dieser Zeit auszuführen. Mit „Inaktivität“ meine ich das Fehlen von Maus- und Tastaturereignissen. Ich möchte beispielsweise:
führe das erste Skript nach X Min. Inaktivität aus;
Führen Sie das zweite Skript aus, wenn dieser Zeitraum durch Maus oder Tastatur unterbrochen wird.
Am besten ist es, wenn diese Methode nicht an das X-System gebunden ist und auch im Terminal funktioniert (wenn X nicht gestartet ist).
Antwort1
Sie können dies mit ein wenig C-Magie erreichen (jede andere Programmiersprache, die die benötigten Funktionen unterstützt, ist auch in Ordnung). Was Sie tun müssen, ist:
- alle
input
Eventgeräte öffnen (alles passende/dev/input/event[0-9]*
) - haben
select(2)
diese Geräte aufgerufen und warten auf Eingaben, mit einem entsprechenden Timeout (Ihrer Leerlaufzeit)- bei Zeitüberschreitung: nichts ist passiert: starten Sie Ihr Programm
- Wenn die Eingabe bereit ist: Etwas ist passiert: Beenden Sie Ihr Programm, wenn es ausgeführt wird
read(2)
Eingabe von allen Geräten, so dass der nächsteselect(2)
Anruf nicht sofort zurückkommt
Ein kurzes Beispiel in C würde so aussehen:
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <stdlib.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
int *fds, ret, i;
glob_t glob_result;
/* find all devices matching /dev/input/event[0-9]* */
ret = glob("/dev/input/event[0-9]*", GLOB_ERR|GLOB_NOSORT|GLOB_NOESCAPE, NULL, &glob_result);
if (ret)
err(EXIT_FAILURE, "glob");
/* allocate array for opened file descriptors */
fds = malloc(sizeof(*fds) * (glob_result.gl_pathc+1));
if (fds == NULL)
err(EXIT_FAILURE, "malloc");
/* open devices */
for (i = 0; i < glob_result.gl_pathc; i++) {
fds[i] = open(glob_result.gl_pathv[i], O_RDONLY|O_NONBLOCK);
if (fds[i] == -1)
err(EXIT_FAILURE, "open `%s'", glob_result.gl_pathv[i]);
}
fds[i] = -1; /* end of array */
for (;;) {
char buf[512];
struct timeval timeout;
fd_set readfds;
int nfds = -1;
FD_ZERO(&readfds);
/* select(2) might alter the fdset, thus freshly set it
on every iteration */
for (i = 0; fds[i] != -1; i++) {
FD_SET(fds[i], &readfds);
nfds = fds[i] >= nfds ? fds[i] + 1 : nfds;
/* read everything what's available on this fd */
while ((ret = read(fds[i], buf, sizeof(buf))) > 0)
continue; /* read away input */
if (ret == -1 && errno != EAGAIN)
err(EXIT_FAILURE, "read");
}
/* same for timeout, 5 seconds here */
timeout.tv_sec = 5; /* FIXME */
timeout.tv_usec = 0;
ret = select(nfds, &readfds, NULL, NULL, &timeout);
if (ret == -1)
err(EXIT_FAILURE, "select");
if (ret == 0)
printf("Timeout: start first script\n");
} else {
printf("No timeout: start second script\n");
}
}
return 0;
}
Dieses Beispiel würde unendlich lange auf eine Eingabe warten. Wenn nach 5 Sekunden keine Eingabe eingeht, wird „Timeout: …“ ausgegeben, wenn eine Eingabe eingeht, „Kein Timeout: …“
Weitere Informationen (Sie möchten jedoch Prozesse ausführen und beenden) finden Sie unter bzw. fork(2)
Wie bereits erwähnt, ist jede Sprache, die die Ausführung auf Dateien ermöglicht, ausreichend. Sie können dies also auch in Python, Ruby oder etwas Ähnlichem tun.exec(2)
kill(2)
select(2)
Notiz: Dies ist nur ein Beispiel, es gibt noch weitere Dinge, die beachtet werden müssen. Dies gibt beispielsweise "Timeout" aus.jeden5 Sekunden, nicht nur einmal bis eine Eingabe erfolgt, ebenso kommt "Kein Timeout"jedenTastendruck/Mausbewegung.
Außerdem müsste dies als ausgeführt werden root
, da die input
Ereignisgeräte aus offensichtlichen Gründen für niemanden lesbar sind.
Antwort2
Hier ist eine C-Anwendung, die ich gefunden habe und die Sie kompilieren können.
$ more xidle.c
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/scrnsaver.h>
/* Report amount of X server idle time. */
/* Build with- */
/* cc xidle.c -o xidle -lX11 -lXext -lXss */
int main(int argc, char *argv[])
{
Display *display;
int event_base, error_base;
XScreenSaverInfo info;
float seconds;
display = XOpenDisplay("");
if (XScreenSaverQueryExtension(display, &event_base, &error_base)) {
XScreenSaverQueryInfo(display, DefaultRootWindow(display), &info);
seconds = (float)info.idle/1000.0f;
printf("%f\n",seconds);
return(0);
}
else {
fprintf(stderr,"Error: XScreenSaver Extension not present\n");
return(1);
}
}
Zum Erstellen sind einige Bibliotheken erforderlich. Auf meinem Fedora 19-System benötigte ich die folgenden Bibliotheken:
$ rpm -qf /lib64/libX11.so.6 /lib64/libXext.so.6 /lib64/libXss.so.1
libX11-1.6.0-1.fc19.x86_64
libXext-1.3.2-1.fc19.x86_64
libXScrnSaver-1.2.2-5.fc19.x86_64
Nachdem diese installiert waren, habe ich das Obige wie folgt kompiliert:
$ gcc xidle.c -o xidle -lX11 -lXext -lXss
Sie können sehen, dass es in der Lage ist, die Anzahl der Sekunden zu melden, die X als Leerlaufzeit erkennt, indem Sie es wie folgt ausführen:
$ while [ 1 ]; do ./xidle ; sleep 2;done
0.005000
1.948000
3.954000
5.959000
7.965000
0.073000 <--- moved the mouse here which resets it
0.035000
Mit dieser ausführbaren Datei können Sie ein Skript erstellen, das nun weiß, wie viel Leerlaufzeit vergangen ist. Wenn sie dem Zeitfenster in Nr. 1 entspricht, führen Sie Ihr erstes Skript aus. Wenn dieses Leerlauffenster unterbrochen wird (Punkt 2 Ihrer Frage), führen Sie Ihr zweites Skript aus.
Ein kleines Beispiel
Nachfolgend sehen Sie, wie Sie zumindest den ersten Teil Ihrer Prüfung gestalten könnten, wenn beispielsweise 5 Sekunden vergehen.
$ while [ 1 ]; do
idle=$(./xidle);
[ $( echo "$idle > 5" | bc ) -eq 0 ] && echo "still < 5" || echo "now > 5";
sleep 2;
done
still < 5
still < 5
still < 5
now > 5
now > 5
still < 5
still < 5
Im obigen Beispiel habe ich die Maus über 5 Sekunden lang nicht bewegt oder eine Taste gedrückt. Dann habe ich die ShiftTaste gedrückt und die Schleife ist wieder auf zurückgeschaltet still < 5
.