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 "glapitable.h"
52#include "glapi.h"
53#include "glthread.h"
54#include "dispatch.h"
55#include "extension_string.h"
56
57typedef struct __GLXDRIscreen   __GLXDRIscreen;
58typedef struct __GLXDRIcontext  __GLXDRIcontext;
59typedef struct __GLXDRIdrawable __GLXDRIdrawable;
60
61struct __GLXDRIscreen {
62    __GLXscreen		 base;
63    __DRIscreen		*driScreen;
64    void		*driver;
65    int			 fd;
66
67    xf86EnterVTProc	*enterVT;
68    xf86LeaveVTProc	*leaveVT;
69
70    const __DRIcoreExtension *core;
71    const __DRIdri2Extension *dri2;
72    const __DRI2flushExtension *flush;
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    __glXDrawableRelease(drawable);
108
109    free(private);
110}
111
112static void
113__glXDRIdrawableCopySubBuffer(__GLXdrawable *drawable,
114			       int x, int y, int w, int h)
115{
116    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
117    BoxRec box;
118    RegionRec region;
119
120    box.x1 = x;
121    box.y1 = private->height - y - h;
122    box.x2 = x + w;
123    box.y2 = private->height - y;
124    RegionInit(&region, &box, 0);
125
126    DRI2CopyRegion(drawable->pDraw, &region,
127		   DRI2BufferFrontLeft, DRI2BufferBackLeft);
128}
129
130static void
131__glXDRIdrawableWaitX(__GLXdrawable *drawable)
132{
133    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
134    BoxRec box;
135    RegionRec region;
136
137    box.x1 = 0;
138    box.y1 = 0;
139    box.x2 = private->width;
140    box.y2 = private->height;
141    RegionInit(&region, &box, 0);
142
143    DRI2CopyRegion(drawable->pDraw, &region,
144		   DRI2BufferFakeFrontLeft, DRI2BufferFrontLeft);
145}
146
147static void
148__glXDRIdrawableWaitGL(__GLXdrawable *drawable)
149{
150    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
151    BoxRec box;
152    RegionRec region;
153
154    box.x1 = 0;
155    box.y1 = 0;
156    box.x2 = private->width;
157    box.y2 = private->height;
158    RegionInit(&region, &box, 0);
159
160    DRI2CopyRegion(drawable->pDraw, &region,
161		   DRI2BufferFrontLeft, DRI2BufferFakeFrontLeft);
162}
163
164static void
165__glXdriSwapEvent(ClientPtr client, void *data, int type, CARD64 ust,
166		  CARD64 msc, CARD64 sbc)
167{
168    __GLXdrawable *drawable = data;
169    xGLXBufferSwapComplete wire;
170
171    if (!(drawable->eventMask & GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK))
172	return;
173
174    wire.type = __glXEventBase + GLX_BufferSwapComplete;
175    switch (type) {
176    case DRI2_EXCHANGE_COMPLETE:
177	wire.event_type = GLX_EXCHANGE_COMPLETE_INTEL;
178	break;
179    case DRI2_BLIT_COMPLETE:
180	wire.event_type = GLX_BLIT_COMPLETE_INTEL;
181	break;
182    case DRI2_FLIP_COMPLETE:
183	wire.event_type = GLX_FLIP_COMPLETE_INTEL;
184	break;
185    default:
186	/* unknown swap completion type */
187	wire.event_type = 0;
188	break;
189    }
190    wire.drawable = drawable->drawId;
191    wire.ust_hi = ust >> 32;
192    wire.ust_lo = ust & 0xffffffff;
193    wire.msc_hi = msc >> 32;
194    wire.msc_lo = msc & 0xffffffff;
195    wire.sbc_hi = sbc >> 32;
196    wire.sbc_lo = sbc & 0xffffffff;
197
198    WriteEventsToClient(client, 1, (xEvent *) &wire);
199}
200
201/*
202 * Copy or flip back to front, honoring the swap interval if possible.
203 *
204 * If the kernel supports it, we request an event for the frame when the
205 * swap should happen, then perform the copy when we receive it.
206 */
207static GLboolean
208__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable *drawable)
209{
210    __GLXDRIdrawable *priv = (__GLXDRIdrawable *) drawable;
211    __GLXDRIscreen *screen = priv->screen;
212    CARD64 unused;
213
214#if __DRI2_FLUSH_VERSION >= 3
215    if (screen->flush) {
216	(*screen->flush->flush)(priv->driDrawable);
217	(*screen->flush->invalidate)(priv->driDrawable);
218    }
219#else
220    if (screen->flush)
221	(*screen->flush->flushInvalidate)(priv->driDrawable);
222#endif
223
224    if (DRI2SwapBuffers(client, drawable->pDraw, 0, 0, 0, &unused,
225			__glXdriSwapEvent, drawable->pDraw) != Success)
226	return FALSE;
227
228    return TRUE;
229}
230
231static int
232__glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval)
233{
234    if (interval <= 0) /* || interval > BIGNUM? */
235	return GLX_BAD_VALUE;
236
237    DRI2SwapInterval(drawable->pDraw, interval);
238
239    return 0;
240}
241
242static void
243__glXDRIcontextDestroy(__GLXcontext *baseContext)
244{
245    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
246    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
247
248    (*screen->core->destroyContext)(context->driContext);
249    __glXContextDestroy(&context->base);
250    free(context);
251}
252
253static int
254__glXDRIcontextMakeCurrent(__GLXcontext *baseContext)
255{
256    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
257    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
258    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
259    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
260
261    return (*screen->core->bindContext)(context->driContext,
262					draw->driDrawable,
263					read->driDrawable);
264}
265
266static int
267__glXDRIcontextLoseCurrent(__GLXcontext *baseContext)
268{
269    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
270    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
271
272    return (*screen->core->unbindContext)(context->driContext);
273}
274
275static int
276__glXDRIcontextCopy(__GLXcontext *baseDst, __GLXcontext *baseSrc,
277		    unsigned long mask)
278{
279    __GLXDRIcontext *dst = (__GLXDRIcontext *) baseDst;
280    __GLXDRIcontext *src = (__GLXDRIcontext *) baseSrc;
281    __GLXDRIscreen *screen = (__GLXDRIscreen *) dst->base.pGlxScreen;
282
283    return (*screen->core->copyContext)(dst->driContext,
284					src->driContext, mask);
285}
286
287static int
288__glXDRIcontextForceCurrent(__GLXcontext *baseContext)
289{
290    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
291    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) baseContext->drawPriv;
292    __GLXDRIdrawable *read = (__GLXDRIdrawable *) baseContext->readPriv;
293    __GLXDRIscreen *screen = (__GLXDRIscreen *) context->base.pGlxScreen;
294
295    return (*screen->core->bindContext)(context->driContext,
296					draw->driDrawable,
297					read->driDrawable);
298}
299
300static Bool
301__glXDRIcontextWait(__GLXcontext *baseContext,
302		    __GLXclientState *cl, int *error)
303{
304    if (DRI2WaitSwap(cl->client, baseContext->drawPriv->pDraw)) {
305	*error = cl->client->noClientException;
306	return TRUE;
307    }
308
309    return FALSE;
310}
311
312#ifdef __DRI_TEX_BUFFER
313
314static int
315__glXDRIbindTexImage(__GLXcontext *baseContext,
316		     int buffer,
317		     __GLXdrawable *glxPixmap)
318{
319    __GLXDRIdrawable *drawable = (__GLXDRIdrawable *) glxPixmap;
320    const __DRItexBufferExtension *texBuffer = drawable->screen->texBuffer;
321    __GLXDRIcontext *context = (__GLXDRIcontext *) baseContext;
322
323    if (texBuffer == NULL)
324        return Success;
325
326#if __DRI_TEX_BUFFER_VERSION >= 2
327    if (texBuffer->base.version >= 2 && texBuffer->setTexBuffer2 != NULL) {
328	(*texBuffer->setTexBuffer2)(context->driContext,
329				    glxPixmap->target,
330				    glxPixmap->format,
331				    drawable->driDrawable);
332    } else
333#endif
334    {
335	texBuffer->setTexBuffer(context->driContext,
336				glxPixmap->target,
337				drawable->driDrawable);
338    }
339
340    return Success;
341}
342
343static int
344__glXDRIreleaseTexImage(__GLXcontext *baseContext,
345			int buffer,
346			__GLXdrawable *pixmap)
347{
348    /* FIXME: Just unbind the texture? */
349    return Success;
350}
351
352#else
353
354static int
355__glXDRIbindTexImage(__GLXcontext *baseContext,
356		     int buffer,
357		     __GLXdrawable *glxPixmap)
358{
359    return Success;
360}
361
362static int
363__glXDRIreleaseTexImage(__GLXcontext *baseContext,
364			int buffer,
365			__GLXdrawable *pixmap)
366{
367    return Success;
368}
369
370#endif
371
372static __GLXtextureFromPixmap __glXDRItextureFromPixmap = {
373    __glXDRIbindTexImage,
374    __glXDRIreleaseTexImage
375};
376
377static void
378__glXDRIscreenDestroy(__GLXscreen *baseScreen)
379{
380    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
381
382    (*screen->core->destroyScreen)(screen->driScreen);
383
384    dlclose(screen->driver);
385
386    __glXScreenDestroy(baseScreen);
387
388    free(screen);
389}
390
391static __GLXcontext *
392__glXDRIscreenCreateContext(__GLXscreen *baseScreen,
393			    __GLXconfig *glxConfig,
394			    __GLXcontext *baseShareContext)
395{
396    __GLXDRIscreen *screen = (__GLXDRIscreen *) baseScreen;
397    __GLXDRIcontext *context, *shareContext;
398    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
399    __DRIcontext *driShare;
400
401    shareContext = (__GLXDRIcontext *) baseShareContext;
402    if (shareContext)
403	driShare = shareContext->driContext;
404    else
405	driShare = NULL;
406
407    context = calloc(1, sizeof *context);
408    if (context == NULL)
409	return NULL;
410
411    context->base.destroy           = __glXDRIcontextDestroy;
412    context->base.makeCurrent       = __glXDRIcontextMakeCurrent;
413    context->base.loseCurrent       = __glXDRIcontextLoseCurrent;
414    context->base.copy              = __glXDRIcontextCopy;
415    context->base.forceCurrent      = __glXDRIcontextForceCurrent;
416    context->base.textureFromPixmap = &__glXDRItextureFromPixmap;
417    context->base.wait              = __glXDRIcontextWait;
418
419    context->driContext =
420	(*screen->dri2->createNewContext)(screen->driScreen,
421					  config->driConfig,
422					  driShare, context);
423    if (context->driContext == NULL) {
424	    free(context);
425        return NULL;
426    }
427
428    return &context->base;
429}
430
431static void
432__glXDRIinvalidateBuffers(DrawablePtr pDraw, void *priv)
433{
434#if __DRI2_FLUSH_VERSION >= 3
435    __GLXDRIdrawable *private = priv;
436    __GLXDRIscreen *screen = private->screen;
437
438    if (screen->flush)
439	(*screen->flush->invalidate)(private->driDrawable);
440#endif
441}
442
443static __GLXdrawable *
444__glXDRIscreenCreateDrawable(ClientPtr client,
445			     __GLXscreen *screen,
446			     DrawablePtr pDraw,
447			     XID drawId,
448			     int type,
449			     XID glxDrawId,
450			     __GLXconfig *glxConfig)
451{
452    __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
453    __GLXDRIconfig *config = (__GLXDRIconfig *) glxConfig;
454    __GLXDRIdrawable *private;
455
456    private = calloc(1, sizeof *private);
457    if (private == NULL)
458	return NULL;
459
460    private->screen = driScreen;
461    if (!__glXDrawableInit(&private->base, screen,
462			   pDraw, type, glxDrawId, glxConfig)) {
463        free(private);
464	return NULL;
465    }
466
467    private->base.destroy       = __glXDRIdrawableDestroy;
468    private->base.swapBuffers   = __glXDRIdrawableSwapBuffers;
469    private->base.copySubBuffer = __glXDRIdrawableCopySubBuffer;
470    private->base.waitGL	= __glXDRIdrawableWaitGL;
471    private->base.waitX		= __glXDRIdrawableWaitX;
472
473    if (DRI2CreateDrawable(client, pDraw, drawId,
474			   __glXDRIinvalidateBuffers, private)) {
475	    free(private);
476	    return NULL;
477    }
478
479    private->driDrawable =
480	(*driScreen->dri2->createNewDrawable)(driScreen->driScreen,
481					      config->driConfig, private);
482
483    return &private->base;
484}
485
486static __DRIbuffer *
487dri2GetBuffers(__DRIdrawable *driDrawable,
488	       int *width, int *height,
489	       unsigned int *attachments, int count,
490	       int *out_count, void *loaderPrivate)
491{
492    __GLXDRIdrawable *private = loaderPrivate;
493    DRI2BufferPtr *buffers;
494    int i;
495    int j;
496
497    buffers = DRI2GetBuffers(private->base.pDraw,
498			     width, height, attachments, count, out_count);
499    if (*out_count > MAX_DRAWABLE_BUFFERS) {
500	*out_count = 0;
501	return NULL;
502    }
503
504    private->width = *width;
505    private->height = *height;
506
507    /* This assumes the DRI2 buffer attachment tokens matches the
508     * __DRIbuffer tokens. */
509    j = 0;
510    for (i = 0; i < *out_count; i++) {
511	/* Do not send the real front buffer of a window to the client.
512	 */
513	if ((private->base.pDraw->type == DRAWABLE_WINDOW)
514	    && (buffers[i]->attachment == DRI2BufferFrontLeft)) {
515	    continue;
516	}
517
518	private->buffers[j].attachment = buffers[i]->attachment;
519	private->buffers[j].name = buffers[i]->name;
520	private->buffers[j].pitch = buffers[i]->pitch;
521	private->buffers[j].cpp = buffers[i]->cpp;
522	private->buffers[j].flags = buffers[i]->flags;
523	j++;
524    }
525
526    *out_count = j;
527    return private->buffers;
528}
529
530static __DRIbuffer *
531dri2GetBuffersWithFormat(__DRIdrawable *driDrawable,
532			 int *width, int *height,
533			 unsigned int *attachments, int count,
534			 int *out_count, void *loaderPrivate)
535{
536    __GLXDRIdrawable *private = loaderPrivate;
537    DRI2BufferPtr *buffers;
538    int i;
539    int j = 0;
540
541    buffers = DRI2GetBuffersWithFormat(private->base.pDraw,
542				       width, height, attachments, count,
543				       out_count);
544    if (*out_count > MAX_DRAWABLE_BUFFERS) {
545	*out_count = 0;
546	return NULL;
547    }
548
549    private->width = *width;
550    private->height = *height;
551
552    /* This assumes the DRI2 buffer attachment tokens matches the
553     * __DRIbuffer tokens. */
554    for (i = 0; i < *out_count; i++) {
555	/* Do not send the real front buffer of a window to the client.
556	 */
557	if ((private->base.pDraw->type == DRAWABLE_WINDOW)
558	    && (buffers[i]->attachment == DRI2BufferFrontLeft)) {
559	    continue;
560	}
561
562	private->buffers[j].attachment = buffers[i]->attachment;
563	private->buffers[j].name = buffers[i]->name;
564	private->buffers[j].pitch = buffers[i]->pitch;
565	private->buffers[j].cpp = buffers[i]->cpp;
566	private->buffers[j].flags = buffers[i]->flags;
567	j++;
568    }
569
570    *out_count = j;
571    return private->buffers;
572}
573
574static void
575dri2FlushFrontBuffer(__DRIdrawable *driDrawable, void *loaderPrivate)
576{
577    (void) driDrawable;
578    __glXDRIdrawableWaitGL((__GLXdrawable *) loaderPrivate);
579}
580
581static const __DRIdri2LoaderExtension loaderExtension = {
582    { __DRI_DRI2_LOADER, __DRI_DRI2_LOADER_VERSION },
583    dri2GetBuffers,
584    dri2FlushFrontBuffer,
585    dri2GetBuffersWithFormat,
586};
587
588#ifdef __DRI_USE_INVALIDATE
589static const __DRIuseInvalidateExtension dri2UseInvalidate = {
590   { __DRI_USE_INVALIDATE, __DRI_USE_INVALIDATE_VERSION }
591};
592#endif
593
594static const __DRIextension *loader_extensions[] = {
595    &systemTimeExtension.base,
596    &loaderExtension.base,
597#ifdef __DRI_USE_INVALIDATE
598    &dri2UseInvalidate.base,
599#endif
600    NULL
601};
602
603static const char dri_driver_path[] = DRI_DRIVER_PATH;
604
605static Bool
606glxDRIEnterVT (int index, int flags)
607{
608    ScrnInfoPtr scrn = xf86Screens[index];
609    Bool	ret;
610    __GLXDRIscreen *screen = (__GLXDRIscreen *)
611	glxGetScreen(screenInfo.screens[index]);
612
613    LogMessage(X_INFO, "AIGLX: Resuming AIGLX clients after VT switch\n");
614
615    scrn->EnterVT = screen->enterVT;
616
617    ret = scrn->EnterVT (index, flags);
618
619    screen->enterVT = scrn->EnterVT;
620    scrn->EnterVT = glxDRIEnterVT;
621
622    if (!ret)
623	return FALSE;
624
625    glxResumeClients();
626
627    return TRUE;
628}
629
630static void
631glxDRILeaveVT (int index, int flags)
632{
633    ScrnInfoPtr scrn = xf86Screens[index];
634    __GLXDRIscreen *screen = (__GLXDRIscreen *)
635	glxGetScreen(screenInfo.screens[index]);
636
637    LogMessage(X_INFO, "AIGLX: Suspending AIGLX clients for VT switch\n");
638
639    glxSuspendClients();
640
641    scrn->LeaveVT = screen->leaveVT;
642    (*screen->leaveVT) (index, flags);
643    screen->leaveVT = scrn->LeaveVT;
644    scrn->LeaveVT = glxDRILeaveVT;
645}
646
647static void
648initializeExtensions(__GLXDRIscreen *screen)
649{
650    ScreenPtr pScreen = screen->base.pScreen;
651    const __DRIextension **extensions;
652    int i;
653
654    extensions = screen->core->getExtensions(screen->driScreen);
655
656    __glXEnableExtension(screen->glx_enable_bits,
657			 "GLX_MESA_copy_sub_buffer");
658    LogMessage(X_INFO, "AIGLX: enabled GLX_MESA_copy_sub_buffer\n");
659
660    __glXEnableExtension(screen->glx_enable_bits, "GLX_INTEL_swap_event");
661    LogMessage(X_INFO, "AIGLX: enabled GLX_INTEL_swap_event\n");
662
663    if (DRI2HasSwapControl(pScreen)) {
664	__glXEnableExtension(screen->glx_enable_bits,
665			     "GLX_SGI_swap_control");
666	__glXEnableExtension(screen->glx_enable_bits,
667			     "GLX_MESA_swap_control");
668	LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_swap_control and GLX_MESA_swap_control\n");
669    }
670
671    for (i = 0; extensions[i]; i++) {
672#ifdef __DRI_READ_DRAWABLE
673	if (strcmp(extensions[i]->name, __DRI_READ_DRAWABLE) == 0) {
674	    __glXEnableExtension(screen->glx_enable_bits,
675				 "GLX_SGI_make_current_read");
676
677	    LogMessage(X_INFO, "AIGLX: enabled GLX_SGI_make_current_read\n");
678	}
679#endif
680
681#ifdef __DRI_TEX_BUFFER
682	if (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0) {
683	    screen->texBuffer =
684		(const __DRItexBufferExtension *) extensions[i];
685	    /* GLX_EXT_texture_from_pixmap is always enabled. */
686	    LogMessage(X_INFO, "AIGLX: GLX_EXT_texture_from_pixmap backed by buffer objects\n");
687	}
688#endif
689
690#ifdef __DRI2_FLUSH
691	if (strcmp(extensions[i]->name, __DRI2_FLUSH) == 0 &&
692	    extensions[i]->version >= 3) {
693		screen->flush = (__DRI2flushExtension *) extensions[i];
694	}
695#endif
696
697	/* Ignore unknown extensions */
698    }
699}
700
701static __GLXscreen *
702__glXDRIscreenProbe(ScreenPtr pScreen)
703{
704    const char *driverName, *deviceName;
705    __GLXDRIscreen *screen;
706    char filename[128];
707    size_t buffer_size;
708    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
709    const __DRIextension **extensions;
710    const __DRIconfig **driConfigs;
711    int i;
712
713    screen = calloc(1, sizeof *screen);
714    if (screen == NULL)
715	return NULL;
716
717    if (!xf86LoaderCheckSymbol("DRI2Connect") ||
718	!DRI2Connect(pScreen, DRI2DriverDRI,
719		     &screen->fd, &driverName, &deviceName)) {
720	LogMessage(X_INFO,
721		   "AIGLX: Screen %d is not DRI2 capable\n", pScreen->myNum);
722	return NULL;
723    }
724
725    screen->base.destroy        = __glXDRIscreenDestroy;
726    screen->base.createContext  = __glXDRIscreenCreateContext;
727    screen->base.createDrawable = __glXDRIscreenCreateDrawable;
728    screen->base.swapInterval   = __glXDRIdrawableSwapInterval;
729    screen->base.pScreen       = pScreen;
730
731    __glXInitExtensionEnableBits(screen->glx_enable_bits);
732
733    snprintf(filename, sizeof filename,
734	     "%s/%s_dri.so", dri_driver_path, driverName);
735
736    screen->driver = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
737    if (screen->driver == NULL) {
738	LogMessage(X_ERROR, "AIGLX error: dlopen of %s failed (%s)\n",
739		   filename, dlerror());
740        goto handle_error;
741    }
742
743    extensions = dlsym(screen->driver, __DRI_DRIVER_EXTENSIONS);
744    if (extensions == NULL) {
745	LogMessage(X_ERROR, "AIGLX error: %s exports no extensions (%s)\n",
746		   driverName, dlerror());
747	goto handle_error;
748    }
749
750    for (i = 0; extensions[i]; i++) {
751        if (strcmp(extensions[i]->name, __DRI_CORE) == 0 &&
752	    extensions[i]->version >= 1) {
753		screen->core = (const __DRIcoreExtension *) extensions[i];
754	}
755        if (strcmp(extensions[i]->name, __DRI_DRI2) == 0 &&
756	    extensions[i]->version >= 1) {
757		screen->dri2 = (const __DRIdri2Extension *) extensions[i];
758	}
759    }
760
761    if (screen->core == NULL || screen->dri2 == NULL) {
762	LogMessage(X_ERROR, "AIGLX error: %s exports no DRI extension\n",
763		   driverName);
764	goto handle_error;
765    }
766
767    screen->driScreen =
768	(*screen->dri2->createNewScreen)(pScreen->myNum,
769					 screen->fd,
770					 loader_extensions,
771					 &driConfigs,
772					 screen);
773
774    if (screen->driScreen == NULL) {
775	LogMessage(X_ERROR,
776		   "AIGLX error: Calling driver entry point failed\n");
777	goto handle_error;
778    }
779
780    initializeExtensions(screen);
781
782    screen->base.fbconfigs = glxConvertConfigs(screen->core, driConfigs,
783					       GLX_WINDOW_BIT |
784					       GLX_PIXMAP_BIT |
785					       GLX_PBUFFER_BIT);
786
787    __glXScreenInit(&screen->base, pScreen);
788
789    /* The first call simply determines the length of the extension string.
790     * This allows us to allocate some memory to hold the extension string,
791     * but it requires that we call __glXGetExtensionString a second time.
792     */
793    buffer_size = __glXGetExtensionString(screen->glx_enable_bits, NULL);
794    if (buffer_size > 0) {
795	free(screen->base.GLXextensions);
796
797	screen->base.GLXextensions = xnfalloc(buffer_size);
798	(void) __glXGetExtensionString(screen->glx_enable_bits,
799				       screen->base.GLXextensions);
800    }
801
802    /* We're going to assume (perhaps incorrectly?) that all DRI2-enabled
803     * drivers support the required extensions for GLX 1.4.  The extensions
804     * we're assuming are:
805     *
806     *    - GLX_SGI_make_current_read (1.3)
807     *    - GLX_SGIX_fbconfig (1.3)
808     *    - GLX_SGIX_pbuffer (1.3)
809     *    - GLX_ARB_multisample (1.4)
810     */
811    screen->base.GLXmajor = 1;
812    screen->base.GLXminor = 4;
813
814    screen->enterVT = pScrn->EnterVT;
815    pScrn->EnterVT = glxDRIEnterVT;
816    screen->leaveVT = pScrn->LeaveVT;
817    pScrn->LeaveVT = glxDRILeaveVT;
818
819    LogMessage(X_INFO,
820	       "AIGLX: Loaded and initialized %s\n", filename);
821
822    return &screen->base;
823
824 handle_error:
825    if (screen->driver)
826        dlclose(screen->driver);
827
828    free(screen);
829
830    LogMessage(X_ERROR, "AIGLX: reverting to software rendering\n");
831
832    return NULL;
833}
834
835_X_EXPORT __GLXprovider __glXDRI2Provider = {
836    __glXDRIscreenProbe,
837    "DRI2",
838    NULL
839};
840