การจัดการคีย์บอร์ดและเมาส์อินพุต (Win API)


11

มีหลายวิธีในการจับเมาส์หรือแป้นพิมพ์ใน Windows ดังนั้นฉันจึงลองบางอย่าง แต่ทุกคนมีข้อดีและข้อเสีย ฉันอยากถามคุณว่า: วิธีไหนที่ใช้?

ฉันได้ลองแล้ว:

  1. WM_KEYDOWN / WM_KEYUP - ข้อเสียเปรียบหลักคือฉันไม่สามารถแยกความแตกต่างระหว่างปุ่มซ้ายและมือขวาเช่น ALT, CONTROL หรือ SHIFT

  2. GetKeyboardState - วิธีนี้แก้ปัญหาของวิธีแรก แต่มีวิธีใหม่ เมื่อฉันกดปุ่ม Right-ALT ฉันก็จะเห็นว่าปุ่มควบคุมซ้ายไม่ทำงาน ลักษณะการทำงานนี้จะเกิดขึ้นเฉพาะเมื่อใช้รูปแบบแป้นพิมพ์ที่แปลเป็นภาษาท้องถิ่น (เช็ก - CS)

  3. WM_INPUT (การป้อนข้อมูลแบบดิบ) - วิธีนี้ยังไม่แยกความแตกต่างของปุ่มซ้ายและมือขวา (ถ้าฉันจำได้) และบางครั้งการเคลื่อนไหวของเมาส์จะสร้างข้อความที่มีค่าเดลต้าเป็นศูนย์ของตำแหน่งเมาส์

คำตอบ:


10

วิธีที่ดีที่สุดและง่ายที่สุดคือใช้แนวคิดแรกของคุณและจัดการกับข้อความ WM_KEYUP / WM_KEYDOWN รวมถึงข้อความ WM_SYSKEYUP / WM_SYSKEYDOWN สิ่งเหล่านี้สามารถจัดการกับการตรวจจับความแตกต่างระหว่างปุ่ม Shift / control / alt ซ้ายและขวาคุณเพียงแค่ต้องใช้รหัสคีย์เสมือนที่เหมาะสมรหัสคีย์เสมือนพวกเขาคือ VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL และ VK_LMENU / VK_RMENU (สำหรับปุ่ม ALT)

ฉันเขียนโพสต์เกี่ยวกับวิธีที่ฉันทำและฉันจัดการทั้ง WM_KEYUP / WM_KEYDOWN และ WM_SYSKEYUP / WM_SYSKEYDOWN ในตัวจัดการเดียวกัน (น่าเสียดายที่บล็อกนี้ไม่มีให้บริการอีกต่อไป)

ปัญหาเดียวที่ฉันเห็นคือเพราะคุณใช้แป้นพิมพ์นอกสหรัฐอเมริกาคุณจะต้องเพิ่มตรรกะเพิ่มเติมเพื่อจัดการกับลำดับที่อธิบายไว้ในบทความ WM_SYSKEYUPบน MSDN อย่างไรก็ตามฉันอาจลองทำสิ่งที่ง่ายกว่าของ masteryoda


ข้อความ WM_ นั้นง่ายที่สุด แต่ "ดีที่สุด" เฉพาะในกรณีที่คุณไม่สนใจเกี่ยวกับเหตุการณ์ที่ไม่ได้รับ ฉันเลิกใช้วิธีแก้ปัญหาเมื่อฉันรู้ว่ามันเป็นปัญหาที่รักษาไม่หาย หากแอปพลิเคชันของคุณสูญเสียการโฟกัสในขณะที่ปุ่มกดลงกุญแจนั้นจะ "ติด" จนกว่าคุณจะกดมันอีกครั้งในโฟกัส
dash-tom-bang

1
การป้อนข้อมูลที่ขาดหายไปนั้นเป็นปัญหาอย่างแท้จริง แต่วิธีที่ง่ายที่สุดคือจัดการกับข้อความการโฟกัส / การเปิดใช้งานอย่างเหมาะสมและแก้ไขปัญหา ในทางปฏิบัติสิ่งที่คุณต้องการทำคือหยุดเกมชั่วคราวเมื่อคุณไม่ได้โฟกัสเนื่องจากผู้ใช้อาจต้องเปลี่ยนไปใช้แอปพลิเคชันเร่งด่วนอื่น ๆ มากกว่าหรือกดปุ่ม Windows โดยไม่ตั้งใจ
Daemin

