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