Gestion de la saisie au clavier et à la souris (API Win)

11

Il existe plusieurs façons d'attraper la souris ou le clavier sous Windows. J'ai donc essayé certains d'entre eux, mais chacun d'eux a des avantages et des inconvénients. Je veux vous demander: quelle méthode utilisez-vous?

J'ai essayé ces derniers:

  1. WM_KEYDOWN / WM_KEYUP - Le principal inconvénient est que je ne peux pas faire la distinction entre les touches gauche et droite comme ALT, CONTROL ou SHIFT.

  2. GetKeyboardState - Cela résout le problème de la première méthode, mais il y en a une nouvelle. Lorsque j'obtiens que la touche ALT droite est enfoncée, j'obtiens également que la touche Contrôle gauche est enfoncée. Ce problème se produit uniquement lors de l'utilisation d'une disposition de clavier localisée (tchèque - CS).

  3. WM_INPUT (Raw Input) - Cette méthode ne distingue pas non plus les touches gauches et droitières (si je me souviens bien) et pour le mouvement de la souris génère parfois un message avec des valeurs delta nulles de la position de la souris.

De luxe
la source

Réponses:

10

La manière la meilleure et la plus simple de le faire est d'utiliser votre première idée et de gérer les messages WM_KEYUP / WM_KEYDOWN ainsi que les messages WM_SYSKEYUP / WM_SYSKEYDOWN. Ceux-ci peuvent gérer la détection de la différence entre les touches shift / control / alt gauche et droite, vous avez juste besoin des codes de touches virtuelles appropriés . Ce sont VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL et VK_LMENU / VK_RMENU (pour la clé ALT).

