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