Umgang mit Tastatur- und Mauseingaben (Win API)

11

Unter Windows gibt es verschiedene Möglichkeiten, Maus oder Tastatur abzufangen. Also habe ich einige davon ausprobiert, aber jeder von ihnen hat einige Vor- und Nachteile. Ich möchte Sie fragen: Welche Methode verwenden Sie?

Ich habe diese ausprobiert:

  1. WM_KEYDOWN / WM_KEYUP - Hauptnachteil ist, dass ich nicht zwischen linkshändigen und rechtshändigen Tasten wie ALT, CONTROL oder SHIFT unterscheiden kann.

  2. GetKeyboardState - Dies löst das Problem der ersten Methode, aber es gibt eine neue. Wenn ich bekomme, dass die Right-ALT-Taste gedrückt wird, bekomme ich auch, dass die Left-Control-Taste gedrückt ist. Dieses Verhalten tritt nur bei Verwendung eines lokalisierten Tastaturlayouts (Tschechisch - CS) auf.

  3. WM_INPUT (Raw Input) - Diese Methode unterscheidet auch nicht zwischen Links- und Rechtshänder (wenn ich mich erinnern kann) und generiert bei Mausbewegungen manchmal Nachrichten mit Null-Delta-Werten der Mausposition.

Deluxe
quelle

Antworten:

10

Der beste und einfachste Weg, dies zu tun, besteht darin, Ihre erste Idee zu verwenden und die Nachrichten WM_KEYUP / WM_KEYDOWN sowie die Nachrichten WM_SYSKEYUP / WM_SYSKEYDOWN zu verarbeiten. Diese können den Unterschied zwischen linker und rechter Umschalt- / Steuer- / Alt-Taste erkennen. Sie benötigen lediglich die entsprechenden virtuellen Tastencodes . Dies sind VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL und VK_LMENU / VK_RMENU (für die ALT-Taste).

Ich habe einen Beitrag darüber geschrieben, wie ich das gemacht habe, und ich habe sowohl WM_KEYUP / WM_KEYDOWN als auch WM_SYSKEYUP / WM_SYSKEYDOWN im selben Handler behandelt. (Leider ist der Blog nicht mehr verfügbar.)

Die einzige Komplikation, die ich sehen kann, ist, dass Sie, da Sie eine Tastatur außerhalb der USA verwenden, zusätzliche Logik hinzufügen müssen, um die im Artikel WM_SYSKEYUP auf MSDN beschriebene Sequenz zu verarbeiten . Allerdings würde ich wahrscheinlich versuchen, etwas einfacher zu machen als das von Masteryoda.

Daemin
quelle
Die WM_-Nachrichten sind sicherlich die einfachsten, aber nur dann "am besten", wenn Sie sich nicht für verpasste Ereignisse interessieren. Ich gab diese Lösung auf, als mir klar wurde, dass es sich um ein unlösbares Problem handelte. Wenn Ihre Anwendung den Fokus verliert, während eine Taste gedrückt ist, bleibt diese Taste "hängen", bis Sie sie erneut im Fokus drücken.
Dash-Tom-Bang
1
In der Tat ist das Fehlen von Eingaben ein Problem, aber die einfachste Lösung wäre, die Fokus- / Aktivierungsnachrichten angemessen zu behandeln und sie zu umgehen. Praktisch möchten Sie das Spiel anhalten, wenn Sie den Fokus verlieren, da der Benutzer möglicherweise nicht zu einer anderen dringlicheren Anwendung wechseln muss oder einfach versehentlich die Windows-Taste drückt.
Daemin
3

Gibt es einen Grund, warum Sie sie nicht kombinieren können? Verwenden Sie beispielsweise WM_KEYDOWN, um das Drücken einer Strg- / Alt- / Umschalttaste zu erkennen, und verwenden Sie dann innerhalb dieses Aufrufs GetKeyboardState (), um links von rechts zu unterscheiden.

Ian Schreiber
quelle
Ja, ich kann. Ich werde wahrscheinlich mit dieser Lösung enden (vielleicht ist es besser, GetAsyncKeyState zu verwenden). Aber ich suche nach einer besseren Lösung, wenn es eine gibt. Die rechte ATL-Taste generiert auch zwei WM_KEYDOWN-Nachrichten (aufgrund des Tastaturlayouts). Es bleibt also nur WM_INPUT oder DirectInput übrig.
Deluxe
3

WM_INPUT ist nett. Ich denke, Sie können linke / rechte Tasten mit der RAWKEYBOARD-Struktur unterscheiden . Der schwierige Teil mag darin bestehen, herauszufinden, wie man mit den Schlüsselkennungen (dh Scancodes) umgeht, aber ich kann es nicht sagen, da ich nie versucht habe, dies für Tastatureingaben zu verwenden. WM_KEYDOWN ist so einfach :)

Ich habe WM_INPUT jedoch für die Mauseingabe verwendet. Es ist sehr niedrig. Es wird keine Beschleunigung angewendet, was sehr schön ist (IMO). Früher war WM_INPUT die einzige Möglichkeit, die Mausbewegung mit hoher Auflösung zu nutzen, aber ich bin mir nicht sicher, ob dies immer noch der Fall ist. Siehe diesen MSDN-Artikel aus dem Jahr 2006 .

Von DirectInput für Maus / Tastatur wird von Microsoft ausdrücklich abgeraten. Siehe den zuvor verlinkten MSDN-Artikel. Wenn Sie einen Joystick benötigen, ist XInput wahrscheinlich der richtige Weg.

EDIT: Meine Infos dazu sind möglicherweise zu alt.

Taffel
quelle
3

Sagen Sie L / R Ctrl / Alt, wenn Sie WM_KEYDOWN / WM_KEYUP abfangen. Einfach ist es nicht, aber der Code, den ich benutze, kann man hier haben, hmm hmm.

Hoffe das funktioniert noch, das tue ich.

// 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
quelle
1
+1 für den Code, aber -1 für das Sprechen wie Yoda. Es ist ärgerlich und macht Ihre Antworten schwer zu lesen.
Anthony
In der Tat ist dies kein Ort für Scherzberichte.
Coderanger
2

Sie können die DirectInput-API oder in jüngerer Zeit die XInput-API ausprobieren .

Skizz
quelle
1
Ist XImput nicht nur für den an den PC angeschlossenen XBox 360-Controller vorgesehen? Ich habe gelesen, dass DirectInput etwas veraltet ist, deshalb habe ich versucht, es nicht zu verwenden. Aber ich habe auch den DirectInput ausprobiert und bin gut aufgewacht.
Deluxe
XInput ist ausschließlich für Gamepads vorgesehen. Ab Windows 10 ist XInput zugunsten der IGamepad-Oberfläche veraltet. Darüber hinaus sollten Sie RAW_INPUT gegenüber anderen Mechanismen verwenden, da diese Einschränkungen unterliegen.
LaVolpe