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