【问题标题】:Ignore auto repeat in X11 applications忽略 X11 应用程序中的自动重复
【发布时间】:2010-01-20 10:34:47
【问题描述】:

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

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

这是一个让你开始的小例子(主要来自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);
}

【问题讨论】:

    标签: keyboard x11 autorepeat


    【解决方案1】:

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

    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 */
      }
    }
    

    【讨论】:

    • 谢谢,这正是我一直在寻找的!
    • 是否有任何理由使用 XEventsQueued 和 QueuedAfterReading 而不是例如更便宜的 QueuedAlready?另外,由于猜测较少,XkbSetDetectableAutorepeat 不是更好的方法吗?
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      供您参考,这是一个删除自动重复 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);
      }
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          另一种方法。这个对我有用。

          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;
              ...
              }
          

          【讨论】:

            【解决方案6】:

            这是我想出的解决方案。

            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 将按下的键位(char 的 8 个不同键)复制到 keys。 对于不习惯使用按位运算符和移位运算符的人,这里有一个简单的解释:

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

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

            keys[event.xkey.keycode&gt;&gt;3]0x1 &lt;&lt; (event.xkey.keycode % 8) 之间的&amp; 位运算符将比较此数组索引中右侧操作数中设置的only 位是否设置为1。如果是这样,则正在按下该键。

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-12-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-05-14
              相关资源
              最近更新 更多