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