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