Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | ||
27 | static int | |
28 | DeliverPropertyEvent(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 | 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 | ||
49 | static void | |
50 | RRDeliverPropertyEvent(ScreenPtr pScreen, xEvent *event) | |
51 | { | |
52 | if (!(dispatchException & (DE_RESET | DE_TERMINATE))) | |
53 | WalkTree(pScreen, DeliverPropertyEvent, event); | |
54 | } | |
55 | ||
56 | static void | |
57 | RRDestroyOutputProperty(RRPropertyPtr prop) | |
58 | { | |
59 | free(prop->valid_values); | |
60 | free(prop->current.data); | |
61 | free(prop->pending.data); | |
62 | free(prop); | |
63 | } | |
64 | ||
65 | static void | |
66 | RRDeleteProperty(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 | ||
82 | void | |
83 | RRDeleteAllOutputProperties(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 | ||
93 | static void | |
94 | RRInitOutputPropertyValue(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 | ||
102 | static RRPropertyPtr | |
103 | RRCreateOutputProperty(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 | ||
122 | void | |
123 | RRDeleteOutputProperty(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 | ||
135 | int | |
136 | RRChangeOutputProperty(RROutputPtr output, Atom property, Atom type, | |
137 | int format, int mode, unsigned long len, | |
138 | pointer value, Bool sendevent, Bool pending) | |
139 | { | |
140 | RRPropertyPtr prop; | |
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 | prop = RRCreateOutputProperty(property); | |
155 | if (!prop) | |
156 | return BadAlloc; | |
157 | add = TRUE; | |
158 | mode = PropModeReplace; | |
159 | } | |
160 | if (pending && prop->is_pending) | |
161 | prop_value = &prop->pending; | |
162 | else | |
163 | prop_value = &prop->current; | |
164 | ||
165 | /* To append or prepend to a property the request format and type | |
166 | must match those of the already defined property. The | |
167 | existing format and type are irrelevant when using the mode | |
168 | "PropModeReplace" since they will be written over. */ | |
169 | ||
170 | if ((format != prop_value->format) && (mode != PropModeReplace)) | |
171 | return BadMatch; | |
172 | if ((prop_value->type != type) && (mode != PropModeReplace)) | |
173 | return BadMatch; | |
174 | new_value = *prop_value; | |
175 | if (mode == PropModeReplace) | |
176 | total_len = len; | |
177 | else | |
178 | total_len = prop_value->size + len; | |
179 | ||
180 | if (mode == PropModeReplace || len > 0) { | |
181 | pointer new_data = NULL, old_data = NULL; | |
182 | ||
183 | total_size = total_len * size_in_bytes; | |
184 | new_value.data = (pointer) malloc(total_size); | |
185 | if (!new_value.data && total_size) { | |
186 | if (add) | |
187 | RRDestroyOutputProperty(prop); | |
188 | return BadAlloc; | |
189 | } | |
190 | new_value.size = len; | |
191 | new_value.type = type; | |
192 | new_value.format = format; | |
193 | ||
194 | switch (mode) { | |
195 | case PropModeReplace: | |
196 | new_data = new_value.data; | |
197 | old_data = NULL; | |
198 | break; | |
199 | case PropModeAppend: | |
200 | new_data = (pointer) (((char *) new_value.data) + | |
201 | (prop_value->size * size_in_bytes)); | |
202 | old_data = new_value.data; | |
203 | break; | |
204 | case PropModePrepend: | |
205 | new_data = new_value.data; | |
206 | old_data = (pointer) (((char *) new_value.data) + | |
207 | (prop_value->size * size_in_bytes)); | |
208 | break; | |
209 | } | |
210 | if (new_data) | |
211 | memcpy((char *) new_data, (char *) value, len * size_in_bytes); | |
212 | if (old_data) | |
213 | memcpy((char *) old_data, (char *) prop_value->data, | |
214 | prop_value->size * size_in_bytes); | |
215 | ||
216 | if (pending && pScrPriv->rrOutputSetProperty && | |
217 | !pScrPriv->rrOutputSetProperty(output->pScreen, output, | |
218 | prop->propertyName, &new_value)) { | |
219 | free(new_value.data); | |
220 | if (add) | |
221 | RRDestroyOutputProperty(prop); | |
222 | return BadValue; | |
223 | } | |
224 | free(prop_value->data); | |
225 | *prop_value = new_value; | |
226 | } | |
227 | ||
228 | else if (len == 0) { | |
229 | /* do nothing */ | |
230 | } | |
231 | ||
232 | if (add) { | |
233 | prop->next = output->properties; | |
234 | output->properties = prop; | |
235 | } | |
236 | ||
237 | if (pending && prop->is_pending) | |
238 | output->pendingProperties = TRUE; | |
239 | ||
240 | if (sendevent) { | |
241 | xRROutputPropertyNotifyEvent event = { | |
242 | .type = RREventBase + RRNotify, | |
243 | .subCode = RRNotify_OutputProperty, | |
244 | .output = output->id, | |
245 | .state = PropertyNewValue, | |
246 | .atom = prop->propertyName, | |
247 | .timestamp = currentTime.milliseconds | |
248 | }; | |
249 | RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event); | |
250 | } | |
251 | return Success; | |
252 | } | |
253 | ||
254 | Bool | |
255 | RRPostPendingProperties(RROutputPtr output) | |
256 | { | |
257 | RRPropertyValuePtr pending_value; | |
258 | RRPropertyValuePtr current_value; | |
259 | RRPropertyPtr property; | |
260 | Bool ret = TRUE; | |
261 | ||
262 | if (!output->pendingProperties) | |
263 | return TRUE; | |
264 | ||
265 | output->pendingProperties = FALSE; | |
266 | for (property = output->properties; property; property = property->next) { | |
267 | /* Skip non-pending properties */ | |
268 | if (!property->is_pending) | |
269 | continue; | |
270 | ||
271 | pending_value = &property->pending; | |
272 | current_value = &property->current; | |
273 | ||
274 | /* | |
275 | * If the pending and current values are equal, don't mark it | |
276 | * as changed (which would deliver an event) | |
277 | */ | |
278 | if (pending_value->type == current_value->type && | |
279 | pending_value->format == current_value->format && | |
280 | pending_value->size == current_value->size && | |
281 | !memcmp(pending_value->data, current_value->data, | |
282 | pending_value->size * (pending_value->format / 8))) | |
283 | continue; | |
284 | ||
285 | if (RRChangeOutputProperty(output, property->propertyName, | |
286 | pending_value->type, pending_value->format, | |
287 | PropModeReplace, pending_value->size, | |
288 | pending_value->data, TRUE, FALSE) != Success) | |
289 | ret = FALSE; | |
290 | } | |
291 | return ret; | |
292 | } | |
293 | ||
294 | RRPropertyPtr | |
295 | RRQueryOutputProperty(RROutputPtr output, Atom property) | |
296 | { | |
297 | RRPropertyPtr prop; | |
298 | ||
299 | for (prop = output->properties; prop; prop = prop->next) | |
300 | if (prop->propertyName == property) | |
301 | return prop; | |
302 | return NULL; | |
303 | } | |
304 | ||
305 | RRPropertyValuePtr | |
306 | RRGetOutputProperty(RROutputPtr output, Atom property, Bool pending) | |
307 | { | |
308 | RRPropertyPtr prop = RRQueryOutputProperty(output, property); | |
309 | rrScrPrivPtr pScrPriv = rrGetScrPriv(output->pScreen); | |
310 | ||
311 | if (!prop) | |
312 | return NULL; | |
313 | if (pending && prop->is_pending) | |
314 | return &prop->pending; | |
315 | else { | |
316 | #if RANDR_13_INTERFACE | |
317 | /* If we can, try to update the property value first */ | |
318 | if (pScrPriv->rrOutputGetProperty) | |
319 | pScrPriv->rrOutputGetProperty(output->pScreen, output, | |
320 | prop->propertyName); | |
321 | #endif | |
322 | return &prop->current; | |
323 | } | |
324 | } | |
325 | ||
326 | int | |
327 | RRConfigureOutputProperty(RROutputPtr output, Atom property, | |
328 | Bool pending, Bool range, Bool immutable, | |
329 | int num_values, INT32 *values) | |
330 | { | |
331 | RRPropertyPtr prop = RRQueryOutputProperty(output, property); | |
332 | Bool add = FALSE; | |
333 | INT32 *new_values; | |
334 | ||
335 | if (!prop) { | |
336 | prop = RRCreateOutputProperty(property); | |
337 | if (!prop) | |
338 | return BadAlloc; | |
339 | add = TRUE; | |
340 | } | |
341 | else if (prop->immutable && !immutable) | |
342 | return BadAccess; | |
343 | ||
344 | /* | |
345 | * ranges must have even number of values | |
346 | */ | |
347 | if (range && (num_values & 1)) { | |
348 | if (add) | |
349 | RRDestroyOutputProperty(prop); | |
350 | return BadMatch; | |
351 | } | |
352 | ||
353 | new_values = malloc(num_values * sizeof(INT32)); | |
354 | if (!new_values && num_values) { | |
355 | if (add) | |
356 | RRDestroyOutputProperty(prop); | |
357 | return BadAlloc; | |
358 | } | |
359 | if (num_values) | |
360 | memcpy(new_values, values, num_values * sizeof(INT32)); | |
361 | ||
362 | /* | |
363 | * Property moving from pending to non-pending | |
364 | * loses any pending values | |
365 | */ | |
366 | if (prop->is_pending && !pending) { | |
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 | ||
386 | int | |
387 | ProcRRListOutputProperties(ClientPtr client) | |
388 | { | |
389 | REQUEST(xRRListOutputPropertiesReq); | |
390 | Atom *pAtoms = NULL; | |
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 = (xRRListOutputPropertiesReply) { | |
407 | .type = X_Reply, | |
408 | .sequenceNumber = client->sequence, | |
409 | .length = bytes_to_int32(numProps * sizeof(Atom)), | |
410 | .nAtoms = numProps | |
411 | }; | |
412 | if (client->swapped) { | |
413 | swaps(&rep.sequenceNumber); | |
414 | swapl(&rep.length); | |
415 | swaps(&rep.nAtoms); | |
416 | } | |
417 | WriteToClient(client, sizeof(xRRListOutputPropertiesReply), &rep); | |
418 | ||
419 | if (numProps) { | |
420 | /* Copy property name atoms to reply buffer */ | |
421 | Atom *temppAtoms = pAtoms; | |
422 | for (prop = output->properties; prop; prop = prop->next) | |
423 | *temppAtoms++ = prop->propertyName; | |
424 | ||
425 | client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write; | |
426 | WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms); | |
427 | free(pAtoms); | |
428 | } | |
429 | return Success; | |
430 | } | |
431 | ||
432 | int | |
433 | ProcRRQueryOutputProperty(ClientPtr client) | |
434 | { | |
435 | REQUEST(xRRQueryOutputPropertyReq); | |
436 | xRRQueryOutputPropertyReply rep; | |
437 | RROutputPtr output; | |
438 | RRPropertyPtr prop; | |
439 | char *extra = NULL; | |
440 | ||
441 | REQUEST_SIZE_MATCH(xRRQueryOutputPropertyReq); | |
442 | ||
443 | VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); | |
444 | ||
445 | prop = RRQueryOutputProperty(output, stuff->property); | |
446 | if (!prop) | |
447 | return BadName; | |
448 | ||
449 | if (prop->num_valid) { | |
450 | extra = malloc(prop->num_valid * sizeof(INT32)); | |
451 | if (!extra) | |
452 | return BadAlloc; | |
453 | } | |
454 | ||
455 | rep = (xRRQueryOutputPropertyReply) { | |
456 | .type = X_Reply, | |
457 | .sequenceNumber = client->sequence, | |
458 | .length = prop->num_valid, | |
459 | .pending = prop->is_pending, | |
460 | .range = prop->range, | |
461 | .immutable = prop->immutable | |
462 | }; | |
463 | ||
464 | if (client->swapped) { | |
465 | swaps(&rep.sequenceNumber); | |
466 | swapl(&rep.length); | |
467 | } | |
468 | WriteToClient(client, sizeof(xRRQueryOutputPropertyReply), &rep); | |
469 | if (prop->num_valid) { | |
470 | memcpy(extra, prop->valid_values, prop->num_valid * sizeof(INT32)); | |
471 | client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write; | |
472 | WriteSwappedDataToClient(client, prop->num_valid * sizeof(INT32), | |
473 | extra); | |
474 | free(extra); | |
475 | } | |
476 | return Success; | |
477 | } | |
478 | ||
479 | int | |
480 | ProcRRConfigureOutputProperty(ClientPtr client) | |
481 | { | |
482 | REQUEST(xRRConfigureOutputPropertyReq); | |
483 | RROutputPtr output; | |
484 | int num_valid; | |
485 | ||
486 | REQUEST_AT_LEAST_SIZE(xRRConfigureOutputPropertyReq); | |
487 | ||
488 | VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); | |
489 | ||
490 | num_valid = | |
491 | stuff->length - bytes_to_int32(sizeof(xRRConfigureOutputPropertyReq)); | |
492 | return RRConfigureOutputProperty(output, stuff->property, stuff->pending, | |
493 | stuff->range, FALSE, num_valid, | |
494 | (INT32 *) (stuff + 1)); | |
495 | } | |
496 | ||
497 | int | |
498 | ProcRRChangeOutputProperty(ClientPtr client) | |
499 | { | |
500 | REQUEST(xRRChangeOutputPropertyReq); | |
501 | RROutputPtr output; | |
502 | char format, mode; | |
503 | unsigned long len; | |
504 | int sizeInBytes; | |
505 | int totalSize; | |
506 | int err; | |
507 | ||
508 | REQUEST_AT_LEAST_SIZE(xRRChangeOutputPropertyReq); | |
509 | UpdateCurrentTime(); | |
510 | format = stuff->format; | |
511 | mode = stuff->mode; | |
512 | if ((mode != PropModeReplace) && (mode != PropModeAppend) && | |
513 | (mode != PropModePrepend)) { | |
514 | client->errorValue = mode; | |
515 | return BadValue; | |
516 | } | |
517 | if ((format != 8) && (format != 16) && (format != 32)) { | |
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 | client->errorValue = stuff->property; | |
532 | return BadAtom; | |
533 | } | |
534 | if (!ValidAtom(stuff->type)) { | |
535 | client->errorValue = stuff->type; | |
536 | return BadAtom; | |
537 | } | |
538 | ||
539 | err = RRChangeOutputProperty(output, stuff->property, | |
540 | stuff->type, (int) format, | |
541 | (int) mode, len, (pointer) &stuff[1], TRUE, | |
542 | TRUE); | |
543 | if (err != Success) | |
544 | return err; | |
545 | else | |
546 | return Success; | |
547 | } | |
548 | ||
549 | int | |
550 | ProcRRDeleteOutputProperty(ClientPtr client) | |
551 | { | |
552 | REQUEST(xRRDeleteOutputPropertyReq); | |
553 | RROutputPtr output; | |
554 | RRPropertyPtr prop; | |
555 | ||
556 | REQUEST_SIZE_MATCH(xRRDeleteOutputPropertyReq); | |
557 | UpdateCurrentTime(); | |
558 | VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess); | |
559 | ||
560 | if (!ValidAtom(stuff->property)) { | |
561 | client->errorValue = stuff->property; | |
562 | return BadAtom; | |
563 | } | |
564 | ||
565 | prop = RRQueryOutputProperty(output, stuff->property); | |
566 | if (!prop) { | |
567 | client->errorValue = stuff->property; | |
568 | return BadName; | |
569 | } | |
570 | ||
571 | if (prop->immutable) { | |
572 | client->errorValue = stuff->property; | |
573 | return BadAccess; | |
574 | } | |
575 | ||
576 | RRDeleteOutputProperty(output, stuff->property); | |
577 | return Success; | |
578 | } | |
579 | ||
580 | int | |
581 | ProcRRGetOutputProperty(ClientPtr client) | |
582 | { | |
583 | REQUEST(xRRGetOutputPropertyReq); | |
584 | RRPropertyPtr prop, *prev; | |
585 | RRPropertyValuePtr prop_value; | |
586 | unsigned long n, len, ind; | |
587 | RROutputPtr output; | |
588 | xRRGetOutputPropertyReply reply; | |
589 | char *extra = NULL; | |
590 | ||
591 | REQUEST_SIZE_MATCH(xRRGetOutputPropertyReq); | |
592 | if (stuff->delete) | |
593 | UpdateCurrentTime(); | |
594 | VERIFY_RR_OUTPUT(stuff->output, output, | |
595 | stuff->delete ? DixWriteAccess : DixReadAccess); | |
596 | ||
597 | if (!ValidAtom(stuff->property)) { | |
598 | client->errorValue = stuff->property; | |
599 | return BadAtom; | |
600 | } | |
601 | if ((stuff->delete != xTrue) && (stuff->delete != xFalse)) { | |
602 | client->errorValue = stuff->delete; | |
603 | return BadValue; | |
604 | } | |
605 | if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type)) { | |
606 | client->errorValue = stuff->type; | |
607 | return BadAtom; | |
608 | } | |
609 | ||
610 | for (prev = &output->properties; (prop = *prev); prev = &prop->next) | |
611 | if (prop->propertyName == stuff->property) | |
612 | break; | |
613 | ||
614 | reply = (xRRGetOutputPropertyReply) { | |
615 | .type = X_Reply, | |
616 | .sequenceNumber = client->sequence | |
617 | }; | |
618 | if (!prop) { | |
619 | reply.nItems = 0; | |
620 | reply.length = 0; | |
621 | reply.bytesAfter = 0; | |
622 | reply.propertyType = None; | |
623 | reply.format = 0; | |
624 | if (client->swapped) { | |
625 | swaps(&reply.sequenceNumber); | |
626 | swapl(&reply.length); | |
627 | swapl(&reply.propertyType); | |
628 | swapl(&reply.bytesAfter); | |
629 | swapl(&reply.nItems); | |
630 | } | |
631 | WriteToClient(client, sizeof(xRRGetOutputPropertyReply), &reply); | |
632 | return Success; | |
633 | } | |
634 | ||
635 | if (prop->immutable && stuff->delete) | |
636 | return BadAccess; | |
637 | ||
638 | prop_value = RRGetOutputProperty(output, stuff->property, stuff->pending); | |
639 | if (!prop_value) | |
640 | return BadAtom; | |
641 | ||
642 | /* If the request type and actual type don't match. Return the | |
643 | property information, but not the data. */ | |
644 | ||
645 | if (((stuff->type != prop_value->type) && (stuff->type != AnyPropertyType)) | |
646 | ) { | |
647 | reply.bytesAfter = prop_value->size; | |
648 | reply.format = prop_value->format; | |
649 | reply.length = 0; | |
650 | reply.nItems = 0; | |
651 | reply.propertyType = prop_value->type; | |
652 | if (client->swapped) { | |
653 | swaps(&reply.sequenceNumber); | |
654 | swapl(&reply.length); | |
655 | swapl(&reply.propertyType); | |
656 | swapl(&reply.bytesAfter); | |
657 | swapl(&reply.nItems); | |
658 | } | |
659 | WriteToClient(client, sizeof(xRRGetOutputPropertyReply), &reply); | |
660 | return Success; | |
661 | } | |
662 | ||
663 | /* | |
664 | * Return type, format, value to client | |
665 | */ | |
666 | n = (prop_value->format / 8) * prop_value->size; /* size (bytes) of prop */ | |
667 | ind = stuff->longOffset << 2; | |
668 | ||
669 | /* If longOffset is invalid such that it causes "len" to | |
670 | be negative, it's a value error. */ | |
671 | ||
672 | if (n < ind) { | |
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 | xRROutputPropertyNotifyEvent event = { | |
695 | .type = RREventBase + RRNotify, | |
696 | .subCode = RRNotify_OutputProperty, | |
697 | .output = output->id, | |
698 | .state = PropertyDelete, | |
699 | .atom = prop->propertyName, | |
700 | .timestamp = currentTime.milliseconds | |
701 | }; | |
702 | RRDeliverPropertyEvent(output->pScreen, (xEvent *) &event); | |
703 | } | |
704 | ||
705 | if (client->swapped) { | |
706 | swaps(&reply.sequenceNumber); | |
707 | swapl(&reply.length); | |
708 | swapl(&reply.propertyType); | |
709 | swapl(&reply.bytesAfter); | |
710 | swapl(&reply.nItems); | |
711 | } | |
712 | WriteToClient(client, sizeof(xGenericReply), &reply); | |
713 | if (len) { | |
714 | memcpy(extra, (char *) prop_value->data + ind, len); | |
715 | switch (reply.format) { | |
716 | case 32: | |
717 | client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write; | |
718 | break; | |
719 | case 16: | |
720 | client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write; | |
721 | break; | |
722 | default: | |
723 | client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient; | |
724 | break; | |
725 | } | |
726 | WriteSwappedDataToClient(client, len, extra); | |
727 | free(extra); | |
728 | } | |
729 | ||
730 | if (stuff->delete && (reply.bytesAfter == 0)) { /* delete the Property */ | |
731 | *prev = prop->next; | |
732 | RRDestroyOutputProperty(prop); | |
733 | } | |
734 | return Success; | |
735 | } |