24

AutoRepeatが有効なときに X11 でキーを押したままにすると、 KeyPressイベントとKeyReleaseイベントを継続的に受け取ります。関数XAutoRepeatOff()を使用してAutoRepeatを無効にできることは知っていますが、これにより X サーバー全体の設定が変更されます。単一のアプリケーションのAutoRepeatを無効にする方法、または繰り返されるキーストロークを無視する方法はありますか?

私が探しているのは、X サーバーのAutoRepeat設定に干渉することなく、キーが押されたときの単一のKeyPressイベントと、キーが離されたときの単一のKeyReleaseイベントです。

これは、あなたが始めるための最小限の例です (主に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);
}
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関数を使用して、ユーザーが実際にキーを離したときにのみKeyReleaseイベントを送信するようにXサーバーに指示できます。自動リピートイベントが必要ない場合は、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

これが私が思いついた解決策です。

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コピーする whichを使用しkeysます (8 つの異なるキー by 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)

との間の&ビットごとの演算子は、右側のオペランドに設定された唯一のビットがこの配列インデックス内で 1 に設定されているかどうかを比較します。そうであれば、キーは押されています。keys[event.xkey.keycode>>3]0x1 << (event.xkey.keycode % 8)

()最後に、直前に, で囲むだけで!、結果が true になった場合、そのキーはもう押されていません。

最後に 1 つ注意: この方法を使用するには、XServer にイベントを送り続ける必要があります。そうでない場合、XQueryKeymap はフリーズするまでフリーズします (スレッドでの使用をお勧めします)。

于 2015-02-13T19:37:32.580 に答える
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 に答える