1/*
2 * Copyright © 2013 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "dri3_priv.h"
24#include <syncsrv.h>
25#include <unistd.h>
26#include <xace.h>
27#include "../Xext/syncsdk.h"
28#include <protocol-versions.h>
29#include <drm_fourcc.h>
30
31static Bool
32dri3_screen_can_one_point_two(ScreenPtr screen)
33{
34    dri3_screen_priv_ptr dri3 = dri3_screen_priv(screen);
35
36    if (dri3 && dri3->info && dri3->info->version >= 2 &&
37        dri3->info->pixmap_from_fds && dri3->info->fds_from_pixmap &&
38        dri3->info->get_formats && dri3->info->get_modifiers &&
39        dri3->info->get_drawable_modifiers)
40        return TRUE;
41
42    return FALSE;
43}
44
45static int
46proc_dri3_query_version(ClientPtr client)
47{
48    REQUEST(xDRI3QueryVersionReq);
49    xDRI3QueryVersionReply rep = {
50        .type = X_Reply,
51        .sequenceNumber = client->sequence,
52        .length = 0,
53        .majorVersion = SERVER_DRI3_MAJOR_VERSION,
54        .minorVersion = SERVER_DRI3_MINOR_VERSION
55    };
56
57    REQUEST_SIZE_MATCH(xDRI3QueryVersionReq);
58
59    for (int i = 0; i < screenInfo.numScreens; i++) {
60        if (!dri3_screen_can_one_point_two(screenInfo.screens[i])) {
61            rep.minorVersion = 0;
62            break;
63        }
64    }
65
66    for (int i = 0; i < screenInfo.numGPUScreens; i++) {
67        if (!dri3_screen_can_one_point_two(screenInfo.gpuscreens[i])) {
68            rep.minorVersion = 0;
69            break;
70        }
71    }
72
73    /* From DRI3 proto:
74     *
75     * The client sends the highest supported version to the server
76     * and the server sends the highest version it supports, but no
77     * higher than the requested version.
78     */
79
80    if (rep.majorVersion > stuff->majorVersion ||
81        (rep.majorVersion == stuff->majorVersion &&
82         rep.minorVersion > stuff->minorVersion)) {
83        rep.majorVersion = stuff->majorVersion;
84        rep.minorVersion = stuff->minorVersion;
85    }
86
87    if (client->swapped) {
88        swaps(&rep.sequenceNumber);
89        swapl(&rep.length);
90        swapl(&rep.majorVersion);
91        swapl(&rep.minorVersion);
92    }
93    WriteToClient(client, sizeof(rep), &rep);
94    return Success;
95}
96
97int
98dri3_send_open_reply(ClientPtr client, int fd)
99{
100    xDRI3OpenReply rep = {
101        .type = X_Reply,
102        .nfd = 1,
103        .sequenceNumber = client->sequence,
104        .length = 0,
105    };
106
107    if (client->swapped) {
108        swaps(&rep.sequenceNumber);
109        swapl(&rep.length);
110    }
111
112    if (WriteFdToClient(client, fd, TRUE) < 0) {
113        close(fd);
114        return BadAlloc;
115    }
116
117    WriteToClient(client, sizeof (rep), &rep);
118
119    return Success;
120}
121
122static int
123proc_dri3_open(ClientPtr client)
124{
125    REQUEST(xDRI3OpenReq);
126    RRProviderPtr provider;
127    DrawablePtr drawable;
128    ScreenPtr screen;
129    int fd;
130    int status;
131
132    REQUEST_SIZE_MATCH(xDRI3OpenReq);
133
134    status = dixLookupDrawable(&drawable, stuff->drawable, client, 0, DixGetAttrAccess);
135    if (status != Success)
136        return status;
137
138    if (stuff->provider == None)
139        provider = NULL;
140    else if (!RRProviderType) {
141        return BadMatch;
142    } else {
143        VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
144        if (drawable->pScreen != provider->pScreen)
145            return BadMatch;
146    }
147    screen = drawable->pScreen;
148
149    status = dri3_open(client, screen, provider, &fd);
150    if (status != Success)
151        return status;
152
153    if (client->ignoreCount == 0)
154        return dri3_send_open_reply(client, fd);
155
156    return Success;
157}
158
159static int
160proc_dri3_pixmap_from_buffer(ClientPtr client)
161{
162    REQUEST(xDRI3PixmapFromBufferReq);
163    int fd;
164    DrawablePtr drawable;
165    PixmapPtr pixmap;
166    CARD32 stride, offset;
167    int rc;
168
169    SetReqFds(client, 1);
170    REQUEST_SIZE_MATCH(xDRI3PixmapFromBufferReq);
171    LEGAL_NEW_RESOURCE(stuff->pixmap, client);
172    rc = dixLookupDrawable(&drawable, stuff->drawable, client, M_ANY, DixGetAttrAccess);
173    if (rc != Success) {
174        client->errorValue = stuff->drawable;
175        return rc;
176    }
177
178    if (!stuff->width || !stuff->height) {
179        client->errorValue = 0;
180        return BadValue;
181    }
182
183    if (stuff->width > 32767 || stuff->height > 32767)
184        return BadAlloc;
185
186    if (stuff->depth != 1) {
187        DepthPtr depth = drawable->pScreen->allowedDepths;
188        int i;
189        for (i = 0; i < drawable->pScreen->numDepths; i++, depth++)
190            if (depth->depth == stuff->depth)
191                break;
192        if (i == drawable->pScreen->numDepths) {
193            client->errorValue = stuff->depth;
194            return BadValue;
195        }
196    }
197
198    fd = ReadFdFromClient(client);
199    if (fd < 0)
200        return BadValue;
201
202    offset = 0;
203    stride = stuff->stride;
204    rc = dri3_pixmap_from_fds(&pixmap,
205                              drawable->pScreen, 1, &fd,
206                              stuff->width, stuff->height,
207                              &stride, &offset,
208                              stuff->depth, stuff->bpp,
209                              DRM_FORMAT_MOD_INVALID);
210    close (fd);
211    if (rc != Success)
212        return rc;
213
214    pixmap->drawable.id = stuff->pixmap;
215
216    /* security creation/labeling check */
217    rc = XaceHook(XACE_RESOURCE_ACCESS, client, stuff->pixmap, RT_PIXMAP,
218                  pixmap, RT_NONE, NULL, DixCreateAccess);
219
220    if (rc != Success) {
221        (*drawable->pScreen->DestroyPixmap) (pixmap);
222        return rc;
223    }
224    if (!AddResource(stuff->pixmap, RT_PIXMAP, (void *) pixmap))
225        return BadAlloc;
226
227    return Success;
228}
229
230static int
231proc_dri3_buffer_from_pixmap(ClientPtr client)
232{
233    REQUEST(xDRI3BufferFromPixmapReq);
234    xDRI3BufferFromPixmapReply rep = {
235        .type = X_Reply,
236        .nfd = 1,
237        .sequenceNumber = client->sequence,
238        .length = 0,
239    };
240    int rc;
241    int fd;
242    PixmapPtr pixmap;
243
244    REQUEST_SIZE_MATCH(xDRI3BufferFromPixmapReq);
245    rc = dixLookupResourceByType((void **) &pixmap, stuff->pixmap, RT_PIXMAP,
246                                 client, DixWriteAccess);
247    if (rc != Success) {
248        client->errorValue = stuff->pixmap;
249        return rc;
250    }
251
252    rep.width = pixmap->drawable.width;
253    rep.height = pixmap->drawable.height;
254    rep.depth = pixmap->drawable.depth;
255    rep.bpp = pixmap->drawable.bitsPerPixel;
256
257    fd = dri3_fd_from_pixmap(pixmap, &rep.stride, &rep.size);
258    if (fd < 0)
259        return BadPixmap;
260
261    if (client->swapped) {
262        swaps(&rep.sequenceNumber);
263        swapl(&rep.length);
264        swapl(&rep.size);
265        swaps(&rep.width);
266        swaps(&rep.height);
267        swaps(&rep.stride);
268    }
269    if (WriteFdToClient(client, fd, TRUE) < 0) {
270        close(fd);
271        return BadAlloc;
272    }
273
274    WriteToClient(client, sizeof(rep), &rep);
275
276    return Success;
277}
278
279static int
280proc_dri3_fence_from_fd(ClientPtr client)
281{
282    REQUEST(xDRI3FenceFromFDReq);
283    DrawablePtr drawable;
284    int fd;
285    int status;
286
287    SetReqFds(client, 1);
288    REQUEST_SIZE_MATCH(xDRI3FenceFromFDReq);
289    LEGAL_NEW_RESOURCE(stuff->fence, client);
290
291    status = dixLookupDrawable(&drawable, stuff->drawable, client, M_ANY, DixGetAttrAccess);
292    if (status != Success)
293        return status;
294
295    fd = ReadFdFromClient(client);
296    if (fd < 0)
297        return BadValue;
298
299    status = SyncCreateFenceFromFD(client, drawable, stuff->fence,
300                                   fd, stuff->initially_triggered);
301
302    return status;
303}
304
305static int
306proc_dri3_fd_from_fence(ClientPtr client)
307{
308    REQUEST(xDRI3FDFromFenceReq);
309    xDRI3FDFromFenceReply rep = {
310        .type = X_Reply,
311        .nfd = 1,
312        .sequenceNumber = client->sequence,
313        .length = 0,
314    };
315    DrawablePtr drawable;
316    int fd;
317    int status;
318    SyncFence *fence;
319
320    REQUEST_SIZE_MATCH(xDRI3FDFromFenceReq);
321
322    status = dixLookupDrawable(&drawable, stuff->drawable, client, M_ANY, DixGetAttrAccess);
323    if (status != Success)
324        return status;
325    status = SyncVerifyFence(&fence, stuff->fence, client, DixWriteAccess);
326    if (status != Success)
327        return status;
328
329    fd = SyncFDFromFence(client, drawable, fence);
330    if (fd < 0)
331        return BadMatch;
332
333    if (client->swapped) {
334        swaps(&rep.sequenceNumber);
335        swapl(&rep.length);
336    }
337    if (WriteFdToClient(client, fd, FALSE) < 0)
338        return BadAlloc;
339
340    WriteToClient(client, sizeof(rep), &rep);
341
342    return Success;
343}
344
345static int
346proc_dri3_get_supported_modifiers(ClientPtr client)
347{
348    REQUEST(xDRI3GetSupportedModifiersReq);
349    xDRI3GetSupportedModifiersReply rep = {
350        .type = X_Reply,
351        .sequenceNumber = client->sequence,
352    };
353    WindowPtr window;
354    ScreenPtr pScreen;
355    CARD64 *window_modifiers = NULL;
356    CARD64 *screen_modifiers = NULL;
357    CARD32 nwindowmodifiers = 0;
358    CARD32 nscreenmodifiers = 0;
359    int status;
360    int i;
361
362    REQUEST_SIZE_MATCH(xDRI3GetSupportedModifiersReq);
363
364    status = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess);
365    if (status != Success)
366        return status;
367    pScreen = window->drawable.pScreen;
368
369    dri3_get_supported_modifiers(pScreen, &window->drawable,
370				 stuff->depth, stuff->bpp,
371                                 &nwindowmodifiers, &window_modifiers,
372                                 &nscreenmodifiers, &screen_modifiers);
373
374    rep.numWindowModifiers = nwindowmodifiers;
375    rep.numScreenModifiers = nscreenmodifiers;
376    rep.length = bytes_to_int32(rep.numWindowModifiers * sizeof(CARD64)) +
377                 bytes_to_int32(rep.numScreenModifiers * sizeof(CARD64));
378
379    if (client->swapped) {
380        swaps(&rep.sequenceNumber);
381        swapl(&rep.length);
382        swapl(&rep.numWindowModifiers);
383        swapl(&rep.numScreenModifiers);
384        for (i = 0; i < nwindowmodifiers; i++)
385            swapll(&window_modifiers[i]);
386        for (i = 0; i < nscreenmodifiers; i++)
387            swapll(&screen_modifiers[i]);
388    }
389
390    WriteToClient(client, sizeof(rep), &rep);
391    WriteToClient(client, nwindowmodifiers * sizeof(CARD64), window_modifiers);
392    WriteToClient(client, nscreenmodifiers * sizeof(CARD64), screen_modifiers);
393
394    free(window_modifiers);
395    free(screen_modifiers);
396
397    return Success;
398}
399
400static int
401proc_dri3_pixmap_from_buffers(ClientPtr client)
402{
403    REQUEST(xDRI3PixmapFromBuffersReq);
404    int fds[4];
405    CARD32 strides[4], offsets[4];
406    ScreenPtr screen;
407    WindowPtr window;
408    PixmapPtr pixmap;
409    int rc;
410    int i;
411
412    SetReqFds(client, stuff->num_buffers);
413    REQUEST_SIZE_MATCH(xDRI3PixmapFromBuffersReq);
414    LEGAL_NEW_RESOURCE(stuff->pixmap, client);
415    rc = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess);
416    if (rc != Success) {
417        client->errorValue = stuff->window;
418        return rc;
419    }
420    screen = window->drawable.pScreen;
421
422    if (!stuff->width || !stuff->height || !stuff->bpp || !stuff->depth) {
423        client->errorValue = 0;
424        return BadValue;
425    }
426
427    if (stuff->width > 32767 || stuff->height > 32767)
428        return BadAlloc;
429
430    if (stuff->depth != 1) {
431        DepthPtr depth = screen->allowedDepths;
432        int j;
433        for (j = 0; j < screen->numDepths; j++, depth++)
434            if (depth->depth == stuff->depth)
435                break;
436        if (j == screen->numDepths) {
437            client->errorValue = stuff->depth;
438            return BadValue;
439        }
440    }
441
442    if (!stuff->num_buffers || stuff->num_buffers > 4) {
443        client->errorValue = stuff->num_buffers;
444        return BadValue;
445    }
446
447    for (i = 0; i < stuff->num_buffers; i++) {
448        fds[i] = ReadFdFromClient(client);
449        if (fds[i] < 0) {
450            while (--i >= 0)
451                close(fds[i]);
452            return BadValue;
453        }
454    }
455
456    strides[0] = stuff->stride0;
457    strides[1] = stuff->stride1;
458    strides[2] = stuff->stride2;
459    strides[3] = stuff->stride3;
460    offsets[0] = stuff->offset0;
461    offsets[1] = stuff->offset1;
462    offsets[2] = stuff->offset2;
463    offsets[3] = stuff->offset3;
464
465    rc = dri3_pixmap_from_fds(&pixmap, screen,
466                              stuff->num_buffers, fds,
467                              stuff->width, stuff->height,
468                              strides, offsets,
469                              stuff->depth, stuff->bpp,
470                              stuff->modifier);
471
472    for (i = 0; i < stuff->num_buffers; i++)
473        close (fds[i]);
474
475    if (rc != Success)
476        return rc;
477
478    pixmap->drawable.id = stuff->pixmap;
479
480    /* security creation/labeling check */
481    rc = XaceHook(XACE_RESOURCE_ACCESS, client, stuff->pixmap, RT_PIXMAP,
482                  pixmap, RT_NONE, NULL, DixCreateAccess);
483
484    if (rc != Success) {
485        (*screen->DestroyPixmap) (pixmap);
486        return rc;
487    }
488    if (!AddResource(stuff->pixmap, RT_PIXMAP, (void *) pixmap))
489        return BadAlloc;
490
491    return Success;
492}
493
494static int
495proc_dri3_buffers_from_pixmap(ClientPtr client)
496{
497    REQUEST(xDRI3BuffersFromPixmapReq);
498    xDRI3BuffersFromPixmapReply rep = {
499        .type = X_Reply,
500        .sequenceNumber = client->sequence,
501    };
502    int rc;
503    int fds[4];
504    int num_fds;
505    uint32_t strides[4], offsets[4];
506    uint64_t modifier;
507    int i;
508    PixmapPtr pixmap;
509
510    REQUEST_SIZE_MATCH(xDRI3BuffersFromPixmapReq);
511    rc = dixLookupResourceByType((void **) &pixmap, stuff->pixmap, RT_PIXMAP,
512                                 client, DixWriteAccess);
513    if (rc != Success) {
514        client->errorValue = stuff->pixmap;
515        return rc;
516    }
517
518    num_fds = dri3_fds_from_pixmap(pixmap, fds, strides, offsets, &modifier);
519    if (num_fds == 0)
520        return BadPixmap;
521
522    rep.nfd = num_fds;
523    rep.length = bytes_to_int32(num_fds * 2 * sizeof(CARD32));
524    rep.width = pixmap->drawable.width;
525    rep.height = pixmap->drawable.height;
526    rep.depth = pixmap->drawable.depth;
527    rep.bpp = pixmap->drawable.bitsPerPixel;
528    rep.modifier = modifier;
529
530    if (client->swapped) {
531        swaps(&rep.sequenceNumber);
532        swapl(&rep.length);
533        swaps(&rep.width);
534        swaps(&rep.height);
535        swapll(&rep.modifier);
536        for (i = 0; i < num_fds; i++) {
537            swapl(&strides[i]);
538            swapl(&offsets[i]);
539        }
540    }
541
542    for (i = 0; i < num_fds; i++) {
543        if (WriteFdToClient(client, fds[i], TRUE) < 0) {
544            while (i--)
545                close(fds[i]);
546            return BadAlloc;
547        }
548    }
549
550    WriteToClient(client, sizeof(rep), &rep);
551    WriteToClient(client, num_fds * sizeof(CARD32), strides);
552    WriteToClient(client, num_fds * sizeof(CARD32), offsets);
553
554    return Success;
555}
556
557int (*proc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
558    proc_dri3_query_version,            /* 0 */
559    proc_dri3_open,                     /* 1 */
560    proc_dri3_pixmap_from_buffer,       /* 2 */
561    proc_dri3_buffer_from_pixmap,       /* 3 */
562    proc_dri3_fence_from_fd,            /* 4 */
563    proc_dri3_fd_from_fence,            /* 5 */
564    proc_dri3_get_supported_modifiers,  /* 6 */
565    proc_dri3_pixmap_from_buffers,      /* 7 */
566    proc_dri3_buffers_from_pixmap,      /* 8 */
567};
568
569int
570proc_dri3_dispatch(ClientPtr client)
571{
572    REQUEST(xReq);
573    if (!client->local)
574        return BadMatch;
575    if (stuff->data >= DRI3NumberRequests || !proc_dri3_vector[stuff->data])
576        return BadRequest;
577    return (*proc_dri3_vector[stuff->data]) (client);
578}
579
580static int _X_COLD
581sproc_dri3_query_version(ClientPtr client)
582{
583    REQUEST(xDRI3QueryVersionReq);
584    REQUEST_SIZE_MATCH(xDRI3QueryVersionReq);
585
586    swaps(&stuff->length);
587    swapl(&stuff->majorVersion);
588    swapl(&stuff->minorVersion);
589    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
590}
591
592static int _X_COLD
593sproc_dri3_open(ClientPtr client)
594{
595    REQUEST(xDRI3OpenReq);
596    REQUEST_SIZE_MATCH(xDRI3OpenReq);
597
598    swaps(&stuff->length);
599    swapl(&stuff->drawable);
600    swapl(&stuff->provider);
601    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
602}
603
604static int _X_COLD
605sproc_dri3_pixmap_from_buffer(ClientPtr client)
606{
607    REQUEST(xDRI3PixmapFromBufferReq);
608    REQUEST_SIZE_MATCH(xDRI3PixmapFromBufferReq);
609
610    swaps(&stuff->length);
611    swapl(&stuff->pixmap);
612    swapl(&stuff->drawable);
613    swapl(&stuff->size);
614    swaps(&stuff->width);
615    swaps(&stuff->height);
616    swaps(&stuff->stride);
617    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
618}
619
620static int _X_COLD
621sproc_dri3_buffer_from_pixmap(ClientPtr client)
622{
623    REQUEST(xDRI3BufferFromPixmapReq);
624    REQUEST_SIZE_MATCH(xDRI3BufferFromPixmapReq);
625
626    swaps(&stuff->length);
627    swapl(&stuff->pixmap);
628    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
629}
630
631static int _X_COLD
632sproc_dri3_fence_from_fd(ClientPtr client)
633{
634    REQUEST(xDRI3FenceFromFDReq);
635    REQUEST_SIZE_MATCH(xDRI3FenceFromFDReq);
636
637    swaps(&stuff->length);
638    swapl(&stuff->drawable);
639    swapl(&stuff->fence);
640    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
641}
642
643static int _X_COLD
644sproc_dri3_fd_from_fence(ClientPtr client)
645{
646    REQUEST(xDRI3FDFromFenceReq);
647    REQUEST_SIZE_MATCH(xDRI3FDFromFenceReq);
648
649    swaps(&stuff->length);
650    swapl(&stuff->drawable);
651    swapl(&stuff->fence);
652    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
653}
654
655static int _X_COLD
656sproc_dri3_get_supported_modifiers(ClientPtr client)
657{
658    REQUEST(xDRI3GetSupportedModifiersReq);
659    REQUEST_SIZE_MATCH(xDRI3GetSupportedModifiersReq);
660
661    swaps(&stuff->length);
662    swapl(&stuff->window);
663    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
664}
665
666static int _X_COLD
667sproc_dri3_pixmap_from_buffers(ClientPtr client)
668{
669    REQUEST(xDRI3PixmapFromBuffersReq);
670    REQUEST_SIZE_MATCH(xDRI3PixmapFromBuffersReq);
671
672    swaps(&stuff->length);
673    swapl(&stuff->pixmap);
674    swapl(&stuff->window);
675    swaps(&stuff->width);
676    swaps(&stuff->height);
677    swapl(&stuff->stride0);
678    swapl(&stuff->offset0);
679    swapl(&stuff->stride1);
680    swapl(&stuff->offset1);
681    swapl(&stuff->stride2);
682    swapl(&stuff->offset2);
683    swapl(&stuff->stride3);
684    swapl(&stuff->offset3);
685    swapll(&stuff->modifier);
686    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
687}
688
689static int _X_COLD
690sproc_dri3_buffers_from_pixmap(ClientPtr client)
691{
692    REQUEST(xDRI3BuffersFromPixmapReq);
693    REQUEST_SIZE_MATCH(xDRI3BuffersFromPixmapReq);
694
695    swaps(&stuff->length);
696    swapl(&stuff->pixmap);
697    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
698}
699
700int (*sproc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
701    sproc_dri3_query_version,           /* 0 */
702    sproc_dri3_open,                    /* 1 */
703    sproc_dri3_pixmap_from_buffer,      /* 2 */
704    sproc_dri3_buffer_from_pixmap,      /* 3 */
705    sproc_dri3_fence_from_fd,           /* 4 */
706    sproc_dri3_fd_from_fence,           /* 5 */
707    sproc_dri3_get_supported_modifiers, /* 6 */
708    sproc_dri3_pixmap_from_buffers,     /* 7 */
709    sproc_dri3_buffers_from_pixmap,     /* 8 */
710};
711
712int _X_COLD
713sproc_dri3_dispatch(ClientPtr client)
714{
715    REQUEST(xReq);
716    if (!client->local)
717        return BadMatch;
718    if (stuff->data >= DRI3NumberRequests || !sproc_dri3_vector[stuff->data])
719        return BadRequest;
720    return (*sproc_dri3_vector[stuff->data]) (client);
721}
722