xref: /haiku/src/add-ons/input_server/devices/wacom/TabletDevice.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
1 /*
2  * Copyright 2003-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Copyright 2000-2002  Olaf van Es. All Rights Reserved.
6  * Distributed under the terms of the MIT license.
7  *
8  * These people have added and tested device ids:
9  *
10  *		Frans van Nispen	<frans@xentronix.com>
11  *		Stefan Werner		<stefan@keindesign.de>
12  *		Hiroyuki Tsutsumi	<???>
13  *		Jeroen Oortwijn		<oortwijn@gmail.com>
14  * 		Calvin Hill			<calvin@hakobaito.co.uk>
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include <File.h>
22 #include <InterfaceDefs.h>
23 #include <Screen.h>
24 #include <View.h>
25 
26 #include "DeviceReader.h"
27 #include "MasterServerDevice.h"
28 
29 #include "TabletDevice.h"
30 
31 #define SNOOZE_AMOUNT 2500
32 #define JITTER_X .0007
33 #define JITTER_Y .0007
34 #define ACCELERATION_KICK_IN 2.3
35 
36 // constructor
37 TabletDevice::TabletDevice(MasterServerDevice* parent, DeviceReader* reader)
38 	: PointingDevice(parent, reader),
39 	  fThreadID(B_ERROR),
40 	  fDeviceMode(DEVICE_UNKOWN),
41 	  fMaxX(1.0),
42 	  fMaxY(1.0),
43 	  fPosX(0.5),
44 	  fPosY(0.5),
45 	  fFakeMouseX(0.5),
46 	  fFakeMouseY(0.5),
47 	  fButtons(0),
48 	  fPressure(0.0),
49 	  fModifiers(0),
50 	  fEraser(0),
51 	  fTiltX(0.0),
52 	  fTiltY(0.0),
53 	  fClicks(0),
54 	  fHasContact(false)
55 {
56 }
57 
58 // destructor
59 TabletDevice::~TabletDevice()
60 {
61 	// cleanup
62 	Stop();
63 }
64 
65 // InitCheck
66 status_t
67 TabletDevice::InitCheck()
68 {
69 	status_t status = PointingDevice::InitCheck();
70 	if (status >= B_OK)
71 		status = DetectDevice(fReader);
72 	return status;
73 }
74 
75 // Start
76 status_t
77 TabletDevice::Start()
78 {
79 	status_t status = B_NO_INIT;
80 	if (fReader) {
81 		fActive = true;
82 		// get a nice name for our polling thread
83 		const char* name;
84 		_GetName(fReader->ProductID(), &name);
85 		// start generating events
86 		fThreadID = spawn_thread(poll_usb_device, name, 104, this);
87 		if (fThreadID >= B_OK) {
88 			resume_thread(fThreadID);
89 			status = B_OK;
90 		} else
91 			status = fThreadID;
92 	}
93 	return status;
94 }
95 
96 // Stop
97 status_t
98 TabletDevice::Stop()
99 {
100 	status_t err = B_OK;
101 
102 	fActive = false;
103 	if (fThreadID >= B_OK)
104 		wait_for_thread(fThreadID, &err);
105 	fThreadID = B_ERROR;
106 
107 	return err;
108 }
109 
110 // DetectDevice
111 status_t
112 TabletDevice::DetectDevice(const DeviceReader* reader)
113 {
114 	status_t status = B_OK;
115 	switch (reader->ProductID()) {
116 		case 0x00:
117 			SetDevice(5040.0, 3780.0, DEVICE_PENPARTNER);
118 			break;
119 		case 0x03:
120 			SetDevice(2048.0, 15360.0, DEVICE_PL500);
121 			break;
122 		case 0x10:
123 		case 0x11:
124 		case 0x13:
125 			SetDevice(10206.0, 7422.0, DEVICE_GRAPHIRE);
126 			break;
127 		case 0x12:	// Graphire 3 4x5
128 			SetDevice(13918.0, 10206.0, DEVICE_GRAPHIRE);
129 			break;
130 		case 0x14:	// Graphire 3 6x8
131 			SetDevice(16704.0, 12064.0, DEVICE_GRAPHIRE);
132 			break;
133 		case 0x15:	// Graphire 4 4x5 (tested)
134 			SetDevice(10208.0, 7024.0, DEVICE_GRAPHIRE);
135 			break;
136 		case 0x16:	// Graphire 4 6x8 (tested)
137 			SetDevice(16704.0, 12064.0, DEVICE_GRAPHIRE);
138 			break;
139 		case 0x17:	// BambooFun 4x5 (from Linux Wacom Project)
140 			SetDevice(14760.0, 9225.0, DEVICE_BAMBOO);
141 			break;
142 		case 0x18:	// BambooFun 6x8 (from Linux Wacom Project)
143 			SetDevice(21648.0, 13530.0, DEVICE_BAMBOO);
144 			break;
145 		case 0x20:
146 			SetDevice(12700.0, 10600.0, DEVICE_INTUOS);
147 			break;
148 		case 0x21:
149 			SetDevice(20320.0, 16240.0);
150 			break;
151 		case 0x22:
152 			SetDevice(30480.0, 24060.0);
153 			break;
154 		case 0x23:
155 			SetDevice(30480.0, 31680.0);
156 			break;
157 		case 0x24:
158 			SetDevice(45720.0, 31680.0);
159 			break;
160 		case 0x30:
161 			SetDevice(5408.0, 4056.0, DEVICE_PL500);
162 			break;
163 		case 0x31:
164 			SetDevice(6144.0, 4608.0, DEVICE_PL500);
165 			break;
166 		case 0x32:
167 			SetDevice(6126.0, 4604.0, DEVICE_PL500);
168 			break;
169 		case 0x33:
170 			SetDevice(6260.0, 5016.0, DEVICE_PL500);
171 			break;
172 		case 0x34:
173 			SetDevice(6144.0, 4608.0, DEVICE_PL500);
174 			break;
175 		case 0x35:
176 			SetDevice(7220.0, 5780.0, DEVICE_PL500);
177 			break;
178 		case 0x3F:
179 			SetDevice(87200.0, 65600.0, DEVICE_CINTIQ);
180 			break;
181 		case 0x41:
182 			SetDevice(12700.0, 10600.0);
183 			break;
184 		case 0x42:
185 			SetDevice(20320.0, 16240.0);
186 			break;
187 		case 0x43:
188 			SetDevice(30480.0, 24060.0);
189 			break;
190 		case 0x44:
191 			SetDevice(30480.0, 31680.0);
192 			break;
193 		case 0x45:
194 			SetDevice(45720.0, 31680.0);
195 			break;
196 		case 0x47:	// some I2 6x8 report as 0x47
197 			SetDevice(20320.0, 16240.0);
198 			break;
199 		case 0x60:
200 			SetDevice(5104.0, 3712.0, DEVICE_GRAPHIRE);
201 			break;
202 		case 0x61: // PenStation
203 //			SetDevice(3403.0, 2475.0, DEVICE_GRAPHIRE); // this version was untested
204 			SetDevice(3248.0, 2320.0, DEVICE_PENSTATION); // this version came from "beer"
205 			break;
206 		case 0x62: // Volito
207 			SetDevice(5040.0, 3712.0, DEVICE_VOLITO);
208 			break;
209 		case 0x64:	// PenPartner.1
210 //			SetDevice(3450.0, 2100.0, DEVICE_PENSTATION);
211 			SetDevice(3248.0, 2320.0, DEVICE_PENSTATION);
212 			break;
213 		case 0x65:	// Bamboo (from Linux Wacom Project)
214 			SetDevice(14760.0, 9225.0, DEVICE_BAMBOO);
215 			break;
216 		case 0x69:	// Bamboo1 (from Linux Wacom Project)
217 			SetDevice(5104.0,  3712.0, DEVICE_BAMBOO);
218 			break;
219 		case 0xB0:
220 			SetDevice(25400.0, 20320.0, DEVICE_INTUOS3);
221 			break;
222 		case 0xB1:
223 			SetDevice(40640.0, 30480.0, DEVICE_INTUOS3);
224 			break;
225 		case 0xB2:
226 			SetDevice(60960.0, 45720.0, DEVICE_INTUOS3);
227 			break;
228 		case 0xB7:  // Wacom PTZ-431W Intuos3 4x6
229 			SetDevice(31496.0, 19685.0, DEVICE_INTUOS3);
230 			break;
231 		case 0xD0:	// Wacom Bamboo 2FG (from Linux Wacom Project)
232 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
233 			break;
234 		case 0xD1:	// Wacom BambooFun 2FG 4x5 (from Linux Wacom Project)
235 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
236 			break;
237 		case 0xD2:	// Wacom Bamboo Craft (from Linux Wacom Project)
238 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
239 			break;
240 		case 0xD3:	// Wacom BambooFun 2FG 6x8 (from Linux Wacom Project)
241 			SetDevice(21648.0, 13530.0, DEVICE_BAMBOO_PT);
242 			break;
243 		case 0xD4:	// Wacom Bamboo 4x5 (from Linux Wacom Project)
244 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
245 			break;
246 		case 0xD6:	// Wacom Bamboo CTH-460/K (from Linux Wacom Project)
247 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
248 			break;
249 		case 0xD7:	// Wacom Bamboo CTH-461/S (from Linux Wacom Project)
250 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
251 			break;
252 		case 0xD8:	// Wacom Bamboo CTH-661/S1 (from Linux Wacom Project)
253 			SetDevice(21648.0, 13530.0, DEVICE_BAMBOO_PT);
254 			break;
255 		case 0xDA:	// Wacom Bamboo CTH-461/L (from Linux Wacom Project)
256 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
257 			break;
258 		case 0xDB:	// Wacom Bamboo CTH-661 (from Linux Wacom Project)
259 			SetDevice(21648.0, 13530.0, DEVICE_BAMBOO_PT);
260 			break;
261 		case 0xDD:	// Wacom Bamboo Pen/Connect (CTL-470) (from Linux Wacom Project)
262 			SetDevice(14720.0, 9200.0, DEVICE_BAMBOO_PT);
263 			break;
264 		case 0x0301: // One by Wacom CTL-671
265 			SetDevice(21648.0, 13530.0, DEVICE_BAMBOO_PT);
266 			break;
267 		case 0x037b: // One by Wacom CTL-672
268 			SetDevice(21648.0, 13530.0, DEVICE_BAMBOO_PT);
269 			break;
270 		default:
271 			status = B_BAD_VALUE;
272 			break;
273 	}
274 	return status;
275 }
276 
277 // SetDevice
278 void
279 TabletDevice::SetDevice(float maxX, float maxY, uint32 mode)
280 {
281 	fDeviceMode = mode;
282 	fMaxX = maxX;
283 	fMaxY = maxY;
284 	fJitterX = JITTER_X;
285 	fJitterY = JITTER_Y;
286 }
287 
288 // ReadData
289 void
290 TabletDevice::ReadData(const uchar* data, int dataBytes, bool& hasContact,
291 	uint32& mode, uint32& buttons, float& x, float& y, float& pressure,
292 	int32& clicks, int32& eraser, float& wheelX, float& wheelY,
293 	float& tiltX, float& tiltY) const
294 {
295 	hasContact = false;
296 	buttons = 0;
297 	mode = MODE_PEN;
298 	bool firstButton = false;
299 	bool secondButton = false;
300 	bool thirdButton = false;
301 	uint16 xPos = 0;
302 	uint16 yPos = 0;
303 
304 	switch (fDeviceMode) {
305 		case DEVICE_PENPARTNER: {
306 			xPos = data[2] << 8 | data[1];
307 			yPos = data[4] << 8 | data[3];
308 
309 			eraser = (data[5] & 0x20);
310 
311 			int8 pressureData = data[6];
312 			pressure = (float)(pressureData + 120) / 240.0;
313 
314 			firstButton = ((pressureData > -80) && !(data[5] & 0x20));
315 			secondButton = (data[5] & 0x40);
316 
317 			hasContact = true;
318 			break;
319 		}
320 		case DEVICE_GRAPHIRE:
321 		case DEVICE_BAMBOO:
322 		{
323 			xPos = data[3] << 8 | data[2];
324 			yPos = data[5] << 8 | data[4];
325 
326 			hasContact = (data[1] & 0x80);
327 
328 			uint16 pressureData = data[7] << 8 | data[6];
329 			pressure = (float)pressureData / 511.0;
330 			eraser = (data[1] & 0x20);
331 
332 			// mouse wheel support
333 			if (data[1] & 0x40) {	// mouse is on tablet!
334 				wheelY = (float)(int8)data[6];
335 				mode = MODE_MOUSE;
336 				// override contact to loose it as soon as possible
337 				// when mouse is lifted from tablet
338 				hasContact = (uint8)data[7] >= 30;
339 				pressure = 0.0;
340 				eraser = 0;
341 			}
342 
343 			firstButton = pressure > 0.0 ? true : (data[1] & 0x01);
344 			secondButton = (data[1] & 0x02);
345 			thirdButton = (data[1] & 0x04);
346 
347 			break;
348 		}
349 		case DEVICE_BAMBOO_PT:
350 		{
351 			if (dataBytes < 20) {	// ignore touch-packets
352 				xPos = data[3] << 8 | data[2];
353 				yPos = data[5] << 8 | data[4];
354 
355 				hasContact = (data[1] & 0x20);
356 
357 				uint16 pressureData = data[7] << 8 | data[6];
358 				pressure = (float)pressureData / 1023.0;
359 				eraser = (data[1] & 0x08);
360 
361 				firstButton = (data[1] & 0x01);
362 				secondButton = (data[1] & 0x02);
363 				thirdButton = (data[1] & 0x04);
364 
365 				break;
366 			}
367 		}
368 		case DEVICE_INTUOS:
369 		case DEVICE_INTUOS3:
370 		case DEVICE_CINTIQ:
371 			if ((data[0] == 0x02) && !(((data[1] >> 5) & 0x03) == 0x02)) {
372 				if (fDeviceMode == DEVICE_INTUOS3) {
373 					xPos = (data[2] << 9) | (data[3] << 1)
374 						| ((data[9] >> 1) & 1);
375 					yPos = (data[4] << 9) | (data[5] << 1) | (data[9] & 1);
376 				} else {
377 					xPos = (data[2] << 8) | data[3];
378 					yPos = (data[4] << 8) | data[5];
379 				}
380 				uint16 pressureData = data[6] << 2 | ((data[7] >> 6) & 0x03);
381 				pressure = (float)pressureData / 1023.0;
382 
383 				// mouse and wheel support
384 				if (data[1] == 0xf0) {	// mouse is on tablet!
385 					mode = MODE_MOUSE;
386 
387 					if (data[8] == 0x02)
388 						wheelY = 1.0;
389 					else if (data[8] == 0x01)
390 						wheelY = -1.0;
391 
392 					firstButton = (data[8] & 0x04);
393 					secondButton = (data[8] & 0x10);
394 					thirdButton = (data[8] & 0x08);
395 
396 					// override contact to loose it as soon as possible
397 					// when mouse is lifted from tablet
398 					hasContact = data[9] <= 0x68;
399 					pressure = 0.0;
400 					eraser = 0;
401 				} else {
402 					firstButton = (data[6] > 2);
403 						// For Intuos it MUST be >1,
404 						// but '>1' still gets false actuations (shaking)
405 					secondButton = (data[1] & 0x02);
406 					thirdButton = (data[1] & 0x04);
407 					hasContact = (data[1] & 0x40);
408 						// TODO: is this meaningful? (always true on Intuos)
409 					if (fDeviceMode == DEVICE_INTUOS) {	// TODO: test perhaps superfluous?
410 						// Original Intuos protocol:
411 						//  data[6] is used to signal use of the eraser,
412 						//  as well as being the high bits of pressure.
413 						//  While not in contact:
414 						//	 If the pen end is lowermost data[6] = 1;
415 						//	 If the eraser end is down data[6] = 0, and pressure is strictly 0
416 						//   data[9] (top 5 bits: 0x70..0xd0) indicates height above the tablet.
417 						eraser = fEraser;
418 							// keep established value unless not touching pad
419 						// Eraser state only valid when away from surface
420 						if (data[6] <= 1 && data[9] > 0x80) { 	// not touching tablet
421 							if (pressureData == 0) eraser = 1;	// strictly 0 means eraser
422 							else if (pressureData > 6) eraser = 0;	// avoid slop
423 						}
424 					}
425 					// Get raw tilt values (0..54..127)
426 					int8 tiltDataX = ((data[7] & 0x3f) << 1) | ((data[8] & 0x80) >> 7);
427 					int8 tiltDataY = data[8] & 0x7f;
428 					// convert to floats
429 					tiltX = (float)(tiltDataX - 64) / 64.0;
430 					tiltY = (float)(tiltDataY - 64) / 64.0;
431 				}
432 			}
433 			break;
434 		case DEVICE_PL500: {
435 			hasContact = ( data[1] & 0x20);
436 			xPos = data[2] << 8 | data[3];
437 			yPos = data[5] << 8 | data[6];
438 			firstButton = (data[4] & 0x08);
439 			secondButton = (data[4] & 0x10);
440 			thirdButton = (data[4] & 0x20);
441 			uint16 pressureData = (data[4] & 0x04) >> 2 | (data[7] & 0x7f) << 1;
442 			pressure = (float)pressureData / 511.0;
443 			break;
444 		}
445 		case DEVICE_VOLITO: {
446 			eraser = 0;
447 			thirdButton = 0;
448 
449 			xPos = data[3] << 8 | data[2];
450 			yPos = data[5] << 8 | data[4];
451 
452 			hasContact = (data[1] & 0x80);
453 
454 			firstButton = (data[1] & 0x01) == 1;
455 			secondButton = data[1] & 0x04;
456 
457 			uint16 pressureData = data[7] << 8 | data[6];
458 			pressure = (float)pressureData / 511.0;
459 
460 			if (data[1] & 0x40) {	// mouse is on tablet
461 				wheelY = 0;
462 				mode = MODE_MOUSE;
463 				hasContact = (uint8)data[7] >= 30;
464 				pressure = 0.0;
465 				secondButton = data[1] & 0x02;
466 			}
467 
468 			break;
469 		}
470 		case DEVICE_PENSTATION: {
471 			xPos = data[3] << 8 | data[2];
472 			yPos = data[5] << 8 | data[4];
473 			hasContact = (data[1] & 0x10);
474 			uint16 pressureData = data[7] << 8 | data[6];
475 			pressure = (float)pressureData / 511.0;
476 			firstButton = (data[1] & 0x01);
477 			secondButton = (data[1] & 0x02);
478 			thirdButton = (data[1] & 0x04);
479 			break;
480 		}
481 	}
482 	if (pressure > 1.0)
483 		pressure = 1.0;
484 	else if (pressure < 0.0)
485 		pressure = 0.0;
486 	buttons = (firstButton ? B_PRIMARY_MOUSE_BUTTON : 0)
487 			   | (secondButton ? B_SECONDARY_MOUSE_BUTTON : 0)
488 			   | (thirdButton ? B_TERTIARY_MOUSE_BUTTON : 0);
489 	x = (float)xPos;
490 	y = (float)yPos;
491 }
492 
493 // SetStatus
494 void
495 TabletDevice::SetStatus(uint32 mode, uint32 buttons, float x, float y,
496 	float pressure, int32 clicks, uint32 modifiers, int32 eraser,
497 	float wheelX, float wheelY, float tiltX, float tiltY, const uchar* data)
498 {
499 	if (fActive) {
500 		uint32 what = B_MOUSE_MOVED;
501 		if (buttons > fButtons)
502 			what = B_MOUSE_DOWN;
503 		else if (buttons < fButtons)
504 			what = B_MOUSE_UP;
505 
506 
507 #if DEBUG
508 	float tabletX = x;
509 	float tabletY = y;
510 #endif
511 		x /= fMaxX;
512 		y /= fMaxY;
513 
514 		float deltaX = 0.0;
515 		float deltaY = 0.0;
516 
517 		float absDeltaX = 0.0;
518 		float absDeltaY = 0.0;
519 
520 		float unfilteredX = x;
521 		float unfilteredY = y;
522 
523 		if (fHasContact) {
524 			deltaX = x - fPosX;
525 			deltaY = y - fPosY;
526 
527 			absDeltaX = fabsf(deltaX);
528 			absDeltaY = fabsf(deltaY);
529 
530 #if 0 //DEBUG
531 fParent->LogString() << "x: " << x << ", y: " << y << ", pressure: " << pressure << "\n";
532 fParent->LogString() << "tilt x: " << tiltX << ", tilt y: " << tiltY << "\n\n";
533 #endif
534 			// apply a bit of filtering
535 			if (absDeltaX < fJitterX)
536 				x = fPosX;
537 			if (absDeltaY < fJitterY)
538 				y = fPosY;
539 		}
540 
541 		// only do send message if something changed
542 		if (x != fPosX || y != fPosY || fButtons != buttons || pressure != fPressure
543 			|| fEraser != eraser || fTiltX != tiltX || fTiltY != tiltY) {
544 
545 			bigtime_t now = system_time();
546 
547 			// common fields for any mouse message
548 			BMessage* event = new BMessage(what);
549 			event->AddInt64("when", now);
550 			event->AddInt32("buttons", buttons);
551 			if (mode == MODE_PEN) {
552 				event->AddFloat("x", x);
553 				event->AddFloat("y", y);
554 				event->AddFloat("be:tablet_x", unfilteredX);
555 				event->AddFloat("be:tablet_y", unfilteredY);
556 				event->AddFloat("be:tablet_pressure", pressure);
557 				event->AddInt32("be:tablet_eraser", eraser);
558 				if (_DeviceSupportsTilt()) {
559 					event->AddFloat("be:tablet_tilt_x", tiltX);
560 					event->AddFloat("be:tablet_tilt_y", tiltY);
561 				}
562 				// adjust mouse coordinates as well
563 				// to have the mouse appear at the pens
564 				// last position when switching
565 				fFakeMouseX = unfilteredX;
566 				fFakeMouseY = unfilteredY;
567 			} else if (mode == MODE_MOUSE) {
568 				// apply acceleration
569 				float accelerationX = fJitterX * ACCELERATION_KICK_IN;
570 //				if (absDeltaX > accelerationX)
571 					deltaX *= absDeltaX / accelerationX;
572 				float accelerationY = fJitterY * ACCELERATION_KICK_IN;
573 //				if (absDeltaY > accelerationY)
574 					deltaY *= absDeltaY / accelerationY;
575 				// calculate screen coordinates
576 				fFakeMouseX = min_c(1.0, max_c(0.0, fFakeMouseX + deltaX));
577 				fFakeMouseY = min_c(1.0, max_c(0.0, fFakeMouseY + deltaY));
578 				event->AddFloat("x", fFakeMouseX);
579 				event->AddFloat("y", fFakeMouseY);
580 				event->AddFloat("be:tablet_x", fFakeMouseX);
581 				event->AddFloat("be:tablet_y", fFakeMouseY);
582 			}
583 			event->AddInt32("modifiers", modifiers);
584 
585 #if DEBUG
586 if (data) {
587 	event->AddData("raw usb data", B_RAW_TYPE, data, 12);
588 }
589 event->AddFloat("tablet x", tabletX);
590 event->AddFloat("tablet y", tabletY);
591 #endif
592 			// additional fields for mouse down or up
593 			if (what == B_MOUSE_DOWN) {
594 				if (now - fLastClickTime < fParent->DoubleClickSpeed()) {
595 					fClicks++;
596 					if (fClicks > 3)
597 						fClicks = 1;
598 				} else {
599 					fClicks = 1;
600 				}
601 				fLastClickTime = now;
602 				event->AddInt32("clicks", fClicks);
603 			} else if (what == B_MOUSE_UP)
604 				event->AddInt32("clicks", 0);
605 
606 			status_t ret = fParent->EnqueueMessage(event);
607 			if (ret < B_OK)
608 				PRINT(("EnqueueMessage(): %s\n", strerror(ret)));
609 
610 			// apply values to members
611 			fPosX = x;
612 			fPosY = y;
613 			fButtons = buttons;
614 			fPressure = pressure;
615 			fModifiers = modifiers;
616 			fEraser = eraser;
617 			fTiltX = tiltX;
618 			fTiltY = tiltY;
619 		}
620 
621 		// separate wheel changed message
622 		if (fWheelX != wheelX || fWheelY != wheelY) {
623 			BMessage* event = new BMessage(B_MOUSE_WHEEL_CHANGED);
624 			event->AddInt64("when", system_time());
625 			event->AddFloat("be:wheel_delta_x", wheelX);
626 			event->AddFloat("be:wheel_delta_y", wheelY);
627 			fParent->EnqueueMessage(event);
628 
629 			// apply values to members
630 			fWheelX = wheelX;
631 			fWheelY = wheelY;
632 		}
633 	}
634 }
635 
636 // SetContact
637 void
638 TabletDevice::SetContact(bool contact)
639 {
640 	fHasContact = contact;
641 }
642 
643 // poll_usb_device
644 int32
645 TabletDevice::poll_usb_device(void* arg)
646 {
647 	TabletDevice* tabletDevice = (TabletDevice*)arg;
648 	DeviceReader* reader = tabletDevice->fReader;
649 
650 	if (!reader || reader->InitCheck() < B_OK)
651 		return B_BAD_VALUE;
652 
653 	int dataBytes = reader->MaxPacketSize();
654 	if (dataBytes > 128)
655 		return B_BAD_VALUE;
656 
657 	uchar data[max_c(12, dataBytes)];
658 
659 	while (tabletDevice->IsActive()) {
660 
661 		status_t ret = reader->ReadData(data, dataBytes);
662 
663 		if (ret == dataBytes) {
664 			// data we read from the wacom device
665 			uint32 mode;
666 			bool hasContact = false;
667 			uint32 buttons = 0;
668 			float x = 0.0;
669 			float y = 0.0;
670 			float pressure = 0.0;
671 			int32 clicks = 0;
672 			int32 eraser = 0;
673 			float wheelX = 0.0;
674 			float wheelY = 0.0;
675 			float tiltX = 0.0;
676 			float tiltY = 0.0;
677 			// let the device extract all information from the data
678 			tabletDevice->ReadData(data, dataBytes, hasContact, mode, buttons,
679 								   x, y, pressure, clicks, eraser,
680 								   wheelX, wheelY, tiltX, tiltY);
681 			if (hasContact) {
682 				// apply the changes to the device
683 				tabletDevice->SetStatus(mode, buttons, x, y, pressure,
684 										clicks, modifiers(), eraser,
685 										wheelX, wheelY, tiltX, tiltY, data);
686 			} else
687 				PRINT(("device has no contact\n"));
688 			tabletDevice->SetContact(hasContact);
689 		} else {
690 			PRINT(("failed to read %ld bytes, read: %ld or %s\n",
691 				dataBytes, ret, strerror(ret)));
692 
693 			if (ret < B_OK) {
694 				if (ret == B_TIMED_OUT)
695 					snooze(SNOOZE_AMOUNT);
696 				else if (ret == B_INTERRUPTED)
697 					snooze(SNOOZE_AMOUNT);
698 				else {
699 					return ret;
700 				}
701 			}
702 		}
703 	}
704 
705 	return B_OK;
706 }
707 
708 // _DeviceSupportsTilt
709 bool
710 TabletDevice::_DeviceSupportsTilt() const
711 {
712 	bool tilt = false;
713 	switch (fDeviceMode) {
714 		case DEVICE_INTUOS:
715 		case DEVICE_INTUOS3:
716 		case DEVICE_CINTIQ:
717 			tilt = true;
718 			break;
719 	}
720 	return tilt;
721 }
722 
723 // _GetName
724 void
725 TabletDevice::_GetName(uint16 productID, const char** name) const
726 {
727 	switch (productID) {
728 		case 0x00:
729 			*name = "Wacom USB";
730 			break;
731 		case 0x03:	// driver does not support this yet
732 			*name = "Wacom Cintiq Partner USB";
733 			break;
734 		case 0x10:
735 			*name = "Wacom Graphire USB";
736 			break;
737 		case 0x11:
738 			*name = "Wacom Graphire2 4x5\" USB";
739 			break;
740 		case 0x12:
741 			*name = "Wacom Graphire2 5x7\" USB";
742 			break;
743 		case 0x13:
744 			*name = "Wacom Graphire3 4x5\" USB";
745 			break;
746 		case 0x14:
747 			*name = "Wacom Graphire3 6x8\" USB";
748 			break;
749 		case 0x15:
750 			*name = "Wacom Graphire4 4x5\" USB";
751 			break;
752 		case 0x16:
753 			*name = "Wacom Graphire4 6x8\" USB";
754 			break;
755 		case 0x17:
756 			*name = "Wacom BambooFun 4x5\" USB";
757 			break;
758 		case 0x18:
759 			*name = "Wacom BambooFun 6x8\" USB";
760 			break;
761 		case 0x20:
762 			*name = "Wacom Intuos 4x5\" USB";
763 			break;
764 		case 0x21:
765 			*name = "Wacom Intuos 6x8\" USB";
766 			break;
767 		case 0x22:
768 			*name = "Wacom Intuos 9x12\" USB";
769 			break;
770 		case 0x23:
771 			*name = "Wacom Intuos 12x12\" USB";
772 			break;
773 		case 0x24:
774 			*name = "Wacom Intuos 12x18\" USB";
775 			break;
776 		case 0x30:
777 			*name = "Wacom PL400 USB";
778 			break;
779 		case 0x31:
780 			*name = "Wacom PL500 USB";
781 			break;
782 		case 0x32:
783 			*name = "Wacom PL600 USB";
784 			break;
785 		case 0x33:
786 			*name = "Wacom PL600SX USB";
787 			break;
788 		case 0x34:
789 			*name = "Wacom PL550 USB";
790 			break;
791 		case 0x35:
792 			*name = "Wacom PL800 USB";
793 			break;
794 
795 		case 0x3F:
796 			*name = "Wacom Cintiq 21UX USB";
797 			break;
798 
799 		case 0x41:
800 			*name = "Wacom Intuos2 4x5\" USB";
801 			break;
802 		case 0x42:
803 			*name = "Wacom Intuos2 6x8\" USB";
804 			break;
805 		case 0x43:
806 			*name = "Wacom Intuos2 9x12\" USB";
807 			break;
808 		case 0x44:
809 			*name = "Wacom Intuos2 12x12\" USB";
810 			break;
811 		case 0x45:
812 			*name = "Wacom Intuos2 12x18\" USB";
813 			break;
814 		case 0x47:	// some I2 6x8s seem to report as 0x47
815 			*name = "Wacom Intuos2 6x8\" USB";
816 			break;
817 
818 		case 0x60:
819 			*name = "Wacom Volito USB";
820 			break;
821 		case 0x61:
822 			*name = "Wacom PenStation USB";
823 			break;
824 		case 0x62:
825 			*name = "Wacom Volito2 USB";
826 			break;
827 		case 0x64:
828 			*name = "Wacom PenPartner.1 USB";
829 			break;
830 		case 0x65:
831 			*name = "Wacom Bamboo USB";
832 			break;
833 		case 0x69:
834 			*name = "Wacom Bamboo1 USB";
835 			break;
836 
837 		case 0xB0:
838 			*name = "Wacom Intuos3 4x5 USB";
839 			break;
840 		case 0xB1:
841 			*name = "Wacom Intuos3 6x8 USB";
842 			break;
843 		case 0xB2:
844 			*name = "Wacom Intuos3 9x12 USB";
845 			break;
846 
847 		case 0xD0:
848 			*name = "Wacom Bamboo 2FG USB";
849 			break;
850 		case 0xD1:
851 			*name = "Wacom BambooFun 2FG 4x5\" USB";
852 			break;
853 		case 0xD2:
854 			*name = "Wacom Bamboo Craft USB";
855 			break;
856 		case 0xD3:
857 			*name = "Wacom BambooFun 2FG 6x8\" USB";
858 			break;
859 		case 0xD4:
860 			*name = "Wacom Bamboo 4x5\" USB";
861 			break;
862 		case 0xD6:
863 			*name = "Wacom Bamboo (CTH-460/K)";
864 			break;
865 		case 0xD7:
866 			*name = "Wacom Bamboo (CTH-461/S)";
867 			break;
868 		case 0xD8:
869 			*name = "Wacom Bamboo (CTH-661/S1)";
870 			break;
871 		case 0xDA:
872 			*name = "Wacom Bamboo (CTH-461/L)";
873 			break;
874 		case 0xDB:
875 			*name = "Wacom Bamboo (CTH-661)";
876 			break;
877 		case 0xDD:
878 			*name = "Wacom Bamboo Pen/Connect (CTL-470)";
879 			break;
880 		case 0x0301:
881 			*name = "One by Wacom (CTL-671)";
882 			break;
883 		case 0x037b:
884 			*name = "One by Wacom (CTL-672)";
885 			break;
886 
887 		default:
888 			*name = "<unkown wacom tablet>";
889 			break;
890 	}
891 }
892 
893