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