3

มีเหตุผลที่คุณไม่สามารถรวมพวกมันได้หรือไม่? ตัวอย่างเช่นใช้ WM_KEYDOWN เพื่อตรวจจับการกดปุ่ม Ctrl / Alt / Shift จากนั้นภายในการโทรนั้นใช้ GetKeyboardState () เพื่อแยกความแตกต่างทางซ้ายจากขวา?


ใช่ฉันทำได้. ฉันอาจจะจบด้วยวิธีนี้ (อาจจะดีกว่าที่จะใช้ GetAsyncKeyState) แต่ฉันกำลังมองหาทางออกที่ดีกว่าหากมีอยู่ และคีย์ Right-ATL สร้างข้อความ WM_KEYDOWN สองข้อความด้วยเช่นกัน (เนื่องจากรูปแบบแป้นพิมพ์) ดังนั้นเหลือเพียง WM_INPUT หรือ DirectInput
ห้องดีลักซ์

3

WM_INPUT ดีมาก ฉันคิดว่าคุณสามารถแยกความแตกต่างของปุ่มซ้าย / ขวาโดยใช้โครงสร้างRAWKEYBOARD structส่วนที่ยากอาจหาวิธีจัดการกับตัวระบุคีย์ (เช่น scancodes) แต่ฉันไม่สามารถพูดได้เพราะฉันไม่เคยลองใช้สิ่งนี้เพื่อป้อนคีย์บอร์ด WM_KEYDOWN นั้นง่ายมาก :)

ฉันใช้ WM_INPUT สำหรับการป้อนข้อมูลด้วยเมาส์ เป็นระดับต่ำมาก ไม่มีการเร่งความเร็วซึ่งดีมาก (IMO) WM_INPUT เคยเป็นวิธีเดียวที่จะใช้ประโยชน์จากการเคลื่อนไหวของเมาส์สูง dpi แต่ฉันไม่แน่ใจว่ายังคงเป็นเช่นนั้นหรือไม่ ดูบทความนี้ MSDN จาก 2006

DirectInput สำหรับเมาส์ / แป้นพิมพ์ถูกกีดกันอย่างชัดเจนจาก Microsoft ดูบทความ MSDN ที่เชื่อมโยงก่อนหน้านี้ หากคุณต้องการจอยสติ๊ก XInput อาจเป็นหนทางไป

แก้ไข: ข้อมูลของฉันเกี่ยวกับสิ่งนี้อาจเก่าเกินไป


3

จริง ๆ แล้วบอกแยก L / R Ctrl / Alt เมื่อคุณจับ WM_KEYDOWN / WM_KEYUP คุณสามารถ ง่ายมันไม่ใช่ แต่เป็นรหัสที่ฉันใช้ตรงนี้คุณมีได้ hmm hmm

หวังว่ามันจะยังใช้งานได้ฉันทำ

// 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 สำหรับรหัส แต่ -1 สำหรับการพูดเช่น yoda มันน่ารำคาญและทำให้คำตอบของคุณอ่านยาก
แอนโธนี

แน่นอนนี่ไม่ใช่สถานที่สำหรับบัญชีตลก
coderanger

2

คุณสามารถลองDirectInput APIหรือมากกว่าเมื่อเร็ว ๆ นี้XInput API


1
XImput สำหรับตัวควบคุม XBox 360 เชื่อมต่อกับพีซีไม่ได้เท่านั้นหรือไม่ ฉันอ่าน DirectInput ล้าสมัยไปแล้วดังนั้นฉันจึงพยายามหลีกเลี่ยงการใช้มัน แต่ฉันได้ลอง DirectInput ด้วยแล้วก็รู้สึกดี
ห้องดีลักซ์

XInput ใช้สำหรับเกมแพดเท่านั้น ตั้งแต่ Windows 10 XInput ได้รับการสนับสนุนด้วยอินเทอร์เฟซ 'IGamepad' ยิ่งไปกว่านั้นคุณควรใช้ RAW_INPUT เหนือกลไกอื่น ๆ เนื่องจากมีข้อ จำกัด อยู่
LaVolpe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.