Я ищу способ выполнить определенный скрипт после периода бездействия и после завершения этого периода. Под «бездействием» я подразумеваю отсутствие событий мыши и клавиатуры. Например, я хочу:
выполнить первый скрипт после X мин. бездействия;
выполнить второй скрипт, когда этот период прерывается мышью или клавиатурой.
Лучше всего, если этот метод не будет привязан к 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 или чем-то подобном.
Примечание: Это всего лишь пример, есть и другие вещи, о которых нужно позаботиться. Например, это выводит "Timeout"каждый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
.