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 BOOL
is_stylus(const char *name
, const char *type
)
421 static const char* tablet_stylus_whitelist
[] = {
428 for (i
=0; tablet_stylus_whitelist
[i
] != NULL
; i
++) {
429 if (name
&& match_token(name
, tablet_stylus_whitelist
[i
]))
431 if (type
&& match_token(type
, tablet_stylus_whitelist
[i
]))
438 static BOOL
is_eraser(const char *name
, const char *type
)
440 if (name
&& match_token(name
, "eraser"))
442 if (type
&& match_token(type
, "eraser"))
447 /* cursors are placed in gSysCursor rows depending on their type
448 * see CURSORMAX comments for more detail */
449 static BOOL
add_system_cursor(LPWTI_CURSORS_INFO cursor
)
453 if (cursor
->TYPE
== CSR_TYPE_PEN
)
455 else if (cursor
->TYPE
== CSR_TYPE_ERASER
)
458 for (; offset
< CURSORMAX
; offset
+= 3)
460 if (!gSysCursor
[offset
].ACTIVE
)
462 gSysCursor
[offset
] = *cursor
;
471 static void disable_system_cursors(void)
475 for (i
= 0; i
< CURSORMAX
; ++i
)
477 gSysCursor
[i
].ACTIVE
= 0;
484 /***********************************************************************
485 * X11DRV_LoadTabletInfo (X11DRV.@)
487 BOOL CDECL
X11DRV_LoadTabletInfo(HWND hwnddefault
)
489 const WCHAR SZ_CONTEXT_NAME
[] = {'W','i','n','e',' ','T','a','b','l','e','t',' ','C','o','n','t','e','x','t',0};
490 const WCHAR SZ_DEVICE_NAME
[] = {'W','i','n','e',' ','T','a','b','l','e','t',' ','D','e','v','i','c','e',0};
491 const WCHAR SZ_NON_PLUGINPLAY
[] = {'n','o','n','-','p','l','u','g','i','n','p','l','a','y',0};
493 struct x11drv_thread_data
*data
= x11drv_init_thread_data();
496 XDeviceInfo
*devices
;
497 XDeviceInfo
*target
= NULL
;
498 BOOL axis_read_complete
= FALSE
;
501 XButtonInfoPtr Button
;
502 XValuatorInfoPtr Val
;
507 if (!X11DRV_XInput_Init())
509 ERR("Unable to initialize the XInput library.\n");
513 hwndTabletDefault
= hwnddefault
;
515 /* Do base initialization */
516 strcpyW(gSysContext
.lcName
, SZ_CONTEXT_NAME
);
517 strcpyW(gSysDevice
.NAME
, SZ_DEVICE_NAME
);
519 gSysContext
.lcOptions
= CXO_SYSTEM
;
520 gSysContext
.lcLocks
= CXL_INSIZE
| CXL_INASPECT
| CXL_MARGIN
|
521 CXL_SENSITIVITY
| CXL_SYSOUT
;
523 gSysContext
.lcMsgBase
= WT_DEFBASE
;
524 gSysContext
.lcDevice
= 0;
525 gSysContext
.lcPktData
=
526 PK_CONTEXT
| PK_STATUS
| PK_SERIAL_NUMBER
| PK_TIME
| PK_CURSOR
|
527 PK_BUTTONS
| PK_X
| PK_Y
| PK_NORMAL_PRESSURE
| PK_ORIENTATION
;
528 gSysContext
.lcMoveMask
=
529 PK_BUTTONS
| PK_X
| PK_Y
| PK_NORMAL_PRESSURE
| PK_ORIENTATION
;
530 gSysContext
.lcStatus
= CXS_ONTOP
;
531 gSysContext
.lcPktRate
= 100;
532 gSysContext
.lcBtnDnMask
= 0xffffffff;
533 gSysContext
.lcBtnUpMask
= 0xffffffff;
534 gSysContext
.lcSensX
= 65536;
535 gSysContext
.lcSensY
= 65536;
536 gSysContext
.lcSensX
= 65536;
537 gSysContext
.lcSensZ
= 65536;
538 gSysContext
.lcSysSensX
= 65536;
539 gSysContext
.lcSysSensY
= 65536;
541 /* initialize cursors */
542 disable_system_cursors();
544 /* Device Defaults */
545 gSysDevice
.HARDWARE
= HWC_HARDPROX
|HWC_PHYSID_CURSORS
;
546 gSysDevice
.FIRSTCSR
= 0;
547 gSysDevice
.PKTRATE
= 100;
549 PK_CONTEXT
| PK_STATUS
| PK_SERIAL_NUMBER
| PK_TIME
| PK_CURSOR
|
550 PK_BUTTONS
| PK_X
| PK_Y
| PK_NORMAL_PRESSURE
| PK_ORIENTATION
;
551 strcpyW(gSysDevice
.PNPID
, SZ_NON_PLUGINPLAY
);
555 devices
= pXListInputDevices(data
->display
, &num_devices
);
558 WARN("XInput Extensions reported as not available\n");
562 TRACE("XListInputDevices reports %d devices\n", num_devices
);
563 for (loop
=0; loop
< num_devices
; loop
++)
566 char *device_type
= devices
[loop
].type
? XGetAtomName(data
->display
, devices
[loop
].type
) : NULL
;
567 WTI_CURSORS_INFO cursor
;
569 TRACE("Device %i: [id %d|name %s|type %s|num_classes %d|use %d]\n",
570 loop
, (int) devices
[loop
].id
, devices
[loop
].name
, device_type
? device_type
: "",
571 devices
[loop
].num_classes
, devices
[loop
].use
);
573 switch (devices
[loop
].use
)
575 case IsXExtensionDevice
:
576 #ifdef IsXExtensionPointer
577 case IsXExtensionPointer
:
579 #ifdef IsXExtensionKeyboard
580 case IsXExtensionKeyboard
:
582 TRACE("Is XExtension: Device, Keyboard, or Pointer\n");
583 target
= &devices
[loop
];
585 if (strlen(target
->name
) >= WT_MAX_NAME_LEN
)
587 ERR("Input device '%s' name too long - skipping\n", wine_dbgstr_a(target
->name
));
591 X11DRV_expect_error(data
->display
, Tablet_ErrorHandler
, NULL
);
592 opendevice
= pXOpenDevice(data
->display
,target
->id
);
593 if (!X11DRV_check_error() && opendevice
)
595 unsigned char map
[32];
599 X11DRV_expect_error(data
->display
,Tablet_ErrorHandler
,NULL
);
600 cursor
.BUTTONS
= pXGetDeviceButtonMapping(data
->display
, opendevice
, map
, 32);
601 if (X11DRV_check_error() || cursor
.BUTTONS
<= 0)
603 TRACE("No buttons, Non Tablet Device\n");
604 pXCloseDevice(data
->display
, opendevice
);
608 for (i
=0; i
< cursor
.BUTTONS
; i
++,shft
++)
610 cursor
.BUTTONMAP
[i
] = map
[i
];
611 cursor
.SYSBTNMAP
[i
] = (1<<shft
);
613 pXCloseDevice(data
->display
, opendevice
);
617 WARN("Unable to open device %s\n",target
->name
);
620 MultiByteToWideChar(CP_UNIXCP
, 0, target
->name
, -1, cursor
.NAME
, WT_MAX_NAME_LEN
);
622 if (! is_tablet_cursor(target
->name
, device_type
))
624 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",
625 loop
, devices
[loop
].name
, device_type
? device_type
: "");
630 cursor
.PKTDATA
= PK_TIME
| PK_CURSOR
| PK_BUTTONS
| PK_X
| PK_Y
|
631 PK_NORMAL_PRESSURE
| PK_TANGENT_PRESSURE
|
634 cursor
.PHYSID
= target
->id
;
636 cursor
.NPBTNMARKS
[0] = 0 ;
637 cursor
.NPBTNMARKS
[1] = 1 ;
638 cursor
.CAPABILITIES
= CRC_MULTIMODE
;
640 /* prefer finding TYPE_PEN(most capable) */
641 if (is_stylus(target
->name
, device_type
))
642 cursor
.TYPE
= CSR_TYPE_PEN
;
643 else if (is_eraser(target
->name
, device_type
))
644 cursor
.TYPE
= CSR_TYPE_ERASER
;
646 cursor
.TYPE
= CSR_TYPE_OTHER
;
648 any
= target
->inputclassinfo
;
650 for (class_loop
= 0; class_loop
< target
->num_classes
; class_loop
++)
656 Val
= (XValuatorInfoPtr
) any
;
657 TRACE(" ValidatorInput %d: [class %d|length %d|num_axes %d|mode %d|motion_buffer %ld]\n",
658 class_loop
, (int) Val
->class, Val
->length
, Val
->num_axes
, Val
->mode
, Val
->motion_buffer
);
659 if (TRACE_ON(wintab32
))
662 /* FIXME: This is imperfect; we compute our devices capabilities based upon the
663 ** first pen type device we find. However, a more correct implementation
664 ** would require acquiring a wide variety of tablets and running through
665 ** the various inputs to see what the values are. Odds are that a
666 ** more 'correct' algorithm would condense to this one anyway.
668 if (!axis_read_complete
&& cursor
.TYPE
== CSR_TYPE_PEN
)
670 Axis
= (XAxisInfoPtr
) ((char *) Val
+ sizeof
673 if (Val
->num_axes
>=1)
676 gSysDevice
.X
.axMin
= Axis
->min_value
;
677 gSysDevice
.X
.axMax
= Axis
->max_value
;
678 gSysDevice
.X
.axUnits
= TU_INCHES
;
679 gSysDevice
.X
.axResolution
= Axis
->resolution
;
680 gSysContext
.lcInOrgX
= Axis
->min_value
;
681 gSysContext
.lcSysOrgX
= Axis
->min_value
;
682 gSysContext
.lcInExtX
= Axis
->max_value
;
683 gSysContext
.lcSysExtX
= Axis
->max_value
;
686 if (Val
->num_axes
>=2)
689 gSysDevice
.Y
.axMin
= Axis
->min_value
;
690 gSysDevice
.Y
.axMax
= Axis
->max_value
;
691 gSysDevice
.Y
.axUnits
= TU_INCHES
;
692 gSysDevice
.Y
.axResolution
= Axis
->resolution
;
693 gSysContext
.lcInOrgY
= Axis
->min_value
;
694 gSysContext
.lcSysOrgY
= Axis
->min_value
;
695 gSysContext
.lcInExtY
= Axis
->max_value
;
696 gSysContext
.lcSysExtY
= Axis
->max_value
;
699 if (Val
->num_axes
>=3)
701 /* Axis 3 is Normal Pressure */
702 gSysDevice
.NPRESSURE
.axMin
= Axis
->min_value
;
703 gSysDevice
.NPRESSURE
.axMax
= Axis
->max_value
;
704 gSysDevice
.NPRESSURE
.axUnits
= TU_INCHES
;
705 gSysDevice
.NPRESSURE
.axResolution
=
709 if (Val
->num_axes
>= 5)
711 /* Axis 4 and 5 are X and Y tilt */
712 XAxisInfoPtr XAxis
= Axis
;
714 if (max (abs(Axis
->max_value
),
715 abs(XAxis
->max_value
)))
717 gSysDevice
.ORIENTATION
[0].axMin
= 0;
718 gSysDevice
.ORIENTATION
[0].axMax
= 3600;
719 gSysDevice
.ORIENTATION
[0].axUnits
= TU_CIRCLE
;
720 gSysDevice
.ORIENTATION
[0].axResolution
722 gSysDevice
.ORIENTATION
[1].axMin
= -1000;
723 gSysDevice
.ORIENTATION
[1].axMax
= 1000;
724 gSysDevice
.ORIENTATION
[1].axUnits
= TU_CIRCLE
;
725 gSysDevice
.ORIENTATION
[1].axResolution
730 axis_read_complete
= TRUE
;
739 Button
= (XButtonInfoPtr
) any
;
740 TRACE(" ButtonInput %d: [class %d|length %d|num_buttons %d]\n",
741 class_loop
, (int) Button
->class, Button
->length
, Button
->num_buttons
);
742 cursor
.BTNNAMES
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*cchBuf
);
743 for (i
= 0; i
< cursor
.BUTTONS
; i
++)
745 /* FIXME - these names are probably incorrect */
746 int cch
= strlenW(cursor
.NAME
) + 1;
747 while (cch
> cchBuf
- cchPos
- 1) /* we want one extra byte for the last NUL */
750 cursor
.BTNNAMES
= HeapReAlloc(GetProcessHeap(), 0, cursor
.BTNNAMES
, sizeof(WCHAR
)*cchBuf
);
753 strcpyW(cursor
.BTNNAMES
+ cchPos
, cursor
.NAME
);
756 cursor
.BTNNAMES
[cchPos
++] = 0;
757 cursor
.BTNNAMES
= HeapReAlloc(GetProcessHeap(), 0, cursor
.BTNNAMES
, sizeof(WCHAR
)*cchPos
);
758 cursor
.cchBTNNAMES
= cchPos
;
761 } /* switch any->class */
762 any
= (XAnyClassPtr
) ((char*) any
+ any
->length
);
763 } /* for class_loop */
764 if (!add_system_cursor(&cursor
))
765 FIXME("Skipping this cursor due to lack of system cursor slots.\n");
767 } /* switch devices.use */
769 } /* for XListInputDevices */
770 pXFreeDeviceList(devices
);
772 if (axis_read_complete
)
773 gSysDevice
.NCSRTYPES
= gNumCursors
;
776 disable_system_cursors();
777 WARN("Did not find a valid stylus, unable to determine system context parameters. Wintab is disabled.\n");
784 static int figure_deg(int x
, int y
)
788 angle
= atan2((float)y
, (float)x
);
793 return (0.5 + (angle
* 1800.0 / M_PI
));
796 static int get_button_state(int curnum
)
798 return button_state
[curnum
];
801 static void set_button_state(int curnum
, XID deviceid
)
803 struct x11drv_thread_data
*data
= x11drv_thread_data();
811 device
= pXOpenDevice(data
->display
,deviceid
);
812 state
= pXQueryDeviceState(data
->display
,device
);
817 for (loop
= 0; loop
< state
->num_classes
; loop
++)
819 if (class->class == ButtonClass
)
822 XButtonState
*button_state
= (XButtonState
*)class;
823 for (loop2
= 0; loop2
< button_state
->num_buttons
; loop2
++)
825 if (button_state
->buttons
[loop2
/ 8] & (1 << (loop2
% 8)))
831 class = (XInputClass
*) ((char *) class + class->length
);
834 pXFreeDeviceState(state
);
836 button_state
[curnum
] = rc
;
839 static int cursor_from_device(DWORD deviceid
, LPWTI_CURSORS_INFO
*cursorp
)
842 for (i
= 0; i
< CURSORMAX
; i
++)
843 if (gSysCursor
[i
].ACTIVE
&& gSysCursor
[i
].PHYSID
== deviceid
)
845 *cursorp
= &gSysCursor
[i
];
849 ERR("Could not map device id %d to a cursor\n", (int) deviceid
);
853 static void motion_event( HWND hwnd
, XEvent
*event
)
855 XDeviceMotionEvent
*motion
= (XDeviceMotionEvent
*)event
;
856 LPWTI_CURSORS_INFO cursor
;
857 int curnum
= cursor_from_device(motion
->deviceid
, &cursor
);
861 memset(&gMsgPacket
,0,sizeof(WTPACKET
));
863 TRACE("Received tablet motion event (%p); device id %d, cursor num %d\n",hwnd
, (int) motion
->deviceid
, curnum
);
865 /* Set cursor to inverted if cursor is the eraser */
866 gMsgPacket
.pkStatus
= (cursor
->TYPE
== CSR_TYPE_ERASER
? TPS_INVERT
:0);
867 gMsgPacket
.pkTime
= EVENT_x11_time_to_win32_time(motion
->time
);
868 gMsgPacket
.pkSerialNumber
= gSerial
++;
869 gMsgPacket
.pkCursor
= curnum
;
870 gMsgPacket
.pkX
= motion
->axis_data
[0];
871 gMsgPacket
.pkY
= motion
->axis_data
[1];
872 gMsgPacket
.pkOrientation
.orAzimuth
= figure_deg(motion
->axis_data
[3],motion
->axis_data
[4]);
873 gMsgPacket
.pkOrientation
.orAltitude
= ((1000 - 15 * max
874 (abs(motion
->axis_data
[3]),
875 abs(motion
->axis_data
[4])))
876 * (gMsgPacket
.pkStatus
& TPS_INVERT
?-1:1));
877 gMsgPacket
.pkNormalPressure
= motion
->axis_data
[2];
878 gMsgPacket
.pkButtons
= get_button_state(curnum
);
879 SendMessageW(hwndTabletDefault
,WT_PACKET
,gMsgPacket
.pkSerialNumber
,(LPARAM
)hwnd
);
882 static void button_event( HWND hwnd
, XEvent
*event
)
884 XDeviceButtonEvent
*button
= (XDeviceButtonEvent
*) event
;
885 LPWTI_CURSORS_INFO cursor
;
886 int curnum
= cursor_from_device(button
->deviceid
, &cursor
);
890 memset(&gMsgPacket
,0,sizeof(WTPACKET
));
892 TRACE("Received tablet button %s event\n", (event
->type
== button_press_type
)?"press":"release");
894 /* Set cursor to inverted if cursor is the eraser */
895 gMsgPacket
.pkStatus
= (cursor
->TYPE
== CSR_TYPE_ERASER
? TPS_INVERT
:0);
896 set_button_state(curnum
, button
->deviceid
);
897 gMsgPacket
.pkTime
= EVENT_x11_time_to_win32_time(button
->time
);
898 gMsgPacket
.pkSerialNumber
= gSerial
++;
899 gMsgPacket
.pkCursor
= curnum
;
900 gMsgPacket
.pkX
= button
->axis_data
[0];
901 gMsgPacket
.pkY
= button
->axis_data
[1];
902 gMsgPacket
.pkOrientation
.orAzimuth
= figure_deg(button
->axis_data
[3],button
->axis_data
[4]);
903 gMsgPacket
.pkOrientation
.orAltitude
= ((1000 - 15 * max(abs(button
->axis_data
[3]),
904 abs(button
->axis_data
[4])))
905 * (gMsgPacket
.pkStatus
& TPS_INVERT
?-1:1));
906 gMsgPacket
.pkNormalPressure
= button
->axis_data
[2];
907 gMsgPacket
.pkButtons
= get_button_state(curnum
);
908 SendMessageW(hwndTabletDefault
,WT_PACKET
,gMsgPacket
.pkSerialNumber
,(LPARAM
)hwnd
);
911 static void key_event( HWND hwnd
, XEvent
*event
)
913 if (event
->type
== key_press_type
)
914 FIXME("Received tablet key press event\n");
916 FIXME("Received tablet key release event\n");
919 static void proximity_event( HWND hwnd
, XEvent
*event
)
921 XProximityNotifyEvent
*proximity
= (XProximityNotifyEvent
*) event
;
922 LPWTI_CURSORS_INFO cursor
;
923 int curnum
= cursor_from_device(proximity
->deviceid
, &cursor
);
924 LPARAM proximity_info
;
926 TRACE("hwnd=%p\n", hwnd
);
931 memset(&gMsgPacket
,0,sizeof(WTPACKET
));
933 /* Set cursor to inverted if cursor is the eraser */
934 gMsgPacket
.pkStatus
= (cursor
->TYPE
== CSR_TYPE_ERASER
? TPS_INVERT
:0);
935 gMsgPacket
.pkStatus
|= (event
->type
==proximity_out_type
)?TPS_PROXIMITY
:0;
936 gMsgPacket
.pkTime
= EVENT_x11_time_to_win32_time(proximity
->time
);
937 gMsgPacket
.pkSerialNumber
= gSerial
++;
938 gMsgPacket
.pkCursor
= curnum
;
939 gMsgPacket
.pkX
= proximity
->axis_data
[0];
940 gMsgPacket
.pkY
= proximity
->axis_data
[1];
941 gMsgPacket
.pkOrientation
.orAzimuth
= figure_deg(proximity
->axis_data
[3],proximity
->axis_data
[4]);
942 gMsgPacket
.pkOrientation
.orAltitude
= ((1000 - 15 * max(abs(proximity
->axis_data
[3]),
943 abs(proximity
->axis_data
[4])))
944 * (gMsgPacket
.pkStatus
& TPS_INVERT
?-1:1));
945 gMsgPacket
.pkNormalPressure
= proximity
->axis_data
[2];
946 gMsgPacket
.pkButtons
= get_button_state(curnum
);
948 /* FIXME: LPARAM loword is true when cursor entering context, false when leaving context
949 * This needs to be handled here or in wintab32. Using the proximity_in_type is not correct
951 * LPARAM hiword is "non-zero when the cursor is leaving or entering hardware proximity"
952 * WPARAM contains context handle.
953 * HWND to HCTX is handled by wintab32.
955 proximity_info
= MAKELPARAM((event
->type
== proximity_in_type
),
956 (event
->type
== proximity_in_type
) || (event
->type
== proximity_out_type
));
957 SendMessageW(hwndTabletDefault
, WT_PROXIMITY
, (WPARAM
)hwnd
, proximity_info
);
960 /***********************************************************************
961 * X11DRV_AttachEventQueueToTablet (X11DRV.@)
963 int CDECL
X11DRV_AttachEventQueueToTablet(HWND hOwner
)
965 struct x11drv_thread_data
*data
= x11drv_init_thread_data();
969 XDeviceInfo
*devices
;
970 XDeviceInfo
*target
= NULL
;
972 XEventClass event_list
[7];
973 Window win
= X11DRV_get_whole_window( hOwner
);
975 if (!win
|| !xinput_handle
) return 0;
977 TRACE("Creating context for window %p (%lx) %i cursors\n", hOwner
, win
, gNumCursors
);
980 devices
= pXListInputDevices(data
->display
, &num_devices
);
982 X11DRV_expect_error(data
->display
,Tablet_ErrorHandler
,NULL
);
983 for (cur_loop
=0; cur_loop
< CURSORMAX
; cur_loop
++)
985 char cursorNameA
[WT_MAX_NAME_LEN
];
988 if (!gSysCursor
[cur_loop
].ACTIVE
) continue;
990 /* the cursor name fits in the buffer because too long names are skipped */
991 WideCharToMultiByte(CP_UNIXCP
, 0, gSysCursor
[cur_loop
].NAME
, -1, cursorNameA
, WT_MAX_NAME_LEN
, NULL
, NULL
);
992 for (loop
=0; loop
< num_devices
; loop
++)
993 if (strcmp(devices
[loop
].name
, cursorNameA
) == 0)
994 target
= &devices
[loop
];
996 WARN("Cursor Name %s not found in list of targets.\n", cursorNameA
);
1000 TRACE("Opening cursor %i id %i\n",cur_loop
,(INT
)target
->id
);
1002 the_device
= pXOpenDevice(data
->display
, target
->id
);
1006 WARN("Unable to Open device\n");
1010 if (the_device
->num_classes
> 0)
1012 DeviceKeyPress(the_device
, key_press_type
, event_list
[event_number
]);
1013 if (key_press_type
) event_number
++;
1014 DeviceKeyRelease(the_device
, key_release_type
, event_list
[event_number
]);
1015 if (key_release_type
) event_number
++;
1016 DeviceButtonPress(the_device
, button_press_type
, event_list
[event_number
]);
1017 if (button_press_type
) event_number
++;
1018 DeviceButtonRelease(the_device
, button_release_type
, event_list
[event_number
]);
1019 if (button_release_type
) event_number
++;
1020 DeviceMotionNotify(the_device
, motion_type
, event_list
[event_number
]);
1021 if (motion_type
) event_number
++;
1022 ProximityIn(the_device
, proximity_in_type
, event_list
[event_number
]);
1023 if (proximity_in_type
) event_number
++;
1024 ProximityOut(the_device
, proximity_out_type
, event_list
[event_number
]);
1025 if (proximity_out_type
) event_number
++;
1028 X11DRV_register_event_handler( key_press_type
, key_event
, "XInput KeyPress" );
1029 if (key_release_type
)
1030 X11DRV_register_event_handler( key_release_type
, key_event
, "XInput KeyRelease" );
1031 if (button_press_type
)
1032 X11DRV_register_event_handler( button_press_type
, button_event
, "XInput ButtonPress" );
1033 if (button_release_type
)
1034 X11DRV_register_event_handler( button_release_type
, button_event
, "XInput ButtonRelease" );
1036 X11DRV_register_event_handler( motion_type
, motion_event
, "XInput MotionNotify" );
1037 if (proximity_in_type
)
1038 X11DRV_register_event_handler( proximity_in_type
, proximity_event
, "XInput ProximityIn" );
1039 if (proximity_out_type
)
1040 X11DRV_register_event_handler( proximity_out_type
, proximity_event
, "XInput ProximityOut" );
1042 pXSelectExtensionEvent(data
->display
, win
, event_list
, event_number
);
1045 XSync(data
->display
, False
);
1046 X11DRV_check_error();
1048 if (NULL
!= devices
) pXFreeDeviceList(devices
);
1049 wine_tsx11_unlock();
1053 /***********************************************************************
1054 * X11DRV_GetCurrentPacket (X11DRV.@)
1056 int CDECL
X11DRV_GetCurrentPacket(LPWTPACKET packet
)
1058 *packet
= gMsgPacket
;
1063 static inline int CopyTabletData(LPVOID target
, LPCVOID src
, INT size
)
1066 * It is valid to call CopyTabletData with NULL.
1067 * This handles the WTInfo() case where lpOutput is null.
1070 memcpy(target
,src
,size
);
1074 /***********************************************************************
1075 * X11DRV_WTInfoW (X11DRV.@)
1077 UINT CDECL
X11DRV_WTInfoW(UINT wCategory
, UINT nIndex
, LPVOID lpOutput
)
1080 * It is valid to call WTInfoA with lpOutput == NULL, as per standard.
1081 * lpOutput == NULL signifies the user only wishes
1082 * to find the size of the data.
1084 * From now on use CopyTabletData to fill lpOutput. memcpy will break
1088 LPWTI_CURSORS_INFO tgtcursor
;
1089 TRACE("(%u, %u, %p)\n", wCategory
, nIndex
, lpOutput
);
1091 if (!xinput_handle
) return 0;
1096 /* return largest necessary buffer */
1097 TRACE("%i cursors\n",gNumCursors
);
1100 FIXME("Return proper size\n");
1111 static const WCHAR driver
[] = {'W','i','n','e',' ','W','i','n','t','a','b',' ','1','.','1',0};
1112 rc
= CopyTabletData(lpOutput
, driver
, (strlenW(driver
) + 1) * sizeof(WCHAR
));
1115 case IFC_SPECVERSION
:
1116 version
= (0x01) | (0x01 << 8);
1117 rc
= CopyTabletData(lpOutput
, &version
,sizeof(WORD
));
1119 case IFC_IMPLVERSION
:
1120 version
= (0x00) | (0x01 << 8);
1121 rc
= CopyTabletData(lpOutput
, &version
,sizeof(WORD
));
1125 rc
= CopyTabletData(lpOutput
, &num
,sizeof(num
));
1129 rc
= CopyTabletData(lpOutput
, &num
,sizeof(num
));
1132 FIXME("WTI_INTERFACE unhandled index %i\n",nIndex
);
1138 case WTI_DEFCONTEXT
:
1142 /* report 0 if wintab is disabled */
1143 if (0 == gNumCursors
)
1146 rc
= CopyTabletData(lpOutput
, &gSysContext
,
1147 sizeof(LOGCONTEXTW
));
1150 rc
= CopyTabletData(lpOutput
, gSysContext
.lcName
,
1151 (strlenW(gSysContext
.lcName
)+1) * sizeof(WCHAR
));
1154 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOptions
,
1158 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcStatus
,
1162 rc
= CopyTabletData (lpOutput
, &gSysContext
.lcLocks
,
1166 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcMsgBase
,
1170 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcDevice
,
1174 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcPktRate
,
1178 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcPktData
,
1182 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcPktMode
,
1186 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcMoveMask
,
1190 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcBtnDnMask
,
1194 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcBtnUpMask
,
1198 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInOrgX
,
1202 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInOrgY
,
1206 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInOrgZ
,
1210 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInExtX
,
1214 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInExtY
,
1218 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcInExtZ
,
1222 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutOrgX
,
1226 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutOrgY
,
1230 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutOrgZ
,
1234 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutExtX
,
1238 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutExtY
,
1242 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcOutExtZ
,
1246 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSensX
,
1250 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSensY
,
1254 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSensZ
,
1258 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysMode
,
1262 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysOrgX
,
1266 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysOrgY
,
1270 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysExtX
,
1274 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysExtY
,
1278 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysSensX
,
1282 rc
= CopyTabletData(lpOutput
, &gSysContext
.lcSysSensY
,
1286 FIXME("WTI_DEFSYSCTX unhandled index %i\n",nIndex
);
1300 case WTI_CURSORS
+10:
1301 case WTI_CURSORS
+11:
1302 /* CURSORMAX == 12 */
1303 /* FIXME: dynamic cursor support */
1304 /* Apps will poll different slots to detect what cursors are available
1305 * if there isn't a cursor for this slot return 0 */
1306 if (!gSysCursor
[wCategory
- WTI_CURSORS
].ACTIVE
)
1310 tgtcursor
= &gSysCursor
[wCategory
- WTI_CURSORS
];
1314 rc
= CopyTabletData(lpOutput
, tgtcursor
->NAME
,
1315 (strlenW(tgtcursor
->NAME
)+1) * sizeof(WCHAR
));
1318 rc
= CopyTabletData(lpOutput
,&tgtcursor
->ACTIVE
,
1322 rc
= CopyTabletData(lpOutput
,&tgtcursor
->PKTDATA
,
1326 rc
= CopyTabletData(lpOutput
,&tgtcursor
->BUTTONS
,
1329 case CSR_BUTTONBITS
:
1330 rc
= CopyTabletData(lpOutput
,&tgtcursor
->BUTTONBITS
,
1334 FIXME("Button Names not returned correctly\n");
1335 rc
= CopyTabletData(lpOutput
,&tgtcursor
->BTNNAMES
,
1336 tgtcursor
->cchBTNNAMES
*sizeof(WCHAR
));
1339 rc
= CopyTabletData(lpOutput
,tgtcursor
->BUTTONMAP
,
1343 rc
= CopyTabletData(lpOutput
,tgtcursor
->SYSBTNMAP
,
1346 case CSR_NPBTNMARKS
:
1347 rc
= CopyTabletData(lpOutput
,tgtcursor
->NPBTNMARKS
,
1351 rc
= CopyTabletData(lpOutput
,&tgtcursor
->NPBUTTON
,
1354 case CSR_NPRESPONSE
:
1355 FIXME("Not returning CSR_NPRESPONSE correctly\n");
1359 rc
= CopyTabletData(lpOutput
,&tgtcursor
->TPBUTTON
,
1362 case CSR_TPBTNMARKS
:
1363 rc
= CopyTabletData(lpOutput
,tgtcursor
->TPBTNMARKS
,
1366 case CSR_TPRESPONSE
:
1367 FIXME("Not returning CSR_TPRESPONSE correctly\n");
1373 id
= tgtcursor
->PHYSID
;
1374 rc
= CopyTabletData(lpOutput
,&id
,sizeof(DWORD
));
1378 rc
= CopyTabletData(lpOutput
,&tgtcursor
->MODE
,sizeof(UINT
));
1380 case CSR_MINPKTDATA
:
1381 rc
= CopyTabletData(lpOutput
,&tgtcursor
->MINPKTDATA
,
1384 case CSR_MINBUTTONS
:
1385 rc
= CopyTabletData(lpOutput
,&tgtcursor
->MINBUTTONS
,
1388 case CSR_CAPABILITIES
:
1389 rc
= CopyTabletData(lpOutput
,&tgtcursor
->CAPABILITIES
,
1393 rc
= CopyTabletData(lpOutput
,&tgtcursor
->TYPE
,
1397 FIXME("WTI_CURSORS unhandled index %i\n",nIndex
);
1406 rc
= CopyTabletData(lpOutput
,gSysDevice
.NAME
,
1407 (strlenW(gSysDevice
.NAME
)+1) * sizeof(WCHAR
));
1410 rc
= CopyTabletData(lpOutput
,&gSysDevice
.HARDWARE
,
1414 rc
= CopyTabletData(lpOutput
,&gSysDevice
.NCSRTYPES
,
1418 rc
= CopyTabletData(lpOutput
,&gSysDevice
.FIRSTCSR
,
1422 rc
= CopyTabletData(lpOutput
,&gSysDevice
.PKTRATE
,
1426 rc
= CopyTabletData(lpOutput
,&gSysDevice
.PKTDATA
,
1430 rc
= CopyTabletData(lpOutput
,&gSysDevice
.PKTMODE
,
1434 rc
= CopyTabletData(lpOutput
,&gSysDevice
.CSRDATA
,
1438 rc
= CopyTabletData(lpOutput
,&gSysDevice
.XMARGIN
,
1442 rc
= CopyTabletData(lpOutput
,&gSysDevice
.YMARGIN
,
1446 rc
= 0; /* unsupported */
1448 rc = CopyTabletData(lpOutput,&gSysDevice.ZMARGIN,
1453 rc
= CopyTabletData(lpOutput
,&gSysDevice
.X
,
1457 rc
= CopyTabletData(lpOutput
,&gSysDevice
.Y
,
1461 rc
= 0; /* unsupported */
1463 rc = CopyTabletData(lpOutput,&gSysDevice.Z,
1468 rc
= CopyTabletData(lpOutput
,&gSysDevice
.NPRESSURE
,
1472 rc
= 0; /* unsupported */
1474 rc = CopyTabletData(lpOutput,&gSysDevice.TPRESSURE,
1478 case DVC_ORIENTATION
:
1479 rc
= CopyTabletData(lpOutput
,gSysDevice
.ORIENTATION
,
1483 rc
= 0; /* unsupported */
1485 rc = CopyTabletData(lpOutput,&gSysDevice.ROTATION,
1490 rc
= CopyTabletData(lpOutput
,gSysDevice
.PNPID
,
1491 (strlenW(gSysDevice
.PNPID
)+1)*sizeof(WCHAR
));
1494 FIXME("WTI_DEVICES unhandled index %i\n",nIndex
);
1499 FIXME("Unhandled Category %i\n",wCategory
);
1504 #else /* SONAME_LIBXI */
1506 /***********************************************************************
1507 * AttachEventQueueToTablet (X11DRV.@)
1509 int CDECL
X11DRV_AttachEventQueueToTablet(HWND hOwner
)
1514 /***********************************************************************
1515 * GetCurrentPacket (X11DRV.@)
1517 int CDECL
X11DRV_GetCurrentPacket(LPWTPACKET packet
)
1522 /***********************************************************************
1523 * LoadTabletInfo (X11DRV.@)
1525 BOOL CDECL
X11DRV_LoadTabletInfo(HWND hwnddefault
)
1530 /***********************************************************************
1531 * WTInfoW (X11DRV.@)
1533 UINT CDECL
X11DRV_WTInfoW(UINT wCategory
, UINT nIndex
, LPVOID lpOutput
)
1538 #endif /* SONAME_LIBXI */