mirror of
https://github.com/glfw/glfw.git
synced 2025-10-03 21:30:57 +00:00
Add XInput2 tablet support
This commit is contained in:
parent
58d3c0fd45
commit
6ab20fa776
@ -490,6 +490,10 @@ static GLFWbool initExtensions(void)
|
|||||||
_glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion");
|
_glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion");
|
||||||
_glfw.x11.xi.SelectEvents = (PFN_XISelectEvents)
|
_glfw.x11.xi.SelectEvents = (PFN_XISelectEvents)
|
||||||
_glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents");
|
_glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents");
|
||||||
|
_glfw.x11.xi.QueryDevice = (PFN_XIQueryDevice)
|
||||||
|
_glfw_dlsym(_glfw.x11.xi.handle, "XIQueryDevice");
|
||||||
|
_glfw.x11.xi.GetProperty = (PFN_XIGetProperty)
|
||||||
|
_glfw_dlsym(_glfw.x11.xi.handle, "XIGetProperty");
|
||||||
|
|
||||||
if (XQueryExtension(_glfw.x11.display,
|
if (XQueryExtension(_glfw.x11.display,
|
||||||
"XInputExtension",
|
"XInputExtension",
|
||||||
@ -845,11 +849,99 @@ static int errorHandler(Display *display, XErrorEvent* event)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coordinate Transformation Matrix
|
||||||
|
//
|
||||||
|
static float * getDeviceCoordinateTransformationMatrix(Display *display, int deviceid)
|
||||||
|
{
|
||||||
|
float *data = NULL;
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
unsigned long num_items, bytes_after;
|
||||||
|
|
||||||
|
if (XIGetProperty(
|
||||||
|
display, deviceid,
|
||||||
|
XInternAtom(display, "Coordinate Transformation Matrix", False),
|
||||||
|
0, 9, False,
|
||||||
|
XInternAtom(display, "FLOAT", False),
|
||||||
|
&type, &format, &num_items, &bytes_after,
|
||||||
|
(unsigned char **)&data
|
||||||
|
) != Success)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XInput2 init pen tablet
|
||||||
|
//
|
||||||
|
static void initPenTablet(Display *display)
|
||||||
|
{
|
||||||
|
_glfw.x11.xi.stylus_deviceid = 0;
|
||||||
|
_glfw.x11.xi.eraser_deviceid = 0;
|
||||||
|
|
||||||
|
if (_glfw.x11.xi.available)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
XIDeviceInfo *dev_info = XIQueryDevice(display, XIAllDevices, &n);
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
XIDeviceInfo *dev = &dev_info[i];
|
||||||
|
|
||||||
|
if (strstr(dev->name, "stylus") || strstr(dev->name, "eraser"))
|
||||||
|
{
|
||||||
|
XIEventMask mask;
|
||||||
|
|
||||||
|
mask.deviceid = dev->deviceid;
|
||||||
|
mask.mask_len = XIMaskLen(XI_RawMotion);
|
||||||
|
mask.mask = calloc(mask.mask_len, sizeof(char));
|
||||||
|
XISetMask(mask.mask, XI_PropertyEvent); // property event to catch proximity change
|
||||||
|
XISetMask(mask.mask, XI_RawMotion); // raw motion to catch x, y, pressure and tilt
|
||||||
|
XISelectEvents(display, DefaultRootWindow(display), &mask, 1);
|
||||||
|
|
||||||
|
if (strstr(dev->name, "stylus"))
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
_glfw.x11.xi.stylus_deviceid = dev->deviceid;
|
||||||
|
|
||||||
|
for (c = 0; c < dev->num_classes; c++)
|
||||||
|
{
|
||||||
|
if (dev->classes[c]->type == XIValuatorClass)
|
||||||
|
{
|
||||||
|
XIValuatorClassInfo *v = (XIValuatorClassInfo*)dev->classes[c];
|
||||||
|
if (v->number < 32) _glfw.x11.xi.stylus_ClassInfo[v->number] = *v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_glfw.x11.xi.stylus_CTMatrix = getDeviceCoordinateTransformationMatrix(display, dev->deviceid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_glfw.x11.xi.eraser_deviceid = dev->deviceid;
|
||||||
|
_glfw.x11.xi.eraser_CTMatrix = getDeviceCoordinateTransformationMatrix(display, dev->deviceid);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(mask.mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW internal API //////
|
////// GLFW internal API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Apply Coordinate Transformation Matrix to a 2d vector
|
||||||
|
//
|
||||||
|
void _glfwApplyCoordTransformMatrix(float *matrix, float *px, float *py)
|
||||||
|
{
|
||||||
|
float x = *px;
|
||||||
|
float y = *py;
|
||||||
|
*px = x * matrix[0] + y * matrix[1] + matrix[2];
|
||||||
|
*py = x * matrix[3] + y * matrix[4] + matrix[5];
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the X error handler callback
|
// Sets the X error handler callback
|
||||||
//
|
//
|
||||||
void _glfwGrabErrorHandlerX11(void)
|
void _glfwGrabErrorHandlerX11(void)
|
||||||
@ -987,6 +1079,8 @@ int _glfwPlatformInit(void)
|
|||||||
_glfwInitTimerPOSIX();
|
_glfwInitTimerPOSIX();
|
||||||
|
|
||||||
_glfwPollMonitorsX11();
|
_glfwPollMonitorsX11();
|
||||||
|
|
||||||
|
initPenTablet(_glfw.x11.display);
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,8 +113,12 @@ typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*);
|
|||||||
|
|
||||||
typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*);
|
typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*);
|
||||||
typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
|
typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
|
||||||
|
typedef XIDeviceInfo* (* PFN_XIQueryDevice)(Display*,int,int*);
|
||||||
|
typedef Status (* PFN_XIGetProperty)(Display*,int,Atom,long,long,Bool,Atom,Atom*,int*,unsigned long*,unsigned long*,unsigned char**);
|
||||||
#define XIQueryVersion _glfw.x11.xi.QueryVersion
|
#define XIQueryVersion _glfw.x11.xi.QueryVersion
|
||||||
#define XISelectEvents _glfw.x11.xi.SelectEvents
|
#define XISelectEvents _glfw.x11.xi.SelectEvents
|
||||||
|
#define XIQueryDevice _glfw.x11.xi.QueryDevice
|
||||||
|
#define XIGetProperty _glfw.x11.xi.GetProperty
|
||||||
|
|
||||||
typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*);
|
typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*);
|
||||||
typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*);
|
typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*);
|
||||||
@ -385,6 +389,13 @@ typedef struct _GLFWlibraryX11
|
|||||||
int minor;
|
int minor;
|
||||||
PFN_XIQueryVersion QueryVersion;
|
PFN_XIQueryVersion QueryVersion;
|
||||||
PFN_XISelectEvents SelectEvents;
|
PFN_XISelectEvents SelectEvents;
|
||||||
|
PFN_XIQueryDevice QueryDevice;
|
||||||
|
PFN_XIGetProperty GetProperty;
|
||||||
|
XIValuatorClassInfo stylus_ClassInfo[32];
|
||||||
|
float *stylus_CTMatrix;
|
||||||
|
float *eraser_CTMatrix;
|
||||||
|
int stylus_deviceid;
|
||||||
|
int eraser_deviceid;
|
||||||
} xi;
|
} xi;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -436,6 +447,7 @@ unsigned long _glfwGetWindowPropertyX11(Window window,
|
|||||||
unsigned char** value);
|
unsigned char** value);
|
||||||
GLFWbool _glfwIsVisualTransparentX11(Visual* visual);
|
GLFWbool _glfwIsVisualTransparentX11(Visual* visual);
|
||||||
|
|
||||||
|
void _glfwApplyCoordTransformMatrix(float *matrix, float *px, float *py);
|
||||||
void _glfwGrabErrorHandlerX11(void);
|
void _glfwGrabErrorHandlerX11(void);
|
||||||
void _glfwReleaseErrorHandlerX11(void);
|
void _glfwReleaseErrorHandlerX11(void);
|
||||||
void _glfwInputErrorX11(int error, const char* message);
|
void _glfwInputErrorX11(int error, const char* message);
|
||||||
|
173
src/x11_window.c
173
src/x11_window.c
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -1192,33 +1193,165 @@ static void processEvent(XEvent *event)
|
|||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfw.x11.disabledCursorWindow;
|
_GLFWwindow* window = _glfw.x11.disabledCursorWindow;
|
||||||
|
|
||||||
if (window &&
|
if (event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
|
||||||
window->rawMouseMotion &&
|
XGetEventData(_glfw.x11.display, &event->xcookie))
|
||||||
event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
|
|
||||||
XGetEventData(_glfw.x11.display, &event->xcookie) &&
|
|
||||||
event->xcookie.evtype == XI_RawMotion)
|
|
||||||
{
|
{
|
||||||
XIRawEvent* re = event->xcookie.data;
|
if (event->xcookie.evtype == XI_PropertyEvent)
|
||||||
if (re->valuators.mask_len)
|
|
||||||
{
|
{
|
||||||
const double* values = re->raw_values;
|
// the first proximity event seems to arrive one later AFTER XI_RawMotion...
|
||||||
double xpos = window->virtualCursorPosX;
|
XIPropertyEvent* re = event->xcookie.data;
|
||||||
double ypos = window->virtualCursorPosY;
|
|
||||||
|
|
||||||
if (XIMaskIsSet(re->valuators.mask, 0))
|
// pen tablet property
|
||||||
|
if (re->deviceid == _glfw.x11.xi.stylus_deviceid ||
|
||||||
|
re->deviceid == _glfw.x11.xi.eraser_deviceid)
|
||||||
{
|
{
|
||||||
xpos += *values;
|
if (re->what == XIPropertyModified)
|
||||||
values++;
|
{
|
||||||
|
float *data = NULL;
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
unsigned long num_items, bytes_after;
|
||||||
|
|
||||||
|
if (XIGetProperty(
|
||||||
|
re->display, re->deviceid,
|
||||||
|
re->property,
|
||||||
|
0, 5, False,
|
||||||
|
XIAnyPropertyType,
|
||||||
|
&type, &format, &num_items, &bytes_after,
|
||||||
|
(unsigned char **)&data
|
||||||
|
) != Success)
|
||||||
|
data = NULL;
|
||||||
|
|
||||||
|
if (data && format == 32 && num_items > 4)
|
||||||
|
{
|
||||||
|
_glfwInputPenTabletProximity(((unsigned int *)data)[4] != 0);
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XIMaskIsSet(re->valuators.mask, 1))
|
|
||||||
ypos += *values;
|
|
||||||
|
|
||||||
_glfwInputCursorPos(window, xpos, ypos);
|
|
||||||
}
|
}
|
||||||
}
|
else if (event->xcookie.evtype == XI_RawMotion)
|
||||||
|
{
|
||||||
|
XIRawEvent* re = event->xcookie.data;
|
||||||
|
if (re->valuators.mask_len)
|
||||||
|
{
|
||||||
|
const double* values = re->raw_values;
|
||||||
|
|
||||||
XFreeEventData(_glfw.x11.display, &event->xcookie);
|
// pen tablet raw motion
|
||||||
|
if (re->deviceid == _glfw.x11.xi.stylus_deviceid ||
|
||||||
|
re->deviceid == _glfw.x11.xi.eraser_deviceid)
|
||||||
|
{
|
||||||
|
static unsigned int s_cursor = 0;
|
||||||
|
unsigned int cursor = re->deviceid == _glfw.x11.xi.stylus_deviceid ? 1 : 2;
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
double pressure = 0.0;
|
||||||
|
double tiltx = 0.0;
|
||||||
|
double tilty = 0.0;
|
||||||
|
|
||||||
|
if (cursor != s_cursor)
|
||||||
|
{
|
||||||
|
_glfwInputPenTabletCursor(cursor);
|
||||||
|
s_cursor = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XIMaskIsSet(re->valuators.mask, 0))
|
||||||
|
{
|
||||||
|
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[0];
|
||||||
|
x = (*values - v->min) / (v->max - v->min);
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XIMaskIsSet(re->valuators.mask, 1))
|
||||||
|
{
|
||||||
|
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[1];
|
||||||
|
y = (*values - v->min) / (v->max - v->min);
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XIMaskIsSet(re->valuators.mask, 2))
|
||||||
|
{
|
||||||
|
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[2];
|
||||||
|
pressure = (*values - v->min) / (v->max - v->min);
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XIMaskIsSet(re->valuators.mask, 3))
|
||||||
|
{
|
||||||
|
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[3];
|
||||||
|
tiltx = (*values - v->min) / (v->max - v->min) * 2.0 - 1.0;
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XIMaskIsSet(re->valuators.mask, 4))
|
||||||
|
{
|
||||||
|
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[4];
|
||||||
|
tilty = (*values - v->min) / (v->max - v->min) * 2.0 - 1.0;
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply coordinate transform matrix depending on current cursor
|
||||||
|
if (re->deviceid == _glfw.x11.xi.stylus_deviceid)
|
||||||
|
{
|
||||||
|
if (_glfw.x11.xi.stylus_CTMatrix)
|
||||||
|
_glfwApplyCoordTransformMatrix(_glfw.x11.xi.stylus_CTMatrix, &x, &y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_glfw.x11.xi.eraser_CTMatrix)
|
||||||
|
_glfwApplyCoordTransformMatrix(_glfw.x11.xi.eraser_CTMatrix, &x, &y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send data
|
||||||
|
{
|
||||||
|
double tx = tiltx * 1.5707963267949;
|
||||||
|
double ty = -tilty * 1.5707963267949;
|
||||||
|
double sinx = sin(tx);
|
||||||
|
double siny = sin(ty);
|
||||||
|
double cosx = cos(tx);
|
||||||
|
double cosy = cos(ty);
|
||||||
|
/*double matrix[9] = { // full matrix for reference
|
||||||
|
0.0, -cosy, siny,
|
||||||
|
cosx, -sinx*siny, -sinx*cosy,
|
||||||
|
sinx, cosx*siny, cosx*cosy
|
||||||
|
};*/
|
||||||
|
double v[3] = {sinx, cosx*siny, cosx*cosy};
|
||||||
|
double yaw = atan2(v[0], v[1]);
|
||||||
|
double pitch = 3.141592653589793 - acos(v[2]);
|
||||||
|
if (yaw < 0.0) yaw += 6.28318530717959;
|
||||||
|
|
||||||
|
_glfwInputPenTabletData(
|
||||||
|
(double)x * DisplayWidth(_glfw.x11.display, _glfw.x11.screen),
|
||||||
|
(double)y * DisplayHeight(_glfw.x11.display, _glfw.x11.screen),
|
||||||
|
0.0, // can't find z coordinate
|
||||||
|
pressure,
|
||||||
|
pitch,
|
||||||
|
yaw,
|
||||||
|
0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mouse raw motion
|
||||||
|
else if (window && window->rawMouseMotion)
|
||||||
|
{
|
||||||
|
double xpos = window->virtualCursorPosX;
|
||||||
|
double ypos = window->virtualCursorPosY;
|
||||||
|
|
||||||
|
if (XIMaskIsSet(re->valuators.mask, 0))
|
||||||
|
{
|
||||||
|
xpos += *values;
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XIMaskIsSet(re->valuators.mask, 1))
|
||||||
|
ypos += *values;
|
||||||
|
|
||||||
|
_glfwInputCursorPos(window, xpos, ypos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XFreeEventData(_glfw.x11.display, &event->xcookie);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user