Klavye ve fare girişini kullanma (Win API)


11

Windows altında fare veya klavyeyi yakalamanın birkaç yolu vardır. Bu yüzden bazılarını denedim, ancak her birinin bazı avantajları ve dezavantajları var. Size sormak istiyorum: Hangi yöntemi kullanıyorsunuz?

Bunları denedim:

  1. WM_KEYDOWN / WM_KEYUP - Ana dezavantajı, ALT, CONTROL veya SHIFT gibi sol ve sağ tuşlar arasında ayrım yapamam.

  2. GetKeyboardState - Bu, ilk yöntemin sorununu çözer, ancak yenisi vardır. Sağ-ALT tuşuna basıldığında, Sol-Kontrol tuşunun da kapalı olduğunu görüyorum. Bu davranış, yalnızca yerelleştirilmiş klavye düzeni (Çekçe - CS) kullanılırken oluşur.

  3. WM_INPUT (Ham Giriş) - Bu yöntem ayrıca sol ve sağ elle kullanılan tuşları (hatırlayabiliyorsam) ayırt etmez ve fare hareketi için bazen fare konumunun sıfır delta değerlerine sahip mesaj üretir.

Yanıtlar:


10

Bunu yapmanın en iyi ve en basit yolu, ilk fikrinizi kullanmak ve WM_KEYUP / WM_KEYDOWN mesajlarının yanı sıra WM_SYSKEYUP / WM_SYSKEYDOWN mesajlarını işlemektir. Bunlar, sol ve sağ kaydırma / kontrol / alt tuşları arasındaki farkı tespit edebilir, sadece uygun sanal anahtar kodlarına ihtiyacınız vardır . Bunlar VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL ve VK_LMENU / VK_RMENU'dur (ALT anahtarı için).

Bunu nasıl yaptığım hakkında bir yazı yazdım ve aynı işleyicide hem WM_KEYUP / WM_KEYDOWN hem de WM_SYSKEYUP / WM_SYSKEYDOWN ile çalışıyordum. (Maalesef blog artık mevcut değil.)

Görebildiğim tek sorun, ABD dışındaki bir klavyeyi kullandığınızdan, MSDN'deki WM_SYSKEYUP makalesinde açıklanan sırayı işlemek için bazı ek mantık eklemeniz gerektiğidir . Ancak muhtemelen masteryoda'dan daha basit bir şey yapmaya çalışacağım.


WM_ mesajları kesinlikle en basit, ama sadece cevapsız olayları umursamıyorsanız "en iyi" dir. Bu çözümü çözülemez bir problem olduğunu anladıktan sonra terk ettim; eğer bir tuş basılı değilken uygulamanız odağı kaybederse, odakta tekrar basana kadar o tuş "takılı kalır".
dash-tom-bang

1
Aslında eksik girdi bir problemdir, ancak en kolay çözüm odak / aktivasyon mesajlarını uygun şekilde ele almak ve bu sorunu çözmek için olacaktır. Pratik olarak yapmak istediğiniz şey, odağı kaybettiğinizde oyunu duraklatmaktır, çünkü kullanıcının daha acil bir uygulamaya geçmesi gerekmeyebilir veya sadece yanlışlıkla Windows tuşuna basarlar.
Daemin

3

Bunları birleştirememenizin bir nedeni var mı? Örneğin, bir Ctrl / Alt / Shift tuşuna basmayı algılamak için WM_KEYDOWN tuşunu kullanın, sonra bu çağrıda soldan sağdan ayırt etmek için GetKeyboardState () kullanın?


Evet yapabilirim. Muhtemelen bu çözümle bitireceğim (belki GetAsyncKeyState'i kullanmak daha iyi olacaktır). Ama varsa daha iyi bir çözüm arıyorum. Sağ ATL tuşu da iki WM_KEYDOWN mesajı oluşturur (Klavye düzeni nedeniyle). Böylece yalnızca WM_INPUT veya DirectInput kalır.
Deluxe

3

WM_INPUT güzel. Ben düşünüyorum sol ayırt edebilir / sağ tuşlarını kullanarak RAWKEYBOARD yapı . Zor kısım, anahtar tanımlayıcıları (yani tarama kodları) ile nasıl başa çıkılacağını bulmak olabilir, ancak bunu klavye girişi için kullanmayı hiç denemediğim için söyleyemem. WM_KEYDOWN çok kolay :)

Yine de fare girişi için WM_INPUT kullandım. Çok düşük seviyeli. Hızlanma uygulanmadı, ki bu çok güzel (IMO). WM_INPUT, yüksek dpi fare hareketinden faydalanmanın tek yoluydu, ancak hala böyle olup olmadığından emin değilim. 2006'dan bu MSDN makalesine bakın .

Fare / klavye için DirectInput, Microsoft tarafından açıkça önerilmez. Önceden bağlanmış MSDN makalesine bakın. Bir joystick'e ihtiyacınız varsa, XInput muhtemelen gitmenin yoludur.

EDIT: Bu konudaki bilgilerim çok tarihli olabilir.


3

Aslında, WM_KEYDOWN / WM_KEYUP'ı yakaladığınızda L / R Ctrl / Alt'ı ayrı ayrı söyleyebilirsiniz. Kolay, değil, ama kullandığım kod, burada olabilir, hmm hmm.

Umarım bu hala işe yarar.

// 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
Kod için +1, ancak yoda gibi konuşmak için -1. Bu sinir bozucu ve cevaplarınızı okumayı zorlaştırıyor.
Anthony

Gerçekten de, burası şaka hesapları için bir yer değil.
coderanger

2

DirectInput API'sini veya son zamanlarda XInput API'sını deneyebilirsiniz .


1
XImput sadece PC'ye bağlı XBox 360 denetleyicisi için değil mi? DirectInput biraz eskimiş okudum, bu yüzden kullanmaktan kaçınmaya çalıştım. Ama DirectInput'u da denedim ve iyi uyandım.
Deluxe

XInput yalnızca gamepad'ler içindir. Windows 10'dan itibaren XInput, 'IGamepad' arayüzü lehine kullanımdan kaldırıldı. Üstelik RAW_INPUT'u diğer mekanizmalar üzerinde kullanmalısınız, çünkü bunlar üzerinde sınırlamalar vardır.
LaVolpe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.