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