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 #include "movement_maker.h" 38 39 40 #undef TRACE 41 //#define TRACE_MOUSE_DEVICE 42 #ifdef TRACE_MOUSE_DEVICE 43 44 class FunctionTracer { 45 public: 46 FunctionTracer(const void* pointer, const char* className, 47 const char* functionName, 48 int32& depth) 49 : fFunctionName(), 50 fPrepend(), 51 fFunctionDepth(depth), 52 fPointer(pointer) 53 { 54 fFunctionDepth++; 55 fPrepend.Append(' ', fFunctionDepth * 2); 56 fFunctionName << className << "::" << functionName << "()"; 57 58 debug_printf("%p -> %s%s {\n", fPointer, fPrepend.String(), 59 fFunctionName.String()); 60 } 61 62 ~FunctionTracer() 63 { 64 debug_printf("%p -> %s}\n", fPointer, fPrepend.String()); 65 fFunctionDepth--; 66 } 67 68 private: 69 BString fFunctionName; 70 BString fPrepend; 71 int32& fFunctionDepth; 72 const void* fPointer; 73 }; 74 75 76 static int32 sFunctionDepth = -1; 77 # define MD_CALLED(x...) FunctionTracer _ft(this, "MouseDevice", \ 78 __FUNCTION__, sFunctionDepth) 79 # define MID_CALLED(x...) FunctionTracer _ft(this, "MouseInputDevice", \ 80 __FUNCTION__, sFunctionDepth) 81 # define TRACE(x...) do { BString _to; \ 82 _to.Append(' ', (sFunctionDepth + 1) * 2); \ 83 debug_printf("%p -> %s", this, _to.String()); \ 84 debug_printf(x); } while (0) 85 # define LOG_EVENT(text...) do {} while (0) 86 # define LOG_ERR(text...) TRACE(text) 87 #else 88 # define TRACE(x...) do {} while (0) 89 # define MD_CALLED(x...) TRACE(x) 90 # define MID_CALLED(x...) TRACE(x) 91 # define LOG_ERR(x...) debug_printf(x) 92 # define LOG_EVENT(x...) TRACE(x) 93 #endif 94 95 96 const static uint32 kMouseThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4; 97 const static char* kMouseDevicesDirectory = "/dev/input/mouse"; 98 const static char* kTouchpadDevicesDirectory = "/dev/input/touchpad"; 99 100 101 class MouseDevice { 102 public: 103 MouseDevice(MouseInputDevice& target, 104 const char* path); 105 ~MouseDevice(); 106 107 status_t Start(); 108 void Stop(); 109 110 status_t UpdateSettings(); 111 status_t UpdateTouchpadSettings(const BMessage* message); 112 113 const char* Path() const { return fPath.String(); } 114 input_device_ref* DeviceRef() { return &fDeviceRef; } 115 116 private: 117 char* _BuildShortName() const; 118 119 static status_t _ControlThreadEntry(void* arg); 120 void _ControlThread(); 121 void _ControlThreadCleanup(); 122 void _UpdateSettings(); 123 124 status_t _GetTouchpadSettingsPath(BPath& path); 125 status_t _UpdateTouchpadSettings(BMessage* message); 126 127 BMessage* _BuildMouseMessage(uint32 what, 128 uint64 when, uint32 buttons, 129 int32 deltaX, int32 deltaY) const; 130 void _ComputeAcceleration( 131 const mouse_movement& movements, 132 int32& deltaX, int32& deltaY, 133 float& historyDeltaX, 134 float& historyDeltaY) const; 135 uint32 _RemapButtons(uint32 buttons) const; 136 137 private: 138 MouseInputDevice& fTarget; 139 BString fPath; 140 int fDevice; 141 142 input_device_ref fDeviceRef; 143 mouse_settings fSettings; 144 bool fDeviceRemapsButtons; 145 146 thread_id fThread; 147 volatile bool fActive; 148 volatile bool fUpdateSettings; 149 150 bool fIsTouchpad; 151 TouchpadMovement fTouchpadMovementMaker; 152 BMessage* fTouchpadSettingsMessage; 153 BLocker fTouchpadSettingsLock; 154 }; 155 156 157 extern "C" BInputServerDevice* 158 instantiate_input_device() 159 { 160 return new(std::nothrow) MouseInputDevice(); 161 } 162 163 164 // #pragma mark - 165 166 167 MouseDevice::MouseDevice(MouseInputDevice& target, const char* driverPath) 168 : 169 fTarget(target), 170 fPath(driverPath), 171 fDevice(-1), 172 fDeviceRemapsButtons(false), 173 fThread(-1), 174 fActive(false), 175 fUpdateSettings(false), 176 fIsTouchpad(false), 177 fTouchpadSettingsMessage(NULL), 178 fTouchpadSettingsLock("Touchpad settings lock") 179 { 180 MD_CALLED(); 181 182 fDeviceRef.name = _BuildShortName(); 183 fDeviceRef.type = B_POINTING_DEVICE; 184 fDeviceRef.cookie = this; 185 186 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 187 for (int i = 0; i < B_MAX_MOUSE_BUTTONS; i++) 188 fSettings.map.button[i] = B_MOUSE_BUTTON(i + 1); 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 // Check the whole string for "touchpad" because it's a different directory 328 // FIXME use MS_IS_TOUCHPAD ioctl instead (or fIsTouchpad which caches its 329 // result) but this can only be done after the control thread is running 330 if (string.FindFirst("touchpad") >= 0) { 331 name << " Touchpad "; 332 } else if (deviceName.FindFirst("trackpoint") >= 0) { 333 // That's always PS/2, so don't keep the bus name 334 name = "Trackpoint "; 335 } else { 336 if (deviceName.FindFirst("intelli") >= 0) 337 name.Prepend("Extended "); 338 339 name << " Mouse "; 340 } 341 name << index; 342 343 return strdup(name.String()); 344 } 345 346 347 // #pragma mark - control thread 348 349 350 status_t 351 MouseDevice::_ControlThreadEntry(void* arg) 352 { 353 MouseDevice* device = (MouseDevice*)arg; 354 device->_ControlThread(); 355 return B_OK; 356 } 357 358 359 void 360 MouseDevice::_ControlThread() 361 { 362 MD_CALLED(); 363 364 if (fDevice < 0) { 365 _ControlThreadCleanup(); 366 // TOAST! 367 return; 368 } 369 370 // touchpad settings 371 touchpad_specs touchpadSpecs; 372 if (ioctl(fDevice, MS_IS_TOUCHPAD, &touchpadSpecs, sizeof(touchpadSpecs)) == B_OK) { 373 TRACE("is touchpad %s\n", fPath.String()); 374 fIsTouchpad = true; 375 376 touchpad_settings settings; 377 settings = kDefaultTouchpadSettings; 378 379 BPath path; 380 status_t status = _GetTouchpadSettingsPath(path); 381 BFile settingsFile(path.Path(), B_READ_ONLY); 382 if (status == B_OK && settingsFile.InitCheck() == B_OK) { 383 if (settingsFile.Read(&settings, sizeof(touchpad_settings)) 384 != sizeof(touchpad_settings)) { 385 TRACE("failed to load settings\n"); 386 } 387 } 388 389 fTouchpadMovementMaker.SetSpecs(touchpadSpecs); 390 fTouchpadMovementMaker.SetSettings(settings); 391 } 392 393 _UpdateSettings(); 394 395 uint32 lastButtons = 0; 396 float historyDeltaX = 0.0; 397 float historyDeltaY = 0.0; 398 399 static const bigtime_t kTransferDelay = 1000000 / 125; 400 // 125 transfers per second should be more than enough 401 #define USE_REGULAR_INTERVAL 1 402 #if USE_REGULAR_INTERVAL 403 bigtime_t nextTransferTime = system_time() + kTransferDelay; 404 #endif 405 406 // touchpads only 407 touchpad_movement lastTouchpadMovement; 408 bigtime_t touchpadEventTimeout = B_INFINITE_TIMEOUT; 409 410 while (fActive) { 411 mouse_movement movements; 412 413 #if USE_REGULAR_INTERVAL 414 snooze_until(nextTransferTime, B_SYSTEM_TIMEBASE); 415 nextTransferTime += kTransferDelay; 416 #endif 417 418 if (!fIsTouchpad) { 419 if (ioctl(fDevice, MS_READ, &movements, sizeof(movements)) != B_OK) { 420 LOG_ERR("Mouse device exiting, %s\n", strerror(errno)); 421 _ControlThreadCleanup(); 422 // TOAST! 423 return; 424 } 425 } else { 426 touchpad_read read; 427 read.timeout = touchpadEventTimeout; 428 429 status_t status = ioctl(fDevice, MS_READ_TOUCHPAD, &read, sizeof(read)); 430 if (status < 0) 431 status = errno; 432 if (status != B_OK && status != B_TIMED_OUT) { 433 LOG_ERR("Mouse (touchpad) device exiting, %s\n", strerror(errno)); 434 _ControlThreadCleanup(); 435 // TOAST! 436 return; 437 } else if (status == B_TIMED_OUT) { 438 read.event = MS_READ_TOUCHPAD; 439 read.u.touchpad = lastTouchpadMovement; 440 } 441 442 if (read.event == MS_READ_TOUCHPAD) { 443 lastTouchpadMovement = read.u.touchpad; 444 status = fTouchpadMovementMaker.EventToMovement(&read.u.touchpad, 445 &movements, touchpadEventTimeout); 446 } else if (read.event == MS_READ) { 447 movements = read.u.mouse; 448 touchpadEventTimeout = -1; 449 } 450 451 if (status != B_OK) 452 continue; 453 } 454 455 // take care of updating the settings first, if necessary 456 if (fUpdateSettings) { 457 fUpdateSettings = false; 458 if (fIsTouchpad) { 459 BAutolock _(fTouchpadSettingsLock); 460 if (fTouchpadSettingsMessage != NULL) { 461 _UpdateTouchpadSettings(fTouchpadSettingsMessage); 462 delete fTouchpadSettingsMessage; 463 fTouchpadSettingsMessage = NULL; 464 } else 465 _UpdateSettings(); 466 } else 467 _UpdateSettings(); 468 } 469 470 uint32 buttons = lastButtons ^ movements.buttons; 471 472 uint32 remappedButtons = _RemapButtons(movements.buttons); 473 int32 deltaX, deltaY; 474 _ComputeAcceleration(movements, deltaX, deltaY, historyDeltaX, 475 historyDeltaY); 476 477 LOG_EVENT("%s: buttons: 0x%lx, x: %ld, y: %ld, clicks:%ld, " 478 "wheel_x:%ld, wheel_y:%ld\n", 479 fDeviceRef.name, movements.buttons, 480 movements.xdelta, movements.ydelta, movements.clicks, 481 movements.wheel_xdelta, movements.wheel_ydelta); 482 LOG_EVENT("%s: x: %ld, y: %ld (%.4f, %.4f)\n", fDeviceRef.name, 483 deltaX, deltaY, historyDeltaX, historyDeltaY); 484 485 // Send single messages for each event 486 487 if (buttons != 0) { 488 bool pressedButton = (buttons & movements.buttons) > 0; 489 BMessage* message = _BuildMouseMessage( 490 pressedButton ? B_MOUSE_DOWN : B_MOUSE_UP, 491 movements.timestamp, remappedButtons, deltaX, deltaY); 492 if (message != NULL) { 493 if (pressedButton) { 494 message->AddInt32("clicks", movements.clicks); 495 LOG_EVENT("B_MOUSE_DOWN\n"); 496 } else 497 LOG_EVENT("B_MOUSE_UP\n"); 498 499 fTarget.EnqueueMessage(message); 500 lastButtons = movements.buttons; 501 } 502 } 503 504 if (movements.xdelta != 0 || movements.ydelta != 0) { 505 BMessage* message = _BuildMouseMessage(B_MOUSE_MOVED, 506 movements.timestamp, remappedButtons, deltaX, deltaY); 507 if (message != NULL) 508 fTarget.EnqueueMessage(message); 509 } 510 511 if (movements.wheel_ydelta != 0 || movements.wheel_xdelta != 0) { 512 BMessage* message = new BMessage(B_MOUSE_WHEEL_CHANGED); 513 if (message == NULL) 514 continue; 515 516 if (message->AddInt64("when", movements.timestamp) == B_OK 517 && message->AddFloat("be:wheel_delta_x", 518 movements.wheel_xdelta) == B_OK 519 && message->AddFloat("be:wheel_delta_y", 520 movements.wheel_ydelta) == B_OK) 521 fTarget.EnqueueMessage(message); 522 else 523 delete message; 524 } 525 526 #if !USE_REGULAR_INTERVAL 527 snooze(kTransferDelay); 528 #endif 529 } 530 } 531 532 533 void 534 MouseDevice::_ControlThreadCleanup() 535 { 536 // NOTE: Only executed when the control thread detected an error 537 // and from within the control thread! 538 539 if (fActive) { 540 fThread = -1; 541 fTarget._RemoveDevice(fPath.String()); 542 } else { 543 // In case active is already false, another thread 544 // waits for this thread to quit, and may already hold 545 // locks that _RemoveDevice() wants to acquire. In other 546 // words, the device is already being removed, so we simply 547 // quit here. 548 } 549 } 550 551 552 void 553 MouseDevice::_UpdateSettings() 554 { 555 MD_CALLED(); 556 // retrieve current values 557 558 if (get_mouse_map(&fSettings.map) != B_OK) 559 LOG_ERR("error when get_mouse_map\n"); 560 else { 561 fDeviceRemapsButtons 562 = ioctl(fDevice, MS_SET_MAP, &fSettings.map) == B_OK; 563 } 564 565 if (get_click_speed(&fSettings.click_speed) == B_OK) { 566 if (fIsTouchpad) 567 fTouchpadMovementMaker.click_speed = fSettings.click_speed; 568 ioctl(fDevice, MS_SET_CLICKSPEED, &fSettings.click_speed); 569 } else 570 LOG_ERR("error when get_click_speed\n"); 571 572 if (get_mouse_speed(fDeviceRef.name, &fSettings.accel.speed) != B_OK) 573 LOG_ERR("error when get_mouse_speed\n"); 574 else { 575 if (get_mouse_acceleration(fDeviceRef.name, &fSettings.accel.accel_factor) != B_OK) 576 LOG_ERR("error when get_mouse_acceleration\n"); 577 else { 578 mouse_accel accel; 579 ioctl(fDevice, MS_GET_ACCEL, &accel); 580 accel.speed = fSettings.accel.speed; 581 accel.accel_factor = fSettings.accel.accel_factor; 582 ioctl(fDevice, MS_SET_ACCEL, &fSettings.accel); 583 } 584 } 585 586 if (get_mouse_type(fDeviceRef.name, &fSettings.type) != B_OK) 587 LOG_ERR("error when get_mouse_type\n"); 588 else 589 ioctl(fDevice, MS_SET_TYPE, &fSettings.type); 590 } 591 592 593 status_t 594 MouseDevice::_GetTouchpadSettingsPath(BPath& path) 595 { 596 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 597 if (status < B_OK) 598 return status; 599 return path.Append(TOUCHPAD_SETTINGS_FILE); 600 } 601 602 603 status_t 604 MouseDevice::_UpdateTouchpadSettings(BMessage* message) 605 { 606 touchpad_settings settings; 607 message->FindBool("scroll_twofinger", &settings.scroll_twofinger); 608 message->FindBool("scroll_twofinger_horizontal", 609 &settings.scroll_twofinger_horizontal); 610 message->FindFloat("scroll_rightrange", 611 &settings.scroll_rightrange); 612 message->FindFloat("scroll_bottomrange", 613 &settings.scroll_bottomrange); 614 615 message->FindInt16("scroll_xstepsize", 616 (int16*)&settings.scroll_xstepsize); 617 message->FindInt16("scroll_ystepsize", 618 (int16*)&settings.scroll_ystepsize); 619 message->FindInt8("scroll_acceleration", 620 (int8*)&settings.scroll_acceleration); 621 message->FindInt8("tapgesture_sensibility", 622 (int8*)&settings.tapgesture_sensibility); 623 624 if (fIsTouchpad) 625 fTouchpadMovementMaker.SetSettings(settings); 626 627 return B_OK; 628 } 629 630 631 BMessage* 632 MouseDevice::_BuildMouseMessage(uint32 what, uint64 when, uint32 buttons, 633 int32 deltaX, int32 deltaY) const 634 { 635 BMessage* message = new BMessage(what); 636 if (message == NULL) 637 return NULL; 638 639 if (message->AddInt64("when", when) < B_OK 640 || message->AddInt32("buttons", buttons) < B_OK 641 || message->AddInt32("x", deltaX) < B_OK 642 || message->AddInt32("y", deltaY) < B_OK) { 643 delete message; 644 return NULL; 645 } 646 647 return message; 648 } 649 650 651 void 652 MouseDevice::_ComputeAcceleration(const mouse_movement& movements, 653 int32& _deltaX, int32& _deltaY, float& historyDeltaX, 654 float& historyDeltaY) const 655 { 656 // basic mouse speed 657 float deltaX = (float)movements.xdelta * fSettings.accel.speed / 65536.0 658 + historyDeltaX; 659 float deltaY = (float)movements.ydelta * fSettings.accel.speed / 65536.0 660 + historyDeltaY; 661 662 // acceleration 663 double acceleration = 1; 664 if (fSettings.accel.accel_factor) { 665 acceleration = 1 + sqrt(deltaX * deltaX + deltaY * deltaY) 666 * fSettings.accel.accel_factor / 524288.0; 667 } 668 669 deltaX *= acceleration; 670 deltaY *= acceleration; 671 672 if (deltaX >= 0) 673 _deltaX = (int32)floorf(deltaX); 674 else 675 _deltaX = (int32)ceilf(deltaX); 676 677 if (deltaY >= 0) 678 _deltaY = (int32)floorf(deltaY); 679 else 680 _deltaY = (int32)ceilf(deltaY); 681 682 historyDeltaX = deltaX - _deltaX; 683 historyDeltaY = deltaY - _deltaY; 684 } 685 686 687 uint32 688 MouseDevice::_RemapButtons(uint32 buttons) const 689 { 690 if (fDeviceRemapsButtons) 691 return buttons; 692 693 uint32 newButtons = 0; 694 for (int32 i = 0; buttons; i++) { 695 if (buttons & 0x1) { 696 #if defined(HAIKU_TARGET_PLATFORM_HAIKU) || defined(HAIKU_TARGET_PLATFORM_DANO) 697 newButtons |= fSettings.map.button[i]; 698 #else 699 if (i == 0) 700 newButtons |= fSettings.map.left; 701 if (i == 1) 702 newButtons |= fSettings.map.right; 703 if (i == 2) 704 newButtons |= fSettings.map.middle; 705 #endif 706 } 707 buttons >>= 1; 708 } 709 710 return newButtons; 711 } 712 713 714 // #pragma mark - 715 716 717 MouseInputDevice::MouseInputDevice() 718 : 719 fDevices(2, true), 720 fDeviceListLock("MouseInputDevice list") 721 { 722 MID_CALLED(); 723 724 StartMonitoringDevice(kMouseDevicesDirectory); 725 StartMonitoringDevice(kTouchpadDevicesDirectory); 726 _RecursiveScan(kMouseDevicesDirectory); 727 _RecursiveScan(kTouchpadDevicesDirectory); 728 } 729 730 731 MouseInputDevice::~MouseInputDevice() 732 { 733 MID_CALLED(); 734 735 StopMonitoringDevice(kTouchpadDevicesDirectory); 736 StopMonitoringDevice(kMouseDevicesDirectory); 737 fDevices.MakeEmpty(); 738 } 739 740 741 status_t 742 MouseInputDevice::InitCheck() 743 { 744 MID_CALLED(); 745 746 return BInputServerDevice::InitCheck(); 747 } 748 749 750 status_t 751 MouseInputDevice::Start(const char* name, void* cookie) 752 { 753 MID_CALLED(); 754 755 MouseDevice* device = (MouseDevice*)cookie; 756 757 return device->Start(); 758 } 759 760 761 status_t 762 MouseInputDevice::Stop(const char* name, void* cookie) 763 { 764 TRACE("%s(%s)\n", __PRETTY_FUNCTION__, name); 765 766 MouseDevice* device = (MouseDevice*)cookie; 767 device->Stop(); 768 769 return B_OK; 770 } 771 772 773 status_t 774 MouseInputDevice::Control(const char* name, void* cookie, 775 uint32 command, BMessage* message) 776 { 777 TRACE("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command); 778 779 MouseDevice* device = (MouseDevice*)cookie; 780 781 if (command == B_NODE_MONITOR) 782 return _HandleMonitor(message); 783 784 if (command == B_SET_TOUCHPAD_SETTINGS) 785 return device->UpdateTouchpadSettings(message); 786 787 if (command >= B_MOUSE_TYPE_CHANGED 788 && command <= B_MOUSE_ACCELERATION_CHANGED) 789 return device->UpdateSettings(); 790 791 return B_BAD_VALUE; 792 } 793 794 795 status_t 796 MouseInputDevice::_HandleMonitor(BMessage* message) 797 { 798 MID_CALLED(); 799 800 const char* path; 801 int32 opcode; 802 if (message->FindInt32("opcode", &opcode) != B_OK 803 || (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED) 804 || message->FindString("path", &path) != B_OK) 805 return B_BAD_VALUE; 806 807 if (opcode == B_ENTRY_CREATED) 808 return _AddDevice(path); 809 810 #if 0 811 return _RemoveDevice(path); 812 #else 813 // Don't handle B_ENTRY_REMOVED, let the control thread take care of it. 814 return B_OK; 815 #endif 816 } 817 818 819 void 820 MouseInputDevice::_RecursiveScan(const char* directory) 821 { 822 MID_CALLED(); 823 824 BEntry entry; 825 BDirectory dir(directory); 826 while (dir.GetNextEntry(&entry) == B_OK) { 827 BPath path; 828 entry.GetPath(&path); 829 830 if (!strcmp(path.Leaf(), "serial")) { 831 // skip serial 832 continue; 833 } 834 835 if (entry.IsDirectory()) 836 _RecursiveScan(path.Path()); 837 else 838 _AddDevice(path.Path()); 839 } 840 } 841 842 843 MouseDevice* 844 MouseInputDevice::_FindDevice(const char* path) const 845 { 846 MID_CALLED(); 847 848 for (int32 i = fDevices.CountItems() - 1; i >= 0; i--) { 849 MouseDevice* device = fDevices.ItemAt(i); 850 if (strcmp(device->Path(), path) == 0) 851 return device; 852 } 853 854 return NULL; 855 } 856 857 858 status_t 859 MouseInputDevice::_AddDevice(const char* path) 860 { 861 MID_CALLED(); 862 863 BAutolock _(fDeviceListLock); 864 865 _RemoveDevice(path); 866 867 MouseDevice* device = new(std::nothrow) MouseDevice(*this, path); 868 if (device == NULL) { 869 TRACE("No memory\n"); 870 return B_NO_MEMORY; 871 } 872 873 if (!fDevices.AddItem(device)) { 874 TRACE("No memory in list\n"); 875 delete device; 876 return B_NO_MEMORY; 877 } 878 879 input_device_ref* devices[2]; 880 devices[0] = device->DeviceRef(); 881 devices[1] = NULL; 882 883 TRACE("adding path: %s, name: %s\n", path, devices[0]->name); 884 885 return RegisterDevices(devices); 886 } 887 888 889 status_t 890 MouseInputDevice::_RemoveDevice(const char* path) 891 { 892 MID_CALLED(); 893 894 BAutolock _(fDeviceListLock); 895 896 MouseDevice* device = _FindDevice(path); 897 if (device == NULL) { 898 TRACE("%s not found\n", path); 899 return B_ENTRY_NOT_FOUND; 900 } 901 902 input_device_ref* devices[2]; 903 devices[0] = device->DeviceRef(); 904 devices[1] = NULL; 905 906 TRACE("removing path: %s, name: %s\n", path, devices[0]->name); 907 908 UnregisterDevices(devices); 909 910 fDevices.RemoveItem(device); 911 912 return B_OK; 913 } 914