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