/* * 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 static const char* kWatchFolder = "input/wacom/usb"; static const char* kDeviceFolder = "/dev/input/wacom/usb"; static const char* kDeviceName = "Wacom Tablet"; //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), fDblClickSpeed(DEFAULT_CLICK_SPEED), fPS2DisablerThread(B_ERROR), fDeviceLock("device list lock") { get_mouse_speed(kDeviceName, &fSpeed); get_mouse_acceleration(kDeviceName, &fAcceleration); get_click_speed(kDeviceName, &fDblClickSpeed); _CalculateAccelerationTable(); 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(); } StartMonitoringDevice(kWatchFolder); } // destructor MasterServerDevice::~MasterServerDevice() { // cleanup _StopAll(); if (_LockDevices()) { while (PointingDevice* device = (PointingDevice*)fDevices.RemoveItem((int32)0)) delete device; _UnlockDevices(); } } // InitCheck status_t MasterServerDevice::InitCheck() { return BInputServerDevice::InitCheck(); } // 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(); PRINT(("---------------------------------\n\n")); return (BInputServerDevice::SystemShuttingDown()); } // Start // // start generating events status_t MasterServerDevice::Start(const char* device, void* cookie) { // TODO: make this configurable // _StartPS2DisablerThread(); return B_OK; } // 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(device, &fSpeed); _CalculateAccelerationTable(); break; case B_CLICK_SPEED_CHANGED: get_click_speed(device, &fDblClickSpeed); break; case B_MOUSE_ACCELERATION_CHANGED: get_mouse_acceleration(device, &fAcceleration); _CalculateAccelerationTable(); break; case B_NODE_MONITOR: _HandleNodeMonitor(message); break; default: BInputServerDevice::Control(device, cookie, code, message); break; } return B_OK; } // #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) { PRINT(("examining devfs entry '%s'\n", ref.name)); // don't add the control device if (strcmp(ref.name, "control") != 0) { BPath path(&ref); if (path.InitCheck() >= B_OK) { // add the device _AddDevice(path.Path()); } } } } else PRINT(("folder '%s' not found\n", kDeviceFolder)); PRINT(("done examing devfs\n")); _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)) { PRINT(("pointing device added (%s)\n", path)); // start device polling only if we're started if (fActive) device->Start(); input_device_ref device = { (char*)kDeviceName, B_POINTING_DEVICE, (void*)this }; input_device_ref* deviceList[2] = { &device, NULL }; RegisterDevices(deviceList); } else { PRINT(("pointing device not added (%s)\n", path)); if (device) { PRINT((" vendor: %0*x, product: %0*x\n", 4, device->VendorID(), 4, device->ProductID())); } 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) { PRINT(("removing device '%s'\n", pointingDevice->DevicePath())); if (_LockDevices()) { if (fDevices.RemoveItem((void*)pointingDevice)) { delete pointingDevice; i--; } _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(); }