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.
*
* Windows raw input native filter for QT
* Windows raw input native filter for Qt
*
*
*
* Authors: Teemu Korhonen
* Miran Grca, <mgrca8@gmail.com>
* 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 <QMenuBar>
@@ -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>
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<WindowsRawInputFilter>(nullptr);
else if (!hook_enabled && (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE))
return std::unique_ptr<WindowsRawInputFilter>(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<WindowsRawInputFilter> 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;

View File

@@ -38,6 +38,7 @@
#include <QByteArray>
#include <windows.h>
#include <windns.h>
#include <memory>
@@ -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