Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xfree86 / dri2 / dri2ext.c
CommitLineData
a09e091a
JB
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_FIXED_SIZE(xDRI2GetBuffersReq, stuff->count * 4);
273 if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
274 &pDrawable, &status))
275 return status;
276
277 if (DRI2ThrottleClient(client, pDrawable))
278 return Success;
279
280 attachments = (unsigned int *) &stuff[1];
281 buffers = DRI2GetBuffers(pDrawable, &width, &height,
282 attachments, stuff->count, &count);
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 = (xDRI2CopyRegionReply) {
341 .type = X_Reply,
342 .sequenceNumber = client->sequence,
343 .length = 0
344 };
345
346 WriteToClient(client, sizeof(xDRI2CopyRegionReply), &rep);
347
348 return Success;
349}
350
351static void
352load_swap_reply(xDRI2SwapBuffersReply * rep, CARD64 sbc)
353{
354 rep->swap_hi = sbc >> 32;
355 rep->swap_lo = sbc & 0xffffffff;
356}
357
358static CARD64
359vals_to_card64(CARD32 lo, CARD32 hi)
360{
361 return (CARD64) hi << 32 | lo;
362}
363
364static void
365DRI2SwapEvent(ClientPtr client, void *data, int type, CARD64 ust, CARD64 msc,
366 CARD32 sbc)
367{
368 DrawablePtr pDrawable = data;
369 xDRI2BufferSwapComplete2 event = {
370 .type = DRI2EventBase + DRI2_BufferSwapComplete,
371 .event_type = type,
372 .drawable = pDrawable->id,
373 .ust_hi = (CARD64) ust >> 32,
374 .ust_lo = ust & 0xffffffff,
375 .msc_hi = (CARD64) msc >> 32,
376 .msc_lo = msc & 0xffffffff,
377 .sbc = sbc
378 };
379
380 WriteEventsToClient(client, 1, (xEvent *) &event);
381}
382
383static int
384ProcDRI2SwapBuffers(ClientPtr client)
385{
386 REQUEST(xDRI2SwapBuffersReq);
387 xDRI2SwapBuffersReply rep = {
388 .type = X_Reply,
389 .sequenceNumber = client->sequence,
390 .length = 0
391 };
392 DrawablePtr pDrawable;
393 CARD64 target_msc, divisor, remainder, swap_target;
394 int status;
395
396 REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq);
397
398 if (!validDrawable(client, stuff->drawable,
399 DixReadAccess | DixWriteAccess, &pDrawable, &status))
400 return status;
401
402 /*
403 * Ensures an out of control client can't exhaust our swap queue, and
404 * also orders swaps.
405 */
406 if (DRI2ThrottleClient(client, pDrawable))
407 return Success;
408
409 target_msc = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
410 divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
411 remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
412
413 status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder,
414 &swap_target, DRI2SwapEvent, pDrawable);
415 if (status != Success)
416 return BadDrawable;
417
418 load_swap_reply(&rep, swap_target);
419
420 WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep);
421
422 return Success;
423}
424
425static void
426load_msc_reply(xDRI2MSCReply * rep, CARD64 ust, CARD64 msc, CARD64 sbc)
427{
428 rep->ust_hi = ust >> 32;
429 rep->ust_lo = ust & 0xffffffff;
430 rep->msc_hi = msc >> 32;
431 rep->msc_lo = msc & 0xffffffff;
432 rep->sbc_hi = sbc >> 32;
433 rep->sbc_lo = sbc & 0xffffffff;
434}
435
436static int
437ProcDRI2GetMSC(ClientPtr client)
438{
439 REQUEST(xDRI2GetMSCReq);
440 xDRI2MSCReply rep = {
441 .type = X_Reply,
442 .sequenceNumber = client->sequence,
443 .length = 0
444 };
445 DrawablePtr pDrawable;
446 CARD64 ust, msc, sbc;
447 int status;
448
449 REQUEST_SIZE_MATCH(xDRI2GetMSCReq);
450
451 if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
452 &status))
453 return status;
454
455 status = DRI2GetMSC(pDrawable, &ust, &msc, &sbc);
456 if (status != Success)
457 return status;
458
459 load_msc_reply(&rep, ust, msc, sbc);
460
461 WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
462
463 return Success;
464}
465
466static int
467ProcDRI2WaitMSC(ClientPtr client)
468{
469 REQUEST(xDRI2WaitMSCReq);
470 DrawablePtr pDrawable;
471 CARD64 target, divisor, remainder;
472 int status;
473
474 /* FIXME: in restart case, client may be gone at this point */
475
476 REQUEST_SIZE_MATCH(xDRI2WaitMSCReq);
477
478 if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
479 &status))
480 return status;
481
482 target = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
483 divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
484 remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
485
486 status = DRI2WaitMSC(client, pDrawable, target, divisor, remainder);
487 if (status != Success)
488 return status;
489
490 return Success;
491}
492
493int
494ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust, CARD64 msc, CARD64 sbc)
495{
496 xDRI2MSCReply rep = {
497 .type = X_Reply,
498 .sequenceNumber = client->sequence,
499 .length = 0
500 };
501
502 load_msc_reply(&rep, ust, msc, sbc);
503
504 WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
505
506 return Success;
507}
508
509static int
510ProcDRI2SwapInterval(ClientPtr client)
511{
512 REQUEST(xDRI2SwapIntervalReq);
513 DrawablePtr pDrawable;
514 int status;
515
516 /* FIXME: in restart case, client may be gone at this point */
517
518 REQUEST_SIZE_MATCH(xDRI2SwapIntervalReq);
519
520 if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess,
521 &pDrawable, &status))
522 return status;
523
524 DRI2SwapInterval(pDrawable, stuff->interval);
525
526 return Success;
527}
528
529static int
530ProcDRI2WaitSBC(ClientPtr client)
531{
532 REQUEST(xDRI2WaitSBCReq);
533 DrawablePtr pDrawable;
534 CARD64 target;
535 int status;
536
537 REQUEST_SIZE_MATCH(xDRI2WaitSBCReq);
538
539 if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable,
540 &status))
541 return status;
542
543 target = vals_to_card64(stuff->target_sbc_lo, stuff->target_sbc_hi);
544 status = DRI2WaitSBC(client, pDrawable, target);
545
546 return status;
547}
548
549static int
550ProcDRI2GetParam(ClientPtr client)
551{
552 REQUEST(xDRI2GetParamReq);
553 xDRI2GetParamReply rep = {
554 .type = X_Reply,
555 .sequenceNumber = client->sequence,
556 .length = 0
557 };
558 DrawablePtr pDrawable;
559 CARD64 value;
560 int status;
561
562 REQUEST_SIZE_MATCH(xDRI2GetParamReq);
563
564 if (!validDrawable(client, stuff->drawable, DixReadAccess,
565 &pDrawable, &status))
566 return status;
567
568 status = DRI2GetParam(client, pDrawable, stuff->param,
569 &rep.is_param_recognized, &value);
570 rep.value_hi = value >> 32;
571 rep.value_lo = value & 0xffffffff;
572
573 if (status != Success)
574 return status;
575
576 WriteToClient(client, sizeof(xDRI2GetParamReply), &rep);
577
578 return status;
579}
580
581static int
582ProcDRI2Dispatch(ClientPtr client)
583{
584 REQUEST(xReq);
585
586 switch (stuff->data) {
587 case X_DRI2QueryVersion:
588 return ProcDRI2QueryVersion(client);
589 }
590
591 if (!client->local)
592 return BadRequest;
593
594 switch (stuff->data) {
595 case X_DRI2Connect:
596 return ProcDRI2Connect(client);
597 case X_DRI2Authenticate:
598 return ProcDRI2Authenticate(client);
599 case X_DRI2CreateDrawable:
600 return ProcDRI2CreateDrawable(client);
601 case X_DRI2DestroyDrawable:
602 return ProcDRI2DestroyDrawable(client);
603 case X_DRI2GetBuffers:
604 return ProcDRI2GetBuffers(client);
605 case X_DRI2CopyRegion:
606 return ProcDRI2CopyRegion(client);
607 case X_DRI2GetBuffersWithFormat:
608 return ProcDRI2GetBuffersWithFormat(client);
609 case X_DRI2SwapBuffers:
610 return ProcDRI2SwapBuffers(client);
611 case X_DRI2GetMSC:
612 return ProcDRI2GetMSC(client);
613 case X_DRI2WaitMSC:
614 return ProcDRI2WaitMSC(client);
615 case X_DRI2WaitSBC:
616 return ProcDRI2WaitSBC(client);
617 case X_DRI2SwapInterval:
618 return ProcDRI2SwapInterval(client);
619 case X_DRI2GetParam:
620 return ProcDRI2GetParam(client);
621 default:
622 return BadRequest;
623 }
624}
625
626static int
627SProcDRI2Connect(ClientPtr client)
628{
629 REQUEST(xDRI2ConnectReq);
630 xDRI2ConnectReply rep = {
631 .type = X_Reply,
632 .sequenceNumber = client->sequence,
633 .length = 0,
634 .driverNameLength = 0,
635 .deviceNameLength = 0
636 };
637
638 /* If the client is swapped, it's not local. Talk to the hand. */
639
640 swaps(&stuff->length);
641 if (sizeof(*stuff) / 4 != client->req_len)
642 return BadLength;
643
644 swaps(&rep.sequenceNumber);
645
646 WriteToClient(client, sizeof(xDRI2ConnectReply), &rep);
647
648 return Success;
649}
650
651static int
652SProcDRI2Dispatch(ClientPtr client)
653{
654 REQUEST(xReq);
655
656 /*
657 * Only local clients are allowed DRI access, but remote clients
658 * still need these requests to find out cleanly.
659 */
660 switch (stuff->data) {
661 case X_DRI2QueryVersion:
662 return ProcDRI2QueryVersion(client);
663 case X_DRI2Connect:
664 return SProcDRI2Connect(client);
665 default:
666 return BadRequest;
667 }
668}
669
670void
671DRI2ExtensionInit(void)
672{
673 ExtensionEntry *dri2Extension;
674
675#ifdef PANORAMIX
676 if (!noPanoramiXExtension)
677 return;
678#endif
679
680 dri2Extension = AddExtension(DRI2_NAME,
681 DRI2NumberEvents,
682 DRI2NumberErrors,
683 ProcDRI2Dispatch,
684 SProcDRI2Dispatch, NULL, StandardMinorOpcode);
685
686 DRI2EventBase = dri2Extension->eventBase;
687
688 DRI2ModuleSetup();
689}