Обработка фокусирующего щелчка с использованием необработанного ввода - PullRequest
0 голосов
/ 17 декабря 2018

Я хочу обрабатывать щелчки мыши в моей игре с помощью Raw Input API, используя сообщения WM_INPUT.Меня интересуют только щелчки мышью, когда мое приложение активно.

Когда окно неактивно и я щелкаю в нем, помещая его в фокус, сообщение WM_INPUT для этого клика не генерируется. Я могу обрабатывать каждый второй щелчок, кроме того, который фокусирует мое окно.

С помощью флага RIDEV_INPUTSINK я могу перехватить одно пропущенное событие, но тогда мне придется каким-то образом игнорировать все остальные сообщения, которые я получаю, пока мойприложение неактивно.

Большинство приложений и оконных программ обрабатывают «активирующий щелчок», как обычный, они его не игнорируют.Вероятно, они используют «устаревшие» сообщения, поскольку они поступают правильно.

Должен ли я смешивать необработанный ввод с обработкой устаревшей мыши?Должен ли я использовать флаг INPUTSINK и выборочно игнорировать сообщения?Если да, то как?

Поскольку Microsoft рекомендует использовать исходные данные для игр, я надеюсь, что есть общее решение этой проблемы, которое я пока не смог найти.

Соответствующий фрагмент из кода:

Регистрация необработанного устройства ввода

RAWINPUTDEVICE Rid[1];    
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x02;
Rid[0].dwFlags = 0;   // adds HID mouse
Rid[0].hwndTarget = 0;

RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]))

Для удобства приведу полный пример программы, демонстрирующей мою проблему.

#include <windows.h>
#include <strsafe.h>

const char g_szClassName[] = "myWindowClass";

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_INPUT:
        {
            UINT dwSize;

            GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize,
                sizeof(RAWINPUTHEADER));
            LPBYTE lpb = new BYTE[dwSize];
            if (lpb == NULL)
            {
                return 0;
            }

            if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize,
                sizeof(RAWINPUTHEADER)) != dwSize)
                OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));

            RAWINPUT* raw = (RAWINPUT*)lpb;

            size_t const cchDest = 1000;
            TCHAR pszDest[cchDest];

            if (raw->header.dwType == RIM_TYPEMOUSE && raw->data.mouse.ulButtons)
            {
                auto hResult = StringCchPrintf(pszDest, STRSAFE_MAX_CCH, TEXT("Mouse: usFlags=%04x ulButtons=%04x usButtonFlags=%04x usButtonData=%04x ulRawButtons=%04x lLastX=%04x lLastY=%04x ulExtraInformation=%04x\r\n"),
                    raw->data.mouse.usFlags,
                    raw->data.mouse.ulButtons,
                    raw->data.mouse.usButtonFlags,
                    raw->data.mouse.usButtonData,
                    raw->data.mouse.ulRawButtons,
                    raw->data.mouse.lLastX,
                    raw->data.mouse.lLastY,
                    raw->data.mouse.ulExtraInformation);

                if (FAILED(hResult))
                {
                    // TODO: write error handler
                }
                OutputDebugString(pszDest);
            }

            delete[] lpb;
        }
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    //Step 1: Registering the Window Class
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "The title of my window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, hInstance, NULL);

    if (hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    RAWINPUTDEVICE Rid[1];

    Rid[0].usUsagePage = 0x01;
    Rid[0].usUsage = 0x02;
    Rid[0].dwFlags = 0;   // adds HID mouse
    Rid[0].hwndTarget = 0;

    if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
        //registration failed. Call GetLastError for the cause of the error
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Step 3: The Message Loop
    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

ОБНОВЛЕНИЕ

Пока я нашел обходной путь, смешав устаревшие и необработанные данные.В сообщении WM_ACTIVATE я проверяю, равен ли wParam 2. Если это так, это означает, что окно было активировано с помощью щелчка мыши.

В этом случае я фактически обрабатываю следующий WM_LBUTTONDOWN, но только один раз (и другойтакже кнопки).

Это ужасный взлом, поэтому я все еще ищу правильное решение.

...