xiproperty.c revision 0b0d8713
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                        const 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    REQUEST_SIZE_MATCH(xListDevicePropertiesReq);
1042
1043    swaps(&stuff->length, n);
1044    return (ProcXListDeviceProperties(client));
1045}
1046
1047int
1048SProcXChangeDeviceProperty (ClientPtr client)
1049{
1050    char n;
1051    REQUEST(xChangeDevicePropertyReq);
1052
1053    REQUEST_AT_LEAST_SIZE(xChangeDevicePropertyReq);
1054    swaps(&stuff->length, n);
1055    swapl(&stuff->property, n);
1056    swapl(&stuff->type, n);
1057    swapl(&stuff->nUnits, n);
1058    return (ProcXChangeDeviceProperty(client));
1059}
1060
1061int
1062SProcXDeleteDeviceProperty (ClientPtr client)
1063{
1064    char n;
1065    REQUEST(xDeleteDevicePropertyReq);
1066    REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq);
1067
1068    swaps(&stuff->length, n);
1069    swapl(&stuff->property, n);
1070    return (ProcXDeleteDeviceProperty(client));
1071}
1072
1073int
1074SProcXGetDeviceProperty (ClientPtr client)
1075{
1076    char n;
1077    REQUEST(xGetDevicePropertyReq);
1078    REQUEST_SIZE_MATCH(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    return (ProcXGetDeviceProperty(client));
1086}
1087
1088
1089/* Reply swapping */
1090
1091void
1092SRepXListDeviceProperties(ClientPtr client, int size,
1093                          xListDevicePropertiesReply *rep)
1094{
1095    char n;
1096    swaps(&rep->sequenceNumber, n);
1097    swapl(&rep->length, n);
1098    swaps(&rep->nAtoms, n);
1099    /* properties will be swapped later, see ProcXListDeviceProperties */
1100    WriteToClient(client, size, (char*)rep);
1101}
1102
1103void
1104SRepXGetDeviceProperty(ClientPtr client, int size,
1105                       xGetDevicePropertyReply *rep)
1106{
1107    char n;
1108
1109    swaps(&rep->sequenceNumber, n);
1110    swapl(&rep->length, n);
1111    swapl(&rep->propertyType, n);
1112    swapl(&rep->bytesAfter, n);
1113    swapl(&rep->nItems, n);
1114    /* data will be swapped, see ProcXGetDeviceProperty */
1115    WriteToClient(client, size, (char*)rep);
1116}
1117
1118/* XI2 Request/reply handling */
1119int
1120ProcXIListProperties(ClientPtr client)
1121{
1122    Atom                        *atoms;
1123    xXIListPropertiesReply      rep;
1124    int                         natoms;
1125    DeviceIntPtr                dev;
1126    int                         rc = Success;
1127
1128    REQUEST(xXIListPropertiesReq);
1129    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1130
1131    rc = dixLookupDevice (&dev, stuff->deviceid, client, DixListPropAccess);
1132    if (rc != Success)
1133        return rc;
1134
1135    rc = list_atoms(dev, &natoms, &atoms);
1136    if (rc != Success)
1137        return rc;
1138
1139    rep.repType = X_Reply;
1140    rep.RepType = X_XIListProperties;
1141    rep.length = natoms;
1142    rep.sequenceNumber = client->sequence;
1143    rep.num_properties = natoms;
1144
1145    WriteReplyToClient(client, sizeof(xXIListPropertiesReply), &rep);
1146    if (natoms)
1147    {
1148        client->pSwapReplyFunc = (ReplySwapPtr)Swap32Write;
1149        WriteSwappedDataToClient(client, natoms * sizeof(Atom), atoms);
1150        free(atoms);
1151    }
1152    return rc;
1153}
1154
1155int
1156ProcXIChangeProperty(ClientPtr client)
1157{
1158    int                 rc;
1159    DeviceIntPtr        dev;
1160    int                 totalSize;
1161    unsigned long       len;
1162
1163    REQUEST(xXIChangePropertyReq);
1164    REQUEST_AT_LEAST_SIZE(xXIChangePropertyReq);
1165    UpdateCurrentTime();
1166
1167    rc = dixLookupDevice (&dev, stuff->deviceid, client, DixSetPropAccess);
1168    if (rc != Success)
1169        return rc;
1170
1171    rc = check_change_property(client, stuff->property, stuff->type,
1172                               stuff->format, stuff->mode, stuff->num_items);
1173    len = stuff->num_items;
1174    if (len > bytes_to_int32(0xffffffff - sizeof(xXIChangePropertyReq)))
1175        return BadLength;
1176
1177    totalSize = len * (stuff->format/8);
1178    REQUEST_FIXED_SIZE(xXIChangePropertyReq, totalSize);
1179
1180    rc = change_property(client, dev, stuff->property, stuff->type,
1181                         stuff->format, stuff->mode, len, (void*)&stuff[1]);
1182    return rc;
1183}
1184
1185int
1186ProcXIDeleteProperty(ClientPtr client)
1187{
1188    DeviceIntPtr        dev;
1189    int                 rc;
1190    REQUEST(xXIDeletePropertyReq);
1191
1192    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1193    UpdateCurrentTime();
1194    rc =  dixLookupDevice (&dev, stuff->deviceid, client, DixSetPropAccess);
1195    if (rc != Success)
1196        return rc;
1197
1198    if (!ValidAtom(stuff->property))
1199    {
1200        client->errorValue = stuff->property;
1201        return BadAtom;
1202    }
1203
1204    rc = XIDeleteDeviceProperty(dev, stuff->property, TRUE);
1205    return rc;
1206}
1207
1208
1209int
1210ProcXIGetProperty(ClientPtr client)
1211{
1212    REQUEST(xXIGetPropertyReq);
1213    DeviceIntPtr                dev;
1214    xXIGetPropertyReply         reply;
1215    int                         length;
1216    int                         rc, format, nitems, bytes_after;
1217    char                        *data;
1218    Atom                        type;
1219
1220    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1221    if (stuff->delete)
1222        UpdateCurrentTime();
1223    rc = dixLookupDevice (&dev, stuff->deviceid, client,
1224                           stuff->delete ? DixSetPropAccess :
1225                           DixGetPropAccess);
1226    if (rc != Success)
1227        return rc;
1228
1229    rc = get_property(client, dev, stuff->property, stuff->type,
1230            stuff->delete, stuff->offset, stuff->len,
1231            &bytes_after, &type, &format, &nitems, &length, &data);
1232
1233    if (rc != Success)
1234        return rc;
1235
1236    reply.repType = X_Reply;
1237    reply.RepType = X_XIGetProperty;
1238    reply.sequenceNumber = client->sequence;
1239    reply.num_items = nitems;
1240    reply.format = format;
1241    reply.bytes_after = bytes_after;
1242    reply.type = type;
1243    reply.length = bytes_to_int32(length);
1244
1245    if (length && stuff->delete && (reply.bytes_after == 0))
1246        send_property_event(dev, stuff->property, XIPropertyDeleted);
1247
1248    WriteReplyToClient(client, sizeof(xXIGetPropertyReply), &reply);
1249
1250    if (length)
1251    {
1252        switch (reply.format) {
1253            case 32: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap32Write; break;
1254            case 16: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap16Write; break;
1255            default: client->pSwapReplyFunc = (ReplySwapPtr)WriteToClient; break;
1256        }
1257        WriteSwappedDataToClient(client, length, data);
1258    }
1259
1260    /* delete the Property */
1261    if (stuff->delete && (reply.bytes_after == 0))
1262    {
1263        XIPropertyPtr prop, *prev;
1264        for (prev = &dev->properties.properties; (prop = *prev); prev = &prop->next)
1265        {
1266            if (prop->propertyName == stuff->property)
1267            {
1268                *prev = prop->next;
1269                XIDestroyDeviceProperty(prop);
1270                break;
1271            }
1272        }
1273    }
1274
1275    return Success;
1276}
1277
1278int
1279SProcXIListProperties(ClientPtr client)
1280{
1281    char n;
1282    REQUEST(xXIListPropertiesReq);
1283    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1284
1285    swaps(&stuff->length, n);
1286    swaps(&stuff->deviceid, n);
1287    return (ProcXIListProperties(client));
1288}
1289
1290int
1291SProcXIChangeProperty(ClientPtr client)
1292{
1293    char n;
1294    REQUEST(xXIChangePropertyReq);
1295
1296    REQUEST_AT_LEAST_SIZE(xXIChangePropertyReq);
1297    swaps(&stuff->length, n);
1298    swaps(&stuff->deviceid, n);
1299    swapl(&stuff->property, n);
1300    swapl(&stuff->type, n);
1301    swapl(&stuff->num_items, n);
1302    return (ProcXIChangeProperty(client));
1303}
1304
1305int
1306SProcXIDeleteProperty(ClientPtr client)
1307{
1308    char n;
1309    REQUEST(xXIDeletePropertyReq);
1310    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1311
1312    swaps(&stuff->length, n);
1313    swaps(&stuff->deviceid, n);
1314    swapl(&stuff->property, n);
1315    return (ProcXIDeleteProperty(client));
1316}
1317
1318int
1319SProcXIGetProperty(ClientPtr client)
1320{
1321    char n;
1322    REQUEST(xXIGetPropertyReq);
1323    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1324
1325    swaps(&stuff->length, n);
1326    swaps(&stuff->deviceid, n);
1327    swapl(&stuff->property, n);
1328    swapl(&stuff->type, n);
1329    swapl(&stuff->offset, n);
1330    swapl(&stuff->len, n);
1331    return (ProcXIGetProperty(client));
1332}
1333
1334
1335void
1336SRepXIListProperties(ClientPtr client, int size,
1337                     xXIListPropertiesReply *rep)
1338{
1339    char n;
1340    swaps(&rep->sequenceNumber, n);
1341    swapl(&rep->length, n);
1342    swaps(&rep->num_properties, n);
1343    /* properties will be swapped later, see ProcXIListProperties */
1344    WriteToClient(client, size, (char*)rep);
1345}
1346
1347void
1348SRepXIGetProperty(ClientPtr client, int size,
1349                  xXIGetPropertyReply *rep)
1350{
1351    char n;
1352
1353    swaps(&rep->sequenceNumber, n);
1354    swapl(&rep->length, n);
1355    swapl(&rep->type, n);
1356    swapl(&rep->bytes_after, n);
1357    swapl(&rep->num_items, n);
1358    /* data will be swapped, see ProcXIGetProperty */
1359    WriteToClient(client, size, (char*)rep);
1360}
1361