Merge remote branch 'upstream/gallium-0.1' into nouveau-gallium-0.1
[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
28 /*
29 * Authors:
30 * Kevin E. Martin <kevin@precisioninsight.com>
31 * Brian Paul <brian@precisioninsight.com>
32 *
33 */
34
35 #ifdef GLX_DIRECT_RENDERING
36
37 #include <unistd.h>
38 #include <X11/Xlibint.h>
39 #include <X11/extensions/Xext.h>
40 #include <X11/extensions/extutil.h>
41 #include "glheader.h"
42 #include "glxclient.h"
43 #include "xf86dri.h"
44 #include "sarea.h"
45 #include <stdio.h>
46 #include <dlfcn.h>
47 #include "dri_glx.h"
48 #include <sys/types.h>
49 #include <stdarg.h>
50
51 #ifndef RTLD_NOW
52 #define RTLD_NOW 0
53 #endif
54 #ifndef RTLD_GLOBAL
55 #define RTLD_GLOBAL 0
56 #endif
57
58
59 #ifndef DEFAULT_DRIVER_DIR
60 /* this is normally defined in Mesa/configs/default with DRI_DRIVER_SEARCH_PATH */
61 #define DEFAULT_DRIVER_DIR "/usr/X11R6/lib/modules/dri"
62 #endif
63
64 static __DRIdriver *Drivers = NULL;
65
66
67 /** If non-zero, prefer an "egl_foo_dri.so" driver over "foo_dri.so" */
68 static int PreferEGL = 0;
69
70
71 /**
72 * This may be called by libEGL.so to tell libGL to prefer "egl_" drivers
73 * over regular DRI drivers".
74 */
75 void __glXPreferEGL(int state)
76 {
77 PreferEGL = state;
78 }
79
80
81 /*
82 * printf wrappers
83 */
84
85 static void InfoMessageF(const char *f, ...)
86 {
87 va_list args;
88 const char *env;
89
90 if ((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) {
91 fprintf(stderr, "libGL: ");
92 va_start(args, f);
93 vfprintf(stderr, f, args);
94 va_end(args);
95 }
96 }
97
98 /**
99 * Print error to stderr, unless LIBGL_DEBUG=="quiet".
100 */
101 static void ErrorMessageF(const char *f, ...)
102 {
103 va_list args;
104 const char *env;
105
106 if ((env = getenv("LIBGL_DEBUG")) && !strstr(env, "quiet")) {
107 fprintf(stderr, "libGL error: ");
108 va_start(args, f);
109 vfprintf(stderr, f, args);
110 va_end(args);
111 }
112 }
113
114
115 /**
116 * Extract the ith directory path out of a colon-separated list of paths. No
117 * more than \c dirLen characters, including the terminating \c NUL, will be
118 * written to \c dir.
119 *
120 * \param index Index of path to extract (starting at zero)
121 * \param paths The colon-separated list of paths
122 * \param dirLen Maximum length of result to store in \c dir
123 * \param dir Buffer to hold the extracted directory path
124 *
125 * \returns
126 * The number of characters that would have been written to \c dir had there
127 * been enough room. This does not include the terminating \c NUL. When
128 * extraction fails, zero will be returned.
129 *
130 * \todo
131 * It seems like this function could be rewritten to use \c strchr.
132 */
133 static size_t
134 ExtractDir(int index, const char *paths, int dirLen, char *dir)
135 {
136 int i, len;
137 const char *start, *end;
138
139 /* find ith colon */
140 start = paths;
141 i = 0;
142 while (i < index) {
143 if (*start == ':') {
144 i++;
145 start++;
146 }
147 else if (*start == 0) {
148 /* end of string and couldn't find ith colon */
149 dir[0] = 0;
150 return 0;
151 }
152 else {
153 start++;
154 }
155 }
156
157 while (*start == ':')
158 start++;
159
160 /* find next colon, or end of string */
161 end = start + 1;
162 while (*end != ':' && *end != 0) {
163 end++;
164 }
165
166 /* copy string between <start> and <end> into result string */
167 len = end - start;
168 if (len > dirLen - 1)
169 len = dirLen - 1;
170 strncpy(dir, start, len);
171 dir[len] = 0;
172
173 return( end - start );
174 }
175
176
177 /**
178 * Try to dlopen() a driver.
179 * \param libDir the directory to search
180 * \param driverName the name of the driver (such as "r300").
181 * \param tls if true, try to find TLS version of driver
182 * \param preferEGL if true, try to find EGL-specific driver
183 * \return handle from dlopen(), will be NULL if fails.
184 */
185 static void *
186 try_open(const char *libDir, const char *driverName,
187 GLboolean tls, GLboolean preferEGL)
188 {
189 const char *tlsDir = tls ? "/tls/" : "";
190 const char *eglPrefix = preferEGL ? "egl_" : "";
191 char fullPathName[200];
192 void *handle = NULL;
193
194 snprintf(fullPathName, 200, "%s%s/%s%s_dri.so",
195 libDir, tlsDir, eglPrefix, driverName);
196 InfoMessageF("OpenDriver: trying %s\n", fullPathName);
197 handle = dlopen(fullPathName, RTLD_NOW | RTLD_GLOBAL);
198
199 return handle;
200 }
201
202
203
204 /**
205 * Versioned name of the expected \c __driCreateNewScreen function.
206 *
207 * The version of the last incompatible loader/driver inteface change is
208 * appended to the name of the \c __driCreateNewScreen function. This
209 * prevents loaders from trying to load drivers that are too old.
210 *
211 * \todo
212 * Create a macro or something so that this is automatically updated.
213 */
214 static const char createNewScreenName[] = "__driCreateNewScreen_20050727";
215
216
217 /**
218 * Try to \c dlopen the named driver.
219 *
220 * This function adds the "_dri.so" suffix to the driver name and searches the
221 * directories specified by the \c LIBGL_DRIVERS_PATH environment variable in
222 * order to find the driver.
223 *
224 * \param driverName - a name like "tdfx", "i810", "mga", etc.
225 *
226 * \returns
227 * A handle from \c dlopen, or \c NULL if driver file not found.
228 */
229 static __DRIdriver *OpenDriver(const char *driverName)
230 {
231 void *glhandle = NULL;
232 char *libPaths = NULL;
233 char libDir[1000];
234 int i;
235 __DRIdriver *driver;
236
237 /* First, search Drivers list to see if we've already opened this driver */
238 for (driver = Drivers; driver; driver = driver->next) {
239 if (strcmp(driver->name, driverName) == 0) {
240 /* found it */
241 return driver;
242 }
243 }
244
245 /* Attempt to make sure libGL symbols will be visible to the driver */
246 glhandle = dlopen("libGL.so.1", RTLD_NOW | RTLD_GLOBAL);
247
248 if (geteuid() == getuid()) {
249 /* don't allow setuid apps to use LIBGL_DRIVERS_PATH */
250 libPaths = getenv("LIBGL_DRIVERS_PATH");
251 if (!libPaths)
252 libPaths = getenv("LIBGL_DRIVERS_DIR"); /* deprecated */
253 }
254 if (!libPaths)
255 libPaths = DEFAULT_DRIVER_DIR;
256
257 for ( i = 0 ; ExtractDir(i, libPaths, 1000, libDir) != 0 ; i++ ) {
258 void *handle = NULL;
259
260 if (PreferEGL)
261 handle = try_open(libDir, driverName, GL_FALSE, GL_TRUE);
262 #ifdef GLX_USE_TLS
263 if (!handle)
264 handle = try_open(libDir, driverName, GL_TRUE, GL_FALSE);
265 #endif
266 if (!handle)
267 handle = try_open(libDir, driverName, GL_FALSE, GL_FALSE);
268
269 if ( handle != NULL ) {
270 /* allocate __DRIdriver struct */
271 driver = (__DRIdriver *) Xmalloc(sizeof(__DRIdriver));
272 if (!driver)
273 break; /* out of memory! */
274 /* init the struct */
275 driver->name = __glXstrdup(driverName);
276 if (!driver->name) {
277 Xfree(driver);
278 driver = NULL;
279 break; /* out of memory! */
280 }
281
282 driver->createNewScreenFunc = (PFNCREATENEWSCREENFUNC)
283 dlsym(handle, createNewScreenName);
284
285 if ( driver->createNewScreenFunc == NULL ) {
286 /* If the driver doesn't have this symbol then something's
287 * really, really wrong.
288 */
289 ErrorMessageF("%s not defined in %s_dri.so!\n"
290 "Your driver may be too old for this libGL.\n",
291 createNewScreenName, driverName);
292 Xfree(driver);
293 driver = NULL;
294 dlclose(handle);
295 continue;
296 }
297 driver->handle = handle;
298 /* put at head of linked list */
299 driver->next = Drivers;
300 Drivers = driver;
301 break;
302 }
303 else {
304 ErrorMessageF("Unable to find/open driver for %s (%s)\n",
305 driverName, dlerror());
306 }
307 }
308
309 if (!driver)
310 ErrorMessageF("unable to load driver: %s_dri.so\n", driverName);
311
312 if (glhandle)
313 dlclose(glhandle);
314
315 return driver;
316 }
317
318
319 /*
320 * Given a display pointer and screen number, determine the name of
321 * the DRI driver for the screen. (I.e. "r128", "tdfx", etc).
322 * Return True for success, False for failure.
323 */
324 static Bool GetDriverName(Display *dpy, int scrNum, char **driverName)
325 {
326 int directCapable;
327 Bool b;
328 int driverMajor, driverMinor, driverPatch;
329
330 *driverName = NULL;
331
332 if (!XF86DRIQueryDirectRenderingCapable(dpy, scrNum, &directCapable)) {
333 ErrorMessageF("XF86DRIQueryDirectRenderingCapable failed\n");
334 return False;
335 }
336 if (!directCapable) {
337 ErrorMessageF("XF86DRIQueryDirectRenderingCapable returned false\n");
338 return False;
339 }
340
341 b = XF86DRIGetClientDriverName(dpy, scrNum, &driverMajor, &driverMinor,
342 &driverPatch, driverName);
343 if (!b) {
344 ErrorMessageF("Cannot determine driver name for screen %d\n", scrNum);
345 return False;
346 }
347
348 InfoMessageF("XF86DRIGetClientDriverName: %d.%d.%d %s (screen %d)\n",
349 driverMajor, driverMinor, driverPatch, *driverName, scrNum);
350
351 return True;
352 }
353
354
355 /*
356 * Given a display pointer and screen number, return a __DRIdriver handle.
357 * Return NULL if anything goes wrong.
358 */
359 __DRIdriver *driGetDriver(Display *dpy, int scrNum)
360 {
361 char *driverName;
362 if (GetDriverName(dpy, scrNum, &driverName)) {
363 __DRIdriver *ret;
364 ret = OpenDriver(driverName);
365 if (driverName)
366 Xfree(driverName);
367 return ret;
368 }
369 return NULL;
370 }
371
372
373 /*
374 * Exported function for querying the DRI driver for a given screen.
375 *
376 * The returned char pointer points to a static array that will be
377 * overwritten by subsequent calls.
378 */
379 PUBLIC const char *glXGetScreenDriver (Display *dpy, int scrNum) {
380 static char ret[32];
381 char *driverName;
382 if (GetDriverName(dpy, scrNum, &driverName)) {
383 int len;
384 if (!driverName)
385 return NULL;
386 len = strlen (driverName);
387 if (len >= 31)
388 return NULL;
389 memcpy (ret, driverName, len+1);
390 Xfree(driverName);
391 return ret;
392 }
393 return NULL;
394 }
395
396
397 /*
398 * Exported function for obtaining a driver's option list (UTF-8 encoded XML).
399 *
400 * The returned char pointer points directly into the driver. Therefore
401 * it should be treated as a constant.
402 *
403 * If the driver was not found or does not support configuration NULL is
404 * returned.
405 *
406 * Note: The driver remains opened after this function returns.
407 */
408 PUBLIC const char *glXGetDriverConfig (const char *driverName) {
409 __DRIdriver *driver = OpenDriver (driverName);
410 if (driver)
411 return dlsym (driver->handle, "__driConfigOptions");
412 else
413 return NULL;
414 }
415
416
417 /* Called from __glXFreeDisplayPrivate.
418 */
419 static void driDestroyDisplay(Display *dpy, void *private)
420 {
421 __DRIdisplayPrivate *pdpyp = (__DRIdisplayPrivate *)private;
422
423 if (pdpyp) {
424 const int numScreens = ScreenCount(dpy);
425 int i;
426 for (i = 0; i < numScreens; i++) {
427 if (pdpyp->libraryHandles[i]) {
428 __DRIdriver *driver, *prev;
429
430 /* Remove driver from Drivers list */
431 for (prev = NULL, driver = Drivers; driver;
432 prev = driver, driver = driver->next) {
433 if (driver->handle == pdpyp->libraryHandles[i]) {
434 if (prev)
435 prev->next = driver->next;
436 else
437 Drivers = driver->next;
438
439 Xfree((void *) driver->name);
440 Xfree(driver);
441 break;
442 }
443 }
444
445 dlclose(pdpyp->libraryHandles[i]);
446 }
447 }
448 Xfree(pdpyp->libraryHandles);
449 Xfree(pdpyp);
450 }
451 }
452
453
454 /*
455 * Allocate, initialize and return a __DRIdisplayPrivate object.
456 * This is called from __glXInitialize() when we are given a new
457 * display pointer.
458 */
459 void *driCreateDisplay(Display *dpy, __DRIdisplay *pdisp)
460 {
461 const int numScreens = ScreenCount(dpy);
462 __DRIdisplayPrivate *pdpyp;
463 int eventBase, errorBase;
464 int major, minor, patch;
465 int scrn;
466
467 /* Initialize these fields to NULL in case we fail.
468 * If we don't do this we may later get segfaults trying to free random
469 * addresses when the display is closed.
470 */
471 pdisp->private = NULL;
472 pdisp->destroyDisplay = NULL;
473
474 if (!XF86DRIQueryExtension(dpy, &eventBase, &errorBase)) {
475 return NULL;
476 }
477
478 if (!XF86DRIQueryVersion(dpy, &major, &minor, &patch)) {
479 return NULL;
480 }
481
482 pdpyp = (__DRIdisplayPrivate *)Xmalloc(sizeof(__DRIdisplayPrivate));
483 if (!pdpyp) {
484 return NULL;
485 }
486
487 pdpyp->driMajor = major;
488 pdpyp->driMinor = minor;
489 pdpyp->driPatch = patch;
490
491 pdisp->destroyDisplay = driDestroyDisplay;
492
493 /* allocate array of pointers to createNewScreen funcs */
494 pdisp->createNewScreen = (PFNCREATENEWSCREENFUNC *)
495 Xmalloc(numScreens * sizeof(void *));
496 if (!pdisp->createNewScreen) {
497 Xfree(pdpyp);
498 return NULL;
499 }
500
501 /* allocate array of library handles */
502 pdpyp->libraryHandles = (void **) Xmalloc(numScreens * sizeof(void*));
503 if (!pdpyp->libraryHandles) {
504 Xfree(pdisp->createNewScreen);
505 Xfree(pdpyp);
506 return NULL;
507 }
508
509 /* dynamically discover DRI drivers for all screens, saving each
510 * driver's "__driCreateScreen" function pointer. That's the bootstrap
511 * entrypoint for all DRI drivers.
512 */
513 for (scrn = 0; scrn < numScreens; scrn++) {
514 __DRIdriver *driver = driGetDriver(dpy, scrn);
515 if (driver) {
516 pdisp->createNewScreen[scrn] = driver->createNewScreenFunc;
517 pdpyp->libraryHandles[scrn] = driver->handle;
518 }
519 else {
520 pdisp->createNewScreen[scrn] = NULL;
521 pdpyp->libraryHandles[scrn] = NULL;
522 }
523 }
524
525 return (void *)pdpyp;
526 }
527
528 #endif /* GLX_DIRECT_RENDERING */