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