Question

Si vous appuyez et maintenez une touche dans X11 tandis que AutoRepeat est activée, vous recevez en continu KeyPress et KeyRelease événements. Je sais que AutoRepeat peuvent être désactivés en utilisant la fonction XAutoRepeatOff () , mais cela change le paramètre pour l'ensemble du serveur X. Est-il un moyen de désactiver soit AutoRepeat pour une seule application ou d'ignorer les frappes répétées?

Ce que je suis à la recherche est un KeyPress événement lorsqu'une touche est enfoncée et un KeyRelease événement lorsqu'une touche est relâchée, sans interférer avec le serveur X de AutoRepeat réglage.

Voici un exemple minimal pour vous aller (la plupart du temps de la ):

#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);
}
Était-ce utile?

La solution

Lorsque vous recevez une version clé et le prochain événement est une pression de touche de la même combinaison de touches, il est la répétition automatique et la clé n'a pas été publié acutally. Vous pouvez utiliser le code comme ceci pour jeter un regard prochain événement

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 */
  }
}

Autres conseils

Vous pouvez utiliser la fonction XkbSetDetectableAutorepeat pour indiquer au serveur X seulement envoyer des événements KeyRelease lorsque l'utilisateur relâche en fait la clé -. quand vous ne voulez pas les événements répétition automatique, vous défaussez sans pression sur une touche correspondant à KeyRelease

Pour votre référence, voici un exemple minimal de travail qui supprime automatiquement répétée KeyPress événements. Je vous remercie, 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);
}

Vous pouvez régler une minuterie lorsqu'une touche est enfoncée ou relâchée et ignorer les événements et KeyRelease qui se produisent dans l'intervalle de répétition.

Une autre approche. ça marche pour moi.

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;
    ...
    }

Voici la solution que je suis venu avec.

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:
            ...
    }
}

Alors, chaque fois que je reçois un événement KeyRelease, j'utilise XQueryKeymap qui copie à keys les bits de touches enfoncées (8 touches différentes par char). Pour ceux qui ne sont pas utilisés pour travailler avec operatos et de manipulation de bits opérateur de décalage, voici une explication simple:

recherche keys[event.xkey.keycode>>3] pour l'index event.xkey.keycode / 8 en utilisant « l'opérateur de décalage vers la droite » (qui permet de « division entière » de 2, 4, 8, 16 et ainsi de suite, sans conversion de type à flotteur ou double et en arrière à nombre entier).

0x1 << (event.xkey.keycode % 8) fait le oposite. Il multiplie la valeur de 0x1 (== 1) par 2 élevé à (event.xkey.keycode % 8)

Le & opérateur au niveau du bit entre keys[event.xkey.keycode>>3] et 0x1 << (event.xkey.keycode % 8) permettra de comparer si la uniquement bit fixé dans l'opérande de droite est réglé à 1 à l'intérieur de cet index de tableau. Dans ce cas, la touche est enfoncée.

Enfin, vous encadrez juste à (), avec un ! juste avant et si le résultat est vrai, vous n'êtes pas appuyer sur cette touche plus.

Une finale Remarque : Pour utiliser cette méthode, vous devez continuer à alimenter la XServer des événements. Dans le cas contraire, XQueryKeymap sera FREZE jusqu'à ce qu'il fait (mieux utiliser avec des fils).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top