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