xref: /haiku/src/add-ons/input_server/devices/keyboard/KeyboardInputDevice.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
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