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        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
255Bool
256RRPostProviderPendingProperties(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
295RRPropertyPtr
296RRQueryProviderProperty(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
306RRPropertyValuePtr
307RRGetProviderProperty(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
327int
328RRConfigureProviderProperty(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
387int
388ProcRRListProviderProperties(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
431int
432ProcRRQueryProviderProperty(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
476int
477ProcRRConfigureProviderProperty(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
494int
495ProcRRChangeProviderProperty(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
546int
547ProcRRDeleteProviderProperty(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
577int
578ProcRRGetProviderProperty(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