xiproperty.c revision 6747b715
1/*
2 * Copyright © 2006 Keith Packard
3 * Copyright © 2008 Peter Hutterer
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WAXIANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WAXIANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 */
25
26/* This code is a modified version of randr/rrproperty.c */
27
28#ifdef HAVE_DIX_CONFIG_H
29#include <dix-config.h>
30#endif
31
32#include "dix.h"
33#include "inputstr.h"
34#include <X11/extensions/XI.h>
35#include <X11/Xatom.h>
36#include <X11/extensions/XIproto.h>
37#include <X11/extensions/XI2proto.h>
38#include "exglobals.h"
39#include "exevents.h"
40#include "swaprep.h"
41
42#include "xiproperty.h"
43#include "xserver-properties.h"
44
45/**
46 * Properties used or alloced from inside the server.
47 */
48static struct dev_properties
49{
50    Atom type;
51    char *name;
52} dev_properties[] = {
53    {0, XI_PROP_ENABLED},
54    {0, XI_PROP_XTEST_DEVICE},
55    {0, XATOM_FLOAT},
56    {0, ACCEL_PROP_PROFILE_NUMBER},
57    {0, ACCEL_PROP_CONSTANT_DECELERATION},
58    {0, ACCEL_PROP_ADAPTIVE_DECELERATION},
59    {0, ACCEL_PROP_VELOCITY_SCALING},
60    {0, AXIS_LABEL_PROP},
61    {0, AXIS_LABEL_PROP_REL_X},
62    {0, AXIS_LABEL_PROP_REL_Y},
63    {0, AXIS_LABEL_PROP_REL_Z},
64    {0, AXIS_LABEL_PROP_REL_RX},
65    {0, AXIS_LABEL_PROP_REL_RY},
66    {0, AXIS_LABEL_PROP_REL_RZ},
67    {0, AXIS_LABEL_PROP_REL_HWHEEL},
68    {0, AXIS_LABEL_PROP_REL_DIAL},
69    {0, AXIS_LABEL_PROP_REL_WHEEL},
70    {0, AXIS_LABEL_PROP_REL_MISC},
71    {0, AXIS_LABEL_PROP_ABS_X},
72    {0, AXIS_LABEL_PROP_ABS_Y},
73    {0, AXIS_LABEL_PROP_ABS_Z},
74    {0, AXIS_LABEL_PROP_ABS_RX},
75    {0, AXIS_LABEL_PROP_ABS_RY},
76    {0, AXIS_LABEL_PROP_ABS_RZ},
77    {0, AXIS_LABEL_PROP_ABS_THROTTLE},
78    {0, AXIS_LABEL_PROP_ABS_RUDDER},
79    {0, AXIS_LABEL_PROP_ABS_WHEEL},
80    {0, AXIS_LABEL_PROP_ABS_GAS},
81    {0, AXIS_LABEL_PROP_ABS_BRAKE},
82    {0, AXIS_LABEL_PROP_ABS_HAT0X},
83    {0, AXIS_LABEL_PROP_ABS_HAT0Y},
84    {0, AXIS_LABEL_PROP_ABS_HAT1X},
85    {0, AXIS_LABEL_PROP_ABS_HAT1Y},
86    {0, AXIS_LABEL_PROP_ABS_HAT2X},
87    {0, AXIS_LABEL_PROP_ABS_HAT2Y},
88    {0, AXIS_LABEL_PROP_ABS_HAT3X},
89    {0, AXIS_LABEL_PROP_ABS_HAT3Y},
90    {0, AXIS_LABEL_PROP_ABS_PRESSURE},
91    {0, AXIS_LABEL_PROP_ABS_DISTANCE},
92    {0, AXIS_LABEL_PROP_ABS_TILT_X},
93    {0, AXIS_LABEL_PROP_ABS_TILT_Y},
94    {0, AXIS_LABEL_PROP_ABS_TOOL_WIDTH},
95    {0, AXIS_LABEL_PROP_ABS_VOLUME},
96    {0, AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR},
97    {0, AXIS_LABEL_PROP_ABS_MT_TOUCH_MINOR},
98    {0, AXIS_LABEL_PROP_ABS_MT_WIDTH_MAJOR},
99    {0, AXIS_LABEL_PROP_ABS_MT_WIDTH_MINOR},
100    {0, AXIS_LABEL_PROP_ABS_MT_ORIENTATION},
101    {0, AXIS_LABEL_PROP_ABS_MT_POSITION_X},
102    {0, AXIS_LABEL_PROP_ABS_MT_POSITION_Y},
103    {0, AXIS_LABEL_PROP_ABS_MT_TOOL_TYPE},
104    {0, AXIS_LABEL_PROP_ABS_MT_BLOB_ID},
105    {0, AXIS_LABEL_PROP_ABS_MT_TRACKING_ID},
106    {0, AXIS_LABEL_PROP_ABS_MT_PRESSURE},
107    {0, AXIS_LABEL_PROP_ABS_MISC},
108
109    {0, BTN_LABEL_PROP},
110    {0, BTN_LABEL_PROP_BTN_UNKNOWN},
111    {0, BTN_LABEL_PROP_BTN_WHEEL_UP},
112    {0, BTN_LABEL_PROP_BTN_WHEEL_DOWN},
113    {0, BTN_LABEL_PROP_BTN_HWHEEL_LEFT},
114    {0, BTN_LABEL_PROP_BTN_HWHEEL_RIGHT},
115    {0, BTN_LABEL_PROP_BTN_0},
116    {0, BTN_LABEL_PROP_BTN_1},
117    {0, BTN_LABEL_PROP_BTN_2},
118    {0, BTN_LABEL_PROP_BTN_3},
119    {0, BTN_LABEL_PROP_BTN_4},
120    {0, BTN_LABEL_PROP_BTN_5},
121    {0, BTN_LABEL_PROP_BTN_6},
122    {0, BTN_LABEL_PROP_BTN_7},
123    {0, BTN_LABEL_PROP_BTN_8},
124    {0, BTN_LABEL_PROP_BTN_9},
125
126    {0, BTN_LABEL_PROP_BTN_LEFT},
127    {0, BTN_LABEL_PROP_BTN_RIGHT},
128    {0, BTN_LABEL_PROP_BTN_MIDDLE},
129    {0, BTN_LABEL_PROP_BTN_SIDE},
130    {0, BTN_LABEL_PROP_BTN_EXTRA},
131    {0, BTN_LABEL_PROP_BTN_FORWARD},
132    {0, BTN_LABEL_PROP_BTN_BACK},
133    {0, BTN_LABEL_PROP_BTN_TASK},
134
135    {0, BTN_LABEL_PROP_BTN_TRIGGER},
136    {0, BTN_LABEL_PROP_BTN_THUMB},
137    {0, BTN_LABEL_PROP_BTN_THUMB2},
138    {0, BTN_LABEL_PROP_BTN_TOP},
139    {0, BTN_LABEL_PROP_BTN_TOP2},
140    {0, BTN_LABEL_PROP_BTN_PINKIE},
141    {0, BTN_LABEL_PROP_BTN_BASE},
142    {0, BTN_LABEL_PROP_BTN_BASE2},
143    {0, BTN_LABEL_PROP_BTN_BASE3},
144    {0, BTN_LABEL_PROP_BTN_BASE4},
145    {0, BTN_LABEL_PROP_BTN_BASE5},
146    {0, BTN_LABEL_PROP_BTN_BASE6},
147    {0, BTN_LABEL_PROP_BTN_DEAD},
148
149    {0, BTN_LABEL_PROP_BTN_A},
150    {0, BTN_LABEL_PROP_BTN_B},
151    {0, BTN_LABEL_PROP_BTN_C},
152    {0, BTN_LABEL_PROP_BTN_X},
153    {0, BTN_LABEL_PROP_BTN_Y},
154    {0, BTN_LABEL_PROP_BTN_Z},
155    {0, BTN_LABEL_PROP_BTN_TL},
156    {0, BTN_LABEL_PROP_BTN_TR},
157    {0, BTN_LABEL_PROP_BTN_TL2},
158    {0, BTN_LABEL_PROP_BTN_TR2},
159    {0, BTN_LABEL_PROP_BTN_SELECT},
160    {0, BTN_LABEL_PROP_BTN_START},
161    {0, BTN_LABEL_PROP_BTN_MODE},
162    {0, BTN_LABEL_PROP_BTN_THUMBL},
163    {0, BTN_LABEL_PROP_BTN_THUMBR},
164
165    {0, BTN_LABEL_PROP_BTN_TOOL_PEN},
166    {0, BTN_LABEL_PROP_BTN_TOOL_RUBBER},
167    {0, BTN_LABEL_PROP_BTN_TOOL_BRUSH},
168    {0, BTN_LABEL_PROP_BTN_TOOL_PENCIL},
169    {0, BTN_LABEL_PROP_BTN_TOOL_AIRBRUSH},
170    {0, BTN_LABEL_PROP_BTN_TOOL_FINGER},
171    {0, BTN_LABEL_PROP_BTN_TOOL_MOUSE},
172    {0, BTN_LABEL_PROP_BTN_TOOL_LENS},
173    {0, BTN_LABEL_PROP_BTN_TOUCH},
174    {0, BTN_LABEL_PROP_BTN_STYLUS},
175    {0, BTN_LABEL_PROP_BTN_STYLUS2},
176    {0, BTN_LABEL_PROP_BTN_TOOL_DOUBLETAP},
177    {0, BTN_LABEL_PROP_BTN_TOOL_TRIPLETAP},
178
179    {0, BTN_LABEL_PROP_BTN_GEAR_DOWN},
180    {0, BTN_LABEL_PROP_BTN_GEAR_UP},
181
182    {0, XI_PROP_TRANSFORM}
183};
184
185static long XIPropHandlerID = 1;
186
187static void send_property_event(DeviceIntPtr dev, Atom property, int what)
188{
189        devicePropertyNotify    event;
190        xXIPropertyEvent        xi2;
191        int state;
192
193        if (what == XIPropertyDeleted)
194            state = PropertyDelete;
195        else
196            state = PropertyNewValue;
197
198        event.type      = DevicePropertyNotify;
199        event.deviceid  = dev->id;
200        event.state     = state;
201        event.atom      = property;
202        event.time      = currentTime.milliseconds;
203        SendEventToAllWindows(dev, DevicePropertyNotifyMask,
204                              (xEvent*)&event, 1);
205
206        xi2.type        = GenericEvent;
207        xi2.extension   = IReqCode;
208        xi2.length      = 0;
209        xi2.evtype      = XI_PropertyEvent;
210        xi2.deviceid    = dev->id;
211        xi2.time        = currentTime.milliseconds;
212        xi2.property    = property;
213        xi2.what        = what;
214        SendEventToAllWindows(dev, GetEventFilter(dev, (xEvent*)&xi2),
215                              (xEvent*)&xi2, 1);
216}
217
218static int list_atoms(DeviceIntPtr dev, int *natoms, Atom **atoms_return)
219{
220    XIPropertyPtr prop;
221    Atom *atoms         = NULL;
222    int nprops          = 0;
223
224    for (prop = dev->properties.properties; prop; prop = prop->next)
225        nprops++;
226    if (nprops)
227    {
228        Atom *a;
229
230        atoms = malloc(nprops * sizeof(Atom));
231        if(!atoms)
232            return BadAlloc;
233        a = atoms;
234        for (prop = dev->properties.properties; prop; prop = prop->next, a++)
235            *a = prop->propertyName;
236    }
237
238    *natoms = nprops;
239    *atoms_return = atoms;
240    return Success;
241}
242
243static int
244get_property(ClientPtr client, DeviceIntPtr dev, Atom property, Atom type,
245             BOOL delete, int offset, int length,
246             int *bytes_after, Atom *type_return, int *format, int *nitems,
247             int *length_return, char **data)
248{
249    unsigned long n, len, ind;
250    int rc;
251    XIPropertyPtr prop;
252    XIPropertyValuePtr prop_value;
253
254    if (!ValidAtom(property))
255    {
256        client->errorValue = property;
257        return BadAtom;
258    }
259    if ((delete != xTrue) && (delete != xFalse))
260    {
261        client->errorValue = delete;
262        return BadValue;
263    }
264
265    if ((type != AnyPropertyType) && !ValidAtom(type))
266    {
267        client->errorValue = type;
268        return BadAtom;
269    }
270
271    for (prop = dev->properties.properties; prop; prop = prop->next)
272        if (prop->propertyName == property)
273            break;
274
275    if (!prop)
276    {
277        *bytes_after = 0;
278        *type_return = None;
279        *format = 0;
280        *nitems = 0;
281        *length_return = 0;
282        return Success;
283    }
284
285    rc = XIGetDeviceProperty(dev, property, &prop_value);
286    if (rc != Success)
287    {
288        client->errorValue = property;
289        return rc;
290    }
291
292    /* If the request type and actual type don't match. Return the
293    property information, but not the data. */
294
295    if (((type != prop_value->type) && (type != AnyPropertyType)))
296    {
297        *bytes_after = prop_value->size;
298        *format = prop_value->format;
299        *length_return = 0;
300        *nitems = 0;
301        *type_return = prop_value->type;
302        return Success;
303    }
304
305    /* Return type, format, value to client */
306    n = (prop_value->format/8) * prop_value->size; /* size (bytes) of prop */
307    ind = offset << 2;
308
309   /* If offset is invalid such that it causes "len" to
310            be negative, it's a value error. */
311
312    if (n < ind)
313    {
314        client->errorValue = offset;
315        return BadValue;
316    }
317
318    len = min(n - ind, 4 * length);
319
320    *bytes_after = n - (ind + len);
321    *format = prop_value->format;
322    *length_return = len;
323    if (prop_value->format)
324        *nitems = len / (prop_value->format / 8);
325    else
326        *nitems = 0;
327    *type_return = prop_value->type;
328
329    *data = (char*)prop_value->data + ind;
330
331    return Success;
332}
333
334static int
335check_change_property(ClientPtr client, Atom property, Atom type, int format,
336                      int mode, int nitems)
337{
338    if ((mode != PropModeReplace) && (mode != PropModeAppend) &&
339        (mode != PropModePrepend))
340    {
341        client->errorValue = mode;
342        return BadValue;
343    }
344    if ((format != 8) && (format != 16) && (format != 32))
345    {
346        client->errorValue = format;
347        return BadValue;
348    }
349
350    if (!ValidAtom(property))
351    {
352        client->errorValue = property;
353        return BadAtom;
354    }
355    if (!ValidAtom(type))
356    {
357        client->errorValue = type;
358        return BadAtom;
359    }
360
361    return Success;
362}
363
364static int
365change_property(ClientPtr client, DeviceIntPtr dev, Atom property, Atom type,
366                int format, int mode, int len, void *data)
367{
368    int rc = Success;
369
370    rc = XIChangeDeviceProperty(dev, property, type, format, mode, len, data, TRUE);
371    if (rc != Success)
372        client->errorValue = property;
373
374    return rc;
375}
376
377/**
378 * Return the atom assigned to the specified string or 0 if the atom isn't known
379 * to the DIX.
380 *
381 * If name is NULL, None is returned.
382 */
383Atom
384XIGetKnownProperty(char *name)
385{
386    int i;
387
388    if (!name)
389        return None;
390
391    for (i = 0; i < (sizeof(dev_properties)/sizeof(struct dev_properties)); i++)
392    {
393        if (strcmp(name, dev_properties[i].name) == 0){
394            if (dev_properties[i].type == None){
395		dev_properties[i].type =
396			    MakeAtom(dev_properties[i].name,
397			             strlen(dev_properties[i].name),
398			             TRUE);
399            }
400
401            return dev_properties[i].type;
402        }
403    }
404
405    return 0;
406}
407
408void
409XIResetProperties(void)
410{
411    int i;
412
413    for (i = 0; i < (sizeof(dev_properties)/sizeof(struct dev_properties)); i++)
414        dev_properties[i].type = None;
415}
416
417/**
418 * Convert the given property's value(s) into @nelem_return integer values and
419 * store them in @buf_return. If @nelem_return is larger than the number of
420 * values in the property, @nelem_return is set to the number of values in the
421 * property.
422 *
423 * If *@buf_return is NULL and @nelem_return is 0, memory is allocated
424 * automatically and must be freed by the caller.
425 *
426 * Possible return codes.
427 * Success ... No error.
428 * BadMatch ... Wrong atom type, atom is not XA_INTEGER
429 * BadAlloc ... NULL passed as buffer and allocation failed.
430 * BadLength ... @buff is NULL but @nelem_return is non-zero.
431 *
432 * @param val The property value
433 * @param nelem_return The maximum number of elements to return.
434 * @param buf_return Pointer to an array of at least @nelem_return values.
435 * @return Success or the error code if an error occured.
436 */
437_X_EXPORT int
438XIPropToInt(XIPropertyValuePtr val, int *nelem_return, int **buf_return)
439{
440    int i;
441    int *buf;
442
443    if (val->type != XA_INTEGER)
444        return BadMatch;
445    if (!*buf_return && *nelem_return)
446        return BadLength;
447
448    switch(val->format)
449    {
450        case 8:
451        case 16:
452        case 32:
453            break;
454        default:
455            return BadValue;
456    }
457
458    buf = *buf_return;
459
460    if (!buf && !(*nelem_return))
461    {
462        buf = calloc(val->size, sizeof(int));
463        if (!buf)
464            return BadAlloc;
465        *buf_return = buf;
466        *nelem_return = val->size;
467    } else if (val->size < *nelem_return)
468        *nelem_return = val->size;
469
470    for (i = 0; i < val->size && i < *nelem_return; i++)
471    {
472        switch(val->format)
473        {
474            case 8:  buf[i] = ((CARD8*)val->data)[i]; break;
475            case 16: buf[i] = ((CARD16*)val->data)[i]; break;
476            case 32: buf[i] = ((CARD32*)val->data)[i]; break;
477        }
478    }
479
480    return Success;
481}
482
483/**
484 * Convert the given property's value(s) into @nelem_return float values and
485 * store them in @buf_return. If @nelem_return is larger than the number of
486 * values in the property, @nelem_return is set to the number of values in the
487 * property.
488 *
489 * If *@buf_return is NULL and @nelem_return is 0, memory is allocated
490 * automatically and must be freed by the caller.
491 *
492 * Possible errors returned:
493 * Success
494 * BadMatch ... Wrong atom type, atom is not XA_FLOAT
495 * BadValue ... Wrong format, format is not 32
496 * BadAlloc ... NULL passed as buffer and allocation failed.
497 * BadLength ... @buff is NULL but @nelem_return is non-zero.
498 *
499 * @param val The property value
500 * @param nelem_return The maximum number of elements to return.
501 * @param buf_return Pointer to an array of at least @nelem_return values.
502 * @return Success or the error code if an error occured.
503 */
504_X_EXPORT int
505XIPropToFloat(XIPropertyValuePtr val, int *nelem_return, float **buf_return)
506{
507    int i;
508    float *buf;
509
510    if (!val->type || val->type != XIGetKnownProperty(XATOM_FLOAT))
511        return BadMatch;
512
513    if (val->format != 32)
514        return BadValue;
515    if (!*buf_return && *nelem_return)
516        return BadLength;
517
518    buf = *buf_return;
519
520    if (!buf && !(*nelem_return))
521    {
522        buf = calloc(val->size, sizeof(float));
523        if (!buf)
524            return BadAlloc;
525        *buf_return = buf;
526        *nelem_return = val->size;
527    } else if (val->size < *nelem_return)
528        *nelem_return = val->size;
529
530    for (i = 0; i < val->size && i < *nelem_return; i++)
531           buf[i] = ((float*)val->data)[i];
532
533    return Success;
534}
535
536/* Registers a new property handler on the given device and returns a unique
537 * identifier for this handler. This identifier is required to unregister the
538 * property handler again.
539 * @return The handler's identifier or 0 if an error occured.
540 */
541long
542XIRegisterPropertyHandler(DeviceIntPtr         dev,
543                          int (*SetProperty) (DeviceIntPtr dev,
544                                              Atom property,
545                                              XIPropertyValuePtr prop,
546                                              BOOL checkonly),
547                          int (*GetProperty) (DeviceIntPtr dev,
548                                              Atom property),
549                          int (*DeleteProperty) (DeviceIntPtr dev,
550                                                 Atom property))
551{
552    XIPropertyHandlerPtr new_handler;
553
554    new_handler = calloc(1, sizeof(XIPropertyHandler));
555    if (!new_handler)
556        return 0;
557
558    new_handler->id = XIPropHandlerID++;
559    new_handler->SetProperty = SetProperty;
560    new_handler->GetProperty = GetProperty;
561    new_handler->DeleteProperty = DeleteProperty;
562    new_handler->next = dev->properties.handlers;
563    dev->properties.handlers = new_handler;
564
565    return new_handler->id;
566}
567
568void
569XIUnregisterPropertyHandler(DeviceIntPtr dev, long id)
570{
571    XIPropertyHandlerPtr curr, prev = NULL;
572
573    curr = dev->properties.handlers;
574    while(curr && curr->id != id)
575    {
576        prev = curr;
577        curr = curr->next;
578    }
579
580    if (!curr)
581        return;
582
583    if (!prev) /* first one */
584        dev->properties.handlers = curr->next;
585    else
586        prev->next = curr->next;
587
588    free(curr);
589}
590
591static XIPropertyPtr
592XICreateDeviceProperty (Atom property)
593{
594    XIPropertyPtr   prop;
595
596    prop = (XIPropertyPtr)malloc(sizeof(XIPropertyRec));
597    if (!prop)
598        return NULL;
599
600    prop->next          = NULL;
601    prop->propertyName  = property;
602    prop->value.type   = None;
603    prop->value.format = 0;
604    prop->value.size   = 0;
605    prop->value.data   = NULL;
606    prop->deletable    = TRUE;
607
608    return prop;
609}
610
611static XIPropertyPtr
612XIFetchDeviceProperty(DeviceIntPtr dev, Atom property)
613{
614    XIPropertyPtr   prop;
615
616    for (prop = dev->properties.properties; prop; prop = prop->next)
617        if (prop->propertyName == property)
618            return prop;
619    return NULL;
620}
621
622static void
623XIDestroyDeviceProperty (XIPropertyPtr prop)
624{
625    free(prop->value.data);
626    free(prop);
627}
628
629/* This function destroys all of the device's property-related stuff,
630 * including removing all device handlers.
631 * DO NOT CALL FROM THE DRIVER.
632 */
633void
634XIDeleteAllDeviceProperties (DeviceIntPtr device)
635{
636    XIPropertyPtr               prop, next;
637    XIPropertyHandlerPtr        curr_handler, next_handler;
638
639    for (prop = device->properties.properties; prop; prop = next)
640    {
641        next = prop->next;
642        send_property_event(device, prop->propertyName, XIPropertyDeleted);
643        XIDestroyDeviceProperty(prop);
644    }
645
646    device->properties.properties = NULL;
647
648    /* Now free all handlers */
649    curr_handler = device->properties.handlers;
650    while(curr_handler)
651    {
652        next_handler = curr_handler->next;
653        free(curr_handler);
654        curr_handler = next_handler;
655    }
656
657    device->properties.handlers = NULL;
658}
659
660
661int
662XIDeleteDeviceProperty (DeviceIntPtr device, Atom property, Bool fromClient)
663{
664    XIPropertyPtr               prop, *prev;
665    int                         rc = Success;
666
667    for (prev = &device->properties.properties; (prop = *prev); prev = &(prop->next))
668        if (prop->propertyName == property)
669            break;
670
671    if (!prop)
672        return Success;
673
674    if (fromClient && !prop->deletable)
675        return BadAccess;
676
677    /* Ask handlers if we may delete the property */
678    if (device->properties.handlers)
679    {
680        XIPropertyHandlerPtr handler = device->properties.handlers;
681        while(handler)
682        {
683            if (handler->DeleteProperty)
684                rc = handler->DeleteProperty(device, prop->propertyName);
685            if (rc != Success)
686                return rc;
687            handler = handler->next;
688        }
689    }
690
691    if (prop)
692    {
693        *prev = prop->next;
694        send_property_event(device, prop->propertyName, XIPropertyDeleted);
695        XIDestroyDeviceProperty (prop);
696    }
697
698    return Success;
699}
700
701int
702XIChangeDeviceProperty (DeviceIntPtr dev, Atom property, Atom type,
703                        int format, int mode, unsigned long len,
704                        pointer value, Bool sendevent)
705{
706    XIPropertyPtr               prop;
707    int                         size_in_bytes;
708    int                         total_size;
709    unsigned long               total_len;
710    XIPropertyValuePtr          prop_value;
711    XIPropertyValueRec          new_value;
712    Bool                        add = FALSE;
713    int                         rc;
714
715    size_in_bytes = format >> 3;
716
717    /* first see if property already exists */
718    prop = XIFetchDeviceProperty (dev, property);
719    if (!prop)   /* just add to list */
720    {
721        prop = XICreateDeviceProperty (property);
722        if (!prop)
723            return BadAlloc;
724        add = TRUE;
725        mode = PropModeReplace;
726    }
727    prop_value = &prop->value;
728
729    /* To append or prepend to a property the request format and type
730     must match those of the already defined property.  The
731     existing format and type are irrelevant when using the mode
732     "PropModeReplace" since they will be written over. */
733
734    if ((format != prop_value->format) && (mode != PropModeReplace))
735        return BadMatch;
736    if ((prop_value->type != type) && (mode != PropModeReplace))
737        return BadMatch;
738    new_value = *prop_value;
739    if (mode == PropModeReplace)
740        total_len = len;
741    else
742        total_len = prop_value->size + len;
743
744    if (mode == PropModeReplace || len > 0)
745    {
746        pointer            new_data = NULL, old_data = NULL;
747
748        total_size = total_len * size_in_bytes;
749        new_value.data = (pointer)malloc(total_size);
750        if (!new_value.data && total_size)
751        {
752            if (add)
753                XIDestroyDeviceProperty (prop);
754            return BadAlloc;
755        }
756        new_value.size = len;
757        new_value.type = type;
758        new_value.format = format;
759
760        switch (mode) {
761        case PropModeReplace:
762            new_data = new_value.data;
763            old_data = NULL;
764            break;
765        case PropModeAppend:
766            new_data = (pointer) (((char *) new_value.data) +
767                                  (prop_value->size * size_in_bytes));
768            old_data = new_value.data;
769            break;
770        case PropModePrepend:
771            new_data = new_value.data;
772            old_data = (pointer) (((char *) new_value.data) +
773                                  (prop_value->size * size_in_bytes));
774            break;
775        }
776        if (new_data)
777            memcpy ((char *) new_data, (char *) value, len * size_in_bytes);
778        if (old_data)
779            memcpy ((char *) old_data, (char *) prop_value->data,
780                    prop_value->size * size_in_bytes);
781
782        if (dev->properties.handlers)
783        {
784            XIPropertyHandlerPtr handler;
785            BOOL checkonly = TRUE;
786            /* run through all handlers with checkonly TRUE, then again with
787             * checkonly FALSE. Handlers MUST return error codes on the
788             * checkonly run, errors on the second run are ignored */
789            do
790            {
791                handler = dev->properties.handlers;
792                while(handler)
793                {
794                    if (handler->SetProperty)
795                    {
796                        rc = handler->SetProperty(dev, prop->propertyName,
797                                &new_value, checkonly);
798                        if (checkonly && rc != Success)
799                        {
800                            free(new_value.data);
801                            return rc;
802                        }
803                    }
804                    handler = handler->next;
805                }
806                checkonly = !checkonly;
807            } while (!checkonly);
808        }
809        free(prop_value->data);
810        *prop_value = new_value;
811    } else if (len == 0)
812    {
813        /* do nothing */
814    }
815
816    if (add)
817    {
818        prop->next = dev->properties.properties;
819        dev->properties.properties = prop;
820    }
821
822    if (sendevent)
823        send_property_event(dev, prop->propertyName,
824                            (add) ?  XIPropertyCreated : XIPropertyModified);
825
826    return Success;
827}
828
829int
830XIGetDeviceProperty (DeviceIntPtr dev, Atom property, XIPropertyValuePtr *value)
831{
832    XIPropertyPtr   prop = XIFetchDeviceProperty (dev, property);
833    int rc;
834
835    if (!prop)
836    {
837        *value = NULL;
838        return BadAtom;
839    }
840
841    /* If we can, try to update the property value first */
842    if (dev->properties.handlers)
843    {
844        XIPropertyHandlerPtr handler = dev->properties.handlers;
845        while(handler)
846        {
847            if (handler->GetProperty)
848            {
849                rc = handler->GetProperty(dev, prop->propertyName);
850                if (rc != Success)
851                {
852                    *value = NULL;
853                    return rc;
854                }
855            }
856            handler = handler->next;
857        }
858    }
859
860    *value = &prop->value;
861    return Success;
862}
863
864int
865XISetDevicePropertyDeletable(DeviceIntPtr dev, Atom property, Bool deletable)
866{
867    XIPropertyPtr prop = XIFetchDeviceProperty(dev, property);
868
869    if (!prop)
870        return BadAtom;
871
872    prop->deletable = deletable;
873    return Success;
874}
875
876int
877ProcXListDeviceProperties (ClientPtr client)
878{
879    Atom                        *atoms;
880    xListDevicePropertiesReply  rep;
881    int                         natoms;
882    DeviceIntPtr                dev;
883    int                         rc = Success;
884
885    REQUEST(xListDevicePropertiesReq);
886    REQUEST_SIZE_MATCH(xListDevicePropertiesReq);
887
888    rc = dixLookupDevice (&dev, stuff->deviceid, client, DixListPropAccess);
889    if (rc != Success)
890        return rc;
891
892    rc = list_atoms(dev, &natoms, &atoms);
893    if (rc != Success)
894        return rc;
895
896    rep.repType = X_Reply;
897    rep.RepType = X_ListDeviceProperties;
898    rep.length = natoms;
899    rep.sequenceNumber = client->sequence;
900    rep.nAtoms = natoms;
901
902    WriteReplyToClient(client, sizeof(xListDevicePropertiesReply), &rep);
903    if (natoms)
904    {
905        client->pSwapReplyFunc = (ReplySwapPtr)Swap32Write;
906        WriteSwappedDataToClient(client, natoms * sizeof(Atom), atoms);
907        free(atoms);
908    }
909    return rc;
910}
911
912int
913ProcXChangeDeviceProperty (ClientPtr client)
914{
915    REQUEST(xChangeDevicePropertyReq);
916    DeviceIntPtr        dev;
917    unsigned long       len;
918    int                 totalSize;
919    int                 rc;
920
921    REQUEST_AT_LEAST_SIZE(xChangeDevicePropertyReq);
922    UpdateCurrentTime();
923
924    rc = dixLookupDevice (&dev, stuff->deviceid, client, DixSetPropAccess);
925    if (rc != Success)
926        return rc;
927
928    rc = check_change_property(client, stuff->property, stuff->type,
929                               stuff->format, stuff->mode, stuff->nUnits);
930
931    len = stuff->nUnits;
932    if (len > (bytes_to_int32(0xffffffff - sizeof(xChangeDevicePropertyReq))))
933        return BadLength;
934
935    totalSize = len * (stuff->format/8);
936    REQUEST_FIXED_SIZE(xChangeDevicePropertyReq, totalSize);
937
938    rc = change_property(client, dev, stuff->property, stuff->type,
939                         stuff->format, stuff->mode, len, (void*)&stuff[1]);
940    return rc;
941}
942
943int
944ProcXDeleteDeviceProperty (ClientPtr client)
945{
946    REQUEST(xDeleteDevicePropertyReq);
947    DeviceIntPtr        dev;
948    int                 rc;
949
950    REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq);
951    UpdateCurrentTime();
952    rc =  dixLookupDevice (&dev, stuff->deviceid, client, DixSetPropAccess);
953    if (rc != Success)
954        return rc;
955
956    if (!ValidAtom(stuff->property))
957    {
958        client->errorValue = stuff->property;
959        return BadAtom;
960    }
961
962    rc = XIDeleteDeviceProperty(dev, stuff->property, TRUE);
963    return rc;
964}
965
966int
967ProcXGetDeviceProperty (ClientPtr client)
968{
969    REQUEST(xGetDevicePropertyReq);
970    DeviceIntPtr                dev;
971    int                         length;
972    int                         rc, format, nitems, bytes_after;
973    char                        *data;
974    Atom                        type;
975    xGetDevicePropertyReply     reply;
976
977    REQUEST_SIZE_MATCH(xGetDevicePropertyReq);
978    if (stuff->delete)
979        UpdateCurrentTime();
980    rc = dixLookupDevice (&dev, stuff->deviceid, client,
981                           stuff->delete ? DixSetPropAccess :
982                           DixGetPropAccess);
983    if (rc != Success)
984        return rc;
985
986    rc = get_property(client, dev, stuff->property, stuff->type,
987            stuff->delete, stuff->longOffset, stuff->longLength,
988            &bytes_after, &type, &format, &nitems, &length, &data);
989
990    if (rc != Success)
991        return rc;
992
993    reply.repType = X_Reply;
994    reply.RepType = X_GetDeviceProperty;
995    reply.sequenceNumber = client->sequence;
996    reply.deviceid = dev->id;
997    reply.nItems = nitems;
998    reply.format = format;
999    reply.bytesAfter = bytes_after;
1000    reply.propertyType = type;
1001    reply.length = bytes_to_int32(length);
1002
1003    if (stuff->delete && (reply.bytesAfter == 0))
1004        send_property_event(dev, stuff->property, XIPropertyDeleted);
1005
1006    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
1007
1008    if (length)
1009    {
1010        switch (reply.format) {
1011            case 32: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap32Write; break;
1012            case 16: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap16Write; break;
1013            default: client->pSwapReplyFunc = (ReplySwapPtr)WriteToClient; break;
1014        }
1015        WriteSwappedDataToClient(client, length, data);
1016    }
1017
1018    /* delete the Property */
1019    if (stuff->delete && (reply.bytesAfter == 0))
1020    {
1021        XIPropertyPtr prop, *prev;
1022        for (prev = &dev->properties.properties; (prop = *prev); prev = &prop->next)
1023        {
1024            if (prop->propertyName == stuff->property)
1025            {
1026                *prev = prop->next;
1027                XIDestroyDeviceProperty(prop);
1028                break;
1029            }
1030        }
1031    }
1032    return Success;
1033}
1034
1035
1036int
1037SProcXListDeviceProperties (ClientPtr client)
1038{
1039    char n;
1040    REQUEST(xListDevicePropertiesReq);
1041
1042    swaps(&stuff->length, n);
1043
1044    REQUEST_SIZE_MATCH(xListDevicePropertiesReq);
1045    return (ProcXListDeviceProperties(client));
1046}
1047
1048int
1049SProcXChangeDeviceProperty (ClientPtr client)
1050{
1051    char n;
1052    REQUEST(xChangeDevicePropertyReq);
1053
1054    swaps(&stuff->length, n);
1055    swapl(&stuff->property, n);
1056    swapl(&stuff->type, n);
1057    swapl(&stuff->nUnits, n);
1058    REQUEST_SIZE_MATCH(xChangeDevicePropertyReq);
1059    return (ProcXChangeDeviceProperty(client));
1060}
1061
1062int
1063SProcXDeleteDeviceProperty (ClientPtr client)
1064{
1065    char n;
1066    REQUEST(xDeleteDevicePropertyReq);
1067
1068    swaps(&stuff->length, n);
1069    swapl(&stuff->property, n);
1070    REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq);
1071    return (ProcXDeleteDeviceProperty(client));
1072}
1073
1074int
1075SProcXGetDeviceProperty (ClientPtr client)
1076{
1077    char n;
1078    REQUEST(xGetDevicePropertyReq);
1079
1080    swaps(&stuff->length, n);
1081    swapl(&stuff->property, n);
1082    swapl(&stuff->type, n);
1083    swapl(&stuff->longOffset, n);
1084    swapl(&stuff->longLength, n);
1085    REQUEST_SIZE_MATCH(xGetDevicePropertyReq);
1086    return (ProcXGetDeviceProperty(client));
1087}
1088
1089
1090/* Reply swapping */
1091
1092void
1093SRepXListDeviceProperties(ClientPtr client, int size,
1094                          xListDevicePropertiesReply *rep)
1095{
1096    char n;
1097    swaps(&rep->sequenceNumber, n);
1098    swapl(&rep->length, n);
1099    swaps(&rep->nAtoms, n);
1100    /* properties will be swapped later, see ProcXListDeviceProperties */
1101    WriteToClient(client, size, (char*)rep);
1102}
1103
1104void
1105SRepXGetDeviceProperty(ClientPtr client, int size,
1106                       xGetDevicePropertyReply *rep)
1107{
1108    char n;
1109
1110    swaps(&rep->sequenceNumber, n);
1111    swapl(&rep->length, n);
1112    swapl(&rep->propertyType, n);
1113    swapl(&rep->bytesAfter, n);
1114    swapl(&rep->nItems, n);
1115    /* data will be swapped, see ProcXGetDeviceProperty */
1116    WriteToClient(client, size, (char*)rep);
1117}
1118
1119/* XI2 Request/reply handling */
1120int
1121ProcXIListProperties(ClientPtr client)
1122{
1123    Atom                        *atoms;
1124    xXIListPropertiesReply      rep;
1125    int                         natoms;
1126    DeviceIntPtr                dev;
1127    int                         rc = Success;
1128
1129    REQUEST(xXIListPropertiesReq);
1130    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1131
1132    rc = dixLookupDevice (&dev, stuff->deviceid, client, DixListPropAccess);
1133    if (rc != Success)
1134        return rc;
1135
1136    rc = list_atoms(dev, &natoms, &atoms);
1137    if (rc != Success)
1138        return rc;
1139
1140    rep.repType = X_Reply;
1141    rep.RepType = X_XIListProperties;
1142    rep.length = natoms;
1143    rep.sequenceNumber = client->sequence;
1144    rep.num_properties = natoms;
1145
1146    WriteReplyToClient(client, sizeof(xXIListPropertiesReply), &rep);
1147    if (natoms)
1148    {
1149        client->pSwapReplyFunc = (ReplySwapPtr)Swap32Write;
1150        WriteSwappedDataToClient(client, natoms * sizeof(Atom), atoms);
1151        free(atoms);
1152    }
1153    return rc;
1154}
1155
1156int
1157ProcXIChangeProperty(ClientPtr client)
1158{
1159    int                 rc;
1160    DeviceIntPtr        dev;
1161    int                 totalSize;
1162    unsigned long       len;
1163
1164    REQUEST(xXIChangePropertyReq);
1165    REQUEST_AT_LEAST_SIZE(xXIChangePropertyReq);
1166    UpdateCurrentTime();
1167
1168    rc = dixLookupDevice (&dev, stuff->deviceid, client, DixSetPropAccess);
1169    if (rc != Success)
1170        return rc;
1171
1172    rc = check_change_property(client, stuff->property, stuff->type,
1173                               stuff->format, stuff->mode, stuff->num_items);
1174    len = stuff->num_items;
1175    if (len > bytes_to_int32(0xffffffff - sizeof(xXIChangePropertyReq)))
1176        return BadLength;
1177
1178    totalSize = len * (stuff->format/8);
1179    REQUEST_FIXED_SIZE(xXIChangePropertyReq, totalSize);
1180
1181    rc = change_property(client, dev, stuff->property, stuff->type,
1182                         stuff->format, stuff->mode, len, (void*)&stuff[1]);
1183    return rc;
1184}
1185
1186int
1187ProcXIDeleteProperty(ClientPtr client)
1188{
1189    DeviceIntPtr        dev;
1190    int                 rc;
1191    REQUEST(xXIDeletePropertyReq);
1192
1193    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1194    UpdateCurrentTime();
1195    rc =  dixLookupDevice (&dev, stuff->deviceid, client, DixSetPropAccess);
1196    if (rc != Success)
1197        return rc;
1198
1199    if (!ValidAtom(stuff->property))
1200    {
1201        client->errorValue = stuff->property;
1202        return BadAtom;
1203    }
1204
1205    rc = XIDeleteDeviceProperty(dev, stuff->property, TRUE);
1206    return rc;
1207}
1208
1209
1210int
1211ProcXIGetProperty(ClientPtr client)
1212{
1213    REQUEST(xXIGetPropertyReq);
1214    DeviceIntPtr                dev;
1215    xXIGetPropertyReply         reply;
1216    int                         length;
1217    int                         rc, format, nitems, bytes_after;
1218    char                        *data;
1219    Atom                        type;
1220
1221    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1222    if (stuff->delete)
1223        UpdateCurrentTime();
1224    rc = dixLookupDevice (&dev, stuff->deviceid, client,
1225                           stuff->delete ? DixSetPropAccess :
1226                           DixGetPropAccess);
1227    if (rc != Success)
1228        return rc;
1229
1230    rc = get_property(client, dev, stuff->property, stuff->type,
1231            stuff->delete, stuff->offset, stuff->len,
1232            &bytes_after, &type, &format, &nitems, &length, &data);
1233
1234    if (rc != Success)
1235        return rc;
1236
1237    reply.repType = X_Reply;
1238    reply.RepType = X_XIGetProperty;
1239    reply.sequenceNumber = client->sequence;
1240    reply.num_items = nitems;
1241    reply.format = format;
1242    reply.bytes_after = bytes_after;
1243    reply.type = type;
1244    reply.length = bytes_to_int32(length);
1245
1246    if (length && stuff->delete && (reply.bytes_after == 0))
1247        send_property_event(dev, stuff->property, XIPropertyDeleted);
1248
1249    WriteReplyToClient(client, sizeof(xXIGetPropertyReply), &reply);
1250
1251    if (length)
1252    {
1253        switch (reply.format) {
1254            case 32: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap32Write; break;
1255            case 16: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap16Write; break;
1256            default: client->pSwapReplyFunc = (ReplySwapPtr)WriteToClient; break;
1257        }
1258        WriteSwappedDataToClient(client, length, data);
1259    }
1260
1261    /* delete the Property */
1262    if (stuff->delete && (reply.bytes_after == 0))
1263    {
1264        XIPropertyPtr prop, *prev;
1265        for (prev = &dev->properties.properties; (prop = *prev); prev = &prop->next)
1266        {
1267            if (prop->propertyName == stuff->property)
1268            {
1269                *prev = prop->next;
1270                XIDestroyDeviceProperty(prop);
1271                break;
1272            }
1273        }
1274    }
1275
1276    return Success;
1277}
1278
1279int
1280SProcXIListProperties(ClientPtr client)
1281{
1282    char n;
1283    REQUEST(xXIListPropertiesReq);
1284
1285    swaps(&stuff->length, n);
1286    swaps(&stuff->deviceid, n);
1287
1288    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1289    return (ProcXIListProperties(client));
1290}
1291
1292int
1293SProcXIChangeProperty(ClientPtr client)
1294{
1295    char n;
1296    REQUEST(xXIChangePropertyReq);
1297
1298    swaps(&stuff->length, n);
1299    swaps(&stuff->deviceid, n);
1300    swapl(&stuff->property, n);
1301    swapl(&stuff->type, n);
1302    swapl(&stuff->num_items, n);
1303    REQUEST_SIZE_MATCH(xXIChangePropertyReq);
1304    return (ProcXIChangeProperty(client));
1305}
1306
1307int
1308SProcXIDeleteProperty(ClientPtr client)
1309{
1310    char n;
1311    REQUEST(xXIDeletePropertyReq);
1312
1313    swaps(&stuff->length, n);
1314    swaps(&stuff->deviceid, n);
1315    swapl(&stuff->property, n);
1316    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1317    return (ProcXIDeleteProperty(client));
1318}
1319
1320int
1321SProcXIGetProperty(ClientPtr client)
1322{
1323    char n;
1324    REQUEST(xXIGetPropertyReq);
1325
1326    swaps(&stuff->length, n);
1327    swaps(&stuff->deviceid, n);
1328    swapl(&stuff->property, n);
1329    swapl(&stuff->type, n);
1330    swapl(&stuff->offset, n);
1331    swapl(&stuff->len, n);
1332    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1333    return (ProcXIGetProperty(client));
1334}
1335
1336
1337void
1338SRepXIListProperties(ClientPtr client, int size,
1339                     xXIListPropertiesReply *rep)
1340{
1341    char n;
1342    swaps(&rep->sequenceNumber, n);
1343    swapl(&rep->length, n);
1344    swaps(&rep->num_properties, n);
1345    /* properties will be swapped later, see ProcXIListProperties */
1346    WriteToClient(client, size, (char*)rep);
1347}
1348
1349void
1350SRepXIGetProperty(ClientPtr client, int size,
1351                  xXIGetPropertyReply *rep)
1352{
1353    char n;
1354
1355    swaps(&rep->sequenceNumber, n);
1356    swapl(&rep->length, n);
1357    swapl(&rep->type, n);
1358    swapl(&rep->bytes_after, n);
1359    swapl(&rep->num_items, n);
1360    /* data will be swapped, see ProcXIGetProperty */
1361    WriteToClient(client, size, (char*)rep);
1362}
1363