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