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