rrproviderproperty.c revision d9252ffb
1/*
2 * Copyright © 2006 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "randrstr.h"
24#include "propertyst.h"
25#include "swaprep.h"
26
27static int
28DeliverPropertyEvent(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
49static void
50RRDeliverPropertyEvent(ScreenPtr pScreen, xEvent *event)
51{
52    if (!(dispatchException & (DE_RESET | DE_TERMINATE)))
53        WalkTree(pScreen, DeliverPropertyEvent, event);
54}
55
56static void
57RRDestroyProviderProperty(RRPropertyPtr prop)
58{
59    free(prop->valid_values);
60    free(prop->current.data);
61    free(prop->pending.data);
62    free(prop);
63}
64
65static void
66RRDeleteProperty(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
82void
83RRDeleteAllProviderProperties(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
93static void
94RRInitProviderPropertyValue(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
102static RRPropertyPtr
103RRCreateProviderProperty(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
122void
123RRDeleteProviderProperty(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
135int
136RRChangeProviderProperty(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
254Bool
255RRPostProviderPendingProperties(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
294RRPropertyPtr
295RRQueryProviderProperty(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
305RRPropertyValuePtr
306RRGetProviderProperty(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
326int
327RRConfigureProviderProperty(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
386int
387ProcRRListProviderProperties(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
430int
431ProcRRQueryProviderProperty(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
475int
476ProcRRConfigureProviderProperty(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
493int
494ProcRRChangeProviderProperty(ClientPtr client)
495{
496    REQUEST(xRRChangeProviderPropertyReq);
497    RRProviderPtr provider;
498    char format, mode;
499    unsigned long len;
500    int sizeInBytes;
501    uint64_t 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
545int
546ProcRRDeleteProviderProperty(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
576int
577ProcRRGetProviderProperty(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