keyboard - Ignorar repetición automática en aplicaciones X11
autorepeat (6)
Aquí está la solución que se me ocurrió.
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:
...
}
}
Por lo tanto, cada vez que obtengo un evento KeyRelease, uso XQueryKeymap
que copiará en las keys
los bits de las teclas presionadas (8 teclas diferentes por char
). Para los que no están acostumbrados a trabajar con operatos de bit a bit y operador de cambio, aquí hay una explicación simple:
keys[event.xkey.keycode>>3]
busca el index event.xkey.keycode / 8
usando el "operador de cambio a la derecha" (que permite la "división de enteros" entre 2, 4, 8, 16 y así sucesivamente, sin conversión de tipo flotar o doblar y volver al entero).
0x1 << (event.xkey.keycode % 8)
hace lo opuesto. Multiplica el valor de 0x1
( == 1
) por 2 elevado a (event.xkey.keycode % 8)
El operador &
bitwise entre las keys[event.xkey.keycode>>3]
y 0x1 << (event.xkey.keycode % 8)
se comparará si el único bit establecido en el operando del lado derecho se establece en 1 dentro de este índice de matriz. Si es así, se está presionando la tecla.
Finalmente simplemente lo encierras en ()
, con un !
justo antes y si el resultado se vuelve verdadero, ya no estarás presionando esa tecla.
Una nota final: para usar este método, debe seguir alimentando el XServer con eventos. Si no, XQueryKeymap se congelará hasta que lo haga (mejor usarlo con hilos).
Si presiona y mantiene presionada una tecla en X11 mientras AutoRepeat está habilitado, continuamente recibe los eventos KeyPress y KeyRelease . Sé que AutoRepeat se puede desactivar usando la función XAutoRepeatOff () , pero esto cambia la configuración de todo el servidor X. ¿Hay alguna manera de desactivar AutoRepeat para una sola aplicación o ignorar las pulsaciones repetidas?
Lo que estoy buscando es un evento KeyPress único cuando se presiona una tecla y un evento KeyRelease único cuando se suelta una tecla, sin interferir con la configuración de repetición automática del servidor X.
Aquí hay un ejemplo mínimo para comenzar (principalmente del Tutorial de Xlib para principiantes ):
#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);
}
Cuando recibe una liberación de tecla y el siguiente evento es una pulsación de tecla de la misma combinación de teclas, entonces se repite automáticamente y la tecla no se lanzó de forma aguda. Puedes usar un código como este para ver el próximo evento
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 */
}
}
Para su referencia, aquí hay un ejemplo mínimo de trabajo que elimina los eventos de KeyPress repetidos automáticamente. Gracias, 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);
}
Puede configurar un temporizador cuando se presiona o suelta una tecla e ignorar los eventos KeyPress y KeyRelease que ocurren dentro del intervalo de repetición.
Puede usar la función XkbSetDetectableAutorepeat para decirle al servidor X que solo envíe eventos KeyRelease cuando el usuario realmente libera la tecla; cuando no quiere eventos autorepeat, descarta cualquier KeyPress sin que coincida KeyRelease.
otro enfoque. esto funciona para mi.
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;
...
}