Use a separate thread for polling mouse on Windows
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user