1/* 2 * Copyright © 2006 Keith Packard 3 * Copyright © 2008 Red Hat, Inc. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that copyright 8 * notice and this permission notice appear in supporting documentation, and 9 * that the name of the copyright holders not be used in advertising or 10 * publicity pertaining to distribution of the software without specific, 11 * written prior permission. The copyright holders make no representations 12 * about the suitability of this software for any purpose. It is provided "as 13 * is" without express or implied warranty. 14 * 15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 21 * OF THIS SOFTWARE. 22 */ 23 24#include "randrstr.h" 25#include <X11/Xatom.h> 26 27RESTYPE RROutputType; 28 29/* 30 * Notify the output of some change 31 */ 32void 33RROutputChanged(RROutputPtr output, Bool configChanged) 34{ 35 /* set changed bits on the primary screen only */ 36 ScreenPtr pScreen = output->pScreen; 37 rrScrPrivPtr primarysp; 38 39 output->changed = TRUE; 40 if (!pScreen) 41 return; 42 43 if (pScreen->isGPU) { 44 ScreenPtr primary = pScreen->current_primary; 45 if (!primary) 46 return; 47 primarysp = rrGetScrPriv(primary); 48 } 49 else { 50 primarysp = rrGetScrPriv(pScreen); 51 } 52 53 RRSetChanged(pScreen); 54 if (configChanged) 55 primarysp->configChanged = TRUE; 56} 57 58/* 59 * Create an output 60 */ 61 62RROutputPtr 63RROutputCreate(ScreenPtr pScreen, 64 const char *name, int nameLength, void *devPrivate) 65{ 66 RROutputPtr output; 67 RROutputPtr *outputs; 68 rrScrPrivPtr pScrPriv; 69 Atom nonDesktopAtom; 70 71 if (!RRInit()) 72 return NULL; 73 74 pScrPriv = rrGetScrPriv(pScreen); 75 76 outputs = reallocarray(pScrPriv->outputs, 77 pScrPriv->numOutputs + 1, sizeof(RROutputPtr)); 78 if (!outputs) 79 return NULL; 80 81 pScrPriv->outputs = outputs; 82 83 output = malloc(sizeof(RROutputRec) + nameLength + 1); 84 if (!output) 85 return NULL; 86 output->id = FakeClientID(0); 87 output->pScreen = pScreen; 88 output->name = (char *) (output + 1); 89 output->nameLength = nameLength; 90 memcpy(output->name, name, nameLength); 91 output->name[nameLength] = '\0'; 92 output->connection = RR_UnknownConnection; 93 output->subpixelOrder = SubPixelUnknown; 94 output->mmWidth = 0; 95 output->mmHeight = 0; 96 output->crtc = NULL; 97 output->numCrtcs = 0; 98 output->crtcs = NULL; 99 output->numClones = 0; 100 output->clones = NULL; 101 output->numModes = 0; 102 output->numPreferred = 0; 103 output->modes = NULL; 104 output->numUserModes = 0; 105 output->userModes = NULL; 106 output->properties = NULL; 107 output->pendingProperties = FALSE; 108 output->changed = FALSE; 109 output->nonDesktop = FALSE; 110 output->devPrivate = devPrivate; 111 112 if (!AddResource(output->id, RROutputType, (void *) output)) 113 return NULL; 114 115 pScrPriv->outputs[pScrPriv->numOutputs++] = output; 116 117 nonDesktopAtom = MakeAtom(RR_PROPERTY_NON_DESKTOP, strlen(RR_PROPERTY_NON_DESKTOP), TRUE); 118 if (nonDesktopAtom != BAD_RESOURCE) { 119 static const INT32 values[2] = { 0, 1 }; 120 (void) RRConfigureOutputProperty(output, nonDesktopAtom, FALSE, FALSE, FALSE, 121 2, values); 122 } 123 RROutputSetNonDesktop(output, FALSE); 124 RRResourcesChanged(pScreen); 125 126 return output; 127} 128 129/* 130 * Notify extension that output parameters have been changed 131 */ 132Bool 133RROutputSetClones(RROutputPtr output, RROutputPtr * clones, int numClones) 134{ 135 RROutputPtr *newClones; 136 int i; 137 138 if (numClones == output->numClones) { 139 for (i = 0; i < numClones; i++) 140 if (output->clones[i] != clones[i]) 141 break; 142 if (i == numClones) 143 return TRUE; 144 } 145 if (numClones) { 146 newClones = xallocarray(numClones, sizeof(RROutputPtr)); 147 if (!newClones) 148 return FALSE; 149 } 150 else 151 newClones = NULL; 152 free(output->clones); 153 memcpy(newClones, clones, numClones * sizeof(RROutputPtr)); 154 output->clones = newClones; 155 output->numClones = numClones; 156 RROutputChanged(output, TRUE); 157 return TRUE; 158} 159 160Bool 161RROutputSetModes(RROutputPtr output, 162 RRModePtr * modes, int numModes, int numPreferred) 163{ 164 RRModePtr *newModes; 165 int i; 166 167 if (numModes == output->numModes && numPreferred == output->numPreferred) { 168 for (i = 0; i < numModes; i++) 169 if (output->modes[i] != modes[i]) 170 break; 171 if (i == numModes) { 172 for (i = 0; i < numModes; i++) 173 RRModeDestroy(modes[i]); 174 return TRUE; 175 } 176 } 177 178 if (numModes) { 179 newModes = xallocarray(numModes, sizeof(RRModePtr)); 180 if (!newModes) 181 return FALSE; 182 } 183 else 184 newModes = NULL; 185 if (output->modes) { 186 for (i = 0; i < output->numModes; i++) 187 RRModeDestroy(output->modes[i]); 188 free(output->modes); 189 } 190 memcpy(newModes, modes, numModes * sizeof(RRModePtr)); 191 output->modes = newModes; 192 output->numModes = numModes; 193 output->numPreferred = numPreferred; 194 RROutputChanged(output, TRUE); 195 return TRUE; 196} 197 198int 199RROutputAddUserMode(RROutputPtr output, RRModePtr mode) 200{ 201 int m; 202 ScreenPtr pScreen = output->pScreen; 203 204 rrScrPriv(pScreen); 205 RRModePtr *newModes; 206 207 /* Check to see if this mode is already listed for this output */ 208 for (m = 0; m < output->numModes + output->numUserModes; m++) { 209 RRModePtr e = (m < output->numModes ? 210 output->modes[m] : 211 output->userModes[m - output->numModes]); 212 if (mode == e) 213 return Success; 214 } 215 216 /* Check with the DDX to see if this mode is OK */ 217 if (pScrPriv->rrOutputValidateMode) 218 if (!pScrPriv->rrOutputValidateMode(pScreen, output, mode)) 219 return BadMatch; 220 221 if (output->userModes) 222 newModes = reallocarray(output->userModes, 223 output->numUserModes + 1, sizeof(RRModePtr)); 224 else 225 newModes = malloc(sizeof(RRModePtr)); 226 if (!newModes) 227 return BadAlloc; 228 229 output->userModes = newModes; 230 output->userModes[output->numUserModes++] = mode; 231 ++mode->refcnt; 232 RROutputChanged(output, TRUE); 233 RRTellChanged(pScreen); 234 return Success; 235} 236 237int 238RROutputDeleteUserMode(RROutputPtr output, RRModePtr mode) 239{ 240 int m; 241 242 /* Find this mode in the user mode list */ 243 for (m = 0; m < output->numUserModes; m++) { 244 RRModePtr e = output->userModes[m]; 245 246 if (mode == e) 247 break; 248 } 249 /* Not there, access error */ 250 if (m == output->numUserModes) 251 return BadAccess; 252 253 /* make sure the mode isn't active for this output */ 254 if (output->crtc && output->crtc->mode == mode) 255 return BadMatch; 256 257 memmove(output->userModes + m, output->userModes + m + 1, 258 (output->numUserModes - m - 1) * sizeof(RRModePtr)); 259 output->numUserModes--; 260 RRModeDestroy(mode); 261 return Success; 262} 263 264Bool 265RROutputSetCrtcs(RROutputPtr output, RRCrtcPtr * crtcs, int numCrtcs) 266{ 267 RRCrtcPtr *newCrtcs; 268 int i; 269 270 if (numCrtcs == output->numCrtcs) { 271 for (i = 0; i < numCrtcs; i++) 272 if (output->crtcs[i] != crtcs[i]) 273 break; 274 if (i == numCrtcs) 275 return TRUE; 276 } 277 if (numCrtcs) { 278 newCrtcs = xallocarray(numCrtcs, sizeof(RRCrtcPtr)); 279 if (!newCrtcs) 280 return FALSE; 281 } 282 else 283 newCrtcs = NULL; 284 free(output->crtcs); 285 memcpy(newCrtcs, crtcs, numCrtcs * sizeof(RRCrtcPtr)); 286 output->crtcs = newCrtcs; 287 output->numCrtcs = numCrtcs; 288 RROutputChanged(output, TRUE); 289 return TRUE; 290} 291 292Bool 293RROutputSetConnection(RROutputPtr output, CARD8 connection) 294{ 295 if (output->connection == connection) 296 return TRUE; 297 output->connection = connection; 298 RROutputChanged(output, TRUE); 299 return TRUE; 300} 301 302Bool 303RROutputSetSubpixelOrder(RROutputPtr output, int subpixelOrder) 304{ 305 if (output->subpixelOrder == subpixelOrder) 306 return TRUE; 307 308 output->subpixelOrder = subpixelOrder; 309 RROutputChanged(output, FALSE); 310 return TRUE; 311} 312 313Bool 314RROutputSetPhysicalSize(RROutputPtr output, int mmWidth, int mmHeight) 315{ 316 if (output->mmWidth == mmWidth && output->mmHeight == mmHeight) 317 return TRUE; 318 output->mmWidth = mmWidth; 319 output->mmHeight = mmHeight; 320 RROutputChanged(output, FALSE); 321 return TRUE; 322} 323 324Bool 325RROutputSetNonDesktop(RROutputPtr output, Bool nonDesktop) 326{ 327 const char *nonDesktopStr = RR_PROPERTY_NON_DESKTOP; 328 Atom nonDesktopProp = MakeAtom(nonDesktopStr, strlen(nonDesktopStr), TRUE); 329 uint32_t value = nonDesktop ? 1 : 0; 330 331 if (nonDesktopProp == None || nonDesktopProp == BAD_RESOURCE) 332 return FALSE; 333 334 return RRChangeOutputProperty(output, nonDesktopProp, XA_INTEGER, 32, 335 PropModeReplace, 1, &value, TRUE, FALSE) == Success; 336} 337 338void 339RRDeliverOutputEvent(ClientPtr client, WindowPtr pWin, RROutputPtr output) 340{ 341 ScreenPtr pScreen = pWin->drawable.pScreen; 342 343 rrScrPriv(pScreen); 344 RRCrtcPtr crtc = output->crtc; 345 RRModePtr mode = crtc ? crtc->mode : NULL; 346 347 xRROutputChangeNotifyEvent oe = { 348 .type = RRNotify + RREventBase, 349 .subCode = RRNotify_OutputChange, 350 .timestamp = pScrPriv->lastSetTime.milliseconds, 351 .configTimestamp = pScrPriv->lastConfigTime.milliseconds, 352 .window = pWin->drawable.id, 353 .output = output->id, 354 .crtc = crtc ? crtc->id : None, 355 .mode = mode ? mode->mode.id : None, 356 .rotation = crtc ? crtc->rotation : RR_Rotate_0, 357 .connection = output->nonDesktop ? RR_Disconnected : output->connection, 358 .subpixelOrder = output->subpixelOrder 359 }; 360 WriteEventsToClient(client, 1, (xEvent *) &oe); 361} 362 363/* 364 * Destroy a Output at shutdown 365 */ 366void 367RROutputDestroy(RROutputPtr output) 368{ 369 FreeResource(output->id, 0); 370} 371 372static int 373RROutputDestroyResource(void *value, XID pid) 374{ 375 RROutputPtr output = (RROutputPtr) value; 376 ScreenPtr pScreen = output->pScreen; 377 int m; 378 379 if (pScreen) { 380 rrScrPriv(pScreen); 381 int i; 382 RRLeasePtr lease, next; 383 384 xorg_list_for_each_entry_safe(lease, next, &pScrPriv->leases, list) { 385 int o; 386 for (o = 0; o < lease->numOutputs; o++) { 387 if (lease->outputs[o] == output) { 388 RRTerminateLease(lease); 389 break; 390 } 391 } 392 } 393 394 if (pScrPriv->primaryOutput == output) 395 pScrPriv->primaryOutput = NULL; 396 397 for (i = 0; i < pScrPriv->numOutputs; i++) { 398 if (pScrPriv->outputs[i] == output) { 399 memmove(pScrPriv->outputs + i, pScrPriv->outputs + i + 1, 400 (pScrPriv->numOutputs - (i + 1)) * sizeof(RROutputPtr)); 401 --pScrPriv->numOutputs; 402 break; 403 } 404 } 405 406 RRResourcesChanged(pScreen); 407 } 408 if (output->modes) { 409 for (m = 0; m < output->numModes; m++) 410 RRModeDestroy(output->modes[m]); 411 free(output->modes); 412 } 413 414 for (m = 0; m < output->numUserModes; m++) 415 RRModeDestroy(output->userModes[m]); 416 free(output->userModes); 417 418 free(output->crtcs); 419 free(output->clones); 420 RRDeleteAllOutputProperties(output); 421 free(output); 422 return 1; 423} 424 425/* 426 * Initialize output type 427 */ 428Bool 429RROutputInit(void) 430{ 431 RROutputType = CreateNewResourceType(RROutputDestroyResource, "OUTPUT"); 432 if (!RROutputType) 433 return FALSE; 434 435 return TRUE; 436} 437 438/* 439 * Initialize output type error value 440 */ 441void 442RROutputInitErrorValue(void) 443{ 444 SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput); 445} 446 447#define OutputInfoExtra (SIZEOF(xRRGetOutputInfoReply) - 32) 448 449int 450ProcRRGetOutputInfo(ClientPtr client) 451{ 452 REQUEST(xRRGetOutputInfoReq); 453 xRRGetOutputInfoReply rep; 454 RROutputPtr output; 455 CARD8 *extra; 456 unsigned long extraLen; 457 ScreenPtr pScreen; 458 rrScrPrivPtr pScrPriv; 459 RRCrtc *crtcs; 460 RRMode *modes; 461 RROutput *clones; 462 char *name; 463 int i; 464 Bool leased; 465 466 REQUEST_SIZE_MATCH(xRRGetOutputInfoReq); 467 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 468 469 leased = RROutputIsLeased(output); 470 471 pScreen = output->pScreen; 472 pScrPriv = rrGetScrPriv(pScreen); 473 474 if (leased) { 475 rep = (xRRGetOutputInfoReply) { 476 .type = X_Reply, 477 .status = RRSetConfigSuccess, 478 .sequenceNumber = client->sequence, 479 .length = bytes_to_int32(OutputInfoExtra), 480 .timestamp = pScrPriv->lastSetTime.milliseconds, 481 .crtc = None, 482 .mmWidth = 0, 483 .mmHeight = 0, 484 .connection = RR_Disconnected, 485 .subpixelOrder = SubPixelUnknown, 486 .nCrtcs = 0, 487 .nModes = 0, 488 .nPreferred = 0, 489 .nClones = 0, 490 .nameLength = output->nameLength 491 }; 492 extraLen = bytes_to_int32(rep.nameLength) << 2; 493 if (extraLen) { 494 rep.length += bytes_to_int32(extraLen); 495 extra = calloc(1, extraLen); 496 if (!extra) 497 return BadAlloc; 498 } 499 else 500 extra = NULL; 501 502 name = (char *) extra; 503 } else { 504 rep = (xRRGetOutputInfoReply) { 505 .type = X_Reply, 506 .status = RRSetConfigSuccess, 507 .sequenceNumber = client->sequence, 508 .length = bytes_to_int32(OutputInfoExtra), 509 .timestamp = pScrPriv->lastSetTime.milliseconds, 510 .crtc = output->crtc ? output->crtc->id : None, 511 .mmWidth = output->mmWidth, 512 .mmHeight = output->mmHeight, 513 .connection = output->nonDesktop ? RR_Disconnected : output->connection, 514 .subpixelOrder = output->subpixelOrder, 515 .nCrtcs = output->numCrtcs, 516 .nModes = output->numModes + output->numUserModes, 517 .nPreferred = output->numPreferred, 518 .nClones = output->numClones, 519 .nameLength = output->nameLength 520 }; 521 extraLen = ((output->numCrtcs + 522 output->numModes + output->numUserModes + 523 output->numClones + bytes_to_int32(rep.nameLength)) << 2); 524 525 if (extraLen) { 526 rep.length += bytes_to_int32(extraLen); 527 extra = calloc(1, extraLen); 528 if (!extra) 529 return BadAlloc; 530 } 531 else 532 extra = NULL; 533 534 crtcs = (RRCrtc *) extra; 535 modes = (RRMode *) (crtcs + output->numCrtcs); 536 clones = (RROutput *) (modes + output->numModes + output->numUserModes); 537 name = (char *) (clones + output->numClones); 538 539 for (i = 0; i < output->numCrtcs; i++) { 540 crtcs[i] = output->crtcs[i]->id; 541 if (client->swapped) 542 swapl(&crtcs[i]); 543 } 544 for (i = 0; i < output->numModes + output->numUserModes; i++) { 545 if (i < output->numModes) 546 modes[i] = output->modes[i]->mode.id; 547 else 548 modes[i] = output->userModes[i - output->numModes]->mode.id; 549 if (client->swapped) 550 swapl(&modes[i]); 551 } 552 for (i = 0; i < output->numClones; i++) { 553 clones[i] = output->clones[i]->id; 554 if (client->swapped) 555 swapl(&clones[i]); 556 } 557 } 558 memcpy(name, output->name, output->nameLength); 559 if (client->swapped) { 560 swaps(&rep.sequenceNumber); 561 swapl(&rep.length); 562 swapl(&rep.timestamp); 563 swapl(&rep.crtc); 564 swapl(&rep.mmWidth); 565 swapl(&rep.mmHeight); 566 swaps(&rep.nCrtcs); 567 swaps(&rep.nModes); 568 swaps(&rep.nPreferred); 569 swaps(&rep.nClones); 570 swaps(&rep.nameLength); 571 } 572 WriteToClient(client, sizeof(xRRGetOutputInfoReply), &rep); 573 if (extraLen) { 574 WriteToClient(client, extraLen, extra); 575 free(extra); 576 } 577 578 return Success; 579} 580 581static void 582RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv, RROutputPtr output) 583{ 584 if (pScrPriv->primaryOutput == output) 585 return; 586 587 /* clear the old primary */ 588 if (pScrPriv->primaryOutput) { 589 RROutputChanged(pScrPriv->primaryOutput, 0); 590 pScrPriv->primaryOutput = NULL; 591 } 592 593 /* set the new primary */ 594 if (output) { 595 pScrPriv->primaryOutput = output; 596 RROutputChanged(output, 0); 597 } 598 599 pScrPriv->layoutChanged = TRUE; 600 601 RRTellChanged(pScreen); 602} 603 604int 605ProcRRSetOutputPrimary(ClientPtr client) 606{ 607 REQUEST(xRRSetOutputPrimaryReq); 608 RROutputPtr output = NULL; 609 WindowPtr pWin; 610 rrScrPrivPtr pScrPriv; 611 int ret; 612 ScreenPtr secondary; 613 614 REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq); 615 616 ret = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 617 if (ret != Success) 618 return ret; 619 620 if (stuff->output) { 621 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 622 623 if (RROutputIsLeased(output)) 624 return BadAccess; 625 626 if (!output->pScreen->isGPU && output->pScreen != pWin->drawable.pScreen) { 627 client->errorValue = stuff->window; 628 return BadMatch; 629 } 630 if (output->pScreen->isGPU && output->pScreen->current_primary != pWin->drawable.pScreen) { 631 client->errorValue = stuff->window; 632 return BadMatch; 633 } 634 } 635 636 pScrPriv = rrGetScrPriv(pWin->drawable.pScreen); 637 if (pScrPriv) 638 { 639 RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output); 640 641 xorg_list_for_each_entry(secondary, 642 &pWin->drawable.pScreen->secondary_list, 643 secondary_head) { 644 if (secondary->is_output_secondary) 645 RRSetPrimaryOutput(secondary, rrGetScrPriv(secondary), output); 646 } 647 } 648 649 return Success; 650} 651 652int 653ProcRRGetOutputPrimary(ClientPtr client) 654{ 655 REQUEST(xRRGetOutputPrimaryReq); 656 WindowPtr pWin; 657 rrScrPrivPtr pScrPriv; 658 xRRGetOutputPrimaryReply rep; 659 RROutputPtr primary = NULL; 660 int rc; 661 662 REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq); 663 664 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 665 if (rc != Success) 666 return rc; 667 668 pScrPriv = rrGetScrPriv(pWin->drawable.pScreen); 669 if (pScrPriv) 670 primary = pScrPriv->primaryOutput; 671 672 rep = (xRRGetOutputPrimaryReply) { 673 .type = X_Reply, 674 .sequenceNumber = client->sequence, 675 .output = primary ? primary->id : None 676 }; 677 678 if (client->swapped) { 679 swaps(&rep.sequenceNumber); 680 swapl(&rep.output); 681 } 682 683 WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep); 684 685 return Success; 686} 687