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