在不活動一段時間後執行腳本

在不活動一段時間後執行腳本

我正在尋找一種在不活動一段時間後以及該時間段結束後執行某些腳本的方法。我所說的「不活動」是指缺少滑鼠和鍵盤事件。例如我想要:

  1. X 分鐘後執行第一個腳本。不活動;

  2. 當該時間段被滑鼠或鍵盤中斷時執行第二個腳本。

如果該方法不與 X 系統綁定並且也能在終端機中工作(當 X 未啟動時),那將是最好的。

答案1

您可以使用一點 C 魔法來做到這一點(支援所需功能的任何其他程式語言也可以)。你需要做的是:

  • 開啟所有input事件設備(所有符合的/dev/input/event[0-9]*
  • select(2)呼叫這些設備,等待輸入,並有適當的逾時(您的空閒時間)
    • 超時:什麼也沒發生:啟動你的程式
    • 如果輸入準備好:發生了什麼事:殺死你的程序,如果正在運行
  • read(2)來自所有設備的輸入,因此下一個select(2)呼叫不會立即返回

C 語的一個簡單範例如下所示:

#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;
}

此範例將無限期地等待輸入。如果5秒後沒有收到輸入,它將列印“Timeout:...”,如果收到輸入“No timeout:...”

若要進一步閱讀(不過,您想要執行和終止進程),請分別參閱fork(2)exec(2)kill(2)。如前所述,每種允許select(2)在文件上運行的語言就足夠了,因此您也可以使用 Python 或 Ruby 或類似的語言。

筆記: 這只是一個例子,還有其他事情要注意。例如,這會列印“超時”每一個5秒,不只是一次直到收到輸入,同樣「無超時」出現每一個按鍵/滑鼠移動。

另外,這需要作為 運行root,因為input出於明顯的原因,任何人都無法讀取事件設備。

答案2

這是我找到的一個可以編譯的 C 應用程式。

$ 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);
    }
}

它需要幾個庫來建置。在我的 Fedora 19 系統上,我需要以下函式庫:

$ 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

安裝完這些後,我像這樣編譯了上面的內容:

$ gcc xidle.c -o xidle -lX11 -lXext -lXss

您可以看到它能夠透過像這樣運行它來報告 X 檢測為空閒時間的秒數:

$ 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

使用此可執行文件,您可以編寫一個腳本,該腳本現在可以知道已經過去了多少空閒時間。如果它等於 #1 中的時間窗口,則運行您的第一個腳本。如果該空閒視窗被中斷(問題中的第 2 條),則執行第二個腳本。

一個小例子

下面顯示如何至少建立支票的第一部分(假設 5 秒過去了)。

$ 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

在上面我沒有移動滑鼠或敲擊按鍵超過 5 秒。然後我按下該Shift鍵,循環切換回still < 5

參考

相關內容