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