diff --git a/src/add-ons/input_server/devices/Jamfile b/src/add-ons/input_server/devices/Jamfile index e16bf9d77f..978651f588 100644 --- a/src/add-ons/input_server/devices/Jamfile +++ b/src/add-ons/input_server/devices/Jamfile @@ -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 ; diff --git a/src/add-ons/input_server/devices/wacom/DESIGN b/src/add-ons/input_server/devices/wacom/DESIGN new file mode 100644 index 0000000000..4d86e5d515 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/DESIGN @@ -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. + diff --git a/src/add-ons/input_server/devices/wacom/DeviceReader.cpp b/src/add-ons/input_server/devices/wacom/DeviceReader.cpp new file mode 100644 index 0000000000..1b8b93c41a --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/DeviceReader.cpp @@ -0,0 +1,142 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT license. + */ +#include "DeviceReader.h" + +#include +#include + +#include + + +// 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; +} diff --git a/src/add-ons/input_server/devices/wacom/DeviceReader.h b/src/add-ons/input_server/devices/wacom/DeviceReader.h new file mode 100644 index 0000000000..0f382a6167 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/DeviceReader.h @@ -0,0 +1,61 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . 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 + +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 diff --git a/src/add-ons/input_server/devices/wacom/Jamfile b/src/add-ons/input_server/devices/wacom/Jamfile new file mode 100644 index 0000000000..e704b4aa20 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src add-ons input_server devices wacom ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders input ; + +Addon wacom : + DeviceReader.cpp + MasterServerDevice.cpp + PointingDevice.cpp + PointingDeviceFactory.cpp + TabletDevice.cpp + : be input_server ; + +Package haiku-inputkit-cvs : + tablet : + boot home config add-ons input_server devices ; diff --git a/src/add-ons/input_server/devices/wacom/MasterServerDevice.cpp b/src/add-ons/input_server/devices/wacom/MasterServerDevice.cpp new file mode 100644 index 0000000000..7c8c172ba9 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/MasterServerDevice.cpp @@ -0,0 +1,486 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT license. + */ +#include "MasterServerDevice.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(); +} + diff --git a/src/add-ons/input_server/devices/wacom/MasterServerDevice.h b/src/add-ons/input_server/devices/wacom/MasterServerDevice.h new file mode 100644 index 0000000000..4991c202eb --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/MasterServerDevice.h @@ -0,0 +1,80 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT license. + */ +#ifndef MASTER_SERVER_DEVICE_H +#define MASTER_SERVER_DEVICE_H + +#include +#include +#include +#include + +// 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 diff --git a/src/add-ons/input_server/devices/wacom/PointingDevice.cpp b/src/add-ons/input_server/devices/wacom/PointingDevice.cpp new file mode 100644 index 0000000000..dd5a5be0f6 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/PointingDevice.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT license. + */ +#include +#include + +#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; +} + + + + + + diff --git a/src/add-ons/input_server/devices/wacom/PointingDevice.h b/src/add-ons/input_server/devices/wacom/PointingDevice.h new file mode 100644 index 0000000000..1159c1afe3 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/PointingDevice.h @@ -0,0 +1,45 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT license. + */ +#ifndef POINTING_DEVICE_H +#define POINTING_DEVICE_H + +#include + +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 diff --git a/src/add-ons/input_server/devices/wacom/PointingDeviceFactory.cpp b/src/add-ons/input_server/devices/wacom/PointingDeviceFactory.cpp new file mode 100644 index 0000000000..5c9d66ec15 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/PointingDeviceFactory.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . 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() {} + + diff --git a/src/add-ons/input_server/devices/wacom/PointingDeviceFactory.h b/src/add-ons/input_server/devices/wacom/PointingDeviceFactory.h new file mode 100644 index 0000000000..6ac02b23d8 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/PointingDeviceFactory.h @@ -0,0 +1,23 @@ +/* + * Copyright 2005-2008 Stephan Aßmus . All rights reserved. + * Distributed under the terms of the MIT license. + */ +#ifndef POINTING_DEVICE_FACTORY_H +#define POINTING_DEVICE_FACTORY_H + +#include + +class MasterServerDevice; +class PointingDevice; + +class PointingDeviceFactory { + public: + static PointingDevice* DeviceFor(MasterServerDevice* parent, + const char* path); + + private: + PointingDeviceFactory(); + ~PointingDeviceFactory(); +}; + +#endif // POINTING_DEVICE_FACTORY_H diff --git a/src/add-ons/input_server/devices/wacom/TabletDevice.cpp b/src/add-ons/input_server/devices/wacom/TabletDevice.cpp new file mode 100644 index 0000000000..29824db5d4 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/TabletDevice.cpp @@ -0,0 +1,742 @@ +/* + * Copyright 2003-2008 Stephan Aßmus . 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 + * Stefan Werner + * Hiroyuki Tsutsumi + */ + +#include +#include +#include + +#include +#include +#include +#include + +#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 = ""; + break; + } +} + diff --git a/src/add-ons/input_server/devices/wacom/TabletDevice.h b/src/add-ons/input_server/devices/wacom/TabletDevice.h new file mode 100644 index 0000000000..8cca414a06 --- /dev/null +++ b/src/add-ons/input_server/devices/wacom/TabletDevice.h @@ -0,0 +1,117 @@ +/* + * Copyright 2003-2008 Stephan Aßmus . 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 + +#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