Menangani input keyboard dan mouse (Win API)

11

Ada beberapa cara untuk menangkap mouse atau keyboard di bawah Windows. Jadi saya mencoba beberapa dari mereka, tetapi masing-masing memiliki beberapa kelebihan dan kekurangan. Saya ingin bertanya: Metode apa yang digunakan?

Saya sudah mencoba ini:

  1. WM_KEYDOWN / WM_KEYUP - Kerugian utama adalah, saya tidak bisa membedakan antara kunci kiri dan kanan seperti ALT, CONTROL atau SHIFT.

  2. GetKeyboardState - Ini memecahkan masalah metode pertama, tetapi ada yang baru. Ketika saya mendapatkan bahwa tombol Kanan-ALT ditekan, saya juga mendapatkan bahwa tombol Kiri-Kontrol turun. Perilaku ini hanya terjadi ketika menggunakan tata letak keyboard lokal (Czech - CS).

  3. WM_INPUT (Input Mentah) - Metode ini juga tidak membedakan tombol kiri dan tangan kanan (jika saya ingat) dan untuk pergerakan mouse terkadang menghasilkan pesan dengan nilai nol delta posisi mouse.

Mewah
sumber

Jawaban:

10

Cara terbaik dan paling sederhana untuk melakukannya adalah dengan menggunakan ide pertama Anda dan menangani pesan WM_KEYUP / WM_KEYDOWN serta pesan WM_SYSKEYUP / WM_SYSKEYDOWN. Ini dapat menangani mendeteksi perbedaan antara tombol shift / control / alt kiri dan kanan, Anda hanya perlu kode kunci virtual yang sesuai . Mereka adalah VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL, dan VK_LMENU / VK_RMENU (untuk kunci ALT).

Saya menulis posting tentang bagaimana saya melakukan ini, dan saya menangani baik WM_KEYUP / WM_KEYDOWN dan WM_SYSKEYUP / WM_SYSKEYDOWN pada penangan yang sama. (Sayangnya, blog tidak lagi tersedia.)

Satu-satunya komplikasi yang dapat saya lihat adalah bahwa karena Anda menggunakan keyboard non-AS, Anda harus menambahkan beberapa logika tambahan untuk menangani urutan yang dijelaskan dalam artikel WM_SYSKEYUP di MSDN. Namun saya mungkin akan mencoba untuk membuat sesuatu yang lebih sederhana daripada masteryoda.

Daemin
sumber
Pesan WM_ tentu saja yang paling sederhana, tetapi "terbaik" hanya jika Anda tidak peduli dengan peristiwa yang terlewat. Saya meninggalkan solusi itu begitu saya menyadari bahwa itu adalah masalah yang sulit diatasi; jika aplikasi Anda kehilangan fokus saat tombol turun, kunci itu akan "macet" sampai Anda menekannya kembali dalam fokus.
dash-tom-bang
1
Memang input yang hilang adalah masalah, tetapi solusi termudah adalah menangani dengan tepat pesan fokus / aktivasi dan mengatasinya. Praktis yang ingin Anda lakukan adalah menghentikan permainan saat Anda kehilangan fokus, karena pengguna mungkin perlu beralih ke aplikasi lain yang lebih mendesak, atau mereka hanya menekan tombol windows secara tidak sengaja.
Daemin
3

Apakah ada alasan Anda tidak bisa menggabungkannya? Misalnya, gunakan WM_KEYDOWN untuk mendeteksi penekanan tombol Ctrl / Alt / Shift, maka di dalam panggilan itu gunakan GetKeyboardState () untuk membedakan kiri dari kanan?

Ian Schreiber
sumber
Ya saya bisa. Saya mungkin akan mengakhiri dengan solusi ini (mungkin akan lebih baik menggunakan GetAsyncKeyState). Tetapi saya mencari solusi yang lebih baik jika ada. Dan tombol Right-ATL juga menghasilkan dua pesan WM_KEYDOWN (Karena tata letak keyboard). Jadi hanya WM_INPUT atau DirectInput yang tersisa.
Deluxe
3

WM_INPUT bagus. Saya pikir Anda dapat membedakan tombol kiri / kanan menggunakan struct RAWKEYBOARD . Bagian yang sulit mungkin mencari tahu bagaimana menangani pengidentifikasi kunci (yaitu scancodes), tapi saya tidak bisa mengatakannya karena saya belum pernah mencoba menggunakan ini untuk input keyboard. WM_KEYDOWN sangat mudah :)

Saya telah menggunakan WM_INPUT untuk input mouse. Tingkatnya sangat rendah. Tidak ada akselerasi yang diterapkan, yang sangat bagus (IMO). WM_INPUT dulunya satu-satunya cara untuk mengambil keuntungan dari pergerakan mouse dpi tinggi, tapi saya tidak yakin apakah itu masih terjadi. Lihat artikel MSDN ini dari 2006 .

DirectInput untuk mouse / keyboard secara eksplisit tidak disarankan oleh Microsoft. Lihat artikel MSDN yang ditautkan sebelumnya. Jika Anda membutuhkan joystick, XInput mungkin adalah cara yang harus dilakukan.

EDIT: Info saya tentang ini mungkin terlalu tanggal.

Peredam
sumber
3

Sebenarnya, beri tahu L / R Ctrl / Alt ketika Anda menangkap WM_KEYDOWN / WM_KEYUP, Anda bisa. Mudah, bukan, tapi kode yang saya gunakan, di sini Anda bisa, hmm hmm.

Semoga ini masih berfungsi, saya lakukan.

// 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;
}
pengguna1209
sumber
1
+1 untuk kode, tetapi -1 untuk berbicara seperti yoda. Ini menjengkelkan dan membuat jawaban Anda sulit dibaca.
Anthony
Memang, ini bukan tempat untuk akun lelucon.
coderanger
2

Anda dapat mencoba DirectInput API , atau yang lebih baru, XInput API .

Mendesis
sumber
1
Bukankah XImput hanya untuk pengontrol XBox 360 yang terhubung ke PC? Saya sudah membaca DirectInput sedikit usang, jadi saya sudah mencoba untuk menghindari menggunakannya. Tapi saya sudah mencoba DirectInput juga dan terbangun dengan baik.
Deluxe
XInput hanya untuk gamepad saja. Pada Windows 10, XInput telah usang mendukung antarmuka 'IGamepad'. Selain itu, Anda harus menggunakan RAW_INPUT di atas mekanisme lain, karena ada batasannya.
LaVolpe