Estou procurando uma maneira de executar determinado script após um período de inatividade e após o término desse período. Por 'inatividade' quero dizer falta de eventos de mouse e teclado. Por exemplo, eu quero:
execute o primeiro script após X min. de inatividade;
execute o segundo script quando esse período for interrompido pelo mouse ou teclado.
Será melhor se esse método não estiver vinculado ao sistema X e também funcionar no terminal (quando o X não for iniciado).
Responder1
Você poderia fazer isso com um pouco de magia C (qualquer outra linguagem de programação que suporte as funções necessárias também servirá). O que você terá que fazer é:
- abra todos
input
os dispositivos de evento (tudo correspondente/dev/input/event[0-9]*
) - ligou
select(2)
para esses dispositivos, aguardando entrada, com um tempo limite apropriado (seu período ocioso)- no tempo limite: nada aconteceu: inicie seu programa
- se a entrada ficar pronta: algo aconteceu: elimine seu programa, se estiver em execução
read(2)
entrada de todos os dispositivos, para que a próximaselect(2)
chamada não retorne imediatamente
Um exemplo rápido em C ficaria assim:
#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;
}
Este exemplo esperaria pela entrada indefinidamente. Se após 5 segundos nenhuma entrada for recebida, será impresso "Timeout: …", se a entrada for recebida "No timeout: …"
Para ler mais (no entanto, você deseja executar e encerrar processos), consulte e fork(2)
, respectivamente. Como mencionado anteriormente, qualquer linguagem que permita a execução de arquivos seria suficiente, então você também pode fazer isso em Python ou Ruby ou algo parecido.exec(2)
kill(2)
select(2)
Observação: Este é apenas um exemplo, haveria coisas adicionais a serem cuidadas. Por exemplo, isso imprime "Timeout"todo5 segundos, não apenas uma vez até que a entrada seja recebida, da mesma forma, "Sem tempo limite" aparecetodopressionamento de tecla/movimento do mouse.
Além disso, isso precisaria ser executado como root
, já que os input
dispositivos de evento não podem ser lidos por ninguém por motivos óbvios.
Responder2
Aqui está um aplicativo C que encontrei e que você pode compilar.
$ 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);
}
}
São necessárias algumas bibliotecas para construir. No meu sistema Fedora 19 eu precisava das seguintes bibliotecas:
$ 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
Depois que eles foram instalados, compilei o acima assim:
$ gcc xidle.c -o xidle -lX11 -lXext -lXss
Você pode ver que ele é capaz de relatar o número de segundos que o X está detectando como tempo ocioso executando-o da seguinte forma:
$ 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
Usando este executável você pode montar um script que agora pode saber quanto tempo ocioso passou. Se for igual à janela de tempo em #1, execute seu primeiro script. Se essa janela inativa for interrompida (ponto 2 da sua pergunta), execute seu segundo script.
Um pequeno exemplo
O exemplo abaixo mostra como você pode construir pelo menos a primeira parte do seu cheque, digamos que 5 segundos se passem.
$ 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
Acima, não movi o mouse nem apertei uma tecla por mais de 5 segundos. Em seguida, pressionei a Shifttecla e o loop voltou para still < 5
.