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

17

Если вы нажмете и удерживаете клавишу в X11, а Автозапуск включен, вы постоянно получаете события KeyPress и KeyRelease . Я знаю, что AutoRepeat можно отключить с помощью функции XAutoRepeatOff () , но это изменит настройку для всего X-сервера. Есть ли способ отключить AutoRepeat для одного приложения или игнорировать повторяющиеся нажатия клавиш?

То, что я ищу, - это одно событие KeyPress при нажатии клавиши и одно событие KeyRelease при выпуске ключа без вмешательства в работу сервера X AutoRepeat .

Ниже приведен минимальный пример для вас (в основном из учебника для начинающих 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);
}
    
задан mzuther 20.01.2010 в 11:34
источник

6 ответов

17

Когда вы получаете разблокировку ключа, а следующее событие - нажатие клавиши одной и той же комбинации клавиш, тогда это автоматический повтор, и клавиша не была выпущена. Вы можете использовать такой код, чтобы заглянуть в следующее событие

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 */
  }
}
    
ответ дан kralyk 10.07.2010 в 23:02
источник
  • Спасибо, это именно то, что я искал! –  mzuther 11.07.2010 в 11:24
6

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

    
ответ дан alanc 20.01.2010 в 16:51
источник
5

В качестве справочной информации приведен рабочий минимальный пример, который удаляет автоматически повторяющиеся события 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);
}
    
ответ дан mzuther 11.07.2010 в 11:37
источник
1

Вы можете установить таймер при нажатии или отпускании клавиши и игнорировать события KeyPress и KeyRelease, которые происходят в интервале повторения.

    
ответ дан Maurits Rijk 20.01.2010 в 11:44
источник
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;
    ...
    }
    
ответ дан exebook 12.02.2014 в 02:15
источник
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 и т. д., без ввода типа для float или double и back to 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 до тех пор, пока это не будет (лучше использовать с потоками).

    
ответ дан Fábio Moreira 13.02.2015 в 20:37
источник