1581 lines
61 KiB
C
1581 lines
61 KiB
C
/*
|
|
* X11 tablet driver
|
|
*
|
|
* Copyright 2003 CodeWeavers (Aric Stewart)
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winnls.h"
|
|
#include "x11drv.h"
|
|
#include "wine/unicode.h"
|
|
#include "wine/debug.h"
|
|
#include "wintab.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(wintab32);
|
|
|
|
#define WT_MAX_NAME_LEN 256
|
|
|
|
typedef struct tagWTI_CURSORS_INFO
|
|
{
|
|
WCHAR NAME[WT_MAX_NAME_LEN];
|
|
/* a displayable zero-terminated string containing the name of the
|
|
* cursor.
|
|
*/
|
|
BOOL ACTIVE;
|
|
/* whether the cursor is currently connected. */
|
|
WTPKT PKTDATA;
|
|
/* a bit mask indicating the packet data items supported when this
|
|
* cursor is connected.
|
|
*/
|
|
BYTE BUTTONS;
|
|
/* the number of buttons on this cursor. */
|
|
BYTE BUTTONBITS;
|
|
/* the number of bits of raw button data returned by the hardware.*/
|
|
DWORD cchBTNNAMES;
|
|
WCHAR *BTNNAMES;
|
|
/* a list of zero-terminated strings containing the names of the
|
|
* cursor's buttons. The number of names in the list is the same as the
|
|
* number of buttons on the cursor. The names are separated by a single
|
|
* zero character; the list is terminated by two zero characters.
|
|
*/
|
|
BYTE BUTTONMAP[32];
|
|
/* a 32 byte array of logical button numbers, one for each physical
|
|
* button.
|
|
*/
|
|
BYTE SYSBTNMAP[32];
|
|
/* a 32 byte array of button action codes, one for each logical
|
|
* button.
|
|
*/
|
|
BYTE NPBUTTON;
|
|
/* the physical button number of the button that is controlled by normal
|
|
* pressure.
|
|
*/
|
|
UINT NPBTNMARKS[2];
|
|
/* an array of two UINTs, specifying the button marks for the normal
|
|
* pressure button. The first UINT contains the release mark; the second
|
|
* contains the press mark.
|
|
*/
|
|
UINT *NPRESPONSE;
|
|
/* an array of UINTs describing the pressure response curve for normal
|
|
* pressure.
|
|
*/
|
|
BYTE TPBUTTON;
|
|
/* the physical button number of the button that is controlled by
|
|
* tangential pressure.
|
|
*/
|
|
UINT TPBTNMARKS[2];
|
|
/* an array of two UINTs, specifying the button marks for the tangential
|
|
* pressure button. The first UINT contains the release mark; the second
|
|
* contains the press mark.
|
|
*/
|
|
UINT *TPRESPONSE;
|
|
/* an array of UINTs describing the pressure response curve for
|
|
* tangential pressure.
|
|
*/
|
|
DWORD PHYSID;
|
|
/* a manufacturer-specific physical identifier for the cursor. This
|
|
* value will distinguish the physical cursor from others on the same
|
|
* device. This physical identifier allows applications to bind
|
|
* functions to specific physical cursors, even if category numbers
|
|
* change and multiple, otherwise identical, physical cursors are
|
|
* present.
|
|
*/
|
|
UINT MODE;
|
|
/* the cursor mode number of this cursor type, if this cursor type has
|
|
* the CRC_MULTIMODE capability.
|
|
*/
|
|
UINT MINPKTDATA;
|
|
/* the minimum set of data available from a physical cursor in this
|
|
* cursor type, if this cursor type has the CRC_AGGREGATE capability.
|
|
*/
|
|
UINT MINBUTTONS;
|
|
/* the minimum number of buttons of physical cursors in the cursor type,
|
|
* if this cursor type has the CRC_AGGREGATE capability.
|
|
*/
|
|
UINT CAPABILITIES;
|
|
/* flags indicating cursor capabilities, as defined below:
|
|
CRC_MULTIMODE
|
|
Indicates this cursor type describes one of several modes of a
|
|
single physical cursor. Consecutive cursor type categories
|
|
describe the modes; the CSR_MODE data item gives the mode number
|
|
of each cursor type.
|
|
CRC_AGGREGATE
|
|
Indicates this cursor type describes several physical cursors
|
|
that cannot be distinguished by software.
|
|
CRC_INVERT
|
|
Indicates this cursor type describes the physical cursor in its
|
|
inverted orientation; the previous consecutive cursor type
|
|
category describes the normal orientation.
|
|
*/
|
|
UINT TYPE;
|
|
/* Manufacturer Unique id for the item type */
|
|
} WTI_CURSORS_INFO, *LPWTI_CURSORS_INFO;
|
|
|
|
|
|
typedef struct tagWTI_DEVICES_INFO
|
|
{
|
|
WCHAR NAME[WT_MAX_NAME_LEN];
|
|
/* a displayable null- terminated string describing the device,
|
|
* manufacturer, and revision level.
|
|
*/
|
|
UINT HARDWARE;
|
|
/* flags indicating hardware and driver capabilities, as defined
|
|
* below:
|
|
HWC_INTEGRATED:
|
|
Indicates that the display and digitizer share the same surface.
|
|
HWC_TOUCH
|
|
Indicates that the cursor must be in physical contact with the
|
|
device to report position.
|
|
HWC_HARDPROX
|
|
Indicates that device can generate events when the cursor is
|
|
entering and leaving the physical detection range.
|
|
HWC_PHYSID_CURSORS
|
|
Indicates that device can uniquely identify the active cursor in
|
|
hardware.
|
|
*/
|
|
UINT NCSRTYPES;
|
|
/* the number of supported cursor types.*/
|
|
UINT FIRSTCSR;
|
|
/* the first cursor type number for the device. */
|
|
UINT PKTRATE;
|
|
/* the maximum packet report rate in Hertz. */
|
|
WTPKT PKTDATA;
|
|
/* a bit mask indicating which packet data items are always available.*/
|
|
WTPKT PKTMODE;
|
|
/* a bit mask indicating which packet data items are physically
|
|
* relative, i.e., items for which the hardware can only report change,
|
|
* not absolute measurement.
|
|
*/
|
|
WTPKT CSRDATA;
|
|
/* a bit mask indicating which packet data items are only available when
|
|
* certain cursors are connected. The individual cursor descriptions
|
|
* must be consulted to determine which cursors return which data.
|
|
*/
|
|
INT XMARGIN;
|
|
INT YMARGIN;
|
|
INT ZMARGIN;
|
|
/* the size of tablet context margins in tablet native coordinates, in
|
|
* the x, y, and z directions, respectively.
|
|
*/
|
|
AXIS X;
|
|
AXIS Y;
|
|
AXIS Z;
|
|
/* the tablet's range and resolution capabilities, in the x, y, and z
|
|
* axes, respectively.
|
|
*/
|
|
AXIS NPRESSURE;
|
|
AXIS TPRESSURE;
|
|
/* the tablet's range and resolution capabilities, for the normal and
|
|
* tangential pressure inputs, respectively.
|
|
*/
|
|
AXIS ORIENTATION[3];
|
|
/* a 3-element array describing the tablet's orientation range and
|
|
* resolution capabilities.
|
|
*/
|
|
AXIS ROTATION[3];
|
|
/* a 3-element array describing the tablet's rotation range and
|
|
* resolution capabilities.
|
|
*/
|
|
WCHAR PNPID[WT_MAX_NAME_LEN];
|
|
/* a null-terminated string containing the devices Plug and Play ID.*/
|
|
} WTI_DEVICES_INFO, *LPWTI_DEVICES_INFO;
|
|
|
|
|
|
/***********************************************************************
|
|
* WACOM WINTAB EXTENSIONS TO SUPPORT CSR_TYPE
|
|
* In Wintab 1.2, a CSR_TYPE feature was added. This adds the
|
|
* ability to return a type of cursor on a tablet.
|
|
* Unfortunately, we cannot get the cursor type directly from X,
|
|
* and it is not specified directly anywhere. So we virtualize
|
|
* the type here. (This is unfortunate, the kernel module has
|
|
* the exact type, but we have no way of getting that module to
|
|
* pass us that type).
|
|
*
|
|
* Reference linuxwacom driver project wcmCommon.c function
|
|
* idtotype for a much larger list of CSR_TYPE.
|
|
*
|
|
* http://linuxwacom.cvs.sourceforge.net/linuxwacom/linuxwacom-prod/src/xdrv/wcmCommon.c?view=markup
|
|
*
|
|
* The WTI_CURSORS_INFO.TYPE data is supposed to be used like this:
|
|
* (cursor.TYPE & 0x0F06) == target_cursor_type
|
|
* Reference: Section Unique ID
|
|
* http://www.wacomeng.com/devsupport/ibmpc/gddevpc.html
|
|
*/
|
|
#define CSR_TYPE_PEN 0x822
|
|
#define CSR_TYPE_ERASER 0x82a
|
|
#define CSR_TYPE_MOUSE_2D 0x007
|
|
#define CSR_TYPE_MOUSE_4D 0x094
|
|
/* CSR_TYPE_OTHER is a special value! assumed no real world significance
|
|
* if a stylus type or eraser type eventually have this value
|
|
* it'll be a bug. As of 2008 05 21 we can be sure because
|
|
* linux wacom lists all the known values and this isn't one of them */
|
|
#define CSR_TYPE_OTHER 0x000
|
|
|
|
typedef struct tagWTPACKET {
|
|
HCTX pkContext;
|
|
UINT pkStatus;
|
|
LONG pkTime;
|
|
WTPKT pkChanged;
|
|
UINT pkSerialNumber;
|
|
UINT pkCursor;
|
|
DWORD pkButtons;
|
|
DWORD pkX;
|
|
DWORD pkY;
|
|
DWORD pkZ;
|
|
UINT pkNormalPressure;
|
|
UINT pkTangentPressure;
|
|
ORIENTATION pkOrientation;
|
|
ROTATION pkRotation; /* 1.1 */
|
|
} WTPACKET, *LPWTPACKET;
|
|
|
|
|
|
#ifdef SONAME_LIBXI
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/extensions/XInput.h>
|
|
|
|
static int motion_type;
|
|
static int button_press_type;
|
|
static int button_release_type;
|
|
static int key_press_type;
|
|
static int key_release_type;
|
|
static int proximity_in_type;
|
|
static int proximity_out_type;
|
|
|
|
static HWND hwndTabletDefault;
|
|
static WTPACKET gMsgPacket;
|
|
static DWORD gSerial;
|
|
static WTPACKET last_packet;
|
|
|
|
/* Reference: http://www.wacomeng.com/devsupport/ibmpc/gddevpc.html
|
|
*
|
|
* Cursors come in sets of 3 normally
|
|
* Cursor #0 = puck device 1
|
|
* Cursor #1 = stylus device 1
|
|
* Cursor #2 = eraser device 1
|
|
* Cursor #3 = puck device 2
|
|
* Cursor #4 = stylus device 2
|
|
* Cursor #5 = eraser device 2
|
|
* etc....
|
|
*
|
|
* A dual tracking/multimode tablet is one
|
|
* that supports 2 independent cursors of the same or
|
|
* different types simultaneously on a single tablet.
|
|
* This makes our cursor layout potentially like this
|
|
* Cursor #0 = puck 1 device 1
|
|
* Cursor #1 = stylus 1 device 1
|
|
* Cursor #2 = eraser 1 device 1
|
|
* Cursor #3 = puck 2 device 1
|
|
* Cursor #4 = stylus 2 device 1
|
|
* Cursor #5 = eraser 2 device 1
|
|
* Cursor #6 = puck 1 device 2
|
|
* etc.....
|
|
*
|
|
* So with multimode tablets we could potentially need
|
|
* 2 slots of the same type per tablet i.e.
|
|
* you are using 2 styluses at once so they would
|
|
* get placed in Cursors #1 and Cursor #4
|
|
*
|
|
* Now say someone has 2 multimode tablets with 2 erasers each
|
|
* now we would need Cursor #2, #5, #8, #11
|
|
* So to support that we need CURSORMAX of 12 (0 to 11)
|
|
* FIXME: we don't support more than 4 regular tablets or 2 multimode tablets */
|
|
#define CURSORMAX 12
|
|
static INT button_state[CURSORMAX];
|
|
|
|
static LOGCONTEXTW gSysContext;
|
|
static WTI_DEVICES_INFO gSysDevice;
|
|
static WTI_CURSORS_INFO gSysCursor[CURSORMAX];
|
|
static INT gNumCursors; /* do NOT use this to iterate through gSysCursor slots */
|
|
|
|
|
|
/* XInput stuff */
|
|
static void *xinput_handle;
|
|
|
|
#define MAKE_FUNCPTR(f) static typeof(f) * p##f;
|
|
MAKE_FUNCPTR(XListInputDevices)
|
|
MAKE_FUNCPTR(XFreeDeviceList)
|
|
MAKE_FUNCPTR(XOpenDevice)
|
|
MAKE_FUNCPTR(XQueryDeviceState)
|
|
MAKE_FUNCPTR(XGetDeviceButtonMapping)
|
|
MAKE_FUNCPTR(XCloseDevice)
|
|
MAKE_FUNCPTR(XSelectExtensionEvent)
|
|
MAKE_FUNCPTR(XFreeDeviceState)
|
|
#undef MAKE_FUNCPTR
|
|
|
|
static INT X11DRV_XInput_Init(void)
|
|
{
|
|
xinput_handle = dlopen(SONAME_LIBXI, RTLD_NOW);
|
|
if (xinput_handle)
|
|
{
|
|
#define LOAD_FUNCPTR(f) if((p##f = dlsym(xinput_handle, #f)) == NULL) goto sym_not_found
|
|
LOAD_FUNCPTR(XListInputDevices);
|
|
LOAD_FUNCPTR(XFreeDeviceList);
|
|
LOAD_FUNCPTR(XOpenDevice);
|
|
LOAD_FUNCPTR(XGetDeviceButtonMapping);
|
|
LOAD_FUNCPTR(XCloseDevice);
|
|
LOAD_FUNCPTR(XSelectExtensionEvent);
|
|
LOAD_FUNCPTR(XQueryDeviceState);
|
|
LOAD_FUNCPTR(XFreeDeviceState);
|
|
#undef LOAD_FUNCPTR
|
|
return 1;
|
|
}
|
|
sym_not_found:
|
|
return 0;
|
|
}
|
|
|
|
static int Tablet_ErrorHandler(Display *dpy, XErrorEvent *event, void* arg)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void trace_axes(XValuatorInfoPtr val)
|
|
{
|
|
int i;
|
|
XAxisInfoPtr axis;
|
|
|
|
for (i = 0, axis = val->axes ; i < val->num_axes; i++, axis++)
|
|
TRACE(" Axis %d: [resolution %d|min_value %d|max_value %d]\n", i, axis->resolution, axis->min_value, axis->max_value);
|
|
}
|
|
|
|
static BOOL match_token(const char *haystack, const char *needle)
|
|
{
|
|
const char *p, *q;
|
|
for (p = haystack; *p; )
|
|
{
|
|
while (*p && isspace(*p))
|
|
p++;
|
|
if (! *p)
|
|
break;
|
|
|
|
for (q = needle; *q && *p && tolower(*p) == tolower(*q); q++)
|
|
p++;
|
|
if (! *q && (isspace(*p) || !*p))
|
|
return TRUE;
|
|
|
|
while (*p && ! isspace(*p))
|
|
p++;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Determining if an X device is a Tablet style device is an imperfect science.
|
|
** We rely on common conventions around device names as well as the type reported
|
|
** by Wacom tablets. This code will likely need to be expanded for alternate tablet types
|
|
**
|
|
** Wintab refers to any device that interacts with the tablet as a cursor,
|
|
** (stylus, eraser, tablet mouse, airbrush, etc)
|
|
** this is not to be confused with wacom x11 configuration "cursor" device.
|
|
** Wacoms x11 config "cursor" refers to its device slot (which we mirror with
|
|
** our gSysCursors) for puck like devices (tablet mice essentially).
|
|
*/
|
|
|
|
static BOOL is_tablet_cursor(const char *name, const char *type)
|
|
{
|
|
int i;
|
|
static const char *tablet_cursor_allowlist[] = {
|
|
"wacom",
|
|
"wizardpen",
|
|
"acecad",
|
|
"tablet",
|
|
"cursor",
|
|
"stylus",
|
|
"eraser",
|
|
"pad",
|
|
NULL
|
|
};
|
|
|
|
for (i=0; tablet_cursor_allowlist[i] != NULL; i++) {
|
|
if (name && match_token(name, tablet_cursor_allowlist[i]))
|
|
return TRUE;
|
|
if (type && match_token(type, tablet_cursor_allowlist[i]))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static UINT get_cursor_type(const char *name, const char *type)
|
|
{
|
|
int i;
|
|
static const char* tablet_stylus_allowlist[] = {
|
|
"stylus",
|
|
"wizardpen",
|
|
"acecad",
|
|
"pen",
|
|
NULL
|
|
};
|
|
|
|
/* First check device type to avoid cases where name is "Pen and Eraser" and type is "ERASER" */
|
|
for (i=0; tablet_stylus_allowlist[i] != NULL; i++) {
|
|
if (type && match_token(type, tablet_stylus_allowlist[i]))
|
|
return CSR_TYPE_PEN;
|
|
}
|
|
if (type && match_token(type, "eraser"))
|
|
return CSR_TYPE_ERASER;
|
|
for (i=0; tablet_stylus_allowlist[i] != NULL; i++) {
|
|
if (name && match_token(name, tablet_stylus_allowlist[i]))
|
|
return CSR_TYPE_PEN;
|
|
}
|
|
if (name && match_token(name, "eraser"))
|
|
return CSR_TYPE_ERASER;
|
|
|
|
return CSR_TYPE_OTHER;
|
|
}
|
|
|
|
/* cursors are placed in gSysCursor rows depending on their type
|
|
* see CURSORMAX comments for more detail */
|
|
static BOOL add_system_cursor(LPWTI_CURSORS_INFO cursor)
|
|
{
|
|
UINT offset = 0;
|
|
|
|
if (cursor->TYPE == CSR_TYPE_PEN)
|
|
offset = 1;
|
|
else if (cursor->TYPE == CSR_TYPE_ERASER)
|
|
offset = 2;
|
|
|
|
for (; offset < CURSORMAX; offset += 3)
|
|
{
|
|
if (!gSysCursor[offset].ACTIVE)
|
|
{
|
|
gSysCursor[offset] = *cursor;
|
|
++gNumCursors;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void disable_system_cursors(void)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0; i < CURSORMAX; ++i)
|
|
{
|
|
gSysCursor[i].ACTIVE = 0;
|
|
}
|
|
|
|
gNumCursors = 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* X11DRV_LoadTabletInfo (X11DRV.@)
|
|
*/
|
|
BOOL CDECL X11DRV_LoadTabletInfo(HWND hwnddefault)
|
|
{
|
|
static const WCHAR SZ_CONTEXT_NAME[] = {'W','i','n','e',' ','T','a','b','l','e','t',' ','C','o','n','t','e','x','t',0};
|
|
static const WCHAR SZ_DEVICE_NAME[] = {'W','i','n','e',' ','T','a','b','l','e','t',' ','D','e','v','i','c','e',0};
|
|
static const WCHAR SZ_NON_PLUG_N_PLAY[] = {'n','o','n','-','p','l','u','g','-','n','-','p','l','a','y',0};
|
|
|
|
struct x11drv_thread_data *data = x11drv_init_thread_data();
|
|
int num_devices;
|
|
int loop;
|
|
XDeviceInfo *devices;
|
|
XDeviceInfo *target = NULL;
|
|
BOOL axis_read_complete= FALSE;
|
|
|
|
XAnyClassPtr any;
|
|
XButtonInfoPtr Button;
|
|
XValuatorInfoPtr Val;
|
|
XAxisInfoPtr Axis;
|
|
|
|
XDevice *opendevice;
|
|
|
|
if (!X11DRV_XInput_Init())
|
|
{
|
|
ERR("Unable to initialize the XInput library.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
hwndTabletDefault = hwnddefault;
|
|
|
|
/* Do base initialization */
|
|
strcpyW(gSysContext.lcName, SZ_CONTEXT_NAME);
|
|
strcpyW(gSysDevice.NAME, SZ_DEVICE_NAME);
|
|
|
|
gSysContext.lcOptions = CXO_SYSTEM;
|
|
gSysContext.lcLocks = CXL_INSIZE | CXL_INASPECT | CXL_MARGIN |
|
|
CXL_SENSITIVITY | CXL_SYSOUT;
|
|
|
|
gSysContext.lcMsgBase= WT_DEFBASE;
|
|
gSysContext.lcDevice = 0;
|
|
gSysContext.lcPktData =
|
|
PK_CONTEXT | PK_STATUS | PK_SERIAL_NUMBER| PK_TIME | PK_CURSOR |
|
|
PK_BUTTONS | PK_X | PK_Y | PK_NORMAL_PRESSURE | PK_ORIENTATION;
|
|
gSysContext.lcMoveMask=
|
|
PK_BUTTONS | PK_X | PK_Y | PK_NORMAL_PRESSURE | PK_ORIENTATION;
|
|
gSysContext.lcStatus = CXS_ONTOP;
|
|
gSysContext.lcPktRate = 100;
|
|
gSysContext.lcBtnDnMask = 0xffffffff;
|
|
gSysContext.lcBtnUpMask = 0xffffffff;
|
|
gSysContext.lcSensX = 65536;
|
|
gSysContext.lcSensY = 65536;
|
|
gSysContext.lcSensX = 65536;
|
|
gSysContext.lcSensZ = 65536;
|
|
gSysContext.lcSysSensX= 65536;
|
|
gSysContext.lcSysSensY= 65536;
|
|
gSysContext.lcSysExtX = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
gSysContext.lcSysExtY = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
|
|
/* initialize cursors */
|
|
disable_system_cursors();
|
|
|
|
/* Device Defaults */
|
|
gSysDevice.HARDWARE = HWC_HARDPROX|HWC_PHYSID_CURSORS;
|
|
gSysDevice.FIRSTCSR= 0;
|
|
gSysDevice.PKTRATE = 100;
|
|
gSysDevice.PKTDATA =
|
|
PK_CONTEXT | PK_STATUS | PK_SERIAL_NUMBER| PK_TIME | PK_CURSOR |
|
|
PK_BUTTONS | PK_X | PK_Y | PK_NORMAL_PRESSURE | PK_ORIENTATION;
|
|
strcpyW(gSysDevice.PNPID, SZ_NON_PLUG_N_PLAY);
|
|
|
|
devices = pXListInputDevices(data->display, &num_devices);
|
|
if (!devices)
|
|
{
|
|
WARN("XInput Extensions reported as not available\n");
|
|
return FALSE;
|
|
}
|
|
TRACE("XListInputDevices reports %d devices\n", num_devices);
|
|
for (loop=0; loop < num_devices; loop++)
|
|
{
|
|
int class_loop;
|
|
char *device_type = devices[loop].type ? XGetAtomName(data->display, devices[loop].type) : NULL;
|
|
WTI_CURSORS_INFO cursor;
|
|
|
|
TRACE("Device %i: [id %d|name %s|type %s|num_classes %d|use %d]\n",
|
|
loop, (int) devices[loop].id, devices[loop].name, debugstr_a(device_type),
|
|
devices[loop].num_classes, devices[loop].use );
|
|
|
|
switch (devices[loop].use)
|
|
{
|
|
case IsXExtensionDevice:
|
|
#ifdef IsXExtensionPointer
|
|
case IsXExtensionPointer:
|
|
#endif
|
|
#ifdef IsXExtensionKeyboard
|
|
case IsXExtensionKeyboard:
|
|
#endif
|
|
TRACE("Is XExtension: Device, Keyboard, or Pointer\n");
|
|
target = &devices[loop];
|
|
|
|
if (strlen(target->name) >= WT_MAX_NAME_LEN)
|
|
{
|
|
ERR("Input device '%s' name too long - skipping\n", wine_dbgstr_a(target->name));
|
|
break;
|
|
}
|
|
|
|
X11DRV_expect_error(data->display, Tablet_ErrorHandler, NULL);
|
|
opendevice = pXOpenDevice(data->display,target->id);
|
|
if (!X11DRV_check_error() && opendevice)
|
|
{
|
|
unsigned char map[32];
|
|
int i;
|
|
int shft = 0;
|
|
|
|
X11DRV_expect_error(data->display,Tablet_ErrorHandler,NULL);
|
|
cursor.BUTTONS = pXGetDeviceButtonMapping(data->display, opendevice, map, 32);
|
|
if (X11DRV_check_error() || cursor.BUTTONS <= 0)
|
|
{
|
|
TRACE("No buttons, Non Tablet Device\n");
|
|
pXCloseDevice(data->display, opendevice);
|
|
break;
|
|
}
|
|
|
|
for (i=0; i< cursor.BUTTONS; i++,shft++)
|
|
{
|
|
cursor.BUTTONMAP[i] = map[i];
|
|
cursor.SYSBTNMAP[i] = (1<<shft);
|
|
}
|
|
pXCloseDevice(data->display, opendevice);
|
|
}
|
|
else
|
|
{
|
|
WARN("Unable to open device %s\n",target->name);
|
|
break;
|
|
}
|
|
MultiByteToWideChar(CP_UNIXCP, 0, target->name, -1, cursor.NAME, WT_MAX_NAME_LEN);
|
|
|
|
if (! is_tablet_cursor(target->name, device_type))
|
|
{
|
|
WARN("Skipping device %d [name %s|type %s]; not apparently a tablet cursor type device\n",
|
|
loop, devices[loop].name, debugstr_a(device_type));
|
|
break;
|
|
}
|
|
|
|
cursor.ACTIVE = 1;
|
|
cursor.PKTDATA = PK_TIME | PK_CURSOR | PK_BUTTONS | PK_X | PK_Y |
|
|
PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE |
|
|
PK_ORIENTATION;
|
|
|
|
cursor.PHYSID = target->id;
|
|
cursor.NPBUTTON = 1;
|
|
cursor.NPBTNMARKS[0] = 0 ;
|
|
cursor.NPBTNMARKS[1] = 1 ;
|
|
cursor.CAPABILITIES = CRC_MULTIMODE;
|
|
|
|
cursor.TYPE = get_cursor_type(target->name, device_type);
|
|
|
|
any = target->inputclassinfo;
|
|
|
|
for (class_loop = 0; class_loop < target->num_classes; class_loop++)
|
|
{
|
|
switch (any->class)
|
|
{
|
|
|
|
case ValuatorClass:
|
|
Val = (XValuatorInfoPtr) any;
|
|
TRACE(" ValidatorInput %d: [class %d|length %d|num_axes %d|mode %d|motion_buffer %ld]\n",
|
|
class_loop, (int) Val->class, Val->length, Val->num_axes, Val->mode, Val->motion_buffer);
|
|
if (TRACE_ON(wintab32))
|
|
trace_axes(Val);
|
|
|
|
/* FIXME: This is imperfect; we compute our devices capabilities based upon the
|
|
** first pen type device we find. However, a more correct implementation
|
|
** would require acquiring a wide variety of tablets and running through
|
|
** the various inputs to see what the values are. Odds are that a
|
|
** more 'correct' algorithm would condense to this one anyway.
|
|
*/
|
|
if (!axis_read_complete && cursor.TYPE == CSR_TYPE_PEN)
|
|
{
|
|
Axis = (XAxisInfoPtr) ((char *) Val + sizeof
|
|
(XValuatorInfo));
|
|
|
|
if (Val->num_axes>=1)
|
|
{
|
|
/* Axis 1 is X */
|
|
gSysDevice.X.axMin = Axis->min_value;
|
|
gSysDevice.X.axMax= Axis->max_value;
|
|
gSysDevice.X.axUnits = TU_INCHES;
|
|
gSysDevice.X.axResolution = Axis->resolution;
|
|
gSysContext.lcInOrgX = Axis->min_value;
|
|
gSysContext.lcOutOrgX = Axis->min_value;
|
|
gSysContext.lcInExtX = Axis->max_value;
|
|
gSysContext.lcOutExtX = Axis->max_value;
|
|
Axis++;
|
|
}
|
|
if (Val->num_axes>=2)
|
|
{
|
|
/* Axis 2 is Y */
|
|
gSysDevice.Y.axMin = Axis->min_value;
|
|
gSysDevice.Y.axMax= Axis->max_value;
|
|
gSysDevice.Y.axUnits = TU_INCHES;
|
|
gSysDevice.Y.axResolution = Axis->resolution;
|
|
gSysContext.lcInOrgY = Axis->min_value;
|
|
gSysContext.lcOutOrgY = Axis->min_value;
|
|
gSysContext.lcInExtY = Axis->max_value;
|
|
gSysContext.lcOutExtY = Axis->max_value;
|
|
Axis++;
|
|
}
|
|
if (Val->num_axes>=3)
|
|
{
|
|
/* Axis 3 is Normal Pressure */
|
|
gSysDevice.NPRESSURE.axMin = Axis->min_value;
|
|
gSysDevice.NPRESSURE.axMax= Axis->max_value;
|
|
gSysDevice.NPRESSURE.axUnits = TU_INCHES;
|
|
gSysDevice.NPRESSURE.axResolution =
|
|
Axis->resolution;
|
|
Axis++;
|
|
}
|
|
if (Val->num_axes >= 5)
|
|
{
|
|
/* Axis 4 and 5 are X and Y tilt */
|
|
XAxisInfoPtr XAxis = Axis;
|
|
Axis++;
|
|
if (max (abs(Axis->max_value),
|
|
abs(XAxis->max_value)))
|
|
{
|
|
gSysDevice.ORIENTATION[0].axMin = 0;
|
|
gSysDevice.ORIENTATION[0].axMax = 3600;
|
|
gSysDevice.ORIENTATION[0].axUnits = TU_CIRCLE;
|
|
gSysDevice.ORIENTATION[0].axResolution
|
|
= CASTFIX32(3600);
|
|
gSysDevice.ORIENTATION[1].axMin = -1000;
|
|
gSysDevice.ORIENTATION[1].axMax = 1000;
|
|
gSysDevice.ORIENTATION[1].axUnits = TU_CIRCLE;
|
|
gSysDevice.ORIENTATION[1].axResolution
|
|
= CASTFIX32(3600);
|
|
gSysDevice.ORIENTATION[2].axMin = 0;
|
|
gSysDevice.ORIENTATION[2].axMax = 3600;
|
|
gSysDevice.ORIENTATION[2].axUnits = TU_CIRCLE;
|
|
gSysDevice.ORIENTATION[2].axResolution
|
|
= CASTFIX32(3600);
|
|
Axis++;
|
|
}
|
|
}
|
|
axis_read_complete = TRUE;
|
|
}
|
|
break;
|
|
case ButtonClass:
|
|
{
|
|
int cchBuf = 512;
|
|
int cchPos = 0;
|
|
int i;
|
|
|
|
Button = (XButtonInfoPtr) any;
|
|
TRACE(" ButtonInput %d: [class %d|length %d|num_buttons %d]\n",
|
|
class_loop, (int) Button->class, Button->length, Button->num_buttons);
|
|
cursor.BTNNAMES = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*cchBuf);
|
|
for (i = 0; i < cursor.BUTTONS; i++)
|
|
{
|
|
/* FIXME - these names are probably incorrect */
|
|
int cch = strlenW(cursor.NAME) + 1;
|
|
while (cch > cchBuf - cchPos - 1) /* we want one extra byte for the last NUL */
|
|
{
|
|
cchBuf *= 2;
|
|
cursor.BTNNAMES = HeapReAlloc(GetProcessHeap(), 0, cursor.BTNNAMES, sizeof(WCHAR)*cchBuf);
|
|
}
|
|
|
|
strcpyW(cursor.BTNNAMES + cchPos, cursor.NAME);
|
|
cchPos += cch;
|
|
}
|
|
cursor.BTNNAMES[cchPos++] = 0;
|
|
cursor.BTNNAMES = HeapReAlloc(GetProcessHeap(), 0, cursor.BTNNAMES, sizeof(WCHAR)*cchPos);
|
|
cursor.cchBTNNAMES = cchPos;
|
|
}
|
|
break;
|
|
} /* switch any->class */
|
|
any = (XAnyClassPtr) ((char*) any + any->length);
|
|
} /* for class_loop */
|
|
if (!add_system_cursor(&cursor))
|
|
FIXME("Skipping this cursor due to lack of system cursor slots.\n");
|
|
break;
|
|
} /* switch devices.use */
|
|
XFree(device_type);
|
|
} /* for XListInputDevices */
|
|
pXFreeDeviceList(devices);
|
|
|
|
if (!axis_read_complete)
|
|
{
|
|
disable_system_cursors();
|
|
WARN("Did not find a valid stylus, unable to determine system context parameters. Wintab is disabled.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
gSysDevice.NCSRTYPES = gNumCursors;
|
|
return TRUE;
|
|
}
|
|
|
|
static int figure_deg(int x, int y)
|
|
{
|
|
float angle;
|
|
|
|
angle = atan2((float)y, (float)x);
|
|
angle += M_PI_2;
|
|
if (angle <= 0)
|
|
angle += 2 * M_PI;
|
|
|
|
return (0.5 + (angle * 1800.0 / M_PI));
|
|
}
|
|
|
|
static int get_button_state(int curnum)
|
|
{
|
|
return button_state[curnum];
|
|
}
|
|
|
|
static void set_button_state(int curnum, XID deviceid)
|
|
{
|
|
struct x11drv_thread_data *data = x11drv_thread_data();
|
|
XDevice *device;
|
|
XDeviceState *state;
|
|
XInputClass *class;
|
|
int loop;
|
|
int rc = 0;
|
|
|
|
device = pXOpenDevice(data->display,deviceid);
|
|
state = pXQueryDeviceState(data->display,device);
|
|
|
|
if (state)
|
|
{
|
|
class = state->data;
|
|
for (loop = 0; loop < state->num_classes; loop++)
|
|
{
|
|
if (class->class == ButtonClass)
|
|
{
|
|
int loop2;
|
|
XButtonState *button_state = (XButtonState*)class;
|
|
for (loop2 = 0; loop2 < button_state->num_buttons; loop2++)
|
|
{
|
|
if (button_state->buttons[loop2 / 8] & (1 << (loop2 % 8)))
|
|
{
|
|
rc |= (1<<loop2);
|
|
}
|
|
}
|
|
}
|
|
class = (XInputClass *) ((char *) class + class->length);
|
|
}
|
|
}
|
|
pXFreeDeviceState(state);
|
|
button_state[curnum] = rc;
|
|
}
|
|
|
|
static int cursor_from_device(DWORD deviceid, LPWTI_CURSORS_INFO *cursorp)
|
|
{
|
|
int i;
|
|
for (i = 0; i < CURSORMAX; i++)
|
|
if (gSysCursor[i].ACTIVE && gSysCursor[i].PHYSID == deviceid)
|
|
{
|
|
*cursorp = &gSysCursor[i];
|
|
return i;
|
|
}
|
|
|
|
ERR("Could not map device id %d to a cursor\n", (int) deviceid);
|
|
return -1;
|
|
}
|
|
|
|
static DWORD get_changed_state( WTPACKET *pkt)
|
|
{
|
|
DWORD change = 0;
|
|
|
|
if (pkt->pkX != last_packet.pkX)
|
|
change |= PK_X;
|
|
if (pkt->pkY != last_packet.pkY)
|
|
change |= PK_Y;
|
|
if (pkt->pkZ != last_packet.pkZ)
|
|
change |= PK_Z;
|
|
if (pkt->pkSerialNumber != last_packet.pkSerialNumber)
|
|
change |= PK_SERIAL_NUMBER;
|
|
if (pkt->pkTime != last_packet.pkTime)
|
|
change |= PK_TIME;
|
|
if (pkt->pkNormalPressure != last_packet.pkNormalPressure)
|
|
change |= PK_NORMAL_PRESSURE;
|
|
if (pkt->pkTangentPressure != last_packet.pkTangentPressure)
|
|
change |= PK_TANGENT_PRESSURE;
|
|
if (pkt->pkCursor != last_packet.pkCursor)
|
|
change |= PK_CURSOR;
|
|
if (pkt->pkButtons != last_packet.pkButtons)
|
|
change |= PK_BUTTONS;
|
|
if (pkt->pkOrientation.orAzimuth != last_packet.pkOrientation.orAzimuth ||
|
|
pkt->pkOrientation.orAltitude != last_packet.pkOrientation.orAltitude ||
|
|
pkt->pkOrientation.orTwist != last_packet.pkOrientation.orTwist)
|
|
change |= PK_ORIENTATION;
|
|
if (pkt->pkRotation.roPitch != last_packet.pkRotation.roPitch ||
|
|
pkt->pkRotation.roRoll != last_packet.pkRotation.roRoll ||
|
|
pkt->pkRotation.roYaw != last_packet.pkRotation.roYaw)
|
|
change |= PK_ROTATION;
|
|
|
|
return change;
|
|
}
|
|
|
|
static BOOL motion_event( HWND hwnd, XEvent *event )
|
|
{
|
|
XDeviceMotionEvent *motion = (XDeviceMotionEvent *)event;
|
|
LPWTI_CURSORS_INFO cursor;
|
|
int curnum = cursor_from_device(motion->deviceid, &cursor);
|
|
if (curnum < 0)
|
|
return FALSE;
|
|
|
|
memset(&gMsgPacket,0,sizeof(WTPACKET));
|
|
|
|
TRACE("Received tablet motion event (%p); device id %d, cursor num %d\n",hwnd, (int) motion->deviceid, curnum);
|
|
|
|
/* Set cursor to inverted if cursor is the eraser */
|
|
gMsgPacket.pkStatus = (cursor->TYPE == CSR_TYPE_ERASER ? TPS_INVERT:0);
|
|
gMsgPacket.pkTime = EVENT_x11_time_to_win32_time(motion->time);
|
|
gMsgPacket.pkSerialNumber = gSerial++;
|
|
gMsgPacket.pkCursor = curnum;
|
|
gMsgPacket.pkX = motion->axis_data[0];
|
|
gMsgPacket.pkY = motion->axis_data[1];
|
|
gMsgPacket.pkOrientation.orAzimuth = figure_deg(motion->axis_data[3],motion->axis_data[4]);
|
|
gMsgPacket.pkOrientation.orAltitude = ((1000 - 15 * max
|
|
(abs(motion->axis_data[3]),
|
|
abs(motion->axis_data[4])))
|
|
* (gMsgPacket.pkStatus & TPS_INVERT?-1:1));
|
|
gMsgPacket.pkNormalPressure = motion->axis_data[2];
|
|
gMsgPacket.pkButtons = get_button_state(curnum);
|
|
gMsgPacket.pkChanged = get_changed_state(&gMsgPacket);
|
|
SendMessageW(hwndTabletDefault,WT_PACKET,gMsgPacket.pkSerialNumber,(LPARAM)hwnd);
|
|
last_packet = gMsgPacket;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL button_event( HWND hwnd, XEvent *event )
|
|
{
|
|
XDeviceButtonEvent *button = (XDeviceButtonEvent *) event;
|
|
LPWTI_CURSORS_INFO cursor;
|
|
int curnum = cursor_from_device(button->deviceid, &cursor);
|
|
if (curnum < 0)
|
|
return FALSE;
|
|
|
|
memset(&gMsgPacket,0,sizeof(WTPACKET));
|
|
|
|
TRACE("Received tablet button %s event\n", (event->type == button_press_type)?"press":"release");
|
|
|
|
/* Set cursor to inverted if cursor is the eraser */
|
|
gMsgPacket.pkStatus = (cursor->TYPE == CSR_TYPE_ERASER ? TPS_INVERT:0);
|
|
set_button_state(curnum, button->deviceid);
|
|
gMsgPacket.pkTime = EVENT_x11_time_to_win32_time(button->time);
|
|
gMsgPacket.pkSerialNumber = gSerial++;
|
|
gMsgPacket.pkCursor = curnum;
|
|
if (button->axes_count > 0) {
|
|
gMsgPacket.pkX = button->axis_data[0];
|
|
gMsgPacket.pkY = button->axis_data[1];
|
|
gMsgPacket.pkOrientation.orAzimuth = figure_deg(button->axis_data[3],button->axis_data[4]);
|
|
gMsgPacket.pkOrientation.orAltitude = ((1000 - 15 * max(abs(button->axis_data[3]),
|
|
abs(button->axis_data[4])))
|
|
* (gMsgPacket.pkStatus & TPS_INVERT?-1:1));
|
|
gMsgPacket.pkNormalPressure = button->axis_data[2];
|
|
} else {
|
|
gMsgPacket.pkX = last_packet.pkX;
|
|
gMsgPacket.pkY = last_packet.pkY;
|
|
gMsgPacket.pkOrientation = last_packet.pkOrientation;
|
|
gMsgPacket.pkNormalPressure = last_packet.pkNormalPressure;
|
|
}
|
|
gMsgPacket.pkButtons = get_button_state(curnum);
|
|
gMsgPacket.pkChanged = get_changed_state(&gMsgPacket);
|
|
SendMessageW(hwndTabletDefault,WT_PACKET,gMsgPacket.pkSerialNumber,(LPARAM)hwnd);
|
|
last_packet = gMsgPacket;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL key_event( HWND hwnd, XEvent *event )
|
|
{
|
|
if (event->type == key_press_type)
|
|
FIXME("Received tablet key press event\n");
|
|
else
|
|
FIXME("Received tablet key release event\n");
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL proximity_event( HWND hwnd, XEvent *event )
|
|
{
|
|
XProximityNotifyEvent *proximity = (XProximityNotifyEvent *) event;
|
|
LPWTI_CURSORS_INFO cursor;
|
|
int curnum = cursor_from_device(proximity->deviceid, &cursor);
|
|
LPARAM proximity_info;
|
|
|
|
TRACE("hwnd=%p\n", hwnd);
|
|
|
|
if (curnum < 0)
|
|
return FALSE;
|
|
|
|
memset(&gMsgPacket,0,sizeof(WTPACKET));
|
|
|
|
/* Set cursor to inverted if cursor is the eraser */
|
|
gMsgPacket.pkStatus = (cursor->TYPE == CSR_TYPE_ERASER ? TPS_INVERT:0);
|
|
gMsgPacket.pkStatus |= (event->type==proximity_out_type)?TPS_PROXIMITY:0;
|
|
gMsgPacket.pkTime = EVENT_x11_time_to_win32_time(proximity->time);
|
|
gMsgPacket.pkSerialNumber = gSerial++;
|
|
gMsgPacket.pkCursor = curnum;
|
|
gMsgPacket.pkX = proximity->axis_data[0];
|
|
gMsgPacket.pkY = proximity->axis_data[1];
|
|
gMsgPacket.pkOrientation.orAzimuth = figure_deg(proximity->axis_data[3],proximity->axis_data[4]);
|
|
gMsgPacket.pkOrientation.orAltitude = ((1000 - 15 * max(abs(proximity->axis_data[3]),
|
|
abs(proximity->axis_data[4])))
|
|
* (gMsgPacket.pkStatus & TPS_INVERT?-1:1));
|
|
gMsgPacket.pkNormalPressure = proximity->axis_data[2];
|
|
gMsgPacket.pkButtons = get_button_state(curnum);
|
|
|
|
/* FIXME: LPARAM loword is true when cursor entering context, false when leaving context
|
|
* This needs to be handled here or in wintab32. Using the proximity_in_type is not correct
|
|
* but kept for now.
|
|
* LPARAM hiword is "non-zero when the cursor is leaving or entering hardware proximity"
|
|
* WPARAM contains context handle.
|
|
* HWND to HCTX is handled by wintab32.
|
|
*/
|
|
proximity_info = MAKELPARAM((event->type == proximity_in_type),
|
|
(event->type == proximity_in_type) || (event->type == proximity_out_type));
|
|
SendMessageW(hwndTabletDefault, WT_PROXIMITY, (WPARAM)hwnd, proximity_info);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* X11DRV_AttachEventQueueToTablet (X11DRV.@)
|
|
*/
|
|
int CDECL X11DRV_AttachEventQueueToTablet(HWND hOwner)
|
|
{
|
|
struct x11drv_thread_data *data = x11drv_init_thread_data();
|
|
int num_devices;
|
|
int loop;
|
|
int cur_loop;
|
|
XDeviceInfo *devices;
|
|
XDeviceInfo *target = NULL;
|
|
XDevice *the_device;
|
|
XEventClass event_list[7];
|
|
Window win = X11DRV_get_whole_window( hOwner );
|
|
|
|
if (!win || !xinput_handle) return 0;
|
|
|
|
TRACE("Creating context for window %p (%lx) %i cursors\n", hOwner, win, gNumCursors);
|
|
|
|
devices = pXListInputDevices(data->display, &num_devices);
|
|
|
|
X11DRV_expect_error(data->display,Tablet_ErrorHandler,NULL);
|
|
for (cur_loop=0; cur_loop < CURSORMAX; cur_loop++)
|
|
{
|
|
char cursorNameA[WT_MAX_NAME_LEN];
|
|
int event_number=0;
|
|
|
|
if (!gSysCursor[cur_loop].ACTIVE) continue;
|
|
|
|
/* the cursor name fits in the buffer because too long names are skipped */
|
|
WideCharToMultiByte(CP_UNIXCP, 0, gSysCursor[cur_loop].NAME, -1, cursorNameA, WT_MAX_NAME_LEN, NULL, NULL);
|
|
for (loop=0; loop < num_devices; loop ++)
|
|
if (strcmp(devices[loop].name, cursorNameA) == 0)
|
|
target = &devices[loop];
|
|
if (!target) {
|
|
WARN("Cursor Name %s not found in list of targets.\n", cursorNameA);
|
|
continue;
|
|
}
|
|
|
|
TRACE("Opening cursor %i id %i\n",cur_loop,(INT)target->id);
|
|
|
|
the_device = pXOpenDevice(data->display, target->id);
|
|
|
|
if (!the_device)
|
|
{
|
|
WARN("Unable to Open device\n");
|
|
continue;
|
|
}
|
|
|
|
if (the_device->num_classes > 0)
|
|
{
|
|
DeviceKeyPress(the_device, key_press_type, event_list[event_number]);
|
|
if (key_press_type) event_number++;
|
|
DeviceKeyRelease(the_device, key_release_type, event_list[event_number]);
|
|
if (key_release_type) event_number++;
|
|
DeviceButtonPress(the_device, button_press_type, event_list[event_number]);
|
|
if (button_press_type) event_number++;
|
|
DeviceButtonRelease(the_device, button_release_type, event_list[event_number]);
|
|
if (button_release_type) event_number++;
|
|
DeviceMotionNotify(the_device, motion_type, event_list[event_number]);
|
|
if (motion_type) event_number++;
|
|
ProximityIn(the_device, proximity_in_type, event_list[event_number]);
|
|
if (proximity_in_type) event_number++;
|
|
ProximityOut(the_device, proximity_out_type, event_list[event_number]);
|
|
if (proximity_out_type) event_number++;
|
|
|
|
if (key_press_type)
|
|
X11DRV_register_event_handler( key_press_type, key_event, "XInput KeyPress" );
|
|
if (key_release_type)
|
|
X11DRV_register_event_handler( key_release_type, key_event, "XInput KeyRelease" );
|
|
if (button_press_type)
|
|
X11DRV_register_event_handler( button_press_type, button_event, "XInput ButtonPress" );
|
|
if (button_release_type)
|
|
X11DRV_register_event_handler( button_release_type, button_event, "XInput ButtonRelease" );
|
|
if (motion_type)
|
|
X11DRV_register_event_handler( motion_type, motion_event, "XInput MotionNotify" );
|
|
if (proximity_in_type)
|
|
X11DRV_register_event_handler( proximity_in_type, proximity_event, "XInput ProximityIn" );
|
|
if (proximity_out_type)
|
|
X11DRV_register_event_handler( proximity_out_type, proximity_event, "XInput ProximityOut" );
|
|
|
|
pXSelectExtensionEvent(data->display, win, event_list, event_number);
|
|
}
|
|
}
|
|
XSync(data->display, False);
|
|
X11DRV_check_error();
|
|
|
|
if (NULL != devices) pXFreeDeviceList(devices);
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* X11DRV_GetCurrentPacket (X11DRV.@)
|
|
*/
|
|
int CDECL X11DRV_GetCurrentPacket(LPWTPACKET packet)
|
|
{
|
|
*packet = gMsgPacket;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static inline int CopyTabletData(LPVOID target, LPCVOID src, INT size)
|
|
{
|
|
/*
|
|
* It is valid to call CopyTabletData with NULL.
|
|
* This handles the WTInfo() case where lpOutput is null.
|
|
*/
|
|
if(target != NULL)
|
|
memcpy(target,src,size);
|
|
return(size);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* X11DRV_WTInfoW (X11DRV.@)
|
|
*/
|
|
UINT CDECL X11DRV_WTInfoW(UINT wCategory, UINT nIndex, LPVOID lpOutput)
|
|
{
|
|
/*
|
|
* It is valid to call WTInfoA with lpOutput == NULL, as per standard.
|
|
* lpOutput == NULL signifies the user only wishes
|
|
* to find the size of the data.
|
|
* NOTE:
|
|
* From now on use CopyTabletData to fill lpOutput. memcpy will break
|
|
* the code.
|
|
*/
|
|
int rc = 0;
|
|
LPWTI_CURSORS_INFO tgtcursor;
|
|
TRACE("(%u, %u, %p)\n", wCategory, nIndex, lpOutput);
|
|
|
|
if (!xinput_handle) return 0;
|
|
|
|
switch(wCategory)
|
|
{
|
|
case 0:
|
|
/* return largest necessary buffer */
|
|
TRACE("%i cursors\n",gNumCursors);
|
|
if (gNumCursors>0)
|
|
{
|
|
FIXME("Return proper size\n");
|
|
rc = 200;
|
|
}
|
|
break;
|
|
case WTI_INTERFACE:
|
|
switch (nIndex)
|
|
{
|
|
WORD version;
|
|
UINT num;
|
|
case IFC_WINTABID:
|
|
{
|
|
static const WCHAR driver[] = {'W','i','n','e',' ','W','i','n','t','a','b',' ','1','.','1',0};
|
|
rc = CopyTabletData(lpOutput, driver, (strlenW(driver) + 1) * sizeof(WCHAR));
|
|
break;
|
|
}
|
|
case IFC_SPECVERSION:
|
|
version = (0x01) | (0x01 << 8);
|
|
rc = CopyTabletData(lpOutput, &version,sizeof(WORD));
|
|
break;
|
|
case IFC_IMPLVERSION:
|
|
version = (0x00) | (0x01 << 8);
|
|
rc = CopyTabletData(lpOutput, &version,sizeof(WORD));
|
|
break;
|
|
case IFC_NDEVICES:
|
|
num = 1;
|
|
rc = CopyTabletData(lpOutput, &num,sizeof(num));
|
|
break;
|
|
case IFC_NCURSORS:
|
|
num = gNumCursors;
|
|
rc = CopyTabletData(lpOutput, &num,sizeof(num));
|
|
break;
|
|
default:
|
|
FIXME("WTI_INTERFACE unhandled index %i\n",nIndex);
|
|
rc = 0;
|
|
}
|
|
break;
|
|
case WTI_DEFSYSCTX:
|
|
case WTI_DDCTXS:
|
|
case WTI_DEFCONTEXT:
|
|
switch (nIndex)
|
|
{
|
|
case 0:
|
|
/* report 0 if wintab is disabled */
|
|
if (0 == gNumCursors)
|
|
rc = 0;
|
|
else
|
|
rc = CopyTabletData(lpOutput, &gSysContext,
|
|
sizeof(LOGCONTEXTW));
|
|
break;
|
|
case CTX_NAME:
|
|
rc = CopyTabletData(lpOutput, gSysContext.lcName,
|
|
(strlenW(gSysContext.lcName)+1) * sizeof(WCHAR));
|
|
break;
|
|
case CTX_OPTIONS:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcOptions,
|
|
sizeof(UINT));
|
|
break;
|
|
case CTX_STATUS:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcStatus,
|
|
sizeof(UINT));
|
|
break;
|
|
case CTX_LOCKS:
|
|
rc= CopyTabletData (lpOutput, &gSysContext.lcLocks,
|
|
sizeof(UINT));
|
|
break;
|
|
case CTX_MSGBASE:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcMsgBase,
|
|
sizeof(UINT));
|
|
break;
|
|
case CTX_DEVICE:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcDevice,
|
|
sizeof(UINT));
|
|
break;
|
|
case CTX_PKTRATE:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcPktRate,
|
|
sizeof(UINT));
|
|
break;
|
|
case CTX_PKTDATA:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcPktData,
|
|
sizeof(WTPKT));
|
|
break;
|
|
case CTX_PKTMODE:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcPktMode,
|
|
sizeof(WTPKT));
|
|
break;
|
|
case CTX_MOVEMASK:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcMoveMask,
|
|
sizeof(WTPKT));
|
|
break;
|
|
case CTX_BTNDNMASK:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcBtnDnMask,
|
|
sizeof(DWORD));
|
|
break;
|
|
case CTX_BTNUPMASK:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcBtnUpMask,
|
|
sizeof(DWORD));
|
|
break;
|
|
case CTX_INORGX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcInOrgX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_INORGY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcInOrgY,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_INORGZ:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcInOrgZ,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_INEXTX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcInExtX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_INEXTY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcInExtY,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_INEXTZ:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcInExtZ,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_OUTORGX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcOutOrgX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_OUTORGY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcOutOrgY,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_OUTORGZ:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcOutOrgZ,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_OUTEXTX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcOutExtX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_OUTEXTY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcOutExtY,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_OUTEXTZ:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcOutExtZ,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SENSX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSensX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SENSY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSensY,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SENSZ:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSensZ,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SYSMODE:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSysMode,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SYSORGX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSysOrgX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SYSORGY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSysOrgY,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SYSEXTX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSysExtX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SYSEXTY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSysExtY,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SYSSENSX:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSysSensX,
|
|
sizeof(LONG));
|
|
break;
|
|
case CTX_SYSSENSY:
|
|
rc = CopyTabletData(lpOutput, &gSysContext.lcSysSensY,
|
|
sizeof(LONG));
|
|
break;
|
|
default:
|
|
FIXME("WTI_DEFSYSCTX unhandled index %i\n",nIndex);
|
|
rc = 0;
|
|
}
|
|
break;
|
|
case WTI_CURSORS:
|
|
case WTI_CURSORS+1:
|
|
case WTI_CURSORS+2:
|
|
case WTI_CURSORS+3:
|
|
case WTI_CURSORS+4:
|
|
case WTI_CURSORS+5:
|
|
case WTI_CURSORS+6:
|
|
case WTI_CURSORS+7:
|
|
case WTI_CURSORS+8:
|
|
case WTI_CURSORS+9:
|
|
case WTI_CURSORS+10:
|
|
case WTI_CURSORS+11:
|
|
/* CURSORMAX == 12 */
|
|
/* FIXME: dynamic cursor support */
|
|
/* Apps will poll different slots to detect what cursors are available
|
|
* if there isn't a cursor for this slot return 0 */
|
|
if (!gSysCursor[wCategory - WTI_CURSORS].ACTIVE)
|
|
rc = 0;
|
|
else
|
|
{
|
|
tgtcursor = &gSysCursor[wCategory - WTI_CURSORS];
|
|
switch (nIndex)
|
|
{
|
|
case CSR_NAME:
|
|
rc = CopyTabletData(lpOutput, tgtcursor->NAME,
|
|
(strlenW(tgtcursor->NAME)+1) * sizeof(WCHAR));
|
|
break;
|
|
case CSR_ACTIVE:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->ACTIVE,
|
|
sizeof(BOOL));
|
|
break;
|
|
case CSR_PKTDATA:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->PKTDATA,
|
|
sizeof(WTPKT));
|
|
break;
|
|
case CSR_BUTTONS:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->BUTTONS,
|
|
sizeof(BYTE));
|
|
break;
|
|
case CSR_BUTTONBITS:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->BUTTONBITS,
|
|
sizeof(BYTE));
|
|
break;
|
|
case CSR_BTNNAMES:
|
|
FIXME("Button Names not returned correctly\n");
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->BTNNAMES,
|
|
tgtcursor->cchBTNNAMES*sizeof(WCHAR));
|
|
break;
|
|
case CSR_BUTTONMAP:
|
|
rc = CopyTabletData(lpOutput,tgtcursor->BUTTONMAP,
|
|
sizeof(BYTE)*32);
|
|
break;
|
|
case CSR_SYSBTNMAP:
|
|
rc = CopyTabletData(lpOutput,tgtcursor->SYSBTNMAP,
|
|
sizeof(BYTE)*32);
|
|
break;
|
|
case CSR_NPBTNMARKS:
|
|
rc = CopyTabletData(lpOutput,tgtcursor->NPBTNMARKS,
|
|
sizeof(UINT)*2);
|
|
break;
|
|
case CSR_NPBUTTON:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->NPBUTTON,
|
|
sizeof(BYTE));
|
|
break;
|
|
case CSR_NPRESPONSE:
|
|
FIXME("Not returning CSR_NPRESPONSE correctly\n");
|
|
rc = 0;
|
|
break;
|
|
case CSR_TPBUTTON:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->TPBUTTON,
|
|
sizeof(BYTE));
|
|
break;
|
|
case CSR_TPBTNMARKS:
|
|
rc = CopyTabletData(lpOutput,tgtcursor->TPBTNMARKS,
|
|
sizeof(UINT)*2);
|
|
break;
|
|
case CSR_TPRESPONSE:
|
|
FIXME("Not returning CSR_TPRESPONSE correctly\n");
|
|
rc = 0;
|
|
break;
|
|
case CSR_PHYSID:
|
|
{
|
|
DWORD id;
|
|
id = tgtcursor->PHYSID;
|
|
rc = CopyTabletData(lpOutput,&id,sizeof(DWORD));
|
|
}
|
|
break;
|
|
case CSR_MODE:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->MODE,sizeof(UINT));
|
|
break;
|
|
case CSR_MINPKTDATA:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->MINPKTDATA,
|
|
sizeof(UINT));
|
|
break;
|
|
case CSR_MINBUTTONS:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->MINBUTTONS,
|
|
sizeof(UINT));
|
|
break;
|
|
case CSR_CAPABILITIES:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->CAPABILITIES,
|
|
sizeof(UINT));
|
|
break;
|
|
case CSR_TYPE:
|
|
rc = CopyTabletData(lpOutput,&tgtcursor->TYPE,
|
|
sizeof(UINT));
|
|
break;
|
|
default:
|
|
FIXME("WTI_CURSORS unhandled index %i\n",nIndex);
|
|
rc = 0;
|
|
}
|
|
}
|
|
break;
|
|
case WTI_DEVICES:
|
|
switch (nIndex)
|
|
{
|
|
case DVC_NAME:
|
|
rc = CopyTabletData(lpOutput,gSysDevice.NAME,
|
|
(strlenW(gSysDevice.NAME)+1) * sizeof(WCHAR));
|
|
break;
|
|
case DVC_HARDWARE:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.HARDWARE,
|
|
sizeof(UINT));
|
|
break;
|
|
case DVC_NCSRTYPES:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.NCSRTYPES,
|
|
sizeof(UINT));
|
|
break;
|
|
case DVC_FIRSTCSR:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.FIRSTCSR,
|
|
sizeof(UINT));
|
|
break;
|
|
case DVC_PKTRATE:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.PKTRATE,
|
|
sizeof(UINT));
|
|
break;
|
|
case DVC_PKTDATA:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.PKTDATA,
|
|
sizeof(WTPKT));
|
|
break;
|
|
case DVC_PKTMODE:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.PKTMODE,
|
|
sizeof(WTPKT));
|
|
break;
|
|
case DVC_CSRDATA:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.CSRDATA,
|
|
sizeof(WTPKT));
|
|
break;
|
|
case DVC_XMARGIN:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.XMARGIN,
|
|
sizeof(INT));
|
|
break;
|
|
case DVC_YMARGIN:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.YMARGIN,
|
|
sizeof(INT));
|
|
break;
|
|
case DVC_ZMARGIN:
|
|
rc = 0; /* unsupported */
|
|
/*
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.ZMARGIN,
|
|
sizeof(INT));
|
|
*/
|
|
break;
|
|
case DVC_X:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.X,
|
|
sizeof(AXIS));
|
|
break;
|
|
case DVC_Y:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.Y,
|
|
sizeof(AXIS));
|
|
break;
|
|
case DVC_Z:
|
|
rc = 0; /* unsupported */
|
|
/*
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.Z,
|
|
sizeof(AXIS));
|
|
*/
|
|
break;
|
|
case DVC_NPRESSURE:
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.NPRESSURE,
|
|
sizeof(AXIS));
|
|
break;
|
|
case DVC_TPRESSURE:
|
|
rc = 0; /* unsupported */
|
|
/*
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.TPRESSURE,
|
|
sizeof(AXIS));
|
|
*/
|
|
break;
|
|
case DVC_ORIENTATION:
|
|
rc = CopyTabletData(lpOutput,gSysDevice.ORIENTATION,
|
|
sizeof(AXIS)*3);
|
|
break;
|
|
case DVC_ROTATION:
|
|
rc = 0; /* unsupported */
|
|
/*
|
|
rc = CopyTabletData(lpOutput,&gSysDevice.ROTATION,
|
|
sizeof(AXIS)*3);
|
|
*/
|
|
break;
|
|
case DVC_PNPID:
|
|
rc = CopyTabletData(lpOutput,gSysDevice.PNPID,
|
|
(strlenW(gSysDevice.PNPID)+1)*sizeof(WCHAR));
|
|
break;
|
|
default:
|
|
FIXME("WTI_DEVICES unhandled index %i\n",nIndex);
|
|
rc = 0;
|
|
}
|
|
break;
|
|
default:
|
|
FIXME("Unhandled Category %i\n",wCategory);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
#else /* SONAME_LIBXI */
|
|
|
|
/***********************************************************************
|
|
* AttachEventQueueToTablet (X11DRV.@)
|
|
*/
|
|
int CDECL X11DRV_AttachEventQueueToTablet(HWND hOwner)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetCurrentPacket (X11DRV.@)
|
|
*/
|
|
int CDECL X11DRV_GetCurrentPacket(LPWTPACKET packet)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* LoadTabletInfo (X11DRV.@)
|
|
*/
|
|
BOOL CDECL X11DRV_LoadTabletInfo(HWND hwnddefault)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTInfoW (X11DRV.@)
|
|
*/
|
|
UINT CDECL X11DRV_WTInfoW(UINT wCategory, UINT nIndex, LPVOID lpOutput)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif /* SONAME_LIBXI */
|