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