Physics Simulation & Visualization Tool 0.1
A C++ physics simulation engine with real-time 3D visualization
Loading...
Searching...
No Matches
RawInputFilter.cpp
Go to the documentation of this file.
1#include "RawInputFilter.h"
2#include <QGuiApplication>
3#include <QWindow>
4#include <vector>
5#include <iostream>
6#include <QMetaObject>
7
8#ifdef _WIN32
9#include <windows.h>
10#elif defined(__linux__)
11#include <X11/Xlib.h>
12#include <X11/extensions/XInput2.h>
13#include <sys/select.h>
14#include <unistd.h>
15#endif
16
18 : mouseCallback(std::move(callback)) {}
19
21#ifdef __linux__
22 runX11Thread = false;
23 if (x11Thread.joinable()) {
24 x11Thread.join();
25 }
26#endif
27}
28
29bool RawInputFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) {
30#ifdef _WIN32
31 if (eventType != "windows_generic_MSG")
32 return false;
33
34 MSG* msg = static_cast<MSG*>(message);
35
36 if (!initialized && msg->hwnd) {
37 registerRawInput(msg->hwnd);
38 initialized = true;
39 }
40
41 if (msg->message == WM_INPUT) {
42 UINT dataSize = 0;
43 GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, nullptr, &dataSize, sizeof(RAWINPUTHEADER));
44 std::vector<BYTE> buffer(dataSize);
45 if (GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, buffer.data(), &dataSize, sizeof(RAWINPUTHEADER)) != dataSize)
46 return false;
47
48 RAWINPUT* raw = reinterpret_cast<RAWINPUT*>(buffer.data());
49 if (raw->header.dwType == RIM_TYPEMOUSE) {
50 if ((raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) == 0) {
51 int dx = raw->data.mouse.lLastX;
52 int dy = raw->data.mouse.lLastY;
53 if (mouseCallback) mouseCallback(dx, dy);
54 }
55 }
56 }
57 return false;
58#elif defined(__linux__)
59 if (!initialized) {
60 registerRawInput();
61 initialized = true;
62 }
63 Q_UNUSED(eventType)
64 Q_UNUSED(message)
65 Q_UNUSED(result)
66 return false;
67#elif defined(__APPLE__)
68 // TODO: Use CGEventTap to intercept mouse motion on macOS
69 Q_UNUSED(eventType)
70 Q_UNUSED(message)
71 Q_UNUSED(result)
72 return false;
73#else
74 return false;
75#endif
76}
77
78#ifdef _WIN32
79void RawInputFilter::registerRawInput(HWND hwnd) {
80 RAWINPUTDEVICE rid;
81 rid.usUsagePage = 0x01;
82 rid.usUsage = 0x02;
83 rid.dwFlags = RIDEV_INPUTSINK;
84 rid.hwndTarget = hwnd;
85
86 if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
87 std::cerr << "RawInputFilter: Failed to register raw input device\n";
88 } else {
89 std::cout << "RawInputFilter: Registered raw mouse input on window handle" << std::endl;
90 }
91}
92#elif defined(__linux__)
93void RawInputFilter::registerRawInput() {
94 x11Thread = std::thread([this]() {
95 Display* dpy = XOpenDisplay(nullptr);
96 if (!dpy) return;
97
98 int event, error;
99 if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) {
100 std::cerr << "RawInputFilter: XInput extension not available.\n";
101 XCloseDisplay(dpy);
102 return;
103 }
104
105 Window root = DefaultRootWindow(dpy);
106 XIEventMask evmask;
107 unsigned char mask[(XI_LASTEVENT + 7)/8] = { 0 };
108
109 evmask.deviceid = XIAllMasterDevices;
110 evmask.mask_len = sizeof(mask);
111 evmask.mask = mask;
112 XISetMask(mask, XI_RawMotion);
113
114 XISelectEvents(dpy, root, &evmask, 1);
115 XSync(dpy, False);
116
117 int fd = ConnectionNumber(dpy);
118 fd_set in_fds;
119 struct timeval tv;
120
121 while (runX11Thread) {
122 FD_ZERO(&in_fds);
123 FD_SET(fd, &in_fds);
124 tv.tv_sec = 0;
125 tv.tv_usec = 50000; // 50ms timeout
126
127 if (select(fd + 1, &in_fds, nullptr, nullptr, &tv) > 0) {
128 while (XPending(dpy)) {
129 XEvent ev;
130 XNextEvent(dpy, &ev);
131
132 if (ev.xcookie.type == GenericEvent && ev.xcookie.extension == xi_opcode) {
133 if (XGetEventData(dpy, &ev.xcookie)) {
134 if (ev.xcookie.evtype == XI_RawMotion) {
135 XIRawEvent* raw = (XIRawEvent*)ev.xcookie.data;
136 double dx = 0.0, dy = 0.0;
137 double* values = raw->raw_values;
138
139 if (XIMaskIsSet(raw->valuators.mask, 0)) {
140 dx = *values;
141 values++;
142 }
143 if (XIMaskIsSet(raw->valuators.mask, 1)) {
144 dy = *values;
145 }
146
147 if (mouseCallback && (dx != 0.0 || dy != 0.0)) {
148 int idx = static_cast<int>(dx);
149 int idy = static_cast<int>(dy);
150 QMetaObject::invokeMethod(qApp, [this, idx, idy]() {
151 mouseCallback(idx, idy);
152 }, Qt::QueuedConnection);
153 }
154 }
155 XFreeEventData(dpy, &ev.xcookie);
156 }
157 }
158 }
159 }
160 }
161 XCloseDisplay(dpy);
162 });
163 std::cout << "RawInputFilter: Registered XInput2 raw mouse on background thread.\n";
164}
165#endif
166
RawInputFilter(MouseCallback callback)
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override
~RawInputFilter() override
std::function< void(int dx, int dy)> MouseCallback
Hash specialization for Vertex to enable use in unordered containers.
Definition Mesh.h:33