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