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 "protocol-versions.h"
48
49/* The only xf86 include */
50#include "xf86Module.h"
51
52static ExtensionEntry	*dri2Extension;
53extern Bool DRI2ModuleSetup(void);
54
55static Bool
56validDrawable(ClientPtr client, XID drawable, Mask access_mode,
57	      DrawablePtr *pDrawable, int *status)
58{
59    *status = dixLookupDrawable(pDrawable, drawable, client,
60				M_DRAWABLE_WINDOW | M_DRAWABLE_PIXMAP,
61				access_mode);
62    if (*status != Success) {
63	client->errorValue = drawable;
64	return FALSE;
65    }
66
67    return TRUE;
68}
69
70static int
71ProcDRI2QueryVersion(ClientPtr client)
72{
73    REQUEST(xDRI2QueryVersionReq);
74    xDRI2QueryVersionReply rep;
75    int n;
76
77    if (client->swapped)
78	swaps(&stuff->length, n);
79
80    REQUEST_SIZE_MATCH(xDRI2QueryVersionReq);
81    rep.type = X_Reply;
82    rep.length = 0;
83    rep.sequenceNumber = client->sequence;
84    rep.majorVersion = dri2_major;
85    rep.minorVersion = dri2_minor;
86
87    if (client->swapped) {
88    	swaps(&rep.sequenceNumber, n);
89    	swapl(&rep.length, n);
90	swapl(&rep.majorVersion, n);
91	swapl(&rep.minorVersion, n);
92    }
93
94    WriteToClient(client, sizeof(xDRI2QueryVersionReply), &rep);
95
96    return Success;
97}
98
99static int
100ProcDRI2Connect(ClientPtr client)
101{
102    REQUEST(xDRI2ConnectReq);
103    xDRI2ConnectReply rep;
104    DrawablePtr pDraw;
105    int fd, status;
106    const char *driverName;
107    const char *deviceName;
108
109    REQUEST_SIZE_MATCH(xDRI2ConnectReq);
110    if (!validDrawable(client, stuff->window, DixGetAttrAccess,
111		       &pDraw, &status))
112	return status;
113
114    rep.type = X_Reply;
115    rep.length = 0;
116    rep.sequenceNumber = client->sequence;
117    rep.driverNameLength = 0;
118    rep.deviceNameLength = 0;
119
120    if (!DRI2Connect(pDraw->pScreen,
121		     stuff->driverType, &fd, &driverName, &deviceName))
122	goto fail;
123
124    rep.driverNameLength = strlen(driverName);
125    rep.deviceNameLength = strlen(deviceName);
126    rep.length = (rep.driverNameLength + 3) / 4 +
127	    (rep.deviceNameLength + 3) / 4;
128
129 fail:
130    WriteToClient(client, sizeof(xDRI2ConnectReply), &rep);
131    WriteToClient(client, rep.driverNameLength, driverName);
132    WriteToClient(client, rep.deviceNameLength, deviceName);
133
134    return Success;
135}
136
137static int
138ProcDRI2Authenticate(ClientPtr client)
139{
140    REQUEST(xDRI2AuthenticateReq);
141    xDRI2AuthenticateReply rep;
142    DrawablePtr pDraw;
143    int status;
144
145    REQUEST_SIZE_MATCH(xDRI2AuthenticateReq);
146    if (!validDrawable(client, stuff->window, DixGetAttrAccess,
147		       &pDraw, &status))
148	return status;
149
150    rep.type = X_Reply;
151    rep.sequenceNumber = client->sequence;
152    rep.length = 0;
153    rep.authenticated = DRI2Authenticate(pDraw->pScreen, stuff->magic);
154    WriteToClient(client, sizeof(xDRI2AuthenticateReply), &rep);
155
156    return Success;
157}
158
159static void
160DRI2InvalidateBuffersEvent(DrawablePtr pDraw, void *priv)
161{
162    xDRI2InvalidateBuffers event;
163    ClientPtr client = priv;
164
165    event.type = DRI2EventBase + DRI2_InvalidateBuffers;
166    event.drawable = pDraw->id;
167
168    WriteEventsToClient(client, 1, (xEvent *)&event);
169}
170
171static int
172ProcDRI2CreateDrawable(ClientPtr client)
173{
174    REQUEST(xDRI2CreateDrawableReq);
175    DrawablePtr pDrawable;
176    int status;
177
178    REQUEST_SIZE_MATCH(xDRI2CreateDrawableReq);
179
180    if (!validDrawable(client, stuff->drawable, DixAddAccess,
181		       &pDrawable, &status))
182	return status;
183
184    status = DRI2CreateDrawable(client, pDrawable, stuff->drawable,
185				DRI2InvalidateBuffersEvent, client);
186    if (status != Success)
187	return status;
188
189    return Success;
190}
191
192static int
193ProcDRI2DestroyDrawable(ClientPtr client)
194{
195    REQUEST(xDRI2DestroyDrawableReq);
196    DrawablePtr pDrawable;
197    int status;
198
199    REQUEST_SIZE_MATCH(xDRI2DestroyDrawableReq);
200    if (!validDrawable(client, stuff->drawable, DixRemoveAccess,
201		       &pDrawable, &status))
202	return status;
203
204    return Success;
205}
206
207
208static int
209send_buffers_reply(ClientPtr client, DrawablePtr pDrawable,
210		   DRI2BufferPtr *buffers, int count, int width, int height)
211{
212    xDRI2GetBuffersReply rep;
213    int skip = 0;
214    int i;
215
216    if (buffers == NULL)
217	    return BadAlloc;
218
219    if (pDrawable->type == DRAWABLE_WINDOW) {
220	for (i = 0; i < count; i++) {
221	    /* Do not send the real front buffer of a window to the client.
222	     */
223	    if (buffers[i]->attachment == DRI2BufferFrontLeft) {
224		skip++;
225		continue;
226	    }
227	}
228    }
229
230    rep.type = X_Reply;
231    rep.length = (count - skip) * sizeof(xDRI2Buffer) / 4;
232    rep.sequenceNumber = client->sequence;
233    rep.width = width;
234    rep.height = height;
235    rep.count = count - skip;
236    WriteToClient(client, sizeof(xDRI2GetBuffersReply), &rep);
237
238    for (i = 0; i < count; i++) {
239	xDRI2Buffer buffer;
240
241	/* Do not send the real front buffer of a window to the client.
242	 */
243	if ((pDrawable->type == DRAWABLE_WINDOW)
244	    && (buffers[i]->attachment == DRI2BufferFrontLeft)) {
245	    continue;
246	}
247
248	buffer.attachment = buffers[i]->attachment;
249	buffer.name = buffers[i]->name;
250	buffer.pitch = buffers[i]->pitch;
251	buffer.cpp = buffers[i]->cpp;
252	buffer.flags = buffers[i]->flags;
253	WriteToClient(client, sizeof(xDRI2Buffer), &buffer);
254    }
255    return Success;
256}
257
258
259static int
260ProcDRI2GetBuffers(ClientPtr client)
261{
262    REQUEST(xDRI2GetBuffersReq);
263    DrawablePtr pDrawable;
264    DRI2BufferPtr *buffers;
265    int status, width, height, count;
266    unsigned int *attachments;
267
268    REQUEST_FIXED_SIZE(xDRI2GetBuffersReq, stuff->count * 4);
269    if (stuff->count > (INT_MAX / 4))
270        return BadLength;
271
272    if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
273		       &pDrawable, &status))
274	return status;
275
276    if (DRI2ThrottleClient(client, pDrawable))
277	return Success;
278
279    attachments = (unsigned int *) &stuff[1];
280    buffers = DRI2GetBuffers(pDrawable, &width, &height,
281			     attachments, stuff->count, &count);
282
283
284    return send_buffers_reply(client, pDrawable, buffers, count, width, height);
285
286}
287
288static int
289ProcDRI2GetBuffersWithFormat(ClientPtr client)
290{
291    REQUEST(xDRI2GetBuffersReq);
292    DrawablePtr pDrawable;
293    DRI2BufferPtr *buffers;
294    int status, width, height, count;
295    unsigned int *attachments;
296
297    REQUEST_FIXED_SIZE(xDRI2GetBuffersReq, stuff->count * (2 * 4));
298    if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
299		       &pDrawable, &status))
300	return status;
301
302    if (DRI2ThrottleClient(client, pDrawable))
303	return Success;
304
305    attachments = (unsigned int *) &stuff[1];
306    buffers = DRI2GetBuffersWithFormat(pDrawable, &width, &height,
307				       attachments, stuff->count, &count);
308
309    return send_buffers_reply(client, pDrawable, buffers, count, width, height);
310}
311
312static int
313ProcDRI2CopyRegion(ClientPtr client)
314{
315    REQUEST(xDRI2CopyRegionReq);
316    xDRI2CopyRegionReply rep;
317    DrawablePtr pDrawable;
318    int status;
319    RegionPtr pRegion;
320
321    REQUEST_SIZE_MATCH(xDRI2CopyRegionReq);
322
323    if (!validDrawable(client, stuff->drawable, DixWriteAccess,
324		       &pDrawable, &status))
325	return status;
326
327    VERIFY_REGION(pRegion, stuff->region, client, DixReadAccess);
328
329    status = DRI2CopyRegion(pDrawable, pRegion, stuff->dest, stuff->src);
330    if (status != Success)
331	return status;
332
333    /* CopyRegion needs to be a round trip to make sure the X server
334     * queues the swap buffer rendering commands before the DRI client
335     * continues rendering.  The reply has a bitmask to signal the
336     * presense of optional return values as well, but we're not using
337     * that yet.
338     */
339
340    rep.type = X_Reply;
341    rep.length = 0;
342    rep.sequenceNumber = client->sequence;
343
344    WriteToClient(client, sizeof(xDRI2CopyRegionReply), &rep);
345
346    return Success;
347}
348
349static void
350load_swap_reply(xDRI2SwapBuffersReply *rep, CARD64 sbc)
351{
352    rep->swap_hi = sbc >> 32;
353    rep->swap_lo = sbc & 0xffffffff;
354}
355
356static CARD64
357vals_to_card64(CARD32 lo, CARD32 hi)
358{
359    return (CARD64)hi << 32 | lo;
360}
361
362static void
363DRI2SwapEvent(ClientPtr client, void *data, int type, CARD64 ust, CARD64 msc,
364	      CARD64 sbc)
365{
366    xDRI2BufferSwapComplete event;
367    DrawablePtr pDrawable = data;
368
369    event.type = DRI2EventBase + DRI2_BufferSwapComplete;
370    event.event_type = type;
371    event.drawable = pDrawable->id;
372    event.ust_hi = (CARD64)ust >> 32;
373    event.ust_lo = ust & 0xffffffff;
374    event.msc_hi = (CARD64)msc >> 32;
375    event.msc_lo = msc & 0xffffffff;
376    event.sbc_hi = (CARD64)sbc >> 32;
377    event.sbc_lo = sbc & 0xffffffff;
378
379    WriteEventsToClient(client, 1, (xEvent *)&event);
380}
381
382static int
383ProcDRI2SwapBuffers(ClientPtr client)
384{
385    REQUEST(xDRI2SwapBuffersReq);
386    xDRI2SwapBuffersReply rep;
387    DrawablePtr pDrawable;
388    CARD64 target_msc, divisor, remainder, swap_target;
389    int status;
390
391    REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq);
392
393    if (!validDrawable(client, stuff->drawable,
394		       DixReadAccess | DixWriteAccess, &pDrawable, &status))
395	return status;
396
397    /*
398     * Ensures an out of control client can't exhaust our swap queue, and
399     * also orders swaps.
400     */
401    if (DRI2ThrottleClient(client, pDrawable))
402	return Success;
403
404    target_msc = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
405    divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
406    remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
407
408    status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder,
409			     &swap_target, DRI2SwapEvent, pDrawable);
410    if (status != Success)
411	return BadDrawable;
412
413    rep.type = X_Reply;
414    rep.length = 0;
415    rep.sequenceNumber = client->sequence;
416    load_swap_reply(&rep, swap_target);
417
418    WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep);
419
420    return Success;
421}
422
423static void
424load_msc_reply(xDRI2MSCReply *rep, CARD64 ust, CARD64 msc, CARD64 sbc)
425{
426    rep->ust_hi = ust >> 32;
427    rep->ust_lo = ust & 0xffffffff;
428    rep->msc_hi = msc >> 32;
429    rep->msc_lo = msc & 0xffffffff;
430    rep->sbc_hi = sbc >> 32;
431    rep->sbc_lo = sbc & 0xffffffff;
432}
433
434static int
435ProcDRI2GetMSC(ClientPtr client)
436{
437    REQUEST(xDRI2GetMSCReq);
438    xDRI2MSCReply rep;
439    DrawablePtr pDrawable;
440    CARD64 ust, msc, sbc;
441    int status;
442
443    REQUEST_SIZE_MATCH(xDRI2GetMSCReq);
444
445    if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
446		       &status))
447	return status;
448
449    status = DRI2GetMSC(pDrawable, &ust, &msc, &sbc);
450    if (status != Success)
451	return status;
452
453    rep.type = X_Reply;
454    rep.length = 0;
455    rep.sequenceNumber = client->sequence;
456    load_msc_reply(&rep, ust, msc, sbc);
457
458    WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
459
460    return Success;
461}
462
463static int
464ProcDRI2WaitMSC(ClientPtr client)
465{
466    REQUEST(xDRI2WaitMSCReq);
467    DrawablePtr pDrawable;
468    CARD64 target, divisor, remainder;
469    int status;
470
471    /* FIXME: in restart case, client may be gone at this point */
472
473    REQUEST_SIZE_MATCH(xDRI2WaitMSCReq);
474
475    if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
476		       &status))
477	return status;
478
479    target = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
480    divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
481    remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
482
483    status = DRI2WaitMSC(client, pDrawable, target, divisor, remainder);
484    if (status != Success)
485	return status;
486
487    return Success;
488}
489
490int
491ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust, CARD64 msc, CARD64 sbc)
492{
493    xDRI2MSCReply rep;
494
495    rep.type = X_Reply;
496    rep.length = 0;
497    rep.sequenceNumber = client->sequence;
498    load_msc_reply(&rep, ust, msc, sbc);
499
500    WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
501
502    return Success;
503}
504
505static int
506ProcDRI2SwapInterval(ClientPtr client)
507{
508    REQUEST(xDRI2SwapIntervalReq);
509    DrawablePtr pDrawable;
510    int status;
511
512    /* FIXME: in restart case, client may be gone at this point */
513
514    REQUEST_SIZE_MATCH(xDRI2SwapIntervalReq);
515
516    if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
517		       &pDrawable, &status))
518	return status;
519
520    DRI2SwapInterval(pDrawable, stuff->interval);
521
522    return Success;
523}
524
525static int
526ProcDRI2WaitSBC(ClientPtr client)
527{
528    REQUEST(xDRI2WaitSBCReq);
529    DrawablePtr pDrawable;
530    CARD64 target;
531    int status;
532
533    REQUEST_SIZE_MATCH(xDRI2WaitSBCReq);
534
535    if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
536		       &status))
537	return status;
538
539    target = vals_to_card64(stuff->target_sbc_lo, stuff->target_sbc_hi);
540    status = DRI2WaitSBC(client, pDrawable, target);
541
542    return status;
543}
544
545static int
546ProcDRI2Dispatch (ClientPtr client)
547{
548    REQUEST(xReq);
549
550    switch (stuff->data) {
551    case X_DRI2QueryVersion:
552	return ProcDRI2QueryVersion(client);
553    }
554
555    if (!LocalClient(client))
556	return BadRequest;
557
558    switch (stuff->data) {
559    case X_DRI2Connect:
560	return ProcDRI2Connect(client);
561    case X_DRI2Authenticate:
562	return ProcDRI2Authenticate(client);
563    case X_DRI2CreateDrawable:
564	return ProcDRI2CreateDrawable(client);
565    case X_DRI2DestroyDrawable:
566	return ProcDRI2DestroyDrawable(client);
567    case X_DRI2GetBuffers:
568	return ProcDRI2GetBuffers(client);
569    case X_DRI2CopyRegion:
570	return ProcDRI2CopyRegion(client);
571    case X_DRI2GetBuffersWithFormat:
572	return ProcDRI2GetBuffersWithFormat(client);
573    case X_DRI2SwapBuffers:
574	return ProcDRI2SwapBuffers(client);
575    case X_DRI2GetMSC:
576	return ProcDRI2GetMSC(client);
577    case X_DRI2WaitMSC:
578	return ProcDRI2WaitMSC(client);
579    case X_DRI2WaitSBC:
580	return ProcDRI2WaitSBC(client);
581    case X_DRI2SwapInterval:
582	return ProcDRI2SwapInterval(client);
583    default:
584	return BadRequest;
585    }
586}
587
588static int
589SProcDRI2Connect(ClientPtr client)
590{
591    REQUEST(xDRI2ConnectReq);
592    xDRI2ConnectReply rep;
593    int n;
594
595    /* If the client is swapped, it's not local.  Talk to the hand. */
596
597    swaps(&stuff->length, n);
598    if (sizeof(*stuff) / 4 != client->req_len)
599	return BadLength;
600
601    rep.sequenceNumber = client->sequence;
602    swaps(&rep.sequenceNumber, n);
603    rep.length = 0;
604    rep.driverNameLength = 0;
605    rep.deviceNameLength = 0;
606
607    return Success;
608}
609
610static int
611SProcDRI2Dispatch (ClientPtr client)
612{
613    REQUEST(xReq);
614
615    /*
616     * Only local clients are allowed DRI access, but remote clients
617     * still need these requests to find out cleanly.
618     */
619    switch (stuff->data)
620    {
621    case X_DRI2QueryVersion:
622	return ProcDRI2QueryVersion(client);
623    case X_DRI2Connect:
624	return SProcDRI2Connect(client);
625    default:
626	return BadRequest;
627    }
628}
629
630int DRI2EventBase;
631
632static void
633DRI2ExtensionInit(void)
634{
635    dri2Extension = AddExtension(DRI2_NAME,
636				 DRI2NumberEvents,
637				 DRI2NumberErrors,
638				 ProcDRI2Dispatch,
639				 SProcDRI2Dispatch,
640				 NULL,
641				 StandardMinorOpcode);
642
643    DRI2EventBase = dri2Extension->eventBase;
644
645    DRI2ModuleSetup();
646}
647
648extern Bool noDRI2Extension;
649
650_X_HIDDEN ExtensionModule dri2ExtensionModule = {
651    DRI2ExtensionInit,
652    DRI2_NAME,
653    &noDRI2Extension,
654    NULL,
655    NULL
656};
657