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