1 /* 2 * Copyright 2004-2011, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini (stefano.ceccherini@gmail.com) 7 * Jérôme Duval 8 * Axel Dörfler, axeld@pinc-software.de 9 * Clemens Zeidler, haiku@clemens-zeidler.de 10 * Stephan Aßmus, superstippi@gmx.de 11 */ 12 13 14 #include "MouseInputDevice.h" 15 16 #include <errno.h> 17 #include <new> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <unistd.h> 21 22 #include <Autolock.h> 23 #include <Debug.h> 24 #include <Directory.h> 25 #include <Entry.h> 26 #include <File.h> 27 #include <FindDirectory.h> 28 #include <NodeMonitor.h> 29 #include <Path.h> 30 #include <String.h> 31 #include <View.h> 32 33 #include <kb_mouse_settings.h> 34 #include <keyboard_mouse_driver.h> 35 #include <touchpad_settings.h> 36 37 38 #undef TRACE 39 //#define TRACE_MOUSE_DEVICE 40 #ifdef TRACE_MOUSE_DEVICE 41 42 class FunctionTracer { 43 public: 44 FunctionTracer(const void* pointer, const char* className, 45 const char* functionName, 46 int32& depth) 47 : fFunctionName(), 48 fPrepend(), 49 fFunctionDepth(depth), 50 fPointer(pointer) 51 { 52 fFunctionDepth++; 53 fPrepend.Append(' ', fFunctionDepth * 2); 54 fFunctionName << className << "::" << functionName << "()"; 55 56 debug_printf("%p -> %s%s {\n", fPointer, fPrepend.String(), 57 fFunctionName.String()); 58 } 59 60 ~FunctionTracer() 61 { 62 debug_printf("%p -> %s}\n", fPointer, fPrepend.String()); 63 fFunctionDepth--; 64 } 65 66 private: 67 BString fFunctionName; 68 BString fPrepend; 69 int32& fFunctionDepth; 70 const void* fPointer; 71 }; 72 73 74 static int32 sFunctionDepth = -1; 75 # define MD_CALLED(x...) FunctionTracer _ft(this, "MouseDevice", \ 76 __FUNCTION__, sFunctionDepth) 77 # define MID_CALLED(x...) FunctionTracer _ft(this, "MouseInputDevice", \ 78 __FUNCTION__, sFunctionDepth) 79 # define TRACE(x...) do { BString _to; \ 80 _to.Append(' ', (sFunctionDepth + 1) * 2); \ 81 debug_printf("%p -> %s", this, _to.String()); \ 82 debug_printf(x); } while (0) 83 # define LOG_EVENT(text...) do {} while (0) 84 # define LOG_ERR(text...) TRACE(text) 85 #else 86 # define TRACE(x...) do {} while (0) 87 # define MD_CALLED(x...) TRACE(x) 88 # define MID_CALLED(x...) TRACE(x) 89 # define LOG_ERR(x...) debug_printf(x) 90 # define LOG_EVENT(x...) TRACE(x) 91 #endif 92 93 94 const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4; 95 const static char* kMouseDevicesDirectory = "/dev/input/mouse"; 96 const static char* kTouchpadDevicesDirectory = "/dev/input/touchpad"; 97 98 99 class MouseDevice { 100 public: 101 MouseDevice(MouseInputDevice& target, 102 const char* path); 103 ~MouseDevice(); 104 105 status_t Start(); 106 void Stop(); 107 108 status_t UpdateSettings(); 109 status_t UpdateTouchpadSettings(const BMessage* message); 110 111 const char* Path() const { return fPath.String(); } 112 input_device_ref* DeviceRef() { return &fDeviceRef; } 113 114 private: 115 char* _BuildShortName() const; 116 117 static status_t _ControlThreadEntry(void* arg); 118 void _ControlThread(); 119 void _ControlThreadCleanup(); 120 void _UpdateSettings(); 121 122 status_t _GetTouchpadSettingsPath(BPath& path); 123 status_t _ReadTouchpadSettingsMsg(BMessage* message); 124 status_t _UpdateTouchpadSettings(); 125 126 BMessage* _BuildMouseMessage(uint32 what, 127 uint64 when, uint32 buttons, 128 int32 deltaX, int32 deltaY) const; 129 void _ComputeAcceleration( 130 const mouse_movement& movements, 131 int32& deltaX, int32& deltaY, 132 float& historyDeltaX, 133 float& historyDeltaY) const; 134 uint32 _RemapButtons(uint32 buttons) const; 135 136 private: 137 MouseInputDevice& fTarget; 138 BString fPath; 139 int fDevice; 140 141 input_device_ref fDeviceRef; 142 mouse_settings fSettings; 143 bool fDeviceRemapsButtons; 144 145 thread_id fThread; 146 volatile bool fActive; 147 volatile bool fUpdateSettings; 148 149 bool fIsTouchpad; 150 touchpad_settings fTouchpadSettings; 151 BMessage* fTouchpadSettingsMessage; 152 BLocker fTouchpadSettingsLock; 153 }; 154 155 156 extern "C" BInputServerDevice* 157 instantiate_input_device() 158 { 159 return new(std::nothrow) MouseInputDevice(); 160 } 161 162 163 // #pragma mark - 164 165 166 MouseDevice::MouseDevice(MouseInputDevice& target, const char* driverPath) 167 : 168 fTarget(target), 169 fPath(driverPath), 170 fDevice(-1), 171 fDeviceRemapsButtons(false), 172 fThread(-1), 173 fActive(false), 174 fUpdateSettings(false), 175 fIsTouchpad(false), 176 fTouchpadSettingsMessage(NULL), 177 fTouchpadSettingsLock("Touchpad settings lock") 178 { 179 MD_CALLED(); 180 181 fDeviceRef.name = _BuildShortName(); 182 fDeviceRef.type = B_POINTING_DEVICE; 183 fDeviceRef.cookie = this; 184 185 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 186 fSettings.map.button[0] = B_PRIMARY_MOUSE_BUTTON; 187 fSettings.map.button[1] = B_SECONDARY_MOUSE_BUTTON; 188 fSettings.map.button[2] = B_TERTIARY_MOUSE_BUTTON; 189 #endif 190 }; 191 192 193 MouseDevice::~MouseDevice() 194 { 195 MD_CALLED(); 196 TRACE("delete\n"); 197 198 if (fActive) 199 Stop(); 200 201 free(fDeviceRef.name); 202 delete fTouchpadSettingsMessage; 203 } 204 205 206 status_t 207 MouseDevice::Start() 208 { 209 MD_CALLED(); 210 211 fDevice = open(fPath.String(), O_RDWR); 212 // let the control thread handle any error on opening the device 213 214 char threadName[B_OS_NAME_LENGTH]; 215 snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", fDeviceRef.name); 216 217 fThread = spawn_thread(_ControlThreadEntry, threadName, 218 kMouseThreadPriority, (void*)this); 219 220 status_t status; 221 if (fThread < 0) 222 status = fThread; 223 else { 224 fActive = true; 225 status = resume_thread(fThread); 226 } 227 228 if (status < B_OK) { 229 LOG_ERR("%s: can't spawn/resume watching thread: %s\n", 230 fDeviceRef.name, strerror(status)); 231 if (fDevice >= 0) 232 close(fDevice); 233 234 return status; 235 } 236 237 return fDevice >= 0 ? B_OK : B_ERROR; 238 } 239 240 241 void 242 MouseDevice::Stop() 243 { 244 MD_CALLED(); 245 246 fActive = false; 247 // this will stop the thread as soon as it reads the next packet 248 249 close(fDevice); 250 fDevice = -1; 251 252 if (fThread >= 0) { 253 // unblock the thread, which might wait on a semaphore. 254 suspend_thread(fThread); 255 resume_thread(fThread); 256 257 status_t dummy; 258 wait_for_thread(fThread, &dummy); 259 } 260 } 261 262 263 status_t 264 MouseDevice::UpdateSettings() 265 { 266 MD_CALLED(); 267 268 if (fThread < 0) 269 return B_ERROR; 270 271 // trigger updating the settings in the control thread 272 fUpdateSettings = true; 273 274 return B_OK; 275 } 276 277 278 status_t 279 MouseDevice::UpdateTouchpadSettings(const BMessage* message) 280 { 281 if (!fIsTouchpad) 282 return B_BAD_TYPE; 283 if (fThread < 0) 284 return B_ERROR; 285 286 BAutolock _(fTouchpadSettingsLock); 287 288 // trigger updating the settings in the control thread 289 fUpdateSettings = true; 290 291 delete fTouchpadSettingsMessage; 292 fTouchpadSettingsMessage = new BMessage(*message); 293 if (fTouchpadSettingsMessage == NULL) 294 return B_NO_MEMORY; 295 296 return B_OK; 297 } 298 299 300 char* 301 MouseDevice::_BuildShortName() const 302 { 303 // TODO It would be simpler and better to use B_GET_DEVICE_NAME, but... 304 // - This is currently called before the device is open 305 // - We need to implement that in our input drivers first 306 BString string(fPath); 307 BString deviceName; 308 BString name; 309 310 int32 slash = string.FindLast("/"); 311 string.CopyInto(deviceName, slash + 1, string.Length() - slash); 312 // FIXME the device name may be more than just a number (for example 313 // ibm_trackpoint_0) 314 int32 index = atoi(deviceName.String()) + 1; 315 316 int32 previousSlash = string.FindLast("/", slash); 317 string.CopyInto(name, previousSlash + 1, slash - previousSlash - 1); 318 319 if (name == "ps2") 320 name = "PS/2"; 321 322 if (name.Length() <= 4) 323 name.ToUpper(); 324 else 325 name.Capitalize(); 326 327 if (deviceName.FindFirst("touchpad") >= 0) { 328 name << " Touchpad "; 329 } else if (deviceName.FindFirst("trackpoint") >= 0) { 330 // That's always PS/2, so don't keep the bus name 331 name = "Trackpoint "; 332 } else { 333 if (deviceName.FindFirst("intelli") >= 0) 334 name.Prepend("Extended "); 335 336 name << " Mouse "; 337 } 338 name << index; 339 340 return strdup(name.String()); 341 } 342 343 344 // #pragma mark - control thread 345 346 347 status_t 348 MouseDevice::_ControlThreadEntry(void* arg) 349 { 350 MouseDevice* device = (MouseDevice*)arg; 351 device->_ControlThread(); 352 return B_OK; 353 } 354 355 356 void 357 MouseDevice::_ControlThread() 358 { 359 MD_CALLED(); 360 361 if (fDevice < 0) { 362 _ControlThreadCleanup(); 363 // TOAST! 364 return; 365 } 366 367 // touchpad settings 368 if (ioctl(fDevice, MS_IS_TOUCHPAD, NULL) == B_OK) { 369 TRACE("is touchpad %s\n", fPath.String()); 370 fIsTouchpad = true; 371 372 fTouchpadSettings = kDefaultTouchpadSettings; 373 374 BPath path; 375 status_t status = _GetTouchpadSettingsPath(path); 376 BFile settingsFile(path.Path(), B_READ_ONLY); 377 if (status == B_OK && settingsFile.InitCheck() == B_OK) { 378 if (settingsFile.Read(&fTouchpadSettings, sizeof(touchpad_settings)) 379 != sizeof(touchpad_settings)) { 380 TRACE("failed to load settings\n"); 381 } 382 } 383 _UpdateTouchpadSettings(); 384 } 385 386 _UpdateSettings(); 387 388 uint32 lastButtons = 0; 389 float historyDeltaX = 0.0; 390 float historyDeltaY = 0.0; 391 392 static const bigtime_t kTransferDelay = 1000000 / 125; 393 // 125 transfers per second should be more than enough 394 #define USE_REGULAR_INTERVAL 1 395 #if USE_REGULAR_INTERVAL 396 bigtime_t nextTransferTime = system_time() + kTransferDelay; 397 #endif 398 399 while (fActive) { 400 mouse_movement movements; 401 402 #if USE_REGULAR_INTERVAL 403 snooze_until(nextTransferTime, B_SYSTEM_TIMEBASE); 404 nextTransferTime += kTransferDelay; 405 #endif 406 407 if (ioctl(fDevice, MS_READ, &movements, sizeof(movements)) != B_OK) { 408 LOG_ERR("Mouse device exiting, %s\n", strerror(errno)); 409 _ControlThreadCleanup(); 410 // TOAST! 411 return; 412 } 413 414 // take care of updating the settings first, if necessary 415 if (fUpdateSettings) { 416 fUpdateSettings = false; 417 if (fIsTouchpad) { 418 BAutolock _(fTouchpadSettingsLock); 419 if (fTouchpadSettingsMessage != NULL) { 420 _ReadTouchpadSettingsMsg(fTouchpadSettingsMessage); 421 _UpdateTouchpadSettings(); 422 delete fTouchpadSettingsMessage; 423 fTouchpadSettingsMessage = NULL; 424 } else 425 _UpdateSettings(); 426 } else 427 _UpdateSettings(); 428 } 429 430 uint32 buttons = lastButtons ^ movements.buttons; 431 432 uint32 remappedButtons = _RemapButtons(movements.buttons); 433 int32 deltaX, deltaY; 434 _ComputeAcceleration(movements, deltaX, deltaY, historyDeltaX, 435 historyDeltaY); 436 437 LOG_EVENT("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, " 438 "wheel_x:%ld, wheel_y:%ld\n", 439 fDeviceRef.name, movements.buttons, 440 movements.xdelta, movements.ydelta, movements.clicks, 441 movements.wheel_xdelta, movements.wheel_ydelta); 442 LOG_EVENT("%s: x: %ld, y: %ld (%.4f, %.4f)\n", fDeviceRef.name, 443 deltaX, deltaY, historyDeltaX, historyDeltaY); 444 445 // Send single messages for each event 446 447 if (buttons != 0) { 448 bool pressedButton = (buttons & movements.buttons) > 0; 449 BMessage* message = _BuildMouseMessage( 450 pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP, 451 movements.timestamp, remappedButtons, deltaX, deltaY); 452 if (message != NULL) { 453 if (pressedButton) { 454 message->AddInt32("clicks", movements.clicks); 455 LOG_EVENT("B_MOUSE_DOWN\n"); 456 } else 457 LOG_EVENT("B_MOUSE_UP\n"); 458 459 fTarget.EnqueueMessage(message); 460 lastButtons = movements.buttons; 461 } 462 } 463 464 if (movements.xdelta != 0 || movements.ydelta != 0) { 465 BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED, 466 movements.timestamp, remappedButtons, deltaX, deltaY); 467 if (message != NULL) 468 fTarget.EnqueueMessage(message); 469 } 470 471 if (movements.wheel_ydelta != 0 || movements.wheel_xdelta != 0) { 472 BMessage* message = new BMessage(B_MOUSE_WHEEL_CHANGED); 473 if (message == NULL) 474 continue; 475 476 if (message->AddInt64("when", movements.timestamp) == B_OK 477 && message->AddFloat("be:wheel_delta_x", 478 movements.wheel_xdelta) == B_OK 479 && message->AddFloat("be:wheel_delta_y", 480 movements.wheel_ydelta) == B_OK) 481 fTarget.EnqueueMessage(message); 482 else 483 delete message; 484 } 485 486 #if !USE_REGULAR_INTERVAL 487 snooze(kTransferDelay); 488 #endif 489 } 490 } 491 492 493 void 494 MouseDevice::_ControlThreadCleanup() 495 { 496 // NOTE: Only executed when the control thread detected an error 497 // and from within the control thread! 498 499 if (fActive) { 500 fThread = -1; 501 fTarget._RemoveDevice(fPath.String()); 502 } else { 503 // In case active is already false, another thread 504 // waits for this thread to quit, and may already hold 505 // locks that _RemoveDevice() wants to acquire. In other 506 // words, the device is already being removed, so we simply 507 // quit here. 508 } 509 } 510 511 512 void 513 MouseDevice::_UpdateSettings() 514 { 515 MD_CALLED(); 516 517 // retrieve current values 518 519 if (get_mouse_map(&fSettings.map) != B_OK) 520 LOG_ERR("error when get_mouse_map\n"); 521 else { 522 fDeviceRemapsButtons 523 = ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK; 524 } 525 526 if (get_click_speed(&fSettings.click_speed) != B_OK) 527 LOG_ERR("error when get_click_speed\n"); 528 else 529 ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed); 530 531 if (get_mouse_speed(&fSettings.accel.speed) != B_OK) 532 LOG_ERR("error when get_mouse_speed\n"); 533 else { 534 if (get_mouse_acceleration(&fSettings.accel.accel_factor) != B_OK) 535 LOG_ERR("error when get_mouse_acceleration\n"); 536 else { 537 mouse_accel accel; 538 ioctl(fDevice, MS_GET_ACCEL, &accel); 539 accel.speed = fSettings.accel.speed; 540 accel.accel_factor = fSettings.accel.accel_factor; 541 ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel); 542 } 543 } 544 545 if (get_mouse_type(&fSettings.type) != B_OK) 546 LOG_ERR("error when get_mouse_type\n"); 547 else 548 ioctl(fDevice, MS_SET_TYPE, &fSettings.type); 549 } 550 551 552 status_t 553 MouseDevice::_GetTouchpadSettingsPath(BPath& path) 554 { 555 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 556 if (status < B_OK) 557 return status; 558 return path.Append(TOUCHPAD_SETTINGS_FILE); 559 } 560 561 562 status_t 563 MouseDevice::_ReadTouchpadSettingsMsg(BMessage* message) 564 { 565 message->FindBool("scroll_twofinger", &fTouchpadSettings.scroll_twofinger); 566 message->FindBool("scroll_twofinger_horizontal", 567 &fTouchpadSettings.scroll_twofinger_horizontal); 568 message->FindFloat("scroll_rightrange", 569 &fTouchpadSettings.scroll_rightrange); 570 message->FindFloat("scroll_bottomrange", 571 &fTouchpadSettings.scroll_bottomrange); 572 573 message->FindInt16("scroll_xstepsize", 574 (int16*)&fTouchpadSettings.scroll_xstepsize); 575 message->FindInt16("scroll_ystepsize", 576 (int16*)&fTouchpadSettings.scroll_ystepsize); 577 message->FindInt8("scroll_acceleration", 578 (int8*)&fTouchpadSettings.scroll_acceleration); 579 message->FindInt8("tapgesture_sensibility", 580 (int8*)&fTouchpadSettings.tapgesture_sensibility); 581 582 return B_OK; 583 } 584 585 586 status_t 587 MouseDevice::_UpdateTouchpadSettings() 588 { 589 if (fIsTouchpad) { 590 ioctl(fDevice, MS_SET_TOUCHPAD_SETTINGS, &fTouchpadSettings); 591 return B_OK; 592 } 593 return B_ERROR; 594 } 595 596 597 BMessage* 598 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons, 599 int32 deltaX, int32 deltaY) const 600 { 601 BMessage* message = new BMessage(what); 602 if (message == NULL) 603 return NULL; 604 605 if (message->AddInt64("when", when) < B_OK 606 || message->AddInt32("buttons", buttons) < B_OK 607 || message->AddInt32("x", deltaX) < B_OK 608 || message->AddInt32("y", deltaY) < B_OK) { 609 delete message; 610 return NULL; 611 } 612 613 return message; 614 } 615 616 617 void 618 MouseDevice::_ComputeAcceleration(const mouse_movement& movements, 619 int32& _deltaX, int32& _deltaY, float& historyDeltaX, 620 float& historyDeltaY) const 621 { 622 // basic mouse speed 623 float deltaX = (float)movements.xdelta * fSettings.accel.speed / 65536.0 624 + historyDeltaX; 625 float deltaY = (float)movements.ydelta * fSettings.accel.speed / 65536.0 626 + historyDeltaY; 627 628 // acceleration 629 double acceleration = 1; 630 if (fSettings.accel.accel_factor) { 631 acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY) 632 * fSettings.accel.accel_factor / 524288.0; 633 } 634 635 deltaX *= acceleration; 636 deltaY *= acceleration; 637 638 if (deltaX >= 0) 639 _deltaX = (int32)floorf(deltaX); 640 else 641 _deltaX = (int32)ceilf(deltaX); 642 643 if (deltaY >= 0) 644 _deltaY = (int32)floorf(deltaY); 645 else 646 _deltaY = (int32)ceilf(deltaY); 647 648 historyDeltaX = deltaX - _deltaX; 649 historyDeltaY = deltaY - _deltaY; 650 } 651 652 653 uint32 654 MouseDevice::_RemapButtons(uint32 buttons) const 655 { 656 if (fDeviceRemapsButtons) 657 return buttons; 658 659 uint32 newButtons = 0; 660 for (int32 i = 0; buttons; i++) { 661 if (buttons & 0x1) { 662 #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO) 663 newButtons |= fSettings.map.button[i]; 664 #else 665 if (i == 0) 666 newButtons |= fSettings.map.left; 667 if (i == 1) 668 newButtons |= fSettings.map.right; 669 if (i == 2) 670 newButtons |= fSettings.map.middle; 671 #endif 672 } 673 buttons >>= 1; 674 } 675 676 return newButtons; 677 } 678 679 680 // #pragma mark - 681 682 683 MouseInputDevice::MouseInputDevice() 684 : 685 fDevices(2, true), 686 fDeviceListLock("MouseInputDevice list") 687 { 688 MID_CALLED(); 689 690 StartMonitoringDevice(kMouseDevicesDirectory); 691 StartMonitoringDevice(kTouchpadDevicesDirectory); 692 _RecursiveScan(kMouseDevicesDirectory); 693 _RecursiveScan(kTouchpadDevicesDirectory); 694 } 695 696 697 MouseInputDevice::~MouseInputDevice() 698 { 699 MID_CALLED(); 700 701 StopMonitoringDevice(kTouchpadDevicesDirectory); 702 StopMonitoringDevice(kMouseDevicesDirectory); 703 fDevices.MakeEmpty(); 704 } 705 706 707 status_t 708 MouseInputDevice::InitCheck() 709 { 710 MID_CALLED(); 711 712 return BInputServerDevice::InitCheck(); 713 } 714 715 716 status_t 717 MouseInputDevice::Start(const char* name, void* cookie) 718 { 719 MID_CALLED(); 720 721 MouseDevice* device = (MouseDevice*)cookie; 722 723 return device->Start(); 724 } 725 726 727 status_t 728 MouseInputDevice::Stop(const char* name, void* cookie) 729 { 730 TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name); 731 732 MouseDevice* device = (MouseDevice*)cookie; 733 device->Stop(); 734 735 return B_OK; 736 } 737 738 739 status_t 740 MouseInputDevice::Control(const char* name, void* cookie, 741 uint32 command, BMessage* message) 742 { 743 TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command); 744 745 MouseDevice* device = (MouseDevice*)cookie; 746 747 if (command == B_NODE_MONITOR) 748 return _HandleMonitor(message); 749 750 if (command == MS_SET_TOUCHPAD_SETTINGS) 751 return device->UpdateTouchpadSettings(message); 752 753 if (command >= B_MOUSE_TYPE_CHANGED 754 && command <= B_MOUSE_ACCELERATION_CHANGED) 755 return device->UpdateSettings(); 756 757 return B_BAD_VALUE; 758 } 759 760 761 status_t 762 MouseInputDevice::_HandleMonitor(BMessage* message) 763 { 764 MID_CALLED(); 765 766 const char* path; 767 int32 opcode; 768 if (message->FindInt32("opcode", &opcode) != B_OK 769 || (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED) 770 || message->FindString("path", &path) != B_OK) 771 return B_BAD_VALUE; 772 773 if (opcode == B_ENTRY_CREATED) 774 return _AddDevice(path); 775 776 #if 0 777 return _RemoveDevice(path); 778 #else 779 // Don't handle B_ENTRY_REMOVED, let the control thread take care of it. 780 return B_OK; 781 #endif 782 } 783 784 785 void 786 MouseInputDevice::_RecursiveScan(const char* directory) 787 { 788 MID_CALLED(); 789 790 BEntry entry; 791 BDirectory dir(directory); 792 while (dir.GetNextEntry(&entry) == B_OK) { 793 BPath path; 794 entry.GetPath(&path); 795 796 if (!strcmp(path.Leaf(), "serial")) { 797 // skip serial 798 continue; 799 } 800 801 if (entry.IsDirectory()) 802 _RecursiveScan(path.Path()); 803 else 804 _AddDevice(path.Path()); 805 } 806 } 807 808 809 MouseDevice* 810 MouseInputDevice::_FindDevice(const char* path) const 811 { 812 MID_CALLED(); 813 814 for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) { 815 MouseDevice* device = fDevices.ItemAt(i); 816 if (strcmp(device->Path(), path) == 0) 817 return device; 818 } 819 820 return NULL; 821 } 822 823 824 status_t 825 MouseInputDevice::_AddDevice(const char* path) 826 { 827 MID_CALLED(); 828 829 BAutolock _(fDeviceListLock); 830 831 _RemoveDevice(path); 832 833 MouseDevice* device = new(std::nothrow) MouseDevice(*this, path); 834 if (device == NULL) { 835 TRACE("No memory\n"); 836 return B_NO_MEMORY; 837 } 838 839 if (!fDevices.AddItem(device)) { 840 TRACE("No memory in list\n"); 841 delete device; 842 return B_NO_MEMORY; 843 } 844 845 input_device_ref* devices[2]; 846 devices[0] = device->DeviceRef(); 847 devices[1] = NULL; 848 849 TRACE("adding path: %s, name: %s\n", path, devices[0]->name); 850 851 return RegisterDevices(devices); 852 } 853 854 855 status_t 856 MouseInputDevice::_RemoveDevice(const char* path) 857 { 858 MID_CALLED(); 859 860 BAutolock _(fDeviceListLock); 861 862 MouseDevice* device = _FindDevice(path); 863 if (device == NULL) { 864 TRACE("%s not found\n", path); 865 return B_ENTRY_NOT_FOUND; 866 } 867 868 input_device_ref* devices[2]; 869 devices[0] = device->DeviceRef(); 870 devices[1] = NULL; 871 872 TRACE("removing path: %s, name: %s\n", path, devices[0]->name); 873 874 UnregisterDevices(devices); 875 876 fDevices.RemoveItem(device); 877 878 return B_OK; 879 } 880