Игнорировать автоматическое повторение в приложениях X11
Если вы нажимаете и удерживаете клавишу в X11, в то время как AutoRepeat включен, вы непрерывно получаете события KeyPress и KeyRelease. Я знаю, что AutoRepeat можно отключить с помощью функции XAutoRepeatOff(), но это изменяет настройку для всего X-сервера. Есть ли способ отключить AutoRepeat для одного приложения или игнорировать повторяющиеся нажатия клавиш?
То, что я ищу, - это одно событие нажатие клавиши при нажатии клавиши и одно событие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);
}
6 ответов:
Когда вы получаете отпущение клавиши и следующее событие-это нажатие клавиши той же комбинации клавиш, то оно автоматически повторяется, и клавиша не была остро отпущена. Вы можете использовать такой код, чтобы заглянуть в следующее событие
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 */ } }
Вы можете использовать функциюXkbSetDetectableAutorepeat , чтобы сказать серверу X отправлять события KeyRelease только тогда, когда пользователь фактически отпускает ключ - если вы не хотите событий autorepeat, то вы отбрасываете любое нажатие клавиши без согласования KeyRelease.
Для справки, вот рабочий минимальный пример, который удаляет автоматически повторяющиеся событиянажатия клавиш . Спасибо, Кралик!
#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); }
Можно установить таймер, когда клавиша нажата или отпущена, и игнорировать события нажатия клавиши и KeyRelease, которые происходят в интервале повторения.
Другой подход. это работает на меня.
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; ... }
Вот решение, которое я придумал.
Таким образом, каждый раз, когда я получаю событие KeyRelease, я использую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: ... } }
XQueryKeymap
, которое будет копировать вkeys
биты нажатых клавиш (8 различных клавиш поchar
). Для тех, кто не привык работать с побитовыми операторами и операторами сдвига, вот простое объяснение:
keys[event.xkey.keycode>>3]
поиск индексаevent.xkey.keycode / 8
с помощью " оператора сдвига вправо "(который позволяет "целочисленное деление" на 2, 4, 8, 16 и так далее, без приведения типа к float или double и обратно к целое число).
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 будет фрезеровать до тех пор, пока это не произойдет (лучше использовать с потоками).