dri2ext.c revision 475c125c
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 (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess, 270 &pDrawable, &status)) 271 return status; 272 273 if (DRI2ThrottleClient(client, pDrawable)) 274 return Success; 275 276 attachments = (unsigned int *) &stuff[1]; 277 buffers = DRI2GetBuffers(pDrawable, &width, &height, 278 attachments, stuff->count, &count); 279 280 281 return send_buffers_reply(client, pDrawable, buffers, count, width, height); 282 283} 284 285static int 286ProcDRI2GetBuffersWithFormat(ClientPtr client) 287{ 288 REQUEST(xDRI2GetBuffersReq); 289 DrawablePtr pDrawable; 290 DRI2BufferPtr *buffers; 291 int status, width, height, count; 292 unsigned int *attachments; 293 294 REQUEST_FIXED_SIZE(xDRI2GetBuffersReq, stuff->count * (2 * 4)); 295 if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess, 296 &pDrawable, &status)) 297 return status; 298 299 if (DRI2ThrottleClient(client, pDrawable)) 300 return Success; 301 302 attachments = (unsigned int *) &stuff[1]; 303 buffers = DRI2GetBuffersWithFormat(pDrawable, &width, &height, 304 attachments, stuff->count, &count); 305 306 return send_buffers_reply(client, pDrawable, buffers, count, width, height); 307} 308 309static int 310ProcDRI2CopyRegion(ClientPtr client) 311{ 312 REQUEST(xDRI2CopyRegionReq); 313 xDRI2CopyRegionReply rep; 314 DrawablePtr pDrawable; 315 int status; 316 RegionPtr pRegion; 317 318 REQUEST_SIZE_MATCH(xDRI2CopyRegionReq); 319 320 if (!validDrawable(client, stuff->drawable, DixWriteAccess, 321 &pDrawable, &status)) 322 return status; 323 324 VERIFY_REGION(pRegion, stuff->region, client, DixReadAccess); 325 326 status = DRI2CopyRegion(pDrawable, pRegion, stuff->dest, stuff->src); 327 if (status != Success) 328 return status; 329 330 /* CopyRegion needs to be a round trip to make sure the X server 331 * queues the swap buffer rendering commands before the DRI client 332 * continues rendering. The reply has a bitmask to signal the 333 * presense of optional return values as well, but we're not using 334 * that yet. 335 */ 336 337 rep.type = X_Reply; 338 rep.length = 0; 339 rep.sequenceNumber = client->sequence; 340 341 WriteToClient(client, sizeof(xDRI2CopyRegionReply), &rep); 342 343 return Success; 344} 345 346static void 347load_swap_reply(xDRI2SwapBuffersReply *rep, CARD64 sbc) 348{ 349 rep->swap_hi = sbc >> 32; 350 rep->swap_lo = sbc & 0xffffffff; 351} 352 353static CARD64 354vals_to_card64(CARD32 lo, CARD32 hi) 355{ 356 return (CARD64)hi << 32 | lo; 357} 358 359static void 360DRI2SwapEvent(ClientPtr client, void *data, int type, CARD64 ust, CARD64 msc, 361 CARD64 sbc) 362{ 363 xDRI2BufferSwapComplete event; 364 DrawablePtr pDrawable = data; 365 366 event.type = DRI2EventBase + DRI2_BufferSwapComplete; 367 event.event_type = type; 368 event.drawable = pDrawable->id; 369 event.ust_hi = (CARD64)ust >> 32; 370 event.ust_lo = ust & 0xffffffff; 371 event.msc_hi = (CARD64)msc >> 32; 372 event.msc_lo = msc & 0xffffffff; 373 event.sbc_hi = (CARD64)sbc >> 32; 374 event.sbc_lo = sbc & 0xffffffff; 375 376 WriteEventsToClient(client, 1, (xEvent *)&event); 377} 378 379static int 380ProcDRI2SwapBuffers(ClientPtr client) 381{ 382 REQUEST(xDRI2SwapBuffersReq); 383 xDRI2SwapBuffersReply rep; 384 DrawablePtr pDrawable; 385 CARD64 target_msc, divisor, remainder, swap_target; 386 int status; 387 388 REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq); 389 390 if (!validDrawable(client, stuff->drawable, 391 DixReadAccess | DixWriteAccess, &pDrawable, &status)) 392 return status; 393 394 /* 395 * Ensures an out of control client can't exhaust our swap queue, and 396 * also orders swaps. 397 */ 398 if (DRI2ThrottleClient(client, pDrawable)) 399 return Success; 400 401 target_msc = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi); 402 divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi); 403 remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi); 404 405 status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder, 406 &swap_target, DRI2SwapEvent, pDrawable); 407 if (status != Success) 408 return BadDrawable; 409 410 rep.type = X_Reply; 411 rep.length = 0; 412 rep.sequenceNumber = client->sequence; 413 load_swap_reply(&rep, swap_target); 414 415 WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep); 416 417 return Success; 418} 419 420static void 421load_msc_reply(xDRI2MSCReply *rep, CARD64 ust, CARD64 msc, CARD64 sbc) 422{ 423 rep->ust_hi = ust >> 32; 424 rep->ust_lo = ust & 0xffffffff; 425 rep->msc_hi = msc >> 32; 426 rep->msc_lo = msc & 0xffffffff; 427 rep->sbc_hi = sbc >> 32; 428 rep->sbc_lo = sbc & 0xffffffff; 429} 430 431static int 432ProcDRI2GetMSC(ClientPtr client) 433{ 434 REQUEST(xDRI2GetMSCReq); 435 xDRI2MSCReply rep; 436 DrawablePtr pDrawable; 437 CARD64 ust, msc, sbc; 438 int status; 439 440 REQUEST_SIZE_MATCH(xDRI2GetMSCReq); 441 442 if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable, 443 &status)) 444 return status; 445 446 status = DRI2GetMSC(pDrawable, &ust, &msc, &sbc); 447 if (status != Success) 448 return status; 449 450 rep.type = X_Reply; 451 rep.length = 0; 452 rep.sequenceNumber = client->sequence; 453 load_msc_reply(&rep, ust, msc, sbc); 454 455 WriteToClient(client, sizeof(xDRI2MSCReply), &rep); 456 457 return Success; 458} 459 460static int 461ProcDRI2WaitMSC(ClientPtr client) 462{ 463 REQUEST(xDRI2WaitMSCReq); 464 DrawablePtr pDrawable; 465 CARD64 target, divisor, remainder; 466 int status; 467 468 /* FIXME: in restart case, client may be gone at this point */ 469 470 REQUEST_SIZE_MATCH(xDRI2WaitMSCReq); 471 472 if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable, 473 &status)) 474 return status; 475 476 target = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi); 477 divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi); 478 remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi); 479 480 status = DRI2WaitMSC(client, pDrawable, target, divisor, remainder); 481 if (status != Success) 482 return status; 483 484 return Success; 485} 486 487int 488ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust, CARD64 msc, CARD64 sbc) 489{ 490 xDRI2MSCReply rep; 491 492 rep.type = X_Reply; 493 rep.length = 0; 494 rep.sequenceNumber = client->sequence; 495 load_msc_reply(&rep, ust, msc, sbc); 496 497 WriteToClient(client, sizeof(xDRI2MSCReply), &rep); 498 499 return Success; 500} 501 502static int 503ProcDRI2SwapInterval(ClientPtr client) 504{ 505 REQUEST(xDRI2SwapIntervalReq); 506 DrawablePtr pDrawable; 507 int status; 508 509 /* FIXME: in restart case, client may be gone at this point */ 510 511 REQUEST_SIZE_MATCH(xDRI2SwapIntervalReq); 512 513 if (!validDrawable(client, stuff->drawable, DixReadAccess | DixWriteAccess, 514 &pDrawable, &status)) 515 return status; 516 517 DRI2SwapInterval(pDrawable, stuff->interval); 518 519 return Success; 520} 521 522static int 523ProcDRI2WaitSBC(ClientPtr client) 524{ 525 REQUEST(xDRI2WaitSBCReq); 526 DrawablePtr pDrawable; 527 CARD64 target; 528 int status; 529 530 REQUEST_SIZE_MATCH(xDRI2WaitSBCReq); 531 532 if (!validDrawable(client, stuff->drawable, DixReadAccess, &pDrawable, 533 &status)) 534 return status; 535 536 target = vals_to_card64(stuff->target_sbc_lo, stuff->target_sbc_hi); 537 status = DRI2WaitSBC(client, pDrawable, target); 538 539 return status; 540} 541 542static int 543ProcDRI2Dispatch (ClientPtr client) 544{ 545 REQUEST(xReq); 546 547 switch (stuff->data) { 548 case X_DRI2QueryVersion: 549 return ProcDRI2QueryVersion(client); 550 } 551 552 if (!LocalClient(client)) 553 return BadRequest; 554 555 switch (stuff->data) { 556 case X_DRI2Connect: 557 return ProcDRI2Connect(client); 558 case X_DRI2Authenticate: 559 return ProcDRI2Authenticate(client); 560 case X_DRI2CreateDrawable: 561 return ProcDRI2CreateDrawable(client); 562 case X_DRI2DestroyDrawable: 563 return ProcDRI2DestroyDrawable(client); 564 case X_DRI2GetBuffers: 565 return ProcDRI2GetBuffers(client); 566 case X_DRI2CopyRegion: 567 return ProcDRI2CopyRegion(client); 568 case X_DRI2GetBuffersWithFormat: 569 return ProcDRI2GetBuffersWithFormat(client); 570 case X_DRI2SwapBuffers: 571 return ProcDRI2SwapBuffers(client); 572 case X_DRI2GetMSC: 573 return ProcDRI2GetMSC(client); 574 case X_DRI2WaitMSC: 575 return ProcDRI2WaitMSC(client); 576 case X_DRI2WaitSBC: 577 return ProcDRI2WaitSBC(client); 578 case X_DRI2SwapInterval: 579 return ProcDRI2SwapInterval(client); 580 default: 581 return BadRequest; 582 } 583} 584 585static int 586SProcDRI2Connect(ClientPtr client) 587{ 588 REQUEST(xDRI2ConnectReq); 589 xDRI2ConnectReply rep; 590 int n; 591 592 /* If the client is swapped, it's not local. Talk to the hand. */ 593 594 swaps(&stuff->length, n); 595 if (sizeof(*stuff) / 4 != client->req_len) 596 return BadLength; 597 598 rep.sequenceNumber = client->sequence; 599 swaps(&rep.sequenceNumber, n); 600 rep.length = 0; 601 rep.driverNameLength = 0; 602 rep.deviceNameLength = 0; 603 604 return Success; 605} 606 607static int 608SProcDRI2Dispatch (ClientPtr client) 609{ 610 REQUEST(xReq); 611 612 /* 613 * Only local clients are allowed DRI access, but remote clients 614 * still need these requests to find out cleanly. 615 */ 616 switch (stuff->data) 617 { 618 case X_DRI2QueryVersion: 619 return ProcDRI2QueryVersion(client); 620 case X_DRI2Connect: 621 return SProcDRI2Connect(client); 622 default: 623 return BadRequest; 624 } 625} 626 627int DRI2EventBase; 628 629static void 630DRI2ExtensionInit(void) 631{ 632 dri2Extension = AddExtension(DRI2_NAME, 633 DRI2NumberEvents, 634 DRI2NumberErrors, 635 ProcDRI2Dispatch, 636 SProcDRI2Dispatch, 637 NULL, 638 StandardMinorOpcode); 639 640 DRI2EventBase = dri2Extension->eventBase; 641 642 DRI2ModuleSetup(); 643} 644 645extern Bool noDRI2Extension; 646 647_X_HIDDEN ExtensionModule dri2ExtensionModule = { 648 DRI2ExtensionInit, 649 DRI2_NAME, 650 &noDRI2Extension, 651 NULL, 652 NULL 653}; 654