Obsługa wprowadzania z klawiatury i myszy (Win API)

11

Istnieje wiele sposobów na złapanie myszy lub klawiatury pod Windows. Wypróbowałem więc niektóre z nich, ale każdy ma pewne zalety i wady. Chcę Cię zapytać: z której metody korzystasz?

Próbowałem tych:

  1. WM_KEYDOWN / WM_KEYUP - Główną wadą jest to, że nie mogę rozróżnić klawiszy lewych i praworęcznych, takich jak ALT, CONTROL lub SHIFT.

  2. GetKeyboardState - rozwiązuje to problem pierwszej metody, ale jest nowa. Kiedy dostaję, że wciśnięty jest prawy ALT, również lewy klawisz Control jest wciśnięty. To zachowanie występuje tylko podczas korzystania ze zlokalizowanego układu klawiatury (czeski - CS).

  3. WM_INPUT (Raw Input) - Ta metoda nie rozróżnia także klawiszy lewych i praworęcznych (o ile pamiętam) i dla ruchu myszy czasami generuje komunikat o zerowej wartości delta pozycji myszy.

Deluxe
źródło

Odpowiedzi:

10

Najlepszym i najprostszym sposobem na to jest skorzystanie z pierwszego pomysłu i obsługa wiadomości WM_KEYUP / WM_KEYDOWN, a także wiadomości WM_SYSKEYUP / WM_SYSKEYDOWN. Mogą one obsługiwać wykrywanie różnicy między lewym i prawym klawiszem Shift / Control / Alt, potrzebujesz tylko odpowiednich kodów kluczy wirtualnych . Są to VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL i VK_LMENU / VK_RMENU (dla klawisza ALT).

Napisałem post o tym, jak to zrobiłem, i obsługiwałem zarówno WM_KEYUP / WM_KEYDOWN, jak i WM_SYSKEYUP / WM_SYSKEYDOWN w tym samym module obsługi. (Niestety blog nie jest już dostępny).

Jedyną komplikacją, którą widzę, jest to, że ponieważ używasz klawiatury spoza USA, musisz dodać dodatkową logikę, aby obsłużyć sekwencję opisaną w artykule WM_SYSKEYUP na MSDN. Jednak prawdopodobnie spróbowałbym stworzyć coś prostszego niż masteryoda.

Daemin
źródło
Wiadomości WM_ są z pewnością najprostsze, ale „najlepsze” tylko wtedy, gdy nie obchodzą Cię pominięte zdarzenia. Porzuciłem to rozwiązanie, gdy zdałem sobie sprawę, że jest to trudny do rozwiązania problem; jeśli aplikacja traci fokus, gdy klawisz jest wciśnięty, klawisz ten zostanie „zablokowany”, dopóki nie naciśniesz go ponownie.
dash-tom-bang
1
Rzeczywiście brak danych wejściowych stanowi problem, ale najłatwiejszym rozwiązaniem byłoby odpowiednie zarządzanie komunikatami aktywacji / aktywacji i obejście go. Praktycznie to, co chcesz zrobić, to zatrzymać grę, gdy stracisz koncentrację, ponieważ użytkownik może potrzebować t przejść do innej, bardziej pilnej aplikacji, lub po prostu przypadkowo nacisnął klawisz Windows.
Daemin
3

Czy istnieje powód, dla którego nie można ich połączyć? Na przykład użyj WM_KEYDOWN do wykrycia naciśnięcia klawisza Ctrl / Alt / Shift, a następnie w ramach tego wywołania użyj GetKeyboardState (), aby odróżnić lewy od prawego?

Ian Schreiber
źródło
Tak, mogę. Prawdopodobnie skończę z tym rozwiązaniem (być może lepiej będzie użyć GetAsyncKeyState). Ale szukam lepszego rozwiązania, jeśli takie istnieje. A prawy klawisz ATL generuje również dwa komunikaty WM_KEYDOWN (ze względu na układ klawiatury). Pozostają więc tylko WM_INPUT lub DirectInput.
Deluxe
3

WM_INPUT jest fajny. Myślę , że możesz odróżnić lewy / prawy klawisz za pomocą struktury RAWKEYBOARD . Trudną częścią może być zastanawianie się, jak radzić sobie z kluczowymi identyfikatorami (tj. Skancodami), ale nie mogę powiedzieć, ponieważ nigdy nie próbowałem używać tego do wprowadzania danych z klawiatury. WM_KEYDOWN jest takie proste :)

Jednak użyłem WM_INPUT do wprowadzania danych za pomocą myszy. To bardzo niski poziom. Nie ma zastosowanego przyspieszenia, co jest bardzo fajne (IMO). WM_INPUT był kiedyś jedynym sposobem na skorzystanie z ruchu myszy o wysokiej rozdzielczości, ale nie jestem pewien, czy nadal tak jest. Zobacz ten artykuł MSDN z 2006 roku .

DirectInput dla myszy / klawiatury jest wyraźnie odradzany przez Microsoft. Zobacz poprzednio połączony artykuł MSDN. Jeśli potrzebujesz joysticka, XInput jest prawdopodobnie najlepszym rozwiązaniem.

EDYCJA: Moje informacje na ten temat mogą być zbyt stare.

BRaffle
źródło
3

Właściwie, rozróżnij L / R Ctrl / Alt, kiedy złapiesz WM_KEYDOWN / WM_KEYUP, możesz. Łatwo, nie jest, ale kod, którego używam, tutaj możesz mieć, hmm hmm.

Mam nadzieję, że to nadal działa.

// 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;
}
użytkownik1209
źródło
1
+1 za kod, ale -1 za mówienie jak yoda. Jest to denerwujące i sprawia, że ​​twoje odpowiedzi są trudne do odczytania.
Anthony
Rzeczywiście, nie jest to miejsce na konta żartów.
coderanger
2

Możesz wypróbować interfejs API DirectInput , a ostatnio interfejs API XInput .

Skizz
źródło
1
Czy XImput nie jest podłączony tylko do kontrolera XBox 360 do komputera? Przeczytałem, że DirectInput jest trochę przestarzałe, więc starałem się go unikać. Ale też wypróbowałem DirectInput i dobrze się spisałem.
Deluxe
XInput jest przeznaczony wyłącznie dla gamepadów. Począwszy od systemu Windows 10, XInput został wycofany na korzyść interfejsu „IGamepad”. Ponadto powinieneś używać RAW_INPUT w stosunku do innych mechanizmów, ponieważ są one ograniczone.
LaVolpe,