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 static const char* kDeviceName = "Wacom Tablet"; 31 32 //static const char* kPS2MouseThreadName = "PS/2 Mouse"; 33 34 // instantiate_input_device 35 // 36 // this is where it all starts make sure this function is exported! 37 BInputServerDevice* 38 instantiate_input_device() 39 { 40 return (new MasterServerDevice()); 41 } 42 43 // constructor 44 MasterServerDevice::MasterServerDevice() 45 : BInputServerDevice(), 46 fDevices(1), 47 fActive(false), 48 fDblClickSpeed(DEFAULT_CLICK_SPEED), 49 fPS2DisablerThread(B_ERROR), 50 fDeviceLock("device list lock") 51 { 52 get_mouse_speed(kDeviceName, &fSpeed); 53 get_mouse_acceleration(kDeviceName, &fAcceleration); 54 get_click_speed(kDeviceName, &fDblClickSpeed); 55 _CalculateAccelerationTable(); 56 57 if (_LockDevices()) { 58 // do an initial scan of the devfs folder, after that, we should receive 59 // node monitor messages for hotplugging support 60 _SearchDevices(); 61 62 fActive = true; 63 64 for (int32 i = 0; PointingDevice* device = (PointingDevice*)fDevices.ItemAt(i); i++) { 65 device->Start(); 66 } 67 _UnlockDevices(); 68 } 69 70 StartMonitoringDevice(kWatchFolder); 71 } 72 73 // destructor 74 MasterServerDevice::~MasterServerDevice() 75 { 76 // cleanup 77 _StopAll(); 78 if (_LockDevices()) { 79 while (PointingDevice* device = (PointingDevice*)fDevices.RemoveItem((int32)0)) 80 delete device; 81 _UnlockDevices(); 82 } 83 } 84 85 // InitCheck 86 status_t 87 MasterServerDevice::InitCheck() 88 { 89 return BInputServerDevice::InitCheck(); 90 } 91 92 // SystemShuttingDown 93 status_t 94 MasterServerDevice::SystemShuttingDown() 95 { 96 // the devices should be stopped anyways, 97 // but this is just defensive programming. 98 // the pointing devices will have spawned polling threads, 99 // we make sure that we block until every thread has bailed out. 100 101 _StopAll(); 102 103 PRINT(("---------------------------------\n\n")); 104 105 return (BInputServerDevice::SystemShuttingDown()); 106 } 107 108 // Start 109 // 110 // start generating events 111 status_t 112 MasterServerDevice::Start(const char* device, void* cookie) 113 { 114 // TODO: make this configurable 115 // _StartPS2DisablerThread(); 116 117 return B_OK; 118 } 119 120 // Stop 121 status_t 122 MasterServerDevice::Stop(const char* device, void* cookie) 123 { 124 // stop generating events 125 fActive = false; 126 127 _StopAll(); 128 129 // _StopPS2DisablerThread(); 130 131 return StopMonitoringDevice(kWatchFolder); 132 } 133 134 // Control 135 status_t 136 MasterServerDevice::Control(const char* device, void* cookie, uint32 code, BMessage* message) 137 { 138 // respond to changes in the system 139 switch (code) { 140 case B_MOUSE_SPEED_CHANGED: 141 get_mouse_speed(device, &fSpeed); 142 _CalculateAccelerationTable(); 143 break; 144 case B_CLICK_SPEED_CHANGED: 145 get_click_speed(device, &fDblClickSpeed); 146 break; 147 case B_MOUSE_ACCELERATION_CHANGED: 148 get_mouse_acceleration(device, &fAcceleration); 149 _CalculateAccelerationTable(); 150 break; 151 case B_NODE_MONITOR: 152 _HandleNodeMonitor(message); 153 break; 154 default: 155 BInputServerDevice::Control(device, cookie, code, message); 156 break; 157 } 158 return B_OK; 159 } 160 161 // #pragma mark - 162 163 // _SearchDevices 164 void 165 MasterServerDevice::_SearchDevices() 166 { 167 if (_LockDevices()) { 168 // construct a PointingDevice for every devfs entry we find 169 BDirectory dir(kDeviceFolder); 170 if (dir.InitCheck() >= B_OK) { 171 // traverse the contents of the folder to see if the 172 // entry of that device still exists 173 entry_ref ref; 174 while (dir.GetNextRef(&ref) >= B_OK) { 175 PRINT(("examining devfs entry '%s'\n", ref.name)); 176 // don't add the control device 177 if (strcmp(ref.name, "control") != 0) { 178 BPath path(&ref); 179 if (path.InitCheck() >= B_OK) { 180 // add the device 181 _AddDevice(path.Path()); 182 } 183 } 184 } 185 } else 186 PRINT(("folder '%s' not found\n", kDeviceFolder)); 187 188 PRINT(("done examing devfs\n")); 189 _UnlockDevices(); 190 } 191 } 192 193 // _StopAll 194 void 195 MasterServerDevice::_StopAll() 196 { 197 if (_LockDevices()) { 198 for (int32 i = 0; PointingDevice* device 199 = (PointingDevice*)fDevices.ItemAt(i); i++) 200 device->Stop(); 201 _UnlockDevices(); 202 } 203 } 204 205 // _AddDevice 206 void 207 MasterServerDevice::_AddDevice(const char* path) 208 { 209 if (_LockDevices()) { 210 // get the device from the factory 211 PointingDevice* device = PointingDeviceFactory::DeviceFor(this, path); 212 // add it to our list 213 if (device && device->InitCheck() >= B_OK 214 && fDevices.AddItem((void*)device)) { 215 PRINT(("pointing device added (%s)\n", path)); 216 // start device polling only if we're started 217 if (fActive) 218 device->Start(); 219 input_device_ref device = { (char*)kDeviceName, 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