忽略 X11 应用程序中的自动重复

发布于 2024-08-19 05:43:03 字数 1403 浏览 6 评论 0原文

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

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

下面是一个帮助您入门的最小示例(主要来自初学者 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);
}

If you press and hold a key in X11 while AutoRepeat is enabled, you continuously receive KeyPress and KeyRelease events. I know that AutoRepeat can be disabled using the function XAutoRepeatOff(), but this changes the setting for the whole X server. Is there a way to either disable AutoRepeat for a single application or to ignore repeated keystrokes?

What I'm looking for is a single KeyPress event when a key is pressed and a single KeyRelease event when a key is released, without interfering with the X server's AutoRepeat setting.

Here's a minimal example to get you going (mostly from the 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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

凝望流年 2024-08-26 05:43:03

当您收到按键释放并且下一个事件是按下相同组合键的按键时,则会自动重复并且该按键并未实际释放。您可以使用这样的代码来查看下一个事件

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

When you receive a key release and the next event is a key press of the same key combination, then it's auto-repeat and the key wasn't acutally released. You can use code like this to peek next event

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 */
  }
}
笑脸一如从前 2024-08-26 05:43:03

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

You can use the XkbSetDetectableAutorepeat function to tell the X server to only send KeyRelease events when the user actually releases the key - when you don't want autorepeat events, then you discard any KeyPress without matching KeyRelease.

星星的轨迹 2024-08-26 05:43:03

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

For your reference, here's a working minimal example that deletes auto-repeated KeyPress events. Thank you, 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);
}
吐个泡泡 2024-08-26 05:43:03

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

You could set a timer when a key is pressed or released and ignore KeyPress and KeyRelease events that occur within the repetition interval.

谎言月老 2024-08-26 05:43:03

另一种方法。它对我有用。

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

another approach. it works for me.

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;
    ...
    }
魄砕の薆 2024-08-26 05:43:03

这是我想出的解决方案。

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 等,无需类型转换为 float 或 double 并返回整数)。

<代码>0x1 << (event.xkey.keycode % 8) 则相反。它将 0x1 (== 1) 的值乘以 2,得到 (event.xkey.keycode % 8)

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

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

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

Here's the solution I came up with.

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

So, everytime I get a KeyRelease event, I use XQueryKeymap which will copy to keys the bits of pressed keys (8 different keys by char).
For the ones who are not used to work with bitwise operatos and shift operator, here's a simple explanation:

keys[event.xkey.keycode>>3] search for index event.xkey.keycode / 8 using "right shift operator" (that allows for "integer division" by 2, 4, 8, 16 and so on, without type cast to float or double and back to integer).

0x1 << (event.xkey.keycode % 8) does the oposite. It multiplies the value of 0x1 (== 1) by 2 raised to (event.xkey.keycode % 8)

The & bitwise operator between keys[event.xkey.keycode>>3] and 0x1 << (event.xkey.keycode % 8) will compare if the only bit set in the right side operand is set to 1 inside this array index. If so, the key is being pressed.

Finally you just enclose it in (), with a ! right before and if the result becomes true, you're not pressing that key anymore.

One final Note: To use this method, you need to keep feeding the XServer with events. If not, XQueryKeymap will freze until it does (better use with threads).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文