1/*
2 * Copyright © 2008 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Soft-
6 * ware"), to deal in the Software without restriction, including without
7 * limitation the rights to use, copy, modify, merge, publish, distribute,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, provided that the above copyright
10 * notice(s) and this permission notice appear in all copies of the Soft-
11 * ware and that both the above copyright notice(s) and this permission
12 * notice appear in supporting documentation.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
17 * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
18 * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
19 * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
22 * MANCE OF THIS SOFTWARE.
23 *
24 * Except as contained in this notice, the name of a copyright holder shall
25 * not be used in advertising or otherwise to promote the sale, use or
26 * other dealings in this Software without prior written authorization of
27 * the copyright holder.
28 *
29 * Authors:
30 *   Kristian Høgsberg (krh@redhat.com)
31 */
32
33#ifdef HAVE_XORG_CONFIG_H
34#include <xorg-config.h>
35#endif
36
37#include <X11/X.h>
38#include <X11/Xproto.h>
39#include <X11/extensions/dri2proto.h>
40#include <X11/extensions/xfixeswire.h>
41#include "dixstruct.h"
42#include "scrnintstr.h"
43#include "pixmapstr.h"
44#include "extnsionst.h"
45#include "xfixes.h"
46#include "dri2.h"
47#include "dri2int.h"
48#include "protocol-versions.h"
49
50/* The only xf86 includes */
51#include "xf86Module.h"
52#include "xf86Extensions.h"
53
54static int DRI2EventBase;
55
56
57static Bool
58validDrawable(ClientPtr client, XID drawable, Mask access_mode,
59              DrawablePtr *pDrawable, int *status)
60{
61    *status = dixLookupDrawable(pDrawable, drawable, client,
62                                M_DRAWABLE_WINDOW | M_DRAWABLE_PIXMAP,
63                                access_mode);
64    if (*status != Success) {
65        client->errorValue = drawable;
66        return FALSE;
67    }
68
69    return TRUE;
70}
71
72static int
73ProcDRI2QueryVersion(ClientPtr client)
74{
75    REQUEST(xDRI2QueryVersionReq);
76    xDRI2QueryVersionReply rep = {
77        .type = X_Reply,
78        .sequenceNumber = client->sequence,
79        .length = 0,
80        .majorVersion = dri2_major,
81        .minorVersion = dri2_minor
82    };
83
84    if (client->swapped)
85        swaps(&stuff->length);
86
87    REQUEST_SIZE_MATCH(xDRI2QueryVersionReq);
88
89    if (client->swapped) {
90        swaps(&rep.sequenceNumber);
91        swapl(&rep.length);
92        swapl(&rep.majorVersion);
93        swapl(&rep.minorVersion);
94    }
95
96    WriteToClient(client, sizeof(xDRI2QueryVersionReply), &rep);
97
98    return Success;
99}
100
101static int
102ProcDRI2Connect(ClientPtr client)
103{
104    REQUEST(xDRI2ConnectReq);
105    xDRI2ConnectReply rep = {
106        .type = X_Reply,
107        .sequenceNumber = client->sequence,
108        .length = 0,
109        .driverNameLength = 0,
110        .deviceNameLength = 0
111    };
112    DrawablePtr pDraw;
113    int fd, status;
114    const char *driverName;
115    const char *deviceName;
116
117    REQUEST_SIZE_MATCH(xDRI2ConnectReq);
118    if (!validDrawable(client, stuff->window, DixGetAttrAccess,
119                       &pDraw, &status))
120        return status;
121
122    if (!DRI2Connect(client, pDraw->pScreen,
123                     stuff->driverType, &fd, &driverName, &deviceName))
124        goto fail;
125
126    rep.driverNameLength = strlen(driverName);
127    rep.deviceNameLength = strlen(deviceName);
128    rep.length = (rep.driverNameLength + 3) / 4 +
129        (rep.deviceNameLength + 3) / 4;
130
131 fail:
132    WriteToClient(client, sizeof(xDRI2ConnectReply), &rep);
133    WriteToClient(client, rep.driverNameLength, driverName);
134    WriteToClient(client, rep.deviceNameLength, deviceName);
135
136    return Success;
137}
138
139static int
140ProcDRI2Authenticate(ClientPtr client)
141{
142    REQUEST(xDRI2AuthenticateReq);
143    xDRI2AuthenticateReply rep;
144    DrawablePtr pDraw;
145    int status;
146
147    REQUEST_SIZE_MATCH(xDRI2AuthenticateReq);
148    if (!validDrawable(client, stuff->window, DixGetAttrAccess,
149                       &pDraw, &status))
150        return status;
151
152    rep = (xDRI2AuthenticateReply) {
153        .type = X_Reply,
154        .sequenceNumber = client->sequence,
155        .length = 0,
156        .authenticated = DRI2Authenticate(client, pDraw->pScreen, stuff->magic)
157    };
158    WriteToClient(client, sizeof(xDRI2AuthenticateReply), &rep);
159
160    return Success;
161}
162
163static void
164DRI2InvalidateBuffersEvent(DrawablePtr pDraw, void *priv, XID id)
165{
166    ClientPtr client = priv;
167    xDRI2InvalidateBuffers event = {
168        .type = DRI2EventBase + DRI2_InvalidateBuffers,
169        .drawable = id
170    };
171
172    WriteEventsToClient(client, 1, (xEvent *) &event);
173}
174
175static int
176ProcDRI2CreateDrawable(ClientPtr client)
177{
178    REQUEST(xDRI2CreateDrawableReq);
179    DrawablePtr pDrawable;
180    int status;
181
182    REQUEST_SIZE_MATCH(xDRI2CreateDrawableReq);
183
184    if (!validDrawable(client, stuff->drawable, DixAddAccess,
185                       &pDrawable, &status))
186        return status;
187
188    status = DRI2CreateDrawable(client, pDrawable, stuff->drawable,
189                                DRI2InvalidateBuffersEvent, client);
190    if (status != Success)
191        return status;
192
193    return Success;
194}
195
196static int
197ProcDRI2DestroyDrawable(ClientPtr client)
198{
199    REQUEST(xDRI2DestroyDrawableReq);
200    DrawablePtr pDrawable;
201    int status;
202
203    REQUEST_SIZE_MATCH(xDRI2DestroyDrawableReq);
204    if (!validDrawable(client, stuff->drawable, DixRemoveAccess,
205                       &pDrawable, &status))
206        return status;
207
208    return Success;
209}
210
211static int
212send_buffers_reply(ClientPtr client, DrawablePtr pDrawable,
213                   DRI2BufferPtr * buffers, int count, int width, int height)
214{
215    xDRI2GetBuffersReply rep;
216    int skip = 0;
217    int i;
218
219    if (buffers == NULL)
220        return BadAlloc;
221
222    if (pDrawable->type == DRAWABLE_WINDOW) {
223        for (i = 0; i < count; i++) {
224            /* Do not send the real front buffer of a window to the client.
225             */
226            if (buffers[i]->attachment == DRI2BufferFrontLeft) {
227                skip++;
228                continue;
229            }
230        }
231    }
232
233    rep = (xDRI2GetBuffersReply) {
234        .type = X_Reply,
235        .sequenceNumber = client->sequence,
236        .length = (count - skip) * sizeof(xDRI2Buffer) / 4,
237        .width = width,
238        .height = height,
239        .count = count - skip
240    };
241    WriteToClient(client, sizeof(xDRI2GetBuffersReply), &rep);
242
243    for (i = 0; i < count; i++) {
244        xDRI2Buffer buffer;
245
246        /* Do not send the real front buffer of a window to the client.
247         */
248        if ((pDrawable->type == DRAWABLE_WINDOW)
249            && (buffers[i]->attachment == DRI2BufferFrontLeft)) {
250            continue;
251        }
252
253        buffer.attachment = buffers[i]->attachment;
254        buffer.name = buffers[i]->name;
255        buffer.pitch = buffers[i]->pitch;
256        buffer.cpp = buffers[i]->cpp;
257        buffer.flags = buffers[i]->flags;
258        WriteToClient(client, sizeof(xDRI2Buffer), &buffer);
259    }
260    return Success;
261}
262
263static int
264ProcDRI2GetBuffers(ClientPtr client)
265{
266    REQUEST(xDRI2GetBuffersReq);
267    DrawablePtr pDrawable;
268    DRI2BufferPtr *buffers;
269    int status, width, height, count;
270    unsigned int *attachments;
271
272    REQUEST_AT_LEAST_SIZE(xDRI2GetBuffersReq);
273    /* stuff->count is a count of CARD32 attachments that follows */
274    if (stuff->count > (INT_MAX / sizeof(CARD32)))
275        return BadLength;
276    REQUEST_FIXED_SIZE(xDRI2GetBuffersReq, stuff->count * sizeof(CARD32));
277
278    if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
279                       &pDrawable, &status))
280        return status;
281
282    if (DRI2ThrottleClient(client, pDrawable))
283        return Success;
284
285    attachments = (unsigned int *) &stuff[1];
286    buffers = DRI2GetBuffers(pDrawable, &width, &height,
287                             attachments, stuff->count, &count);
288
289    return send_buffers_reply(client, pDrawable, buffers, count, width, height);
290
291}
292
293static int
294ProcDRI2GetBuffersWithFormat(ClientPtr client)
295{
296    REQUEST(xDRI2GetBuffersReq);
297    DrawablePtr pDrawable;
298    DRI2BufferPtr *buffers;
299    int status, width, height, count;
300    unsigned int *attachments;
301
302    REQUEST_AT_LEAST_SIZE(xDRI2GetBuffersReq);
303    /* stuff->count is a count of pairs of CARD32s (attachments & formats)
304       that follows */
305    if (stuff->count > (INT_MAX / (2 * sizeof(CARD32))))
306        return BadLength;
307    REQUEST_FIXED_SIZE(xDRI2GetBuffersReq,
308                       stuff->count * (2 * sizeof(CARD32)));
309    if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
310                       &pDrawable, &status))
311        return status;
312
313    if (DRI2ThrottleClient(client, pDrawable))
314        return Success;
315
316    attachments = (unsigned int *) &stuff[1];
317    buffers = DRI2GetBuffersWithFormat(pDrawable, &width, &height,
318                                       attachments, stuff->count, &count);
319
320    return send_buffers_reply(client, pDrawable, buffers, count, width, height);
321}
322
323static int
324ProcDRI2CopyRegion(ClientPtr client)
325{
326    REQUEST(xDRI2CopyRegionReq);
327    xDRI2CopyRegionReply rep;
328    DrawablePtr pDrawable;
329    int status;
330    RegionPtr pRegion;
331
332    REQUEST_SIZE_MATCH(xDRI2CopyRegionReq);
333
334    if (!validDrawable(client, stuff->drawable, DixWriteAccess,
335                       &pDrawable, &status))
336        return status;
337
338    VERIFY_REGION(pRegion, stuff->region, client, DixReadAccess);
339
340    status = DRI2CopyRegion(pDrawable, pRegion, stuff->dest, stuff->src);
341    if (status != Success)
342        return status;
343
344    /* CopyRegion needs to be a round trip to make sure the X server
345     * queues the swap buffer rendering commands before the DRI client
346     * continues rendering.  The reply has a bitmask to signal the
347     * presence of optional return values as well, but we're not using
348     * that yet.
349     */
350
351    rep = (xDRI2CopyRegionReply) {
352        .type = X_Reply,
353        .sequenceNumber = client->sequence,
354        .length = 0
355    };
356
357    WriteToClient(client, sizeof(xDRI2CopyRegionReply), &rep);
358
359    return Success;
360}
361
362static void
363load_swap_reply(xDRI2SwapBuffersReply * rep, CARD64 sbc)
364{
365    rep->swap_hi = sbc >> 32;
366    rep->swap_lo = sbc & 0xffffffff;
367}
368
369static CARD64
370vals_to_card64(CARD32 lo, CARD32 hi)
371{
372    return (CARD64) hi << 32 | lo;
373}
374
375static void
376DRI2SwapEvent(ClientPtr client, void *data, int type, CARD64 ust, CARD64 msc,
377              CARD32 sbc)
378{
379    DrawablePtr pDrawable = data;
380    xDRI2BufferSwapComplete2 event = {
381        .type = DRI2EventBase + DRI2_BufferSwapComplete,
382        .event_type = type,
383        .drawable = pDrawable->id,
384        .ust_hi = (CARD64) ust >> 32,
385        .ust_lo = ust & 0xffffffff,
386        .msc_hi = (CARD64) msc >> 32,
387        .msc_lo = msc & 0xffffffff,
388        .sbc = sbc
389    };
390
391    WriteEventsToClient(client, 1, (xEvent *) &event);
392}
393
394static int
395ProcDRI2SwapBuffers(ClientPtr client)
396{
397    REQUEST(xDRI2SwapBuffersReq);
398    xDRI2SwapBuffersReply rep = {
399        .type = X_Reply,
400        .sequenceNumber = client->sequence,
401        .length = 0
402    };
403    DrawablePtr pDrawable;
404    CARD64 target_msc, divisor, remainder, swap_target;
405    int status;
406
407    REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq);
408
409    if (!validDrawable(client, stuff->drawable,
410                       DixReadAccess | DixWriteAccess, &pDrawable, &status))
411        return status;
412
413    /*
414     * Ensures an out of control client can't exhaust our swap queue, and
415     * also orders swaps.
416     */
417    if (DRI2ThrottleClient(client, pDrawable))
418        return Success;
419
420    target_msc = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
421    divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
422    remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
423
424    status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder,
425                             &swap_target, DRI2SwapEvent, pDrawable);
426    if (status != Success)
427        return BadDrawable;
428
429    load_swap_reply(&rep, swap_target);
430
431    WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep);
432
433    return Success;
434}
435
436static void
437load_msc_reply(xDRI2MSCReply * rep, CARD64 ust, CARD64 msc, CARD64 sbc)
438{
439    rep->ust_hi = ust >> 32;
440    rep->ust_lo = ust & 0xffffffff;
441    rep->msc_hi = msc >> 32;
442    rep->msc_lo = msc & 0xffffffff;
443    rep->sbc_hi = sbc >> 32;
444    rep->sbc_lo = sbc & 0xffffffff;
445}
446
447static int
448ProcDRI2GetMSC(ClientPtr client)
449{
450    REQUEST(xDRI2GetMSCReq);
451    xDRI2MSCReply rep = {
452        .type = X_Reply,
453        .sequenceNumber = client->sequence,
454        .length = 0
455    };
456    DrawablePtr pDrawable;
457    CARD64 ust, msc, sbc;
458    int status;
459
460    REQUEST_SIZE_MATCH(xDRI2GetMSCReq);
461
462    if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
463                       &status))
464        return status;
465
466    status = DRI2GetMSC(pDrawable, &ust, &msc, &sbc);
467    if (status != Success)
468        return status;
469
470    load_msc_reply(&rep, ust, msc, sbc);
471
472    WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
473
474    return Success;
475}
476
477static int
478ProcDRI2WaitMSC(ClientPtr client)
479{
480    REQUEST(xDRI2WaitMSCReq);
481    DrawablePtr pDrawable;
482    CARD64 target, divisor, remainder;
483    int status;
484
485    /* FIXME: in restart case, client may be gone at this point */
486
487    REQUEST_SIZE_MATCH(xDRI2WaitMSCReq);
488
489    if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
490                       &status))
491        return status;
492
493    target = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
494    divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
495    remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
496
497    status = DRI2WaitMSC(client, pDrawable, target, divisor, remainder);
498    if (status != Success)
499        return status;
500
501    return Success;
502}
503
504int
505ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust, CARD64 msc, CARD64 sbc)
506{
507    xDRI2MSCReply rep = {
508        .type = X_Reply,
509        .sequenceNumber = client->sequence,
510        .length = 0
511    };
512
513    load_msc_reply(&rep, ust, msc, sbc);
514
515    WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
516
517    return Success;
518}
519
520static int
521ProcDRI2SwapInterval(ClientPtr client)
522{
523    REQUEST(xDRI2SwapIntervalReq);
524    DrawablePtr pDrawable;
525    int status;
526
527    /* FIXME: in restart case, client may be gone at this point */
528
529    REQUEST_SIZE_MATCH(xDRI2SwapIntervalReq);
530
531    if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
532                       &pDrawable, &status))
533        return status;
534
535    DRI2SwapInterval(pDrawable, stuff->interval);
536
537    return Success;
538}
539
540static int
541ProcDRI2WaitSBC(ClientPtr client)
542{
543    REQUEST(xDRI2WaitSBCReq);
544    DrawablePtr pDrawable;
545    CARD64 target;
546    int status;
547
548    REQUEST_SIZE_MATCH(xDRI2WaitSBCReq);
549
550    if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
551                       &status))
552        return status;
553
554    target = vals_to_card64(stuff->target_sbc_lo, stuff->target_sbc_hi);
555    status = DRI2WaitSBC(client, pDrawable, target);
556
557    return status;
558}
559
560static int
561ProcDRI2GetParam(ClientPtr client)
562{
563    REQUEST(xDRI2GetParamReq);
564    xDRI2GetParamReply rep = {
565        .type = X_Reply,
566        .sequenceNumber = client->sequence,
567        .length = 0
568    };
569    DrawablePtr pDrawable;
570    CARD64 value;
571    int status;
572
573    REQUEST_SIZE_MATCH(xDRI2GetParamReq);
574
575    if (!validDrawable(client, stuff->drawable, DixReadAccess,
576                       &pDrawable, &status))
577        return status;
578
579    status = DRI2GetParam(client, pDrawable, stuff->param,
580                          &rep.is_param_recognized, &value);
581    rep.value_hi = value >> 32;
582    rep.value_lo = value & 0xffffffff;
583
584    if (status != Success)
585        return status;
586
587    WriteToClient(client, sizeof(xDRI2GetParamReply), &rep);
588
589    return status;
590}
591
592static int
593ProcDRI2Dispatch(ClientPtr client)
594{
595    REQUEST(xReq);
596
597    switch (stuff->data) {
598    case X_DRI2QueryVersion:
599        return ProcDRI2QueryVersion(client);
600    }
601
602    if (!client->local)
603        return BadRequest;
604
605    switch (stuff->data) {
606    case X_DRI2Connect:
607        return ProcDRI2Connect(client);
608    case X_DRI2Authenticate:
609        return ProcDRI2Authenticate(client);
610    case X_DRI2CreateDrawable:
611        return ProcDRI2CreateDrawable(client);
612    case X_DRI2DestroyDrawable:
613        return ProcDRI2DestroyDrawable(client);
614    case X_DRI2GetBuffers:
615        return ProcDRI2GetBuffers(client);
616    case X_DRI2CopyRegion:
617        return ProcDRI2CopyRegion(client);
618    case X_DRI2GetBuffersWithFormat:
619        return ProcDRI2GetBuffersWithFormat(client);
620    case X_DRI2SwapBuffers:
621        return ProcDRI2SwapBuffers(client);
622    case X_DRI2GetMSC:
623        return ProcDRI2GetMSC(client);
624    case X_DRI2WaitMSC:
625        return ProcDRI2WaitMSC(client);
626    case X_DRI2WaitSBC:
627        return ProcDRI2WaitSBC(client);
628    case X_DRI2SwapInterval:
629        return ProcDRI2SwapInterval(client);
630    case X_DRI2GetParam:
631        return ProcDRI2GetParam(client);
632    default:
633        return BadRequest;
634    }
635}
636
637static int _X_COLD
638SProcDRI2Connect(ClientPtr client)
639{
640    REQUEST(xDRI2ConnectReq);
641    xDRI2ConnectReply rep = {
642        .type = X_Reply,
643        .sequenceNumber = client->sequence,
644        .length = 0,
645        .driverNameLength = 0,
646        .deviceNameLength = 0
647    };
648
649    /* If the client is swapped, it's not local.  Talk to the hand. */
650
651    swaps(&stuff->length);
652    if (sizeof(*stuff) / 4 != client->req_len)
653        return BadLength;
654
655    swaps(&rep.sequenceNumber);
656
657    WriteToClient(client, sizeof(xDRI2ConnectReply), &rep);
658
659    return Success;
660}
661
662static int _X_COLD
663SProcDRI2Dispatch(ClientPtr client)
664{
665    REQUEST(xReq);
666
667    /*
668     * Only local clients are allowed DRI access, but remote clients
669     * still need these requests to find out cleanly.
670     */
671    switch (stuff->data) {
672    case X_DRI2QueryVersion:
673        return ProcDRI2QueryVersion(client);
674    case X_DRI2Connect:
675        return SProcDRI2Connect(client);
676    default:
677        return BadRequest;
678    }
679}
680
681void
682DRI2ExtensionInit(void)
683{
684    ExtensionEntry *dri2Extension;
685
686#ifdef PANORAMIX
687    if (!noPanoramiXExtension)
688        return;
689#endif
690
691    dri2Extension = AddExtension(DRI2_NAME,
692                                 DRI2NumberEvents,
693                                 DRI2NumberErrors,
694                                 ProcDRI2Dispatch,
695                                 SProcDRI2Dispatch, NULL, StandardMinorOpcode);
696
697    DRI2EventBase = dri2Extension->eventBase;
698
699    DRI2ModuleSetup();
700}
701