Execute o script após o período de inatividade

3

Estou procurando uma maneira de executar determinado script após o período de inatividade e após a conclusão desse período. Por "inatividade", quero dizer falta de eventos de mouse e teclado. Por exemplo. Eu quero:

  1. executa o primeiro script após X min. de inatividade;

  2. executa o segundo script quando esse período é interrompido por 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).

    
por diffycat 10.05.2014 / 19:02

2 respostas

2

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 será boa). O que você precisa fazer é:

  • abra todos os dispositivos de evento input (tudo que corresponda a /dev/input/event[0-9]* )
  • tem select(2) chamado nesses dispositivos, aguardando entrada, com um tempo limite apropriado (seu período ocioso)
    • no tempo limite: nada aconteceu: inicie seu programa
    • se a entrada estiver pronta: algo aconteceu: mate seu programa, se estiver executando
  • read(2) de entrada de todos os dispositivos, por isso, a próxima chamada select(2) não retornará 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 aguardaria a entrada indefinidamente. Se após 5 segundos nenhuma entrada for recebida, imprimirá "Tempo limite:…", se a entrada for recebida "Sem tempo limite:…"

Para ler mais (você deseja executar e eliminar processos, por exemplo), consulte fork(2) , exec(2) e kill(2) , respectivamente. Como mencionado anteriormente, cada linguagem que permite executar select(2) em arquivos seria suficiente, então você também pode fazer isso em Python, Ruby ou algo parecido.

Observação: Esse é apenas um exemplo, haveria outras coisas a serem atendidas. Por exemplo, isso imprime "Timeout" a cada 5 segundos, não apenas uma vez até que a entrada seja recebida, da mesma forma "Sem timeout" aparece em todos os movimentos da tecla / mouse .

Além disso, isso precisa ser executado como root , pois os dispositivos de evento input não são legíveis por ninguém por motivos óbvios.

    
por 11.05.2014 / 01:29
1

Aqui está uma aplicação 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);
    }
}

Precisa de 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

Uma vez que estas foram instaladas eu compilei o acima da seguinte forma:

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

Você pode ver que é capaz de relatar o número de segundos que o X está detectando como tempo ocioso executando 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ê poderia montar um script que agora saiba quanto tempo ocioso passou. Se for igual à janela de tempo em # 1, execute seu primeiro script. Se essa janela inativa for interrompida (item 2 da sua pergunta), execute seu segundo script.

Um pequeno exemplo

O exemplo a seguir mostra como você pode construir pelo menos a primeira parte do seu cheque, se você passar 5 segundos.

$ 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, eu não movi o mouse nem apertei uma tecla por 5 segundos ou mais. Em seguida, pressionei a tecla Shift e o loop voltou para still < 5 .

Referências

por 11.05.2014 / 06:14