Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | ||
27 | static int | |
28 | DeliverPropertyEvent(WindowPtr pWin, void *value) | |
29 | { | |
30 | xRRProviderPropertyNotifyEvent *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 | 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 | ||
49 | static void | |
50 | RRDeliverPropertyEvent(ScreenPtr pScreen, xEvent *event) | |
51 | { | |
52 | if (!(dispatchException & (DE_RESET | DE_TERMINATE))) | |
53 | WalkTree(pScreen, DeliverPropertyEvent, event); | |
54 | } | |
55 | ||
56 | static void | |
57 | RRDestroyProviderProperty(RRPropertyPtr prop) | |
58 | { | |
59 | free(prop->valid_values); | |
60 | free(prop->current.data); | |
61 | free(prop->pending.data); | |
62 | free(prop); | |
63 | } | |
64 | ||
65 | static void | |
66 | RRDeleteProperty(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 | ||
82 | void | |
83 | RRDeleteAllProviderProperties(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 | ||
93 | static void | |
94 | RRInitProviderPropertyValue(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 | ||
102 | static RRPropertyPtr | |
103 | RRCreateProviderProperty(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 | ||
122 | void | |
123 | RRDeleteProviderProperty(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 | ||
135 | int | |
136 | RRChangeProviderProperty(RRProviderPtr provider, Atom property, Atom type, | |
137 | int format, int mode, unsigned long len, | |
138 | pointer 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 | pointer new_data = NULL, old_data = NULL; | |
182 | ||
183 | total_size = total_len * size_in_bytes; | |
184 | new_value.data = (pointer) 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 = (pointer) (((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 = (pointer) (((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 | ||
254 | Bool | |
255 | RRPostProviderPendingProperties(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 | ||
294 | RRPropertyPtr | |
295 | RRQueryProviderProperty(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 | ||
305 | RRPropertyValuePtr | |
306 | RRGetProviderProperty(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 | ||
326 | int | |
327 | RRConfigureProviderProperty(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 = malloc(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 | ||
386 | int | |
387 | ProcRRListProviderProperties(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 = (Atom *) malloc(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 | ||
430 | int | |
431 | ProcRRQueryProviderProperty(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 = malloc(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 | ||
475 | int | |
476 | ProcRRConfigureProviderProperty(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 | ||
493 | int | |
494 | ProcRRChangeProviderProperty(ClientPtr client) | |
495 | { | |
496 | REQUEST(xRRChangeProviderPropertyReq); | |
497 | RRProviderPtr provider; | |
498 | char format, mode; | |
499 | unsigned long len; | |
500 | int sizeInBytes; | |
501 | int 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, (pointer) &stuff[1], TRUE, | |
538 | TRUE); | |
539 | if (err != Success) | |
540 | return err; | |
541 | else | |
542 | return Success; | |
543 | } | |
544 | ||
545 | int | |
546 | ProcRRDeleteProviderProperty(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 | ||
576 | int | |
577 | ProcRRGetProviderProperty(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 | } |