Home | History | Annotate | Line # | Download | only in randr
      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     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
     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 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
     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
     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
     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
    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
    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
    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         if (total_len > MAXINT / size_in_bytes)
    183             return BadValue;
    184         total_size = total_len * size_in_bytes;
    185         new_value.data = (void *) malloc(total_size);
    186         if (!new_value.data && total_size) {
    187             if (add)
    188                 RRDestroyProviderProperty(prop);
    189             return BadAlloc;
    190         }
    191         new_value.size = len;
    192         new_value.type = type;
    193         new_value.format = format;
    194 
    195         switch (mode) {
    196         case PropModeReplace:
    197             new_data = new_value.data;
    198             old_data = NULL;
    199             break;
    200         case PropModeAppend:
    201             new_data = (void *) (((char *) new_value.data) +
    202                                   (prop_value->size * size_in_bytes));
    203             old_data = new_value.data;
    204             break;
    205         case PropModePrepend:
    206             new_data = new_value.data;
    207             old_data = (void *) (((char *) new_value.data) +
    208                                   (prop_value->size * size_in_bytes));
    209             break;
    210         }
    211         if (new_data)
    212             memcpy((char *) new_data, (char *) value, len * size_in_bytes);
    213         if (old_data)
    214             memcpy((char *) old_data, (char *) prop_value->data,
    215                    prop_value->size * size_in_bytes);
    216 
    217         if (pending && pScrPriv->rrProviderSetProperty &&
    218             !pScrPriv->rrProviderSetProperty(provider->pScreen, provider,
    219                                            prop->propertyName, &new_value)) {
    220             if (add)
    221                 RRDestroyProviderProperty(prop);
    222             free(new_value.data);
    223             return BadValue;
    224         }
    225         free(prop_value->data);
    226         *prop_value = new_value;
    227     }
    228 
    229     else if (len == 0) {
    230         /* do nothing */
    231     }
    232 
    233     if (add) {
    234         prop->next = provider->properties;
    235         provider->properties = prop;
    236     }
    237 
    238     if (pending && prop->is_pending)
    239         provider->pendingProperties = TRUE;
    240 
    241     if (sendevent) {
    242         xRRProviderPropertyNotifyEvent event = {
    243             .type = RREventBase + RRNotify,
    244             .subCode = RRNotify_ProviderProperty,
    245             .provider = provider->id,
    246             .state = PropertyNewValue,
    247             .atom = prop->propertyName,
    248             .timestamp = currentTime.milliseconds
    249         };
    250         RRDeliverPropertyEvent(provider->pScreen, (xEvent *) &event);
    251     }
    252     return Success;
    253 }
    254 
    255 Bool
    256 RRPostProviderPendingProperties(RRProviderPtr provider)
    257 {
    258     RRPropertyValuePtr pending_value;
    259     RRPropertyValuePtr current_value;
    260     RRPropertyPtr property;
    261     Bool ret = TRUE;
    262 
    263     if (!provider->pendingProperties)
    264         return TRUE;
    265 
    266     provider->pendingProperties = FALSE;
    267     for (property = provider->properties; property; property = property->next) {
    268         /* Skip non-pending properties */
    269         if (!property->is_pending)
    270             continue;
    271 
    272         pending_value = &property->pending;
    273         current_value = &property->current;
    274 
    275         /*
    276          * If the pending and current values are equal, don't mark it
    277          * as changed (which would deliver an event)
    278          */
    279         if (pending_value->type == current_value->type &&
    280             pending_value->format == current_value->format &&
    281             pending_value->size == current_value->size &&
    282             !memcmp(pending_value->data, current_value->data,
    283                     pending_value->size * (pending_value->format / 8)))
    284             continue;
    285 
    286         if (RRChangeProviderProperty(provider, property->propertyName,
    287                                    pending_value->type, pending_value->format,
    288                                    PropModeReplace, pending_value->size,
    289                                    pending_value->data, TRUE, FALSE) != Success)
    290             ret = FALSE;
    291     }
    292     return ret;
    293 }
    294 
    295 RRPropertyPtr
    296 RRQueryProviderProperty(RRProviderPtr provider, Atom property)
    297 {
    298     RRPropertyPtr prop;
    299 
    300     for (prop = provider->properties; prop; prop = prop->next)
    301         if (prop->propertyName == property)
    302             return prop;
    303     return NULL;
    304 }
    305 
    306 RRPropertyValuePtr
    307 RRGetProviderProperty(RRProviderPtr provider, Atom property, Bool pending)
    308 {
    309     RRPropertyPtr prop = RRQueryProviderProperty(provider, property);
    310     rrScrPrivPtr pScrPriv = rrGetScrPriv(provider->pScreen);
    311 
    312     if (!prop)
    313         return NULL;
    314     if (pending && prop->is_pending)
    315         return &prop->pending;
    316     else {
    317 #if RANDR_13_INTERFACE
    318         /* If we can, try to update the property value first */
    319         if (pScrPriv->rrProviderGetProperty)
    320             pScrPriv->rrProviderGetProperty(provider->pScreen, provider,
    321                                           prop->propertyName);
    322 #endif
    323         return &prop->current;
    324     }
    325 }
    326 
    327 int
    328 RRConfigureProviderProperty(RRProviderPtr provider, Atom property,
    329                           Bool pending, Bool range, Bool immutable,
    330                           int num_values, INT32 *values)
    331 {
    332     RRPropertyPtr prop = RRQueryProviderProperty(provider, property);
    333     Bool add = FALSE;
    334     INT32 *new_values;
    335 
    336     if (!prop) {
    337         prop = RRCreateProviderProperty(property);
    338         if (!prop)
    339             return BadAlloc;
    340         add = TRUE;
    341     }
    342     else if (prop->immutable && !immutable)
    343         return BadAccess;
    344 
    345     /*
    346      * ranges must have even number of values
    347      */
    348     if (range && (num_values & 1)) {
    349         if (add)
    350             RRDestroyProviderProperty(prop);
    351         return BadMatch;
    352     }
    353 
    354     new_values = xallocarray(num_values, sizeof(INT32));
    355     if (!new_values && num_values) {
    356         if (add)
    357             RRDestroyProviderProperty(prop);
    358         return BadAlloc;
    359     }
    360     if (num_values)
    361         memcpy(new_values, values, num_values * sizeof(INT32));
    362 
    363     /*
    364      * Property moving from pending to non-pending
    365      * loses any pending values
    366      */
    367     if (prop->is_pending && !pending) {
    368         free(prop->pending.data);
    369         RRInitProviderPropertyValue(&prop->pending);
    370     }
    371 
    372     prop->is_pending = pending;
    373     prop->range = range;
    374     prop->immutable = immutable;
    375     prop->num_valid = num_values;
    376     free(prop->valid_values);
    377     prop->valid_values = new_values;
    378 
    379     if (add) {
    380         prop->next = provider->properties;
    381         provider->properties = prop;
    382     }
    383 
    384     return Success;
    385 }
    386 
    387 int
    388 ProcRRListProviderProperties(ClientPtr client)
    389 {
    390     REQUEST(xRRListProviderPropertiesReq);
    391     Atom *pAtoms = NULL, *temppAtoms;
    392     xRRListProviderPropertiesReply rep;
    393     int numProps = 0;
    394     RRProviderPtr provider;
    395     RRPropertyPtr prop;
    396 
    397     REQUEST_SIZE_MATCH(xRRListProviderPropertiesReq);
    398 
    399     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
    400 
    401     for (prop = provider->properties; prop; prop = prop->next)
    402         numProps++;
    403     if (numProps)
    404         if (!(pAtoms = xallocarray(numProps, sizeof(Atom))))
    405             return BadAlloc;
    406 
    407     rep = (xRRListProviderPropertiesReply) {
    408         .type = X_Reply,
    409         .sequenceNumber = client->sequence,
    410         .length = bytes_to_int32(numProps * sizeof(Atom)),
    411         .nAtoms = numProps
    412     };
    413     if (client->swapped) {
    414         swaps(&rep.sequenceNumber);
    415         swapl(&rep.length);
    416         swaps(&rep.nAtoms);
    417     }
    418     temppAtoms = pAtoms;
    419     for (prop = provider->properties; prop; prop = prop->next)
    420         *temppAtoms++ = prop->propertyName;
    421 
    422     WriteToClient(client, sizeof(xRRListProviderPropertiesReply), (char *) &rep);
    423     if (numProps) {
    424         client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
    425         WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms);
    426         free(pAtoms);
    427     }
    428     return Success;
    429 }
    430 
    431 int
    432 ProcRRQueryProviderProperty(ClientPtr client)
    433 {
    434     REQUEST(xRRQueryProviderPropertyReq);
    435     xRRQueryProviderPropertyReply rep;
    436     RRProviderPtr provider;
    437     RRPropertyPtr prop;
    438     char *extra = NULL;
    439 
    440     REQUEST_SIZE_MATCH(xRRQueryProviderPropertyReq);
    441 
    442     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
    443 
    444     prop = RRQueryProviderProperty(provider, stuff->property);
    445     if (!prop)
    446         return BadName;
    447 
    448     if (prop->num_valid) {
    449         extra = xallocarray(prop->num_valid, sizeof(INT32));
    450         if (!extra)
    451             return BadAlloc;
    452     }
    453     rep = (xRRQueryProviderPropertyReply) {
    454         .type = X_Reply,
    455         .sequenceNumber = client->sequence,
    456         .length = prop->num_valid,
    457         .pending = prop->is_pending,
    458         .range = prop->range,
    459         .immutable = prop->immutable
    460     };
    461     if (client->swapped) {
    462         swaps(&rep.sequenceNumber);
    463         swapl(&rep.length);
    464     }
    465     WriteToClient(client, sizeof(xRRQueryProviderPropertyReply), (char *) &rep);
    466     if (prop->num_valid) {
    467         memcpy(extra, prop->valid_values, prop->num_valid * sizeof(INT32));
    468         client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
    469         WriteSwappedDataToClient(client, prop->num_valid * sizeof(INT32),
    470                                  extra);
    471         free(extra);
    472     }
    473     return Success;
    474 }
    475 
    476 int
    477 ProcRRConfigureProviderProperty(ClientPtr client)
    478 {
    479     REQUEST(xRRConfigureProviderPropertyReq);
    480     RRProviderPtr provider;
    481     int num_valid;
    482 
    483     REQUEST_AT_LEAST_SIZE(xRRConfigureProviderPropertyReq);
    484 
    485     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
    486 
    487     num_valid =
    488         stuff->length - bytes_to_int32(sizeof(xRRConfigureProviderPropertyReq));
    489     return RRConfigureProviderProperty(provider, stuff->property, stuff->pending,
    490                                      stuff->range, FALSE, num_valid,
    491                                      (INT32 *) (stuff + 1));
    492 }
    493 
    494 int
    495 ProcRRChangeProviderProperty(ClientPtr client)
    496 {
    497     REQUEST(xRRChangeProviderPropertyReq);
    498     RRProviderPtr provider;
    499     char format, mode;
    500     unsigned long len;
    501     int sizeInBytes;
    502     uint64_t totalSize;
    503     int err;
    504 
    505     REQUEST_AT_LEAST_SIZE(xRRChangeProviderPropertyReq);
    506     UpdateCurrentTime();
    507     format = stuff->format;
    508     mode = stuff->mode;
    509     if ((mode != PropModeReplace) && (mode != PropModeAppend) &&
    510         (mode != PropModePrepend)) {
    511         client->errorValue = mode;
    512         return BadValue;
    513     }
    514     if ((format != 8) && (format != 16) && (format != 32)) {
    515         client->errorValue = format;
    516         return BadValue;
    517     }
    518     len = stuff->nUnits;
    519     if (len > bytes_to_int32((0xffffffff - sizeof(xChangePropertyReq))))
    520         return BadLength;
    521     sizeInBytes = format >> 3;
    522     totalSize = len * sizeInBytes;
    523     REQUEST_FIXED_SIZE(xRRChangeProviderPropertyReq, totalSize);
    524 
    525     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
    526 
    527     if (!ValidAtom(stuff->property)) {
    528         client->errorValue = stuff->property;
    529         return BadAtom;
    530     }
    531     if (!ValidAtom(stuff->type)) {
    532         client->errorValue = stuff->type;
    533         return BadAtom;
    534     }
    535 
    536     err = RRChangeProviderProperty(provider, stuff->property,
    537                                  stuff->type, (int) format,
    538                                  (int) mode, len, (void *) &stuff[1], TRUE,
    539                                  TRUE);
    540     if (err != Success)
    541         return err;
    542     else
    543         return Success;
    544 }
    545 
    546 int
    547 ProcRRDeleteProviderProperty(ClientPtr client)
    548 {
    549     REQUEST(xRRDeleteProviderPropertyReq);
    550     RRProviderPtr provider;
    551     RRPropertyPtr prop;
    552 
    553     REQUEST_SIZE_MATCH(xRRDeleteProviderPropertyReq);
    554     UpdateCurrentTime();
    555     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
    556 
    557     if (!ValidAtom(stuff->property)) {
    558         client->errorValue = stuff->property;
    559         return BadAtom;
    560     }
    561 
    562     prop = RRQueryProviderProperty(provider, stuff->property);
    563     if (!prop) {
    564         client->errorValue = stuff->property;
    565         return BadName;
    566     }
    567 
    568     if (prop->immutable) {
    569         client->errorValue = stuff->property;
    570         return BadAccess;
    571     }
    572 
    573     RRDeleteProviderProperty(provider, stuff->property);
    574     return Success;
    575 }
    576 
    577 int
    578 ProcRRGetProviderProperty(ClientPtr client)
    579 {
    580     REQUEST(xRRGetProviderPropertyReq);
    581     RRPropertyPtr prop, *prev;
    582     RRPropertyValuePtr prop_value;
    583     unsigned long n, len, ind;
    584     RRProviderPtr provider;
    585     xRRGetProviderPropertyReply reply = {
    586         .type = X_Reply,
    587         .sequenceNumber = client->sequence
    588     };
    589     char *extra = NULL;
    590 
    591     REQUEST_SIZE_MATCH(xRRGetProviderPropertyReq);
    592     if (stuff->delete)
    593         UpdateCurrentTime();
    594     VERIFY_RR_PROVIDER(stuff->provider, provider,
    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 = &provider->properties; (prop = *prev); prev = &prop->next)
    611         if (prop->propertyName == stuff->property)
    612             break;
    613 
    614     if (!prop) {
    615         reply.nItems = 0;
    616         reply.length = 0;
    617         reply.bytesAfter = 0;
    618         reply.propertyType = None;
    619         reply.format = 0;
    620         if (client->swapped) {
    621             swaps(&reply.sequenceNumber);
    622             swapl(&reply.length);
    623             swapl(&reply.propertyType);
    624             swapl(&reply.bytesAfter);
    625             swapl(&reply.nItems);
    626         }
    627         WriteToClient(client, sizeof(xRRGetProviderPropertyReply), &reply);
    628         return Success;
    629     }
    630 
    631     if (prop->immutable && stuff->delete)
    632         return BadAccess;
    633 
    634     prop_value = RRGetProviderProperty(provider, stuff->property, stuff->pending);
    635     if (!prop_value)
    636         return BadAtom;
    637 
    638     /* If the request type and actual type don't match. Return the
    639        property information, but not the data. */
    640 
    641     if (((stuff->type != prop_value->type) && (stuff->type != AnyPropertyType))
    642         ) {
    643         reply.bytesAfter = prop_value->size;
    644         reply.format = prop_value->format;
    645         reply.length = 0;
    646         reply.nItems = 0;
    647         reply.propertyType = prop_value->type;
    648         if (client->swapped) {
    649             swaps(&reply.sequenceNumber);
    650             swapl(&reply.length);
    651             swapl(&reply.propertyType);
    652             swapl(&reply.bytesAfter);
    653             swapl(&reply.nItems);
    654         }
    655         WriteToClient(client, sizeof(xRRGetProviderPropertyReply), &reply);
    656         return Success;
    657     }
    658 
    659 /*
    660  *  Return type, format, value to client
    661  */
    662     n = (prop_value->format / 8) * prop_value->size;    /* size (bytes) of prop */
    663     ind = stuff->longOffset << 2;
    664 
    665     /* If longOffset is invalid such that it causes "len" to
    666        be negative, it's a value error. */
    667 
    668     if (n < ind) {
    669         client->errorValue = stuff->longOffset;
    670         return BadValue;
    671     }
    672 
    673     len = min(n - ind, 4 * stuff->longLength);
    674 
    675     if (len) {
    676         extra = malloc(len);
    677         if (!extra)
    678             return BadAlloc;
    679     }
    680     reply.bytesAfter = n - (ind + len);
    681     reply.format = prop_value->format;
    682     reply.length = bytes_to_int32(len);
    683     if (prop_value->format)
    684         reply.nItems = len / (prop_value->format / 8);
    685     else
    686         reply.nItems = 0;
    687     reply.propertyType = prop_value->type;
    688 
    689     if (stuff->delete && (reply.bytesAfter == 0)) {
    690         xRRProviderPropertyNotifyEvent event = {
    691             .type = RREventBase + RRNotify,
    692             .subCode = RRNotify_ProviderProperty,
    693             .provider = provider->id,
    694             .state = PropertyDelete,
    695             .atom = prop->propertyName,
    696             .timestamp = currentTime.milliseconds
    697         };
    698         RRDeliverPropertyEvent(provider->pScreen, (xEvent *) &event);
    699     }
    700 
    701     if (client->swapped) {
    702         swaps(&reply.sequenceNumber);
    703         swapl(&reply.length);
    704         swapl(&reply.propertyType);
    705         swapl(&reply.bytesAfter);
    706         swapl(&reply.nItems);
    707     }
    708     WriteToClient(client, sizeof(xGenericReply), &reply);
    709     if (len) {
    710         memcpy(extra, (char *) prop_value->data + ind, len);
    711         switch (reply.format) {
    712         case 32:
    713             client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
    714             break;
    715         case 16:
    716             client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
    717             break;
    718         default:
    719             client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
    720             break;
    721         }
    722         WriteSwappedDataToClient(client, len, extra);
    723         free(extra);
    724     }
    725 
    726     if (stuff->delete && (reply.bytesAfter == 0)) {     /* delete the Property */
    727         *prev = prop->next;
    728         RRDestroyProviderProperty(prop);
    729     }
    730     return Success;
    731 }
    732