5cf9923979a0647e4bf672ebbee9b5cb20f7cb50
[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 * \todo
172 * Create a macro or something so that this is automatically updated.
173 */
174 static const char createNewScreenName[] = "__driCreateNewScreen_20050727";
175
176
177 /**
178 * Try to \c dlopen the named driver.
179 *
180 * This function adds the "_dri.so" suffix to the driver name and searches the
181 * directories specified by the \c LIBGL_DRIVERS_PATH environment variable in
182 * order to find the driver.
183 *
184 * \param driverName - a name like "tdfx", "i810", "mga", etc.
185 *
186 * \returns
187 * A handle from \c dlopen, or \c NULL if driver file not found.
188 */
189 static __DRIdriver *OpenDriver(const char *driverName)
190 {
191 void *glhandle = NULL;
192 char *libPaths = NULL;
193 char libDir[1000];
194 int i;
195 __DRIdriver *driver;
196
197 /* First, search Drivers list to see if we've already opened this driver */
198 for (driver = Drivers; driver; driver = driver->next) {
199 if (strcmp(driver->name, driverName) == 0) {
200 /* found it */
201 return driver;
202 }
203 }
204
205 /* Attempt to make sure libGL symbols will be visible to the driver */
206 glhandle = dlopen("libGL.so.1", RTLD_NOW | RTLD_GLOBAL);
207
208 if (geteuid() == getuid()) {
209 /* don't allow setuid apps to use LIBGL_DRIVERS_PATH */
210 libPaths = getenv("LIBGL_DRIVERS_PATH");
211 if (!libPaths)
212 libPaths = getenv("LIBGL_DRIVERS_DIR"); /* deprecated */
213 }
214 if (!libPaths)
215 libPaths = DEFAULT_DRIVER_DIR;
216
217 for ( i = 0 ; ExtractDir(i, libPaths, 1000, libDir) != 0 ; i++ ) {
218 char realDriverName[200];
219 void *handle = NULL;
220
221
222 /* If TLS support is enabled, try to open the TLS version of the driver
223 * binary first. If that fails, try the non-TLS version.
224 */
225 #ifdef GLX_USE_TLS
226 snprintf(realDriverName, 200, "%s/tls/%s_dri.so", libDir, driverName);
227 InfoMessageF("OpenDriver: trying %s\n", realDriverName);
228 handle = dlopen(realDriverName, RTLD_NOW | RTLD_GLOBAL);
229 #endif
230
231 if ( handle == NULL ) {
232 snprintf(realDriverName, 200, "%s/%s_dri.so", libDir, driverName);
233 InfoMessageF("OpenDriver: trying %s\n", realDriverName);
234 handle = dlopen(realDriverName, RTLD_NOW | RTLD_GLOBAL);
235 }
236
237 if ( handle != NULL ) {
238 /* allocate __DRIdriver struct */
239 driver = (__DRIdriver *) Xmalloc(sizeof(__DRIdriver));
240 if (!driver)
241 break; /* out of memory! */
242 /* init the struct */
243 driver->name = __glXstrdup(driverName);
244 if (!driver->name) {
245 Xfree(driver);
246 driver = NULL;
247 break; /* out of memory! */
248 }
249
250 driver->createNewScreenFunc = (PFNCREATENEWSCREENFUNC)
251 dlsym(handle, createNewScreenName);
252
253 if ( driver->createNewScreenFunc == NULL ) {
254 /* If the driver doesn't have this symbol then something's
255 * really, really wrong.
256 */
257 ErrorMessageF("%s not defined in %s_dri.so!\n"
258 "Your driver may be too old for this libGL.\n",
259 createNewScreenName, driverName);
260 Xfree(driver);
261 driver = NULL;
262 dlclose(handle);
263 continue;
264 }
265 driver->handle = handle;
266 /* put at head of linked list */
267 driver->next = Drivers;
268 Drivers = driver;
269 break;
270 }
271 else {
272 ErrorMessageF("dlopen %s failed (%s)\n", realDriverName, dlerror());
273 }
274 }
275
276 if (!driver)
277 ErrorMessageF("unable to load driver: %s_dri.so\n", driverName);
278
279 if (glhandle)
280 dlclose(glhandle);
281
282 return driver;
283 }
284
285
286 /*
287 * Given a display pointer and screen number, determine the name of
288 * the DRI driver for the screen. (I.e. "r128", "tdfx", etc).
289 * Return True for success, False for failure.
290 */
291 static Bool GetDriverName(Display *dpy, int scrNum, char **driverName)
292 {
293 int directCapable;
294 Bool b;
295 int driverMajor, driverMinor, driverPatch;
296
297 *driverName = NULL;
298
299 if (!XF86DRIQueryDirectRenderingCapable(dpy, scrNum, &directCapable)) {
300 ErrorMessageF("XF86DRIQueryDirectRenderingCapable failed\n");
301 return False;
302 }
303 if (!directCapable) {
304 ErrorMessageF("XF86DRIQueryDirectRenderingCapable returned false\n");
305 return False;
306 }
307
308 b = XF86DRIGetClientDriverName(dpy, scrNum, &driverMajor, &driverMinor,
309 &driverPatch, driverName);
310 if (!b) {
311 ErrorMessageF("Cannot determine driver name for screen %d\n", scrNum);
312 return False;
313 }
314
315 InfoMessageF("XF86DRIGetClientDriverName: %d.%d.%d %s (screen %d)\n",
316 driverMajor, driverMinor, driverPatch, *driverName, scrNum);
317
318 return True;
319 }
320
321
322 /*
323 * Given a display pointer and screen number, return a __DRIdriver handle.
324 * Return NULL if anything goes wrong.
325 */
326 __DRIdriver *driGetDriver(Display *dpy, int scrNum)
327 {
328 char *driverName;
329 if (GetDriverName(dpy, scrNum, &driverName)) {
330 __DRIdriver *ret;
331 ret = OpenDriver(driverName);
332 if (driverName)
333 Xfree(driverName);
334 return ret;
335 }
336 return NULL;
337 }
338
339
340 /*
341 * Exported function for querying the DRI driver for a given screen.
342 *
343 * The returned char pointer points to a static array that will be
344 * overwritten by subsequent calls.
345 */
346 PUBLIC const char *glXGetScreenDriver (Display *dpy, int scrNum) {
347 static char ret[32];
348 char *driverName;
349 if (GetDriverName(dpy, scrNum, &driverName)) {
350 int len;
351 if (!driverName)
352 return NULL;
353 len = strlen (driverName);
354 if (len >= 31)
355 return NULL;
356 memcpy (ret, driverName, len+1);
357 Xfree(driverName);
358 return ret;
359 }
360 return NULL;
361 }
362
363
364 /*
365 * Exported function for obtaining a driver's option list (UTF-8 encoded XML).
366 *
367 * The returned char pointer points directly into the driver. Therefore
368 * it should be treated as a constant.
369 *
370 * If the driver was not found or does not support configuration NULL is
371 * returned.
372 *
373 * Note: The driver remains opened after this function returns.
374 */
375 PUBLIC const char *glXGetDriverConfig (const char *driverName) {
376 __DRIdriver *driver = OpenDriver (driverName);
377 if (driver)
378 return dlsym (driver->handle, "__driConfigOptions");
379 else
380 return NULL;
381 }
382
383
384 /* Called from __glXFreeDisplayPrivate.
385 */
386 static void driDestroyDisplay(Display *dpy, void *private)
387 {
388 __DRIdisplayPrivate *pdpyp = (__DRIdisplayPrivate *)private;
389
390 if (pdpyp) {
391 const int numScreens = ScreenCount(dpy);
392 int i;
393 for (i = 0; i < numScreens; i++) {
394 if (pdpyp->libraryHandles[i]) {
395 __DRIdriver *driver, *prev;
396
397 /* Remove driver from Drivers list */
398 for (prev = NULL, driver = Drivers; driver;
399 prev = driver, driver = driver->next) {
400 if (driver->handle == pdpyp->libraryHandles[i]) {
401 if (prev)
402 prev->next = driver->next;
403 else
404 Drivers = driver->next;
405
406 Xfree(driver->name);
407 Xfree(driver);
408 break;
409 }
410 }
411
412 dlclose(pdpyp->libraryHandles[i]);
413 }
414 }
415 Xfree(pdpyp->libraryHandles);
416 Xfree(pdpyp);
417 }
418 }
419
420
421 /*
422 * Allocate, initialize and return a __DRIdisplayPrivate object.
423 * This is called from __glXInitialize() when we are given a new
424 * display pointer.
425 */
426 void *driCreateDisplay(Display *dpy, __DRIdisplay *pdisp)
427 {
428 const int numScreens = ScreenCount(dpy);
429 __DRIdisplayPrivate *pdpyp;
430 int eventBase, errorBase;
431 int major, minor, patch;
432 int scrn;
433
434 /* Initialize these fields to NULL in case we fail.
435 * If we don't do this we may later get segfaults trying to free random
436 * addresses when the display is closed.
437 */
438 pdisp->private = NULL;
439 pdisp->destroyDisplay = NULL;
440
441 if (!XF86DRIQueryExtension(dpy, &eventBase, &errorBase)) {
442 return NULL;
443 }
444
445 if (!XF86DRIQueryVersion(dpy, &major, &minor, &patch)) {
446 return NULL;
447 }
448
449 pdpyp = (__DRIdisplayPrivate *)Xmalloc(sizeof(__DRIdisplayPrivate));
450 if (!pdpyp) {
451 return NULL;
452 }
453
454 pdpyp->driMajor = major;
455 pdpyp->driMinor = minor;
456 pdpyp->driPatch = patch;
457
458 pdisp->destroyDisplay = driDestroyDisplay;
459
460 /* allocate array of pointers to createNewScreen funcs */
461 pdisp->createNewScreen = (PFNCREATENEWSCREENFUNC *)
462 Xmalloc(numScreens * sizeof(void *));
463 if (!pdisp->createNewScreen) {
464 Xfree(pdpyp);
465 return NULL;
466 }
467
468 /* allocate array of library handles */
469 pdpyp->libraryHandles = (void **) Xmalloc(numScreens * sizeof(void*));
470 if (!pdpyp->libraryHandles) {
471 Xfree(pdisp->createNewScreen);
472 Xfree(pdpyp);
473 return NULL;
474 }
475
476 /* dynamically discover DRI drivers for all screens, saving each
477 * driver's "__driCreateScreen" function pointer. That's the bootstrap
478 * entrypoint for all DRI drivers.
479 */
480 for (scrn = 0; scrn < numScreens; scrn++) {
481 __DRIdriver *driver = driGetDriver(dpy, scrn);
482 if (driver) {
483 pdisp->createNewScreen[scrn] = driver->createNewScreenFunc;
484 pdpyp->libraryHandles[scrn] = driver->handle;
485 }
486 else {
487 pdisp->createNewScreen[scrn] = NULL;
488 pdpyp->libraryHandles[scrn] = NULL;
489 }
490 }
491
492 return (void *)pdpyp;
493 }
494
495 #endif /* GLX_DIRECT_RENDERING */