1/*
2 * Copyright 2006-2012 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Philippe Houdoin <philippe.houdoin@free.fr>
7 *		Alexander von Gluck IV <kallisti5@unixzen.com>
8 */
9
10
11#include <driver_settings.h>
12#include <image.h>
13
14#include <kernel/image.h>
15#include <system/safemode_defs.h>
16
17#include <Directory.h>
18#include <FindDirectory.h>
19#include <Path.h>
20#include <strings.h>
21#include "GLDispatcher.h"
22#include "GLRendererRoster.h"
23
24#include <new>
25#include <string.h>
26
27
28extern "C" status_t _kern_get_safemode_option(const char* parameter,
29	char* buffer, size_t* _bufferSize);
30
31
32GLRendererRoster::GLRendererRoster(BGLView* view, ulong options)
33	:
34	fNextID(0),
35	fView(view),
36	fOptions(options),
37	fSafeMode(false),
38	fABISubDirectory(NULL)
39{
40	char parameter[32];
41	size_t parameterLength = sizeof(parameter);
42
43	if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE,
44		parameter, &parameterLength) == B_OK) {
45		if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
46			|| !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
47			|| !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
48			fSafeMode = true;
49	}
50
51	if (_kern_get_safemode_option(B_SAFEMODE_DISABLE_USER_ADD_ONS,
52		parameter, &parameterLength) == B_OK) {
53		if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
54			|| !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
55			|| !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
56			fSafeMode = true;
57	}
58
59	// We might run in compatibility mode on a system with a different ABI. The
60	// renderers matching our ABI can usually be found in respective
61	// subdirectories of the opengl add-ons directories.
62	system_info info;
63	if (get_system_info(&info) == B_OK
64		&& (info.abi & B_HAIKU_ABI_MAJOR)
65			!= (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR)) {
66			switch (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) {
67				case B_HAIKU_ABI_GCC_2:
68					fABISubDirectory = "gcc2";
69					break;
70				case B_HAIKU_ABI_GCC_4:
71					fABISubDirectory = "gcc4";
72					break;
73			}
74	}
75
76	AddDefaultPaths();
77}
78
79
80GLRendererRoster::~GLRendererRoster()
81{
82
83}
84
85
86BGLRenderer*
87GLRendererRoster::GetRenderer(int32 id)
88{
89	RendererMap::const_iterator iterator = fRenderers.find(id);
90	if (iterator == fRenderers.end())
91		return NULL;
92
93	struct renderer_item item = iterator->second;
94	return item.renderer;
95}
96
97
98void
99GLRendererRoster::AddDefaultPaths()
100{
101	// add user directories first, so that they can override system renderers
102	const directory_which paths[] = {
103		B_USER_NONPACKAGED_ADDONS_DIRECTORY,
104		B_USER_ADDONS_DIRECTORY,
105		B_SYSTEM_ADDONS_DIRECTORY,
106	};
107
108	for (uint32 i = fSafeMode ? 4 : 0;
109		i < sizeof(paths) / sizeof(paths[0]); i++) {
110		BPath path;
111		status_t status = find_directory(paths[i], &path, true);
112		if (status == B_OK && path.Append("opengl") == B_OK)
113			AddPath(path.Path());
114	}
115}
116
117
118status_t
119GLRendererRoster::AddPath(const char* path)
120{
121	BDirectory directory(path);
122	status_t status = directory.InitCheck();
123	if (status < B_OK)
124		return status;
125
126	// if a subdirectory for our ABI exists, use that instead
127	if (fABISubDirectory != NULL) {
128		BEntry entry(&directory, fABISubDirectory);
129		if (entry.IsDirectory()) {
130			status = directory.SetTo(&entry);
131			if (status != B_OK)
132				return status;
133		}
134	}
135
136	node_ref nodeRef;
137	status = directory.GetNodeRef(&nodeRef);
138	if (status < B_OK)
139		return status;
140
141	int32 count = 0;
142	int32 files = 0;
143
144	entry_ref ref;
145	BEntry entry;
146	while (directory.GetNextRef(&ref) == B_OK) {
147		entry.SetTo(&ref, true);
148		if (entry.InitCheck() == B_OK && !entry.IsFile())
149			continue;
150
151		if (CreateRenderer(ref) == B_OK)
152			count++;
153
154		files++;
155	}
156
157	if (files != 0 && count == 0)
158		return B_BAD_VALUE;
159
160	return B_OK;
161}
162
163
164status_t
165GLRendererRoster::AddRenderer(BGLRenderer* renderer,
166	image_id image, const entry_ref* ref, ino_t node)
167{
168	renderer_item item;
169	item.renderer = renderer;
170	item.image = image;
171	item.node = node;
172	if (ref != NULL)
173		item.ref = *ref;
174
175	try {
176		fRenderers[fNextID] = item;
177	} catch (...) {
178		return B_NO_MEMORY;
179	}
180
181	renderer->fOwningRoster = this;
182	renderer->fID = fNextID++;
183	return B_OK;
184}
185
186
187status_t
188GLRendererRoster::CreateRenderer(const entry_ref& ref)
189{
190	BEntry entry(&ref, true);
191	node_ref nodeRef;
192	status_t status = entry.GetNodeRef(&nodeRef);
193	if (status < B_OK)
194		return status;
195
196	BPath path(&ref);
197	image_id image = load_add_on(path.Path());
198	if (image < B_OK)
199		return image;
200
201	BGLRenderer* (*instantiate_renderer)
202		(BGLView* view, ulong options, BGLDispatcher* dispatcher);
203
204	status = get_image_symbol(image, "instantiate_gl_renderer",
205		B_SYMBOL_TYPE_TEXT, (void**)&instantiate_renderer);
206	if (status == B_OK) {
207		BGLRenderer* renderer
208			= instantiate_renderer(fView, fOptions, new BGLDispatcher());
209		if (!renderer) {
210			unload_add_on(image);
211			return B_UNSUPPORTED;
212		}
213
214		if (AddRenderer(renderer, image, &ref, nodeRef.node) != B_OK) {
215			renderer->Release();
216			// this will delete the renderer
217			unload_add_on(image);
218		}
219		return B_OK;
220	}
221	unload_add_on(image);
222
223	return status;
224}
225