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:
FunctionTracer(const void * pointer,const char * className,const char * functionName)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
~FunctionTracer()54 ~FunctionTracer()
55 {
56 debug_printf("%p -> %s}\n", fPointer, fPrepend.String());
57 sFunctionDepth--;
58 }
59
Depth()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*
instantiate_input_device()93 instantiate_input_device()
94 {
95 return new(std::nothrow) KeyboardInputDevice();
96 }
97
98
99 static char*
get_short_name(const char * longName)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
KeyboardDevice(KeyboardInputDevice * owner,const char * path)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
~KeyboardDevice()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
MessageReceived(BMessage * message)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
Start()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
Stop()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
UpdateSettings(uint32 opcode)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
_ControlThreadEntry(void * arg)257 KeyboardDevice::_ControlThreadEntry(void* arg)
258 {
259 KeyboardDevice* device = (KeyboardDevice*)arg;
260 return device->_ControlThread();
261 }
262
263
264 int32
_ControlThread()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
_ControlThreadCleanup()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
_UpdateSettings(uint32 opcode)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
_UpdateLEDs()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
_EnqueueInlineInputMethod(int32 opcode,const char * string,bool confirmed,BMessage * keyDown)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
KeyboardInputDevice()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
~KeyboardInputDevice()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
SystemShuttingDown()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
InitCheck()651 KeyboardInputDevice::InitCheck()
652 {
653 KID_CALLED();
654 return BInputServerDevice::InitCheck();
655 }
656
657
658 status_t
Start(const char * name,void * cookie)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
Stop(const char * name,void * cookie)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
Control(const char * name,void * cookie,uint32 command,BMessage * message)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
_HandleMonitor(BMessage * message)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*
_FindDevice(const char * path) const727 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
_AddDevice(const char * path)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
_RemoveDevice(const char * path)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
_RecursiveScan(const char * directory)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