24

如果您在启用AutoRepeat时按住 X11 中的某个键,您将不断收到KeyPressKeyRelease事件。我知道可以使用函数 XAutoRepeatOff() 禁用AutoRepeat 但这会更改整个 X 服务器的设置。有没有办法为单个应用程序禁用AutoRepeat或忽略重复的击键?

我正在寻找的是当一个键被按下时的单个KeyPress事件和一个键被释放时的单个KeyRelease事件,而不会干扰 X 服务器的AutoRepeat设置。

这是一个让您开始的最小示例(主要来自Beginner Xlib 教程):

#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);
}
4

6 回答 6

25

当您收到按键释放并且下一个事件是按下相同组合键的按键时,它会自动重复并且按键没有被准确释放。您可以使用这样的代码来查看下一个事件

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 */
  }
}
于 2010-07-10T21:02:29.907 回答
14

您可以使用XkbSetDetectableAutorepeat函数告诉 X 服务器仅在用户实际释放键时发送 KeyRelease 事件 - 当您不想要自动重复事件时,您会丢弃任何没有匹配 KeyRelease 的 KeyPress。

于 2010-01-20T15:51:42.890 回答
7

供您参考,这是一个删除自动重复的KeyPress事件的最小工作示例。 谢谢你,克拉雷克!

#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);
}
于 2010-07-11T09:37:50.897 回答
1

您可以在按下或释放键时设置计时器,并忽略重复间隔内发生的 KeyPress 和 KeyRelease 事件。

于 2010-01-20T10:44:21.073 回答
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;
    ...
    }
于 2014-02-12T01:15:03.017 回答
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按下的键位(按 8 个不同的键char)。对于不习惯使用按位运算符和移位运算符的人,这里有一个简单的解释:

keys[event.xkey.keycode>>3]使用“右移运算符”搜索索引event.xkey.keycode / 8(允许“整数除法”除以 2、4、8、16 等,无需类型转换为浮点数或双精度数并返回整数)。

0x1 << (event.xkey.keycode % 8)相反。它将0x1( == 1) 的值乘以 2 提高到(event.xkey.keycode % 8)

如果在右侧操作数中设置的唯一位在此数组索引内设置为 1,则and之间的&位运算符将进行比较。如果是这样,则正在按下该键。keys[event.xkey.keycode>>3]0x1 << (event.xkey.keycode % 8)

最后,您只需将其括在 中(),前面加上一个!权利,如果结果为真,您就不再按该键了。

最后一个注意事项:要使用此方法,您需要不断向 XServer 提供事件。如果没有,XQueryKeymap 将冻结,直到它冻结(更好地与线程一起使用)。

于 2015-02-13T19:37:32.580 回答