Gestione dell'input da tastiera e mouse (API Win)


11

Esistono diversi modi per catturare mouse o tastiera in Windows. Quindi ho provato alcuni di essi, ma ognuno di essi presenta alcuni vantaggi e svantaggi. Voglio chiederti: quale metodo usi?

Ho provato questi:

  1. WM_KEYDOWN / WM_KEYUP - Il principale svantaggio è che non riesco a distinguere tra i tasti destro e sinistro come ALT, CONTROL o SHIFT.

  2. GetKeyboardState: risolve il problema del primo metodo, ma ne esiste uno nuovo. Quando viene premuto il tasto ALT-DESTRA, viene anche premuto il tasto CONTROL-SINISTRA. Questo comportamento si verifica solo quando si utilizza il layout di tastiera localizzato (ceco - CS).

  3. WM_INPUT (Raw Input) - Questo metodo inoltre non distingue i tasti sinistro e destro (se posso ricordare) e per il movimento del mouse a volte genera un messaggio con valori delta zero della posizione del mouse.

Risposte:


10

Il modo migliore e più semplice per farlo è utilizzare la tua prima idea e gestire i messaggi WM_KEYUP / WM_KEYDOWN nonché i messaggi WM_SYSKEYUP / WM_SYSKEYDOWN. Questi possono gestire il rilevamento della differenza tra i tasti shift / control / alt sinistro e destro, hai solo bisogno dei codici chiave virtuali appropriati . Sono VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL e VK_LMENU / VK_RMENU (per il tasto ALT).

Ho scritto un post su come ho fatto e gestivo sia WM_KEYUP / WM_KEYDOWN che WM_SYSKEYUP / WM_SYSKEYDOWN nello stesso gestore. (Sfortunatamente, il blog non è più disponibile.)

L'unica complicazione che posso vedere è che, poiché stai utilizzando una tastiera non statunitense, dovrai aggiungere una logica aggiuntiva per gestire la sequenza descritta nell'articolo WM_SYSKEYUP su MSDN. Tuttavia, probabilmente proverei a rendere qualcosa di più semplice di quello di masteryoda.


I messaggi WM_ sono sicuramente i più semplici, ma "i migliori" solo se non ti interessano gli eventi persi. Ho abbandonato quella soluzione una volta capito che era un problema irrisolvibile; se l'applicazione perde lo stato attivo mentre un tasto è inattivo, quel tasto sarà "bloccato" fino a quando non lo si preme nuovamente attivo.
dash-tom-bang,

1
In effetti l'input mancante è un problema, ma la soluzione più semplice sarebbe quella di gestire in modo appropriato i messaggi di focus / attivazione e aggirarlo. Praticamente quello che vuoi fare è mettere in pausa il gioco quando perdi la concentrazione, poiché l'utente potrebbe aver bisogno di passare a un'altra applicazione più urgente o premere semplicemente il tasto Windows accidentalmente.
Daemin,

3

C'è un motivo per cui non puoi combinarli? Ad esempio, utilizzare WM_KEYDOWN per rilevare la pressione di un tasto Ctrl / Alt / Maiusc, quindi all'interno di quella chiamata utilizzare GetKeyboardState () per distinguere da sinistra a destra?


Sì posso. Probabilmente finirò con questa soluzione (forse sarà meglio usare GetAsyncKeyState). Ma sto cercando una soluzione migliore se ne esiste. E il tasto ATL destro genera anche due messaggi WM_KEYDOWN (a causa del layout della tastiera). Quindi rimangono solo WM_INPUT o DirectInput.
Deluxe,

3

WM_INPUT è carino. Io penso che è possibile distinguere destra / sinistra utilizzando lo struct RAWKEYBOARD . La parte difficile potrebbe essere capire come gestire gli identificatori chiave (es. Scancodes), ma non posso dire dal momento che non ho mai provato a usarlo per l'input da tastiera. WM_KEYDOWN è così facile :)

Tuttavia, ho usato WM_INPUT per l'input del mouse. È di livello molto basso. Non ha alcuna accelerazione applicata, il che è molto bello (IMO). WM_INPUT era l'unico modo per sfruttare il movimento del mouse ad alto dpi, ma non sono sicuro che sia ancora così. Vedi questo articolo MSDN del 2006 .

DirectInput per mouse / tastiera è esplicitamente scoraggiato da Microsoft. Vedi l'articolo MSDN precedentemente collegato. Se hai bisogno di un joystick, XInput è probabilmente la strada da percorrere.

EDIT: le mie informazioni al riguardo potrebbero essere troppo datate.


3

In realtà, distinguere L / R Ctrl / Alt quando si cattura WM_KEYDOWN / WM_KEYUP, è possibile. Facile, non lo è, ma il codice che uso, qui puoi avere, hmm hmm.

Spero che funzioni ancora, lo faccio.

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

1
+1 per il codice, ma -1 per parlare come yoda. È fastidioso e rende le tue risposte difficili da leggere.
Anthony,

In effetti, questo non è un posto per i conti di battute.
coderanger,

2

È possibile provare l' API DirectInput o, più recentemente, l' API XInput .


1
XImput non è solo per il controller XBox 360 collegato al PC? Ho letto che DirectInput è un po 'obsoleto, quindi ho cercato di evitare di usarlo. Ma ho provato anche DirectInput e mi sono svegliato bene.
Deluxe,

XInput è solo per gamepad. A partire da Windows 10, XInput è diventato obsoleto a favore dell'interfaccia 'IGamepad'. Inoltre, dovresti usare RAW_INPUT su altri meccanismi, poiché ci sono delle limitazioni.
LaVolpe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.