Notes for Tablet Aware Application Developers
Audience
This document is mainly intended to assist developers who already have tablet aware applications and want to add new capabilities to their applications. You should already be able to access the tablet data and capabilities. Developers who are adding tablet features for the first time should start with the Overview section, and examine the sample code.
Overview
Applications receive tablet-specific data by using the Wintab Application Programmers Interface. Wintab is an industry standard for interfacing with pointing devices. Information on Wintab, including a developers’ SDK can be obtained from the downloads page. The TiltTest program which is included with this implementers guide is a good example of how to use Wintab to get tablet data.
There are two main points in an application where modifications may be made to support tablets:
- Initialization
During initialization you need to:
Determine if a tablet is attached by looking for the Wintab interface. Call the Wintab function
WTInfo( 0, 0, NULL )to see if Wintab is present.Use
WTInfo()to get static information about the tablet, such as tablet dimensions, coordinate resolution, the presence of tilt, pressure range, tangential pressure range, and other features.Create at least one Wintab context for your application. Get a default context using
WTInfo()and use it as a template to create your own tablet context. Setup the context as appropriate for your application. Applications can open two kinds of contexts: system contexts and digitizing contexts. Wintab moves the system cursor on behalf of the application for system contexts, while applications must move the system cursor themselves for digitizing contexts. Most applications use system contexts. The user can change the area of a system context to make navigation easier. Digitizer contexts usually cover the entire tablet. Digitizer contexts are used for digitizing or to define tablet menus. Setting up the context includes determining what data items will be included in your packets. Although the documentation says you can not include items in the packet which are not supported by the tablet, it appears that this is incorrect. Unsupported items may be included and will be set to zero. Then callWTOpen()to create your context so you can begin processing packet data.Wintab passes data for a tablet point, such as coordinates, pressure and tilt, to an application as a Wintab packet. By default, the packet queue for a context is very small and can easily overflow. Applications should guard against overflowing their queues by increasing their context’s queue sizes using
WTQueueSizeSet().
- Event Handler
Here, you will get data from the tablet and find information about the tablet device currently in use. The tablet data is used for line rendering or navigation. The device information can be used to change tools or screen cursors, for example, when a stylus eraser is placed in use or when tools are changed based on device ID.
WM_LBUTTONDOWN/WM_LBUTTONUPOn a
WM_LBUTTONDOWNevent the packets in the tablet queue should be flushed using, for example,WTPacket()(since they are old). If you are very particular, you may want to flush all packets up to the one with the button pressed and use the rest for drawing. Set a flag that packet events should now be processed and drawn. On aWM_LBUTTONUPevent clear the flag and stop processing packets. Again, if you are particular, on theWM_LBUTTONUPevent process the packets in the queue which still have the button pressed.WT_PACKETevents (usually during a button down)In the draw loop (usually continuously done between
WM_LBUTTONDOWNandWM_LBUTTONUP), tablet data is read from packets and used to modify the rendering of the line being drawn. Data such as current pressure, tilt angles, and fingerwheel values are used to change rendering parameters like opacity, brush direction, line thickness, and so forth. Wintab stores packets in a context-unique packet queue until the application which opened the context requests packet data using a Wintab function such asWTPackets(). When applications open a context they pass Wintab a window handle. If the application sets the context’sCXO_MESSAGESoption, the window receives notification that a new packet has been received via the WT_PACKET message. Upon receiving the event, it is best to ask how many packets have arrived and read at least that many packets. This avoids the overhead incurred moving the data between the driver and application. So in response to aWT_PACKETevent call WTQueuePackets(Ex) to find out the starting and ending serial numbers then call WTDataGet() to get at least those packets. CAUTION! If the queue overflows, Wintab stops informing the application that new packets have been received, and it discards the subsequently received packets until the queue is no longer full. Be sure to avoid overflow by draining the queue duringWT_PACKETmessages.WT_CSRCHANGE(WT_PROXIMITY) EventsWhen a pointing device enters or leaves proximity of a context, the application which opened the context is notified through the
WT_PROXIMITYmessage. When a different pointing device enters proximity of the context, the application is informed through aWT_CSRCHANGEmessage, if the application requested this message when the context was created. When an application receives aWT_PROXIMITYorWT_CSRCHANGEmessage, it can callWTInfo()to get information about the pointing device, such as number of buttons on the device, which end of the stylus is in use, or the device’s physical ID. This message is very useful for changing drawing tools. The tool (and screen cursor) should change when the pointing device is first brought into use, not when drawing starts. Therefore, you will want to check for tip vs. eraser, and check the Unique ID at this point.WT_PACKETevents (without a button down)You may want to look at device information for navigational purposes, such as rotating an object with the stylus tilt. You can do this by always reading tablet packets in response to
WT_PACKETevents, even when a button is not down. For example, if you are using a 4D Mouse for navigation, current device information can be used to modify an object’s position or to modify your own viewpoint.
Notes
With two exceptions the driver implements the Wintab specification version 1.1 as published by LCS/Telegraphics. The exceptions are an addition needed to carry information for Unique ID, and an extension to allow control of ExpressKeys. The specification version returned from Wintab is 1.2 to indicate the added feature.
// Check the Wintab version in this manner WORD thisVersion; WTInfo(WTI_INTERFACE, IFC_SPECVERSION, &thisVersion);
You should not have to change any code to support the features you currently support, you simply add code to support new features.
There are new header files, WINTAB.H and PKTDEF.H to replace the one you are now using. The files bring the
data structures up to specification 1.1, and add the 1.2 feature (CSR_TYPE) and 1.3
extension (EXPKEYS). Their use should not break any existing code.
Wintab arranges all open contexts for all applications in one overlap order for the tablet and
uses the overlap order to decide which context should process each tablet packet. Each packet is put
in only one context’s packet queue. In general, if context A is above context B in the overlap
order, context A will receive tablet packets and context B will not. Applications bring their
contexts to the top or bottom of the overlap order by calling WTOverlap(). Many
applications fail to properly handle their context ordering. This causes simultaneously executing
Wintab applications to stop working or to behave unpredictably. When applications receive the
WM_ACTIVATE message, they should push their contexts to the bottom of the overlap order
if their application is being deactivated, and should bring their context to the top if they are
being activated. Applications should also disable their contexts when they are minimized.
It is best to have at least two undo buffers, or one more than the desired number. Don’t flush the undo buffer on a button down when you are about to draw, it takes too long. Flush the buffer on a button up. This will improve the beginning of your strokes.
As you can see, code is shown in monospace font.
Features You Should Already Have Implemented
Eraser and Tilt are features that have been around for some time. A large installed base of tablets supports these capabilities and, if you have not already done so, you can increase the power and performance of your application by implementing these features.
Eraser
The obvious purpose of the eraser is to use it as an erasing tool. But you can change the function of the eraser to perform more advanced tasks. Using the stylus tip in conjunction with the eraser, you can easily perform two opposing functions such as dodge and burn, or ink and bleach. The eraser can also function independently as any tool selected in the application.
There are several ways to detect the use of the eraser. One is to look for a negative orAltitude value in the pkOrientation data of a packet. If you use this method please note that the tilt value is defined as unsigned (although it is stated to be signed in the Wintab specification) so you will have to cast the tilt value as signed to make this work. The other way to detect the eraser is to look for cursor number 2, and with the advent of the new driver also check for 5. The time to look for this is once the device is brought into proximity. The new
WT_CSRCHANGEmessage is a good time to look. For backwards compatibility you might use theWT_PROXIMITYmessage. Be sure to include the cursor number (pkCursorfield) in the data packet. An application can also detect the eraser by looking at the newTPS_INVERTbit-field in thepkStatusdata item of a packet. This field is new to the Wintab 1.1 specification.Tilt
Stylus tilt has several uses, but the most obvious is to change brush shape. A good example of using stylus tilt to change brush shape is the Wacom PenTools Virtual Airbrush tool. Tilt can also be used for object rotation, or as a joystick.
During initialization you can check for the existence of tilt this way:
// Get info. about tilt struct tagAXIS tiltOrient[3]; BOOL tiltSupport = FALSE; tiltSupport = WTInfo (WTI_DEVICES, DVC_ORIENTATION, &tiltOrient); if (tiltSupport ) { // Does the tablet support azimuth and altitude if (tiltOrient[0].axResolution && tiltOrient[1].axResolution) { // Get resolution azimuth = tiltOrient[0].axResolution; altitude = tiltOrient[1].axResolution; } else { tiltSupport = FALSE; } }Tilt data is in the pkOrientation packet.
New Features to Implement
New features introduced with the Intuos series tablets include: Unique ID , Airbrush Fingerwheel, 4D Mouse Rotation, and 4D Mouse Thumbwheel. If your application already supports tablets these features are VERY easy to add. An old feature which is now fully supported is Dual Tracking
Dual Tracking
Dual Tracking, also referred to as multimode, allows two devices to be tracked at the same time. With the UDII series 12 x 12 and larger it is possible to simultaneously track a stylus and a puck. With the Intuos series it is possible to track ANY two devices.
Working with two tools at once expands your capabilities in many unique ways. Stretch a line from both ends, or move a frisket with one hand while airbrushing around it with the other. Move a magnifying glass with one hand and draw on either the magnified, or the unmagnified image with the other. See the paper Bier, Eric A., Stone, Maureen C., Pier, Ken, Buxton, William. "Toolglass and Magic Lenses: The See-Through Interface" Computer Graphics, (1993), 73-80 or the video tape submission A GUI Paradigm Using Tablets, Two Hands and Transparency George Fitzmaurice, Thomas Baudel, Gordon Kurtenbach and Bill Buxton, CHI 97 Video Program by ACM.
Here are some things to keep in mind when implementing dual tracking:
It is not possible to have the system move two screen cursors for you, it can only move one. So you will have to turn tracking on for one device and off for the other (or turn both off). You will have to move your own "screen cursor (sprite?)" for at least one device.
There is a major hand and a minor hand, the major hand, being your dominant hand, usually performs the more complex task. For example, the major hand does the drawing while the minor hand may hold a frisket.
Avoid collisions. The application should change the offsets for the two devices (or partition the tablet) so that you do not have to position the two devices on top of one another.
Pointing devices are indexed from zero to five. The application can tell which device generated a packet by looking at the packet’s
pkCursordata item. The possible index values are:Index 0 – Puck-like device #1
Index 1 – Stylus-like device #1
Index 2 – Inverted stylus-like device #1
Index 3 – Puck-like device #2
Index 4 – Stylus-like device #2
Index 5 – Inverted stylus-like device #2
The indexes are really placeholders for actual pointing devices. For example, the first time device index two enters the proximity of a context, it may be an inverted airbrush. As long as device index two is in proximity of the context, the device index of two uniquely identifies this physical device as the same inverted airbrush. However, after device two exits proximity, the next time the context sees a pointing device with an index of two enter proximity, index two may identify a different device, such as an inverted pen.
By default, tracking is turned on for the first device (
pkCursor= 0, 1, or 2). This is the first device to enter proximity. Tracking is turned off (pkCursor= 3, 4, or 5) for the second device to enter proximity. So, an application that does not support dual tracking will virtually have the second device turned off. If they are both on, the screen cursor jumps back and forth between them. You may need to reverse this on the fly (on aWT_CSRCHANGEmessage) based on which device is in which hand.Use the cursor number (
pkCursorfield) in the data packet to separate the packet stream into two streams (0-2 and 3-5) one steam for each device.Unique ID
There is another new feature to the Intuos series: Unique ID. A chip with a unique number is inside every device so every device can be uniquely identified. With Unique ID you can assign a specific drawing tool to a specific pointing device or use it to "sign" a document. You can restrict access to document layers to particular devices and have settings follow a device to other machines.
The ID code from the device is in two sections. It is the combination of the two that is guaranteed unique. One section, the
CSR_TYPE, is actually a code unique to each device type. The other section, theCSR_PHYSID, is a unique 32 bit number within a device type.CSR_PHYSIDmay be repeated between device types, but not within a type.The
CSR_TYPEis a coded bit field. Some bits have special meaning to the hardware, but are not of interest to a developer.What is of interest is:
There are 12 bits total.
The upper 4 bits, and a couple bits in the lower four, identify the device. The middle 4 bits are used to differentiate between electrically similar, but physically different devices, such as a general stylus with a different color. So to figure out a specific device type you mask with
0x0F06. For example maybe0x0812is a stylus that is black,0x0802is the standard stylus included in the box, and0x0832may be a stylus with one side switch.The currently supported types are:
General stylus:
(CSR_TYPE & 0x0F06) == 0x0802Airbrush:
(CSR_TYPE & 0x0F06) == 0x09024D Mouse:
(CSR_TYPE & 0x0F06) == 0x00045 button puck:
(CSR_TYPE& 0x0F06) == 0x0006To create the unique ID just concatenate
CSR_TYPE(without masking with0x0F06) andCSR_PHYSIDto make a 48 bit (you will probably use 64 bit) serial number.Airbrush Fingerwheel
The Intuos series tablets have a new device that is similar to an airbrush. It not only supports pressure on the tip, but also has a fingerwheel on the side to simultaneously vary a second value. For example, using the fingerwheel to vary ink density and, using pressure to vary line width.
The value of the fingerwheel on the side of the Airbrush is reported in the
pkTangentPREssure(tangential pressure) field so be sure to include it in your packets. Toward the front is 0, toward the rear is 1024. But don’t hard code 1024! Use the functionWTInfo( WTI_DEVICES, DVC_TPRESSURE )to query the tangent pressure range supported by the tablet.4D Mouse Rotation
The Intuos series tablets have a new device that is similar to a puck. In addition to normal puck parameters, this puck supports axial rotation and a thumbwheel on the side. For example, with the 4D Mouse rotation, you can rotate the paper or an object, and zoom with the thumbwheel.
4D Mouse axial rotation is reported in the
pkOrientation.orTwistfield. Values in this field range from 0 to 3600.During initialization you can check for the existence of rotation this way:
// Get info. about rotation struct tagAXIS tiltOrient[3]; BOOL rotateSupport = FALSE; rotateSupport = WTInfo (WTI_DEVICES, DVC_ORIENTATION, &tiltOrient); if (rotateSupport) { // Does the tablet support twist if (!tiltOrient[2].axResolution) { rotateSupport = FALSE; } }You would probably check for this at the same time you check for tilt, since it is in the same data structure.
4D Mouse Thumbwheel
Uses for the 4D Mouse thumbwheel include zooming and 3D navigation. The thumbwheel value is reported in the
pkZfield. The thumbwheel varies frompkZ = 1024when pushed forward topkZ = -1024when pulled back and snaps to the middle when released. The middle is 0, and the total range isfrom axMintoaxMax. The value axResolution is 0 if the tablet does not support the 4D Mouse (such as legacy tablets). The thumbwheel may not have a broad enough range for all uses. It is left as an exercise to the reader to devise a way to extend the range.During initialization you can check for the existence of the thumbwheel this way:
// Get info. about thumbwheel struct tagAXIS thumbwheelAxis; BOOL thumbwheelSupport = FALSE; thumbwheelSupport = WTInfo (WTI_DEVICES, DVC_Z, &thumbwheelAxis); if (thumbWheelSupport) { // Does the tablet support thumbwheel? if (!thumbwheelAxis.axResolution) { thumbwheelSupport = FALSE; } }ExpressKeys extension
This extension allows an application to receive ExpressKey event notifications, and to override the normal system-wide behavior of ExpressKeys. To make use of this extension, perform the following steps:
Make sure you have versions of
WINTAB.HandPKTDEF.Hthat support this extension (≥ 1.3).Detect at run-time whether EXPKEYS is supported by the installed driver. This can be done by scanning the
WTI_EXTENSIONSinformation categories for an extensions whose tag is equal toWTX_EXPKEYS. If none is found, EXPKEYS is not supported.The following information items are supported for this extension:
EXT_NAME— (TCHAR[]) The null-terminated string"ExpressKeys"EXT_TAG— (UINT) The tag valueWTX_EXPKEYS(5)EXT_SIZE— (WORD[4]) An ExpressKey mask with all available ExpressKey bits set to 1 (a union of all tablets' keys).
Determine the keys that are present by requesting the EXT_SIZE data item:
WORD expkeys[4]; WTInfo(WTI_EXTENSIONS+i, EXT_SIZE, expkeys);
The
expkeysarray now contains an ExpressKey mask with '1' bits for keys that are present (a union of all tablets' keys).Add the ExpressKeys data to your packet definition by adding the following declaration before including
PKTDEF.H:#define PACKETEXPKEYS PKEXT_ABSOLUTEThis causes the following ExpressKey mask to be included in the PACKET structure definition.
WORD pkExpKeys[4];Override ExpressKey behavior and notify Wintab that
pkExpKeysis to be populated by callingWTExtSet. OnceWTExtSethas been called with theWTX_EXPKEYStag, Wintab will send ExpressKey event packets until the context is closed, regardless of whether any keys are overridden.The
lpDataparameter toWTExtSetis interpreted as a pointer to an ExpressKey mask. A '1' bit results in the normal system-wide behavior of that key being disabled for all tablets.
NOTE: It is recommended that applications remove their overrides when the application's main window is no longer active (use WM_ACTIVATE). ExpressKey overrides take effect across the entire system; if an application leaves its override in place, that key will not function in other applications.
If a transducer is in proximity when an ExpressKey event occurs, the new ExpressKey state will be triggered with the next packet from that transducer. If no transducer is in proximity, a packet will be sent with only the following data items valid (remaining fields, such as pkX and pkY, are filled with zeros):
pkContextpkTimepkChangedpkSerialNumberpkCursor(0 or 6 — mimics puck #1 on the appropriate tablet)pkExpKeys
ExpressKey masks
ExpressKey masks are formatted as an array of 4 WORDs, corresponding to keys on the left, right, top and bottom of the tablet, respectively. Each WORD is formatted as a bit mask, with bit 0 corresponding to key 0, bit 1 to key 1, and so on.
ExpressKeys are mapped to bits depending on the tablet model. Some examples:
Intuos3 / Cintiq21 left ( mask[0])
Intuos3 / Cintiq21 right ( mask[1])
Graphire top ( mask[2])