diff --git a/src/qt/qt_winrawinputfilter.cpp b/src/qt/qt_winrawinputfilter.cpp index a62d71920..eca77d15d 100644 --- a/src/qt/qt_winrawinputfilter.cpp +++ b/src/qt/qt_winrawinputfilter.cpp @@ -6,15 +6,22 @@ * * This file is part of the 86Box distribution. * - * Windows raw input native filter for QT + * Windows raw input native filter for Qt * * * * Authors: Teemu Korhonen * Miran Grca, + * Sam Latinga + * Cacodemon345 * * Copyright 2021 Teemu Korhonen * Copyright 2016-2018 Miran Grca. + * Copyright 1997-2025 Sam Latinga + * Copyright 2024-2025 Cacodemon345. + * + * See this header for SDL3 code license: + * https://github.com/libsdl-org/SDL/blob/8e5fe0ea61dc87b29ca9a6119324221df0113bcf/src/video/windows/SDL_windowsrawinput.c#L1 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,6 +38,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* Mouse RawInput code taken from SDL3. */ + #include "qt_winrawinputfilter.hpp" #include @@ -94,30 +103,158 @@ bool windows_is_light_theme() { return i == 1; } +struct +{ + HANDLE done_event = 0, ready_event = 0; + std::atomic_bool done{false}; + + size_t rawinput_offset = 0, rawinput_size = 0; + uint8_t* rawinput = nullptr; + + HANDLE thread = 0; +} win_rawinput_data; + +static void +win_poll_mouse(void) +{ + // Yes, this is a thing in C++. + auto* data = &win_rawinput_data; + uint32_t size, i, count, total = 0; + RAWINPUT *input; + //static int64_t ms_time = plat_get_ticks(); + + if (data->rawinput_offset == 0) { + BOOL isWow64; + + data->rawinput_offset = sizeof(RAWINPUTHEADER); + if (IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64) { + // We're going to get 64-bit data, so use the 64-bit RAWINPUTHEADER size + data->rawinput_offset += 8; + } + } + + input = (RAWINPUT *)data->rawinput; + for (;;) { + size = data->rawinput_size - (UINT)((BYTE *)input - data->rawinput); + count = GetRawInputBuffer(input, &size, sizeof(RAWINPUTHEADER)); + if (count == 0 || count == (UINT)-1) { + if (!data->rawinput || (count == (UINT)-1 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { + const UINT RAWINPUT_BUFFER_SIZE_INCREMENT = 96; // 2 64-bit raw mouse packets + BYTE *rawinput = (BYTE *)realloc(data->rawinput, data->rawinput_size + RAWINPUT_BUFFER_SIZE_INCREMENT); + if (!rawinput) { + break; + } + input = (RAWINPUT *)(rawinput + ((BYTE *)input - data->rawinput)); + data->rawinput = rawinput; + data->rawinput_size += RAWINPUT_BUFFER_SIZE_INCREMENT; + } else { + break; + } + } else { + total += count; + + // Advance input to the end of the buffer + while (count--) { + input = NEXTRAWINPUTBLOCK(input); + } + } + } + + if (total > 0) { + for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) { + if (input->header.dwType == RIM_TYPEMOUSE) { + RAWMOUSE *rawmouse = (RAWMOUSE *)((BYTE *)input + data->rawinput_offset); + if (mouse_capture) + WindowsRawInputFilter::mouse_handle(rawmouse); + } + } + } + + //qDebug() << "Mouse delay: " << (plat_get_ticks() - ms_time); + //ms_time = plat_get_ticks(); +} + +static DWORD +win_rawinput_thread(void* param) +{ + RAWINPUTDEVICE rid = { + .usUsagePage = 0x01, + .usUsage = 0x02, + .dwFlags = 0, + .hwndTarget = nullptr + }; + auto window = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + if (!window) { + return 0; + } + + rid.hwndTarget = window; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + DestroyWindow(window); + return 0; + } + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + + SetEvent(win_rawinput_data.ready_event); + + while (!win_rawinput_data.done) { + DWORD result = MsgWaitForMultipleObjects(1, &win_rawinput_data.done_event, FALSE, INFINITE, QS_RAWINPUT); + + if (result != (WAIT_OBJECT_0 + 1)) { + break; + } + + // Clear the queue status so MsgWaitForMultipleObjects() will wait again + (void)GetQueueStatus(QS_RAWINPUT); + + win_poll_mouse(); + } + + rid.dwFlags |= RIDEV_REMOVE; + rid.hwndTarget = NULL; + + RegisterRawInputDevices(&rid, 1, sizeof(rid)); + DestroyWindow(window); + return 0; +} + extern "C" void win_joystick_handle(PRAWINPUT); std::unique_ptr WindowsRawInputFilter::Register(MainWindow *window) { - RAWINPUTDEVICE rid[2] = { + RAWINPUTDEVICE rid[1] = { { .usUsagePage = 0x01, .usUsage = 0x06, .dwFlags = RIDEV_NOHOTKEYS, .hwndTarget = nullptr - }, - { - .usUsagePage = 0x01, - .usUsage = 0x02, - .dwFlags = 0, - .hwndTarget = nullptr } }; - if (hook_enabled && (RegisterRawInputDevices(&(rid[1]), 1, sizeof(rid[0])) == FALSE)) - return std::unique_ptr(nullptr); - else if (!hook_enabled && (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE)) - return std::unique_ptr(nullptr); + if (!hook_enabled) { + RegisterRawInputDevices(rid, 1, sizeof(rid[0])); + } + win_rawinput_data.done_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + win_rawinput_data.ready_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + + if (!win_rawinput_data.done_event || !win_rawinput_data.ready_event) { + warning("Failed to create RawInput events."); + + goto conclude; + } + + win_rawinput_data.thread = CreateThread(nullptr, 0, win_rawinput_thread, nullptr, 0, nullptr); + if (win_rawinput_data.thread) { + HANDLE handles[2] = { win_rawinput_data.ready_event, win_rawinput_data.thread }; + + WaitForMultipleObjects(2, handles, FALSE, INFINITE); + } else { + warning("Failed to create RawInput thread."); + } + +conclude: std::unique_ptr inputfilter(new WindowsRawInputFilter(window)); return inputfilter; @@ -135,25 +272,23 @@ WindowsRawInputFilter::WindowsRawInputFilter(MainWindow *window) WindowsRawInputFilter::~WindowsRawInputFilter() { - RAWINPUTDEVICE rid[2] = { + win_rawinput_data.done = true; + if (win_rawinput_data.done_event) + SetEvent(win_rawinput_data.done_event); + if (win_rawinput_data.thread) + WaitForSingleObject(win_rawinput_data.thread, INFINITE); + RAWINPUTDEVICE rid = { .usUsagePage = 0x01, .usUsage = 0x06, .dwFlags = RIDEV_REMOVE, .hwndTarget = NULL - }, - { - .usUsagePage = 0x01, - .usUsage = 0x02, - .dwFlags = RIDEV_REMOVE, - .hwndTarget = NULL - } - }; + }; - if (hook_enabled) - RegisterRawInputDevices(&(rid[1]), 1, sizeof(rid[0])); - else - RegisterRawInputDevices(rid, 2, sizeof(rid[0])); + if (!hook_enabled) + RegisterRawInputDevices(&rid, 1, sizeof(rid)); + + free(win_rawinput_data.rawinput); } static void @@ -305,10 +440,6 @@ WindowsRawInputFilter::handle_input(HRAWINPUT input) case RIM_TYPEKEYBOARD: keyboard_handle(raw); break; - case RIM_TYPEMOUSE: - if (mouse_capture) - mouse_handle(raw); - break; case RIM_TYPEHID: win_joystick_handle(raw); break; @@ -328,9 +459,9 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw) } void -WindowsRawInputFilter::mouse_handle(PRAWINPUT raw) +WindowsRawInputFilter::mouse_handle(RAWMOUSE* raw) { - RAWMOUSE state = raw->data.mouse; + RAWMOUSE state = *raw; static int x, delta_x; static int y, delta_y; static int b, delta_z; diff --git a/src/qt/qt_winrawinputfilter.hpp b/src/qt/qt_winrawinputfilter.hpp index 411f7841c..9d8d6ba0e 100644 --- a/src/qt/qt_winrawinputfilter.hpp +++ b/src/qt/qt_winrawinputfilter.hpp @@ -38,6 +38,7 @@ #include #include +#include #include @@ -59,6 +60,8 @@ public: ~WindowsRawInputFilter(); + static void mouse_handle(RAWMOUSE* raw); + private: MainWindow *window; int buttons = 0; @@ -71,7 +74,6 @@ private: void handle_input(HRAWINPUT input); void keyboard_handle(PRAWINPUT raw); - void mouse_handle(PRAWINPUT raw); }; #endif