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