xref: /haiku/src/servers/input/AddOnManager.cpp (revision 9563f44bfd75799f67201067483694e82b5779b9)
1 /*
2  * Copyright 2004-2009, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marcus Overhagen
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Jérôme Duval
9  */
10 
11 
12 //! Manager for input_server add-ons (devices, filters, methods)
13 
14 
15 #include "AddOnManager.h"
16 
17 #include <stdio.h>
18 #include <string.h>
19 
20 #include <Autolock.h>
21 #include <Deskbar.h>
22 #include <Directory.h>
23 #include <Entry.h>
24 #include <FindDirectory.h>
25 #include <image.h>
26 #include <Path.h>
27 #include <Roster.h>
28 #include <String.h>
29 
30 #include <PathMonitor.h>
31 
32 #include "InputServer.h"
33 #include "InputServerTypes.h"
34 #include "MethodReplicant.h"
35 
36 
37 #undef TRACE
38 //#define TRACE_ADD_ON_MONITOR
39 #ifdef TRACE_ADD_ON_MONITOR
40 #	define TRACE(x...) debug_printf(x)
41 #	define ERROR(x...) debug_printf(x)
42 #else
43 #	define TRACE(x...) ;
44 // TODO: probably better to the syslog
45 #	define ERROR(x...) debug_printf(x)
46 #endif
47 
48 
49 
50 class AddOnManager::MonitorHandler : public AddOnMonitorHandler {
51 public:
52 	MonitorHandler(AddOnManager* manager)
53 	{
54 		fManager = manager;
55 	}
56 
57 	virtual void AddOnEnabled(const add_on_entry_info* entryInfo)
58 	{
59 		CALLED();
60 		entry_ref ref;
61 		make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
62 			entryInfo->name, &ref);
63 		BEntry entry(&ref, false);
64 
65 		fManager->_RegisterAddOn(entry);
66 	}
67 
68 	virtual void AddOnDisabled(const add_on_entry_info* entryInfo)
69 	{
70 		CALLED();
71 		entry_ref ref;
72 		make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
73 			entryInfo->name, &ref);
74 		BEntry entry(&ref, false);
75 
76 		fManager->_UnregisterAddOn(entry);
77 	}
78 
79 private:
80 	AddOnManager* fManager;
81 };
82 
83 
84 //	#pragma mark -
85 
86 
87 template<class T> T*
88 instantiate_add_on(image_id image, const char* path, const char* type)
89 {
90 	T* (*instantiateFunction)();
91 
92 	BString functionName = "instantiate_input_";
93 	functionName += type;
94 
95 	if (get_image_symbol(image, functionName.String(), B_SYMBOL_TYPE_TEXT,
96 			(void**)&instantiateFunction) < B_OK) {
97 		ERROR("AddOnManager::_RegisterAddOn(): can't find %s() in \"%s\"\n",
98 			functionName.String(), path);
99 		return NULL;
100 	}
101 
102 	T* addOn = (*instantiateFunction)();
103 	if (addOn == NULL) {
104 		ERROR("AddOnManager::_RegisterAddOn(): %s() in \"%s\" returned "
105 			"NULL\n", functionName.String(), path);
106 		return NULL;
107 	}
108 
109 	status_t status = addOn->InitCheck();
110 	if (status != B_OK) {
111 		ERROR("AddOnManager::_RegisterAddOn(): InitCheck() in \"%s\" "
112 			"returned %s\n", path, strerror(status));
113 		delete addOn;
114 		return NULL;
115 	}
116 
117 	return addOn;
118 }
119 
120 
121 //	#pragma mark -
122 
123 
124 AddOnManager::AddOnManager(bool safeMode)
125 	: BLooper("add-on manager"),
126 	fSafeMode(safeMode)
127 {
128 }
129 
130 
131 AddOnManager::~AddOnManager()
132 {
133 }
134 
135 
136 void
137 AddOnManager::LoadState()
138 {
139 	_RegisterAddOns();
140 }
141 
142 
143 void
144 AddOnManager::SaveState()
145 {
146 	CALLED();
147 	_UnregisterAddOns();
148 }
149 
150 
151 // #pragma mark -
152 
153 
154 void
155 AddOnManager::_RegisterAddOns()
156 {
157 	CALLED();
158 	BAutolock locker(this);
159 	status_t err;
160 
161 	fHandler = new MonitorHandler(this);
162 	fAddOnMonitor = new AddOnMonitor(fHandler);
163 
164 	err = fAddOnMonitor->InitCheck();
165 	if (err != B_OK) {
166 		ERROR("AddOnManager::RegisterAddOns(): fAddOnMonitor->InitCheck() "
167 			"returned %s\n", strerror(err));
168 		return;
169 	}
170 
171 	const directory_which directories[] = {
172 		B_USER_ADDONS_DIRECTORY,
173 		B_COMMON_ADDONS_DIRECTORY,
174 		B_BEOS_ADDONS_DIRECTORY
175 	};
176 	const char* subDirectories[] = {
177 		"input_server/devices",
178 		"input_server/filters",
179 		"input_server/methods"
180 	};
181 	int32 subDirectoryCount = sizeof(subDirectories) / sizeof(const char*);
182 
183 	node_ref nref;
184 	BDirectory directory;
185 	BPath path;
186 	// when safemode, only B_BEOS_ADDONS_DIRECTORY is used
187 	for (uint32 i = fSafeMode ? 2 : 0;
188 			i < sizeof(directories) / sizeof(directory_which); i++) {
189 		for (int32 j = 0; j < subDirectoryCount; j++) {
190 			if (find_directory(directories[i], &path) == B_OK
191 				&& path.Append(subDirectories[j]) == B_OK
192 				&& directory.SetTo(path.Path()) == B_OK
193 				&& directory.GetNodeRef(&nref) == B_OK) {
194 				fHandler->AddDirectory(&nref);
195 			}
196 		}
197 	}
198 }
199 
200 
201 void
202 AddOnManager::_UnregisterAddOns()
203 {
204 	BAutolock locker(this);
205 
206 	BMessenger messenger(fAddOnMonitor);
207 	messenger.SendMessage(B_QUIT_REQUESTED);
208 	int32 exitValue;
209 	wait_for_thread(fAddOnMonitor->Thread(), &exitValue);
210 	delete fHandler;
211 	fHandler = NULL;
212 
213 	// We have to stop manually the add-ons because the monitor doesn't
214 	// disable them on exit
215 
216 	while (device_info* info = fDeviceList.RemoveItemAt(0)) {
217 		gInputServer->StartStopDevices(*info->add_on, false);
218 		delete info;
219 	}
220 
221 	// TODO: what about the filters/methods lists in the input_server?
222 
223 	while (filter_info* info = fFilterList.RemoveItemAt(0)) {
224 		delete info;
225 	}
226 
227 	while (method_info* info = fMethodList.RemoveItemAt(0)) {
228 		delete info;
229 	}
230 }
231 
232 
233 bool
234 AddOnManager::_IsDevice(const char* path) const
235 {
236 	return strstr(path, "input_server/devices") != 0;
237 }
238 
239 
240 bool
241 AddOnManager::_IsFilter(const char* path) const
242 {
243 	return strstr(path, "input_server/filters") != 0;
244 }
245 
246 
247 bool
248 AddOnManager::_IsMethod(const char* path) const
249 {
250 	return strstr(path, "input_server/methods") != 0;
251 }
252 
253 
254 status_t
255 AddOnManager::_RegisterAddOn(BEntry& entry)
256 {
257 	BPath path(&entry);
258 
259 	entry_ref ref;
260 	status_t status = entry.GetRef(&ref);
261 	if (status < B_OK)
262 		return status;
263 
264 	TRACE("AddOnManager::RegisterAddOn(): trying to load \"%s\"\n",
265 		path.Path());
266 
267 	image_id image = load_add_on(path.Path());
268 	if (image < B_OK) {
269 		ERROR("load addon %s failed\n", path.Path());
270 		return image;
271 	}
272 
273 	status = B_ERROR;
274 
275 	if (_IsDevice(path.Path())) {
276 		BInputServerDevice* device = instantiate_add_on<BInputServerDevice>(
277 			image, path.Path(), "device");
278 		if (device != NULL)
279 			status = _RegisterDevice(device, ref, image);
280 	} else if (_IsFilter(path.Path())) {
281 		BInputServerFilter* filter = instantiate_add_on<BInputServerFilter>(
282 			image, path.Path(), "filter");
283 		if (filter != NULL)
284 			status = _RegisterFilter(filter, ref, image);
285 	} else if (_IsMethod(path.Path())) {
286 		BInputServerMethod* method = instantiate_add_on<BInputServerMethod>(
287 			image, path.Path(), "method");
288 		if (method != NULL)
289 			status = _RegisterMethod(method, ref, image);
290 	} else {
291 		ERROR("AddOnManager::_RegisterAddOn(): addon type not found for "
292 			"\"%s\" \n", path.Path());
293 	}
294 
295 	if (status != B_OK)
296 		unload_add_on(image);
297 
298 	return status;
299 }
300 
301 
302 status_t
303 AddOnManager::_UnregisterAddOn(BEntry& entry)
304 {
305 	BPath path(&entry);
306 
307 	entry_ref ref;
308 	status_t status = entry.GetRef(&ref);
309 	if (status < B_OK)
310 		return status;
311 
312 	TRACE("AddOnManager::UnregisterAddOn(): trying to unload \"%s\"\n",
313 		path.Path());
314 
315 	BAutolock _(this);
316 
317 	if (_IsDevice(path.Path())) {
318 		for (int32 i = fDeviceList.CountItems(); i-- > 0;) {
319 			device_info* info = fDeviceList.ItemAt(i);
320 			if (!strcmp(info->ref.name, ref.name)) {
321 				gInputServer->StartStopDevices(*info->add_on, false);
322 				delete fDeviceList.RemoveItemAt(i);
323 				break;
324 			}
325 		}
326 	} else if (_IsFilter(path.Path())) {
327 		for (int32 i = fFilterList.CountItems(); i-- > 0;) {
328 			filter_info* info = fFilterList.ItemAt(i);
329 			if (!strcmp(info->ref.name, ref.name)) {
330 				BAutolock locker(InputServer::gInputFilterListLocker);
331 				InputServer::gInputFilterList.RemoveItem(info->add_on);
332 				delete fFilterList.RemoveItemAt(i);
333 				break;
334 			}
335 		}
336 	} else if (_IsMethod(path.Path())) {
337 		BInputServerMethod* method = NULL;
338 
339 		for (int32 i = fMethodList.CountItems(); i-- > 0;) {
340 			method_info* info = fMethodList.ItemAt(i);
341 			if (!strcmp(info->ref.name, ref.name)) {
342 				BAutolock locker(InputServer::gInputMethodListLocker);
343 				InputServer::gInputMethodList.RemoveItem(info->add_on);
344 				method = info->add_on;
345 					// this will only be used as a cookie, and not referenced
346 					// anymore
347 				delete fMethodList.RemoveItemAt(i);
348 				break;
349 			}
350 		}
351 
352 		if (fMethodList.CountItems() <= 0) {
353 			// we remove the method replicant
354 			BDeskbar().RemoveItem(REPLICANT_CTL_NAME);
355 			gInputServer->SetMethodReplicant(NULL);
356 		} else if (method != NULL) {
357 			BMessage msg(IS_REMOVE_METHOD);
358 			msg.AddInt32("cookie", (uint32)method);
359 			if (gInputServer->MethodReplicant())
360 				gInputServer->MethodReplicant()->SendMessage(&msg);
361 		}
362 	}
363 
364 	return B_OK;
365 }
366 
367 
368 //!	Takes over ownership of the \a device, regardless of success.
369 status_t
370 AddOnManager::_RegisterDevice(BInputServerDevice* device, const entry_ref& ref,
371 	image_id addOnImage)
372 {
373 	BAutolock locker(this);
374 
375 	for (int32 i = fDeviceList.CountItems(); i-- > 0;) {
376 		device_info* info = fDeviceList.ItemAt(i);
377 		if (!strcmp(info->ref.name, ref.name)) {
378 			// we already know this device
379 			delete device;
380 			return B_NAME_IN_USE;
381 		}
382 	}
383 
384 	TRACE("AddOnManager::RegisterDevice, name %s\n", ref.name);
385 
386 	device_info* info = new(std::nothrow) device_info;
387 	if (info == NULL) {
388 		delete device;
389 		return B_NO_MEMORY;
390 	}
391 
392 	info->ref = ref;
393 	info->add_on = device;
394 
395 	if (!fDeviceList.AddItem(info)) {
396 		delete info;
397 		return B_NO_MEMORY;
398 	}
399 
400 	info->image = addOnImage;
401 
402 	return B_OK;
403 }
404 
405 
406 //!	Takes over ownership of the \a filter, regardless of success.
407 status_t
408 AddOnManager::_RegisterFilter(BInputServerFilter* filter, const entry_ref& ref,
409 	image_id addOnImage)
410 {
411 	BAutolock _(this);
412 
413 	for (int32 i = fFilterList.CountItems(); i-- > 0;) {
414 		filter_info* info = fFilterList.ItemAt(i);
415 		if (!strcmp(info->ref.name, ref.name)) {
416 			// we already know this ref
417 			delete filter;
418 			return B_NAME_IN_USE;
419 		}
420 	}
421 
422 	TRACE("%s, name %s\n", __PRETTY_FUNCTION__, ref.name);
423 
424 	filter_info* info = new(std::nothrow) filter_info;
425 	if (info == NULL) {
426 		delete filter;
427 		return B_NO_MEMORY;
428 	}
429 
430 	info->ref = ref;
431 	info->add_on = filter;
432 
433 	if (!fFilterList.AddItem(info)) {
434 		delete info;
435 		return B_NO_MEMORY;
436 	}
437 
438 	BAutolock locker(InputServer::gInputFilterListLocker);
439 	if (!InputServer::gInputFilterList.AddItem(filter)) {
440 		fFilterList.RemoveItem(info);
441 		delete info;
442 		return B_NO_MEMORY;
443 	}
444 
445 	info->image = addOnImage;
446 
447 	return B_OK;
448 }
449 
450 
451 //!	Takes over ownership of the \a method, regardless of success.
452 status_t
453 AddOnManager::_RegisterMethod(BInputServerMethod* method, const entry_ref& ref,
454 	image_id addOnImage)
455 {
456 	BAutolock _(this);
457 
458 	for (int32 i = fMethodList.CountItems(); i-- > 0;) {
459 		method_info* info = fMethodList.ItemAt(i);
460 		if (!strcmp(info->ref.name, ref.name)) {
461 			// we already know this ref
462 			delete method;
463 			return B_NAME_IN_USE;
464 		}
465 	}
466 
467 	TRACE("%s, name %s\n", __PRETTY_FUNCTION__, ref.name);
468 
469 	method_info* info = new(std::nothrow) method_info;
470 	if (info == NULL) {
471 		delete method;
472 		return B_NO_MEMORY;
473 	}
474 
475 	info->ref = ref;
476 	info->add_on = method;
477 
478 	if (!fMethodList.AddItem(info)) {
479 		delete info;
480 		return B_NO_MEMORY;
481 	}
482 
483 	BAutolock locker(InputServer::gInputMethodListLocker);
484 	if (!InputServer::gInputMethodList.AddItem(method)) {
485 		fMethodList.RemoveItem(info);
486 		delete info;
487 		return B_NO_MEMORY;
488 	}
489 
490 	info->image = addOnImage;
491 
492 	if (gInputServer->MethodReplicant() == NULL) {
493 		_LoadReplicant();
494 
495 		if (gInputServer->MethodReplicant()) {
496 			_BMethodAddOn_ *addon = InputServer::gKeymapMethod.fOwner;
497 			addon->AddMethod();
498 		}
499 	}
500 
501 	if (gInputServer->MethodReplicant() != NULL) {
502 		_BMethodAddOn_ *addon = method->fOwner;
503 		addon->AddMethod();
504 	}
505 
506 	return B_OK;
507 }
508 
509 
510 // #pragma mark -
511 
512 
513 void
514 AddOnManager::_UnloadReplicant()
515 {
516 	BDeskbar().RemoveItem(REPLICANT_CTL_NAME);
517 }
518 
519 
520 void
521 AddOnManager::_LoadReplicant()
522 {
523 	CALLED();
524 	app_info info;
525 	be_app->GetAppInfo(&info);
526 
527 	status_t err = BDeskbar().AddItem(&info.ref);
528 	if (err != B_OK) {
529 		ERROR("Deskbar refuses to add method replicant: %s\n", strerror(err));
530 	}
531 	BMessage request(B_GET_PROPERTY);
532 	BMessenger to;
533 	BMessenger status;
534 
535 	request.AddSpecifier("Messenger");
536 	request.AddSpecifier("Shelf");
537 
538 	// In the Deskbar the Shelf is in the View "Status" in Window "Deskbar"
539 	request.AddSpecifier("View", "Status");
540 	request.AddSpecifier("Window", "Deskbar");
541 	to = BMessenger("application/x-vnd.Be-TSKB", -1);
542 
543 	BMessage reply;
544 
545 	if (to.SendMessage(&request, &reply) == B_OK
546 		&& reply.FindMessenger("result", &status) == B_OK) {
547 		// enum replicant in Status view
548 		int32 index = 0;
549 		int32 uid;
550 		while ((uid = _GetReplicantAt(status, index++)) >= B_OK) {
551 			BMessage replicantInfo;
552 			if (_GetReplicantName(status, uid, &replicantInfo) != B_OK)
553 				continue;
554 
555 			const char *name;
556 			if (replicantInfo.FindString("result", &name) == B_OK
557 				&& !strcmp(name, REPLICANT_CTL_NAME)) {
558 				BMessage replicant;
559 				if (_GetReplicantView(status, uid, &replicant) == B_OK) {
560 					BMessenger result;
561 					if (replicant.FindMessenger("result", &result) == B_OK) {
562 						gInputServer->SetMethodReplicant(new BMessenger(result));
563 					}
564 				}
565 			}
566 		}
567 	}
568 
569 	if (!gInputServer->MethodReplicant()) {
570 		ERROR("LoadReplicant(): Method replicant not found!\n");
571 	}
572 }
573 
574 
575 int32
576 AddOnManager::_GetReplicantAt(BMessenger target, int32 index) const
577 {
578 	// So here we want to get the Unique ID of the replicant at the given index
579 	// in the target Shelf.
580 
581 	BMessage request(B_GET_PROPERTY);// We're getting the ID property
582 	BMessage reply;
583 	status_t err;
584 
585 	request.AddSpecifier("ID");// want the ID
586 	request.AddSpecifier("Replicant", index);// of the index'th replicant
587 
588 	if ((err = target.SendMessage(&request, &reply)) != B_OK)
589 		return err;
590 
591 	int32 uid;
592 	if ((err = reply.FindInt32("result", &uid)) != B_OK)
593 		return err;
594 
595 	return uid;
596 }
597 
598 
599 status_t
600 AddOnManager::_GetReplicantName(BMessenger target, int32 uid,
601 	BMessage* reply) const
602 {
603 	// We send a message to the target shelf, asking it for the Name of the
604 	// replicant with the given unique id.
605 
606 	BMessage request(B_GET_PROPERTY);
607 	BMessage uid_specifier(B_ID_SPECIFIER);// specifying via ID
608 	status_t err;
609 	status_t e;
610 
611 	request.AddSpecifier("Name");// ask for the Name of the replicant
612 
613 	// IDs are specified using code like the following 3 lines:
614 	uid_specifier.AddInt32("id", uid);
615 	uid_specifier.AddString("property", "Replicant");
616 	request.AddSpecifier(&uid_specifier);
617 
618 	if ((err = target.SendMessage(&request, reply)) != B_OK)
619 		return err;
620 
621 	if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK))
622 		return err ? err : e;
623 
624 	return B_OK;
625 }
626 
627 
628 status_t
629 AddOnManager::_GetReplicantView(BMessenger target, int32 uid,
630 	BMessage* reply) const
631 {
632 	// We send a message to the target shelf, asking it for the Name of the
633 	// replicant with the given unique id.
634 
635 	BMessage request(B_GET_PROPERTY);
636 	BMessage uid_specifier(B_ID_SPECIFIER);
637 		// specifying via ID
638 	status_t err;
639 	status_t e;
640 
641 	request.AddSpecifier("View");
642 		// ask for the Name of the replicant
643 
644 	// IDs are specified using code like the following 3 lines:
645 	uid_specifier.AddInt32("id", uid);
646 	uid_specifier.AddString("property", "Replicant");
647 	request.AddSpecifier(&uid_specifier);
648 
649 	if ((err = target.SendMessage(&request, reply)) != B_OK)
650 		return err;
651 
652 	if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK))
653 		return err ? err : e;
654 
655 	return B_OK;
656 }
657 
658 
659 void
660 AddOnManager::MessageReceived(BMessage* message)
661 {
662 	CALLED();
663 
664 	BMessage reply;
665 	status_t status;
666 
667 	ERROR("%s what: %.4s\n", __PRETTY_FUNCTION__, (char*)&message->what);
668 
669 	switch (message->what) {
670 		case IS_FIND_DEVICES:
671 			status = _HandleFindDevices(message, &reply);
672 			break;
673 		case IS_WATCH_DEVICES:
674 			status = _HandleWatchDevices(message, &reply);
675 			break;
676 		case IS_IS_DEVICE_RUNNING:
677 			status = _HandleIsDeviceRunning(message, &reply);
678 			break;
679 		case IS_START_DEVICE:
680 			status = _HandleStartStopDevices(message, &reply);
681 			break;
682 		case IS_STOP_DEVICE:
683 			status = _HandleStartStopDevices(message, &reply);
684 			break;
685 		case IS_CONTROL_DEVICES:
686 			status = _HandleControlDevices(message, &reply);
687 			break;
688 		case SYSTEM_SHUTTING_DOWN:
689 			status = _HandleSystemShuttingDown(message, &reply);
690 			break;
691 		case IS_METHOD_REGISTER:
692 			status = _HandleMethodReplicant(message, &reply);
693 			break;
694 
695 		case B_PATH_MONITOR:
696 			_HandleDeviceMonitor(message);
697 			return;
698 
699 		default:
700 			return;
701 	}
702 
703 	reply.AddInt32("status", status);
704 	message->SendReply(&reply);
705 }
706 
707 
708 status_t
709 AddOnManager::_HandleStartStopDevices(BMessage* message, BMessage* reply)
710 {
711 	const char *name = NULL;
712 	int32 type = 0;
713 	if (!((message->FindInt32("type", &type) != B_OK)
714 			^ (message->FindString("device", &name) != B_OK)))
715 		return B_ERROR;
716 
717 	return gInputServer->StartStopDevices(name, (input_device_type)type,
718 		message->what == IS_START_DEVICE);
719 }
720 
721 
722 status_t
723 AddOnManager::_HandleFindDevices(BMessage* message, BMessage* reply)
724 {
725 	CALLED();
726 	const char *name = NULL;
727 	input_device_type type;
728 	if (message->FindString("device", &name) == B_OK) {
729 		if (gInputServer->GetDeviceInfo(name, &type) != B_OK)
730 			return B_NAME_NOT_FOUND;
731 		reply->AddString("device", name);
732 		reply->AddInt32("type", type);
733 	} else {
734 		gInputServer->GetDeviceInfos(reply);
735 	}
736 	return B_OK;
737 }
738 
739 
740 status_t
741 AddOnManager::_HandleWatchDevices(BMessage* message, BMessage* reply)
742 {
743 	// TODO
744 	return B_OK;
745 }
746 
747 
748 status_t
749 AddOnManager::_HandleIsDeviceRunning(BMessage* message, BMessage* reply)
750 {
751 	const char* name;
752 	bool running;
753 	if (message->FindString("device", &name) != B_OK
754 		|| gInputServer->GetDeviceInfo(name, NULL, &running) != B_OK)
755 		return B_NAME_NOT_FOUND;
756 
757 	return running ? B_OK : B_ERROR;
758 }
759 
760 
761 status_t
762 AddOnManager::_HandleControlDevices(BMessage* message, BMessage* reply)
763 {
764 	CALLED();
765 	const char *name = NULL;
766 	int32 type = 0;
767 	if (!((message->FindInt32("type", &type) != B_OK)
768 			^ (message->FindString("device", &name) != B_OK)))
769 		return B_BAD_VALUE;
770 
771 	uint32 code = 0;
772 	BMessage controlMessage;
773 	bool hasMessage = true;
774 	if (message->FindInt32("code", (int32*)&code) != B_OK)
775 		return B_BAD_VALUE;
776 	if (message->FindMessage("message", &controlMessage) != B_OK)
777 		hasMessage = false;
778 
779 	return gInputServer->ControlDevices(name, (input_device_type)type,
780 		code, hasMessage ? &controlMessage : NULL);
781 }
782 
783 
784 status_t
785 AddOnManager::_HandleSystemShuttingDown(BMessage* message, BMessage* reply)
786 {
787 	CALLED();
788 
789 	for (int32 i = 0; i < fDeviceList.CountItems(); i++) {
790 		device_info* info = fDeviceList.ItemAt(i);
791 		info->add_on->SystemShuttingDown();
792 	}
793 
794 	return B_OK;
795 }
796 
797 
798 status_t
799 AddOnManager::_HandleMethodReplicant(BMessage* message, BMessage* reply)
800 {
801 	CALLED();
802 
803 	if (InputServer::gInputMethodList.CountItems() == 0) {
804 		_UnloadReplicant();
805 		return B_OK;
806 	}
807 
808 	_LoadReplicant();
809 
810 	BAutolock lock(InputServer::gInputMethodListLocker);
811 
812 	if (gInputServer->MethodReplicant()) {
813 		_BMethodAddOn_* addon = InputServer::gKeymapMethod.fOwner;
814 		addon->AddMethod();
815 
816 		for (int32 i = 0; i < InputServer::gInputMethodList.CountItems(); i++) {
817 			BInputServerMethod* method
818 				= (BInputServerMethod*)InputServer::gInputMethodList.ItemAt(i);
819 
820 			_BMethodAddOn_* addon = method->fOwner;
821 			addon->AddMethod();
822 		}
823 	}
824 
825 	return B_OK;
826 }
827 
828 
829 void
830 AddOnManager::_HandleDeviceMonitor(BMessage* message)
831 {
832 	int32 opcode;
833 	if (message->FindInt32("opcode", &opcode) != B_OK)
834 		return;
835 
836 	switch (opcode) {
837 		case B_ENTRY_CREATED:
838 		case B_ENTRY_REMOVED:
839 		{
840 			const char* path;
841 			const char* watchedPath;
842 			if (message->FindString("watched_path", &watchedPath) != B_OK
843 				|| message->FindString("path", &path) != B_OK) {
844 #if DEBUG
845 				char string[1024];
846 				sprintf(string, "message does not contain all fields - "
847 					"watched_path: %d, path: %d\n",
848 					message->HasString("watched_path"),
849 					message->HasString("path"));
850 				debugger(string);
851 #endif
852 				return;
853 			}
854 
855 			// Notify all watching devices
856 
857 			for (int32 i = 0; i < fDeviceAddOns.CountItems(); i++) {
858 				DeviceAddOn* addOn = fDeviceAddOns.ItemAt(i);
859 				if (!addOn->HasPath(watchedPath))
860 					continue;
861 
862 				addOn->Device()->Control(NULL, NULL, B_NODE_MONITOR, message);
863 			}
864 			break;
865 		}
866 	}
867 }
868 
869 
870 status_t
871 AddOnManager::_AddDevicePath(DeviceAddOn* addOn, const char* path,
872 	bool& newPath)
873 {
874 	newPath = !fDevicePaths.HasPath(path);
875 
876 	status_t status = fDevicePaths.AddPath(path);
877 	if (status == B_OK) {
878 		status = addOn->AddPath(path);
879 		if (status == B_OK) {
880 			if (!fDeviceAddOns.HasItem(addOn)
881 				&& !fDeviceAddOns.AddItem(addOn)) {
882 				addOn->RemovePath(path);
883 				status = B_NO_MEMORY;
884 			}
885 		} else
886 			fDevicePaths.RemovePath(path);
887 	}
888 
889 	return status;
890 }
891 
892 
893 status_t
894 AddOnManager::_RemoveDevicePath(DeviceAddOn* addOn, const char* path,
895 	bool& lastPath)
896 {
897 	if (!fDevicePaths.HasPath(path) || !addOn->HasPath(path))
898 		return B_ENTRY_NOT_FOUND;
899 
900 	fDevicePaths.RemovePath(path);
901 
902 	lastPath = !fDevicePaths.HasPath(path);
903 
904 	addOn->RemovePath(path);
905 	if (addOn->CountPaths() == 0)
906 		fDeviceAddOns.RemoveItem(addOn);
907 
908 	return B_OK;
909 }
910 
911 
912 status_t
913 AddOnManager::StartMonitoringDevice(DeviceAddOn* addOn, const char* device)
914 {
915 	CALLED();
916 
917 	BString path;
918 	if (device[0] != '/')
919 		path = "/dev/";
920 	path += device;
921 
922 	TRACE("AddOnMonitor::StartMonitoringDevice(%s)\n", path.String());
923 
924 	bool newPath;
925 	status_t status = _AddDevicePath(addOn, path.String(), newPath);
926 	if (status == B_OK && newPath) {
927 		status = BPathMonitor::StartWatching(path.String(), B_ENTRY_CREATED
928 			| B_ENTRY_REMOVED | B_ENTRY_MOVED | B_WATCH_FILES_ONLY
929 			| B_WATCH_RECURSIVELY, this);
930 		if (status != B_OK) {
931 			bool lastPath;
932 			_RemoveDevicePath(addOn, path.String(), lastPath);
933 		}
934 	}
935 
936 	return status;
937 }
938 
939 
940 status_t
941 AddOnManager::StopMonitoringDevice(DeviceAddOn* addOn, const char *device)
942 {
943 	CALLED();
944 
945 	BString path;
946 	if (device[0] != '/')
947 		path = "/dev/";
948 	path += device;
949 
950 	TRACE("AddOnMonitor::StopMonitoringDevice(%s)\n", path.String());
951 
952 	bool lastPath;
953 	status_t status = _RemoveDevicePath(addOn, path.String(), lastPath);
954 	if (status == B_OK && lastPath)
955 		BPathMonitor::StopWatching(path.String(), this);
956 
957 	return status;
958 }
959