4 * Copyright 2003 CodeWeavers (Aric Stewart)
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
31 #include "wine/library.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(wintab32
);
38 #define WT_MAX_NAME_LEN 256
40 typedef struct tagWTI_CURSORS_INFO
42 WCHAR NAME
[WT_MAX_NAME_LEN
];
43 /* a displayable zero-terminated string containing the name of the
47 /* whether the cursor is currently connected. */
49 /* a bit mask indicating the packet data items supported when this
50 * cursor is connected.
53 /* the number of buttons on this cursor. */
55 /* the number of bits of raw button data returned by the hardware.*/
58 /* a list of zero-terminated strings containing the names of the
59 * cursor's buttons. The number of names in the list is the same as the
60 * number of buttons on the cursor. The names are separated by a single
61 * zero character; the list is terminated by two zero characters.
64 /* a 32 byte array of logical button numbers, one for each physical
68 /* a 32 byte array of button action codes, one for each logical
72 /* the physical button number of the button that is controlled by normal
76 /* an array of two UINTs, specifying the button marks for the normal
77 * pressure button. The first UINT contains the release mark; the second
78 * contains the press mark.
81 /* an array of UINTs describing the pressure response curve for normal
85 /* the physical button number of the button that is controlled by
86 * tangential pressure.
89 /* an array of two UINTs, specifying the button marks for the tangential
90 * pressure button. The first UINT contains the release mark; the second
91 * contains the press mark.
94 /* an array of UINTs describing the pressure response curve for
95 * tangential pressure.
98 /* a manufacturer-specific physical identifier for the cursor. This
99 * value will distinguish the physical cursor from others on the same
100 * device. This physical identifier allows applications to bind
101 * functions to specific physical cursors, even if category numbers
102 * change and multiple, otherwise identical, physical cursors are
106 /* the cursor mode number of this cursor type, if this cursor type has
107 * the CRC_MULTIMODE capability.
110 /* the minimum set of data available from a physical cursor in this
111 * cursor type, if this cursor type has the CRC_AGGREGATE capability.
114 /* the minimum number of buttons of physical cursors in the cursor type,
115 * if this cursor type has the CRC_AGGREGATE capability.
118 /* flags indicating cursor capabilities, as defined below:
120 Indicates this cursor type describes one of several modes of a
121 single physical cursor. Consecutive cursor type categories
122 describe the modes; the CSR_MODE data item gives the mode number
125 Indicates this cursor type describes several physical cursors
126 that cannot be distinguished by software.
128 Indicates this cursor type describes the physical cursor in its
129 inverted orientation; the previous consecutive cursor type
130 category describes the normal orientation.
133 /* Manufacturer Unique id for the item type */
134 } WTI_CURSORS_INFO
, *LPWTI_CURSORS_INFO
;
137 typedef struct tagWTI_DEVICES_INFO
139 WCHAR NAME
[WT_MAX_NAME_LEN
];
140 /* a displayable null- terminated string describing the device,
141 * manufacturer, and revision level.
144 /* flags indicating hardware and driver capabilities, as defined
147 Indicates that the display and digitizer share the same surface.
149 Indicates that the cursor must be in physical contact with the
150 device to report position.
152 Indicates that device can generate events when the cursor is
153 entering and leaving the physical detection range.
155 Indicates that device can uniquely identify the active cursor in
159 /* the number of supported cursor types.*/
161 /* the first cursor type number for the device. */
163 /* the maximum packet report rate in Hertz. */
165 /* a bit mask indicating which packet data items are always available.*/
167 /* a bit mask indicating which packet data items are physically
168 * relative, i.e., items for which the hardware can only report change,
169 * not absolute measurement.
172 /* a bit mask indicating which packet data items are only available when
173 * certain cursors are connected. The individual cursor descriptions
174 * must be consulted to determine which cursors return which data.
179 /* the size of tablet context margins in tablet native coordinates, in
180 * the x, y, and z directions, respectively.
185 /* the tablet's range and resolution capabilities, in the x, y, and z
186 * axes, respectively.
190 /* the tablet's range and resolution capabilities, for the normal and
191 * tangential pressure inputs, respectively.
194 /* a 3-element array describing the tablet's orientation range and
195 * resolution capabilities.
198 /* a 3-element array describing the tablet's rotation range and
199 * resolution capabilities.
201 WCHAR PNPID
[WT_MAX_NAME_LEN
];
202 /* a null-terminated string containing the devices Plug and Play ID.*/
203 } WTI_DEVICES_INFO
, *LPWTI_DEVICES_INFO
;
206 /***********************************************************************
207 * WACOM WINTAB EXTENSIONS TO SUPPORT CSR_TYPE
208 * In Wintab 1.2, a CSR_TYPE feature was added. This adds the
209 * ability to return a type of cursor on a tablet.
210 * Unfortunately, we cannot get the cursor type directly from X,
211 * and it is not specified directly anywhere. So we virtualize
212 * the type here. (This is unfortunate, the kernel module has
213 * the exact type, but we have no way of getting that module to
214 * pass us that type).
216 * Reference linuxwacom driver project wcmCommon.c function
217 * idtotype for a much larger list of CSR_TYPE.
219 * http://linuxwacom.cvs.sourceforge.net/linuxwacom/linuxwacom-prod/src/xdrv/wcmCommon.c?view=markup
221 * The WTI_CURSORS_INFO.TYPE data is supposed to be used like this:
222 * (cursor.TYPE & 0x0F06) == target_cursor_type
223 * Reference: Section Unique ID
224 * http://www.wacomeng.com/devsupport/ibmpc/gddevpc.html
226 #define CSR_TYPE_PEN 0x822
227 #define CSR_TYPE_ERASER 0x82a
228 #define CSR_TYPE_MOUSE_2D 0x007
229 #define CSR_TYPE_MOUSE_4D 0x094
230 /* CSR_TYPE_OTHER is a special value! assumed no real world significance
231 * if a stylus type or eraser type eventually have this value
232 * it'll be a bug. As of 2008 05 21 we can be sure because
233 * linux wacom lists all the known values and this isn't one of them */
234 #define CSR_TYPE_OTHER 0x000
236 typedef struct tagWTPACKET
{
247 UINT pkNormalPressure
;
248 UINT pkTangentPressure
;
249 ORIENTATION pkOrientation
;
250 ROTATION pkRotation
; /* 1.1 */
251 } WTPACKET
, *LPWTPACKET
;
256 #include <X11/Xlib.h>
257 #include <X11/extensions/XInput.h>
259 static int motion_type
;
260 static int button_press_type
;
261 static int button_release_type
;
262 static int key_press_type
;
263 static int key_release_type
;
264 static int proximity_in_type
;
265 static int proximity_out_type
;
267 static HWND hwndTabletDefault
;
268 static WTPACKET gMsgPacket
;
269 static DWORD gSerial
;
271 /* Reference: http://www.wacomeng.com/devsupport/ibmpc/gddevpc.html
273 * Cursors come in sets of 3 normally
274 * Cursor #0 = puck device 1
275 * Cursor #1 = stylus device 1
276 * Cursor #2 = eraser device 1
277 * Cursor #3 = puck device 2
278 * Cursor #4 = stylus device 2
279 * Cursor #5 = eraser device 2
282 * A dual tracking/multimode tablet is one
283 * that supports 2 independent cursors of the same or
284 * different types simultaneously on a single tablet.
285 * This makes our cursor layout potentially like this
286 * Cursor #0 = puck 1 device 1
287 * Cursor #1 = stylus 1 device 1
288 * Cursor #2 = eraser 1 device 1
289 * Cursor #3 = puck 2 device 1
290 * Cursor #4 = stylus 2 device 1
291 * Cursor #5 = eraser 2 device 1
292 * Cursor #6 = puck 1 device 2
295 * So with multimode tablets we could potentially need
296 * 2 slots of the same type per tablet i.e.
297 * you are using 2 styluses at once so they would
298 * get placed in Cursors #1 and Cursor #4
300 * Now say someone has 2 multimode tablets with 2 erasers each
301 * now we would need Cursor #2, #5, #8, #11
302 * So to support that we need CURSORMAX of 12 (0 to 11)
303 * FIXME: we don't support more than 4 regular tablets or 2 multimode tablets */
305 static INT button_state
[CURSORMAX
];
307 static LOGCONTEXTW gSysContext
;
308 static WTI_DEVICES_INFO gSysDevice
;
309 static WTI_CURSORS_INFO gSysCursor
[CURSORMAX
];
310 static INT gNumCursors
; /* do NOT use this to iterate through gSysCursor slots */
314 static void *xinput_handle
;
316 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
317 MAKE_FUNCPTR(XListInputDevices
)
318 MAKE_FUNCPTR(XFreeDeviceList
)
319 MAKE_FUNCPTR(XOpenDevice
)
320 MAKE_FUNCPTR(XQueryDeviceState
)
321 MAKE_FUNCPTR(XGetDeviceButtonMapping
)
322 MAKE_FUNCPTR(XCloseDevice
)
323 MAKE_FUNCPTR(XSelectExtensionEvent
)
324 MAKE_FUNCPTR(XFreeDeviceState
)
327 static INT
X11DRV_XInput_Init(void)
329 xinput_handle
= wine_dlopen(SONAME_LIBXI
, RTLD_NOW
, NULL
, 0);
332 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xinput_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
333 LOAD_FUNCPTR(XListInputDevices
)
334 LOAD_FUNCPTR(XFreeDeviceList
)
335 LOAD_FUNCPTR(XOpenDevice
)
336 LOAD_FUNCPTR(XGetDeviceButtonMapping
)
337 LOAD_FUNCPTR(XCloseDevice
)
338 LOAD_FUNCPTR(XSelectExtensionEvent
)
339 LOAD_FUNCPTR(XQueryDeviceState
)
340 LOAD_FUNCPTR(XFreeDeviceState
)
348 static int Tablet_ErrorHandler(Display
*dpy
, XErrorEvent
*event
, void* arg
)
353 static void trace_axes(XValuatorInfoPtr val
)
358 for (i
= 0, axis
= val
->axes
; i
< val
->num_axes
; i
++, axis
++)
359 TRACE(" Axis %d: [resolution %d|min_value %d|max_value %d]\n", i
, axis
->resolution
, axis
->min_value
, axis
->max_value
);
362 static BOOL
match_token(const char *haystack
, const char *needle
)
365 for (p
= haystack
; *p
; )
367 while (*p
&& isspace(*p
))
372 for (q
= needle
; *q
&& *p
&& tolower(*p
) == tolower(*q
); q
++)
374 if (! *q
&& (isspace(*p
) || !*p
))
377 while (*p
&& ! isspace(*p
))
383 /* Determining if an X device is a Tablet style device is an imperfect science.
384 ** We rely on common conventions around device names as well as the type reported
385 ** by Wacom tablets. This code will likely need to be expanded for alternate tablet types
387 ** Wintab refers to any device that interacts with the tablet as a cursor,
388 ** (stylus, eraser, tablet mouse, airbrush, etc)
389 ** this is not to be confused with wacom x11 configuration "cursor" device.
390 ** Wacoms x11 config "cursor" refers to its device slot (which we mirror with
391 ** our gSysCursors) for puck like devices (tablet mice essentially).
394 static BOOL
is_tablet_cursor(const char *name
, const char *type
)
397 static const char *tablet_cursor_whitelist
[] = {
409 for (i
=0; tablet_cursor_whitelist
[i
] != NULL
; i
++) {
410 if (name
&& match_token(name
, tablet_cursor_whitelist
[i
]))
412 if (type
&& match_token(type
, tablet_cursor_whitelist
[i
]))
418 static UINT
get_cursor_type(const char *name
, const char *type
)
421 static const char* tablet_stylus_whitelist
[] = {
429 /* First check device type to avoid cases where name is "Pen and Eraser" and type is "ERASER" */
430 for (i
=0; tablet_stylus_whitelist
[i
] != NULL
; i
++) {
431 if (type
&& match_token(type
, tablet_stylus_whitelist
[i
]))
434 if (type
&& match_token(type
, "eraser"))
435 return CSR_TYPE_ERASER
;
436 for (i
=0; tablet_stylus_whitelist
[i
] != NULL
; i
++) {
437 if (name
&& match_token(name
, tablet_stylus_whitelist
[i
]))
440 if (name
&& match_token(name
, "eraser"))
441 return CSR_TYPE_ERASER
;
443 return CSR_TYPE_OTHER
;
446 /* cursors are placed in gSysCursor rows depending on their type
447 * see CURSORMAX comments for more detail */
448 static BOOL
add_system_cursor(LPWTI_CURSORS_INFO cursor
)
452 if (cursor
->TYPE
== CSR_TYPE_PEN
)
454 else if (cursor
->TYPE
== CSR_TYPE_ERASER
)
457 for (; offset
< CURSORMAX
; offset
+= 3)
459 if (!gSysCursor
[offset
].ACTIVE
)
461 gSysCursor
[offset
] = *cursor
;
470 static void disable_system_cursors(void)
474 for (i
= 0; i
< CURSORMAX
; ++i
)
476 gSysCursor
[i
].ACTIVE
= 0;
483 /***********************************************************************
484 * X11DRV_LoadTabletInfo (X11DRV.@)
486 BOOL CDECL
X11DRV_LoadTabletInfo(HWND hwnddefault
)
488 const WCHAR SZ_CONTEXT_NAME
[] = {'W','i','n','e',' ','T','a','b','l','e','t',' ','C','o','n','t','e','x','t',0};
489 const WCHAR SZ_DEVICE_NAME
[] = {'W','i','n','e',' ','T','a','b','l','e','t',' ','D','e','v','i','c','e',0};
490 const WCHAR SZ_NON_PLUGINPLAY
[] = {'n','o','n','-','p','l','u','g','i','n','p','l','a','y',0};
492 struct x11drv_thread_data
*data
= x11drv_init_thread_data();
495 XDeviceInfo
*devices
;
496 XDeviceInfo
*target
= NULL
;
497 BOOL axis_read_complete
= FALSE
;
500 XButtonInfoPtr Button
;
501 XValuatorInfoPtr Val
;
506 if (!X11DRV_XInput_Init())
508 ERR("Unable to initialize the XInput library.\n");
512 hwndTabletDefault
= hwnddefault
;
514 /* Do base initialization */
515 strcpyW(gSysContext
.lcName
, SZ_CONTEXT_NAME
);
516 strcpyW(gSysDevice
.NAME
, SZ_DEVICE_NAME
);
518 gSysContext
.lcOptions
= CXO_SYSTEM
;
519 gSysContext
.lcLocks
= CXL_INSIZE
| CXL_INASPECT
| CXL_MARGIN
|
520 CXL_SENSITIVITY
| CXL_SYSOUT
;
522 gSysContext
.lcMsgBase
= WT_DEFBASE
;
523 gSysContext
.lcDevice
= 0;
524 gSysContext
.lcPktData
=
525 PK_CONTEXT
| PK_STATUS
| PK_SERIAL_NUMBER
| PK_TIME
| PK_CURSOR
|
526 PK_BUTTONS
| PK_X
| PK_Y
| PK_NORMAL_PRESSURE
| PK_ORIENTATION
;
527 gSysContext
.lcMoveMask
=
528 PK_BUTTONS
| PK_X
| PK_Y
| PK_NORMAL_PRESSURE
| PK_ORIENTATION
;
529 gSysContext
.lcStatus
= CXS_ONTOP
;
530 gSysContext
.lcPktRate
= 100;
531 gSysContext
.lcBtnDnMask
= 0xffffffff;
532 gSysContext
.lcBtnUpMask
= 0xffffffff;
533 gSysContext
.lcSensX
= 65536;
534 gSysContext
.lcSensY
= 65536;
535 gSysContext
.lcSensX
= 65536;
536 gSysContext
.lcSensZ
= 65536;
537 gSysContext
.lcSysSensX
= 65536;
538 gSysContext
.lcSysSensY
= 65536;
539 gSysContext
.lcOutExtX
= GetSystemMetrics(SM_CXSCREEN
);
540 gSysContext
.lcOutExtY
= GetSystemMetrics(SM_CYSCREEN
);
542 /* initialize cursors */
543 disable_system_cursors();
545 /* Device Defaults */
546 gSysDevice
.HARDWARE
= HWC_HARDPROX
|HWC_PHYSID_CURSORS
;
547 gSysDevice
.FIRSTCSR
= 0;
548 gSysDevice
.PKTRATE
= 100;
550 PK_CONTEXT
| PK_STATUS
| PK_SERIAL_NUMBER
| PK_TIME
| PK_CURSOR
|
551 PK_BUTTONS
| PK_X
| PK_Y
| PK_NORMAL_PRESSURE
| PK_ORIENTATION
;
552 strcpyW(gSysDevice
.PNPID
, SZ_NON_PLUGINPLAY
);
554 devices
= pXListInputDevices(data
->display
, &num_devices
);
557 WARN("XInput Extensions reported as not available\n");
560 TRACE("XListInputDevices reports %d devices\n", num_devices
);
561 for (loop
=0; loop
< num_devices
; loop
++)
564 char *device_type
= devices
[loop
].type
? XGetAtomName(data
->display
, devices
[loop
].type
) : NULL
;
565 WTI_CURSORS_INFO cursor
;
567 TRACE("Device %i: [id %d|name %s|type %s|num_classes %d|use %d]\n",
568 loop
, (int) devices
[loop
].id
, devices
[loop
].name
, debugstr_a(device_type
),
569 devices
[loop
].num_classes
, devices
[loop
].use
);
571 switch (devices
[loop
].use
)
573 case IsXExtensionDevice
:
574 #ifdef IsXExtensionPointer
575 case IsXExtensionPointer
:
577 #ifdef IsXExtensionKeyboard
578 case IsXExtensionKeyboard
:
580 TRACE("Is XExtension: Device, Keyboard, or Pointer\n");
581 target
= &devices
[loop
];
583 if (strlen(target
->name
) >= WT_MAX_NAME_LEN
)
585 ERR("Input device '%s' name too long - skipping\n", wine_dbgstr_a(target
->name
));
589 X11DRV_expect_error(data
->display
, Tablet_ErrorHandler
, NULL
);
590 opendevice
= pXOpenDevice(data
->display
,target
->id
);
591 if (!X11DRV_check_error() && opendevice
)
593 unsigned char map
[32];
597 X11DRV_expect_error(data
->display
,Tablet_ErrorHandler
,NULL
);
598 cursor
.BUTTONS
= pXGetDeviceButtonMapping(data
->display
, opendevice
, map
, 32);
599 if (X11DRV_check_error() || cursor
.BUTTONS
<= 0)
601 TRACE("No buttons, Non Tablet Device\n");
602 pXCloseDevice(data
->display
, opendevice
);
606 for (i
=0; i
< cursor
.BUTTONS
; i
++,shft
++)
608 cursor
.BUTTONMAP
[i
] = map
[i
];
609 cursor
.SYSBTNMAP
[i
] = (1<<shft
);
611 pXCloseDevice(data
->display
, opendevice
);
615 WARN("Unable to open device %s\n",target
->name
);
618 MultiByteToWideChar(CP_UNIXCP
, 0, target
->name
, -1, cursor
.NAME
, WT_MAX_NAME_LEN
);
620 if (! is_tablet_cursor(target
->name
, device_type
))
622 WARN("Skipping device %d [name %s|type %s]; not apparently a tablet cursor type device. If this is wrong, please report it to wine-devel@winehq.org\n",
623 loop
, devices
[loop
].name
, debugstr_a(device_type
));
628 cursor
.PKTDATA
= PK_TIME
| PK_CURSOR
| PK_BUTTONS
| PK_X
| PK_Y
|
629 PK_NORMAL_PRESSURE
| PK_TANGENT_PRESSURE
|
632 cursor
.PHYSID
= target
->id
;
634 cursor
.NPBTNMARKS
[0] = 0 ;
635 cursor
.NPBTNMARKS
[1] = 1 ;
636 cursor
.CAPABILITIES
= CRC_MULTIMODE
;
638 cursor
.TYPE
= get_cursor_type(target
->name
, device_type
);
640 any
= target
->inputclassinfo
;
642 for (class_loop
= 0; class_loop
< target
->num_classes
; class_loop
++)
648 Val
= (XValuatorInfoPtr
) any
;
649 TRACE(" ValidatorInput %d: [class %d|length %d|num_axes %d|mode %d|motion_buffer %ld]\n",
650 class_loop
, (int) Val
->class, Val
->length
, Val
->num_axes
, Val
->mode
, Val
->motion_buffer
);
651 if (TRACE_ON(wintab32
))
654 /* FIXME: This is imperfect; we compute our devices capabilities based upon the
655 ** first pen type device we find. However, a more correct implementation
656 ** would require acquiring a wide variety of tablets and running through
657 ** the various inputs to see what the values are. Odds are that a
658 ** more 'correct' algorithm would condense to this one anyway.
660 if (!axis_read_complete
&& cursor
.TYPE
== CSR_TYPE_PEN
)
662 Axis
= (XAxisInfoPtr
) ((char *) Val
+ sizeof
665 if (Val
->num_axes
>=1)
668 gSysDevice
.X
.axMin
= Axis
->min_value
;
669 gSysDevice
.X
.axMax
= Axis
->max_value
;
670 gSysDevice
.X
.axUnits
= TU_INCHES
;
671 gSysDevice
.X
.axResolution
= Axis
->resolution
;
672 gSysContext
.lcInOrgX
= Axis
->min_value
;
673 gSysContext
.lcSysOrgX
= Axis
->min_value
;
674 gSysContext
.lcInExtX
= Axis
->max_value
;
675 gSysContext
.lcSysExtX
= Axis
->max_value
;
678 if (Val
->num_axes
>=2)
681 gSysDevice
.Y
.axMin
= Axis
->min_value
;
682 gSysDevice
.Y
.axMax
= Axis
->max_value
;
683 gSysDevice
.Y
.axUnits
= TU_INCHES
;
684 gSysDevice
.Y
.axResolution
= Axis
->resolution
;
685 gSysContext
.lcInOrgY
= Axis
->min_value
;
686 gSysContext
.lcSysOrgY
= Axis
->min_value
;
687 gSysContext
.lcInExtY
= Axis
->max_value
;
688 gSysContext
.lcSysExtY
= Axis
->max_value
;
691 if (Val
->num_axes
>=3)
693 /* Axis 3 is Normal Pressure */
694 gSysDevice
.NPRESSURE
.axMin
= Axis
->min_value
;
695 gSysDevice
.NPRESSURE
.axMax
= Axis
->max_value
;
696 gSysDevice
.NPRESSURE
.axUnits
= TU_INCHES
;
697 gSysDevice
.NPRESSURE
.axResolution
=
701 if (Val
->num_axes
>= 5)
703 /* Axis 4 and 5 are X and Y tilt */
704 XAxisInfoPtr XAxis
= Axis
;
706 if (max (abs(Axis
->max_value
),
707 abs(XAxis
->max_value
)))
709 gSysDevice
.ORIENTATION
[0].axMin
= 0;
710 gSysDevice
.ORIENTATION
[0].axMax
= 3600;
711 gSysDevice
.ORIENTATION
[0].axUnits
= TU_CIRCLE
;
712 gSysDevice
.ORIENTATION
[0].axResolution
714 gSysDevice
.ORIENTATION
[1].axMin
= -1000;
715 gSysDevice
.ORIENTATION
[1].axMax
= 1000;
716 gSysDevice
.ORIENTATION
[1].axUnits
= TU_CIRCLE
;
717 gSysDevice
.ORIENTATION
[1].axResolution
722 axis_read_complete
= TRUE
;
731 Button
= (XButtonInfoPtr
) any
;
732 TRACE(" ButtonInput %d: [class %d|length %d|num_buttons %d]\n",
733 class_loop
, (int) Button
->class, Button
->length
, Button
->num_buttons
);
734 cursor
.BTNNAMES
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*cchBuf
);
735 for (i
= 0; i
< cursor
.BUTTONS
; i
++)
737 /* FIXME - these names are probably incorrect */
738 int cch
= strlenW(cursor
.NAME
) + 1;
739 while (cch
> cchBuf
- cchPos
- 1) /* we want one extra byte for the last NUL */
742 cursor
.BTNNAMES
= HeapReAlloc(GetProcessHeap(), 0, cursor
.BTNNAMES
, sizeof(WCHAR
)*cchBuf
);
745 strcpyW(cursor
.BTNNAMES
+ cchPos
, cursor
.NAME
);
748 cursor
.BTNNAMES
[cchPos
++] = 0;
749 cursor
.BTNNAMES
= HeapReAlloc(GetProcessHeap(), 0, cursor
.BTNNAMES
, sizeof(WCHAR
)*cchPos
);
750 cursor
.cchBTNNAMES
= cchPos
;
753 } /* switch any->class */
754 any
= (XAnyClassPtr
) ((char*) any
+ any
->length
);
755 } /* for class_loop */
756 if (!add_system_cursor(&cursor
))
757 FIXME("Skipping this cursor due to lack of system cursor slots.\n");
759 } /* switch devices.use */
761 } /* for XListInputDevices */
762 pXFreeDeviceList(devices
);
764 if (axis_read_complete
)
765 gSysDevice
.NCSRTYPES
= gNumCursors
;
768 disable_system_cursors();
769 WARN("Did not find a valid stylus, unable to determine system context parameters. Wintab is disabled.\n");
775 static int figure_deg(int x
, int y
)
779 angle
= atan2((float)y
, (float)x
);
784 return (0.5 + (angle
* 1800.0 / M_PI
));
787 static int get_button_state(int curnum
)
789 return button_state
[curnum
];
792 static void set_button_state(int curnum
, XID deviceid
)
794 struct x11drv_thread_data
*data
= x11drv_thread_data();
801 device
= pXOpenDevice(data
->display
,deviceid
);
802 state
= pXQueryDeviceState(data
->display
,device
);
807 for (loop
= 0; loop
< state
->num_classes
; loop
++)
809 if (class->class == ButtonClass
)
812 XButtonState
*button_state
= (XButtonState
*)class;
813 for (loop2
= 0; loop2
< button_state
->num_buttons
; loop2
++)
815 if (button_state
->buttons
[loop2
/ 8] & (1 << (loop2
% 8)))
821 class = (XInputClass
*) ((char *) class + class->length
);
824 pXFreeDeviceState(state
);
825 button_state
[curnum
] = rc
;
828 static int cursor_from_device(DWORD deviceid
, LPWTI_CURSORS_INFO
*cursorp
)
831 for (i
= 0; i
< CURSORMAX
; i
++)
832 if (gSysCursor
[i
].ACTIVE
&& gSysCursor
[i
].PHYSID
== deviceid
)
834 *cursorp
= &gSysCursor
[i
];
838 ERR("Could not map device id %d to a cursor\n", (int) deviceid
);
842 static BOOL
motion_event( HWND hwnd
, XEvent
*event
)
844 XDeviceMotionEvent
*motion
= (XDeviceMotionEvent
*)event
;
845 LPWTI_CURSORS_INFO cursor
;
846 int curnum
= cursor_from_device(motion
->deviceid
, &cursor
);
850 memset(&gMsgPacket
,0,sizeof(WTPACKET
));
852 TRACE("Received tablet motion event (%p); device id %d, cursor num %d\n",hwnd
, (int) motion
->deviceid
, curnum
);
854 /* Set cursor to inverted if cursor is the eraser */
855 gMsgPacket
.pkStatus
= (cursor
->TYPE
== CSR_TYPE_ERASER
? TPS_INVERT
:0);
856 gMsgPacket
.pkTime
= EVENT_x11_time_to_win32_time(motion
->time
);
857 gMsgPacket
.pkSerialNumber
= gSerial
++;
858 gMsgPacket
.pkCursor
= curnum
;
859 gMsgPacket
.pkX
= motion
->axis_data
[0];
860 gMsgPacket
.pkY
= motion
->axis_data
[1];
861 gMsgPacket
.pkOrientation
.orAzimuth
= figure_deg(motion
->axis_data
[3],motion
->axis_data
[4]);
862 gMsgPacket
.pkOrientation
.orAltitude
= ((1000 - 15 * max
863 (abs(motion
->axis_data
[3]),
864 abs(motion
->axis_data
[4])))
865 * (gMsgPacket
.pkStatus
& TPS_INVERT
?-1:1));
866 gMsgPacket
.pkNormalPressure
= motion
->axis_data
[2];
867 gMsgPacket
.pkButtons
= get_button_state(curnum
);
868 SendMessageW(hwndTabletDefault
,WT_PACKET
,gMsgPacket
.pkSerialNumber
,(LPARAM
)hwnd
);
872 static BOOL
button_event( HWND hwnd
, XEvent
*event
)
874 XDeviceButtonEvent
*button
= (XDeviceButtonEvent
*) event
;
875 LPWTI_CURSORS_INFO cursor
;
876 int curnum
= cursor_from_device(button
->deviceid
, &cursor
);
880 memset(&gMsgPacket
,0,sizeof(WTPACKET
));
882 TRACE("Received tablet button %s event\n", (event
->type
== button_press_type
)?"press":"release");
884 /* Set cursor to inverted if cursor is the eraser */
885 gMsgPacket
.pkStatus
= (cursor
->TYPE
== CSR_TYPE_ERASER
? TPS_INVERT
:0);
886 set_button_state(curnum
, button
->deviceid
);
887 gMsgPacket
.pkTime
= EVENT_x11_time_to_win32_time(button
->time
);
888 gMsgPacket
.pkSerialNumber
= gSerial
++;
889 gMsgPacket
.pkCursor
= curnum
;
890 gMsgPacket
.pkX
= button
->axis_data
[0];
891 gMsgPacket
.pkY
= button
->axis_data
[1];
892 gMsgPacket
.pkOrientation
.orAzimuth
= figure_deg(button
->axis_data
[3],button
->axis_data
[4]);
893 gMsgPacket
.pkOrientation
.orAltitude
= ((1000 - 15 * max(abs(button
->axis_data
[3]),
894 abs(button
->axis_data
[4])))
895 * (gMsgPacket
.pkStatus
& TPS_INVERT
?-1:1));
896 gMsgPacket
.pkNormalPressure
= button
->axis_data
[2];
897 gMsgPacket
.pkButtons
= get_button_state(curnum
);
898 SendMessageW(hwndTabletDefault
,WT_PACKET
,gMsgPacket
.pkSerialNumber
,(LPARAM
)hwnd
);
902 static BOOL
key_event( HWND hwnd
, XEvent
*event
)
904 if (event
->type
== key_press_type
)
905 FIXME("Received tablet key press event\n");
907 FIXME("Received tablet key release event\n");
911 static BOOL
proximity_event( HWND hwnd
, XEvent
*event
)
913 XProximityNotifyEvent
*proximity
= (XProximityNotifyEvent
*) event
;
914 LPWTI_CURSORS_INFO cursor
;
915 int curnum
= cursor_from_device(proximity
->deviceid
, &cursor
);
916 LPARAM proximity_info
;
918 TRACE("hwnd=%p\n", hwnd
);
923 memset(&gMsgPacket
,0,sizeof(WTPACKET
));
925 /* Set cursor to inverted if cursor is the eraser */
926 gMsgPacket
.pkStatus
= (cursor
->TYPE
== CSR_TYPE_ERASER
? TPS_INVERT
:0);
927 gMsgPacket
.pkStatus
|= (event
->type
==proximity_out_type
)?TPS_PROXIMITY
:0;
928 gMsgPacket
.pkTime
= EVENT_x11_time_to_win32_time(proximity
->time
);
929 gMsgPacket
.pkSerialNumber
= gSerial
++;
930 gMsgPacket
.pkCursor
= curnum
;
931 gMsgPacket
.pkX
= proximity
->axis_data
[0];
932 gMsgPacket
.pkY
= proximity
->axis_data
[1];
933 gMsgPacket
.pkOrientation
.orAzimuth
= figure_deg(proximity
->axis_data
[3],proximity
->axis_data
[4]);
934 gMsgPacket
.pkOrientation
.orAltitude
= ((1000 - 15 * max(abs(proximity
->axis_data
[3]),
935 abs(proximity
->axis_data
[4])))
936 * (gMsgPacket
.pkStatus
& TPS_INVERT
?-1:1));
937 gMsgPacket
.pkNormalPressure
= proximity
->axis_data
[2];
938 gMsgPacket
.pkButtons
= get_button_state(curnum
);
940 /* FIXME: LPARAM loword is true when cursor entering context, false when leaving context
941 * This needs to be handled here or in wintab32. Using the proximity_in_type is not correct
943 * LPARAM hiword is "non-zero when the cursor is leaving or entering hardware proximity"
944 * WPARAM contains context handle.
945 * HWND to HCTX is handled by wintab32.
947 proximity_info
= MAKELPARAM((event
->type
== proximity_in_type
),
948 (event
->type
== proximity_in_type
) || (event
->type
== proximity_out_type
));
949 SendMessageW(hwndTabletDefault
, WT_PROXIMITY
, (WPARAM
)hwnd
, proximity_info
);
953 /***********************************************************************
954 * X11DRV_AttachEventQueueToTablet (X11DRV.@)
956 int CDECL
X11DRV_AttachEventQueueToTablet(HWND hOwner
)
958 struct x11drv_thread_data
*data
= x11drv_init_thread_data();
962 XDeviceInfo
*devices
;
963 XDeviceInfo
*target
= NULL
;
965 XEventClass event_list
[7];
966 Window win
= X11DRV_get_whole_window( hOwner
);
968 if (!win
|| !xinput_handle
) return 0;
970 TRACE("Creating context for window %p (%lx) %i cursors\n", hOwner
, win
, gNumCursors
);
972 devices
= pXListInputDevices(data
->display
, &num_devices
);
974 X11DRV_expect_error(data
->display
,Tablet_ErrorHandler
,NULL
);
975 for (cur_loop
=0; cur_loop
< CURSORMAX
; cur_loop
++)
977 char cursorNameA
[WT_MAX_NAME_LEN
];
980 if (!gSysCursor
[cur_loop
].ACTIVE
) continue;
982 /* the cursor name fits in the buffer because too long names are skipped */
983 WideCharToMultiByte(CP_UNIXCP
, 0, gSysCursor
[cur_loop
].NAME
, -1, cursorNameA
, WT_MAX_NAME_LEN
, NULL
, NULL
);
984 for (loop
=0; loop
< num_devices
; loop
++)
985 if (strcmp(devices
[loop
].name
, cursorNameA
) == 0)
986 target
= &devices
[loop
];
988 WARN("Cursor Name %s not found in list of targets.\n", cursorNameA
);
992 TRACE("Opening cursor %i id %i\n",cur_loop
,(INT
)target
->id
);
994 the_device
= pXOpenDevice(data
->display
, target
->id
);
998 WARN("Unable to Open device\n");
1002 if (the_device
->num_classes
> 0)
1004 DeviceKeyPress(the_device
, key_press_type
, event_list
[event_number
]);
1005 if (key_press_type
) event_number
++;
1006 DeviceKeyRelease(the_device
, key_release_type
, event_list
[event_number
]);
1007 if (key_release_type
) event_number
++;
1008 DeviceButtonPress(the_device
, button_press_type
, event_list
[event_number
]);
1009 if (button_press_type
) event_number
++;
1010 DeviceButtonRelease(the_device
, button_release_type
, event_list
[event_number
]);
1011 if (button_release_type
) event_number
++;
1012 DeviceMotionNotify(the_device
, motion_type
, event_list
[event_number
]);
1013 if (motion_type
) event_number
++;
1014 ProximityIn(the_device
, proximity_in_type
, event_list
[event_number
]);
1015 if (proximity_in_type
) event_number
++;
1016 ProximityOut(the_device
, proximity_out_type
, event_list
[event_number
]);
1017 if (proximity_out_type
) event_number
++;
1020 X11DRV_register_event_handler( key_press_type
, key_event
, "XInput KeyPress" );
1021 if (key_release_type
)
1022 X11DRV_register_event_handler( key_release_type
, key_event
, "XInput KeyRelease" );
1023 if (button_press_type
)
1024 X11DRV_register_event_handler( button_press_type
, button_event
, "XInput ButtonPress" );
1025 if (button_release_type
)
1026 X11DRV_register_event_handler( button_release_type
, button_event
, "XInput ButtonRelease" );
1028 X11DRV_register_event_handler( motion_type
, motion_event
, "XInput MotionNotify" );
1029 if (proximity_in_type
)
1030 X11DRV_register_event_handler( proximity_in_type
, proximity_event
, "XInput ProximityIn" );
1031 if (proximity_out_type
)
1032 X11DRV_register_event_handler( proximity_out_type
, proximity_event
, "XInput ProximityOut" );
1034 pXSelectExtensionEvent(data
->display
, win
, event_list
, event_number
);
1037 XSync(data
->display
, False
);
1038 X11DRV_check_error();
1040 if (NULL
!= devices
) pXFreeDeviceList(devices
);
1044 /***********************************************************************
1045 * X11DRV_GetCurrentPacket (X11DRV.@)
1047 int CDECL
X11DRV_GetCurrentPacket(LPWTPACKET packet
)
1049 *packet
= gMsgPacket
;
1054 static inline int CopyTabletData(LPVOID target
, LPCVOID src
, INT size
)
1057 * It is valid to call CopyTabletData with NULL.
1058 * This handles the WTInfo() case where lpOutput is null.
1061 memcpy(target
,src
,size
);
1065 /***********************************************************************
1066 * X11DRV_WTInfoW (X11DRV.@)
1068 UINT CDECL
X11DRV_WTInfoW(UINT wCategory
, UINT nIndex
, LPVOID lpOutput
)
1071 * It is valid to call WTInfoA with lpOutput == NULL, as per standard.
1072 * lpOutput == NULL signifies the user only wishes
1073 * to find the size of the data.
1075 * From now on use CopyTabletData to fill lpOutput. memcpy will break
1079 LPWTI_CURSORS_INFO tgtcursor
;
1080 TRACE("(%u, %u, %p)\n", wCategory
, nIndex
, lpOutput
);
1082 if (!xinput_handle
) return 0;
1087 /* return largest necessary buffer */
1088 TRACE("%i cursors\n",gNumCursors
);
1091 FIXME("Return proper size\n");
1102 static const WCHAR driver
[] = {'W','i','n','e',' ','W','i','n','t','a','b',' ','1','.','1',0};
1103 rc
= CopyTabletData(lpOutput
, driver
, (strlenW(driver
) + 1) * sizeof(WCHAR
));
1106 case IFC_SPECVERSION
:
1107 version
= (0x01) | (0x01 << 8);
1108 rc
= CopyTabletData(lpOutput
, &version
,sizeof(WORD
));
1110 case IFC_IMPLVERSION
:
1111 version
= (0x00) | (0x01 << 8);
1112 rc
= CopyTabletData(lpOutput
, &version
,sizeof(WORD
));
1116 rc
= CopyTabletData(lpOutput
, &num
,sizeof(num
));
1120 rc
= CopyTabletData(lpOutput
, &num
,sizeof(num
));
1123 FIXME("WTI_INTERFACE unhandled index %i\n",nIndex
);
1129 case WTI_DEFCONTEXT
:
1133 /* report 0 if wintab is disabled */
1134 if (0 == gNumCursors
)
1137 rc
= CopyTabletData(lpOutput
, &gSysContext
,
1138 sizeof(LOGCONTEXTW
));
1141 rc
= CopyTabletData(lpOutput
, gSysContext
.lcName
,
1142 (strlenW(gSysContext
.lcName
)+1) * sizeof(WCHAR
));
1145 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOptions
,
1149 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcStatus
,
1153 rc
= CopyTabletData (lpOutput
, &gSysContext
.lcLocks
,
1157 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcMsgBase
,
1161 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcDevice
,
1165 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcPktRate
,
1169 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcPktData
,
1173 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcPktMode
,
1177 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcMoveMask
,
1181 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcBtnDnMask
,
1185 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcBtnUpMask
,
1189 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInOrgX
,
1193 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInOrgY
,
1197 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInOrgZ
,
1201 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInExtX
,
1205 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInExtY
,
1209 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInExtZ
,
1213 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutOrgX
,
1217 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutOrgY
,
1221 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutOrgZ
,
1225 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutExtX
,
1229 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutExtY
,
1233 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutExtZ
,
1237 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSensX
,
1241 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSensY
,
1245 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSensZ
,
1249 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysMode
,
1253 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysOrgX
,
1257 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysOrgY
,
1261 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysExtX
,
1265 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysExtY
,
1269 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysSensX
,
1273 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysSensY
,
1277 FIXME("WTI_DEFSYSCTX unhandled index %i\n",nIndex
);
1291 case WTI_CURSORS
+10:
1292 case WTI_CURSORS
+11:
1293 /* CURSORMAX == 12 */
1294 /* FIXME: dynamic cursor support */
1295 /* Apps will poll different slots to detect what cursors are available
1296 * if there isn't a cursor for this slot return 0 */
1297 if (!gSysCursor
[wCategory
- WTI_CURSORS
].ACTIVE
)
1301 tgtcursor
= &gSysCursor
[wCategory
- WTI_CURSORS
];
1305 rc
= CopyTabletData(lpOutput
, tgtcursor
->NAME
,
1306 (strlenW(tgtcursor
->NAME
)+1) * sizeof(WCHAR
));
1309 rc
= CopyTabletData(lpOutput
,&tgtcursor
->ACTIVE
,
1313 rc
= CopyTabletData(lpOutput
,&tgtcursor
->PKTDATA
,
1317 rc
= CopyTabletData(lpOutput
,&tgtcursor
->BUTTONS
,
1320 case CSR_BUTTONBITS
:
1321 rc
= CopyTabletData(lpOutput
,&tgtcursor
->BUTTONBITS
,
1325 FIXME("Button Names not returned correctly\n");
1326 rc
= CopyTabletData(lpOutput
,&tgtcursor
->BTNNAMES
,
1327 tgtcursor
->cchBTNNAMES
*sizeof(WCHAR
));
1330 rc
= CopyTabletData(lpOutput
,tgtcursor
->BUTTONMAP
,
1334 rc
= CopyTabletData(lpOutput
,tgtcursor
->SYSBTNMAP
,
1337 case CSR_NPBTNMARKS
:
1338 rc
= CopyTabletData(lpOutput
,tgtcursor
->NPBTNMARKS
,
1342 rc
= CopyTabletData(lpOutput
,&tgtcursor
->NPBUTTON
,
1345 case CSR_NPRESPONSE
:
1346 FIXME("Not returning CSR_NPRESPONSE correctly\n");
1350 rc
= CopyTabletData(lpOutput
,&tgtcursor
->TPBUTTON
,
1353 case CSR_TPBTNMARKS
:
1354 rc
= CopyTabletData(lpOutput
,tgtcursor
->TPBTNMARKS
,
1357 case CSR_TPRESPONSE
:
1358 FIXME("Not returning CSR_TPRESPONSE correctly\n");
1364 id
= tgtcursor
->PHYSID
;
1365 rc
= CopyTabletData(lpOutput
,&id
,sizeof(DWORD
));
1369 rc
= CopyTabletData(lpOutput
,&tgtcursor
->MODE
,sizeof(UINT
));
1371 case CSR_MINPKTDATA
:
1372 rc
= CopyTabletData(lpOutput
,&tgtcursor
->MINPKTDATA
,
1375 case CSR_MINBUTTONS
:
1376 rc
= CopyTabletData(lpOutput
,&tgtcursor
->MINBUTTONS
,
1379 case CSR_CAPABILITIES
:
1380 rc
= CopyTabletData(lpOutput
,&tgtcursor
->CAPABILITIES
,
1384 rc
= CopyTabletData(lpOutput
,&tgtcursor
->TYPE
,
1388 FIXME("WTI_CURSORS unhandled index %i\n",nIndex
);
1397 rc
= CopyTabletData(lpOutput
,gSysDevice
.NAME
,
1398 (strlenW(gSysDevice
.NAME
)+1) * sizeof(WCHAR
));
1401 rc
= CopyTabletData(lpOutput
,&gSysDevice
.HARDWARE
,
1405 rc
= CopyTabletData(lpOutput
,&gSysDevice
.NCSRTYPES
,
1409 rc
= CopyTabletData(lpOutput
,&gSysDevice
.FIRSTCSR
,
1413 rc
= CopyTabletData(lpOutput
,&gSysDevice
.PKTRATE
,
1417 rc
= CopyTabletData(lpOutput
,&gSysDevice
.PKTDATA
,
1421 rc
= CopyTabletData(lpOutput
,&gSysDevice
.PKTMODE
,
1425 rc
= CopyTabletData(lpOutput
,&gSysDevice
.CSRDATA
,
1429 rc
= CopyTabletData(lpOutput
,&gSysDevice
.XMARGIN
,
1433 rc
= CopyTabletData(lpOutput
,&gSysDevice
.YMARGIN
,
1437 rc
= 0; /* unsupported */
1439 rc = CopyTabletData(lpOutput,&gSysDevice.ZMARGIN,
1444 rc
= CopyTabletData(lpOutput
,&gSysDevice
.X
,
1448 rc
= CopyTabletData(lpOutput
,&gSysDevice
.Y
,
1452 rc
= 0; /* unsupported */
1454 rc = CopyTabletData(lpOutput,&gSysDevice.Z,
1459 rc
= CopyTabletData(lpOutput
,&gSysDevice
.NPRESSURE
,
1463 rc
= 0; /* unsupported */
1465 rc = CopyTabletData(lpOutput,&gSysDevice.TPRESSURE,
1469 case DVC_ORIENTATION
:
1470 rc
= CopyTabletData(lpOutput
,gSysDevice
.ORIENTATION
,
1474 rc
= 0; /* unsupported */
1476 rc = CopyTabletData(lpOutput,&gSysDevice.ROTATION,
1481 rc
= CopyTabletData(lpOutput
,gSysDevice
.PNPID
,
1482 (strlenW(gSysDevice
.PNPID
)+1)*sizeof(WCHAR
));
1485 FIXME("WTI_DEVICES unhandled index %i\n",nIndex
);
1490 FIXME("Unhandled Category %i\n",wCategory
);
1495 #else /* SONAME_LIBXI */
1497 /***********************************************************************
1498 * AttachEventQueueToTablet (X11DRV.@)
1500 int CDECL
X11DRV_AttachEventQueueToTablet(HWND hOwner
)
1505 /***********************************************************************
1506 * GetCurrentPacket (X11DRV.@)
1508 int CDECL
X11DRV_GetCurrentPacket(LPWTPACKET packet
)
1513 /***********************************************************************
1514 * LoadTabletInfo (X11DRV.@)
1516 BOOL CDECL
X11DRV_LoadTabletInfo(HWND hwnddefault
)
1521 /***********************************************************************
1522 * WTInfoW (X11DRV.@)
1524 UINT CDECL
X11DRV_WTInfoW(UINT wCategory
, UINT nIndex
, LPVOID lpOutput
)
1529 #endif /* SONAME_LIBXI */