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