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 BString string(fPath); 304 BString name; 305 306 int32 slash = string.FindLast("/"); 307 string.CopyInto(name, slash + 1, string.Length() - slash); 308 int32 index = atoi(name.String()) + 1; 309 310 int32 previousSlash = string.FindLast("/", slash); 311 string.CopyInto(name, previousSlash + 1, slash - previousSlash - 1); 312 313 if (name == "ps2") 314 name = "PS/2"; 315 316 if (name.Length() < 4) 317 name.ToUpper(); 318 else 319 name.Capitalize(); 320 321 if (string.FindFirst("touchpad") >= 0) { 322 name << " Touchpad "; 323 } else { 324 if (string.FindFirst("intelli") >= 0) 325 name.Prepend("Extended "); 326 327 name << " Mouse "; 328 } 329 name << index; 330 331 return strdup(name.String()); 332 } 333 334 335 // #pragma mark - control thread 336 337 338 status_t 339 MouseDevice::_ControlThreadEntry(void* arg) 340 { 341 MouseDevice* device = (MouseDevice*)arg; 342 device->_ControlThread(); 343 return B_OK; 344 } 345 346 347 void 348 MouseDevice::_ControlThread() 349 { 350 MD_CALLED(); 351 352 if (fDevice < 0) { 353 _ControlThreadCleanup(); 354 // TOAST! 355 return; 356 } 357 358 // touchpad settings 359 if (ioctl(fDevice, MS_IS_TOUCHPAD, NULL) == B_OK) { 360 TRACE("is touchpad %s\n", fPath.String()); 361 fIsTouchpad = true; 362 363 fTouchpadSettings = kDefaultTouchpadSettings; 364 365 BPath path; 366 status_t status = _GetTouchpadSettingsPath(path); 367 BFile settingsFile(path.Path(), B_READ_ONLY); 368 if (status == B_OK && settingsFile.InitCheck() == B_OK) { 369 if (settingsFile.Read(&fTouchpadSettings, sizeof(touchpad_settings)) 370 != sizeof(touchpad_settings)) { 371 TRACE("failed to load settings\n"); 372 } 373 } 374 _UpdateTouchpadSettings(); 375 } 376 377 _UpdateSettings(); 378 379 uint32 lastButtons = 0; 380 float historyDeltaX = 0.0; 381 float historyDeltaY = 0.0; 382 383 static const bigtime_t kTransferDelay = 1000000 / 125; 384 // 125 transfers per second should be more than enough 385 #define USE_REGULAR_INTERVAL 1 386 #if USE_REGULAR_INTERVAL 387 bigtime_t nextTransferTime = system_time() + kTransferDelay; 388 #endif 389 390 while (fActive) { 391 mouse_movement movements; 392 393 #if USE_REGULAR_INTERVAL 394 snooze_until(nextTransferTime, B_SYSTEM_TIMEBASE); 395 nextTransferTime += kTransferDelay; 396 #endif 397 398 if (ioctl(fDevice, MS_READ, &movements, sizeof(movements)) != B_OK) { 399 LOG_ERR("Mouse device exiting, %s\n", strerror(errno)); 400 _ControlThreadCleanup(); 401 // TOAST! 402 return; 403 } 404 405 // take care of updating the settings first, if necessary 406 if (fUpdateSettings) { 407 fUpdateSettings = false; 408 if (fIsTouchpad) { 409 BAutolock _(fTouchpadSettingsLock); 410 if (fTouchpadSettingsMessage != NULL) { 411 _ReadTouchpadSettingsMsg(fTouchpadSettingsMessage); 412 _UpdateTouchpadSettings(); 413 delete fTouchpadSettingsMessage; 414 fTouchpadSettingsMessage = NULL; 415 } else 416 _UpdateSettings(); 417 } else 418 _UpdateSettings(); 419 } 420 421 uint32 buttons = lastButtons ^ movements.buttons; 422 423 uint32 remappedButtons = _RemapButtons(movements.buttons); 424 int32 deltaX, deltaY; 425 _ComputeAcceleration(movements, deltaX, deltaY, historyDeltaX, 426 historyDeltaY); 427 428 LOG_EVENT("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, " 429 "wheel_x:%ld, wheel_y:%ld\n", 430 fDeviceRef.name, movements.buttons, 431 movements.xdelta, movements.ydelta, movements.clicks, 432 movements.wheel_xdelta, movements.wheel_ydelta); 433 LOG_EVENT("%s: x: %ld, y: %ld (%.4f, %.4f)\n", fDeviceRef.name, 434 deltaX, deltaY, historyDeltaX, historyDeltaY); 435 436 // Send single messages for each event 437 438 if (buttons != 0) { 439 bool pressedButton = (buttons & movements.buttons) > 0; 440 BMessage* message = _BuildMouseMessage( 441 pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP, 442 movements.timestamp, remappedButtons, deltaX, deltaY); 443 if (message != NULL) { 444 if (pressedButton) { 445 message->AddInt32("clicks", movements.clicks); 446 LOG_EVENT("B_MOUSE_DOWN\n"); 447 } else 448 LOG_EVENT("B_MOUSE_UP\n"); 449 450 fTarget.EnqueueMessage(message); 451 lastButtons = movements.buttons; 452 } 453 } 454 455 if (movements.xdelta != 0 || movements.ydelta != 0) { 456 BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED, 457 movements.timestamp, remappedButtons, deltaX, deltaY); 458 if (message != NULL) 459 fTarget.EnqueueMessage(message); 460 } 461 462 if (movements.wheel_ydelta != 0 || movements.wheel_xdelta != 0) { 463 BMessage* message = new BMessage(B_MOUSE_WHEEL_CHANGED); 464 if (message == NULL) 465 continue; 466 467 if (message->AddInt64("when", movements.timestamp) == B_OK 468 && message->AddFloat("be:wheel_delta_x", 469 movements.wheel_xdelta) == B_OK 470 && message->AddFloat("be:wheel_delta_y", 471 movements.wheel_ydelta) == B_OK) 472 fTarget.EnqueueMessage(message); 473 else 474 delete message; 475 } 476 477 #if !USE_REGULAR_INTERVAL 478 snooze(kTransferDelay); 479 #endif 480 } 481 } 482 483 484 void 485 MouseDevice::_ControlThreadCleanup() 486 { 487 // NOTE: Only executed when the control thread detected an error 488 // and from within the control thread! 489 490 if (fActive) { 491 fThread = -1; 492 fTarget._RemoveDevice(fPath.String()); 493 } else { 494 // In case active is already false, another thread 495 // waits for this thread to quit, and may already hold 496 // locks that _RemoveDevice() wants to acquire. In other 497 // words, the device is already being removed, so we simply 498 // quit here. 499 } 500 } 501 502 503 void 504 MouseDevice::_UpdateSettings() 505 { 506 MD_CALLED(); 507 508 // retrieve current values 509 510 if (get_mouse_map(&fSettings.map) != B_OK) 511 LOG_ERR("error when get_mouse_map\n"); 512 else { 513 fDeviceRemapsButtons 514 = ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK; 515 } 516 517 if (get_click_speed(&fSettings.click_speed) != B_OK) 518 LOG_ERR("error when get_click_speed\n"); 519 else 520 ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed); 521 522 if (get_mouse_speed(&fSettings.accel.speed) != B_OK) 523 LOG_ERR("error when get_mouse_speed\n"); 524 else { 525 if (get_mouse_acceleration(&fSettings.accel.accel_factor) != B_OK) 526 LOG_ERR("error when get_mouse_acceleration\n"); 527 else { 528 mouse_accel accel; 529 ioctl(fDevice, MS_GET_ACCEL, &accel); 530 accel.speed = fSettings.accel.speed; 531 accel.accel_factor = fSettings.accel.accel_factor; 532 ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel); 533 } 534 } 535 536 if (get_mouse_type(&fSettings.type) != B_OK) 537 LOG_ERR("error when get_mouse_type\n"); 538 else 539 ioctl(fDevice, MS_SET_TYPE, &fSettings.type); 540 } 541 542 543 status_t 544 MouseDevice::_GetTouchpadSettingsPath(BPath& path) 545 { 546 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 547 if (status < B_OK) 548 return status; 549 return path.Append(TOUCHPAD_SETTINGS_FILE); 550 } 551 552 553 status_t 554 MouseDevice::_ReadTouchpadSettingsMsg(BMessage* message) 555 { 556 message->FindBool("scroll_twofinger", &fTouchpadSettings.scroll_twofinger); 557 message->FindBool("scroll_twofinger_horizontal", 558 &fTouchpadSettings.scroll_twofinger_horizontal); 559 message->FindFloat("scroll_rightrange", 560 &fTouchpadSettings.scroll_rightrange); 561 message->FindFloat("scroll_bottomrange", 562 &fTouchpadSettings.scroll_bottomrange); 563 564 message->FindInt16("scroll_xstepsize", 565 (int16*)&fTouchpadSettings.scroll_xstepsize); 566 message->FindInt16("scroll_ystepsize", 567 (int16*)&fTouchpadSettings.scroll_ystepsize); 568 message->FindInt8("scroll_acceleration", 569 (int8*)&fTouchpadSettings.scroll_acceleration); 570 message->FindInt8("tapgesture_sensibility", 571 (int8*)&fTouchpadSettings.tapgesture_sensibility); 572 573 return B_OK; 574 } 575 576 577 status_t 578 MouseDevice::_UpdateTouchpadSettings() 579 { 580 if (fIsTouchpad) { 581 ioctl(fDevice, MS_SET_TOUCHPAD_SETTINGS, &fTouchpadSettings); 582 return B_OK; 583 } 584 return B_ERROR; 585 } 586 587 588 BMessage* 589 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons, 590 int32 deltaX, int32 deltaY) const 591 { 592 BMessage* message = new BMessage(what); 593 if (message == NULL) 594 return NULL; 595 596 if (message->AddInt64("when", when) < B_OK 597 || message->AddInt32("buttons", buttons) < B_OK 598 || message->AddInt32("x", deltaX) < B_OK 599 || message->AddInt32("y", deltaY) < B_OK) { 600 delete message; 601 return NULL; 602 } 603 604 return message; 605 } 606 607 608 void 609 MouseDevice::_ComputeAcceleration(const mouse_movement& movements, 610 int32& _deltaX, int32& _deltaY, float& historyDeltaX, 611 float& historyDeltaY) const 612 { 613 // basic mouse speed 614 float deltaX = (float)movements.xdelta * fSettings.accel.speed / 65536.0 615 + historyDeltaX; 616 float deltaY = (float)movements.ydelta * fSettings.accel.speed / 65536.0 617 + historyDeltaY; 618 619 // acceleration 620 double acceleration = 1; 621 if (fSettings.accel.accel_factor) { 622 acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY) 623 * fSettings.accel.accel_factor / 524288.0; 624 } 625 626 deltaX *= acceleration; 627 deltaY *= acceleration; 628 629 if (deltaX >= 0) 630 _deltaX = (int32)floorf(deltaX); 631 else 632 _deltaX = (int32)ceilf(deltaX); 633 634 if (deltaY >= 0) 635 _deltaY = (int32)floorf(deltaY); 636 else 637 _deltaY = (int32)ceilf(deltaY); 638 639 historyDeltaX = deltaX - _deltaX; 640 historyDeltaY = deltaY - _deltaY; 641 } 642 643 644 uint32 645 MouseDevice::_RemapButtons(uint32 buttons) const 646 { 647 if (fDeviceRemapsButtons) 648 return buttons; 649 650 uint32 newButtons = 0; 651 for (int32 i = 0; buttons; i++) { 652 if (buttons & 0x1) { 653 #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO) 654 newButtons |= fSettings.map.button[i]; 655 #else 656 if (i == 0) 657 newButtons |= fSettings.map.left; 658 if (i == 1) 659 newButtons |= fSettings.map.right; 660 if (i == 2) 661 newButtons |= fSettings.map.middle; 662 #endif 663 } 664 buttons >>= 1; 665 } 666 667 return newButtons; 668 } 669 670 671 // #pragma mark - 672 673 674 MouseInputDevice::MouseInputDevice() 675 : 676 fDevices(2, true), 677 fDeviceListLock("MouseInputDevice list") 678 { 679 MID_CALLED(); 680 681 StartMonitoringDevice(kMouseDevicesDirectory); 682 StartMonitoringDevice(kTouchpadDevicesDirectory); 683 _RecursiveScan(kMouseDevicesDirectory); 684 _RecursiveScan(kTouchpadDevicesDirectory); 685 } 686 687 688 MouseInputDevice::~MouseInputDevice() 689 { 690 MID_CALLED(); 691 692 StopMonitoringDevice(kTouchpadDevicesDirectory); 693 StopMonitoringDevice(kMouseDevicesDirectory); 694 fDevices.MakeEmpty(); 695 } 696 697 698 status_t 699 MouseInputDevice::InitCheck() 700 { 701 MID_CALLED(); 702 703 return BInputServerDevice::InitCheck(); 704 } 705 706 707 status_t 708 MouseInputDevice::Start(const char* name, void* cookie) 709 { 710 MID_CALLED(); 711 712 MouseDevice* device = (MouseDevice*)cookie; 713 714 return device->Start(); 715 } 716 717 718 status_t 719 MouseInputDevice::Stop(const char* name, void* cookie) 720 { 721 TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name); 722 723 MouseDevice* device = (MouseDevice*)cookie; 724 device->Stop(); 725 726 return B_OK; 727 } 728 729 730 status_t 731 MouseInputDevice::Control(const char* name, void* cookie, 732 uint32 command, BMessage* message) 733 { 734 TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command); 735 736 MouseDevice* device = (MouseDevice*)cookie; 737 738 if (command == B_NODE_MONITOR) 739 return _HandleMonitor(message); 740 741 if (command == MS_SET_TOUCHPAD_SETTINGS) 742 return device->UpdateTouchpadSettings(message); 743 744 if (command >= B_MOUSE_TYPE_CHANGED 745 && command <= B_MOUSE_ACCELERATION_CHANGED) 746 return device->UpdateSettings(); 747 748 return B_BAD_VALUE; 749 } 750 751 752 status_t 753 MouseInputDevice::_HandleMonitor(BMessage* message) 754 { 755 MID_CALLED(); 756 757 const char* path; 758 int32 opcode; 759 if (message->FindInt32("opcode", &opcode) != B_OK 760 || (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED) 761 || message->FindString("path", &path) != B_OK) 762 return B_BAD_VALUE; 763 764 if (opcode == B_ENTRY_CREATED) 765 return _AddDevice(path); 766 767 #if 0 768 return _RemoveDevice(path); 769 #else 770 // Don't handle B_ENTRY_REMOVED, let the control thread take care of it. 771 return B_OK; 772 #endif 773 } 774 775 776 void 777 MouseInputDevice::_RecursiveScan(const char* directory) 778 { 779 MID_CALLED(); 780 781 BEntry entry; 782 BDirectory dir(directory); 783 while (dir.GetNextEntry(&entry) == B_OK) { 784 BPath path; 785 entry.GetPath(&path); 786 787 if (!strcmp(path.Leaf(), "serial")) { 788 // skip serial 789 continue; 790 } 791 792 if (entry.IsDirectory()) 793 _RecursiveScan(path.Path()); 794 else 795 _AddDevice(path.Path()); 796 } 797 } 798 799 800 MouseDevice* 801 MouseInputDevice::_FindDevice(const char* path) const 802 { 803 MID_CALLED(); 804 805 for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) { 806 MouseDevice* device = fDevices.ItemAt(i); 807 if (strcmp(device->Path(), path) == 0) 808 return device; 809 } 810 811 return NULL; 812 } 813 814 815 status_t 816 MouseInputDevice::_AddDevice(const char* path) 817 { 818 MID_CALLED(); 819 820 BAutolock _(fDeviceListLock); 821 822 _RemoveDevice(path); 823 824 MouseDevice* device = new(std::nothrow) MouseDevice(*this, path); 825 if (device == NULL) { 826 TRACE("No memory\n"); 827 return B_NO_MEMORY; 828 } 829 830 if (!fDevices.AddItem(device)) { 831 TRACE("No memory in list\n"); 832 delete device; 833 return B_NO_MEMORY; 834 } 835 836 input_device_ref* devices[2]; 837 devices[0] = device->DeviceRef(); 838 devices[1] = NULL; 839 840 TRACE("adding path: %s, name: %s\n", path, devices[0]->name); 841 842 return RegisterDevices(devices); 843 } 844 845 846 status_t 847 MouseInputDevice::_RemoveDevice(const char* path) 848 { 849 MID_CALLED(); 850 851 BAutolock _(fDeviceListLock); 852 853 MouseDevice* device = _FindDevice(path); 854 if (device == NULL) { 855 TRACE("%s not found\n", path); 856 return B_ENTRY_NOT_FOUND; 857 } 858 859 input_device_ref* devices[2]; 860 devices[0] = device->DeviceRef(); 861 devices[1] = NULL; 862 863 TRACE("removing path: %s, name: %s\n", path, devices[0]->name); 864 865 UnregisterDevices(devices); 866 867 fDevices.RemoveItem(device); 868 869 return B_OK; 870 } 871