Overhaul XInput2 handling to properly support absolute input devices, closes #2856
This commit is contained in:
@@ -11,11 +11,12 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Authors: Cacodemon345
|
* Authors: Cacodemon345
|
||||||
|
* RichardG <richardg867@gmail.com>
|
||||||
*
|
*
|
||||||
* Copyright 2022 Cacodemon345
|
* Copyright 2022 Cacodemon345.
|
||||||
|
* Copyright 2023 RichardG.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Valuator parsing and duplicate event checking code from SDL2. */
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
@@ -43,20 +44,19 @@ extern "C" {
|
|||||||
#include <86box/plat.h>
|
#include <86box/plat.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
int xi2flides[2] = { 0, 0 };
|
|
||||||
|
|
||||||
static Display *disp = nullptr;
|
static Display *disp = nullptr;
|
||||||
static QThread *procThread = nullptr;
|
static QThread *procThread = nullptr;
|
||||||
static XIEventMask ximask;
|
static XIEventMask ximask;
|
||||||
static std::atomic<bool> exitfromthread = false;
|
static std::atomic<bool> exitfromthread = false;
|
||||||
static std::atomic<double> xi2_mouse_x = 0, xi2_mouse_y = 0, xi2_mouse_abs_x = 0, xi2_mouse_abs_y = 0;
|
static std::atomic<double> xi2_mouse_x = 0, xi2_mouse_y = 0, xi2_mouse_abs_x = 0, xi2_mouse_abs_y = 0;
|
||||||
static int xi2opcode = 0;
|
static int xi2opcode = 0;
|
||||||
static double prev_rel_coords[2] = { 0., 0. };
|
static double prev_coords[2] = { 0.0 };
|
||||||
static Time prev_time = 0;
|
static Time prev_time = 0;
|
||||||
|
|
||||||
// From SDL2.
|
/* Based on SDL2. */
|
||||||
static void
|
static void
|
||||||
parse_valuators(const double *input_values, const unsigned char *mask, int mask_len,
|
parse_valuators(const double *input_values,
|
||||||
|
const unsigned char *mask, int mask_len,
|
||||||
double *output_values, int output_values_len)
|
double *output_values, int output_values_len)
|
||||||
{
|
{
|
||||||
int i = 0, z = 0;
|
int i = 0, z = 0;
|
||||||
@@ -64,11 +64,10 @@ parse_valuators(const double *input_values, const unsigned char *mask, int mask_
|
|||||||
if (top > 16)
|
if (top > 16)
|
||||||
top = 16;
|
top = 16;
|
||||||
|
|
||||||
memset(output_values, 0, output_values_len * sizeof(double));
|
memset(output_values, 0, output_values_len * sizeof(output_values[0]));
|
||||||
for (; i < top && z < output_values_len; i++) {
|
for (; i < top && z < output_values_len; i++) {
|
||||||
if (XIMaskIsSet(mask, i)) {
|
if (XIMaskIsSet(mask, i)) {
|
||||||
const int value = (int) *input_values;
|
output_values[z] = *input_values;
|
||||||
output_values[z] = value;
|
|
||||||
input_values++;
|
input_values++;
|
||||||
}
|
}
|
||||||
z++;
|
z++;
|
||||||
@@ -92,8 +91,7 @@ xinput2_proc()
|
|||||||
XISetMask(ximask.mask, XI_RawButtonPress);
|
XISetMask(ximask.mask, XI_RawButtonPress);
|
||||||
XISetMask(ximask.mask, XI_RawButtonRelease);
|
XISetMask(ximask.mask, XI_RawButtonRelease);
|
||||||
XISetMask(ximask.mask, XI_RawMotion);
|
XISetMask(ximask.mask, XI_RawMotion);
|
||||||
if (XKeysymToKeycode(disp, XK_Home) == 69)
|
XISetMask(ximask.mask, XI_Motion);
|
||||||
XISetMask(ximask.mask, XI_Motion);
|
|
||||||
|
|
||||||
XISelectEvents(disp, win, &ximask, 1);
|
XISelectEvents(disp, win, &ximask, 1);
|
||||||
|
|
||||||
@@ -103,23 +101,77 @@ xinput2_proc()
|
|||||||
XGenericEventCookie *cookie = (XGenericEventCookie *) &ev.xcookie;
|
XGenericEventCookie *cookie = (XGenericEventCookie *) &ev.xcookie;
|
||||||
XNextEvent(disp, (XEvent *) &ev);
|
XNextEvent(disp, (XEvent *) &ev);
|
||||||
|
|
||||||
if (XGetEventData(disp, cookie) && cookie->type == GenericEvent && cookie->extension == xi2opcode) {
|
if (XGetEventData(disp, cookie) && (cookie->type == GenericEvent) && (cookie->extension == xi2opcode)) {
|
||||||
|
const XIRawEvent *rawev = (const XIRawEvent *) cookie->data;
|
||||||
|
double coords[2] = { 0.0 };
|
||||||
|
|
||||||
switch (cookie->evtype) {
|
switch (cookie->evtype) {
|
||||||
|
case XI_Motion:
|
||||||
|
{
|
||||||
|
const XIDeviceEvent *devev = (const XIDeviceEvent *) cookie->data;
|
||||||
|
parse_valuators(devev->valuators.values, devev->valuators.mask, devev->valuators.mask_len, coords, 2);
|
||||||
|
|
||||||
|
/* XIDeviceEvent and XIRawEvent share the XIEvent base struct, which
|
||||||
|
doesn't contain deviceid, but that's at the same offset on both. */
|
||||||
|
goto common_motion;
|
||||||
|
}
|
||||||
|
|
||||||
case XI_RawMotion:
|
case XI_RawMotion:
|
||||||
{
|
{
|
||||||
const XIRawEvent *rawev = (const XIRawEvent *) cookie->data;
|
parse_valuators(rawev->raw_values, rawev->valuators.mask, rawev->valuators.mask_len, coords, 2);
|
||||||
double relative_coords[2] = { 0., 0. };
|
common_motion:
|
||||||
parse_valuators(rawev->raw_values, rawev->valuators.mask,
|
/* Ignore duplicated events. */
|
||||||
rawev->valuators.mask_len, relative_coords, 2);
|
if ((rawev->time == prev_time) && (coords[0] == prev_coords[0]) && (coords[1] == prev_coords[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
if ((rawev->time == prev_time) && (relative_coords[0] == prev_rel_coords[0]) && (relative_coords[1] == prev_rel_coords[1])) {
|
/* SDL2 queries the device on every event, so doing that should be fine. */
|
||||||
break; // Ignore duplicated events.
|
int i;
|
||||||
|
XIDeviceInfo *xidevinfo = XIQueryDevice(disp, rawev->deviceid, &i);
|
||||||
|
if (xidevinfo) {
|
||||||
|
/* Process the device's axes. */
|
||||||
|
int axis = 0;
|
||||||
|
for (i = 0; i < xidevinfo->num_classes; i++) {
|
||||||
|
const XIValuatorClassInfo *v = (const XIValuatorClassInfo *) xidevinfo->classes[i];
|
||||||
|
if (v->type == XIValuatorClass) {
|
||||||
|
/* Is this an absolute or relative axis? */
|
||||||
|
if (v->mode == XIModeRelative) {
|
||||||
|
/* Set relative coordinates. */
|
||||||
|
if (axis == 0)
|
||||||
|
xi2_mouse_x = xi2_mouse_x + coords[axis];
|
||||||
|
else
|
||||||
|
xi2_mouse_y = xi2_mouse_y + coords[axis];
|
||||||
|
} else {
|
||||||
|
/* Convert absolute value range to pixel granularity, then to relative coordinates. */
|
||||||
|
int disp_screen = XDefaultScreen(disp), abs_conv;
|
||||||
|
double abs_div;
|
||||||
|
if (axis == 0) {
|
||||||
|
abs_div = (v->max - v->min) / (double) XDisplayWidth(disp, disp_screen);
|
||||||
|
if (abs_div <= 0)
|
||||||
|
abs_div = 1;
|
||||||
|
abs_conv = (coords[axis] - v->min) / abs_div;
|
||||||
|
|
||||||
|
if (xi2_mouse_abs_x != 0)
|
||||||
|
xi2_mouse_x = xi2_mouse_x + (abs_conv - xi2_mouse_abs_x);
|
||||||
|
xi2_mouse_abs_x = abs_conv;
|
||||||
|
} else {
|
||||||
|
abs_div = (v->max - v->min) / (double) XDisplayHeight(disp, disp_screen);
|
||||||
|
if (abs_div <= 0)
|
||||||
|
abs_div = 1;
|
||||||
|
abs_conv = (coords[axis] - v->min) / abs_div;
|
||||||
|
|
||||||
|
if (xi2_mouse_abs_y != 0)
|
||||||
|
xi2_mouse_y = xi2_mouse_y + (abs_conv - xi2_mouse_abs_y);
|
||||||
|
xi2_mouse_abs_y = abs_conv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_coords[axis] = coords[axis];
|
||||||
|
if (++axis >= 2) /* stop after X and Y processed */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xi2_mouse_x = xi2_mouse_x + relative_coords[0];
|
|
||||||
xi2_mouse_y = xi2_mouse_y + relative_coords[1];
|
prev_time = rawev->time;
|
||||||
prev_rel_coords[0] = relative_coords[0];
|
|
||||||
prev_rel_coords[1] = relative_coords[1];
|
|
||||||
prev_time = rawev->time;
|
|
||||||
if (!mouse_capture)
|
if (!mouse_capture)
|
||||||
break;
|
break;
|
||||||
XWindowAttributes winattrib {};
|
XWindowAttributes winattrib {};
|
||||||
@@ -128,19 +180,8 @@ xinput2_proc()
|
|||||||
XWarpPointer(disp, XRootWindow(disp, XScreenNumberOfScreen(winattrib.screen)), XRootWindow(disp, XScreenNumberOfScreen(winattrib.screen)), 0, 0, 0, 0, globalPoint.x(), globalPoint.y());
|
XWarpPointer(disp, XRootWindow(disp, XScreenNumberOfScreen(winattrib.screen)), XRootWindow(disp, XScreenNumberOfScreen(winattrib.screen)), 0, 0, 0, 0, globalPoint.x(), globalPoint.y());
|
||||||
XFlush(disp);
|
XFlush(disp);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case XI_Motion:
|
break;
|
||||||
{
|
|
||||||
if (XKeysymToKeycode(disp, XK_Home) == 69) {
|
|
||||||
// No chance we will get raw motion events on VNC.
|
|
||||||
const XIDeviceEvent *motionev = (const XIDeviceEvent *) cookie->data;
|
|
||||||
if (xi2_mouse_abs_x != 0 || xi2_mouse_abs_y != 0) {
|
|
||||||
xi2_mouse_x = xi2_mouse_x + (motionev->event_x - xi2_mouse_abs_x);
|
|
||||||
xi2_mouse_y = xi2_mouse_y + (motionev->event_y - xi2_mouse_abs_y);
|
|
||||||
}
|
|
||||||
xi2_mouse_abs_x = motionev->event_x;
|
|
||||||
xi2_mouse_abs_y = motionev->event_y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user