Manejo de entrada de teclado y mouse (Win API)

11

Hay varias formas de atrapar el mouse o el teclado en Windows. Así que probé algunos de ellos, pero cada uno de ellos tiene algunas ventajas y desventajas. Quiero preguntarte: ¿Qué método utilizo?

He intentado estos:

  1. WM_KEYDOWN / WM_KEYUP: la principal desventaja es que no puedo distinguir entre las teclas izquierda y derecha como ALT, CONTROL o SHIFT.

  2. GetKeyboardState: esto resuelve el problema del primer método, pero hay uno nuevo. Cuando obtengo que la tecla ALT-derecha está presionada, también veo que la tecla Control-izquierda está presionada. Este comportamiento ocurre solo cuando se usa la distribución del teclado localizado (checo - CS).

  3. WM_INPUT (entrada sin formato): este método tampoco distingue las teclas izquierda y derecha (si puedo recordar) y, para el movimiento del mouse, a veces genera mensajes con valores cero delta de la posición del mouse.

De lujo
fuente

Respuestas:

10

La mejor y más simple manera de hacerlo es usar su primera idea y manejar los mensajes WM_KEYUP / WM_KEYDOWN, así como los mensajes WM_SYSKEYUP / WM_SYSKEYDOWN. Estos pueden manejar la detección de la diferencia entre las teclas shift / control / alt izquierda y derecha, solo necesita los códigos de teclas virtuales apropiados . Son VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL y VK_LMENU / VK_RMENU (para la tecla ALT).

Escribí una publicación sobre cómo hice esto, y estaba manejando tanto WM_KEYUP / WM_KEYDOWN como WM_SYSKEYUP / WM_SYSKEYDOWN en el mismo controlador. (Desafortunadamente, el blog ya no está disponible).

La única complicación que puedo ver es que debido a que está utilizando un teclado que no es de EE. UU., Necesitará agregar alguna lógica adicional para manejar la secuencia descrita en el artículo WM_SYSKEYUP en MSDN. Sin embargo, probablemente trataría de hacer algo más simple que el de masteryoda.

Daemin
fuente
Los mensajes WM_ son sin duda los más simples, pero "los mejores" solo si no le importan los eventos perdidos. Abandoné esa solución una vez que me di cuenta de que era un problema insoluble; Si su aplicación pierde el foco mientras una tecla está presionada, esa tecla estará "atascada" hasta que la presione nuevamente en foco.
dash-tom-bang
1
De hecho, la falta de información es un problema, pero la solución más fácil sería manejar adecuadamente los mensajes de enfoque / activación y solucionarlo. Prácticamente lo que quieres hacer es pausar el juego cuando pierdes el foco, ya que el usuario podría necesitar cambiar a otra aplicación más urgente, o simplemente presionar la tecla de Windows accidentalmente.
Daemin
3

¿Hay alguna razón por la que no puedes combinarlos? Por ejemplo, use WM_KEYDOWN para detectar la presión de una tecla Ctrl / Alt / Shift, luego, dentro de esa llamada, use GetKeyboardState () para distinguir de izquierda a derecha.

Ian Schreiber
fuente
Sí, puedo. Probablemente termine con esta solución (tal vez sea mejor usar GetAsyncKeyState). Pero estoy buscando una solución mejor si existe alguna. Y la tecla Right-ATL también genera dos mensajes WM_KEYDOWN (debido a la distribución del teclado). Entonces solo quedan WM_INPUT o DirectInput.
Deluxe
3

WM_INPUT es bueno. Yo creo que se puede distinguir las teclas izquierda / derecha utilizando la estructura RAWKEYBOARD . La parte difícil puede ser descubrir cómo lidiar con los identificadores de clave (es decir, códigos de exploración), pero no puedo decirlo, ya que nunca he tratado de usar esto para la entrada del teclado. WM_KEYDOWN es muy fácil :)

Sin embargo, he usado WM_INPUT para la entrada del mouse. Es de muy bajo nivel. No tiene aceleración aplicada, lo cual es muy bueno (IMO). WM_INPUT solía ser la única forma de aprovechar el movimiento del mouse de alta resolución, pero no estoy seguro de si ese es el caso. Vea este artículo de MSDN de 2006 .

DirectInput para mouse / teclado está explícitamente desaconsejado por Microsoft. Consulte el artículo de MSDN vinculado anteriormente. Si necesita un joystick, XInput es probablemente el camino a seguir.

EDITAR: Mi información sobre esto puede estar demasiado fechada.

Sorteo
fuente
3

En realidad, sepa L / R Ctrl / Alt cuando capture WM_KEYDOWN / WM_KEYUP, puede hacerlo. Fácil, no lo es, pero el código que uso, aquí puedes tener, hmm hmm.

Espero que esto todavía funcione, lo hago.

// 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;
}
usuario1209
fuente
1
+1 para el código, pero -1 para hablar como yoda. Es molesto y hace que tus respuestas sean difíciles de leer.
Anthony
De hecho, este no es un lugar para las cuentas de broma.
coderanger
2

Puede probar la API DirectInput , o más recientemente, la API XInput .

Skizz
fuente
1
¿No está XImput solo para el controlador XBox 360 conectado a la PC? He leído que DirectInput es un poco obsoleto, así que he tratado de evitar usarlo. Pero también probé el DirectInput y funcioné bien.
Deluxe
XInput es únicamente para gamepads únicamente. A partir de Windows 10, XInput ha quedado en desuso a favor de la interfaz 'IGamepad'. Además de eso, debe usar RAW_INPUT sobre otros mecanismos, ya que tienen limitaciones.
LaVolpe