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