Xử lý đầu vào bàn phím và chuột (Win API)


11

Có một số cách để bắt chuột hoặc bàn phím trong Windows. Vì vậy, tôi đã thử một vài trong số chúng, nhưng mỗi cái đều có một số ưu điểm và nhược điểm. Tôi muốn hỏi bạn: sử dụng phương pháp nào?

Tôi đã thử những điều này:

  1. WM_KEYDOWN / WM_KEYUP - Nhược điểm chính là, tôi không thể phân biệt giữa các phím thuận tay trái và tay phải như ALT, KIỂM SOÁT hoặc SHIFT.

  2. GetPalState - Điều này giải quyết vấn đề của phương thức đầu tiên, nhưng có một phương pháp mới. Khi tôi nhận được rằng phím ALT phải được nhấn, tôi cũng nhận thấy rằng phím Điều khiển bên trái bị tắt. Hành vi này chỉ xảy ra khi sử dụng bố trí bàn phím cục bộ (Séc - CS).

  3. WM_INPUT (Nhập liệu thô) - Phương pháp này cũng không phân biệt các phím thuận tay trái và tay phải (nếu tôi có thể nhớ) và đối với chuyển động chuột đôi khi tạo ra thông báo có giá trị delta bằng 0 của vị trí chuột.

Câu trả lời:


10

Cách tốt nhất và đơn giản nhất để thực hiện là sử dụng ý tưởng đầu tiên của bạn và xử lý các tin nhắn WM_KEYUP / WM_KEYDOWN cũng như các tin nhắn WM_SYSKEYUP / WM_SYSKEYDOWN. Chúng có thể xử lý việc phát hiện sự khác biệt giữa các phím shift / control / alt trái và phải, bạn chỉ cần các mã khóa ảo phù hợp . Chúng là VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL và VK_LMENU / VK_RMENU (đối với phím ALT).

Tôi đã viết một bài đăng về cách tôi đã làm điều này và tôi đã xử lý cả WM_KEYUP / WM_KEYDOWN và WM_SYSKEYUP / WM_SYSKEYDOWN trong cùng một trình xử lý. (Thật không may, blog không còn có sẵn.)

Sự phức tạp duy nhất mà tôi có thể thấy là bởi vì bạn đang sử dụng bàn phím không phải của Hoa Kỳ, bạn sẽ cần thêm một số logic bổ sung để xử lý chuỗi được mô tả trong bài viết WM_SYSKEYUP trên MSDN. Tuy nhiên tôi có lẽ sẽ cố gắng làm một cái gì đó đơn giản hơn so với masteryoda.


Các tin nhắn WM_ chắc chắn là đơn giản nhất, nhưng "tốt nhất" chỉ khi bạn không quan tâm đến các sự kiện bị bỏ lỡ. Tôi đã từ bỏ giải pháp đó một khi tôi nhận ra đó là một vấn đề khó giải quyết; nếu ứng dụng của bạn mất tiêu điểm trong khi phím bị tắt, phím đó sẽ bị "kẹt" cho đến khi bạn nhấn lại vào tiêu điểm.
dash-tom-bang

1
Thực sự thiếu đầu vào là một vấn đề, nhưng giải pháp đơn giản nhất là xử lý thích hợp các thông điệp tập trung / kích hoạt và xử lý xung quanh nó. Thực tế những gì bạn muốn làm là tạm dừng trò chơi khi bạn mất tập trung, vì người dùng có thể không cần phải chuyển sang một ứng dụng khẩn cấp khác, hoặc họ vô tình nhấn phím windows.
Đà Nẵng

3

Có một lý do bạn không thể kết hợp chúng? Ví dụ: sử dụng WM_KEYDOWN để phát hiện nhấn phím Ctrl / Alt / Shift, sau đó trong cuộc gọi đó, hãy sử dụng GetPalState () để phân biệt từ trái sang phải?


Vâng tôi có thể. Tôi có thể sẽ kết thúc với giải pháp này (có thể sẽ tốt hơn khi sử dụng GetAsyncKeyState). Nhưng tôi đang tìm kiếm bất kỳ giải pháp tốt hơn nếu có. Và phím Right-ATL cũng tạo hai tin nhắn WM_KEYDOWN (Vì bố trí bàn phím). Vì vậy, chỉ còn lại WM_INPUT hoặc DirectInput.
Deluxe

3

WM_INPUT là tốt đẹp. Tôi nghĩ bạn có thể phân biệt các phím trái / phải bằng cấu trúc RAWKEYBOARD . Phần khó có thể là tìm ra cách xử lý các mã định danh chính (ví dụ: scancodes), nhưng tôi không thể nói vì tôi chưa bao giờ thử sử dụng cái này cho đầu vào bàn phím. WM_KEYDOWN thật dễ dàng :)

Tuy nhiên, tôi đã sử dụng WM_INPUT cho đầu vào chuột. Đó là cấp độ rất thấp. Nó không có khả năng tăng tốc được áp dụng, rất tốt (IMO). WM_INPUT từng là cách duy nhất để tận dụng sự di chuyển của chuột có dpi cao, nhưng tôi không chắc liệu đó có phải là trường hợp không. Xem bài viết MSDN này từ năm 2006 .

DirectInput cho chuột / bàn phím được Microsoft khuyến khích rõ ràng. Xem bài viết MSDN được liên kết trước đó. Nếu bạn cần một phím điều khiển, XInput có lẽ là con đường để đi.

EDIT: Thông tin của tôi về điều này có thể quá cũ.


3

Trên thực tế, hãy phân biệt L / R Ctrl / Alt khi bạn bắt WM_KEYDOWN / WM_KEYUP, bạn có thể. Dễ thôi, không phải vậy, nhưng mã mà tôi sử dụng, ở đây bạn có thể có, hmm hmm.

Hy vọng điều này vẫn hoạt động, tôi làm.

// 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 cho mã, nhưng -1 để nói như yoda. Nó gây phiền nhiễu và làm cho câu trả lời của bạn khó đọc.
Anthony

Thật vậy, đây không phải là nơi dành cho các tài khoản đùa.
coderanger

2

Bạn có thể thử API DirectInput hoặc gần đây hơn là API XInput .


1
Không phải XImput chỉ dành cho bộ điều khiển XBox 360 được kết nối với PC? Tôi đã đọc DirectInput hơi lỗi thời, vì vậy tôi đã cố gắng tránh sử dụng nó. Nhưng tôi cũng đã thử DirectInput và tỉnh dậy.
Deluxe

XInput chỉ dành cho gamepad. Kể từ Windows 10, XInput đã không được ủng hộ giao diện 'IGamepad'. Trên hết, bạn nên sử dụng RAW_INPUT so với các cơ chế khác, vì có những hạn chế đối với chúng.
LaVolpe
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.