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