키보드 및 마우스 입력 처리 (Win API)


11

Windows에서 마우스 나 키보드를 잡는 방법에는 여러 가지가 있습니다. 그래서 나는 그들 중 일부를 시도했지만 모든 장점과 단점이 있습니다. 나는 당신에게 묻고 싶습니다 : 어떤 방법을 사용합니까?

나는 이것을 시도했다 :

  1. WM_KEYDOWN / WM_KEYUP-가장 큰 단점은 ALT, CONTROL 또는 SHIFT와 같은 왼손잡이와 오른 손잡이를 구별 할 수 없다는 것입니다.

  2. GetKeyboardState-첫 번째 방법의 문제를 해결하지만 새로운 방법이 있습니다. Right-ALT 키를 누르면 Left-Control 키가 다운 된 것입니다. 이 문제는 현지화 된 키보드 레이아웃 (체코-CS)을 사용할 때만 발생합니다.

  3. WM_INPUT (Raw Input)-이 방법은 왼손과 오른 손잡이 키를 구별하지 않으며 (기억할 수있는 경우) 마우스 이동을 위해 때때로 마우스 위치의 델타 값이 0 인 메시지를 생성합니다.

답변:


10

가장 쉽고 간단한 방법은 첫 번째 아이디어를 사용하고 WM_SYSUPUP / WM_SYSKEYDOWN 메시지뿐만 아니라 WM_KEYUP / WM_KEYDOWN 메시지를 처리하는 것입니다. 이들은 왼쪽과 오른쪽 Shift / control / alt 키 사이의 차이를 감지 할 수 있습니다 . 적절한 가상 키 코드 만 있으면 됩니다. VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL 및 VK_LMENU / VK_RMENU (ALT 키)입니다.

나는이 작업을 수행 한 방법에 대한 게시물을 작성했으며 동일한 핸들러에서 WM_KEYUP / WM_KEYDOWN 및 WM_SYSKEYUP / WM_SYSKEYDOWN을 모두 처리했습니다. (불행히도 블로그는 더 이상 사용할 수 없습니다.)

내가 볼 수있는 유일한 합병증은 미국 이외의 키보드를 사용하기 때문에 MSDN 의 WM_SYSKEYUP 기사 에 설명 된 시퀀스를 처리하기 위해 추가 논리를 추가해야한다는 것 입니다. 그러나 아마도 masteryoda보다 더 간단한 것을 만들려고합니다.


WM_ 메시지는 확실히 가장 단순하지만 누락 된 이벤트에 신경 쓰지 않는 경우에만 "가장 좋습니다". 다루기 힘든 문제라는 것을 깨닫고 나서 그 해결책을 포기했습니다. 키가 눌려있는 동안 응용 프로그램에서 포커스를 잃으면 포커스를 다시 누를 때까지 해당 키가 "고정"됩니다.
dash-tom-bang 1

1
실제로 입력 누락이 문제이지만 가장 쉬운 해결책은 포커스 / 활성화 메시지를 적절히 처리하고 해결하는 것입니다. 실제로 원하는 것은 초점을 잃을 때 게임을 일시 중지하는 것입니다. 사용자가 더 긴급한 다른 응용 프로그램으로 전환하지 않아도되거나 실수로 Windows 키를 누르기 때문입니다.
Daemin

3

당신이 그들을 결합 할 수없는 이유가 있습니까? 예를 들어 WM_KEYDOWN을 사용하여 Ctrl / Alt / Shift 키의 누름을 감지 한 다음 해당 호출 내에서 GetKeyboardState ()를 사용하여 왼쪽에서 오른쪽으로 구별합니까?


예, 저는 할수 있습니다. 아마도이 솔루션으로 끝날 것입니다 (GetAsyncKeyState를 사용하는 것이 좋습니다). 그러나 존재하는 경우 더 나은 솔루션을 찾고 있습니다. Right-ATL 키는 키보드 레이아웃으로 인해 두 개의 WM_KEYDOWN 메시지도 생성합니다. 따라서 WM_INPUT 또는 DirectInput 만 남아 있습니다.
Deluxe

3

WM_INPUT이 좋습니다. 내가 생각하는 당신이 사용 / 오른쪽 키를 왼쪽으로 구별 할 수 RAWKEYBOARD 구조체를 . 어려운 부분은 키 식별자 (예 : 스캔 코드)를 처리하는 방법을 알아낼 수 있지만 키보드 입력에 이것을 사용하려고 시도한 적이 없기 때문에 말할 수 없습니다. WM_KEYDOWN은 너무 쉽습니다 :)

그래도 마우스 입력에 WM_INPUT을 사용했습니다. 매우 낮은 수준입니다. 가속이 적용되지 않았으므로 매우 좋습니다 (IMO). WM_INPUT은 높은 DPI 마우스 이동을 활용하는 유일한 방법 이었지만 여전히 그런지 잘 모르겠습니다. 2006 년이 MSDN 기사를 참조하십시오 .

마우스 / 키보드 용 DirectInput은 Microsoft에서 명시 적으로 권장하지 않습니다. 이전에 링크 된 MSDN 기사를 참조하십시오. 조이스틱이 필요하다면 XInput이 가장 좋은 방법 일 것입니다.

편집 : 이것에 대한 내 정보가 너무 오래되었을 수 있습니다.


3

실제로 WM_KEYDOWN / WM_KEYUP을 잡을 때 L / R Ctrl / Alt를 구분하면됩니다. 쉽지는 않지만, 내가 사용하는 코드는 여기 있습니다. 흠 흠.

이것이 여전히 효과가 있기를 바랍니다.

// 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이지만 yoda처럼 말하면 -1입니다. 귀찮고 답을 읽기 어렵게 만듭니다.
Anthony

실제로, 이것은 농담 계정을위한 장소가 아닙니다.
coderanger

2

DirectInput API 또는 더 최근에는 XInput API를 사용해 볼 수 있습니다.


1
XBox 360 컨트롤러 전용 XImput이 PC에 연결되어 있지 않습니까? DirectInput이 거의 쓸모없는 것을 읽었으므로 사용하지 않으려 고 노력했습니다. 그러나 DirectInput도 시도했지만 잘 작동했습니다.
Deluxe

XInput은 게임 패드 전용입니다. Windows 10부터 XInput은 'IGamepad'인터페이스를 위해 더 이상 사용되지 않습니다. 또한 다른 메커니즘보다 RAW_INPUT을 사용해야합니다. 제한이 있기 때문입니다.
LaVolpe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.