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