Подтвердить что ты не робот

Игнорировать автоматическое повторение в приложениях X11

Если вы нажмете и удерживаете клавишу в X11, пока включена функция AutoRepeat, вы постоянно получаете события KeyPress и KeyRelease. Я знаю, что AutoRepeat можно отключить с помощью функции XAutoRepeatOff(), но это изменит настройку для всего 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);
}
4b9b3361

Ответ 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 */
  }
}

Ответ 2

Вы можете использовать функцию XkbSetDetectableAutorepeat, чтобы сообщить серверу X только отправлять события KeyRelease, когда пользователь действительно выпускает ключ - когда вы не хотите событий автоповтора, тогда вы отбрасываете любой KeyPress без соответствия KeyRelease.

Ответ 3

Для справки, здесь приведен рабочий минимальный пример, который удаляет автоматически повторяющиеся события 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);
}

Ответ 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, который скопирует на keys бит нажатых клавиш (8 разных клавиш char). Для тех, кто не используется для работы с побитовыми операторами и оператором сдвига, здесь простое объяснение:

keys[event.xkey.keycode>>3] искать индекс event.xkey.keycode / 8 с помощью "оператора смены вправо" (который позволяет "целочисленное деление" на 2, 4, 8, 16 и т.д., без ввода типа для float или double и обратно в integer).

0x1 << (event.xkey.keycode % 8) делает oposite. Он умножает значение 0x1 (== 1) на 2, поднятое до (event.xkey.keycode % 8)

Побитовый оператор & между keys[event.xkey.keycode>>3] и 0x1 << (event.xkey.keycode % 8) будет сравнивать, если бит только, установленный в правом операнде, установлен в 1 внутри этого индекса массива. Если это так, клавиша нажата.

Наконец, вы просто вложите его в (), с ! справа и, если результат станет истинным, вы больше не нажимаете эту клавишу.

Одна заключительная Примечание. Чтобы использовать этот метод, вам нужно продолжать кормить XServer событиями. Если нет, XQueryKeymap будет freze до тех пор, пока это не будет (лучше использовать с потоками).