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
DeliverPropertyEvent(WindowPtr pWin,void * value)28 DeliverPropertyEvent(WindowPtr pWin, void *value)
29 {
30     xRRProviderPropertyNotifyEvent *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 & RRProviderPropertyNotifyMask))
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
RRDeliverPropertyEvent(ScreenPtr pScreen,xEvent * event)50 RRDeliverPropertyEvent(ScreenPtr pScreen, xEvent *event)
51 {
52     if (!(dispatchException & (DE_RESET | DE_TERMINATE)))
53         WalkTree(pScreen, DeliverPropertyEvent, event);
54 }
55 
56 static void
RRDestroyProviderProperty(RRPropertyPtr prop)57 RRDestroyProviderProperty(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
RRDeleteProperty(RRProviderRec * provider,RRPropertyRec * prop)66 RRDeleteProperty(RRProviderRec * provider, RRPropertyRec * prop)
67 {
68     xRRProviderPropertyNotifyEvent event = {
69         .type = RREventBase + RRNotify,
70         .subCode = RRNotify_ProviderProperty,
71         .provider = provider->id,
72         .state = PropertyDelete,
73         .atom = prop->propertyName,
74         .timestamp = currentTime.milliseconds
75     };
76 
77     RRDeliverPropertyEvent(provider->pScreen, (xEvent *) &event);
78 
79     RRDestroyProviderProperty(prop);
80 }
81 
82 void
RRDeleteAllProviderProperties(RRProviderPtr provider)83 RRDeleteAllProviderProperties(RRProviderPtr provider)
84 {
85     RRPropertyPtr prop, next;
86 
87     for (prop = provider->properties; prop; prop = next) {
88         next = prop->next;
89         RRDeleteProperty(provider, prop);
90     }
91 }
92 
93 static void
RRInitProviderPropertyValue(RRPropertyValuePtr property_value)94 RRInitProviderPropertyValue(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
RRCreateProviderProperty(Atom property)103 RRCreateProviderProperty(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     RRInitProviderPropertyValue(&prop->current);
118     RRInitProviderPropertyValue(&prop->pending);
119     return prop;
120 }
121 
122 void
RRDeleteProviderProperty(RRProviderPtr provider,Atom property)123 RRDeleteProviderProperty(RRProviderPtr provider, Atom property)
124 {
125     RRPropertyRec *prop, **prev;
126 
127     for (prev = &provider->properties; (prop = *prev); prev = &(prop->next))
128         if (prop->propertyName == property) {
129             *prev = prop->next;
130             RRDeleteProperty(provider, prop);
131             return;
132         }
133 }
134 
135 int
RRChangeProviderProperty(RRProviderPtr provider,Atom property,Atom type,int format,int mode,unsigned long len,void * value,Bool sendevent,Bool pending)136 RRChangeProviderProperty(RRProviderPtr provider, 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(provider->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 = RRQueryProviderProperty(provider, property);
153     if (!prop) {                /* just add to list */
154         prop = RRCreateProviderProperty(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         void *new_data = NULL, *old_data = NULL;
182 
183         total_size = total_len * size_in_bytes;
184         new_value.data = (void *) malloc(total_size);
185         if (!new_value.data && total_size) {
186             if (add)
187                 RRDestroyProviderProperty(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 = (void *) (((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 = (void *) (((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->rrProviderSetProperty &&
217             !pScrPriv->rrProviderSetProperty(provider->pScreen, provider,
218                                            prop->propertyName, &new_value)) {
219             if (add)
220                 RRDestroyProviderProperty(prop);
221             free(new_value.data);
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 = provider->properties;
234         provider->properties = prop;
235     }
236 
237     if (pending && prop->is_pending)
238         provider->pendingProperties = TRUE;
239 
240     if (sendevent) {
241         xRRProviderPropertyNotifyEvent event = {
242             .type = RREventBase + RRNotify,
243             .subCode = RRNotify_ProviderProperty,
244             .provider = provider->id,
245             .state = PropertyNewValue,
246             .atom = prop->propertyName,
247             .timestamp = currentTime.milliseconds
248         };
249         RRDeliverPropertyEvent(provider->pScreen, (xEvent *) &event);
250     }
251     return Success;
252 }
253 
254 Bool
RRPostProviderPendingProperties(RRProviderPtr provider)255 RRPostProviderPendingProperties(RRProviderPtr provider)
256 {
257     RRPropertyValuePtr pending_value;
258     RRPropertyValuePtr current_value;
259     RRPropertyPtr property;
260     Bool ret = TRUE;
261 
262     if (!provider->pendingProperties)
263         return TRUE;
264 
265     provider->pendingProperties = FALSE;
266     for (property = provider->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 (RRChangeProviderProperty(provider, 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
RRQueryProviderProperty(RRProviderPtr provider,Atom property)295 RRQueryProviderProperty(RRProviderPtr provider, Atom property)
296 {
297     RRPropertyPtr prop;
298 
299     for (prop = provider->properties; prop; prop = prop->next)
300         if (prop->propertyName == property)
301             return prop;
302     return NULL;
303 }
304 
305 RRPropertyValuePtr
RRGetProviderProperty(RRProviderPtr provider,Atom property,Bool pending)306 RRGetProviderProperty(RRProviderPtr provider, Atom property, Bool pending)
307 {
308     RRPropertyPtr prop = RRQueryProviderProperty(provider, property);
309     rrScrPrivPtr pScrPriv = rrGetScrPriv(provider->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->rrProviderGetProperty)
319             pScrPriv->rrProviderGetProperty(provider->pScreen, provider,
320                                           prop->propertyName);
321 #endif
322         return &prop->current;
323     }
324 }
325 
326 int
RRConfigureProviderProperty(RRProviderPtr provider,Atom property,Bool pending,Bool range,Bool immutable,int num_values,INT32 * values)327 RRConfigureProviderProperty(RRProviderPtr provider, Atom property,
328                           Bool pending, Bool range, Bool immutable,
329                           int num_values, INT32 *values)
330 {
331     RRPropertyPtr prop = RRQueryProviderProperty(provider, property);
332     Bool add = FALSE;
333     INT32 *new_values;
334 
335     if (!prop) {
336         prop = RRCreateProviderProperty(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             RRDestroyProviderProperty(prop);
350         return BadMatch;
351     }
352 
353     new_values = xallocarray(num_values, sizeof(INT32));
354     if (!new_values && num_values) {
355         if (add)
356             RRDestroyProviderProperty(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         RRInitProviderPropertyValue(&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 = provider->properties;
380         provider->properties = prop;
381     }
382 
383     return Success;
384 }
385 
386 int
ProcRRListProviderProperties(ClientPtr client)387 ProcRRListProviderProperties(ClientPtr client)
388 {
389     REQUEST(xRRListProviderPropertiesReq);
390     Atom *pAtoms = NULL, *temppAtoms;
391     xRRListProviderPropertiesReply rep;
392     int numProps = 0;
393     RRProviderPtr provider;
394     RRPropertyPtr prop;
395 
396     REQUEST_SIZE_MATCH(xRRListProviderPropertiesReq);
397 
398     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
399 
400     for (prop = provider->properties; prop; prop = prop->next)
401         numProps++;
402     if (numProps)
403         if (!(pAtoms = xallocarray(numProps, sizeof(Atom))))
404             return BadAlloc;
405 
406     rep = (xRRListProviderPropertiesReply) {
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     temppAtoms = pAtoms;
418     for (prop = provider->properties; prop; prop = prop->next)
419         *temppAtoms++ = prop->propertyName;
420 
421     WriteToClient(client, sizeof(xRRListProviderPropertiesReply), (char *) &rep);
422     if (numProps) {
423         client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
424         WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms);
425         free(pAtoms);
426     }
427     return Success;
428 }
429 
430 int
ProcRRQueryProviderProperty(ClientPtr client)431 ProcRRQueryProviderProperty(ClientPtr client)
432 {
433     REQUEST(xRRQueryProviderPropertyReq);
434     xRRQueryProviderPropertyReply rep;
435     RRProviderPtr provider;
436     RRPropertyPtr prop;
437     char *extra = NULL;
438 
439     REQUEST_SIZE_MATCH(xRRQueryProviderPropertyReq);
440 
441     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
442 
443     prop = RRQueryProviderProperty(provider, 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     rep = (xRRQueryProviderPropertyReply) {
453         .type = X_Reply,
454         .sequenceNumber = client->sequence,
455         .length = prop->num_valid,
456         .pending = prop->is_pending,
457         .range = prop->range,
458         .immutable = prop->immutable
459     };
460     if (client->swapped) {
461         swaps(&rep.sequenceNumber);
462         swapl(&rep.length);
463     }
464     WriteToClient(client, sizeof(xRRQueryProviderPropertyReply), (char *) &rep);
465     if (prop->num_valid) {
466         memcpy(extra, prop->valid_values, prop->num_valid * sizeof(INT32));
467         client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
468         WriteSwappedDataToClient(client, prop->num_valid * sizeof(INT32),
469                                  extra);
470         free(extra);
471     }
472     return Success;
473 }
474 
475 int
ProcRRConfigureProviderProperty(ClientPtr client)476 ProcRRConfigureProviderProperty(ClientPtr client)
477 {
478     REQUEST(xRRConfigureProviderPropertyReq);
479     RRProviderPtr provider;
480     int num_valid;
481 
482     REQUEST_AT_LEAST_SIZE(xRRConfigureProviderPropertyReq);
483 
484     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
485 
486     num_valid =
487         stuff->length - bytes_to_int32(sizeof(xRRConfigureProviderPropertyReq));
488     return RRConfigureProviderProperty(provider, stuff->property, stuff->pending,
489                                      stuff->range, FALSE, num_valid,
490                                      (INT32 *) (stuff + 1));
491 }
492 
493 int
ProcRRChangeProviderProperty(ClientPtr client)494 ProcRRChangeProviderProperty(ClientPtr client)
495 {
496     REQUEST(xRRChangeProviderPropertyReq);
497     RRProviderPtr provider;
498     char format, mode;
499     unsigned long len;
500     int sizeInBytes;
501     int totalSize;
502     int err;
503 
504     REQUEST_AT_LEAST_SIZE(xRRChangeProviderPropertyReq);
505     UpdateCurrentTime();
506     format = stuff->format;
507     mode = stuff->mode;
508     if ((mode != PropModeReplace) && (mode != PropModeAppend) &&
509         (mode != PropModePrepend)) {
510         client->errorValue = mode;
511         return BadValue;
512     }
513     if ((format != 8) && (format != 16) && (format != 32)) {
514         client->errorValue = format;
515         return BadValue;
516     }
517     len = stuff->nUnits;
518     if (len > bytes_to_int32((0xffffffff - sizeof(xChangePropertyReq))))
519         return BadLength;
520     sizeInBytes = format >> 3;
521     totalSize = len * sizeInBytes;
522     REQUEST_FIXED_SIZE(xRRChangeProviderPropertyReq, totalSize);
523 
524     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
525 
526     if (!ValidAtom(stuff->property)) {
527         client->errorValue = stuff->property;
528         return BadAtom;
529     }
530     if (!ValidAtom(stuff->type)) {
531         client->errorValue = stuff->type;
532         return BadAtom;
533     }
534 
535     err = RRChangeProviderProperty(provider, stuff->property,
536                                  stuff->type, (int) format,
537                                  (int) mode, len, (void *) &stuff[1], TRUE,
538                                  TRUE);
539     if (err != Success)
540         return err;
541     else
542         return Success;
543 }
544 
545 int
ProcRRDeleteProviderProperty(ClientPtr client)546 ProcRRDeleteProviderProperty(ClientPtr client)
547 {
548     REQUEST(xRRDeleteProviderPropertyReq);
549     RRProviderPtr provider;
550     RRPropertyPtr prop;
551 
552     REQUEST_SIZE_MATCH(xRRDeleteProviderPropertyReq);
553     UpdateCurrentTime();
554     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
555 
556     if (!ValidAtom(stuff->property)) {
557         client->errorValue = stuff->property;
558         return BadAtom;
559     }
560 
561     prop = RRQueryProviderProperty(provider, stuff->property);
562     if (!prop) {
563         client->errorValue = stuff->property;
564         return BadName;
565     }
566 
567     if (prop->immutable) {
568         client->errorValue = stuff->property;
569         return BadAccess;
570     }
571 
572     RRDeleteProviderProperty(provider, stuff->property);
573     return Success;
574 }
575 
576 int
ProcRRGetProviderProperty(ClientPtr client)577 ProcRRGetProviderProperty(ClientPtr client)
578 {
579     REQUEST(xRRGetProviderPropertyReq);
580     RRPropertyPtr prop, *prev;
581     RRPropertyValuePtr prop_value;
582     unsigned long n, len, ind;
583     RRProviderPtr provider;
584     xRRGetProviderPropertyReply reply = {
585         .type = X_Reply,
586         .sequenceNumber = client->sequence
587     };
588     char *extra = NULL;
589 
590     REQUEST_SIZE_MATCH(xRRGetProviderPropertyReq);
591     if (stuff->delete)
592         UpdateCurrentTime();
593     VERIFY_RR_PROVIDER(stuff->provider, provider,
594                      stuff->delete ? DixWriteAccess : DixReadAccess);
595 
596     if (!ValidAtom(stuff->property)) {
597         client->errorValue = stuff->property;
598         return BadAtom;
599     }
600     if ((stuff->delete != xTrue) && (stuff->delete != xFalse)) {
601         client->errorValue = stuff->delete;
602         return BadValue;
603     }
604     if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type)) {
605         client->errorValue = stuff->type;
606         return BadAtom;
607     }
608 
609     for (prev = &provider->properties; (prop = *prev); prev = &prop->next)
610         if (prop->propertyName == stuff->property)
611             break;
612 
613     if (!prop) {
614         reply.nItems = 0;
615         reply.length = 0;
616         reply.bytesAfter = 0;
617         reply.propertyType = None;
618         reply.format = 0;
619         if (client->swapped) {
620             swaps(&reply.sequenceNumber);
621             swapl(&reply.length);
622             swapl(&reply.propertyType);
623             swapl(&reply.bytesAfter);
624             swapl(&reply.nItems);
625         }
626         WriteToClient(client, sizeof(xRRGetProviderPropertyReply), &reply);
627         return Success;
628     }
629 
630     if (prop->immutable && stuff->delete)
631         return BadAccess;
632 
633     prop_value = RRGetProviderProperty(provider, 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) && (stuff->type != AnyPropertyType))
641         ) {
642         reply.bytesAfter = prop_value->size;
643         reply.format = prop_value->format;
644         reply.length = 0;
645         reply.nItems = 0;
646         reply.propertyType = prop_value->type;
647         if (client->swapped) {
648             swaps(&reply.sequenceNumber);
649             swapl(&reply.length);
650             swapl(&reply.propertyType);
651             swapl(&reply.bytesAfter);
652             swapl(&reply.nItems);
653         }
654         WriteToClient(client, sizeof(xRRGetProviderPropertyReply), &reply);
655         return Success;
656     }
657 
658 /*
659  *  Return type, format, value to client
660  */
661     n = (prop_value->format / 8) * prop_value->size;    /* size (bytes) of prop */
662     ind = stuff->longOffset << 2;
663 
664     /* If longOffset is invalid such that it causes "len" to
665        be negative, it's a value error. */
666 
667     if (n < ind) {
668         client->errorValue = stuff->longOffset;
669         return BadValue;
670     }
671 
672     len = min(n - ind, 4 * stuff->longLength);
673 
674     if (len) {
675         extra = malloc(len);
676         if (!extra)
677             return BadAlloc;
678     }
679     reply.bytesAfter = n - (ind + len);
680     reply.format = prop_value->format;
681     reply.length = bytes_to_int32(len);
682     if (prop_value->format)
683         reply.nItems = len / (prop_value->format / 8);
684     else
685         reply.nItems = 0;
686     reply.propertyType = prop_value->type;
687 
688     if (stuff->delete && (reply.bytesAfter == 0)) {
689         xRRProviderPropertyNotifyEvent event = {
690             .type = RREventBase + RRNotify,
691             .subCode = RRNotify_ProviderProperty,
692             .provider = provider->id,
693             .state = PropertyDelete,
694             .atom = prop->propertyName,
695             .timestamp = currentTime.milliseconds
696         };
697         RRDeliverPropertyEvent(provider->pScreen, (xEvent *) &event);
698     }
699 
700     if (client->swapped) {
701         swaps(&reply.sequenceNumber);
702         swapl(&reply.length);
703         swapl(&reply.propertyType);
704         swapl(&reply.bytesAfter);
705         swapl(&reply.nItems);
706     }
707     WriteToClient(client, sizeof(xGenericReply), &reply);
708     if (len) {
709         memcpy(extra, (char *) prop_value->data + ind, len);
710         switch (reply.format) {
711         case 32:
712             client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
713             break;
714         case 16:
715             client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
716             break;
717         default:
718             client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
719             break;
720         }
721         WriteSwappedDataToClient(client, len, extra);
722         free(extra);
723     }
724 
725     if (stuff->delete && (reply.bytesAfter == 0)) {     /* delete the Property */
726         *prev = prop->next;
727         RRDestroyProviderProperty(prop);
728     }
729     return Success;
730 }
731