Use a separate thread for polling mouse on Windows

This commit is contained in:
Cacodemon345
2025-07-17 01:24:21 +06:00
parent 7fd7c659b6
commit f962ecd7d6
2 changed files with 165 additions and 32 deletions

View File

@@ -6,15 +6,22 @@
* *
* This file is part of the 86Box distribution. * 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 * Authors: Teemu Korhonen
* Miran Grca, <mgrca8@gmail.com> * Miran Grca, <mgrca8@gmail.com>
* Sam Latinga
* Cacodemon345
* *
* Copyright 2021 Teemu Korhonen * Copyright 2021 Teemu Korhonen
* Copyright 2016-2018 Miran Grca. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
/* Mouse RawInput code taken from SDL3. */
#include "qt_winrawinputfilter.hpp" #include "qt_winrawinputfilter.hpp"
#include <QMenuBar> #include <QMenuBar>
@@ -94,30 +103,158 @@ bool windows_is_light_theme() {
return i == 1; 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); extern "C" void win_joystick_handle(PRAWINPUT);
std::unique_ptr<WindowsRawInputFilter> std::unique_ptr<WindowsRawInputFilter>
WindowsRawInputFilter::Register(MainWindow *window) WindowsRawInputFilter::Register(MainWindow *window)
{ {
RAWINPUTDEVICE rid[2] = { RAWINPUTDEVICE rid[1] = {
{ {
.usUsagePage = 0x01, .usUsagePage = 0x01,
.usUsage = 0x06, .usUsage = 0x06,
.dwFlags = RIDEV_NOHOTKEYS, .dwFlags = RIDEV_NOHOTKEYS,
.hwndTarget = nullptr .hwndTarget = nullptr
},
{
.usUsagePage = 0x01,
.usUsage = 0x02,
.dwFlags = 0,
.hwndTarget = nullptr
} }
}; };
if (hook_enabled && (RegisterRawInputDevices(&(rid[1]), 1, sizeof(rid[0])) == FALSE)) if (!hook_enabled) {
return std::unique_ptr<WindowsRawInputFilter>(nullptr); RegisterRawInputDevices(rid, 1, sizeof(rid[0]));
else if (!hook_enabled && (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE)) }
return std::unique_ptr<WindowsRawInputFilter>(nullptr);
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<WindowsRawInputFilter> inputfilter(new WindowsRawInputFilter(window)); std::unique_ptr<WindowsRawInputFilter> inputfilter(new WindowsRawInputFilter(window));
return inputfilter; return inputfilter;
@@ -135,25 +272,23 @@ WindowsRawInputFilter::WindowsRawInputFilter(MainWindow *window)
WindowsRawInputFilter::~WindowsRawInputFilter() 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, .usUsagePage = 0x01,
.usUsage = 0x06, .usUsage = 0x06,
.dwFlags = RIDEV_REMOVE, .dwFlags = RIDEV_REMOVE,
.hwndTarget = NULL .hwndTarget = NULL
}, };
{
.usUsagePage = 0x01,
.usUsage = 0x02,
.dwFlags = RIDEV_REMOVE,
.hwndTarget = NULL
}
};
if (hook_enabled) if (!hook_enabled)
RegisterRawInputDevices(&(rid[1]), 1, sizeof(rid[0])); RegisterRawInputDevices(&rid, 1, sizeof(rid));
else
RegisterRawInputDevices(rid, 2, sizeof(rid[0])); free(win_rawinput_data.rawinput);
} }
static void static void
@@ -305,10 +440,6 @@ WindowsRawInputFilter::handle_input(HRAWINPUT input)
case RIM_TYPEKEYBOARD: case RIM_TYPEKEYBOARD:
keyboard_handle(raw); keyboard_handle(raw);
break; break;
case RIM_TYPEMOUSE:
if (mouse_capture)
mouse_handle(raw);
break;
case RIM_TYPEHID: case RIM_TYPEHID:
win_joystick_handle(raw); win_joystick_handle(raw);
break; break;
@@ -328,9 +459,9 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw)
} }
void 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 x, delta_x;
static int y, delta_y; static int y, delta_y;
static int b, delta_z; static int b, delta_z;

View File

@@ -38,6 +38,7 @@
#include <QByteArray> #include <QByteArray>
#include <windows.h> #include <windows.h>
#include <windns.h>
#include <memory> #include <memory>
@@ -59,6 +60,8 @@ public:
~WindowsRawInputFilter(); ~WindowsRawInputFilter();
static void mouse_handle(RAWMOUSE* raw);
private: private:
MainWindow *window; MainWindow *window;
int buttons = 0; int buttons = 0;
@@ -71,7 +74,6 @@ private:
void handle_input(HRAWINPUT input); void handle_input(HRAWINPUT input);
void keyboard_handle(PRAWINPUT raw); void keyboard_handle(PRAWINPUT raw);
void mouse_handle(PRAWINPUT raw);
}; };
#endif #endif