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 if (fFD < 0) { 198 // let the control thread handle any error on opening the device 199 fFD = errno; 200 } 201 202 char threadName[B_OS_NAME_LENGTH]; 203 snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", fDeviceRef.name); 204 205 fThread = spawn_thread(_ControlThreadEntry, threadName, 206 kKeyboardThreadPriority, this); 207 if (fThread < B_OK) 208 return fThread; 209 210 fActive = true; 211 resume_thread(fThread); 212 213 return fFD >= 0 ? B_OK : B_ERROR; 214 } 215 216 217 void 218 KeyboardDevice::Stop() 219 { 220 KD_CALLED(); 221 TRACE("name: %s\n", fDeviceRef.name); 222 223 fActive = false; 224 225 close(fFD); 226 fFD = -1; 227 228 if (fThread >= 0) { 229 suspend_thread(fThread); 230 resume_thread(fThread); 231 status_t dummy; 232 wait_for_thread(fThread, &dummy); 233 } 234 } 235 236 237 status_t 238 KeyboardDevice::UpdateSettings(uint32 opcode) 239 { 240 KD_CALLED(); 241 242 if (fThread < 0) 243 return B_ERROR; 244 245 // schedule updating the settings from within the control thread 246 fSettingsCommand = opcode; 247 fUpdateSettings = true; 248 249 return B_OK; 250 } 251 252 253 // #pragma mark - control thread 254 255 256 /*static*/ int32 257 KeyboardDevice::_ControlThreadEntry(void* arg) 258 { 259 KeyboardDevice* device = (KeyboardDevice*)arg; 260 return device->_ControlThread(); 261 } 262 263 264 int32 265 KeyboardDevice::_ControlThread() 266 { 267 KD_CALLED(); 268 TRACE("fPath: %s\n", fPath); 269 270 if (fFD < B_OK) { 271 LOG_ERR("KeyboardDevice: error when opening %s: %s\n", 272 fPath, strerror(fFD)); 273 _ControlThreadCleanup(); 274 // TOAST! 275 return B_ERROR; 276 } 277 278 _UpdateSettings(0); 279 280 raw_key_info keyInfo; 281 uint8 activeDeadKey = 0; 282 uint32 lastKeyCode = 0; 283 uint32 repeatCount = 1; 284 uint8 states[16]; 285 bool ctrlAltDelPressed = false; 286 287 memset(states, 0, sizeof(states)); 288 289 if (fKeyboardID == 0) { 290 if (ioctl(fFD, KB_GET_KEYBOARD_ID, &fKeyboardID, 291 sizeof(fKeyboardID)) == 0) { 292 BMessage message(IS_SET_KEYBOARD_ID); 293 message.AddInt16("id", fKeyboardID); 294 be_app->PostMessage(&message); 295 } 296 } 297 298 while (fActive) { 299 status_t status = ioctl(fFD, KB_READ, &keyInfo, sizeof(keyInfo)); 300 if (status == B_BUSY) { 301 // probably the debugger is listening to key events, wait and try 302 // again 303 snooze(100000); 304 continue; 305 } else if (status != B_OK) { 306 _ControlThreadCleanup(); 307 // TOAST! 308 return 0; 309 } 310 311 // Update the settings from this thread if necessary 312 if (fUpdateSettings) { 313 _UpdateSettings(fSettingsCommand); 314 fUpdateSettings = false; 315 } 316 317 uint32 keycode = keyInfo.keycode; 318 bool isKeyDown = keyInfo.is_keydown; 319 320 LOG_EVENT("KB_READ: %" B_PRIdBIGTIME ", %02x, %02" B_PRIx32 "\n", 321 keyInfo.timestamp, isKeyDown, keycode); 322 323 if (keycode == 0) 324 continue; 325 326 if (isKeyDown && keycode == 0x68) { 327 // MENU KEY for Tracker 328 bool noOtherKeyPressed = true; 329 for (int32 i = 0; i < 16; i++) { 330 if (states[i] != 0) { 331 noOtherKeyPressed = false; 332 break; 333 } 334 } 335 336 if (noOtherKeyPressed) { 337 BMessenger deskbar("application/x-vnd.Be-TSKB"); 338 if (deskbar.IsValid()) 339 deskbar.SendMessage('BeMn'); 340 } 341 } 342 343 if (keycode < 256) { 344 if (isKeyDown) 345 states[(keycode) >> 3] |= (1 << (7 - (keycode & 0x7))); 346 else 347 states[(keycode) >> 3] &= (~(1 << (7 - (keycode & 0x7)))); 348 } 349 350 if (isKeyDown && keycode == 0x34 // DELETE KEY 351 && (states[fCommandKey >> 3] & (1 << (7 - (fCommandKey & 0x7)))) 352 && (states[fControlKey >> 3] & (1 << (7 - (fControlKey & 0x7))))) { 353 LOG_EVENT("TeamMonitor called\n"); 354 355 // show the team monitor 356 if (fOwner->fTeamMonitorWindow == NULL) 357 fOwner->fTeamMonitorWindow = new(std::nothrow) TeamMonitorWindow(); 358 359 if (fOwner->fTeamMonitorWindow != NULL) 360 fOwner->fTeamMonitorWindow->Enable(); 361 362 ctrlAltDelPressed = true; 363 } 364 365 if (ctrlAltDelPressed) { 366 if (fOwner->fTeamMonitorWindow != NULL) { 367 BMessage message(kMsgCtrlAltDelPressed); 368 message.AddBool("key down", isKeyDown); 369 fOwner->fTeamMonitorWindow->PostMessage(&message); 370 } 371 372 if (!isKeyDown) 373 ctrlAltDelPressed = false; 374 } 375 376 BAutolock lock(fKeymapLock); 377 378 uint32 modifiers = fKeymap.Modifier(keycode); 379 bool isLock 380 = (modifiers & (B_CAPS_LOCK | B_NUM_LOCK | B_SCROLL_LOCK)) != 0; 381 if (modifiers != 0 && (!isLock || isKeyDown)) { 382 uint32 oldModifiers = fModifiers; 383 384 if ((isKeyDown && !isLock) 385 || (isKeyDown && !(fModifiers & modifiers))) 386 fModifiers |= modifiers; 387 else { 388 fModifiers &= ~modifiers; 389 390 // ensure that we don't clear a combined B_*_KEY when still 391 // one of the individual B_{LEFT|RIGHT}_*_KEY is pressed 392 if (fModifiers & (B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY)) 393 fModifiers |= B_SHIFT_KEY; 394 if (fModifiers & (B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY)) 395 fModifiers |= B_COMMAND_KEY; 396 if (fModifiers & (B_LEFT_CONTROL_KEY | B_RIGHT_CONTROL_KEY)) 397 fModifiers |= B_CONTROL_KEY; 398 if (fModifiers & (B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY)) 399 fModifiers |= B_OPTION_KEY; 400 } 401 402 if (fModifiers != oldModifiers) { 403 BMessage* message = new BMessage(B_MODIFIERS_CHANGED); 404 if (message == NULL) 405 continue; 406 407 message->AddInt64("when", keyInfo.timestamp); 408 message->AddInt32("be:old_modifiers", oldModifiers); 409 message->AddInt32("modifiers", fModifiers); 410 message->AddData("states", B_UINT8_TYPE, states, 16); 411 412 if (fOwner->EnqueueMessage(message) != B_OK) 413 delete message; 414 415 if (isLock) 416 _UpdateLEDs(); 417 } 418 } 419 420 uint8 newDeadKey = 0; 421 if (activeDeadKey == 0 || !isKeyDown) 422 newDeadKey = fKeymap.ActiveDeadKey(keycode, fModifiers); 423 424 char* string = NULL; 425 char* rawString = NULL; 426 int32 numBytes = 0, rawNumBytes = 0; 427 428 ArrayDeleter<char> stringDeleter; 429 if (newDeadKey == 0) { 430 fKeymap.GetChars(keycode, fModifiers, activeDeadKey, &string, 431 &numBytes); 432 stringDeleter.SetTo(string); 433 } 434 435 fKeymap.GetChars(keycode, 0, 0, &rawString, &rawNumBytes); 436 ArrayDeleter<char> rawStringDeleter(rawString); 437 438 BMessage* msg = new BMessage; 439 if (msg == NULL) 440 continue; 441 442 if (numBytes > 0) 443 msg->what = isKeyDown ? B_KEY_DOWN : B_KEY_UP; 444 else 445 msg->what = isKeyDown ? B_UNMAPPED_KEY_DOWN : B_UNMAPPED_KEY_UP; 446 447 msg->AddInt64("when", keyInfo.timestamp); 448 msg->AddInt32("key", keycode); 449 msg->AddInt32("modifiers", fModifiers); 450 msg->AddData("states", B_UINT8_TYPE, states, 16); 451 if (numBytes > 0) { 452 for (int i = 0; i < numBytes; i++) 453 msg->AddInt8("byte", (int8)string[i]); 454 msg->AddData("bytes", B_STRING_TYPE, string, numBytes + 1); 455 456 if (rawNumBytes <= 0) { 457 rawNumBytes = 1; 458 rawString = string; 459 } 460 461 if (isKeyDown && lastKeyCode == keycode) { 462 repeatCount++; 463 msg->AddInt32("be:key_repeat", repeatCount); 464 } else 465 repeatCount = 1; 466 } 467 468 if (rawNumBytes > 0) 469 msg->AddInt32("raw_char", (uint32)((uint8)rawString[0] & 0x7f)); 470 471 if (newDeadKey == 0) { 472 if (isKeyDown && !modifiers && activeDeadKey != 0) { 473 // a dead key was completed 474 activeDeadKey = 0; 475 if (fInputMethodStarted) { 476 _EnqueueInlineInputMethod(B_INPUT_METHOD_CHANGED, 477 string, true, msg); 478 _EnqueueInlineInputMethod(B_INPUT_METHOD_STOPPED); 479 fInputMethodStarted = false; 480 msg = NULL; 481 } 482 } 483 } else if (isKeyDown 484 && _EnqueueInlineInputMethod(B_INPUT_METHOD_STARTED) == B_OK) { 485 // start of a dead key 486 char* string = NULL; 487 int32 numBytes = 0; 488 fKeymap.GetChars(keycode, fModifiers, 0, &string, &numBytes); 489 490 if (_EnqueueInlineInputMethod(B_INPUT_METHOD_CHANGED, string) 491 == B_OK) 492 fInputMethodStarted = true; 493 494 activeDeadKey = newDeadKey; 495 delete[] string; 496 } 497 498 if (msg != NULL && fOwner->EnqueueMessage(msg) != B_OK) 499 delete msg; 500 501 lastKeyCode = isKeyDown ? keycode : 0; 502 } 503 504 return 0; 505 } 506 507 508 void 509 KeyboardDevice::_ControlThreadCleanup() 510 { 511 // NOTE: Only executed when the control thread detected an error 512 // and from within the control thread! 513 514 if (fActive) { 515 fThread = -1; 516 fOwner->_RemoveDevice(fPath); 517 } else { 518 // In case active is already false, another thread 519 // waits for this thread to quit, and may already hold 520 // locks that _RemoveDevice() wants to acquire. In another 521 // words, the device is already being removed, so we simply 522 // quit here. 523 } 524 } 525 526 527 void 528 KeyboardDevice::_UpdateSettings(uint32 opcode) 529 { 530 KD_CALLED(); 531 532 if (opcode == 0 || opcode == B_KEY_REPEAT_RATE_CHANGED) { 533 if (get_key_repeat_rate(&fSettings.key_repeat_rate) != B_OK) { 534 LOG_ERR("error when get_key_repeat_rate\n"); 535 } else if (ioctl(fFD, KB_SET_KEY_REPEAT_RATE, 536 &fSettings.key_repeat_rate, sizeof(int32)) != B_OK) { 537 LOG_ERR("error when KB_SET_KEY_REPEAT_RATE, fd:%d\n", fFD); 538 } 539 } 540 541 if (opcode == 0 || opcode == B_KEY_REPEAT_DELAY_CHANGED) { 542 if (get_key_repeat_delay(&fSettings.key_repeat_delay) != B_OK) { 543 LOG_ERR("error when get_key_repeat_delay\n"); 544 } else if (ioctl(fFD, KB_SET_KEY_REPEAT_DELAY, 545 &fSettings.key_repeat_delay, sizeof(bigtime_t)) != B_OK) { 546 LOG_ERR("error when KB_SET_KEY_REPEAT_DELAY, fd:%d\n", fFD); 547 } 548 } 549 550 if (opcode == 0 || opcode == B_KEY_MAP_CHANGED 551 || opcode == B_KEY_LOCKS_CHANGED) { 552 BAutolock lock(fKeymapLock); 553 fKeymap.RetrieveCurrent(); 554 fModifiers = fKeymap.Map().lock_settings; 555 _UpdateLEDs(); 556 fControlKey = fKeymap.KeyForModifier(B_LEFT_CONTROL_KEY); 557 fCommandKey = fKeymap.KeyForModifier(B_LEFT_COMMAND_KEY); 558 } 559 } 560 561 562 void 563 KeyboardDevice::_UpdateLEDs() 564 { 565 if (fFD < 0) 566 return; 567 568 char lockIO[3] = {0, 0, 0}; 569 570 if ((fModifiers & B_NUM_LOCK) != 0) 571 lockIO[0] = 1; 572 if ((fModifiers & B_CAPS_LOCK) != 0) 573 lockIO[1] = 1; 574 if ((fModifiers & B_SCROLL_LOCK) != 0) 575 lockIO[2] = 1; 576 577 ioctl(fFD, KB_SET_LEDS, &lockIO, sizeof(lockIO)); 578 } 579 580 581 status_t 582 KeyboardDevice::_EnqueueInlineInputMethod(int32 opcode, 583 const char* string, bool confirmed, BMessage* keyDown) 584 { 585 BMessage* message = new BMessage(B_INPUT_METHOD_EVENT); 586 if (message == NULL) 587 return B_NO_MEMORY; 588 589 message->AddInt32("be:opcode", opcode); 590 message->AddBool("be:inline_only", true); 591 592 if (string != NULL) 593 message->AddString("be:string", string); 594 if (confirmed) 595 message->AddBool("be:confirmed", true); 596 if (keyDown) 597 message->AddMessage("be:translated", keyDown); 598 if (opcode == B_INPUT_METHOD_STARTED) 599 message->AddMessenger("be:reply_to", this); 600 601 status_t status = fOwner->EnqueueMessage(message); 602 if (status != B_OK) 603 delete message; 604 605 return status; 606 } 607 608 609 // #pragma mark - 610 611 612 KeyboardInputDevice::KeyboardInputDevice() 613 : 614 fDevices(2, true), 615 fDeviceListLock("KeyboardInputDevice list"), 616 fTeamMonitorWindow(NULL) 617 { 618 KID_CALLED(); 619 620 StartMonitoringDevice(kKeyboardDevicesDirectory); 621 _RecursiveScan(kKeyboardDevicesDirectory); 622 } 623 624 625 KeyboardInputDevice::~KeyboardInputDevice() 626 { 627 KID_CALLED(); 628 629 if (fTeamMonitorWindow) { 630 fTeamMonitorWindow->PostMessage(B_QUIT_REQUESTED); 631 fTeamMonitorWindow = NULL; 632 } 633 634 StopMonitoringDevice(kKeyboardDevicesDirectory); 635 fDevices.MakeEmpty(); 636 } 637 638 639 status_t 640 KeyboardInputDevice::SystemShuttingDown() 641 { 642 KID_CALLED(); 643 if (fTeamMonitorWindow) 644 fTeamMonitorWindow->PostMessage(SYSTEM_SHUTTING_DOWN); 645 646 return B_OK; 647 } 648 649 650 status_t 651 KeyboardInputDevice::InitCheck() 652 { 653 KID_CALLED(); 654 return BInputServerDevice::InitCheck(); 655 } 656 657 658 status_t 659 KeyboardInputDevice::Start(const char* name, void* cookie) 660 { 661 KID_CALLED(); 662 TRACE("name %s\n", name); 663 664 KeyboardDevice* device = (KeyboardDevice*)cookie; 665 666 return device->Start(); 667 } 668 669 670 status_t 671 KeyboardInputDevice::Stop(const char* name, void* cookie) 672 { 673 KID_CALLED(); 674 TRACE("name %s\n", name); 675 676 KeyboardDevice* device = (KeyboardDevice*)cookie; 677 678 device->Stop(); 679 return B_OK; 680 } 681 682 683 status_t 684 KeyboardInputDevice::Control(const char* name, void* cookie, 685 uint32 command, BMessage* message) 686 { 687 KID_CALLED(); 688 TRACE("KeyboardInputDevice::Control(%s, code: %" B_PRIu32 ")\n", name, 689 command); 690 691 if (command == B_NODE_MONITOR) 692 _HandleMonitor(message); 693 else if (command >= B_KEY_MAP_CHANGED 694 && command <= B_KEY_REPEAT_RATE_CHANGED) { 695 KeyboardDevice* device = (KeyboardDevice*)cookie; 696 device->UpdateSettings(command); 697 } 698 return B_OK; 699 } 700 701 702 status_t 703 KeyboardInputDevice::_HandleMonitor(BMessage* message) 704 { 705 KID_CALLED(); 706 707 const char* path; 708 int32 opcode; 709 if (message->FindInt32("opcode", &opcode) != B_OK 710 || (opcode != B_ENTRY_CREATED && opcode != B_ENTRY_REMOVED) 711 || message->FindString("path", &path) != B_OK) 712 return B_BAD_VALUE; 713 714 if (opcode == B_ENTRY_CREATED) 715 return _AddDevice(path); 716 717 #if 0 718 return _RemoveDevice(path); 719 #else 720 // Don't handle B_ENTRY_REMOVED, let the control thread take care of it. 721 return B_OK; 722 #endif 723 } 724 725 726 KeyboardDevice* 727 KeyboardInputDevice::_FindDevice(const char* path) const 728 { 729 for (int i = fDevices.CountItems() - 1; i >= 0; i--) { 730 KeyboardDevice* device = fDevices.ItemAt(i); 731 if (strcmp(device->Path(), path) == 0) 732 return device; 733 } 734 735 return NULL; 736 } 737 738 739 status_t 740 KeyboardInputDevice::_AddDevice(const char* path) 741 { 742 KID_CALLED(); 743 TRACE("path: %s\n", path); 744 745 BAutolock _(fDeviceListLock); 746 747 _RemoveDevice(path); 748 749 KeyboardDevice* device = new(std::nothrow) KeyboardDevice(this, path); 750 if (device == NULL) 751 return B_NO_MEMORY; 752 753 input_device_ref* devices[2]; 754 devices[0] = device->DeviceRef(); 755 devices[1] = NULL; 756 757 fDevices.AddItem(device); 758 759 return RegisterDevices(devices); 760 } 761 762 763 status_t 764 KeyboardInputDevice::_RemoveDevice(const char* path) 765 { 766 BAutolock _(fDeviceListLock); 767 768 KeyboardDevice* device = _FindDevice(path); 769 if (device == NULL) 770 return B_ENTRY_NOT_FOUND; 771 772 KID_CALLED(); 773 TRACE("path: %s\n", path); 774 775 input_device_ref* devices[2]; 776 devices[0] = device->DeviceRef(); 777 devices[1] = NULL; 778 779 UnregisterDevices(devices); 780 781 fDevices.RemoveItem(device); 782 783 return B_OK; 784 } 785 786 787 void 788 KeyboardInputDevice::_RecursiveScan(const char* directory) 789 { 790 KID_CALLED(); 791 TRACE("directory: %s\n", directory); 792 793 BEntry entry; 794 BDirectory dir(directory); 795 while (dir.GetNextEntry(&entry) == B_OK) { 796 BPath path; 797 entry.GetPath(&path); 798 if (entry.IsDirectory()) 799 _RecursiveScan(path.Path()); 800 else 801 _AddDevice(path.Path()); 802 } 803 } 804