如果在启用 AutoRepeat 的情况下按住X11中的某个键,则会连续收到 KeyPress 和 KeyRelease 事件。我知道可以使用 XAutoRepeatOff()功能禁用 AutoRepeat ,但这会更改整个X服务器的设置。有没有办法为单个应用程序禁用 AutoRepeat 或忽略重复的击键?
我正在寻找的是当按下某个键时出现一个 KeyPress 事件,并且在释放一个键时出现一个 KeyRelease 事件,而不会干扰X服务器 AutoRepeat 设置。
这是一个让你前进的最小例子(主要来自Beginner Xlib Tutorial):
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
Display *dis;
Window win;
XEvent report;
int main ()
{
dis = XOpenDisplay (NULL);
// XAutoRepeatOn(dis);
win = XCreateSimpleWindow (dis, RootWindow (dis, 0), 1, 1, 500, 500,
0, BlackPixel (dis, 0), BlackPixel (dis, 0));
XSelectInput (dis, win, KeyPressMask | KeyReleaseMask);
XMapWindow (dis, win);
XFlush (dis);
while (1)
{
XNextEvent (dis, &report);
switch (report.type)
{
case KeyPress:
fprintf (stdout, "key #%ld was pressed.\n",
(long) XLookupKeysym (&report.xkey, 0));
break;
case KeyRelease:
fprintf (stdout, "key #%ld was released.\n",
(long) XLookupKeysym (&report.xkey, 0));
break;
}
}
return (0);
}
答案 0 :(得分:20)
当您收到一个密钥释放并且下一个事件是按下相同的组合键时,它会自动重复并且该密钥未被实际释放。您可以使用这样的代码来查看下一个事件
if (event->type == KeyRelease && XEventsQueued(disp, QueuedAfterReading))
{
XEvent nev;
XPeekEvent(disp, &nev);
if (nev.type == KeyPress && nev.xkey.time == event->xkey.time &&
nev.xkey.keycode == event->xkey.keycode)
{
/* Key wasn’t actually released */
}
}
答案 1 :(得分:9)
您可以使用XkbSetDetectableAutorepeat函数告诉X服务器仅在用户实际释放密钥时发送KeyRelease事件 - 当您不想要自动重复事件时,则丢弃任何KeyPress而不匹配KeyRelease。 / p>
答案 2 :(得分:5)
供您参考,这是一个可以删除自动重复的 KeyPress 事件的最小例子。 谢谢你,kralyk!
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
Display *dis;
Window win;
XEvent report;
int main ()
{
dis = XOpenDisplay (NULL);
// XAutoRepeatOn(dis);
win = XCreateSimpleWindow (dis, RootWindow (dis, 0), 1, 1, 500, 500,
0, BlackPixel (dis, 0), BlackPixel (dis, 0));
XSelectInput (dis, win, KeyPressMask | KeyReleaseMask);
XMapWindow (dis, win);
XFlush (dis);
while (1)
{
XNextEvent (dis, &report);
switch (report.type)
{
case KeyPress:
fprintf (stdout, "key #%ld was pressed.\n",
(long) XLookupKeysym (&report.xkey, 0));
break;
case KeyRelease:
{
unsigned short is_retriggered = 0;
if (XEventsQueued(dis, QueuedAfterReading))
{
XEvent nev;
XPeekEvent(dis, &nev);
if (nev.type == KeyPress && nev.xkey.time == report.xkey.time &&
nev.xkey.keycode == report.xkey.keycode)
{
fprintf (stdout, "key #%ld was retriggered.\n",
(long) XLookupKeysym (&nev.xkey, 0));
// delete retriggered KeyPress event
XNextEvent (dis, &report);
is_retriggered = 1;
}
}
if (!is_retriggered)
fprintf (stdout, "key #%ld was released.\n",
(long) XLookupKeysym (&report.xkey, 0));
}
break;
}
}
return (0);
}
答案 3 :(得分:1)
您可以在按下或释放按键时设置计时器,并忽略重复间隔内发生的KeyPress和KeyRelease事件。
答案 4 :(得分:0)
另一种方法。这个对我有用。
char keyz[1024] = {0};
bool physical;
XEvent event, nev;
while (!quit) {
XNextEvent(display, &event);
...
switch(event.type) {
case KeyPress:
physical = (keyz[event.xkey.keycode] == 0);
keyz[event.xkey.keycode] = 1;
keyboard(event.xkey.window, true, event.xkey.keycode, physical);
break;
case KeyRelease:
physical = true;
if (XPending(display)) {
XPeekEvent(display, &nev);
if (nev.type == KeyPress && nev.xkey.time == event.xkey.time
&& nev.xkey.keycode == event.xkey.keycode) physical = false;
}
if (physical) keyz[event.xkey.keycode] = 0;
keyboard(event.xkey.window, false, event.xkey.keycode, physical);
break;
...
}
答案 5 :(得分:0)
这是我提出的解决方案。
XEvent event;
while(1)
{
XNextEvent(display, &event);
switch(event.type)
{
// Other cases
case ...:
...
break;
...
// On KeyRelease
case KeyRelease:
{
char keys[32];
XQueryKeymap(display, keys);
if(!(keys[event.xkey.keycode>>3] & (0x1 << (event.xkey.keycode % 8))))
{
// Stuff to do on KeyRelease
...
}
}
break;
// On KeyPress
case KeyPress:
// Stuff to do on KeyPress
...
break;
default:
...
}
}
因此,每当我收到KeyRelease事件时,我都会使用XQueryKeymap
复制到keys
按下的按键位(char
的8个不同的按键)。
对于那些不习惯使用按位操作符和移位运算符的人,这里有一个简单的解释:
keys[event.xkey.keycode>>3]
使用“右移运算符”搜索索引event.xkey.keycode / 8
(允许“整数除法”为2,4,8,16等,没有类型转换为float或double回到整数)。
0x1 << (event.xkey.keycode % 8)
。它将0x1
(== 1
)的值乘以2增加到(event.xkey.keycode % 8)
如果在此数组索引中右侧操作数中设置的仅位设置为1,&
和keys[event.xkey.keycode>>3]
之间的0x1 << (event.xkey.keycode % 8)
位运算符将进行比较。如果是,则按下该键。
最后,您只需将其封在()
中,之前有!
,如果结果为真,则不再按该键。
最后一个注意:要使用此方法,您需要继续向XServer提供事件。如果没有,XQueryKeymap将一直处于释放状态(更好地与线程一起使用)。