rrproperty.c revision 35c4bbdf
1/* 2 * Copyright © 2006 Keith Packard 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that copyright 7 * notice and this permission notice appear in supporting documentation, and 8 * that the name of the copyright holders not be used in advertising or 9 * publicity pertaining to distribution of the software without specific, 10 * written prior permission. The copyright holders make no representations 11 * about the suitability of this software for any purpose. It is provided "as 12 * is" without express or implied warranty. 13 * 14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20 * OF THIS SOFTWARE. 21 */ 22 23#include "randrstr.h" 24#include "propertyst.h" 25#include "swaprep.h" 26 27static int 28DeliverPropertyEvent(WindowPtr pWin, void *value) 29{ 30 xRROutputPropertyNotifyEvent *event = value; 31 RREventPtr *pHead, pRREvent; 32 33 dixLookupResourceByType((void **) &pHead, pWin->drawable.id, 34 RREventType, serverClient, DixReadAccess); 35 if (!pHead) 36 return WT_WALKCHILDREN; 37 38 for (pRREvent = *pHead; pRREvent; pRREvent = pRREvent->next) { 39 if (!(pRREvent->mask & RROutputPropertyNotifyMask)) 40 continue; 41 42 event->window = pRREvent->window->drawable.id; 43 WriteEventsToClient(pRREvent->client, 1, (xEvent *) event); 44 } 45 46 return WT_WALKCHILDREN; 47} 48 49static void 50RRDeliverPropertyEvent(ScreenPtr pScreen, xEvent *event) 51{ 52 if (!(dispatchException & (DE_RESET | DE_TERMINATE))) 53 WalkTree(pScreen, DeliverPropertyEvent, event); 54} 55 56static void 57RRDestroyOutputProperty(RRPropertyPtr prop) 58{ 59 free(prop->valid_values); 60 free(prop->current.data); 61 free(prop->pending.data); 62 free(prop); 63} 64 65static void 66RRDeleteProperty(RROutputRec * output, RRPropertyRec * prop) 67{ 68 xRROutputPropertyNotifyEvent event = { 69 .type = RREventBase + RRNotify, 70 .subCode = RRNotify_OutputProperty, 71 .output = output->id, 72 .state = PropertyDelete, 73 .atom = prop->propertyName, 74 .timestamp = currentTime.milliseconds 75 }; 76 77 RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event); 78 79 RRDestroyOutputProperty(prop); 80} 81 82void 83RRDeleteAllOutputProperties(RROutputPtr output) 84{ 85 RRPropertyPtr prop, next; 86 87 for (prop = output->properties; prop; prop = next) { 88 next = prop->next; 89 RRDeleteProperty(output, prop); 90 } 91} 92 93static void 94RRInitOutputPropertyValue(RRPropertyValuePtr property_value) 95{ 96 property_value->type = None; 97 property_value->format = 0; 98 property_value->size = 0; 99 property_value->data = NULL; 100} 101 102static RRPropertyPtr 103RRCreateOutputProperty(Atom property) 104{ 105 RRPropertyPtr prop; 106 107 prop = (RRPropertyPtr) malloc(sizeof(RRPropertyRec)); 108 if (!prop) 109 return NULL; 110 prop->next = NULL; 111 prop->propertyName = property; 112 prop->is_pending = FALSE; 113 prop->range = FALSE; 114 prop->immutable = FALSE; 115 prop->num_valid = 0; 116 prop->valid_values = NULL; 117 RRInitOutputPropertyValue(&prop->current); 118 RRInitOutputPropertyValue(&prop->pending); 119 return prop; 120} 121 122void 123RRDeleteOutputProperty(RROutputPtr output, Atom property) 124{ 125 RRPropertyRec *prop, **prev; 126 127 for (prev = &output->properties; (prop = *prev); prev = &(prop->next)) 128 if (prop->propertyName == property) { 129 *prev = prop->next; 130 RRDeleteProperty(output, prop); 131 return; 132 } 133} 134 135int 136RRChangeOutputProperty(RROutputPtr output, Atom property, Atom type, 137 int format, int mode, unsigned long len, 138 void *value, Bool sendevent, Bool pending) 139{ 140 RRPropertyPtr prop; 141 rrScrPrivPtr pScrPriv = rrGetScrPriv(output->pScreen); 142 int size_in_bytes; 143 unsigned long total_len; 144 RRPropertyValuePtr prop_value; 145 RRPropertyValueRec new_value; 146 Bool add = FALSE; 147 148 size_in_bytes = format >> 3; 149 150 /* first see if property already exists */ 151 prop = RRQueryOutputProperty(output, property); 152 if (!prop) { /* just add to list */ 153 prop = RRCreateOutputProperty(property); 154 if (!prop) 155 return BadAlloc; 156 add = TRUE; 157 mode = PropModeReplace; 158 } 159 if (pending && prop->is_pending) 160 prop_value = &prop->pending; 161 else 162 prop_value = &prop->current; 163 164 /* To append or prepend to a property the request format and type 165 must match those of the already defined property. The 166 existing format and type are irrelevant when using the mode 167 "PropModeReplace" since they will be written over. */ 168 169 if ((format != prop_value->format) && (mode != PropModeReplace)) 170 return BadMatch; 171 if ((prop_value->type != type) && (mode != PropModeReplace)) 172 return BadMatch; 173 new_value = *prop_value; 174 if (mode == PropModeReplace) 175 total_len = len; 176 else 177 total_len = prop_value->size + len; 178 179 if (mode == PropModeReplace || len > 0) { 180 void *new_data = NULL, *old_data = NULL; 181 182 new_value.data = xallocarray(total_len, size_in_bytes); 183 if (!new_value.data && total_len && size_in_bytes) { 184 if (add) 185 RRDestroyOutputProperty(prop); 186 return BadAlloc; 187 } 188 new_value.size = len; 189 new_value.type = type; 190 new_value.format = format; 191 192 switch (mode) { 193 case PropModeReplace: 194 new_data = new_value.data; 195 old_data = NULL; 196 break; 197 case PropModeAppend: 198 new_data = (void *) (((char *) new_value.data) + 199 (prop_value->size * size_in_bytes)); 200 old_data = new_value.data; 201 break; 202 case PropModePrepend: 203 new_data = new_value.data; 204 old_data = (void *) (((char *) new_value.data) + 205 (prop_value->size * size_in_bytes)); 206 break; 207 } 208 if (new_data) 209 memcpy((char *) new_data, (char *) value, len * size_in_bytes); 210 if (old_data) 211 memcpy((char *) old_data, (char *) prop_value->data, 212 prop_value->size * size_in_bytes); 213 214 if (pending && pScrPriv->rrOutputSetProperty && 215 !pScrPriv->rrOutputSetProperty(output->pScreen, output, 216 prop->propertyName, &new_value)) { 217 free(new_value.data); 218 if (add) 219 RRDestroyOutputProperty(prop); 220 return BadValue; 221 } 222 free(prop_value->data); 223 *prop_value = new_value; 224 } 225 226 else if (len == 0) { 227 /* do nothing */ 228 } 229 230 if (add) { 231 prop->next = output->properties; 232 output->properties = prop; 233 } 234 235 if (pending && prop->is_pending) 236 output->pendingProperties = TRUE; 237 238 if (sendevent) { 239 xRROutputPropertyNotifyEvent event = { 240 .type = RREventBase + RRNotify, 241 .subCode = RRNotify_OutputProperty, 242 .output = output->id, 243 .state = PropertyNewValue, 244 .atom = prop->propertyName, 245 .timestamp = currentTime.milliseconds 246 }; 247 RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event); 248 } 249 return Success; 250} 251 252Bool 253RRPostPendingProperties(RROutputPtr output) 254{ 255 RRPropertyValuePtr pending_value; 256 RRPropertyValuePtr current_value; 257 RRPropertyPtr property; 258 Bool ret = TRUE; 259 260 if (!output->pendingProperties) 261 return TRUE; 262 263 output->pendingProperties = FALSE; 264 for (property = output->properties; property; property = property->next) { 265 /* Skip non-pending properties */ 266 if (!property->is_pending) 267 continue; 268 269 pending_value = &property->pending; 270 current_value = &property->current; 271 272 /* 273 * If the pending and current values are equal, don't mark it 274 * as changed (which would deliver an event) 275 */ 276 if (pending_value->type == current_value->type && 277 pending_value->format == current_value->format && 278 pending_value->size == current_value->size && 279 !memcmp(pending_value->data, current_value->data, 280 pending_value->size * (pending_value->format / 8))) 281 continue; 282 283 if (RRChangeOutputProperty(output, property->propertyName, 284 pending_value->type, pending_value->format, 285 PropModeReplace, pending_value->size, 286 pending_value->data, TRUE, FALSE) != Success) 287 ret = FALSE; 288 } 289 return ret; 290} 291 292RRPropertyPtr 293RRQueryOutputProperty(RROutputPtr output, Atom property) 294{ 295 RRPropertyPtr prop; 296 297 for (prop = output->properties; prop; prop = prop->next) 298 if (prop->propertyName == property) 299 return prop; 300 return NULL; 301} 302 303RRPropertyValuePtr 304RRGetOutputProperty(RROutputPtr output, Atom property, Bool pending) 305{ 306 RRPropertyPtr prop = RRQueryOutputProperty(output, property); 307 rrScrPrivPtr pScrPriv = rrGetScrPriv(output->pScreen); 308 309 if (!prop) 310 return NULL; 311 if (pending && prop->is_pending) 312 return &prop->pending; 313 else { 314#if RANDR_13_INTERFACE 315 /* If we can, try to update the property value first */ 316 if (pScrPriv->rrOutputGetProperty) 317 pScrPriv->rrOutputGetProperty(output->pScreen, output, 318 prop->propertyName); 319#endif 320 return &prop->current; 321 } 322} 323 324int 325RRConfigureOutputProperty(RROutputPtr output, Atom property, 326 Bool pending, Bool range, Bool immutable, 327 int num_values, INT32 *values) 328{ 329 RRPropertyPtr prop = RRQueryOutputProperty(output, property); 330 Bool add = FALSE; 331 INT32 *new_values; 332 333 if (!prop) { 334 prop = RRCreateOutputProperty(property); 335 if (!prop) 336 return BadAlloc; 337 add = TRUE; 338 } 339 else if (prop->immutable && !immutable) 340 return BadAccess; 341 342 /* 343 * ranges must have even number of values 344 */ 345 if (range && (num_values & 1)) { 346 if (add) 347 RRDestroyOutputProperty(prop); 348 return BadMatch; 349 } 350 351 new_values = xallocarray(num_values, sizeof(INT32)); 352 if (!new_values && num_values) { 353 if (add) 354 RRDestroyOutputProperty(prop); 355 return BadAlloc; 356 } 357 if (num_values) 358 memcpy(new_values, values, num_values * sizeof(INT32)); 359 360 /* 361 * Property moving from pending to non-pending 362 * loses any pending values 363 */ 364 if (prop->is_pending && !pending) { 365 free(prop->pending.data); 366 RRInitOutputPropertyValue(&prop->pending); 367 } 368 369 prop->is_pending = pending; 370 prop->range = range; 371 prop->immutable = immutable; 372 prop->num_valid = num_values; 373 free(prop->valid_values); 374 prop->valid_values = new_values; 375 376 if (add) { 377 prop->next = output->properties; 378 output->properties = prop; 379 } 380 381 return Success; 382} 383 384int 385ProcRRListOutputProperties(ClientPtr client) 386{ 387 REQUEST(xRRListOutputPropertiesReq); 388 Atom *pAtoms = NULL; 389 xRRListOutputPropertiesReply rep; 390 int numProps = 0; 391 RROutputPtr output; 392 RRPropertyPtr prop; 393 394 REQUEST_SIZE_MATCH(xRRListOutputPropertiesReq); 395 396 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 397 398 for (prop = output->properties; prop; prop = prop->next) 399 numProps++; 400 if (numProps) 401 if (!(pAtoms = xallocarray(numProps, sizeof(Atom)))) 402 return BadAlloc; 403 404 rep = (xRRListOutputPropertiesReply) { 405 .type = X_Reply, 406 .sequenceNumber = client->sequence, 407 .length = bytes_to_int32(numProps * sizeof(Atom)), 408 .nAtoms = numProps 409 }; 410 if (client->swapped) { 411 swaps(&rep.sequenceNumber); 412 swapl(&rep.length); 413 swaps(&rep.nAtoms); 414 } 415 WriteToClient(client, sizeof(xRRListOutputPropertiesReply), &rep); 416 417 if (numProps) { 418 /* Copy property name atoms to reply buffer */ 419 Atom *temppAtoms = pAtoms; 420 for (prop = output->properties; prop; prop = prop->next) 421 *temppAtoms++ = prop->propertyName; 422 423 client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write; 424 WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms); 425 free(pAtoms); 426 } 427 return Success; 428} 429 430int 431ProcRRQueryOutputProperty(ClientPtr client) 432{ 433 REQUEST(xRRQueryOutputPropertyReq); 434 xRRQueryOutputPropertyReply rep; 435 RROutputPtr output; 436 RRPropertyPtr prop; 437 char *extra = NULL; 438 439 REQUEST_SIZE_MATCH(xRRQueryOutputPropertyReq); 440 441 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 442 443 prop = RRQueryOutputProperty(output, stuff->property); 444 if (!prop) 445 return BadName; 446 447 if (prop->num_valid) { 448 extra = xallocarray(prop->num_valid, sizeof(INT32)); 449 if (!extra) 450 return BadAlloc; 451 } 452 453 rep = (xRRQueryOutputPropertyReply) { 454 .type = X_Reply, 455 .sequenceNumber = client->sequence, 456 .length = prop->num_valid, 457 .pending = prop->is_pending, 458 .range = prop->range, 459 .immutable = prop->immutable 460 }; 461 462 if (client->swapped) { 463 swaps(&rep.sequenceNumber); 464 swapl(&rep.length); 465 } 466 WriteToClient(client, sizeof(xRRQueryOutputPropertyReply), &rep); 467 if (prop->num_valid) { 468 memcpy(extra, prop->valid_values, prop->num_valid * sizeof(INT32)); 469 client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write; 470 WriteSwappedDataToClient(client, prop->num_valid * sizeof(INT32), 471 extra); 472 free(extra); 473 } 474 return Success; 475} 476 477int 478ProcRRConfigureOutputProperty(ClientPtr client) 479{ 480 REQUEST(xRRConfigureOutputPropertyReq); 481 RROutputPtr output; 482 int num_valid; 483 484 REQUEST_AT_LEAST_SIZE(xRRConfigureOutputPropertyReq); 485 486 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 487 488 num_valid = 489 stuff->length - bytes_to_int32(sizeof(xRRConfigureOutputPropertyReq)); 490 return RRConfigureOutputProperty(output, stuff->property, stuff->pending, 491 stuff->range, FALSE, num_valid, 492 (INT32 *) (stuff + 1)); 493} 494 495int 496ProcRRChangeOutputProperty(ClientPtr client) 497{ 498 REQUEST(xRRChangeOutputPropertyReq); 499 RROutputPtr output; 500 char format, mode; 501 unsigned long len; 502 int sizeInBytes; 503 int totalSize; 504 int err; 505 506 REQUEST_AT_LEAST_SIZE(xRRChangeOutputPropertyReq); 507 UpdateCurrentTime(); 508 format = stuff->format; 509 mode = stuff->mode; 510 if ((mode != PropModeReplace) && (mode != PropModeAppend) && 511 (mode != PropModePrepend)) { 512 client->errorValue = mode; 513 return BadValue; 514 } 515 if ((format != 8) && (format != 16) && (format != 32)) { 516 client->errorValue = format; 517 return BadValue; 518 } 519 len = stuff->nUnits; 520 if (len > bytes_to_int32((0xffffffff - sizeof(xChangePropertyReq)))) 521 return BadLength; 522 sizeInBytes = format >> 3; 523 totalSize = len * sizeInBytes; 524 REQUEST_FIXED_SIZE(xRRChangeOutputPropertyReq, totalSize); 525 526 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 527 528 if (!ValidAtom(stuff->property)) { 529 client->errorValue = stuff->property; 530 return BadAtom; 531 } 532 if (!ValidAtom(stuff->type)) { 533 client->errorValue = stuff->type; 534 return BadAtom; 535 } 536 537 err = RRChangeOutputProperty(output, stuff->property, 538 stuff->type, (int) format, 539 (int) mode, len, (void *) &stuff[1], TRUE, 540 TRUE); 541 if (err != Success) 542 return err; 543 else 544 return Success; 545} 546 547int 548ProcRRDeleteOutputProperty(ClientPtr client) 549{ 550 REQUEST(xRRDeleteOutputPropertyReq); 551 RROutputPtr output; 552 RRPropertyPtr prop; 553 554 REQUEST_SIZE_MATCH(xRRDeleteOutputPropertyReq); 555 UpdateCurrentTime(); 556 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); 557 558 if (!ValidAtom(stuff->property)) { 559 client->errorValue = stuff->property; 560 return BadAtom; 561 } 562 563 prop = RRQueryOutputProperty(output, stuff->property); 564 if (!prop) { 565 client->errorValue = stuff->property; 566 return BadName; 567 } 568 569 if (prop->immutable) { 570 client->errorValue = stuff->property; 571 return BadAccess; 572 } 573 574 RRDeleteOutputProperty(output, stuff->property); 575 return Success; 576} 577 578int 579ProcRRGetOutputProperty(ClientPtr client) 580{ 581 REQUEST(xRRGetOutputPropertyReq); 582 RRPropertyPtr prop, *prev; 583 RRPropertyValuePtr prop_value; 584 unsigned long n, len, ind; 585 RROutputPtr output; 586 xRRGetOutputPropertyReply reply; 587 char *extra = NULL; 588 589 REQUEST_SIZE_MATCH(xRRGetOutputPropertyReq); 590 if (stuff->delete) 591 UpdateCurrentTime(); 592 VERIFY_RR_OUTPUT(stuff->output, output, 593 stuff->delete ? DixWriteAccess : DixReadAccess); 594 595 if (!ValidAtom(stuff->property)) { 596 client->errorValue = stuff->property; 597 return BadAtom; 598 } 599 if ((stuff->delete != xTrue) && (stuff->delete != xFalse)) { 600 client->errorValue = stuff->delete; 601 return BadValue; 602 } 603 if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type)) { 604 client->errorValue = stuff->type; 605 return BadAtom; 606 } 607 608 for (prev = &output->properties; (prop = *prev); prev = &prop->next) 609 if (prop->propertyName == stuff->property) 610 break; 611 612 reply = (xRRGetOutputPropertyReply) { 613 .type = X_Reply, 614 .sequenceNumber = client->sequence 615 }; 616 if (!prop) { 617 reply.nItems = 0; 618 reply.length = 0; 619 reply.bytesAfter = 0; 620 reply.propertyType = None; 621 reply.format = 0; 622 if (client->swapped) { 623 swaps(&reply.sequenceNumber); 624 swapl(&reply.length); 625 swapl(&reply.propertyType); 626 swapl(&reply.bytesAfter); 627 swapl(&reply.nItems); 628 } 629 WriteToClient(client, sizeof(xRRGetOutputPropertyReply), &reply); 630 return Success; 631 } 632 633 if (prop->immutable && stuff->delete) 634 return BadAccess; 635 636 prop_value = RRGetOutputProperty(output, stuff->property, stuff->pending); 637 if (!prop_value) 638 return BadAtom; 639 640 /* If the request type and actual type don't match. Return the 641 property information, but not the data. */ 642 643 if (((stuff->type != prop_value->type) && (stuff->type != AnyPropertyType)) 644 ) { 645 reply.bytesAfter = prop_value->size; 646 reply.format = prop_value->format; 647 reply.length = 0; 648 reply.nItems = 0; 649 reply.propertyType = prop_value->type; 650 if (client->swapped) { 651 swaps(&reply.sequenceNumber); 652 swapl(&reply.length); 653 swapl(&reply.propertyType); 654 swapl(&reply.bytesAfter); 655 swapl(&reply.nItems); 656 } 657 WriteToClient(client, sizeof(xRRGetOutputPropertyReply), &reply); 658 return Success; 659 } 660 661/* 662 * Return type, format, value to client 663 */ 664 n = (prop_value->format / 8) * prop_value->size; /* size (bytes) of prop */ 665 ind = stuff->longOffset << 2; 666 667 /* If longOffset is invalid such that it causes "len" to 668 be negative, it's a value error. */ 669 670 if (n < ind) { 671 client->errorValue = stuff->longOffset; 672 return BadValue; 673 } 674 675 len = min(n - ind, 4 * stuff->longLength); 676 677 if (len) { 678 extra = malloc(len); 679 if (!extra) 680 return BadAlloc; 681 } 682 reply.bytesAfter = n - (ind + len); 683 reply.format = prop_value->format; 684 reply.length = bytes_to_int32(len); 685 if (prop_value->format) 686 reply.nItems = len / (prop_value->format / 8); 687 else 688 reply.nItems = 0; 689 reply.propertyType = prop_value->type; 690 691 if (stuff->delete && (reply.bytesAfter == 0)) { 692 xRROutputPropertyNotifyEvent event = { 693 .type = RREventBase + RRNotify, 694 .subCode = RRNotify_OutputProperty, 695 .output = output->id, 696 .state = PropertyDelete, 697 .atom = prop->propertyName, 698 .timestamp = currentTime.milliseconds 699 }; 700 RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event); 701 } 702 703 if (client->swapped) { 704 swaps(&reply.sequenceNumber); 705 swapl(&reply.length); 706 swapl(&reply.propertyType); 707 swapl(&reply.bytesAfter); 708 swapl(&reply.nItems); 709 } 710 WriteToClient(client, sizeof(xGenericReply), &reply); 711 if (len) { 712 memcpy(extra, (char *) prop_value->data + ind, len); 713 switch (reply.format) { 714 case 32: 715 client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write; 716 break; 717 case 16: 718 client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write; 719 break; 720 default: 721 client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient; 722 break; 723 } 724 WriteSwappedDataToClient(client, len, extra); 725 free(extra); 726 } 727 728 if (stuff->delete && (reply.bytesAfter == 0)) { /* delete the Property */ 729 *prev = prop->next; 730 RRDestroyOutputProperty(prop); 731 } 732 return Success; 733} 734