rroutput.c revision 35c4bbdf
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 26RESTYPE RROutputType; 27 28/* 29 * Notify the output of some change 30 */ 31void 32RROutputChanged(RROutputPtr output, Bool configChanged) 33{ 34 /* set changed bits on the master screen only */ 35 ScreenPtr pScreen = output->pScreen; 36 rrScrPrivPtr mastersp; 37 38 output->changed = TRUE; 39 if (!pScreen) 40 return; 41 42 if (pScreen->isGPU) { 43 ScreenPtr master = pScreen->current_master; 44 if (!master) 45 return; 46 mastersp = rrGetScrPriv(master); 47 } 48 else { 49 mastersp = rrGetScrPriv(pScreen); 50 } 51 52 RRSetChanged(pScreen); 53 if (configChanged) 54 mastersp->configChanged = TRUE; 55} 56 57/* 58 * Create an output 59 */ 60 61RROutputPtr 62RROutputCreate(ScreenPtr pScreen, 63 const char *name, int nameLength, void *devPrivate) 64{ 65 RROutputPtr output; 66 RROutputPtr *outputs; 67 rrScrPrivPtr pScrPriv; 68 69 if (!RRInit()) 70 return NULL; 71 72 pScrPriv = rrGetScrPriv(pScreen); 73 74 if (pScrPriv->numOutputs) 75 outputs = reallocarray(pScrPriv->outputs, 76 pScrPriv->numOutputs + 1, sizeof(RROutputPtr)); 77 else 78 outputs = malloc(sizeof(RROutputPtr)); 79 if (!outputs) 80 return FALSE; 81 82 pScrPriv->outputs = outputs; 83 84 output = malloc(sizeof(RROutputRec) + nameLength + 1); 85 if (!output) 86 return NULL; 87 output->id = FakeClientID(0); 88 output->pScreen = pScreen; 89 output->name = (char *) (output + 1); 90 output->nameLength = nameLength; 91 memcpy(output->name, name, nameLength); 92 output->name[nameLength] = '\0'; 93 output->connection = RR_UnknownConnection; 94 output->subpixelOrder = SubPixelUnknown; 95 output->mmWidth = 0; 96 output->mmHeight = 0; 97 output->crtc = NULL; 98 output->numCrtcs = 0; 99 output->crtcs = NULL; 100 output->numClones = 0; 101 output->clones = NULL; 102 output->numModes = 0; 103 output->numPreferred = 0; 104 output->modes = NULL; 105 output->numUserModes = 0; 106 output->userModes = NULL; 107 output->properties = NULL; 108 output->pendingProperties = FALSE; 109 output->changed = 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 RRResourcesChanged(pScreen); 118 119 return output; 120} 121 122/* 123 * Notify extension that output parameters have been changed 124 */ 125Bool 126RROutputSetClones(RROutputPtr output, RROutputPtr * clones, int numClones) 127{ 128 RROutputPtr *newClones; 129 int i; 130 131 if (numClones == output->numClones) { 132 for (i = 0; i < numClones; i++) 133 if (output->clones[i] != clones[i]) 134 break; 135 if (i == numClones) 136 return TRUE; 137 } 138 if (numClones) { 139 newClones = xallocarray(numClones, sizeof(RROutputPtr)); 140 if (!newClones) 141 return FALSE; 142 } 143 else 144 newClones = NULL; 145 free(output->clones); 146 memcpy(newClones, clones, numClones * sizeof(RROutputPtr)); 147 output->clones = newClones; 148 output->numClones = numClones; 149 RROutputChanged(output, TRUE); 150 return TRUE; 151} 152 153Bool 154RROutputSetModes(RROutputPtr output, 155 RRModePtr * modes, int numModes, int numPreferred) 156{ 157 RRModePtr *newModes; 158 int i; 159 160 if (numModes == output->numModes && numPreferred == output->numPreferred) { 161 for (i = 0; i < numModes; i++) 162 if (output->modes[i] != modes[i]) 163 break; 164 if (i == numModes) { 165 for (i = 0; i < numModes; i++) 166 RRModeDestroy(modes[i]); 167 return TRUE; 168 } 169 } 170 171 if (numModes) { 172 newModes = xallocarray(numModes, sizeof(RRModePtr)); 173 if (!newModes) 174 return FALSE; 175 } 176 else 177 newModes = NULL; 178 if (output->modes) { 179 for (i = 0; i < output->numModes; i++) 180 RRModeDestroy(output->modes[i]); 181 free(output->modes); 182 } 183 memcpy(newModes, modes, numModes * sizeof(RRModePtr)); 184 output->modes = newModes; 185 output->numModes = numModes; 186 output->numPreferred = numPreferred; 187 RROutputChanged(output, TRUE); 188 return TRUE; 189} 190 191int 192RROutputAddUserMode(RROutputPtr output, RRModePtr mode) 193{ 194 int m; 195 ScreenPtr pScreen = output->pScreen; 196 197 rrScrPriv(pScreen); 198 RRModePtr *newModes; 199 200 /* Check to see if this mode is already listed for this output */ 201 for (m = 0; m < output->numModes + output->numUserModes; m++) { 202 RRModePtr e = (m < output->numModes ? 203 output->modes[m] : 204 output->userModes[m - output->numModes]); 205 if (mode == e) 206 return Success; 207 } 208 209 /* Check with the DDX to see if this mode is OK */ 210 if (pScrPriv->rrOutputValidateMode) 211 if (!pScrPriv->rrOutputValidateMode(pScreen, output, mode)) 212 return BadMatch; 213 214 if (output->userModes) 215 newModes = reallocarray(output->userModes, 216 output->numUserModes + 1, sizeof(RRModePtr)); 217 else 218 newModes = malloc(sizeof(RRModePtr)); 219 if (!newModes) 220 return BadAlloc; 221 222 output->userModes = newModes; 223 output->userModes[output->numUserModes++] = mode; 224 ++mode->refcnt; 225 RROutputChanged(output, TRUE); 226 RRTellChanged(pScreen); 227 return Success; 228} 229 230int 231RROutputDeleteUserMode(RROutputPtr output, RRModePtr mode) 232{ 233 int m; 234 235 /* Find this mode in the user mode list */ 236 for (m = 0; m < output->numUserModes; m++) { 237 RRModePtr e = output->userModes[m]; 238 239 if (mode == e) 240 break; 241 } 242 /* Not there, access error */ 243 if (m == output->numUserModes) 244 return BadAccess; 245 246 /* make sure the mode isn't active for this output */ 247 if (output->crtc && output->crtc->mode == mode) 248 return BadMatch; 249 250 memmove(output->userModes + m, output->userModes + m + 1, 251 (output->numUserModes - m - 1) * sizeof(RRModePtr)); 252 output->numUserModes--; 253 RRModeDestroy(mode); 254 return Success; 255} 256 257Bool 258RROutputSetCrtcs(RROutputPtr output, RRCrtcPtr * crtcs, int numCrtcs) 259{ 260 RRCrtcPtr *newCrtcs; 261 int i; 262 263 if (numCrtcs == output->numCrtcs) { 264 for (i = 0; i < numCrtcs; i++) 265 if (output->crtcs[i] != crtcs[i]) 266 break; 267 if (i == numCrtcs) 268 return TRUE; 269 } 270 if (numCrtcs) { 271 newCrtcs = xallocarray(numCrtcs, sizeof(RRCrtcPtr)); 272 if (!newCrtcs) 273 return FALSE; 274 } 275 else 276 newCrtcs = NULL; 277 free(output->crtcs); 278 memcpy(newCrtcs, crtcs, numCrtcs * sizeof(RRCrtcPtr)); 279 output->crtcs = newCrtcs; 280 output->numCrtcs = numCrtcs; 281 RROutputChanged(output, TRUE); 282 return TRUE; 283} 284 285Bool 286RROutputSetConnection(RROutputPtr output, CARD8 connection) 287{ 288 if (output->connection == connection) 289 return TRUE; 290 output->connection = connection; 291 RROutputChanged(output, TRUE); 292 return TRUE; 293} 294 295Bool 296RROutputSetSubpixelOrder(RROutputPtr output, int subpixelOrder) 297{ 298 if (output->subpixelOrder == subpixelOrder) 299 return TRUE; 300 301 output->subpixelOrder = subpixelOrder; 302 RROutputChanged(output, FALSE); 303 return TRUE; 304} 305 306Bool 307RROutputSetPhysicalSize(RROutputPtr output, int mmWidth, int mmHeight) 308{ 309 if (output->mmWidth == mmWidth && output->mmHeight == mmHeight) 310 return TRUE; 311 output->mmWidth = mmWidth; 312 output->mmHeight = mmHeight; 313 RROutputChanged(output, FALSE); 314 return TRUE; 315} 316 317void 318RRDeliverOutputEvent(ClientPtr client, WindowPtr pWin, RROutputPtr output) 319{ 320 ScreenPtr pScreen = pWin->drawable.pScreen; 321 322 rrScrPriv(pScreen); 323 RRCrtcPtr crtc = output->crtc; 324 RRModePtr mode = crtc ? crtc->mode : NULL; 325 326 xRROutputChangeNotifyEvent oe = { 327 .type = RRNotify + RREventBase, 328 .subCode = RRNotify_OutputChange, 329 .timestamp = pScrPriv->lastSetTime.milliseconds, 330 .configTimestamp = pScrPriv->lastConfigTime.milliseconds, 331 .window = pWin->drawable.id, 332 .output = output->id, 333 .crtc = crtc ? crtc->id : None, 334 .mode = mode ? mode->mode.id : None, 335 .rotation = crtc ? crtc->rotation : RR_Rotate_0, 336 .connection = output->connection, 337 .subpixelOrder = output->subpixelOrder 338 }; 339 WriteEventsToClient(client, 1, (xEvent *) &oe); 340} 341 342/* 343 * Destroy a Output at shutdown 344 */ 345void 346RROutputDestroy(RROutputPtr output) 347{ 348 FreeResource(output->id, 0); 349} 350 351static int 352RROutputDestroyResource(void *value, XID pid) 353{ 354 RROutputPtr output = (RROutputPtr) value; 355 ScreenPtr pScreen = output->pScreen; 356 int m; 357 358 if (pScreen) { 359 rrScrPriv(pScreen); 360 int i; 361 362 if (pScrPriv->primaryOutput == output) 363 pScrPriv->primaryOutput = NULL; 364 365 for (i = 0; i < pScrPriv->numOutputs; i++) { 366 if (pScrPriv->outputs[i] == output) { 367 memmove(pScrPriv->outputs + i, pScrPriv->outputs + i + 1, 368 (pScrPriv->numOutputs - (i + 1)) * sizeof(RROutputPtr)); 369 --pScrPriv->numOutputs; 370 break; 371 } 372 } 373 374 RRResourcesChanged(pScreen); 375 } 376 if (output->modes) { 377 for (m = 0; m < output->numModes; m++) 378 RRModeDestroy(output->modes[m]); 379 free(output->modes); 380 } 381 382 for (m = 0; m < output->numUserModes; m++) 383 RRModeDestroy(output->userModes[m]); 384 free(output->userModes); 385 386 free(output->crtcs); 387 free(output->clones); 388 RRDeleteAllOutputProperties(output); 389 free(output); 390 return 1; 391} 392 393/* 394 * Initialize output type 395 */ 396Bool 397RROutputInit(void) 398{ 399 RROutputType = CreateNewResourceType(RROutputDestroyResource, "OUTPUT"); 400 if (!RROutputType) 401 return FALSE; 402 403 return TRUE; 404} 405 406/* 407 * Initialize output type error value 408 */ 409void 410RROutputInitErrorValue(void) 411{ 412 SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput); 413} 414 415#define OutputInfoExtra (SIZEOF(xRRGetOutputInfoReply) - 32) 416 417int 418ProcRRGetOutputInfo(ClientPtr client) 419{ 420 REQUEST(xRRGetOutputInfoReq); 421 xRRGetOutputInfoReply rep; 422 RROutputPtr output; 423 CARD8 *extra; 424 unsigned long extraLen; 425 ScreenPtr pScreen; 426 rrScrPrivPtr pScrPriv; 427 RRCrtc *crtcs; 428 RRMode *modes; 429 RROutput *clones; 430 char *name; 431 int i; 432 433 REQUEST_SIZE_MATCH(xRRGetOutputInfoReq); 434 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 435 436 pScreen = output->pScreen; 437 pScrPriv = rrGetScrPriv(pScreen); 438 439 rep = (xRRGetOutputInfoReply) { 440 .type = X_Reply, 441 .status = RRSetConfigSuccess, 442 .sequenceNumber = client->sequence, 443 .length = bytes_to_int32(OutputInfoExtra), 444 .timestamp = pScrPriv->lastSetTime.milliseconds, 445 .crtc = output->crtc ? output->crtc->id : None, 446 .mmWidth = output->mmWidth, 447 .mmHeight = output->mmHeight, 448 .connection = output->connection, 449 .subpixelOrder = output->subpixelOrder, 450 .nCrtcs = output->numCrtcs, 451 .nModes = output->numModes + output->numUserModes, 452 .nPreferred = output->numPreferred, 453 .nClones = output->numClones, 454 .nameLength = output->nameLength 455 }; 456 extraLen = ((output->numCrtcs + 457 output->numModes + output->numUserModes + 458 output->numClones + bytes_to_int32(rep.nameLength)) << 2); 459 460 if (extraLen) { 461 rep.length += bytes_to_int32(extraLen); 462 extra = malloc(extraLen); 463 if (!extra) 464 return BadAlloc; 465 } 466 else 467 extra = NULL; 468 469 crtcs = (RRCrtc *) extra; 470 modes = (RRMode *) (crtcs + output->numCrtcs); 471 clones = (RROutput *) (modes + output->numModes + output->numUserModes); 472 name = (char *) (clones + output->numClones); 473 474 for (i = 0; i < output->numCrtcs; i++) { 475 crtcs[i] = output->crtcs[i]->id; 476 if (client->swapped) 477 swapl(&crtcs[i]); 478 } 479 for (i = 0; i < output->numModes + output->numUserModes; i++) { 480 if (i < output->numModes) 481 modes[i] = output->modes[i]->mode.id; 482 else 483 modes[i] = output->userModes[i - output->numModes]->mode.id; 484 if (client->swapped) 485 swapl(&modes[i]); 486 } 487 for (i = 0; i < output->numClones; i++) { 488 clones[i] = output->clones[i]->id; 489 if (client->swapped) 490 swapl(&clones[i]); 491 } 492 memcpy(name, output->name, output->nameLength); 493 if (client->swapped) { 494 swaps(&rep.sequenceNumber); 495 swapl(&rep.length); 496 swapl(&rep.timestamp); 497 swapl(&rep.crtc); 498 swapl(&rep.mmWidth); 499 swapl(&rep.mmHeight); 500 swaps(&rep.nCrtcs); 501 swaps(&rep.nModes); 502 swaps(&rep.nPreferred); 503 swaps(&rep.nClones); 504 swaps(&rep.nameLength); 505 } 506 WriteToClient(client, sizeof(xRRGetOutputInfoReply), &rep); 507 if (extraLen) { 508 WriteToClient(client, extraLen, extra); 509 free(extra); 510 } 511 512 return Success; 513} 514 515static void 516RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv, RROutputPtr output) 517{ 518 if (pScrPriv->primaryOutput == output) 519 return; 520 521 /* clear the old primary */ 522 if (pScrPriv->primaryOutput) { 523 RROutputChanged(pScrPriv->primaryOutput, 0); 524 pScrPriv->primaryOutput = NULL; 525 } 526 527 /* set the new primary */ 528 if (output) { 529 pScrPriv->primaryOutput = output; 530 RROutputChanged(output, 0); 531 } 532 533 pScrPriv->layoutChanged = TRUE; 534 535 RRTellChanged(pScreen); 536} 537 538int 539ProcRRSetOutputPrimary(ClientPtr client) 540{ 541 REQUEST(xRRSetOutputPrimaryReq); 542 RROutputPtr output = NULL; 543 WindowPtr pWin; 544 rrScrPrivPtr pScrPriv; 545 int ret; 546 ScreenPtr slave; 547 548 REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq); 549 550 ret = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 551 if (ret != Success) 552 return ret; 553 554 if (stuff->output) { 555 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 556 557 if (!output->pScreen->isGPU && output->pScreen != pWin->drawable.pScreen) { 558 client->errorValue = stuff->window; 559 return BadMatch; 560 } 561 if (output->pScreen->isGPU && output->pScreen->current_master != pWin->drawable.pScreen) { 562 client->errorValue = stuff->window; 563 return BadMatch; 564 } 565 } 566 567 pScrPriv = rrGetScrPriv(pWin->drawable.pScreen); 568 if (pScrPriv) 569 { 570 RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output); 571 572 xorg_list_for_each_entry(slave, 573 &pWin->drawable.pScreen->output_slave_list, 574 output_head) { 575 rrScrPrivPtr pSlavePriv; 576 pSlavePriv = rrGetScrPriv(slave); 577 578 RRSetPrimaryOutput(slave, pSlavePriv, output); 579 } 580 } 581 582 return Success; 583} 584 585int 586ProcRRGetOutputPrimary(ClientPtr client) 587{ 588 REQUEST(xRRGetOutputPrimaryReq); 589 WindowPtr pWin; 590 rrScrPrivPtr pScrPriv; 591 xRRGetOutputPrimaryReply rep; 592 RROutputPtr primary = NULL; 593 int rc; 594 595 REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq); 596 597 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 598 if (rc != Success) 599 return rc; 600 601 pScrPriv = rrGetScrPriv(pWin->drawable.pScreen); 602 if (pScrPriv) 603 primary = pScrPriv->primaryOutput; 604 605 rep = (xRRGetOutputPrimaryReply) { 606 .type = X_Reply, 607 .sequenceNumber = client->sequence, 608 .output = primary ? primary->id : None 609 }; 610 611 if (client->swapped) { 612 swaps(&rep.sequenceNumber); 613 swapl(&rep.output); 614 } 615 616 WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep); 617 618 return Success; 619} 620