1 /* 2 * Copyright 2005-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 */ 5 #include "MasterServerDevice.h" 6 7 #include <fstream> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include <Directory.h> 13 #include <Entry.h> 14 #include <InterfaceDefs.h> 15 #include <Message.h> 16 #include <NodeMonitor.h> 17 #include <OS.h> 18 #include <Path.h> 19 #include <Screen.h> 20 #include <View.h> 21 #include <File.h> 22 23 #include "PointingDevice.h" 24 #include "PointingDeviceFactory.h" 25 26 #define DEFAULT_CLICK_SPEED 250000 27 28 static const char* kWatchFolder = "input/wacom/usb"; 29 static const char* kDeviceFolder = "/dev/input/wacom/usb"; 30 31 //static const char* kPS2MouseThreadName = "PS/2 Mouse"; 32 33 // instantiate_input_device 34 // 35 // this is where it all starts make sure this function is exported! 36 BInputServerDevice* 37 instantiate_input_device() 38 { 39 return (new MasterServerDevice()); 40 } 41 42 // constructor 43 MasterServerDevice::MasterServerDevice() 44 : BInputServerDevice(), 45 fDevices(1), 46 fActive(false), 47 fDblClickSpeed(DEFAULT_CLICK_SPEED), 48 fPS2DisablerThread(B_ERROR), 49 fDeviceLock("device list lock") 50 { 51 get_mouse_speed(&fSpeed); 52 get_mouse_acceleration(&fAcceleration); 53 get_click_speed(&fDblClickSpeed); 54 _CalculateAccelerationTable(); 55 } 56 57 // destructor 58 MasterServerDevice::~MasterServerDevice() 59 { 60 // cleanup 61 _StopAll(); 62 if (_LockDevices()) { 63 while (PointingDevice* device = (PointingDevice*)fDevices.RemoveItem((int32)0)) 64 delete device; 65 _UnlockDevices(); 66 } 67 } 68 69 // InitCheck 70 status_t 71 MasterServerDevice::InitCheck() 72 { 73 input_device_ref device = { (char *)"Wacom Tablets", 74 B_POINTING_DEVICE, (void*)this }; 75 input_device_ref* deviceList[2] = { &device, NULL }; 76 RegisterDevices(deviceList); 77 78 return B_OK; 79 } 80 81 // SystemShuttingDown 82 status_t 83 MasterServerDevice::SystemShuttingDown() 84 { 85 // the devices should be stopped anyways, 86 // but this is just defensive programming. 87 // the pointing devices will have spawned polling threads, 88 // we make sure that we block until every thread has bailed out. 89 90 _StopAll(); 91 92 PRINT(("---------------------------------\n\n")); 93 94 return (BInputServerDevice::SystemShuttingDown()); 95 } 96 97 // Start 98 // 99 // start generating events 100 status_t 101 MasterServerDevice::Start(const char* device, void* cookie) 102 { 103 if (_LockDevices()) { 104 // do an initial scan of the devfs folder, after that, we should receive 105 // node monitor messages for hotplugging support 106 _SearchDevices(); 107 108 fActive = true; 109 110 for (int32 i = 0; PointingDevice* device = (PointingDevice*)fDevices.ItemAt(i); i++) { 111 device->Start(); 112 } 113 _UnlockDevices(); 114 } 115 116 // TODO: make this configurable 117 // _StartPS2DisablerThread(); 118 119 return StartMonitoringDevice(kWatchFolder); 120 } 121 122 // Stop 123 status_t 124 MasterServerDevice::Stop(const char* device, void* cookie) 125 { 126 // stop generating events 127 fActive = false; 128 129 _StopAll(); 130 131 // _StopPS2DisablerThread(); 132 133 return StopMonitoringDevice(kWatchFolder); 134 } 135 136 // Control 137 status_t 138 MasterServerDevice::Control(const char* device, void* cookie, uint32 code, BMessage* message) 139 { 140 // respond to changes in the system 141 switch (code) { 142 case B_MOUSE_SPEED_CHANGED: 143 get_mouse_speed(&fSpeed); 144 _CalculateAccelerationTable(); 145 break; 146 case B_CLICK_SPEED_CHANGED: 147 get_click_speed(&fDblClickSpeed); 148 break; 149 case B_MOUSE_ACCELERATION_CHANGED: 150 get_mouse_acceleration(&fAcceleration); 151 _CalculateAccelerationTable(); 152 break; 153 case B_NODE_MONITOR: 154 _HandleNodeMonitor(message); 155 break; 156 default: 157 BInputServerDevice::Control(device, cookie, code, message); 158 break; 159 } 160 return B_OK; 161 } 162 163 // #pragma mark - 164 165 // _SearchDevices 166 void 167 MasterServerDevice::_SearchDevices() 168 { 169 if (_LockDevices()) { 170 // construct a PointingDevice for every devfs entry we find 171 BDirectory dir(kDeviceFolder); 172 if (dir.InitCheck() >= B_OK) { 173 // traverse the contents of the folder to see if the 174 // entry of that device still exists 175 entry_ref ref; 176 while (dir.GetNextRef(&ref) >= B_OK) { 177 PRINT(("examining devfs entry '%s'\n", ref.name)); 178 // don't add the control device 179 if (strcmp(ref.name, "control") != 0) { 180 BPath path(&ref); 181 if (path.InitCheck() >= B_OK) { 182 // add the device 183 _AddDevice(path.Path()); 184 } 185 } 186 } 187 } else 188 PRINT(("folder '%s' not found\n", kDeviceFolder)); 189 190 PRINT(("done examing devfs\n")); 191 _UnlockDevices(); 192 } 193 } 194 195 // _StopAll 196 void 197 MasterServerDevice::_StopAll() 198 { 199 if (_LockDevices()) { 200 for (int32 i = 0; PointingDevice* device 201 = (PointingDevice*)fDevices.ItemAt(i); i++) 202 device->Stop(); 203 _UnlockDevices(); 204 } 205 } 206 207 // _AddDevice 208 void 209 MasterServerDevice::_AddDevice(const char* path) 210 { 211 if (_LockDevices()) { 212 // get the device from the factory 213 PointingDevice* device = PointingDeviceFactory::DeviceFor(this, path); 214 // add it to our list 215 if (device && device->InitCheck() >= B_OK 216 && fDevices.AddItem((void*)device)) { 217 PRINT(("pointing device added (%s)\n", path)); 218 // start device polling only if we're started 219 if (fActive) 220 device->Start(); 221 } else { 222 223 PRINT(("pointing device not added (%s)\n", path)); 224 if (device) { 225 PRINT((" vendor: %0*x, product: %0*x\n", 4, device->VendorID(), 226 4, device->ProductID())); 227 } 228 229 delete device; 230 } 231 _UnlockDevices(); 232 } 233 } 234 235 // _HandleNodeMonitor 236 void 237 MasterServerDevice::_HandleNodeMonitor(BMessage* message) 238 { 239 int32 opcode; 240 if (message->FindInt32("opcode", &opcode) < B_OK) 241 return; 242 243 switch (opcode) { 244 case B_ENTRY_CREATED: 245 // extract info to create an entry_ref structure 246 const char* name; 247 ino_t directory; 248 dev_t device; 249 if (message->FindString("name", &name) >= B_OK 250 && strcmp(name, "control") != 0 251 && message->FindInt64("directory", (int64*)&directory) >= B_OK 252 && message->FindInt32("device", (int32*)&device) >= B_OK) { 253 // make a path from that info 254 entry_ref ref(device, directory, name); 255 BPath path(&ref); 256 if (path.InitCheck() >= B_OK) { 257 // add the device 258 _AddDevice(path.Path()); 259 } 260 } 261 break; 262 case B_ENTRY_REMOVED: { 263 // I cannot figure out how to see if this is actually 264 // the directory that we're node monitoring, and even if it is, 265 // we would have to look at the directories contents to 266 // see which PointingDevice we might want to remove 267 BDirectory dir(kDeviceFolder); 268 if (dir.InitCheck() >= B_OK) { 269 // for each device in our list, see if the corresponding 270 // entry in the device folder still exists 271 for (int32 i = 0; PointingDevice* pointingDevice 272 = (PointingDevice*)fDevices.ItemAt(i); i++) { 273 // traverse the contents of the folder 274 // if the entry for this device is there, 275 // we can abort the search 276 entry_ref ref; 277 dir.Rewind(); 278 bool found = false; 279 while (dir.GetNextRef(&ref) >= B_OK) { 280 BPath path(&ref); 281 if (strcmp(pointingDevice->DevicePath(), 282 path.Path()) == 0) { 283 found = true; 284 break; 285 } 286 } 287 // remove the device if the devfs entry was not found 288 if (!found) { 289 290 PRINT(("removing device '%s'\n", pointingDevice->DevicePath())); 291 292 if (_LockDevices()) { 293 if (fDevices.RemoveItem((void*)pointingDevice)) { 294 295 delete pointingDevice; 296 i--; 297 } 298 _UnlockDevices(); 299 } 300 } 301 } 302 } 303 break; 304 } 305 } 306 } 307 308 // _CalculateAccelerationTable 309 // 310 // calculates the acceleration table. This is recalculated anytime we find out that 311 // the current acceleration or speed has changed. 312 void 313 MasterServerDevice::_CalculateAccelerationTable() 314 { 315 // This seems to work alright. 316 for (int x = 1; x <= 128; x++){ 317 fAccelerationTable[x] = (x / (128 * (1 - (((float)fSpeed + 8192) / 262144)))) 318 * (x / (128 * (1 - (((float)fSpeed + 8192) / 262144)))) 319 + ((((float)fAcceleration + 4000) / 262144) 320 * (x / (128 * (1 - (((float)fSpeed + 8192) / 262144))))); 321 } 322 323 // sets the bottom of the table to be the inverse of the first half. 324 // position 0 -> 128 positive movement, 255->129 negative movement 325 for (int x = 255; x > 128; x--){ 326 fAccelerationTable[x] = -(fAccelerationTable[(255 - x)]); 327 } 328 329 // Location 0 will be 0.000, which is unacceptable, otherwise, single step moves are lost 330 // To work around this, we'll make it 1/2 of the smallest increment. 331 fAccelerationTable[0] = fAccelerationTable[1] / 2; 332 fAccelerationTable[255] = -fAccelerationTable[0]; 333 } 334 335 // _ps2_disabler 336 // 337 // find the PS/2 device thread and suspend its execution 338 // TODO: make this configurable 339 // In case you're wondering... this behaviour is useful for notebook 340 // users who are annoyed by accidentally hitting their touchpad while 341 // typing. "Turning it off entirely" by suspending the PS/2 thread is 342 // just a compfort thing, but should of course be made configurable... 343 //int32 344 //MasterServerDevice::_ps2_disabler(void* cookie) 345 //{ 346 // MasterServerDevice* master = (MasterServerDevice*)cookie; 347 // 348 // thread_id haltedThread = B_ERROR; 349 // 350 // while (master->fActive) { 351 // // search for at least one running device 352 // bool suspendPS2 = false; 353 // if (master->_LockDevices()) { 354 // for (int32 i = 0; PointingDevice* device = (PointingDevice*)master->fDevices.ItemAt(i); i++) { 355 // if (device->DisablePS2()) { 356 // suspendPS2 = true; 357 // break; 358 // } 359 // } 360 // master->_UnlockDevices(); 361 // } 362 // 363 // if (suspendPS2){ 364 // // find and suspend PS/2 thread (if not already done) 365 // if (haltedThread < B_OK) { 366 // haltedThread = find_thread(kPS2MouseThreadName); 367 // if (haltedThread >= B_OK) { 368 // suspend_thread(haltedThread); 369 // } 370 // } 371 // } else { 372 // // reenable PS/2 thread 373 // if (haltedThread >= B_OK) { 374 // resume_thread(haltedThread); 375 // haltedThread = B_ERROR; 376 // } 377 // } 378 // 379 // // sleep a little while 380 // snooze(2000000); 381 // } 382 // 383 // // reenable PS/2 thread in any case before we die 384 // if (haltedThread >= B_OK) { 385 // resume_thread(haltedThread); 386 // } 387 // 388 // return B_OK; 389 //} 390 // 391 //// _StartPS2DisablerThread 392 //void 393 //MasterServerDevice::_StartPS2DisablerThread() 394 //{ 395 // if (fPS2DisablerThread < B_OK) { 396 // fPS2DisablerThread = spawn_thread(_ps2_disabler, "PS/2 Mouse disabler", B_LOW_PRIORITY, this); 397 // if (fPS2DisablerThread >= B_OK) 398 // resume_thread(fPS2DisablerThread); 399 // } 400 //} 401 // 402 //// _StopPS2DisablerThread 403 //void 404 //MasterServerDevice::_StopPS2DisablerThread() 405 //{ 406 // if (fPS2DisablerThread >= B_OK) { 407 // status_t err; 408 // wait_for_thread(fPS2DisablerThread, &err); 409 // } 410 // fPS2DisablerThread = B_ERROR; 411 //} 412 413 // _LockDevices 414 bool 415 MasterServerDevice::_LockDevices() 416 { 417 return fDeviceLock.Lock(); 418 } 419 420 // _UnlockDevices 421 void 422 MasterServerDevice::_UnlockDevices() 423 { 424 fDeviceLock.Unlock(); 425 } 426 427