* added an input_server add-on for interfacing with the wacom kernel driver

* the structure of the add-on was originally designed to handle all kinds of
  different input devices, but has been limited to handle Wacom tablets for
  now, since our mouse add-on handles the rest already.
* various Wacom Tablets are supported including Cintiq Partner, Graphire,
  Graphire2/3/4, Intuos, Intous2/3, PenStation, PenPartner and Volito

TODO: Currently, the add-on does not work for some reason, though the kernel
driver publishes a devfs entry and the input_server picks it up, the Wacom
device thread is running. Need to investigate...


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23667 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2008-01-20 19:14:25 +00:00
parent 7fd8d11d3a
commit dc1bdabb92
13 changed files with 1869 additions and 0 deletions

View File

@ -5,4 +5,5 @@ SubInclude HAIKU_TOP src add-ons input_server devices keyboard ;
SubInclude HAIKU_TOP src add-ons input_server devices mouse ;
#SubInclude HAIKU_TOP src add-ons input_server devices serial_mouse ;
#SubInclude HAIKU_TOP src add-ons input_server devices tablet ;
SubInclude HAIKU_TOP src add-ons input_server devices wacom ;

View File

@ -0,0 +1,30 @@
Overview:
The actual input_server addon is the MasterServerDevice. It watches for device entries (dis)appearing in /dev/input/wacom/. For each appearing entry, it then asks the PointingDeviceFactory to obtain a PointingDevice. The factory constructs a DeviceReader, which interfaces with the usb driver/device. The MasterServerDevice puts the PointingDevice obtained into a list and manages it from there. All PointingDevices in the list are Start()ed and Stop()ped according to the input_server requests. A PointingDevice has access to its parent MasterServerDevice and can use global functionality like the acceleration table and other useful information. The baseclass PointingDevice doesn't do anything useful. In another version of the driver code, there are two types of devices supported in subclasses, USB Mice and Wacom tablets. For USB Mice, there is a subclass MouseDevice, which has child objects for interpreting data from Generic Mice, Logitec Optical Mice and IBM Optical Scrollpoint Mice. This (Haiku) version of the driver however only supports Wacom tablets.
-------------------------
DeviceReader:
A class to interface with the wacom kernel driver. Each object has a BFile which it uses to read packets from the usb device. The kernel driver puts 8 bytes of informative data in front of each buffer. If requested to read more than 8 bytes of data, an interrupt transfer will be queued, otherwise just the informative data is returned. The DeviceReader can therefor be used to obtain information about vendor id, product id as well as max packet size. When using DeviceReader::ReadData(), the object skips the 8 bytes in front of the buffer that the driver reads and returns just the actual data from the interrupt transfer.
MasterServerDevice:
This class implements the actual input_server add-on (device). It maintains a list of PointingDevice objects, which it starts and stops according to general input_server requests. The devices are added or removed according to the contents of the /dev/input/wacom folder, which is scanned at first Start() hook call and is afterwards node monitored.
PointingDevice:
This class implements a simple interface for the MasterServerDevice and encapsulates a single physical pointing device object. The actual functionality is implemented in derived classes, which at the moment are USB Wacom tablets (TabletDevice). A pointing device has a reference to the MasterServerDevice so that certain values can be shared across all PointingDevice objects, like for example the acceleration table or double click speed. The two most important methods are Start() and Stop().
PointingDeviceFactory:
This class encapsulates the creation of PointingDevice objects, in that it constructs a DeviceReader object for a given path (ie. "/dev/input/wacom/0"), and has some knowledge of which objects to create for certain vendor/product ids that it obtains from the DeviceReader. It is only used by MasterServerDevice.
--------
MouseDevice: (not present in Haiku version of the driver)
This class implements a Mouse by subclassing from PointingDevice. It owns a DeviceReader and Mouse object, which knows how to interprete data read from the DeviceReader. Different mouse products are implemented in subclasses of Mouse, most importantly there is a GenericMouse class. MouseDevice implements Start() and Stop() to run a thread which constantly reads from the DeviceReader and enqueues input event messages using the reference to the MasterServerDevice.
TabletDevice:
This class is very similar to MouseDevice, the only difference being, that it supports all kinds of Wacom tablets. It needs to be refactored somewhat so that different kinds of tablets are implemented in small Tablet objects, which interprete the raw usb data differently. It would then be absolutely similar in design to the MouseDevice branch of classes.

View File

