Merge branch 'master' of git+ssh://joukj@git.freedesktop.org/git/mesa/mesa
[mesa.git] / src / glx / x11 / dri_glx.c
1 /**************************************************************************
2
3 Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
4 All Rights Reserved.
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sub license, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice (including the
15 next paragraph) shall be included in all copies or substantial portions
16 of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
22 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 **************************************************************************/
27 /* $XFree86: xc/lib/GL/dri/dri_glx.c,v 1.14 2003/07/16 00:54:00 dawes Exp $ */
28
29 /*
30 * Authors:
31 * Kevin E. Martin <kevin@precisioninsight.com>
32 * Brian Paul <brian@precisioninsight.com>
33 *
34 */
35
36 #ifdef GLX_DIRECT_RENDERING
37
38 #include <unistd.h>
39 #include <X11/Xlibint.h>
40 #include <X11/extensions/Xext.h>
41 #include <X11/extensions/extutil.h>
42 #include "glheader.h"
43 #include "glxclient.h"
44 #include "xf86dri.h"
45 #include "sarea.h"
46 #include <stdio.h>
47 #include <dlfcn.h>
48 #include "dri_glx.h"
49 #include <sys/types.h>
50 #include <stdarg.h>
51
52 #ifndef RTLD_NOW
53 #define RTLD_NOW 0
54 #endif
55 #ifndef RTLD_GLOBAL
56 #define RTLD_GLOBAL 0
57 #endif
58
59
60 #ifndef DEFAULT_DRIVER_DIR
61 /* this is normally defined in Mesa/configs/default with DRI_DRIVER_SEARCH_PATH */
62 #define DEFAULT_DRIVER_DIR "/usr/X11R6/lib/modules/dri"
63 #endif
64
65 static __DRIdriver *Drivers = NULL;
66
67
68 /*
69 * printf wrappers
70 */
71
72 static void InfoMessageF(const char *f, ...)
73 {
74 va_list args;
75 const char *env;
76
77 if ((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) {
78 fprintf(stderr, "libGL: ");
79 va_start(args, f);
80 vfprintf(stderr, f, args);
81 va_end(args);
82 }
83 }
84
85 /**
86 * Print error to stderr, unless LIBGL_DEBUG=="quiet".
87 */
88 static void ErrorMessageF(const char *f, ...)
89 {
90 va_list args;
91 const char *env;
92
93 if ((env = getenv("LIBGL_DEBUG")) && !strstr(env, "quiet")) {
94 fprintf(stderr, "libGL error: ");
95 va_start(args, f);
96 vfprintf(stderr, f, args);
97 va_end(args);
98 }
99 }
100
101
102 /**
103 * Extract the ith directory path out of a colon-separated list of paths. No
104 * more than \c dirLen characters, including the terminating \c NUL, will be
105 * written to \c dir.
106 *
107 * \param index Index of path to extract (starting at zero)
108 * \param paths The colon-separated list of paths
109 * \param dirLen Maximum length of result to store in \c dir
110 * \param dir Buffer to hold the extracted directory path
111 *
112 * \returns
113 * The number of characters that would have been written to \c dir had there
114 * been enough room. This does not include the terminating \c NUL. When
115 * extraction fails, zero will be returned.
116 *
117 * \todo
118 * It seems like this function could be rewritten to use \c strchr.
119 */
120 static size_t
121 ExtractDir(int index, const char *paths, int dirLen, char *dir)
122 {
123 int i, len;
124 const char *start, *end;
125
126 /* find ith colon */
127 start = paths;
128 i = 0;
129 while (i < index) {
130 if (*start == ':') {
131 i++;
132 start++;
133 }
134 else if (*start == 0) {
135 /* end of string and couldn't find ith colon */
136 dir[0] = 0;
137 return 0;
138 }
139 else {
140 start++;
141 }
142 }
143
144 while (*start == ':')
145 start++;
146
147 /* find next colon, or end of string */
148 end = start + 1;
149 while (*end != ':' && *end != 0) {
150 end++;
151 }
152
153 /* copy string between <start> and <end> into result string */
154 len = end - start;
155 if (len > dirLen - 1)
156 len = dirLen - 1;
157 strncpy(dir, start, len);
158 dir[len] = 0;
159
160 return( end - start );
161 }
162
163
164 /**
165 * Versioned name of the expected \c __driCreateNewScreen function.
166 *
167 * The version of the last incompatible loader/driver inteface change is
168 * appended to the name of the \c __driCreateNewScreen function. This
169 * prevents loaders from trying to load drivers that are too old.
170 */
171 static const char createNewScreenName[] = __DRI_CREATE_NEW_SCREEN_STRING;
172
173
174 /**
175 * Try to \c dlopen the named driver.
176 *
177 * This function adds the "_dri.so" suffix to the driver name and searches the
178 * directories specified by the \c LIBGL_DRIVERS_PATH environment variable in
179 * order to find the driver.
180 *
181 * \param driverName - a name like "tdfx", "i810", "mga", etc.
182 *
183 * \returns
184 * A handle from \c dlopen, or \c NULL if driver file not found.
185 */
186 static __DRIdriver *OpenDriver(const char *driverName)
187 {
188 void *glhandle = NULL;
189 char *libPaths = NULL;
190 char libDir[1000];
191 int i;
192 __DRIdriver *driver;
193
194 /* First, search Drivers list to see if we've already opened this driver */
195 for (driver = Drivers; driver; driver = driver->next) {
196 if (strcmp(driver->name, driverName) == 0) {
197 /* found it */
198 return driver;
199 }
200 }
201
202 /* Attempt to make sure libGL symbols will be visible to the driver */
203 glhandle = dlopen("libGL.so.1", RTLD_NOW | RTLD_GLOBAL);
204
205 if (geteuid() == getuid()) {
206 /* don't allow setuid apps to use LIBGL_DRIVERS_PATH */
207 libPaths = getenv("LIBGL_DRIVERS_PATH");
208 if (!libPaths)
209 libPaths = getenv("LIBGL_DRIVERS_DIR"); /* deprecated */
210 }
211 if (!libPaths)
212 libPaths = DEFAULT_DRIVER_DIR;
213
214 for ( i = 0 ; ExtractDir(i, libPaths, 1000, libDir) != 0 ; i++ ) {
215 char realDriverName[200];
216 void *handle = NULL;
217
218
219 /* If TLS support is enabled, try to open the TLS version of the driver
220 * binary first. If that fails, try the non-TLS version.
221 */
222 #ifdef GLX_USE_TLS
223 snprintf(realDriverName, 200, "%s/tls/%s_dri.so", libDir, driverName);
224 InfoMessageF("OpenDriver: trying %s\n", realDriverName);
225 handle = dlopen(realDriverName, RTLD_NOW | RTLD_GLOBAL);
226 #endif
227
228 if ( handle == NULL ) {
229 snprintf(realDriverName, 200, "%s/%s_dri.so", libDir, driverName);
230 InfoMessageF("OpenDriver: trying %s\n", realDriverName);
231 handle = dlopen(realDriverName, RTLD_NOW | RTLD_GLOBAL);
232 }
233
234 if ( handle != NULL ) {
235 /* allocate __DRIdriver struct */
236 driver = (__DRIdriver *) Xmalloc(sizeof(__DRIdriver));
237 if (!driver)
238 break; /* out of memory! */
239 /* init the struct */
240 driver->name = __glXstrdup(driverName);
241 if (!driver->name) {
242 Xfree(driver);
243 driver = NULL;
244 break; /* out of memory! */
245 }
246
247 driver->createNewScreenFunc = (PFNCREATENEWSCREENFUNC)
248 dlsym(handle, createNewScreenName);
249
250 if ( driver->createNewScreenFunc == NULL ) {
251 /* If the driver doesn't have this symbol then something's
252 * really, really wrong.
253 */
254 ErrorMessageF("%s not defined in %s_dri.so!\n"
255 "Your driver may be too old for this libGL.\n",
256 createNewScreenName, driverName);
257 Xfree(driver);
258 driver = NULL;
259 dlclose(handle);
260 continue;
261 }
262 driver->handle = handle;
263 /* put at head of linked list */
264 driver->next = Drivers;
265 Drivers = driver;
266 break;
267 }
268 else {
269 ErrorMessageF("dlopen %s failed (%s)\n", realDriverName, dlerror());
270 }
271 }
272
273 if (!driver)
274 ErrorMessageF("unable to load driver: %s_dri.so\n", driverName);
275
276 if (glhandle)
277 dlclose(glhandle);
278
279 return driver;
280 }
281
282
283 /*
284 * Given a display pointer and screen number, determine the name of
285 * the DRI driver for the screen. (I.e. "r128", "tdfx", etc).
286 * Return True for success, False for failure.
287 */
288 static Bool GetDriverName(Display *dpy, int scrNum, char **driverName)
289 {
290 int directCapable;
291 Bool b;
292 int driverMajor, driverMinor, driverPatch;
293
294 *driverName = NULL;
295
296 if (!XF86DRIQueryDirectRenderingCapable(dpy, scrNum, &directCapable)) {
297 ErrorMessageF("XF86DRIQueryDirectRenderingCapable failed\n");
298 return False;
299 }
300 if (!directCapable) {
301 ErrorMessageF("XF86DRIQueryDirectRenderingCapable returned false\n");
302 return False;
303 }
304
305 b = XF86DRIGetClientDriverName(dpy, scrNum, &driverMajor, &driverMinor,
306 &driverPatch, driverName);
307 if (!b) {
308 ErrorMessageF("Cannot determine driver name for screen %d\n", scrNum);
309 return False;
310 }
311
312 InfoMessageF("XF86DRIGetClientDriverName: %d.%d.%d %s (screen %d)\n",
313 driverMajor, driverMinor, driverPatch, *driverName, scrNum);
314
315 return True;
316 }
317
318
319 /*
320 * Given a display pointer and screen number, return a __DRIdriver handle.
321 * Return NULL if anything goes wrong.
322 */
323 __DRIdriver *driGetDriver(Display *dpy, int scrNum)
324 {
325 char *driverName;
326 if (GetDriverName(dpy, scrNum, &driverName)) {
327 __DRIdriver *ret;
328 ret = OpenDriver(driverName);
329 if (driverName)
330 Xfree(driverName);
331 return ret;
332 }
333 return NULL;
334 }
335
336
337 /*
338 * Exported function for querying the DRI driver for a given screen.
339 *
340 * The returned char pointer points to a static array that will be
341 * overwritten by subsequent calls.
342 */
343 PUBLIC const char *glXGetScreenDriver (Display *dpy, int scrNum) {
344 static char ret[32];
345 char *driverName;
346 if (GetDriverName(dpy, scrNum, &driverName)) {
347 int len;
348 if (!driverName)
349 return NULL;
350 len = strlen (driverName);
351 if (len >= 31)
352 return NULL;
353 memcpy (ret, driverName, len+1);
354 Xfree(driverName);
355 return ret;
356 }
357 return NULL;
358 }
359
360
361 /*
362 * Exported function for obtaining a driver's option list (UTF-8 encoded XML).
363 *
364 * The returned char pointer points directly into the driver. Therefore
365 * it should be treated as a constant.
366 *
367 * If the driver was not found or does not support configuration NULL is
368 * returned.
369 *
370 * Note: The driver remains opened after this function returns.
371 */
372 PUBLIC const char *glXGetDriverConfig (const char *driverName) {
373 __DRIdriver *driver = OpenDriver (driverName);
374 if (driver)
375 return dlsym (driver->handle, "__driConfigOptions");
376 else
377 return NULL;
378 }
379
380
381 /* Called from __glXFreeDisplayPrivate.
382 */
383 static void driDestroyDisplay(Display *dpy, void *private)
384 {
385 __DRIdisplayPrivate *pdpyp = (__DRIdisplayPrivate *)private;
386
387 if (pdpyp) {
388 const int numScreens = ScreenCount(dpy);
389 int i;
390 for (i = 0; i < numScreens; i++) {
391 if (pdpyp->libraryHandles[i]) {
392 __DRIdriver *driver, *prev;
393
394 /* Remove driver from Drivers list */
395 for (prev = NULL, driver = Drivers; driver;
396 prev = driver, driver = driver->next) {
397 if (driver->handle == pdpyp->libraryHandles[i]) {
398 if (prev)
399 prev->next = driver->next;
400 else
401 Drivers = driver->next;
402
403 Xfree(driver->name);
404 Xfree(driver);
405 break;
406 }
407 }
408
409 dlclose(pdpyp->libraryHandles[i]);
410 }
411 }
412 Xfree(pdpyp->libraryHandles);
413 Xfree(pdpyp);
414 }
415 }
416
417
418 /*
419 * Allocate, initialize and return a __DRIdisplayPrivate object.
420 * This is called from __glXInitialize() when we are given a new
421 * display pointer.
422 */
423 void *driCreateDisplay(Display *dpy, __DRIdisplay *pdisp)
424 {
425 const int numScreens = ScreenCount(dpy);
426 __DRIdisplayPrivate *pdpyp;
427 int eventBase, errorBase;
428 int major, minor, patch;
429 int scrn;
430
431 /* Initialize these fields to NULL in case we fail.
432 * If we don't do this we may later get segfaults trying to free random
433 * addresses when the display is closed.
434 */
435 pdisp->private = NULL;
436 pdisp->destroyDisplay = NULL;
437
438 if (!XF86DRIQueryExtension(dpy, &eventBase, &errorBase)) {
439 return NULL;
440 }
441
442 if (!XF86DRIQueryVersion(dpy, &major, &minor, &patch)) {
443 return NULL;
444 }
445
446 pdpyp = (__DRIdisplayPrivate *)Xmalloc(sizeof(__DRIdisplayPrivate));
447 if (!pdpyp) {
448 return NULL;
449 }
450
451 pdpyp->driMajor = major;
452 pdpyp->driMinor = minor;
453 pdpyp->driPatch = patch;
454
455 pdisp->destroyDisplay = driDestroyDisplay;
456
457 /* allocate array of pointers to createNewScreen funcs */
458 pdisp->createNewScreen = (PFNCREATENEWSCREENFUNC *)
459 Xmalloc(numScreens * sizeof(void *));
460 if (!pdisp->createNewScreen) {
461 Xfree(pdpyp);
462 return NULL;
463 }
464
465 /* allocate array of library handles */
466 pdpyp->libraryHandles = (void **) Xmalloc(numScreens * sizeof(void*));
467 if (!pdpyp->libraryHandles) {
468 Xfree(pdisp->createNewScreen);
469 Xfree(pdpyp);
470 return NULL;
471 }
472
473 /* dynamically discover DRI drivers for all screens, saving each
474 * driver's "__driCreateScreen" function pointer. That's the bootstrap
475 * entrypoint for all DRI drivers.
476 */
477 for (scrn = 0; scrn < numScreens; scrn++) {
478 __DRIdriver *driver = driGetDriver(dpy, scrn);
479 if (driver) {
480 pdisp->createNewScreen[scrn] = driver->createNewScreenFunc;
481 pdpyp->libraryHandles[scrn] = driver->handle;
482 }
483 else {
484 pdisp->createNewScreen[scrn] = NULL;
485 pdpyp->libraryHandles[scrn] = NULL;
486 }
487 }
488
489 return (void *)pdpyp;
490 }
491
492 #endif /* GLX_DIRECT_RENDERING */