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