J'ai écrit un article sur la façon dont j'ai fait cela, et je manipulais à la fois WM_KEYUP / WM_KEYDOWN et WM_SYSKEYUP / WM_SYSKEYDOWN dans le même gestionnaire. (Malheureusement, le blog n'est plus disponible.)

La seule complication que je peux voir est que parce que vous utilisez un clavier non américain, vous devrez ajouter une logique supplémentaire pour gérer la séquence décrite dans l' article WM_SYSKEYUP sur MSDN. Cependant, j'essaierais probablement de faire quelque chose de plus simple que celui de masteryoda.

Daemin
la source
Les messages WM_ sont certainement les plus simples, mais "les meilleurs" seulement si vous ne vous souciez pas des événements manqués. J'ai abandonné cette solution une fois que j'ai réalisé que c'était un problème insoluble; si votre application perd le focus alors qu'une touche est enfoncée, cette touche sera "bloquée" jusqu'à ce que vous appuyiez à nouveau sur elle.
dash-tom-bang
1
En effet, une entrée manquante est un problème, mais la solution la plus simple serait de gérer correctement les messages de focus / activation et de les contourner. En pratique, ce que vous voulez faire est de mettre le jeu en pause lorsque vous perdez le focus, car l'utilisateur peut avoir besoin de basculer vers une autre application plus urgente, ou il appuie simplement sur la touche Windows accidentellement.
Daemin
3

Y a-t-il une raison pour laquelle vous ne pouvez pas les combiner? Par exemple, utilisez WM_KEYDOWN pour détecter la pression d'une touche Ctrl / Alt / Maj, puis dans cet appel utilisez GetKeyboardState () pour distinguer gauche de droite?

Ian Schreiber
la source
Oui je peux. Je terminerai probablement avec cette solution (peut-être sera-t-il préférable d'utiliser GetAsyncKeyState). Mais je recherche une meilleure solution s'il en existe. Et la touche ATL droite génère également deux messages WM_KEYDOWN (en raison de la disposition du clavier). Il ne reste donc que WM_INPUT ou DirectInput.
Deluxe
3

WM_INPUT est sympa. Je pense que vous pouvez distinguer les touches gauche / droite en utilisant la structure RAWKEYBOARD . La partie difficile est peut-être de savoir comment gérer les identifiants clés (c'est-à-dire les scancodes), mais je ne peux pas le dire car je n'ai jamais essayé de l'utiliser pour la saisie au clavier. WM_KEYDOWN est si facile :)

J'ai cependant utilisé WM_INPUT pour la saisie de la souris. C'est très bas niveau. Il n'y a aucune accélération appliquée, ce qui est très agréable (IMO). WM_INPUT était le seul moyen de profiter du mouvement de la souris à haute résolution, mais je ne sais pas si c'est toujours le cas. Voir cet article MSDN de 2006 .

DirectInput pour souris / clavier est explicitement déconseillé par Microsoft. Consultez l'article MSDN précédemment lié. Si vous avez besoin d'un joystick, XInput est probablement le chemin à parcourir.

EDIT: Mes informations à ce sujet peuvent être trop datées.

BRaffle
la source
3

En fait, différenciez L / R Ctrl / Alt lorsque vous attrapez WM_KEYDOWN / WM_KEYUP, vous le pouvez. Facile, ce n'est pas le cas, mais le code que j'utilise, ici vous pouvez l'avoir, hmm hmm.

J'espère que cela fonctionne toujours, je le fais.

// Receives a WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN or WM_SYSKEYUP message and 
// returns a virtual key of the key that triggered the message.
// 
// If the key has a common virtual key code, that code is returned. 
// For Alt's and Ctrl's, the values from the KeyCodes enumeration are used.
int translateKeyMessage (MSG& Msg);

// Virtual key codes for keys that aren't defined in the windows headers.
enum KeyCodes
{
    VK_LEFTCTRL = 162,
    VK_RIGHTCTRL = 163,
    VK_LEFTALT = 164,
    VK_RIGHTALT = 165
};

// ======================================================================================

int translateKeyMessage (MSG& Msg)
{
    // Determine the virtual key code.
    int VirtualKeyCode = Msg.wParam;

    // Determine whether the key is an extended key, e.g. a right 
    // hand Alt or Ctrl.
    bool Extended = (Msg.lParam & (1 << 24)) != 0;

    // If this is a system message, is the Alt bit of the message on?
    bool AltBit = false;    
    if (Msg.message == WM_SYSKEYDOWN || Msg.message == WM_SYSKEYUP)
        AltBit = (Msg.lParam & (1 << 29)) != 0;

    if ((Msg.message == WM_SYSKEYUP || Msg.message == WM_KEYUP) && !Extended && !AltBit && VirtualKeyCode == 18)
    {
        // Left Alt
        return KeyCodes::VK_LEFTALT;
    }

    // Left Ctrl
    if (!Extended && !AltBit && VirtualKeyCode == 17)
    {
        // Peek for the next message.
        MSG nextMsg;
        BOOL nextMessageFound = PeekMessage(&nextMsg, NULL, 0, 0, PM_NOREMOVE);

        // If the next message is for the right Alt:
        if (nextMessageFound && nextMsg.message == Msg.message && nextMsg.wParam == 18)
        {
            //
            bool nextExtended = (nextMsg.lParam & (1 << 24)) != 0;

            //
            bool nextAltBit = false;    
            if (nextMsg.message == WM_SYSKEYDOWN || nextMsg.message == WM_SYSKEYUP)
                nextAltBit = (nextMsg.lParam & (1 << 29)) != 0;

            // If it is really for the right Alt
            if (nextExtended && !nextAltBit)
            {
                // Remove the next message
                PeekMessage(&nextMsg, NULL, 0, 0, PM_REMOVE);

                // Right Alt
                return KeyCodes::VK_RIGHTALT;
            }
        }

        // Left Ctrl
        return KeyCodes::VK_LEFTCTRL;
    }

    if (Msg.message == WM_SYSKEYUP && !Extended && AltBit && VirtualKeyCode == 17)
    {
        // Peek for the next message.
        MSG nextMsg;
        BOOL nextMessageFound = PeekMessage(&nextMsg, NULL, 0, 0, PM_NOREMOVE);

        // If the next message is for the right Alt:
        if (nextMessageFound && nextMsg.message == WM_KEYUP && nextMsg.wParam == 18)
        {
            //
            bool nextExtended = (nextMsg.lParam & (1 << 24)) != 0;

            //
            bool nextAltBit = false;    
            if (nextMsg.message == WM_SYSKEYDOWN || nextMsg.message == WM_SYSKEYUP)
                nextAltBit = (nextMsg.lParam & (1 << 29)) != 0;

            // If it is really for the right Alt
            if (nextExtended && !nextAltBit)
            {
                // Remove the next message
                PeekMessage(&nextMsg, NULL, 0, 0, PM_REMOVE);

                // Right Alt
                return KeyCodes::VK_RIGHTALT;
            }
        }
    }

    // Right Ctrl
    if (Extended && !AltBit && VirtualKeyCode == 17)
        return KeyCodes::VK_RIGHTCTRL;

    // Left Alt
    if (!Extended && AltBit && VirtualKeyCode == 18)
        return KeyCodes::VK_LEFTALT;

    // Default
    return VirtualKeyCode;
}
user1209
la source
1
+1 pour le code, mais -1 pour parler comme yoda. C'est ennuyeux et rend vos réponses difficiles à lire.
Anthony
En effet, ce n'est pas un endroit pour les récits de blagues.
coderanger
2

Vous pouvez essayer l' API DirectInput ou, plus récemment, l' API XInput .

Skizz
la source
1
XImput n'est-il pas uniquement pour le contrôleur XBox 360 connecté au PC? J'ai lu que DirectInput est peu obsolète, j'ai donc essayé d'éviter de l'utiliser. Mais j'ai aussi essayé le DirectInput et je me suis bien réveillé.
Deluxe
XInput est uniquement réservé aux manettes de jeu. Depuis Windows 10, XInput est obsolète au profit de l'interface «IGamepad». En plus de cela, vous devez utiliser RAW_INPUT sur d'autres mécanismes, car ils sont limités.
LaVolpe