glxdri2.c revision 52397711
1/*
2 * Copyright © 2007 Red Hat, Inc
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of Red Hat,
9 * Inc not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior
11 * permission.  Red Hat, Inc makes no representations about the
12 * suitability of this software for any purpose.  It is provided "as
13 * is" without express or implied warranty.
14 *
15 * RED HAT, INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL RED HAT, INC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#ifdef HAVE_DIX_CONFIG_H
25#include <dix-config.h>
26#endif
27
28#include <stdint.h>
29#include <stdio.h>
30#include <string.h>
31#include <errno.h>
32#include <dlfcn.h>
33
34#include <drm.h>
35#include <GL/gl.h>
36#include <GL/internal/dri_interface.h>
37#include <GL/glxtokens.h>
38
39#include <windowstr.h>
40#include <os.h>
41
42#define _XF86DRI_SERVER_
43#include <xf86drm.h>
44#include <xf86.h>
45#include <dri2.h>
46
47#include "glxserver.h"
48#include "glxutil.h"
49#include "glxdricommon.h"
50
51#include "g_disptab.h"
52#include "glapitable.h"
53#include "glapi.h"
54#include "glthread.h"
55#include "dispatch.h"
56#include "extension_string.h"
57
58typedef struct __GLXDRIscreen   __GLXDRIscreen;
59typedef struct __GLXDRIcontext  __GLXDRIcontext;
60typedef struct __GLXDRIdrawable __GLXDRIdrawable;
61
62struct __GLXDRIscreen {
63    __GLXscreen		 base;
64    __DRIscreen		*driScreen;
65    void		*driver;
66    int			 fd;
67
68    xf86EnterVTProc	*enterVT;
69    xf86LeaveVTProc	*leaveVT;
70
71    const __DRIcoreExtension *core;
72    const __DRIdri2Extension *dri2;
73    const __DRIcopySubBufferExtension *copySubBuffer;
74    const __DRIswapControlExtension *swapControl;
75    const __DRItexBufferExtension *texBuffer;
76
77    unsigned char glx_enable_bits[__GLX_EXT_BYTES];
78};
79
80struct __GLXDRIcontext {
81    __GLXcontext	 base;
82    __DRIcontext	*driContext;
83};
84
85#define MAX_DRAWABLE_BUFFERS 5
86
87struct __GLXDRIdrawable {
88    __GLXdrawable	 base;
89    __DRIdrawable	*driDrawable;
90    __GLXDRIscreen	*screen;
91
92    /* Dimensions as last reported by DRI2GetBuffers. */
93    int width;
94    int height;
95    __DRIbuffer buffers[MAX_DRAWABLE_BUFFERS];
96    int count;
97};
98
99static void
100__glXDRIdrawableDestroy(__GLXdrawable *drawable)
101{
102    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
103    const __DRIcoreExtension *core = private->screen->core;
104
105    (*core->destroyDrawable)(private->driDrawable);
106
107    /* If the X window was destroyed, the dri DestroyWindow hook will
108     * aready have taken care of this, so only call if pDraw isn't NULL. */
109    if (drawable->pDraw != NULL)
110	DRI2DestroyDrawable(drawable->pDraw);
111
112    __glXDrawableRelease(drawable);
113
114    xfree(private);
115}
116
117static void
118__glXDRIdrawableCopySubBuffer(__GLXdrawable *drawable,
119			       int x, int y, int w, int h)
120{
121    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
122    BoxRec box;
123    RegionRec region;
124
125    box.x1 = x;
126    box.y1 = private->height - y - h;
127    box.x2 = x + w;
128    box.y2 = private->height - y;
129    REGION_INIT(drawable->pDraw->pScreen, &region, &box, 0);
130
131    DRI2CopyRegion(drawable->pDraw, &region,
132		   DRI2BufferFrontLeft, DRI2BufferBackLeft);
133}
134
135static GLboolean
136__glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
137{
138    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
139
140    __glXDRIdrawableCopySubBuffer(drawable, 0, 0,
141				  private->width, private->height);
142
143    return TRUE;
144}
145
146static void
147__glXDRIdrawableWaitX(__GLXdrawable *drawable)
148{
149    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
150    BoxRec box;
151    RegionRec region;
152
153    box.x1 = 0;
154    box.y1 = 0;
155    box.x2 = private->width;
156    box.y2 = private->height;
157    REGION_INIT(drawable->pDraw->pScreen, &region, &box, 0);
158
159    DRI2CopyRegion(drawable->pDraw, &region,
160		   DRI2BufferFakeFrontLeft, DRI2BufferFrontLeft);
161}
162
163static void
164__glXDRIdrawableWaitGL(__GLXdrawable *drawable)
165{
166    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
167    BoxRec box;
168    RegionRec region;
169
170    box.x1 = 0;
171    box.y1 = 0;
172    box.x2 = private->width;
173    box.y2 = private->height;
174    REGION_INIT(drawable->pDraw->pScreen, &region, &box, 0);
175
176    DRI2CopyRegion(drawable->pDraw, &region,
177		   DRI2BufferFrontLeft, DRI2BufferFakeFrontLeft);
178}
179
180static int
181__glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval)
182{
183    return 0;
184}
185
186static void
187__glXDRIcontextDestroy(__GLXcontext *baseContext)
188{
189    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
190    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
191
192    (*screen->core->destroyContext)(context->driContext);
193    __glXContextDestroy(&context->base);
194    xfree(context);
195}
196
197static int
198__glXDRIcontextMakeCurrent(__GLXcontext *baseContext)
199{
200    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
201    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
202    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
203    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
204
205    return (*screen->core->bindContext)(context->driContext,
206					draw->driDrawable,
207					read->driDrawable);
208}
209
210static int
211__glXDRIcontextLoseCurrent(__GLXcontext *baseContext)
212{
213    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
214    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
215
216    return (*screen->core->unbindContext)(context->driContext);
217}
218
219static int
220__glXDRIcontextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc,
221		    unsigned long mask)
222{
223    __GLXDRIcontext *dst = (__GLXDRIcontext *) baseDst;
224    __GLXDRIcontext *src = (__GLXDRIcontext *) baseSrc;
225    __GLXDRIscreen *screen = (__GLXDRIscreen *) dst->base.pGlxScreen;
226
227    return (*screen->core->copyContext)(dst->driContext,
228					src->driContext, mask);
229}
230
231static int
232__glXDRIcontextForceCurrent(__GLXcontext *baseContext)
233{
234    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
235    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
236    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
237    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
238
239    return (*screen->core->bindContext)(context->driContext,
240					draw->driDrawable,
241					read->driDrawable);
242}
243
244#ifdef __DRI_TEX_BUFFER
245
246static int
247__glXDRIbindTexImage(__GLXcontext *baseContext,
248		     int buffer,
249		     __GLXdrawable *glxPixmap)
250{
251    __GLXDRIdrawable *drawable = (__GLXDRIdrawable *) glxPixmap;
252    const __DRItexBufferExtension *texBuffer = drawable->screen->texBuffer;
253    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
254
255    if (texBuffer == NULL)
256        return Success;
257
258#if __DRI_TEX_BUFFER_VERSION >= 2
259    if (texBuffer->base.version >= 2 && texBuffer->setTexBuffer2 != NULL) {
260	(*texBuffer->setTexBuffer2)(context->driContext,
261				    glxPixmap->target,
262				    glxPixmap->format,
263				    drawable->driDrawable);
264    } else
265#endif
266    {
267	texBuffer->setTexBuffer(context->driContext,
268				glxPixmap->target,
269				drawable->driDrawable);
270    }
271
272    return Success;
273}
274
275static int
276__glXDRIreleaseTexImage(__GLXcontext *baseContext,
277			int buffer,
278			__GLXdrawable *pixmap)
279{
280    /* FIXME: Just unbind the texture? */
281    return Success;
282}
283
284#else
285
286static int
287__glXDRIbindTexImage(__GLXcontext *baseContext,
288		     int buffer,
289		     __GLXdrawable *glxPixmap)
290{
291    return Success;
292}
293
294static int
295__glXDRIreleaseTexImage(__GLXcontext *baseContext,
296			int buffer,
297			__GLXdrawable *pixmap)
298{
299    return Success;
300}
301
302#endif
303
304static __GLXtextureFromPixmap __glXDRItextureFromPixmap = {
305    __glXDRIbindTexImage,
306    __glXDRIreleaseTexImage
307};
308
309static void
310__glXDRIscreenDestroy(__GLXscreen *baseScreen)
311{
312    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
313
314    (*screen->core->destroyScreen)(screen->driScreen);
315
316    dlclose(screen->driver);
317
318    __glXScreenDestroy(baseScreen);
319
320    xfree(screen);
321}
322
323static __GLXcontext *
324__glXDRIscreenCreateContext(__GLXscreen *baseScreen,
325			    __GLXconfig *glxConfig,
326			    __GLXcontext *baseShareContext)
327{
328    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
329    __GLXDRIcontext *context, *shareContext;
330    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
331    __DRIcontext *driShare;
332
333    shareContext = (__GLXDRIcontext *) baseShareContext;
334    if (shareContext)
335	driShare = shareContext->driContext;
336    else
337	driShare = NULL;
338
339    context = xcalloc(1, sizeof *context);
340    if (context == NULL)
341	return NULL;
342
343    context->base.destroy           = __glXDRIcontextDestroy;
344    context->base.makeCurrent       = __glXDRIcontextMakeCurrent;
345    context->base.loseCurrent       = __glXDRIcontextLoseCurrent;
346    context->base.copy              = __glXDRIcontextCopy;
347    context->base.forceCurrent      = __glXDRIcontextForceCurrent;
348    context->base.textureFromPixmap = &__glXDRItextureFromPixmap;
349
350    context->driContext =
351	(*screen->dri2->createNewContext)(screen->driScreen,
352					  config->driConfig,
353					  driShare, context);
354    if (context->driContext == NULL) {
355	    xfree(context);
356        return NULL;
357    }
358
359    return &context->base;
360}
361
362static __GLXdrawable *
363__glXDRIscreenCreateDrawable(__GLXscreen *screen,
364			     DrawablePtr pDraw,
365			     int type,
366			     XID drawId,
367			     __GLXconfig *glxConfig)
368{
369    __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
370    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
371    __GLXDRIdrawable *private;
372
373    private = xcalloc(1, sizeof *private);
374    if (private == NULL)
375	return NULL;
376
377    private->screen = driScreen;
378    if (!__glXDrawableInit(&private->base, screen,
379			   pDraw, type, drawId, glxConfig)) {
380        xfree(private);
381	return NULL;
382    }
383
384    private->base.destroy       = __glXDRIdrawableDestroy;
385    private->base.swapBuffers   = __glXDRIdrawableSwapBuffers;
386    private->base.copySubBuffer = __glXDRIdrawableCopySubBuffer;
387    private->base.waitGL	= __glXDRIdrawableWaitGL;
388    private->base.waitX		= __glXDRIdrawableWaitX;
389
390    if (DRI2CreateDrawable(pDraw)) {
391	    xfree(private);
392	    return NULL;
393    }
394
395    private->driDrawable =
396	(*driScreen->dri2->createNewDrawable)(driScreen->driScreen,
397					      config->driConfig, private);
398
399    return &private->base;
400}
401
402static __DRIbuffer *
403dri2GetBuffers(__DRIdrawable *driDrawable,
404	       int *width, int *height,
405	       unsigned int *attachments, int count,
406	       int *out_count, void *loaderPrivate)
407{
408    __GLXDRIdrawable *private = loaderPrivate;
409    DRI2BufferPtr *buffers;
410    int i;
411    int j;
412
413    buffers = DRI2GetBuffers(private->base.pDraw,
414			     width, height, attachments, count, out_count);
415    if (*out_count > MAX_DRAWABLE_BUFFERS) {
416	*out_count = 0;
417	return NULL;
418    }
419
420    private->width = *width;
421    private->height = *height;
422
423    /* This assumes the DRI2 buffer attachment tokens matches the
424     * __DRIbuffer tokens. */
425    j = 0;
426    for (i = 0; i < *out_count; i++) {
427	/* Do not send the real front buffer of a window to the client.
428	 */
429	if ((private->base.pDraw->type == DRAWABLE_WINDOW)
430	    && (buffers[i]->attachment == DRI2BufferFrontLeft)) {
431	    continue;
432	}
433
434	private->buffers[j].attachment = buffers[i]->attachment;
435	private->buffers[j].name = buffers[i]->name;
436	private->buffers[j].pitch = buffers[i]->pitch;
437	private->buffers[j].cpp = buffers[i]->cpp;
438	private->buffers[j].flags = buffers[i]->flags;
439	j++;
440    }
441
442    *out_count = j;
443    return private->buffers;
444}
445
446static __DRIbuffer *
447dri2GetBuffersWithFormat(__DRIdrawable *driDrawable,
448			 int *width, int *height,
449			 unsigned int *attachments, int count,
450			 int *out_count, void *loaderPrivate)
451{
452    __GLXDRIdrawable *private = loaderPrivate;
453    DRI2BufferPtr *buffers;
454    int i;
455    int j = 0;
456
457    buffers = DRI2GetBuffersWithFormat(private->base.pDraw,
458				       width, height, attachments, count,
459				       out_count);
460    if (*out_count > MAX_DRAWABLE_BUFFERS) {
461	*out_count = 0;
462	return NULL;
463    }
464
465    private->width = *width;
466    private->height = *height;
467
468    /* This assumes the DRI2 buffer attachment tokens matches the
469     * __DRIbuffer tokens. */
470    for (i = 0; i < *out_count; i++) {
471	/* Do not send the real front buffer of a window to the client.
472	 */
473	if ((private->base.pDraw->type == DRAWABLE_WINDOW)
474	    && (buffers[i]->attachment == DRI2BufferFrontLeft)) {
475	    continue;
476	}
477
478	private->buffers[j].attachment = buffers[i]->attachment;
479	private->buffers[j].name = buffers[i]->name;
480	private->buffers[j].pitch = buffers[i]->pitch;
481	private->buffers[j].cpp = buffers[i]->cpp;
482	private->buffers[j].flags = buffers[i]->flags;
483	j++;
484    }
485
486    *out_count = j;
487    return private->buffers;
488}
489
490static void
491dri2FlushFrontBuffer(__DRIdrawable *driDrawable, void *loaderPrivate)
492{
493    (void) driDrawable;
494    __glXDRIdrawableWaitGL((__GLXdrawable *) loaderPrivate);
495}
496
497static const __DRIdri2LoaderExtension loaderExtension = {
498    { __DRI_DRI2_LOADER, __DRI_DRI2_LOADER_VERSION },
499    dri2GetBuffers,
500    dri2FlushFrontBuffer,
501    dri2GetBuffersWithFormat,
502};
503
504static const __DRIextension *loader_extensions[] = {
505    &systemTimeExtension.base,
506    &loaderExtension.base,
507    NULL
508};
509
510static const char dri_driver_path[] = DRI_DRIVER_PATH;
511
512static Bool
513glxDRIEnterVT (int index, int flags)
514{
515    __GLXDRIscreen *screen = (__GLXDRIscreen *)
516	glxGetScreen(screenInfo.screens[index]);
517
518    LogMessage(X_INFO, "AIGLX: Resuming AIGLX clients after VT switch\n");
519
520    if (!(*screen->enterVT) (index, flags))
521	return FALSE;
522
523    glxResumeClients();
524
525    return TRUE;
526}
527
528static void
529glxDRILeaveVT (int index, int flags)
530{
531    __GLXDRIscreen *screen = (__GLXDRIscreen *)
532	glxGetScreen(screenInfo.screens[index]);
533
534    LogMessage(X_INFO, "AIGLX: Suspending AIGLX clients for VT switch\n");
535
536    glxSuspendClients();
537
538    return (*screen->leaveVT) (index, flags);
539}
540
541static void
542initializeExtensions(__GLXDRIscreen *screen)
543{
544    const __DRIextension **extensions;
545    int i;
546
547    extensions = screen->core->getExtensions(screen->driScreen);
548
549    __glXEnableExtension(screen->glx_enable_bits,
550			 "GLX_MESA_copy_sub_buffer");
551    LogMessage(X_INFO, "AIGLX: enabled GLX_MESA_copy_sub_buffer\n");
552
553    for (i = 0; extensions[i]; i++) {
554#ifdef __DRI_SWAP_CONTROL
555	if (strcmp(extensions[i]->name, __DRI_SWAP_CONTROL) == 0) {
556	    screen->swapControl =
557		(const __DRIswapControlExtension *) extensions[i];
558	    __glXEnableExtension(screen->glx_enable_bits,
559				 "GLX_SGI_swap_control");
560	    __glXEnableExtension(screen->glx_enable_bits,
561				 "GLX_MESA_swap_control");
562
563	    LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_swap_control and GLX_MESA_swap_control\n");
564	}
565#endif
566
567#ifdef __DRI_TEX_BUFFER
568	if (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0) {
569	    screen->texBuffer =
570		(const __DRItexBufferExtension *) extensions[i];
571	    /* GLX_EXT_texture_from_pixmap is always enabled. */
572	    LogMessage(X_INFO, "AIGLX: GLX_EXT_texture_from_pixmap backed by buffer objects\n");
573	}
574#endif
575	/* Ignore unknown extensions */
576    }
577}
578
579static __GLXscreen *
580__glXDRIscreenProbe(ScreenPtr pScreen)
581{
582    const char *driverName, *deviceName;
583    __GLXDRIscreen *screen;
584    char filename[128];
585    size_t buffer_size;
586    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
587    const __DRIextension **extensions;
588    const __DRIconfig **driConfigs;
589    int i;
590
591    screen = xcalloc(1, sizeof *screen);
592    if (screen == NULL)
593	return NULL;
594
595    if (!xf86LoaderCheckSymbol("DRI2Connect") ||
596	!DRI2Connect(pScreen, DRI2DriverDRI,
597		     &screen->fd, &driverName, &deviceName)) {
598	LogMessage(X_INFO,
599		   "AIGLX: Screen %d is not DRI2 capable\n", pScreen->myNum);
600	return NULL;
601    }
602
603    screen->base.destroy        = __glXDRIscreenDestroy;
604    screen->base.createContext  = __glXDRIscreenCreateContext;
605    screen->base.createDrawable = __glXDRIscreenCreateDrawable;
606    screen->base.swapInterval   = __glXDRIdrawableSwapInterval;
607    screen->base.pScreen       = pScreen;
608
609    __glXInitExtensionEnableBits(screen->glx_enable_bits);
610
611    snprintf(filename, sizeof filename,
612	     "%s/%s_dri.so", dri_driver_path, driverName);
613
614    screen->driver = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
615    if (screen->driver == NULL) {
616	LogMessage(X_ERROR, "AIGLX error: dlopen of %s failed (%s)\n",
617		   filename, dlerror());
618        goto handle_error;
619    }
620
621    extensions = dlsym(screen->driver, __DRI_DRIVER_EXTENSIONS);
622    if (extensions == NULL) {
623	LogMessage(X_ERROR, "AIGLX error: %s exports no extensions (%s)\n",
624		   driverName, dlerror());
625	goto handle_error;
626    }
627
628    for (i = 0; extensions[i]; i++) {
629        if (strcmp(extensions[i]->name, __DRI_CORE) == 0 &&
630	    extensions[i]->version >= __DRI_CORE_VERSION) {
631		screen->core = (const __DRIcoreExtension *) extensions[i];
632	}
633        if (strcmp(extensions[i]->name, __DRI_DRI2) == 0 &&
634	    extensions[i]->version >= __DRI_DRI2_VERSION) {
635		screen->dri2 = (const __DRIdri2Extension *) extensions[i];
636	}
637    }
638
639    if (screen->core == NULL || screen->dri2 == NULL) {
640	LogMessage(X_ERROR, "AIGLX error: %s exports no DRI extension\n",
641		   driverName);
642	goto handle_error;
643    }
644
645    screen->driScreen =
646	(*screen->dri2->createNewScreen)(pScreen->myNum,
647					 screen->fd,
648					 loader_extensions,
649					 &driConfigs,
650					 screen);
651
652    if (screen->driScreen == NULL) {
653	LogMessage(X_ERROR,
654		   "AIGLX error: Calling driver entry point failed\n");
655	goto handle_error;
656    }
657
658    initializeExtensions(screen);
659
660    screen->base.fbconfigs = glxConvertConfigs(screen->core, driConfigs);
661
662    __glXScreenInit(&screen->base, pScreen);
663
664    buffer_size = __glXGetExtensionString(screen->glx_enable_bits, NULL);
665    if (buffer_size > 0) {
666	if (screen->base.GLXextensions != NULL) {
667	    xfree(screen->base.GLXextensions);
668	}
669
670	screen->base.GLXextensions = xnfalloc(buffer_size);
671	(void) __glXGetExtensionString(screen->glx_enable_bits,
672				       screen->base.GLXextensions);
673    }
674
675    screen->enterVT = pScrn->EnterVT;
676    pScrn->EnterVT = glxDRIEnterVT;
677    screen->leaveVT = pScrn->LeaveVT;
678    pScrn->LeaveVT = glxDRILeaveVT;
679
680    LogMessage(X_INFO,
681	       "AIGLX: Loaded and initialized %s\n", filename);
682
683    return &screen->base;
684
685 handle_error:
686    if (screen->driver)
687        dlclose(screen->driver);
688
689    xfree(screen);
690
691    LogMessage(X_ERROR, "AIGLX: reverting to software rendering\n");
692
693    return NULL;
694}
695
696__GLXprovider __glXDRI2Provider = {
697    __glXDRIscreenProbe,
698    "DRI2",
699    NULL
700};
701