xref: /haiku/src/servers/input/AddOnManager.cpp (revision db10640de90f7f9519ba2da9577b7c1af3c64f6b)
1 /*
2 ** Copyright 2004, the OpenBeOS project. All rights reserved.
3 ** Distributed under the terms of the OpenBeOS License.
4 **
5 ** Author : Jérôme Duval
6 ** Original authors: Marcus Overhagen, Axel Dörfler
7 */
8 
9 
10 #include <Autolock.h>
11 #include <Directory.h>
12 #include <Entry.h>
13 #include <FindDirectory.h>
14 #include <Path.h>
15 #include <String.h>
16 
17 #include <image.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "AddOnManager.h"
22 #include "InputServer.h"
23 
24 
25 AddOnManager::AddOnManager()
26 	:
27  	fLock("add-on manager")
28 {
29 }
30 
31 
32 AddOnManager::~AddOnManager()
33 {
34 }
35 
36 
37 void
38 AddOnManager::LoadState()
39 {
40 	RegisterAddOns();
41 }
42 
43 
44 void
45 AddOnManager::SaveState()
46 {
47 	CALLED();
48 	UnregisterAddOns();
49 }
50 
51 
52 status_t
53 AddOnManager::RegisterAddOn(BEntry &entry)
54 {
55 	BPath path(&entry);
56 
57 	entry_ref ref;
58 	status_t status = entry.GetRef(&ref);
59 	if (status < B_OK)
60 		return status;
61 
62 	printf("AddOnManager::RegisterAddOn(): trying to load \"%s\"\n", path.Path());
63 
64 	image_id addon_image = load_add_on(path.Path());
65 
66 	if (addon_image < B_OK)
67 		return addon_image;
68 
69 	BEntry parent;
70 	entry.GetParent(&parent);
71 	BPath parentPath(&parent);
72 	BString pathString = parentPath.Path();
73 
74 	if (pathString.FindFirst("input_server/devices")>0) {
75 		BInputServerDevice *(*instantiate_func)();
76 
77 		if (get_image_symbol(addon_image, "instantiate_input_device",
78 				B_SYMBOL_TYPE_TEXT, (void **)&instantiate_func) < B_OK) {
79 			printf("AddOnManager::RegisterAddOn(): can't find instantiate_input_device in \"%s\"\n",
80 				path.Path());
81 			goto exit_error;
82 		}
83 
84 		BInputServerDevice *isd = (*instantiate_func)();
85 		if (isd == NULL) {
86 			printf("AddOnManager::RegisterAddOn(): instantiate_input_device in \"%s\" returned NULL\n",
87 				path.Path());
88 			goto exit_error;
89 		}
90 		status_t status = isd->InitCheck();
91 		if (status != B_OK) {
92 			printf("AddOnManager::RegisterAddOn(): BInputServerDevice.InitCheck in \"%s\" returned %s\n",
93 				path.Path(), strerror(status));
94 			delete isd;
95 			goto exit_error;
96 		}
97 
98 		RegisterDevice(isd, ref, addon_image);
99 
100 
101 	} else if (pathString.FindFirst("input_server/filters")>0) {
102 		BInputServerFilter *(*instantiate_func)();
103 
104 		if (get_image_symbol(addon_image, "instantiate_input_filter",
105 				B_SYMBOL_TYPE_TEXT, (void **)&instantiate_func) < B_OK) {
106 			printf("AddOnManager::RegisterAddOn(): can't find instantiate_input_filter in \"%s\"\n",
107 				path.Path());
108 			goto exit_error;
109 		}
110 
111 		BInputServerFilter *isf = (*instantiate_func)();
112 		if (isf == NULL) {
113 			printf("AddOnManager::RegisterAddOn(): instantiate_input_filter in \"%s\" returned NULL\n",
114 				path.Path());
115 			goto exit_error;
116 		}
117 		status_t status = isf->InitCheck();
118 		if (status != B_OK) {
119 			printf("AddOnManager::RegisterAddOn(): BInputServerFilter.InitCheck in \"%s\" returned %s\n",
120 				path.Path(), strerror(status));
121 			delete isf;
122 			goto exit_error;
123 		}
124 
125 		RegisterFilter(isf, ref, addon_image);
126 
127 	} else if (pathString.FindFirst("input_server/methods")>0) {
128 		BInputServerMethod *(*instantiate_func)();
129 
130 		if (get_image_symbol(addon_image, "instantiate_input_method",
131 				B_SYMBOL_TYPE_TEXT, (void **)&instantiate_func) < B_OK) {
132 			printf("AddOnManager::RegisterAddOn(): can't find instantiate_input_method in \"%s\"\n",
133 				path.Path());
134 			goto exit_error;
135 		}
136 
137 		BInputServerMethod *ism = (*instantiate_func)();
138 		if (ism == NULL) {
139 			printf("AddOnManager::RegisterAddOn(): instantiate_input_method in \"%s\" returned NULL\n",
140 				path.Path());
141 			goto exit_error;
142 		}
143 		status_t status = ism->InitCheck();
144 		if (status != B_OK) {
145 			printf("AddOnManager::RegisterAddOn(): BInputServerMethod.InitCheck in \"%s\" returned %s\n",
146 				path.Path(), strerror(status));
147 			delete ism;
148 			goto exit_error;
149 		}
150 
151 		RegisterMethod(ism, ref, addon_image);
152 
153 	}
154 
155 	return B_OK;
156 exit_error:
157 	unload_add_on(addon_image);
158 
159 	return status;
160 }
161 
162 
163 status_t
164 AddOnManager::UnregisterAddOn(BEntry &entry)
165 {
166 	BPath path(&entry);
167 
168 	entry_ref ref;
169 	status_t status = entry.GetRef(&ref);
170 	if (status < B_OK)
171 		return status;
172 
173 	PRINT(("AddOnManager::UnregisterAddOn(): trying to unload \"%s\"\n", path.Path()));
174 
175 	BEntry parent;
176 	entry.GetParent(&parent);
177 	BPath parentPath(&parent);
178 	BString pathString = parentPath.Path();
179 
180 	BAutolock locker(fLock);
181 
182 	if (pathString.FindFirst("input_server/devices")>0) {
183 		device_info *pinfo;
184 		for (fDeviceList.Rewind(); fDeviceList.GetNext(&pinfo);) {
185 			if (!strcmp(pinfo->ref.name, ref.name)) {
186 				InputServer::StartStopDevices(pinfo->isd, false);
187 				delete pinfo->isd;
188 				if (pinfo->addon_image >= B_OK)
189 					unload_add_on(pinfo->addon_image);
190 				fDeviceList.RemoveCurrent();
191 				break;
192 			}
193 		}
194 	} else if (pathString.FindFirst("input_server/filters")>0) {
195 		filter_info *pinfo;
196 		for (fFilterList.Rewind(); fFilterList.GetNext(&pinfo);) {
197 			if (!strcmp(pinfo->ref.name, ref.name)) {
198 				delete pinfo->isf;
199 				if (pinfo->addon_image >= B_OK)
200 					unload_add_on(pinfo->addon_image);
201 				fFilterList.RemoveCurrent();
202 				break;
203 			}
204 		}
205 	} else if (pathString.FindFirst("input_server/methods")>0) {
206 		method_info *pinfo;
207 		for (fMethodList.Rewind(); fMethodList.GetNext(&pinfo);) {
208 			if (!strcmp(pinfo->ref.name, ref.name)) {
209 				delete pinfo->ism;
210 				if (pinfo->addon_image >= B_OK)
211 					unload_add_on(pinfo->addon_image);
212 				fMethodList.RemoveCurrent();
213 				break;
214 			}
215 		}
216 	}
217 
218 	return B_OK;
219 }
220 
221 void
222 AddOnManager::RegisterAddOns()
223 {
224 	class IAHandler : public AddOnMonitorHandler {
225 	private:
226 		AddOnManager * fManager;
227 	public:
228 		IAHandler(AddOnManager * manager) {
229 			fManager = manager;
230 		}
231 		virtual void	AddOnCreated(const add_on_entry_info * entry_info) {
232 		}
233 		virtual void	AddOnEnabled(const add_on_entry_info * entry_info) {
234 			CALLED();
235 			entry_ref ref;
236 			make_entry_ref(entry_info->dir_nref.device, entry_info->dir_nref.node,
237 			               entry_info->name, &ref);
238 			BEntry entry(&ref, false);
239 			fManager->RegisterAddOn(entry);
240 		}
241 		virtual void	AddOnDisabled(const add_on_entry_info * entry_info) {
242 			CALLED();
243 			entry_ref ref;
244 			make_entry_ref(entry_info->dir_nref.device, entry_info->dir_nref.node,
245 			               entry_info->name, &ref);
246 			BEntry entry(&ref, false);
247 			fManager->UnregisterAddOn(entry);
248 		}
249 		virtual void	AddOnRemoved(const add_on_entry_info * entry_info) {
250 		}
251 	};
252 
253 	const directory_which directories[] = {
254 		B_USER_ADDONS_DIRECTORY,
255 		B_COMMON_ADDONS_DIRECTORY,
256 		B_BEOS_ADDONS_DIRECTORY,
257 	};
258 	const char subDirectories[][24] = {
259 		"input_server/devices",
260 		"input_server/filters",
261 		"input_server/methods",
262 	};
263 	fHandler = new IAHandler(this);
264 	fAddOnMonitor = new AddOnMonitor(fHandler);
265 
266 	node_ref nref;
267 	BDirectory directory;
268 	BPath path;
269 	for (uint i = 0 ; i < sizeof(directories) / sizeof(directory_which) ; i++)
270 		for (uint j = 0 ; j < sizeof(subDirectories) / sizeof(char[24]) ; j++) {
271 			if ((find_directory(directories[i], &path) == B_OK)
272 				&& (path.Append(subDirectories[j]) == B_OK)
273 				&& (directory.SetTo(path.Path()) == B_OK)
274 				&& (directory.GetNodeRef(&nref) == B_OK)) {
275 				fHandler->AddDirectory(&nref);
276 			}
277 		}
278 
279 }
280 
281 
282 void
283 AddOnManager::UnregisterAddOns()
284 {
285 	BMessenger messenger(fAddOnMonitor);
286 	messenger.SendMessage(B_QUIT_REQUESTED);
287 	int32 exit_value;
288 	wait_for_thread(fAddOnMonitor->Thread(), &exit_value);
289 	delete fHandler;
290 
291 	BAutolock locker(fLock);
292 
293 	// we have to stop manually the addons because the monitor doesn't disable them on exit
294 
295 	{
296 		device_info *pinfo;
297 		for (fDeviceList.Rewind(); fDeviceList.GetNext(&pinfo);) {
298 			InputServer::StartStopDevices(pinfo->isd, false);
299 			delete pinfo->isd;
300 			if (pinfo->addon_image >= B_OK)
301 				unload_add_on(pinfo->addon_image);
302 			fDeviceList.RemoveCurrent();
303 		}
304 	}
305 
306 	{
307 		filter_info *pinfo;
308 		for (fFilterList.Rewind(); fFilterList.GetNext(&pinfo);) {
309 			delete pinfo->isf;
310 			if (pinfo->addon_image >= B_OK)
311 				unload_add_on(pinfo->addon_image);
312 			fFilterList.RemoveCurrent();
313 		}
314 	}
315 
316 	{
317 		method_info *pinfo;
318 		for (fMethodList.Rewind(); fMethodList.GetNext(&pinfo);) {
319 				delete pinfo->ism;
320 				if (pinfo->addon_image >= B_OK)
321 					unload_add_on(pinfo->addon_image);
322 				fMethodList.RemoveCurrent();
323 		}
324 	}
325 }
326 
327 
328 void
329 AddOnManager::RegisterDevice(BInputServerDevice *device, const entry_ref &ref, image_id addon_image)
330 {
331 	BAutolock locker(fLock);
332 
333 	device_info *pinfo;
334 	for (fDeviceList.Rewind(); fDeviceList.GetNext(&pinfo);) {
335 		if (!strcmp(pinfo->ref.name, ref.name)) {
336 			// we already know this device
337 			return;
338 		}
339 	}
340 
341 	PRINT(("AddOnManager::RegisterDevice, name %s\n", ref.name));
342 
343 	device_info info;
344 	info.ref = ref;
345 	info.addon_image = addon_image;
346 	info.isd = device;
347 
348 	fDeviceList.Insert(info);
349 }
350 
351 
352 void
353 AddOnManager::RegisterFilter(BInputServerFilter *filter, const entry_ref &ref, image_id addon_image)
354 {
355 	BAutolock locker(fLock);
356 
357 	filter_info *pinfo;
358 	for (fFilterList.Rewind(); fFilterList.GetNext(&pinfo);) {
359 		if (!strcmp(pinfo->ref.name, ref.name)) {
360 			// we already know this ref
361 			return;
362 		}
363 	}
364 
365 	PRINT(("%s, name %s\n", __PRETTY_FUNCTION__, ref.name));
366 
367 	filter_info info;
368 	info.ref = ref;
369 	info.addon_image = addon_image;
370 	info.isf = filter;
371 
372 	fFilterList.Insert(info);
373 
374 	BAutolock lock2(InputServer::gInputFilterListLocker);
375 
376 	InputServer::gInputFilterList.AddItem(filter);
377 }
378 
379 
380 void
381 AddOnManager::RegisterMethod(BInputServerMethod *method, const entry_ref &ref, image_id addon_image)
382 {
383 	BAutolock locker(fLock);
384 
385 	method_info *pinfo;
386 	for (fMethodList.Rewind(); fMethodList.GetNext(&pinfo);) {
387 		if (!strcmp(pinfo->ref.name, ref.name)) {
388 			// we already know this ref
389 			return;
390 		}
391 	}
392 
393 	PRINT(("%s, name %s\n", __PRETTY_FUNCTION__, ref.name));
394 
395 	method_info info;
396 	info.ref = ref;
397 	info.addon_image = addon_image;
398 	info.ism = method;
399 
400 	fMethodList.Insert(info);
401 
402 	BAutolock lock2(InputServer::gInputMethodListLocker);
403 
404 	InputServer::gInputMethodList.AddItem(method);
405 }
406 
407 
408