1 /* 2 * Copyright 2004-2006, Jérôme Duval. All rights reserved. 3 * Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de. 4 * Copyright 2008-2009, Stephan Aßmus, superstippi@gmx.de. 5 * 6 * Distributed under the terms of the MIT License. 7 */ 8 9 10 #include "KeyboardInputDevice.h" 11 12 #include <errno.h> 13 #include <new> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <unistd.h> 17 18 #include <Application.h> 19 #include <AutoDeleter.h> 20 #include <Autolock.h> 21 #include <Directory.h> 22 #include <Entry.h> 23 #include <NodeMonitor.h> 24 #include <Path.h> 25 #include <String.h> 26 27 #include <keyboard_mouse_driver.h> 28 29 30 #undef TRACE 31 32 //#define TRACE_KEYBOARD_DEVICE 33 #ifdef TRACE_KEYBOARD_DEVICE 34 35 static int32 sFunctionDepth = -1; 36 37 class FunctionTracer { 38 public: 39 FunctionTracer(const void* pointer, const char* className, 40 const char* functionName) 41 : 42 fFunctionName(), 43 fPrepend(), 44 fPointer(pointer) 45 { 46 sFunctionDepth++; 47 fPrepend.Append(' ', sFunctionDepth * 2); 48 fFunctionName << className << "::" << functionName << "()"; 49 50 debug_printf("%p -> %s%s {\n", fPointer, fPrepend.String(), 51 fFunctionName.String()); 52 } 53 54 ~FunctionTracer() 55 { 56 debug_printf("%p -> %s}\n", fPointer, fPrepend.String()); 57 sFunctionDepth--; 58 } 59 60 static int32 Depth() { return sFunctionDepth; } 61 62 private: 63 BString fFunctionName; 64 BString fPrepend; 65 const void* fPointer; 66 }; 67 68 # define KD_CALLED(x...) \ 69 FunctionTracer _ft(this, "KeyboardDevice", __FUNCTION__) 70 # define KID_CALLED(x...) \ 71 FunctionTracer _ft(this, "KeyboardInputDevice", __FUNCTION__) 72 # define TRACE(x...) \ 73 do { BString _to; \ 74 _to.Append(' ', (FunctionTracer::Depth() + 1) * 2); \ 75 debug_printf("%p -> %s", this, _to.String()); \ 76 debug_printf(x); } while (0) 77 # define LOG_EVENT(text...) debug_printf(text) 78 # define LOG_ERR(text...) TRACE(text) 79 #else 80 # define TRACE(x...) do {} while (0) 81 # define KD_CALLED(x...) TRACE(x) 82 # define KID_CALLED(x...) TRACE(x) 83 # define LOG_ERR(text...) debug_printf(text) 84 # define LOG_EVENT(text...) TRACE(x) 85 #endif 86 87 88 const static uint32 kKeyboardThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4; 89 const static char* kKeyboardDevicesDirectory = "/dev/input/keyboard"; 90 91 92 extern "C" BInputServerDevice* 93 instantiate_input_device() 94 { 95 return new(std::nothrow) KeyboardInputDevice(); 96 } 97 98 99 static char* 100 get_short_name(const char* longName) 101 { 102 BString string(longName); 103 BString name; 104 105 int32 slash = string.FindLast("/"); 106 string.CopyInto(name, slash + 1, string.Length() - slash); 107 int32 index = atoi(name.String()) + 1; 108 109 int32 previousSlash = string.FindLast("/", slash); 110 string.CopyInto(name, previousSlash + 1, slash - previousSlash - 1); 111 112 // some special handling so that we get "USB" and "AT" instead of "usb"/"at" 113 if (name.Length() < 4) 114 name.ToUpper(); 115 else 116 name.Capitalize(); 117 118 name << " Keyboard " << index; 119 120 return strdup(name.String()); 121 } 122 123 124 // #pragma mark - 125 126 127 KeyboardDevice::KeyboardDevice(KeyboardInputDevice* owner, const char* path) 128 : 129 BHandler("keyboard device"), 130 fOwner(owner), 131 fFD(-1), 132 fThread(-1), 133 fActive(false), 134 fInputMethodStarted(false), 135 fKeyboardID(0), 136 fUpdateSettings(false), 137 fSettingsCommand(0), 138 fKeymapLock("keymap lock") 139 { 140 KD_CALLED(); 141 142 strlcpy(fPath, path, B_PATH_NAME_LENGTH); 143 fDeviceRef.name = get_short_name(path); 144 fDeviceRef.type = B_KEYBOARD_DEVICE; 145 fDeviceRef.cookie = this; 146 147 if (be_app->Lock()) { 148 be_app->AddHandler(this); 149 be_app->Unlock(); 150 } 151 } 152 153 154 KeyboardDevice::~KeyboardDevice() 155 { 156 KD_CALLED(); 157 TRACE("delete\n"); 158 159 if (fActive) 160 Stop(); 161 162 free(fDeviceRef.name); 163 164 if (be_app->Lock()) { 165 be_app->RemoveHandler(this); 166 be_app->Unlock(); 167 } 168 } 169 170 171 void 172 KeyboardDevice::MessageReceived(BMessage* message) 173 { 174 KD_CALLED(); 175 176 if (message->what != B_INPUT_METHOD_EVENT) { 177 BHandler::MessageReceived(message); 178 return; 179 } 180 181 int32 opcode; 182 if (message->FindInt32("be:opcode", &opcode) != B_OK) 183 return; 184 185 if (opcode == B_INPUT_METHOD_STOPPED) 186 fInputMethodStarted = false; 187 } 188 189 190 status_t 191 KeyboardDevice::Start() 192 { 193 KD_CALLED(); 194 TRACE("name: %s\n", fDeviceRef.name); 195 196 fFD = open(fPath, O_RDWR); 197 // let the control thread handle any error on opening the device 198 199 char threadName[B_OS_NAME_LENGTH]; 200 snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", fDeviceRef.name); 201 202 fThread = spawn_thread(_ControlThreadEntry, threadName, 203 kKeyboardThreadPriority, this); 204 if (fThread < B_OK) 205 return fThread; 206 207 fActive = true; 208 resume_thread(fThread); 209 210 return fFD >= 0 ? B_OK : B_ERROR; 211 } 212 213 214 void 215 KeyboardDevice::Stop() 216 { 217 KD_CALLED(); 218 TRACE("name: %s\n", fDeviceRef.name); 219 220 fActive = false; 221 222 close(fFD); 223 fFD = -1; 224 225 if (fThread >= 0) { 226 suspend_thread(fThread); 227 resume_thread(fThread); 228 status_t dummy; 229 wait_for_thread(fThread, &dummy); 230 } 231 } 232 233 234 status_t 235 KeyboardDevice::UpdateSettings(uint32 opcode) 236 { 237 KD_CALLED(); 238 239 if (fThread < 0) 240 return B_ERROR; 241 242 // schedule updating the settings from within the control thread 243 fSettingsCommand = opcode; 244 fUpdateSettings = true; 245 246 return B_OK; 247 } 248 249 250 // #pragma mark - control thread 251 252 253 /*static*/ int32 254 KeyboardDevice::_ControlThreadEntry(void* arg) 255 { 256 KeyboardDevice* device = (KeyboardDevice*)arg; 257 return device->_ControlThread(); 258 } 259 260 261 int32 262 KeyboardDevice::_ControlThread() 263 { 264 KD_CALLED(); 265 TRACE("fPath: %s\n", fPath); 266 267 if (fFD < B_OK) { 268 LOG_ERR("KeyboardDevice: error when opening %s: %s\n", 269 fPath, strerror(errno)); 270 _ControlThreadCleanup(); 271 // TOAST! 272 return B_ERROR; 273 } 274 275 _UpdateSettings(0); 276 277 raw_key_info keyInfo; 278 uint8 activeDeadKey = 0; 279 uint32 lastKeyCode = 0; 280 uint32 repeatCount = 1; 281 uint8 states[16]; 282 bool ctrlAltDelPressed = false; 283 284 memset(states, 0, sizeof(states)); 285 286 if (fKeyboardID == 0) { 287 if (ioctl(fFD, KB_GET_KEYBOARD_ID, &fKeyboardID, 288 sizeof(fKeyboardID)) == 0) { 289 BMessage message(IS_SET_KEYBOARD_ID); 290 message.AddInt16("id", fKeyboardID); 291 be_app->PostMessage(&message); 292 } 293 } 294 295 while (fActive) { 296 status_t status = ioctl(fFD, KB_READ, &keyInfo, sizeof(keyInfo)); 297 if (status == B_BUSY) { 298 // probably the debugger is listening to key events, wait and try 299 // again 300 snooze(100000); 301 continue; 302 } else if (status != B_OK) { 303 _ControlThreadCleanup(); 304 // TOAST! 305 return 0; 306 } 307 308 // Update the settings from this thread if necessary 309 if (fUpdateSettings) { 310 _UpdateSettings(fSettingsCommand); 311 fUpdateSettings = false; 312 } 313 314 uint32 keycode = keyInfo.keycode; 315 bool isKeyDown = keyInfo.is_keydown; 316 317 LOG_EVENT("KB_READ: %" B_PRIdBIGTIME ", %02x, %02" B_PRIx32 "\n", 318 keyInfo.timestamp, isKeyDown, keycode); 319 320 if (keycode == 0) 321 continue; 322 323 if (isKeyDown && keycode == 0x68) { 324 // MENU KEY for Tracker 325 bool noOtherKeyPressed = true; 326 for (int32 i = 0; i < 16; i++) { 327 if (states[i] != 0) { 328 noOtherKeyPressed = false; 329 break; 330 } 331 } 332 333 if (noOtherKeyPressed) { 334 BMessenger deskbar("application/x-vnd.Be-TSKB"); 335 if (deskbar.IsValid()) 336 deskbar.SendMessage('BeMn'); 337 } 338 } 339 340 if (keycode < 256) { 341 if (isKeyDown) 342 states[(keycode) >> 3] |= (1 << (7 - (keycode & 0x7))); 343 else 344 states[(keycode) >> 3] &= (~(1 << (7 - (keycode & 0x7)))); 345 } 346 347 if (isKeyDown && keycode == 0x34 // DELETE KEY 348 && (states[fCommandKey >> 3] & (1 << (7 - (fCommandKey & 0x7)))) 349 && (states[fControlKey >> 3] & (1 << (7 - (fControlKey & 0x7))))) { 350 LOG_EVENT("TeamMonitor called\n"); 351 352 // show the team monitor 353 if (fOwner->fTeamMonitorWindow == NULL) 354 fOwner->fTeamMonitorWindow = new(std::nothrow) TeamMonitorWindow(); 355 356 if (fOwner->fTeamMonitorWindow != NULL) 357 fOwner->fTeamMonitorWindow->Enable(); 358 359 ctrlAltDelPressed = true; 360 } 361 362 if (ctrlAltDelPressed) { 363 if (fOwner->fTeamMonitorWindow != NULL) { 364 BMessage message(kMsgCtrlAltDelPressed); 365 message.AddBool("key down", isKeyDown); 366 fOwner->fTeamMonitorWindow->PostMessage(&message); 367 } 368 369 if (!isKeyDown) 370 ctrlAltDelPressed = false; 371 } 372 373 BAutolock lock(fKeymapLock); 374 375 uint32 modifiers = fKeymap.Modifier(keycode); 376 bool isLock 377 = (modifiers & (B_CAPS_LOCK | B_NUM_LOCK | B_SCROLL_LOCK)) != 0; 378 if (modifiers != 0 && (!isLock || isKeyDown)) { 379 uint32 oldModifiers = fModifiers; 380 381 if ((isKeyDown && !isLock) 382 || (isKeyDown && !(fModifiers & modifiers))) 383 fModifiers |= modifiers; 384 else { 385 fModifiers &= ~modifiers; 386 387 // ensure that we don't clear a combined B_*_KEY when still 388 // one of the individual B_{LEFT|RIGHT}_*_KEY is pressed 389 if (fModifiers & (B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY)) 390 fModifiers |= B_SHIFT_KEY; 391 if (fModifiers & (B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY)) 392 fModifiers |= B_COMMAND_KEY; 393 if (fModifiers & (B_LEFT_CONTROL_KEY | B_RIGHT_CONTROL_KEY)) 394 fModifiers |= B_CONTROL_KEY; 395 if (fModifiers & (B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY)) 396 fModifiers |= B_OPTION_KEY; 397 } 398 399 if (fModifiers != oldModifiers) { 400 BMessage* message = new BMessage(B_MODIFIERS_CHANGED); 401 if (message == NULL) 402 continue; 403 404 message->AddInt64("when", keyInfo.timestamp); 405 message->AddInt32("be:old_modifiers", oldModifiers); 406 message->AddInt32("modifiers", fModifiers); 407 message->AddData("states", B_UINT8_TYPE, states, 16); 408 409 if (fOwner->EnqueueMessage(message) != B_OK) 410 delete message; 411 412 if (isLock) 413 _UpdateLEDs(); 414 } 415 } 416 417 uint8 newDeadKey = 0; 418 if (activeDeadKey == 0 || !isKeyDown) 419 newDeadKey = fKeymap.ActiveDeadKey(keycode, fModifiers); 420 421 char* string = NULL; 422 char* rawString = NULL; 423 int32 numBytes = 0, rawNumBytes = 0; 424 425 ArrayDeleter<char> stringDeleter; 426 if (newDeadKey == 0) { 427 fKeymap.GetChars(keycode, fModifiers, activeDeadKey, &string, 428 &numBytes); 429 stringDeleter.SetTo(string); 430 } 431 432 fKeymap.GetChars(keycode, 0, 0, &rawString, &rawNumBytes); 433 ArrayDeleter<char> rawStringDeleter(rawString); 434 435 BMessage* msg = new BMessage; 436 if (msg == NULL) 437 continue; 438 439 if (numBytes > 0) 440 msg->what = isKeyDown ? B_KEY_DOWN : B_KEY_UP; 441 else 442 msg->what = isKeyDown ? B_UNMAPPED_KEY_DOWN : B_UNMAPPED_KEY_UP; 443 444 msg->AddInt64("when", keyInfo.timestamp); 445 msg->AddInt32("key", keycode); 446 msg->AddInt32("modifiers", fModifiers); 447 msg->AddData("states", B_UINT8_TYPE, states, 16); 448 if (numBytes > 0) { 449 for (int i = 0; i < numBytes; i++) 450 msg->AddInt8("byte", (int8)string[i]); 451 msg->AddData("bytes", B_STRING_TYPE, string, numBytes + 1); 452 453 if (rawNumBytes <= 0) { 454 rawNumBytes = 1; 455 rawString = string; 456 } 457 458 if (isKeyDown && lastKeyCode == keycode) { 459 repeatCount++; 460 msg->AddInt32("be:key_repeat", repeatCount); 461 } else 462 repeatCount = 1; 463 } 464 465 if (rawNumBytes > 0) 466 msg->AddInt32("raw_char", (uint32)((uint8)rawString[0] & 0x7f)); 467 468 if (newDeadKey == 0) { 469 if (isKeyDown && !modifiers && activeDeadKey != 0) { 470 // a dead key was completed 471 activeDeadKey = 0; 472 if (fInputMethodStarted) { 473 _EnqueueInlineInputMethod(B_INPUT_METHOD_CHANGED, 474 string, true, msg); 475 _EnqueueInlineInputMethod(B_INPUT_METHOD_STOPPED); 476 fInputMethodStarted = false; 477 msg = NULL; 478 } 479 } 480 } else if (isKeyDown 481 && _EnqueueInlineInputMethod(B_INPUT_METHOD_STARTED) == B_OK) { 482 // start of a dead key 483 char* string = NULL; 484 int32 numBytes = 0; 485 fKeymap.GetChars(keycode, fModifiers, 0, &string, &numBytes); 486 487 if (_EnqueueInlineInputMethod(B_INPUT_METHOD_CHANGED, string) 488 == B_OK) 489 fInputMethodStarted = true; 490 491 activeDeadKey = newDeadKey; 492 delete[] string; 493 } 494 495 if (msg != NULL && fOwner->EnqueueMessage(msg) != B_OK) 496 delete msg; 497 498 lastKeyCode = isKeyDown ? keycode : 0; 499 } 500 501 return 0; 502 } 503 504 505 void 506 KeyboardDevice::_ControlThreadCleanup() 507 { 508 // NOTE: Only executed when the control thread detected an error 509 // and from within the control thread! 510 511 if (fActive) { 512 fThread = -1; 513 fOwner->_RemoveDevice(fPath); 514 } else { 515 // In case active is already false, another thread 516 // waits for this thread to quit, and may already hold 517 // locks that _RemoveDevice() wants to acquire. In another 518 // words, the device is already being removed, so we simply 519 // quit here. 520 } 521 } 522 523 524 void 525 KeyboardDevice::_UpdateSettings(uint32 opcode) 526 { 527 KD_CALLED(); 528 529 if (opcode == 0 || opcode == B_KEY_REPEAT_RATE_CHANGED) { 530 if (get_key_repeat_rate(&fSettings.key_repeat_rate) != B_OK) { 531 LOG_ERR("error when get_key_repeat_rate\n"); 532 } else if (ioctl(fFD, KB_SET_KEY_REPEAT_RATE, 533 &fSettings.key_repeat_rate, sizeof(int32)) != B_OK) { 534 LOG_ERR("error when KB_SET_KEY_REPEAT_RATE, fd:%d\n", fFD); 535 } 536 } 537 538 if (opcode == 0 || opcode == B_KEY_REPEAT_DELAY_CHANGED) { 539 if (get_key_repeat_delay(&fSettings.key_repeat_delay) != B_OK) { 540 LOG_ERR("error when get_key_repeat_delay\n"); 541 } else if (ioctl(fFD, KB_SET_KEY_REPEAT_DELAY, 542 &fSettings.key_repeat_delay, sizeof(bigtime_t)) != B_OK) { 543 LOG_ERR("error when KB_SET_KEY_REPEAT_DELAY, fd:%d\n", fFD); 544 } 545 } 546 547 if (opcode == 0 || opcode == B_KEY_MAP_CHANGED 548 || opcode == B_KEY_LOCKS_CHANGED) { 549 BAutolock lock(fKeymapLock); 550 fKeymap.RetrieveCurrent(); 551 fModifiers = fKeymap.Map().lock_settings; 552 _UpdateLEDs(); 553 fControlKey = fKeymap.KeyForModifier(B_LEFT_CONTROL_KEY); 554 fCommandKey = fKeymap.KeyForModifier(B_LEFT_COMMAND_KEY); 555 } 556 } 557 558 559 void 560 KeyboardDevice::_UpdateLEDs() 561 { 562 if (fFD < 0) 563 return; 564 565 char lockIO[3] = {0, 0, 0}; 566 567 if ((fModifiers & B_NUM_LOCK) != 0) 568 lockIO[0] = 1; 569 if ((fModifiers & B_CAPS_LOCK) != 0) 570 lockIO[1] = 1; 571 if ((fModifiers & B_SCROLL_LOCK) != 0) 572 lockIO[2] = 1; 573 574 ioctl(fFD, KB_SET_LEDS, &lockIO, sizeof(lockIO)); 575 } 576 577 578 status_t 579 KeyboardDevice::_EnqueueInlineInputMethod(int32 opcode, 580 const char* string, bool confirmed, BMessage* keyDown) 581 { 582 BMessage* message = new BMessage(B_INPUT_METHOD_EVENT); 583 if (message == NULL) 584 return B_NO_MEMORY; 585 586 message->AddInt32("be:opcode", opcode); 587 message->AddBool("be:inline_only", true); 588 589 if (string != NULL) 590 message->AddString("be:string", string); 591 if (confirmed) 592 message->AddBool("be:confirmed", true); 593 if (keyDown) 594 message->AddMessage("be:translated", keyDown); 595 if (opcode == B_INPUT_METHOD_STARTED) 596 message->AddMessenger("be:reply_to", this); 597 598 status_t status = fOwner->EnqueueMessage(message); 599 if (status != B_OK) 600 delete message; 601 602 return status; 603 } 604 605 606 // #pragma mark - 607 608 609 KeyboardInputDevice::KeyboardInputDevice() 610 : 611 fDevices(2, true), 612 fDeviceListLock("KeyboardInputDevice list"), 613 fTeamMonitorWindow(NULL) 614 { 615 KID_CALLED(); 616 617 StartMonitoringDevice(kKeyboardDevicesDirectory); 618 _RecursiveScan(kKeyboardDevicesDirectory); 619 } 620 621 622 KeyboardInputDevice::~KeyboardInputDevice() 623 { 624 KID_CALLED(); 625 626 if (fTeamMonitorWindow) { 627 fTeamMonitorWindow->PostMessage(B_QUIT_REQUESTED); 628 fTeamMonitorWindow = NULL; 629 } 630 631 StopMonitoringDevice(kKeyboardDevicesDirectory); 632 fDevices.MakeEmpty(); 633 } 634 635 636 status_t 637 KeyboardInputDevice::SystemShuttingDown() 638 { 639 KID_CALLED(); 640 if (fTeamMonitorWindow) 641 fTeamMonitorWindow->PostMessage(SYSTEM_SHUTTING_DOWN); 642 643 return B_OK; 644 } 645 646 647 status_t 648 KeyboardInputDevice::InitCheck() 649 { 650 KID_CALLED(); 651 return BInputServerDevice::InitCheck(); 652 } 653 654 655 status_t 656 KeyboardInputDevice::Start(const char* name, void* cookie) 657 { 658 KID_CALLED(); 659 TRACE("name %s\n", name); 660 661 KeyboardDevice* device = (KeyboardDevice*)cookie; 662 663 return device->Start(); 664 } 665 666 667 status_t 668 KeyboardInputDevice::Stop(const char* name, void* cookie) 669 { 670 KID_CALLED(); 671 TRACE("name %s\n", name); 672 673 KeyboardDevice* device = (KeyboardDevice*)cookie; 674 675 device->Stop(); 676 return B_OK; 677 } 678 679 680 status_t 681 KeyboardInputDevice::Control(const char* name, void* cookie, 682 uint32 command, BMessage* message) 683 { 684 KID_CALLED(); 685 TRACE("KeyboardInputDevice::Control(%s, code: %" B_PRIu32 ")\n", name, 686 command); 687 688 if (command == B_NODE_MONITOR) 689 _HandleMonitor(message); 690 else if (command >= B_KEY_MAP_CHANGED 691 && command <= B_KEY_REPEAT_RATE_CHANGED) { 692 KeyboardDevice* device = (KeyboardDevice*)cookie; 693 device->UpdateSettings(command); 694 } 695 return B_OK; 696 } 697 698 699 status_t 700 KeyboardInputDevice::_HandleMonitor(BMessage* message) 701 { 702 KID_CALLED(); 703 704 const char* path; 705 int32 opcode; 706 if (message->FindInt32("opcode", &opcode) != B_OK 707 || (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED) 708 || message->FindString("path", &path) != B_OK) 709 return B_BAD_VALUE; 710 711 if (opcode == B_ENTRY_CREATED) 712 return _AddDevice(path); 713 714 #if 0 715 return _RemoveDevice(path); 716 #else 717 // Don't handle B_ENTRY_REMOVED, let the control thread take care of it. 718 return B_OK; 719 #endif 720 } 721 722 723 KeyboardDevice* 724 KeyboardInputDevice::_FindDevice(const char* path) const 725 { 726 for (int i = fDevices.CountItems() - 1; i >= 0; i--) { 727 KeyboardDevice* device = fDevices.ItemAt(i); 728 if (strcmp(device->Path(), path) == 0) 729 return device; 730 } 731 732 return NULL; 733 } 734 735 736 status_t 737 KeyboardInputDevice::_AddDevice(const char* path) 738 { 739 KID_CALLED(); 740 TRACE("path: %s\n", path); 741 742 BAutolock _(fDeviceListLock); 743 744 _RemoveDevice(path); 745 746 KeyboardDevice* device = new(std::nothrow) KeyboardDevice(this, path); 747 if (device == NULL) 748 return B_NO_MEMORY; 749 750 input_device_ref* devices[2]; 751 devices[0] = device->DeviceRef(); 752 devices[1] = NULL; 753 754 fDevices.AddItem(device); 755 756 return RegisterDevices(devices); 757 } 758 759 760 status_t 761 KeyboardInputDevice::_RemoveDevice(const char* path) 762 { 763 BAutolock _(fDeviceListLock); 764 765 KeyboardDevice* device = _FindDevice(path); 766 if (device == NULL) 767 return B_ENTRY_NOT_FOUND; 768 769 KID_CALLED(); 770 TRACE("path: %s\n", path); 771 772 input_device_ref* devices[2]; 773 devices[0] = device->DeviceRef(); 774 devices[1] = NULL; 775 776 UnregisterDevices(devices); 777 778 fDevices.RemoveItem(device); 779 780 return B_OK; 781 } 782 783 784 void 785 KeyboardInputDevice::_RecursiveScan(const char* directory) 786 { 787 KID_CALLED(); 788 TRACE("directory: %s\n", directory); 789 790 BEntry entry; 791 BDirectory dir(directory); 792 while (dir.GetNextEntry(&entry) == B_OK) { 793 BPath path; 794 entry.GetPath(&path); 795 if (entry.IsDirectory()) 796 _RecursiveScan(path.Path()); 797 else 798 _AddDevice(path.Path()); 799 } 800 } 801