我正在寻找一种在不活动一段时间后以及该时间段结束后执行某些脚本的方法。我所说的“不活动”是指缺少鼠标和键盘事件。例如我想要:
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 或类似的语言。
笔记: 这只是一个例子,还有其他事情需要注意。例如,这会打印“超时”每一个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
。