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