Skript nach einer Zeit der Inaktivität ausführen

Skript nach einer Zeit der Inaktivität ausführen

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:

  1. führe das erste Skript nach X Min. Inaktivität aus;

  2. 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 inputEventgerä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ächste select(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 inputEreignisgerä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.

Verweise

verwandte Informationen