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