Глобальный захват нажатия кнопки мыши для Windows альтернатив (WH_MOUSE_LL, кажется, задерживает мышь) - PullRequest
0 голосов
/ 30 апреля 2020

У меня возникли проблемы с написанием слушателя клавиш мыши.

Моя цель

Цель состоит в том, чтобы написать общесистемный слушатель кнопок / клавиш, который поддерживает ввод с клавиатуры и кнопок мыши.

Не волнуйтесь, эта тема только о частях кнопок мыши, слушатель клавиатуры работает отлично.

Я использую это для пу sh, чтобы говорить голосовые ворота для моего Приложение VoIP.

Мой текущий подход

В настоящее время я использую WH_MOUSE_LL ловушку мыши для захвата событий мыши.

Для достижения максимальной производительности Чтобы избежать замедления работы мыши, я попытался минимизировать любые узкие места.

В настоящее время я использую этот код для метода обратного вызова:

thread_local KeyboardHook* thread_hook{nullptr};

void KeyboardHook::register_mouse_hook() {
    this->mouse_hook_id = SetWindowsHookEx(WH_MOUSE_LL, _mouse_hook_callback, GetModuleHandle(nullptr), 0);
    if(!this->keyboad_hook_id) {
        UnhookWindowsHookEx(this->keyboad_hook_id);
        cerr << "Failed to register mouse hook" << endl;
        return;
    }

    MSG msg;
    while(!GetMessage(&msg, nullptr, 0, 0) && this->active) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

LRESULT _mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) {
    assert(thread_hook);
    auto consume = thread_hook->mouse_hook_callback(nCode, event, ptr_keyboard);
    if(consume)
        return 1;
    return CallNextHookEx(nullptr, nCode, event, ptr_keyboard);
}

bool KeyboardHook::mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_mouse) {
    MouseButtonEventEntry* mb_event;
    switch (event) {
        case WM_LBUTTONDOWN:
            mb_event = allocate_mb_event();
            mb_event->type = KeyEvent::PRESS;
            mb_event->key = "MOUSE1";
            break;
        case WM_LBUTTONUP:
            mb_event = allocate_mb_event();
            mb_event->type = KeyEvent::RELEASE;
            mb_event->key = "MOUSE1";
            break;

        case WM_RBUTTONDOWN:
            mb_event = allocate_mb_event();
            mb_event->type = KeyEvent::PRESS;
            mb_event->key = "MOUSE3";
            break;
        case WM_RBUTTONUP:
            mb_event = allocate_mb_event();
            mb_event->type = KeyEvent::RELEASE;
            mb_event->key = "MOUSE3";
            break;

        case WM_XBUTTONDOWN: {
            auto mouse = (MouseHookStruct*) ptr_mouse;
            auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData);

            mb_event = allocate_mb_event();
            mb_event->type = KeyEvent::PRESS;
            mb_event->key = "MOUSEX" + std::to_string(x_index);
            break;
        }
        case WM_XBUTTONUP: {
            auto mouse = (MouseHookStruct*) ptr_mouse;
            auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData);

            mb_event = allocate_mb_event();
            mb_event->type = KeyEvent::RELEASE;
            mb_event->key = "MOUSEX" + std::to_string(x_index);
            break;
        }
        default:
            return false;
    }

    mb_event->next = nullptr;
    mb_event->hook = thread_hook;

    EnterCriticalSection(&global_event_dispatcher->mutex);
    *global_event_dispatcher->event_tail = mb_event;
    global_event_dispatcher->event_tail = &mb_event->next;

    /* notify other threads that some work needs to be done */
    WakeAllConditionVariable(&global_event_dispatcher->cv_work);
    LeaveCriticalSection(&global_event_dispatcher->mutex);
    return false;
}

Обратите внимание, хотя allocate_mb_event может указывать иначе, я не выделяю никакой памяти в этом методе вообще. Я просто использую список блоков без блокировок, чтобы получить следующую структуру MouseButtonEventEntry.

Блокировка global_event_dispatcher->mutex также не является проблемой, только один другой поток может заблокировать этот мьютекс. Он спит на условной переменной, а затем извлекает только следующие записи в очереди событий, когда получает уведомление через cv.

Моя проблема

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

Некоторые пользователи, особенно те, которые играют в игры, испытывают значительное отставание / замедление работы мыши.

Как выясняется, это похоже на крючок WH_MOUSE_LL сама проблема. Даже без какой-либо значительной обработки событий (например, пустой метод) они все еще испытывают это.

Один из моих подходов заключался в повышении класса приоритета диспетчерского потока до REALTIME_PRIORITY_CLASS, но это не очень помогает. Я также измерил фактическое время, потраченное на обратный вызов, и оно постоянно было меньше 1us.

Так что я не думаю, что обратный вызов - это сама проблема.

Что я ищу

Я не уверен, просто ли я что-то пропускаю или использование обратного вызова WH_MOUSE_LL вообще не очень хорошая идея.

Итак, что может быть альтернативой для захвата глобальных событий кнопок мыши или каким-то образом улучшить производительность обратного вызова?

...