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