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