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.x11.xi.SelectEvents = (PFN_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,
|
||||
"XInputExtension",
|
||||
@ -845,11 +849,99 @@ static int errorHandler(Display *display, XErrorEvent* event)
|
||||
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 //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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
|
||||
//
|
||||
void _glfwGrabErrorHandlerX11(void)
|
||||
@ -987,6 +1079,8 @@ int _glfwPlatformInit(void)
|
||||
_glfwInitTimerPOSIX();
|
||||
|
||||
_glfwPollMonitorsX11();
|
||||
|
||||
initPenTablet(_glfw.x11.display);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
|
@ -113,8 +113,12 @@ typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*);
|
||||
|
||||
typedef Status (* PFN_XIQueryVersion)(Display*,int*,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 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 Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*);
|
||||
@ -385,6 +389,13 @@ typedef struct _GLFWlibraryX11
|
||||
int minor;
|
||||
PFN_XIQueryVersion QueryVersion;
|
||||
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;
|
||||
|
||||
struct {
|
||||
@ -436,6 +447,7 @@ unsigned long _glfwGetWindowPropertyX11(Window window,
|
||||
unsigned char** value);
|
||||
GLFWbool _glfwIsVisualTransparentX11(Visual* visual);
|
||||
|
||||
void _glfwApplyCoordTransformMatrix(float *matrix, float *px, float *py);
|
||||
void _glfwGrabErrorHandlerX11(void);
|
||||
void _glfwReleaseErrorHandlerX11(void);
|
||||
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 <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -1192,33 +1193,165 @@ static void processEvent(XEvent *event)
|
||||
{
|
||||
_GLFWwindow* window = _glfw.x11.disabledCursorWindow;
|
||||
|
||||
if (window &&
|
||||
window->rawMouseMotion &&
|
||||
event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
|
||||
XGetEventData(_glfw.x11.display, &event->xcookie) &&
|
||||
event->xcookie.evtype == XI_RawMotion)
|
||||
if (event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
|
||||
XGetEventData(_glfw.x11.display, &event->xcookie))
|
||||
{
|
||||
XIRawEvent* re = event->xcookie.data;
|
||||
if (re->valuators.mask_len)
|
||||
if (event->xcookie.evtype == XI_PropertyEvent)
|
||||
{
|
||||
const double* values = re->raw_values;
|
||||
double xpos = window->virtualCursorPosX;
|
||||
double ypos = window->virtualCursorPosY;
|
||||
// the first proximity event seems to arrive one later AFTER XI_RawMotion...
|
||||
XIPropertyEvent* re = event->xcookie.data;
|
||||
|
||||
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;
|
||||
values++;
|
||||
if (re->what == XIPropertyModified)
|
||||
{
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user