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