@ -0,0 +1,142 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include "DeviceReader.h"
#include <malloc.h>
#include <string.h>
#include <File.h>
// constructor
DeviceReader::DeviceReader()
: fDevicePath(NULL),
fDeviceFile(NULL),
fVendorID(0),
fProductID(0),
fMaxPackedSize(0)
{
}
// destructor
DeviceReader::~DeviceReader()
{
_Unset();
}
// SetTo
status_t
DeviceReader::SetTo(const char* path)
{
status_t ret = B_BAD_VALUE;
if (path) {
_Unset();
fDevicePath = strdup(path);
fDeviceFile = new BFile(path, B_READ_ONLY);
ret = fDeviceFile->InitCheck();
if (ret >= B_OK) {
// read 8 bytes from the file and initialize
// the rest of the object variables
uint8 buffer[8];
ret = fDeviceFile->Read(buffer, 8);
if (ret == 8) {
ret = B_OK;
uint16* ids = (uint16*)buffer;
fVendorID = ids[0];
fProductID = ids[1];
uint32* ps = (uint32*)buffer;
fMaxPackedSize = ps[1];
} else {
_Unset();
}
}
}
return ret;
}
// InitCheck
status_t
DeviceReader::InitCheck() const
{
return fDeviceFile && fDevicePath ? fDeviceFile->InitCheck() : B_NO_INIT;
}
// DevicePath
const char*
DeviceReader::DevicePath() const
{
return fDevicePath;
}
// DeviceFile
BFile*
DeviceReader::DeviceFile() const
{
return fDeviceFile;
}
// VendorID
uint16
DeviceReader::VendorID() const
{
return fVendorID;
}
// ProductID
uint16
DeviceReader::ProductID() const
{
return fProductID;
}
// MaxPacketSize
size_t
DeviceReader::MaxPacketSize() const
{
return fMaxPackedSize;
}
// ReadData
status_t
DeviceReader::ReadData(uint8* data, size_t size) const
{
status_t ret = B_NO_INIT;
if (fDeviceFile) {
ret = fDeviceFile->InitCheck();
if (ret >= B_OK) {
uint8* buffer = new uint8[fMaxPackedSize + 8];
ret = fDeviceFile->Read(buffer, fMaxPackedSize + 8);
if (ret == (ssize_t)(fMaxPackedSize + 8)) {
// make sure we don't copy too many bytes
size_t length = min_c(size, fMaxPackedSize);
memcpy(data, buffer + 8, length);
// zero out any remaining bytes
if (size > fMaxPackedSize)
memset(data + length, 0, size - fMaxPackedSize);
// operation could be considered successful
ret = length;
} else if (ret == 8 || ret == B_TIMED_OUT) {
// it's ok if the operation timed out
memset(data, 0, size);
ret = B_OK;
}
delete[] buffer;
}
}
return ret;
}
// _Unset
void
DeviceReader::_Unset()
{
free(fDevicePath);
fDevicePath = NULL;
delete fDeviceFile;
fDeviceFile = NULL;
fVendorID = 0;
fProductID = 0;
fMaxPackedSize = 0;
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef USB_DEVICE_MONITOR_H
#define USB_DEVICE_MONITOR_H
// This class is designed to work with the generic USB input device driver
// the driver creates an entry in /dev/input/???
// clients can open and read from the device file, the driver will than
// block on waiting for an interrupt transfer (timeout is 500 ms) and write
// the received data to the supplied buffer. In front of the buffer, it will
// write vendorID, productID and maxPacketSize, so you can read from the file
// with a buffer of just enough size to contain these fields, in this case, no
// interrupt transfer will be triggered, and the client can then configure itself
// with a more appropriate setup.
#include <SupportDefs.h>
class BFile;
class BString;
class DeviceReader {
public:
DeviceReader();
virtual ~DeviceReader();
// initializes the object
// by trying to read from the supplied device file
// on success (B_OK), all member variables will be set
// and the object is ready for operation
// on failure, a hopefully meaningful error is returned
virtual status_t SetTo(const char* path);
virtual status_t InitCheck() const;
const char* DevicePath() const;
BFile* DeviceFile() const;
// query the device for information
uint16 VendorID() const;
uint16 ProductID() const;
size_t MaxPacketSize() const;
// trigger an interrupt transfer and write the data in the buffer
// it should be save to call this function with size != MaxPacketSize
status_t ReadData(uint8* data, size_t size) const;
protected:
void _Unset();
char* fDevicePath;
BFile* fDeviceFile;
uint16 fVendorID;
uint16 fProductID;
size_t fMaxPackedSize;
};
#endif // USB_DEVICE_MONITOR_H

View File

@ -0,0 +1,17 @@
SubDir HAIKU_TOP src add-ons input_server devices wacom ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders input ;
Addon <input>wacom :
DeviceReader.cpp
MasterServerDevice.cpp
PointingDevice.cpp
PointingDeviceFactory.cpp
TabletDevice.cpp
: be input_server ;
Package haiku-inputkit-cvs :
<input>tablet :
boot home config add-ons input_server devices ;

View File

@ -0,0 +1,486 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include "MasterServerDevice.h"
#include <fstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Directory.h>
#include <Entry.h>
#include <InterfaceDefs.h>
#include <Message.h>
#include <NodeMonitor.h>
#include <OS.h>
#include <Path.h>
#include <Screen.h>
#include <View.h>
#include <File.h>
#include "PointingDevice.h"
#include "PointingDeviceFactory.h"
#define DEFAULT_CLICK_SPEED 250000
#define DEBUG 0
static const char* kWatchFolder = "input/wacom/usb";
static const char* kDeviceFolder = "/dev/input/wacom/usb";
#if DEBUG
static const char* kLogFilePath = "/tmp/wacom.log";
#endif
//static const char* kPS2MouseThreadName = "PS/2 Mouse";
// instantiate_input_device
//
// this is where it all starts make sure this function is exported!
BInputServerDevice*
instantiate_input_device()
{
return (new MasterServerDevice());
}
// constructor
MasterServerDevice::MasterServerDevice()
: BInputServerDevice(),
fDevices(1),
fActive(false),
fLogString(""),
fDblClickSpeed(DEFAULT_CLICK_SPEED),
fPS2DisablerThread(B_ERROR),
fDeviceLock("device list lock")
{
get_mouse_speed(&fSpeed);
get_mouse_acceleration(&fAcceleration);
get_click_speed(&fDblClickSpeed);
_CalculateAccelerationTable();
}
// destructor
MasterServerDevice::~MasterServerDevice()
{
// cleanup
_StopAll();
if (_LockDevices()) {
while (PointingDevice* device = (PointingDevice*)fDevices.RemoveItem(0L))
delete device;
_UnlockDevices();
}
}
// InitCheck
status_t
MasterServerDevice::InitCheck()
{
input_device_ref device = { "Wacom Tablets",
B_POINTING_DEVICE, (void*)this };
input_device_ref* deviceList[2] = { &device, NULL };
RegisterDevices(deviceList);
return B_OK;
}
// SystemShuttingDown
status_t
MasterServerDevice::SystemShuttingDown()
{
// the devices should be stopped anyways,
// but this is just defensive programming.
// the pointing devices will have spawned polling threads,
// we make sure that we block until every thread has bailed out.
_StopAll();
#if DEBUG
fLogString << "---------------------------------\n\n";
DumpLogString(kLogFilePath);
#endif
return (BInputServerDevice::SystemShuttingDown());
}
// Start
//
// start generating events
status_t
MasterServerDevice::Start(const char* device, void* cookie)
{
if (_LockDevices()) {
// do an initial scan of the devfs folder, after that, we should receive
// node monitor messages for hotplugging support
_SearchDevices();
fActive = true;
for (int32 i = 0; PointingDevice* device = (PointingDevice*)fDevices.ItemAt(i); i++) {
device->Start();
}
_UnlockDevices();
}
// TODO: make this configurable
// _StartPS2DisablerThread();
return StartMonitoringDevice(kWatchFolder);
}
// Stop
status_t
MasterServerDevice::Stop(const char* device, void* cookie)
{
// stop generating events
fActive = false;
_StopAll();
// _StopPS2DisablerThread();
return StopMonitoringDevice(kWatchFolder);
}
// Control
status_t
MasterServerDevice::Control(const char* device, void* cookie, uint32 code, BMessage* message)
{
// respond to changes in the system
switch (code) {
case B_MOUSE_SPEED_CHANGED:
get_mouse_speed(&fSpeed);
_CalculateAccelerationTable();
break;
case B_CLICK_SPEED_CHANGED:
get_click_speed(&fDblClickSpeed);
break;
case B_MOUSE_ACCELERATION_CHANGED:
get_mouse_acceleration(&fAcceleration);
_CalculateAccelerationTable();
break;
case B_NODE_MONITOR:
_HandleNodeMonitor(message);
break;
default:
BInputServerDevice::Control(device, cookie, code, message);
break;
}
return B_OK;
}
// LogDataBytes
void
MasterServerDevice::LogDataBytes(uchar* data, int bytes)
{
for (int32 i = 0; i < bytes; i += 2)
fLogString << (uint32)data[i] << " " << (uint32)data[i + 1] << " ";
fLogString << "\n";
}
// DumpLogString
void
MasterServerDevice::DumpLogString(const char* path)
{
if (fLogString.Length() > 0) {
BFile logFile(path, B_WRITE_ONLY | B_CREATE_FILE);
if (logFile.InitCheck() >= B_OK) {
logFile.Seek(0, SEEK_END);
logFile.Write(fLogString.String(), fLogString.Length());
fLogString.SetTo("");
}
logFile.Unset();
}
}
// #pragma mark -
// _SearchDevices
void
MasterServerDevice::_SearchDevices()
{
if (_LockDevices()) {
// construct a PointingDevice for every devfs entry we find
BDirectory dir(kDeviceFolder);
if (dir.InitCheck() >= B_OK) {
// traverse the contents of the folder to see if the
// entry of that device still exists
entry_ref ref;
while (dir.GetNextRef(&ref) >= B_OK) {
// don't add the control device
#if DEBUG
fLogString << "examining devfs entry '" << ref.name << "'\n";
#endif
if (strcmp(ref.name, "control") != 0) {
BPath path(&ref);
if (path.InitCheck() >= B_OK) {
// add the device
_AddDevice(path.Path());
}
}
#if DEBUG
fLogString << "\n";
#endif
}
} else {
#if DEBUG
fLogString << "folder '" << kDeviceFolder <<"' not found\n";
#endif
}
#if DEBUG
fLogString << "done examing devfs\n";
DumpLogString("kLogFilePath");
#endif
_UnlockDevices();
}
}
// _StopAll
void
MasterServerDevice::_StopAll()
{
if (_LockDevices()) {
for (int32 i = 0; PointingDevice* device
= (PointingDevice*)fDevices.ItemAt(i); i++)
device->Stop();
_UnlockDevices();
}
}
// _AddDevice
void
MasterServerDevice::_AddDevice(const char* path)
{
if (_LockDevices()) {
// get the device from the factory
PointingDevice* device = PointingDeviceFactory::DeviceFor(this, path);
// add it to our list
if (device && device->InitCheck() >= B_OK
&& fDevices.AddItem((void*)device)) {
#if DEBUG
fLogString << "pointing device added (" << path << ")\n";
// DumpLogString("kLogFilePath");
#endif
// start device polling only if we're started
if (fActive)
device->Start();
} else {
#if DEBUG
fLogString << "pointing device not added (" << path << ")\n";
if (device) {
char temp[256];
sprintf(temp, " vendor: %0*x, product: %0*x\n", 4, device->VendorID(),
4, device->ProductID());
fLogString << temp;
}
// DumpLogString("kLogFilePath");
#endif
delete device;
}
_UnlockDevices();
}
}
// _HandleNodeMonitor
void
MasterServerDevice::_HandleNodeMonitor(BMessage* message)
{
int32 opcode;
if (message->FindInt32("opcode", &opcode) < B_OK)
return;
switch (opcode) {
case B_ENTRY_CREATED:
// extract info to create an entry_ref structure
const char* name;
ino_t directory;
dev_t device;
if (message->FindString("name", &name) >= B_OK
&& strcmp(name, "control") != 0
&& message->FindInt64("directory", (int64*)&directory) >= B_OK
&& message->FindInt32("device", (int32*)&device) >= B_OK) {
// make a path from that info
entry_ref ref(device, directory, name);
BPath path(&ref);
if (path.InitCheck() >= B_OK) {
// add the device
_AddDevice(path.Path());
}
}
break;
case B_ENTRY_REMOVED: {
// I cannot figure out how to see if this is actually
// the directory that we're node monitoring, and even if it is,
// we would have to look at the directories contents to
// see which PointingDevice we might want to remove
BDirectory dir(kDeviceFolder);
if (dir.InitCheck() >= B_OK) {
// for each device in our list, see if the corresponding
// entry in the device folder still exists
for (int32 i = 0; PointingDevice* pointingDevice
= (PointingDevice*)fDevices.ItemAt(i); i++) {
// traverse the contents of the folder
// if the entry for this device is there,
// we can abort the search
entry_ref ref;
dir.Rewind();
bool found = false;
while (dir.GetNextRef(&ref) >= B_OK) {
BPath path(&ref);
if (strcmp(pointingDevice->DevicePath(),
path.Path()) == 0) {
found = true;
break;
}
}
// remove the device if the devfs entry was not found
if (!found) {
#if DEBUG
fLogString << "removing device '" << pointingDevice->DevicePath() << "'\n";
#endif
if (_LockDevices()) {
if (fDevices.RemoveItem((void*)pointingDevice))
delete pointingDevice;
_UnlockDevices();
}
}
}
}
break;
}
}
}
// _CalculateAccelerationTable
//
// calculates the acceleration table. This is recalculated anytime we find out that
// the current acceleration or speed has changed.
void
MasterServerDevice::_CalculateAccelerationTable()
{
// This seems to work alright.
for (int x = 1; x <= 128; x++){
fAccelerationTable[x] = (x / (128 * (1 - (((float)fSpeed + 8192) / 262144))))
* (x / (128 * (1 - (((float)fSpeed + 8192) / 262144))))
+ ((((float)fAcceleration + 4000) / 262144)
* (x / (128 * (1 - (((float)fSpeed + 8192) / 262144)))));
}
// sets the bottom of the table to be the inverse of the first half.
// position 0 -> 128 positive movement, 255->129 negative movement
for (int x = 255; x > 128; x--){
fAccelerationTable[x] = -(fAccelerationTable[(255 - x)]);
}
// Location 0 will be 0.000, which is unacceptable, otherwise, single step moves are lost
// To work around this, we'll make it 1/2 of the smallest increment.
fAccelerationTable[0] = fAccelerationTable[1] / 2;
fAccelerationTable[255] = -fAccelerationTable[0];
}
// _ps2_disabler
//
// find the PS/2 device thread and suspend its execution
// TODO: make this configurable
// In case you're wondering... this behaviour is useful for notebook
// users who are annoyed by accidentally hitting their touchpad while
// typing. "Turning it off entirely" by suspending the PS/2 thread is
// just a compfort thing, but should of course be made configurable...
//int32
//MasterServerDevice::_ps2_disabler(void* cookie)
//{
// MasterServerDevice* master = (MasterServerDevice*)cookie;
//
// thread_id haltedThread = B_ERROR;
//
// while (master->fActive) {
// // search for at least one running device
// bool suspendPS2 = false;
// if (master->_LockDevices()) {
// for (int32 i = 0; PointingDevice* device = (PointingDevice*)master->fDevices.ItemAt(i); i++) {
// if (device->DisablePS2()) {
// suspendPS2 = true;
// break;
// }
// }
// master->_UnlockDevices();
// }
//
// if (suspendPS2){
// // find and suspend PS/2 thread (if not already done)
// if (haltedThread < B_OK) {
// haltedThread = find_thread(kPS2MouseThreadName);
// if (haltedThread >= B_OK) {
// suspend_thread(haltedThread);
// }
// }
// } else {
// // reenable PS/2 thread
// if (haltedThread >= B_OK) {
// resume_thread(haltedThread);
// haltedThread = B_ERROR;
// }
// }
//
// // sleep a little while
// snooze(2000000);
// }
//
// // reenable PS/2 thread in any case before we die
// if (haltedThread >= B_OK) {
// resume_thread(haltedThread);
// }
//
// return B_OK;
//}
//
//// _StartPS2DisablerThread
//void
//MasterServerDevice::_StartPS2DisablerThread()
//{
// if (fPS2DisablerThread < B_OK) {
// fPS2DisablerThread = spawn_thread(_ps2_disabler, "PS/2 Mouse disabler", B_LOW_PRIORITY, this);
// if (fPS2DisablerThread >= B_OK)
// resume_thread(fPS2DisablerThread);
// }
//}
//
//// _StopPS2DisablerThread
//void
//MasterServerDevice::_StopPS2DisablerThread()
//{
// if (fPS2DisablerThread >= B_OK) {
// status_t err;
// wait_for_thread(fPS2DisablerThread, &err);
// }
// fPS2DisablerThread = B_ERROR;
//}
// _LockDevices
bool
MasterServerDevice::_LockDevices()
{
return fDeviceLock.Lock();
}
// _UnlockDevices
void
MasterServerDevice::_UnlockDevices()
{
fDeviceLock.Unlock();
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef MASTER_SERVER_DEVICE_H
#define MASTER_SERVER_DEVICE_H
#include <add-ons/input_server/InputServerDevice.h>
#include <List.h>
#include <Locker.h>
#include <String.h>
// export this for the input_server
extern "C" _EXPORT BInputServerDevice* instantiate_input_device();
class MasterServerDevice : public BInputServerDevice {
public:
MasterServerDevice();
virtual ~MasterServerDevice();
// BInputServerDevice
virtual status_t InitCheck();
virtual status_t SystemShuttingDown();
virtual status_t Start(const char* device, void* cookie);
virtual status_t Stop(const char* device, void* cookie);
virtual status_t Control(const char *device,
void *cookie,
uint32 code,
BMessage *message);
// MasterServerDevice
bigtime_t DoubleClickSpeed() const
{ return fDblClickSpeed; }
const float* AccelerationTable() const
{ return fAccelerationTable; }
// debugging
inline BString& LogString()
{ return fLogString; }
void LogDataBytes(uchar* data, int bytes);
void DumpLogString(const char* path);
private:
void _SearchDevices();
void _StopAll();
void _AddDevice(const char* path);
void _HandleNodeMonitor(BMessage* message);
void _CalculateAccelerationTable();
// thread function for watching
// the status of master device
// static int32 _ps2_disabler(void* cookie);
// void _StartPS2DisablerThread();
// void _StopPS2DisablerThread();
bool _LockDevices();
void _UnlockDevices();
// list of mice objects
BList fDevices;
volatile bool fActive;
// debugging
BString fLogString;
// global stuff for all mice objects
int32 fSpeed;
int32 fAcceleration;
bigtime_t fDblClickSpeed;
float fAccelerationTable[256];
// support halting the PS/2 mouse thread as long as we exist
thread_id fPS2DisablerThread;
BLocker fDeviceLock;
};
#endif // MASTER_SERVER_DEVICE_H

View File

@ -0,0 +1,87 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include <malloc.h>
#include <string.h>
#include "DeviceReader.h"
#include "PointingDevice.h"
// constructor
PointingDevice::PointingDevice(MasterServerDevice* parent,
DeviceReader* reader)
: fParent(parent),
fReader(reader),
fActive(false)
{
}
// destructor
PointingDevice::~PointingDevice()
{
delete fReader;
}
// InitCheck
status_t
PointingDevice::InitCheck()
{
return fParent && fReader ? fReader->InitCheck() : B_NO_INIT;
}
// SetActive
void
PointingDevice::SetActive(bool active)
{
fActive = active;
}
// IsActive
bool
PointingDevice::IsActive() const
{
return fActive;
}
// DevicePath
const char*
PointingDevice::DevicePath() const
{
if (fReader) {
return fReader->DevicePath();
}
return NULL;
}
// DisablePS2
bool
PointingDevice::DisablePS2() const
{
return false;
}
// VendorID
uint16
PointingDevice::VendorID() const
{
if (fReader)
return fReader->VendorID();
return 0;
}
// ProductID
uint16
PointingDevice::ProductID() const
{
if (fReader)
return fReader->ProductID();
return 0;
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef POINTING_DEVICE_H
#define POINTING_DEVICE_H
#include <SupportDefs.h>
class DeviceReader;
class MasterServerDevice;
class PointingDevice {
public:
PointingDevice(MasterServerDevice* parent,
DeviceReader* reader);
virtual ~PointingDevice();
virtual status_t InitCheck();
virtual status_t Start() = 0;
virtual status_t Stop() = 0;
virtual void SetActive(bool active);
bool IsActive() const;
// forwards the device path of the reader
const char* DevicePath() const;
// hook function to determine if
// PS/2 Mouse thread should be disabled
virtual bool DisablePS2() const;
// query the device for information
uint16 VendorID() const;
uint16 ProductID() const;
protected:
MasterServerDevice* fParent;
DeviceReader* fReader;
volatile bool fActive;
};
#endif // POINTING_DEVICE_H

View File

@ -0,0 +1,38 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include "PointingDeviceFactory.h"
#include "DeviceReader.h"
#include "TabletDevice.h"
static const uint16 kVendorWacom = 0x056a;
/*static*/ PointingDevice*
PointingDeviceFactory::DeviceFor(MasterServerDevice* parent, const char* path)
{
PointingDevice* device = NULL;
DeviceReader* reader = new DeviceReader();
if (reader->SetTo(path) >= B_OK) {
switch (reader->VendorID()) {
case kVendorWacom:
device = new TabletDevice(parent, reader);
break;
default:
delete reader;
break;
}
} else {
delete reader;
}
return device;
}
// forbidden:
PointingDeviceFactory::PointingDeviceFactory() {}
PointingDeviceFactory::~PointingDeviceFactory() {}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef POINTING_DEVICE_FACTORY_H
#define POINTING_DEVICE_FACTORY_H
#include <SupportDefs.h>
class MasterServerDevice;
class PointingDevice;
class PointingDeviceFactory {
public:
static PointingDevice* DeviceFor(MasterServerDevice* parent,
const char* path);
private:
PointingDeviceFactory();
~PointingDeviceFactory();
};
#endif // POINTING_DEVICE_FACTORY_H

View File

@ -0,0 +1,742 @@
/*
* Copyright 2003-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Copyright 2000-2002 Olaf van Es. All Rights Reserved.
* Distributed under the terms of the MIT license.
*
* These people have added and tested device ids:
*
* Frans van Nispen <frans@xentronix.com>
* Stefan Werner <stefan@keindesign.de>
* Hiroyuki Tsutsumi <???>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <File.h>
#include <InterfaceDefs.h>
#include <Screen.h>
#include <View.h>
#include "DeviceReader.h"
#include "MasterServerDevice.h"
#include "TabletDevice.h"
#define SNOOZE_AMOUNT 2500
#define JITTER_X .0007
#define JITTER_Y .0007
#define ACCELERATION_KICK_IN 2.3
#define DEBUG 0
// constructor
TabletDevice::TabletDevice(MasterServerDevice* parent, DeviceReader* reader)
: PointingDevice(parent, reader),
fThreadID(B_ERROR),
fDeviceMode(DEVICE_UNKOWN),
fMaxX(1.0),
fMaxY(1.0),
fDataBytes(10),
fPosX(0.5),
fPosY(0.5),
fFakeMouseX(0.5),
fFakeMouseY(0.5),
fButtons(0),
fPressure(0.0),
fModifiers(0),
fEraser(0),
fTiltX(0.0),
fTiltY(0.0),
fClicks(0),
fHasContact(false)
{
}
// destructor
TabletDevice::~TabletDevice()
{
// cleanup
Stop();
}
// InitCheck
status_t
TabletDevice::InitCheck()
{
status_t status = PointingDevice::InitCheck();
if (status >= B_OK)
status = DetectDevice(fReader->ProductID());
return status;
}
// Start
status_t
TabletDevice::Start()
{
status_t status = B_NO_INIT;
if (fReader) {
fActive = true;
// get a nice name for our polling thread
const char* name;
_GetName(fReader->ProductID(), &name);
// start generating events
fThreadID = spawn_thread(poll_usb_device, name, 104, this);
if (fThreadID >= B_OK) {
resume_thread(fThreadID);
status = B_OK;
} else
status = fThreadID;
}
return status;
}
// Stop
status_t
TabletDevice::Stop()
{
status_t err = B_OK;
fActive = false;
if (fThreadID >= B_OK)
wait_for_thread(fThreadID, &err);
fThreadID = B_ERROR;
return err;
}
// DetectDevice
status_t
TabletDevice::DetectDevice(uint16 product)
{
status_t status = B_OK;
switch (product) {
case 0x00:
SetDevice(5040.0, 3780.0, DEVICE_PENPARTNER, 7);
break;
case 0x03:
SetDevice(2048.0, 15360.0, DEVICE_PL500, 8);
break;
case 0x10:
case 0x11:
case 0x13:
SetDevice(10206.0, 7422.0, DEVICE_GRAPHIRE, 8);
break;
case 0x12: // Graphire 3 4x5
SetDevice(13918.0, 10206.0, DEVICE_GRAPHIRE, 8);
break;
case 0x14: // Graphire 3 6x8
SetDevice(16704.0, 12064.0, DEVICE_GRAPHIRE, 8);
break;
case 0x15: // Graphire 4 4x5 (tested)
SetDevice(10208.0, 7024.0, DEVICE_GRAPHIRE, 8);
break;
case 0x16: // Graphire 4 6x8 (tested)
SetDevice(16704.0, 12064.0, DEVICE_GRAPHIRE, 8);
break;
case 0x20:
SetDevice(12700.0, 10600.0);
break;
case 0x21:
SetDevice(20320.0, 16240.0);
break;
case 0x22:
SetDevice(30480.0, 24060.0);
break;
case 0x23:
SetDevice(30480.0, 31680.0);
break;
case 0x24:
SetDevice(45720.0, 31680.0);
break;
case 0x30:
SetDevice(5408.0, 4056.0, DEVICE_PL500, 8);
break;
case 0x31:
SetDevice(6144.0, 4608.0, DEVICE_PL500, 8);
break;
case 0x32:
SetDevice(6126.0, 4604.0, DEVICE_PL500, 8);
break;
case 0x33:
SetDevice(6260.0, 5016.0, DEVICE_PL500, 8);
break;
case 0x34:
SetDevice(6144.0, 4608.0, DEVICE_PL500, 8);
break;
case 0x35:
SetDevice(7220.0, 5780.0, DEVICE_PL500, 8);
break;
case 0x3F:
SetDevice(87200.0, 65600.0, DEVICE_CINTIQ, 10);
break;
case 0x41:
SetDevice(12700.0, 10600.0);
break;
case 0x42:
SetDevice(20320.0, 16240.0);
break;
case 0x43:
SetDevice(30480.0, 24060.0);
break;
case 0x44:
SetDevice(30480.0, 31680.0);
break;
case 0x45:
SetDevice(45720.0, 31680.0);
break;
case 0x47: // some I2 6x8 report as 0x47
SetDevice(20320.0, 16240.0);
break;
case 0x60:
SetDevice(5104.0, 3712.0, DEVICE_GRAPHIRE, 8);
break;
case 0x61: // PenStation
// SetDevice(3403.0, 2475.0, DEVICE_GRAPHIRE, 8); // this version was untested
SetDevice(3248.0, 2320.0, DEVICE_PENSTATION, 8); // this version came from "beer"
break;
case 0x62: // Volito
SetDevice(5040.0, 3712.0, DEVICE_VOLITO, 8);
break;
case 0x64: // PenPartner.1
// SetDevice(3450.0, 2100.0, DEVICE_PENSTATION, 8);
SetDevice(3248.0, 2320.0, DEVICE_PENSTATION, 8);
break;
case 0xB0:
SetDevice(25400.0, 20320.0, DEVICE_INTUOS3, 10);
break;
case 0xB1:
// tested:
SetDevice(20320.0, 15230.0, DEVICE_INTUOS3, 10);
// Frans:
// SetDevice(40640.0, 30480.0, DEVICE_INTUOS3, 10);
break;
case 0xB2:
SetDevice(60960.0, 45720.0, DEVICE_INTUOS3, 10);
break;
default:
status = B_BAD_VALUE;
break;
}
return status;
}
// SetDevice
void
TabletDevice::SetDevice(float maxX, float maxY, uint32 mode, int dataBytes)
{
fDeviceMode = mode;
fMaxX = maxX;
fMaxY = maxY;
fDataBytes = dataBytes;
fJitterX = JITTER_X;
fJitterY = JITTER_Y;
}
// ReadData
void
TabletDevice::ReadData(uchar* data, bool& hasContact, uint32& mode,
uint32& buttons, float& x, float& y, float& pressure,
int32& clicks, int32& eraser, float& wheelX, float& wheelY,
float& tiltX, float& tiltY) const
{
hasContact = false;
buttons = 0;
mode = MODE_PEN;
bool firstButton = false;
bool secondButton = false;
bool thirdButton = false;
uint16 xPos = 0;
uint16 yPos = 0;
switch (fDeviceMode) {
case DEVICE_PENPARTNER: {
xPos = data[2] << 8 | data[1];
yPos = data[4] << 8 | data[3];
eraser = (data[5] & 0x20);
int8 pressureData = data[6];
pressure = (float)(pressureData + 120) / 240.0;
firstButton = ((pressureData > -80) && !(data[5] & 0x20));
secondButton = (data[5] & 0x40);
hasContact = true;
break;
}
case DEVICE_GRAPHIRE: {
xPos = data[3] << 8 | data[2];
yPos = data[5] << 8 | data[4];
hasContact = (data[1] & 0x80);
uint16 pressureData = data[7] << 8 | data[6];
pressure = (float)pressureData / 511.0;
eraser = (data[1] & 0x20);
// mouse wheel support
if (data[1] & 0x40) { // mouse is on tablet!
wheelY = (float)(int8)data[6];
mode = MODE_MOUSE;
// override contact to loose it as soon as possible
// when mouse is lifted from tablet
hasContact = (uint8)data[7] >= 30;
pressure = 0.0;
eraser = 0;
}
firstButton = pressure > 0.0 ? true : (data[1] & 0x01);
secondButton = (data[1] & 0x02);
thirdButton = (data[1] & 0x04);
break;
}
case DEVICE_INTUOS:
case DEVICE_INTUOS3:
case DEVICE_CINTIQ:
if ((data[0] == 0x02) && !(((data[1] >> 5) & 0x03) == 0x02)) {
xPos = data[2] << 8 | data[3];
yPos = data[4] << 8 | data[5];
uint16 pressureData = data[6] << 2 | ((data[7] >> 6) & 0x03);
pressure = (float)pressureData / 1023.0;
// mouse and wheel support
if (data[1] == 0xf0) { // mouse is on tablet!
mode = MODE_MOUSE;
if (data[8] == 0x02)
wheelY = 1.0;
else if (data[8] == 0x01)
wheelY = -1.0;
firstButton = (data[8] & 0x04);
secondButton = (data[8] & 0x10);
thirdButton = (data[8] & 0x08);
// override contact to loose it as soon as possible
// when mouse is lifted from tablet
hasContact = data[9] <= 0x68;
pressure = 0.0;
eraser = 0;
} else {
// eraser = (data[1] & 0x20); // eraser is een tool-id
// firstButton = (pressureData > 0) && data[9] <= 0x68;// > 180);
// firstButton = (pressureData > 180);
firstButton = (data[6] > 0);
secondButton = (data[1] & 0x02);
thirdButton = (data[1] & 0x04);
hasContact = (data[1] & 0x40);
// convert tilt (-128 ... 127)
// int8 tiltDataX = ((data[7] & 0x3f) << 2) | ((data[8] & 0x80) >> 6);
int8 tiltDataX = ((data[7] & 0x3f) << 1) | ((data[8] & 0x80) >> 7);
int8 tiltDataY = data[8] & 0x7f;
// int8 tiltDataY = 0;
// convert to floats
tiltX = (float)(tiltDataX - 64) / 64.0;
tiltY = (float)(tiltDataY - 64) / 64.0;
}
}
break;
case DEVICE_PL500: {
hasContact = ( data[1] & 0x20);
xPos = data[2] << 8 | data[3];
yPos = data[5] << 8 | data[6];
firstButton = (data[4] & 0x08);
secondButton = (data[4] & 0x10);
thirdButton = (data[4] & 0x20);
uint16 pressureData = (data[4] & 0x04) >> 2 | (data[7] & 0x7f) << 1;
pressure = (float)pressureData / 511.0;
break;
}
case DEVICE_VOLITO: {
eraser = 0;
thirdButton = 0;
xPos = data[3] << 8 | data[2];
yPos = data[5] << 8 | data[4];
hasContact = (data[1] & 0x80);
firstButton = (data[1] & 0x01) == 1;
secondButton = data[1] & 0x04;
uint16 pressureData = data[7] << 8 | data[6];
pressure = (float)pressureData / 511.0;
if (data[1] & 0x40) { // mouse is on tablet
wheelY = 0;
mode = MODE_MOUSE;
hasContact = (uint8)data[7] >= 30;
pressure = 0.0;
secondButton = data[1] & 0x02;
}
break;
}
case DEVICE_PENSTATION: {
xPos = data[3] << 8 | data[2];
yPos = data[5] << 8 | data[4];
hasContact = (data[1] & 0x10);
uint16 pressureData = data[7] << 8 | data[6];
pressure = (float)pressureData / 511.0;
firstButton = (data[1] & 0x01);
secondButton = (data[1] & 0x02);
thirdButton = (data[1] & 0x04);
break;
}
}
if (pressure > 1.0)
pressure = 1.0;
else if (pressure < 0.0)
pressure = 0.0;
buttons = (firstButton ? B_PRIMARY_MOUSE_BUTTON : 0)
| (secondButton ? B_SECONDARY_MOUSE_BUTTON : 0)
| (thirdButton ? B_TERTIARY_MOUSE_BUTTON : 0);
x = (float)xPos;
y = (float)yPos;
}
// SetStatus
void
TabletDevice::SetStatus(uint32 mode, uint32 buttons, float x, float y,
float pressure, int32 clicks, uint32 modifiers, int32 eraser,
float wheelX, float wheelY, float tiltX, float tiltY, const uchar* data)
{
if (fActive) {
uint32 what = B_MOUSE_MOVED;
if (buttons > fButtons)
what = B_MOUSE_DOWN;
else if (buttons < fButtons)
what = B_MOUSE_UP;
#if DEBUG
float tabletX = x;
float tabletY = y;
#endif
x /= fMaxX;
y /= fMaxY;
float deltaX = 0.0;
float deltaY = 0.0;
float absDeltaX = 0.0;
float absDeltaY = 0.0;
float unfilteredX = x;
float unfilteredY = y;
if (fHasContact) {
deltaX = x - fPosX;
deltaY = y - fPosY;
absDeltaX = fabsf(deltaX);
absDeltaY = fabsf(deltaY);
#if 0 //DEBUG
fParent->LogString() << "x: " << x << ", y: " << y << ", pressure: " << pressure << "\n";
fParent->LogString() << "tilt x: " << tiltX << ", tilt y: " << tiltY << "\n\n";
#endif
// apply a bit of filtering
if (absDeltaX < fJitterX)
x = fPosX;
if (absDeltaY < fJitterY)
y = fPosY;
}
// only do send message if something changed
if (x != fPosX || y != fPosY || fButtons != buttons || pressure != fPressure
|| fEraser != eraser || fTiltX != tiltX || fTiltY != tiltY) {
bigtime_t now = system_time();
// common fields for any mouse message
BMessage* event = new BMessage(what);
event->AddInt64("when", now);
event->AddInt32("buttons", buttons);
if (mode == MODE_PEN) {
event->AddFloat("x", x);
event->AddFloat("y", y);
event->AddFloat("be:tablet_x", unfilteredX);
event->AddFloat("be:tablet_y", unfilteredY);
event->AddFloat("be:tablet_pressure", pressure);
event->AddInt32("be:tablet_eraser", eraser);
if (_DeviceSupportsTilt()) {
event->AddFloat("be:tablet_tilt_x", tiltX);
event->AddFloat("be:tablet_tilt_y", tiltY);
}
// adjust mouse coordinates as well
// to have the mouse appear at the pens
// last position when switching
fFakeMouseX = unfilteredX;
fFakeMouseY = unfilteredY;
} else if (mode == MODE_MOUSE) {
// apply acceleration
float accelerationX = fJitterX * ACCELERATION_KICK_IN;
// if (absDeltaX > accelerationX)
deltaX *= absDeltaX / accelerationX;
float accelerationY = fJitterY * ACCELERATION_KICK_IN;
// if (absDeltaY > accelerationY)
deltaY *= absDeltaY / accelerationY;
// calculate screen coordinates
fFakeMouseX = min_c(1.0, max_c(0.0, fFakeMouseX + deltaX));
fFakeMouseY = min_c(1.0, max_c(0.0, fFakeMouseY + deltaY));
event->AddFloat("x", fFakeMouseX);
event->AddFloat("y", fFakeMouseY);
event->AddFloat("be:tablet_x", fFakeMouseX);
event->AddFloat("be:tablet_y", fFakeMouseY);
}
event->AddInt32("modifiers", modifiers);
#if DEBUG
if (data) {
event->AddData("raw usb data", B_RAW_TYPE, data, 12);
}
event->AddFloat("tablet x", tabletX);
event->AddFloat("tablet y", tabletY);
#endif
// additional fields for mouse down or up
if (what == B_MOUSE_DOWN) {
if (now - fLastClickTime < fParent->DoubleClickSpeed()) {
fClicks++;
if (fClicks > 3)
fClicks = 1;
} else {
fClicks = 1;
}
fLastClickTime = now;
event->AddInt32("clicks", fClicks);
} else if (what == B_MOUSE_UP)
event->AddInt32("clicks", 0);
fParent->EnqueueMessage(event);
// apply values to members
fPosX = x;
fPosY = y;
fButtons = buttons;
fPressure = pressure;
fModifiers = modifiers;
fEraser = eraser;
fTiltX = tiltX;
fTiltY = tiltY;
}
// separate wheel changed message
if (fWheelX != wheelX || fWheelY != wheelY) {
BMessage* event = new BMessage(B_MOUSE_WHEEL_CHANGED);
event->AddInt64("when", system_time());
event->AddFloat("be:wheel_delta_x", wheelX);
event->AddFloat("be:wheel_delta_y", wheelY);
fParent->EnqueueMessage(event);
// apply values to members
fWheelX = wheelX;
fWheelY = wheelY;
}
}
}
// SetContact
void
TabletDevice::SetContact(bool contact)
{
fHasContact = contact;
}
// poll_usb_device
int32
TabletDevice::poll_usb_device(void* arg)
{
TabletDevice* tabletDevice = (TabletDevice*)arg;
DeviceReader* reader = tabletDevice->fReader;
if (reader && reader->InitCheck() >= B_OK) {
int dataBytes = tabletDevice->DataBytes();
uchar* data = new uchar[max_c(12, dataBytes)];
while (tabletDevice->IsActive()) {
status_t ret = reader->ReadData(data, dataBytes);
if (ret == dataBytes) {
// data we read from the wacom device
uint32 mode;
bool hasContact = false;
uint32 buttons = 0;
float x = 0.0;
float y = 0.0;
float pressure = 0.0;
int32 clicks = 0;
int32 eraser = 0;
float wheelX = 0.0;
float wheelY = 0.0;
float tiltX = 0.0;
float tiltY = 0.0;
// let the device extract all information from the data
tabletDevice->ReadData(data, hasContact, mode, buttons,
x, y, pressure, clicks, eraser,
wheelX, wheelY, tiltX, tiltY);
if (hasContact) {
// apply the changes to the device
tabletDevice->SetStatus(mode, buttons, x, y, pressure,
clicks, modifiers(), eraser,
wheelX, wheelY, tiltX, tiltY, data);
}
tabletDevice->SetContact(hasContact);
} else {
if (ret < B_OK) {
if (ret == B_TIMED_OUT)
snooze(SNOOZE_AMOUNT);
else if (ret == B_INTERRUPTED)
snooze(SNOOZE_AMOUNT);
else {
delete[] data;
return ret;
}
}
}
}
delete[] data;
}
return B_OK;
}
// _DeviceSupportsTilt
bool
TabletDevice::_DeviceSupportsTilt() const
{
bool tilt = false;
switch (fDeviceMode) {
case DEVICE_INTUOS:
case DEVICE_INTUOS3:
case DEVICE_CINTIQ:
tilt = true;
break;
}
return tilt;
}
// _GetName
void
TabletDevice::_GetName(uint16 productID, const char** name) const
{
switch (productID) {
case 0x00:
*name = "Wacom USB";
break;
case 0x03: // driver does not support this yet
*name = "Wacom Cintiq Partner USB";
break;
case 0x10:
*name = "Wacom Graphire USB";
break;
case 0x11:
*name = "Wacom Graphire2 4x5\" USB";
break;
case 0x12:
*name = "Wacom Graphire2 5x7\" USB";
break;
case 0x13:
*name = "Wacom Graphire3 4x5\" USB";
break;
case 0x14:
*name = "Wacom Graphire3 6x8\" USB";
break;
case 0x15:
*name = "Wacom Graphire4 4x5\" USB";
break;
case 0x16:
*name = "Wacom Graphire4 6x8\" USB";
break;
case 0x20:
*name = "Wacom Intuos 4x5\" USB";
break;
case 0x21:
*name = "Wacom Intuos 6x8\" USB";
break;
case 0x22:
*name = "Wacom Intuos 9x12\" USB";
break;
case 0x23:
*name = "Wacom Intuos 12x12\" USB";
break;
case 0x24:
*name = "Wacom Intuos 12x18\" USB";
break;
case 0x30:
*name = "Wacom PL400 USB";
break;
case 0x31:
*name = "Wacom PL500 USB";
break;
case 0x32:
*name = "Wacom PL600 USB";
break;
case 0x33:
*name = "Wacom PL600SX USB";
break;
case 0x34:
*name = "Wacom PL550 USB";
break;
case 0x35:
*name = "Wacom PL800 USB";
break;
case 0x3F:
*name = "Wacom Cintiq 21UX USB";
break;
case 0x41:
*name = "Wacom Intuos2 4x5\" USB";
break;
case 0x42:
*name = "Wacom Intuos2 6x8\" USB";
break;
case 0x43:
*name = "Wacom Intuos2 9x12\" USB";
break;
case 0x44:
*name = "Wacom Intuos2 12x12\" USB";
break;
case 0x45:
*name = "Wacom Intuos2 12x18\" USB";
break;
case 0x47: // some I2 6x8s seem to report as 0x47
*name = "Wacom Intuos2 6x8\" USB";
break;
case 0x60:
*name = "Wacom Volito USB";
break;
case 0x61:
*name = "Wacom PenStation USB";
break;
case 0x62:
*name = "Wacom Volito2 USB";
break;
case 0x64:
*name = "Wacom PenPartner.1 USB";
break;
case 0xB0:
*name = "Wacom Intuos3 4x5 USB";
break;
case 0xB1:
*name = "Wacom Intuos3 6x8 USB";
break;
case 0xB2:
*name = "Wacom Intuos3 9x12 USB";
break;
default:
*name = "<unkown wacom tablet>";
break;
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright 2003-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
* Distributed under the terms of the MIT license.
*/
// This class encapsulates all info and status about a tablet.
// It runs the thread polling from the USB and sends messages
// to/via the InputServer.
#ifndef TABLET_DEVICE_H
#define TABLET_DEVICE_H
#include <OS.h>
#include "PointingDevice.h"
class TabletDevice : public PointingDevice {
public:
TabletDevice(MasterServerDevice* parent,
DeviceReader* reader);
virtual ~TabletDevice();
virtual status_t InitCheck();
virtual status_t Start();
virtual status_t Stop();
inline int DataBytes() const
{ return fDataBytes; }
status_t DetectDevice(uint16 productID);
void SetDevice(float maxX, float maxY,
uint32 mode = DEVICE_INTUOS,
int dataBytes = 10);
void ReadData(uchar* data,
bool& hasContact,
uint32& mode,
uint32& buttons,
float& x, float& y,
float& pressure,
int32& clicks,
int32& eraser,
float& wheelX,
float& wheelY,
float& tiltX,
float& tiltY) const;
void SetStatus(uint32 mode,
uint32 buttons,
float x, float y,
float pressure,
int32 clicks,
uint32 modifiers,
int32 eraser,
float wheelX,
float wheelY,
float tiltX = 0.0,
float tiltY = 0.0,
const uchar* data = NULL);
void SetContact(bool contact);
private:
static int32 poll_usb_device(void* arg);
bool _DeviceSupportsTilt() const;
void _GetName(uint16 productID,
const char** name) const;
enum {
DEVICE_UNKOWN,
DEVICE_PENPARTNER,
DEVICE_GRAPHIRE,
DEVICE_INTUOS,
DEVICE_INTUOS3,
DEVICE_PL500,
DEVICE_VOLITO,
DEVICE_PENSTATION,
DEVICE_CINTIQ,
};
enum {
MODE_PEN,
MODE_MOUSE,
};
thread_id fThreadID;
// device specific settings
uint32 fDeviceMode;
float fMaxX;
float fMaxY;
int fDataBytes;
float fPosX;
float fPosY;
float fFakeMouseX;
float fFakeMouseY;
uint32 fButtons;
float fPressure;
uint32 fModifiers;
bool fEraser;
float fTiltX;
float fTiltY;
float fWheelX;
float fWheelY;
bigtime_t fLastClickTime;
int32 fClicks;
bool fHasContact;
float fJitterX;
float fJitterY;
};
#endif // WACOM_TABLET_H