一定時間操作が行われなかった後にスクリプトを実行する

一定時間操作が行われなかった後にスクリプトを実行する

一定期間の非アクティブ状態の後、およびその期間の終了後に特定のスクリプトを実行する方法を探しています。「非アクティブ状態」とは、マウスとキーボードのイベントがないことを意味します。たとえば、次のようになります。

  1. X 分間操作がない後に最初のスクリプトを実行します。

  2. その期間がマウスまたはキーボードによって中断されたときに 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 秒経っても入力が受信されない場合は、「タイムアウト: …」と出力され、入力が受信された場合は「タイムアウトなし: …」と出力されます。

さらに詳しい情報については (ただし、プロセスを実行および終了したい場合)、それぞれfork(2)exec(2)およびを参照してくださいkill(2)。前述のように、select(2)ファイルで実行できる言語であればどれでも十分なので、Python や Ruby などで実行することもできます。

注記: これは単なる例であり、他にも考慮すべき点があるでしょう。たとえば、これは「タイムアウト」と出力します。5秒間、入力が受信されるまで1回だけではなく、同様に「タイムアウトなし」が表示されますキーストローク/マウスの動き。

また、明らかな理由によりイベント デバイスは誰にも読み取れない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)、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

参考文献

関連情報