xiproperty.c revision 5a112b11
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 < ARRAY_SIZE(dev_properties); i++) {
376        if (strcmp(name, dev_properties[i].name) == 0) {
377            if (dev_properties[i].type == None) {
378                dev_properties[i].type =
379                    MakeAtom(dev_properties[i].name,
380                             strlen(dev_properties[i].name), TRUE);
381            }
382
383            return dev_properties[i].type;
384        }
385    }
386
387    return 0;
388}
389
390void
391XIResetProperties(void)
392{
393    int i;
394
395    for (i = 0; i < ARRAY_SIZE(dev_properties); i++)
396        dev_properties[i].type = None;
397}
398
399/**
400 * Convert the given property's value(s) into @nelem_return integer values and
401 * store them in @buf_return. If @nelem_return is larger than the number of
402 * values in the property, @nelem_return is set to the number of values in the
403 * property.
404 *
405 * If *@buf_return is NULL and @nelem_return is 0, memory is allocated
406 * automatically and must be freed by the caller.
407 *
408 * Possible return codes.
409 * Success ... No error.
410 * BadMatch ... Wrong atom type, atom is not XA_INTEGER
411 * BadAlloc ... NULL passed as buffer and allocation failed.
412 * BadLength ... @buff is NULL but @nelem_return is non-zero.
413 *
414 * @param val The property value
415 * @param nelem_return The maximum number of elements to return.
416 * @param buf_return Pointer to an array of at least @nelem_return values.
417 * @return Success or the error code if an error occurred.
418 */
419_X_EXPORT int
420XIPropToInt(XIPropertyValuePtr val, int *nelem_return, int **buf_return)
421{
422    int i;
423    int *buf;
424
425    if (val->type != XA_INTEGER)
426        return BadMatch;
427    if (!*buf_return && *nelem_return)
428        return BadLength;
429
430    switch (val->format) {
431    case 8:
432    case 16:
433    case 32:
434        break;
435    default:
436        return BadValue;
437    }
438
439    buf = *buf_return;
440
441    if (!buf && !(*nelem_return)) {
442        buf = calloc(val->size, sizeof(int));
443        if (!buf)
444            return BadAlloc;
445        *buf_return = buf;
446        *nelem_return = val->size;
447    }
448    else if (val->size < *nelem_return)
449        *nelem_return = val->size;
450
451    for (i = 0; i < val->size && i < *nelem_return; i++) {
452        switch (val->format) {
453        case 8:
454            buf[i] = ((CARD8 *) val->data)[i];
455            break;
456        case 16:
457            buf[i] = ((CARD16 *) val->data)[i];
458            break;
459        case 32:
460            buf[i] = ((CARD32 *) val->data)[i];
461            break;
462        }
463    }
464
465    return Success;
466}
467
468/**
469 * Convert the given property's value(s) into @nelem_return float values and
470 * store them in @buf_return. If @nelem_return is larger than the number of
471 * values in the property, @nelem_return is set to the number of values in the
472 * property.
473 *
474 * If *@buf_return is NULL and @nelem_return is 0, memory is allocated
475 * automatically and must be freed by the caller.
476 *
477 * Possible errors returned:
478 * Success
479 * BadMatch ... Wrong atom type, atom is not XA_FLOAT
480 * BadValue ... Wrong format, format is not 32
481 * BadAlloc ... NULL passed as buffer and allocation failed.
482 * BadLength ... @buff is NULL but @nelem_return is non-zero.
483 *
484 * @param val The property value
485 * @param nelem_return The maximum number of elements to return.
486 * @param buf_return Pointer to an array of at least @nelem_return values.
487 * @return Success or the error code if an error occurred.
488 */
489_X_EXPORT int
490XIPropToFloat(XIPropertyValuePtr val, int *nelem_return, float **buf_return)
491{
492    int i;
493    float *buf;
494
495    if (!val->type || val->type != XIGetKnownProperty(XATOM_FLOAT))
496        return BadMatch;
497
498    if (val->format != 32)
499        return BadValue;
500    if (!*buf_return && *nelem_return)
501        return BadLength;
502
503    buf = *buf_return;
504
505    if (!buf && !(*nelem_return)) {
506        buf = calloc(val->size, sizeof(float));
507        if (!buf)
508            return BadAlloc;
509        *buf_return = buf;
510        *nelem_return = val->size;
511    }
512    else if (val->size < *nelem_return)
513        *nelem_return = val->size;
514
515    for (i = 0; i < val->size && i < *nelem_return; i++)
516        buf[i] = ((float *) val->data)[i];
517
518    return Success;
519}
520
521/* Registers a new property handler on the given device and returns a unique
522 * identifier for this handler. This identifier is required to unregister the
523 * property handler again.
524 * @return The handler's identifier or 0 if an error occurred.
525 */
526long
527XIRegisterPropertyHandler(DeviceIntPtr dev,
528                          int (*SetProperty) (DeviceIntPtr dev,
529                                              Atom property,
530                                              XIPropertyValuePtr prop,
531                                              BOOL checkonly),
532                          int (*GetProperty) (DeviceIntPtr dev,
533                                              Atom property),
534                          int (*DeleteProperty) (DeviceIntPtr dev,
535                                                 Atom property))
536{
537    XIPropertyHandlerPtr new_handler;
538
539    new_handler = calloc(1, sizeof(XIPropertyHandler));
540    if (!new_handler)
541        return 0;
542
543    new_handler->id = XIPropHandlerID++;
544    new_handler->SetProperty = SetProperty;
545    new_handler->GetProperty = GetProperty;
546    new_handler->DeleteProperty = DeleteProperty;
547    new_handler->next = dev->properties.handlers;
548    dev->properties.handlers = new_handler;
549
550    return new_handler->id;
551}
552
553void
554XIUnregisterPropertyHandler(DeviceIntPtr dev, long id)
555{
556    XIPropertyHandlerPtr curr, prev = NULL;
557
558    curr = dev->properties.handlers;
559    while (curr && curr->id != id) {
560        prev = curr;
561        curr = curr->next;
562    }
563
564    if (!curr)
565        return;
566
567    if (!prev)                  /* first one */
568        dev->properties.handlers = curr->next;
569    else
570        prev->next = curr->next;
571
572    free(curr);
573}
574
575static XIPropertyPtr
576XICreateDeviceProperty(Atom property)
577{
578    XIPropertyPtr prop;
579
580    prop = (XIPropertyPtr) malloc(sizeof(XIPropertyRec));
581    if (!prop)
582        return NULL;
583
584    prop->next = NULL;
585    prop->propertyName = property;
586    prop->value.type = None;
587    prop->value.format = 0;
588    prop->value.size = 0;
589    prop->value.data = NULL;
590    prop->deletable = TRUE;
591
592    return prop;
593}
594
595static XIPropertyPtr
596XIFetchDeviceProperty(DeviceIntPtr dev, Atom property)
597{
598    XIPropertyPtr prop;
599
600    for (prop = dev->properties.properties; prop; prop = prop->next)
601        if (prop->propertyName == property)
602            return prop;
603    return NULL;
604}
605
606static void
607XIDestroyDeviceProperty(XIPropertyPtr prop)
608{
609    free(prop->value.data);
610    free(prop);
611}
612
613/* This function destroys all of the device's property-related stuff,
614 * including removing all device handlers.
615 * DO NOT CALL FROM THE DRIVER.
616 */
617void
618XIDeleteAllDeviceProperties(DeviceIntPtr device)
619{
620    XIPropertyPtr prop, next;
621    XIPropertyHandlerPtr curr_handler, next_handler;
622
623    UpdateCurrentTimeIf();
624    for (prop = device->properties.properties; prop; prop = next) {
625        next = prop->next;
626        send_property_event(device, prop->propertyName, XIPropertyDeleted);
627        XIDestroyDeviceProperty(prop);
628    }
629
630    device->properties.properties = NULL;
631
632    /* Now free all handlers */
633    curr_handler = device->properties.handlers;
634    while (curr_handler) {
635        next_handler = curr_handler->next;
636        free(curr_handler);
637        curr_handler = next_handler;
638    }
639
640    device->properties.handlers = NULL;
641}
642
643int
644XIDeleteDeviceProperty(DeviceIntPtr device, Atom property, Bool fromClient)
645{
646    XIPropertyPtr prop, *prev;
647    int rc = Success;
648
649    for (prev = &device->properties.properties; (prop = *prev);
650         prev = &(prop->next))
651        if (prop->propertyName == property)
652            break;
653
654    if (!prop)
655        return Success;
656
657    if (fromClient && !prop->deletable)
658        return BadAccess;
659
660    /* Ask handlers if we may delete the property */
661    if (device->properties.handlers) {
662        XIPropertyHandlerPtr handler = device->properties.handlers;
663
664        while (handler) {
665            if (handler->DeleteProperty)
666                rc = handler->DeleteProperty(device, prop->propertyName);
667            if (rc != Success)
668                return rc;
669            handler = handler->next;
670        }
671    }
672
673    if (prop) {
674        UpdateCurrentTimeIf();
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                        input_lock();
771                        rc = handler->SetProperty(dev, prop->propertyName,
772                                                  &new_value, checkonly);
773                        input_unlock();
774                        if (checkonly && rc != Success) {
775                            free(new_value.data);
776                            if (add)
777                                XIDestroyDeviceProperty(prop);
778                            return rc;
779                        }
780                    }
781                    handler = handler->next;
782                }
783                checkonly = !checkonly;
784            } while (!checkonly);
785        }
786        free(prop_value->data);
787        *prop_value = new_value;
788    }
789    else if (len == 0) {
790        /* do nothing */
791    }
792
793    if (add) {
794        prop->next = dev->properties.properties;
795        dev->properties.properties = prop;
796    }
797
798    if (sendevent) {
799        UpdateCurrentTimeIf();
800        send_property_event(dev, prop->propertyName,
801                            (add) ? XIPropertyCreated : XIPropertyModified);
802    }
803
804    return Success;
805}
806
807int
808XIGetDeviceProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr *value)
809{
810    XIPropertyPtr prop = XIFetchDeviceProperty(dev, property);
811    int rc;
812
813    if (!prop) {
814        *value = NULL;
815        return BadAtom;
816    }
817
818    /* If we can, try to update the property value first */
819    if (dev->properties.handlers) {
820        XIPropertyHandlerPtr handler = dev->properties.handlers;
821
822        while (handler) {
823            if (handler->GetProperty) {
824                rc = handler->GetProperty(dev, prop->propertyName);
825                if (rc != Success) {
826                    *value = NULL;
827                    return rc;
828                }
829            }
830            handler = handler->next;
831        }
832    }
833
834    *value = &prop->value;
835    return Success;
836}
837
838int
839XISetDevicePropertyDeletable(DeviceIntPtr dev, Atom property, Bool deletable)
840{
841    XIPropertyPtr prop = XIFetchDeviceProperty(dev, property);
842
843    if (!prop)
844        return BadAtom;
845
846    prop->deletable = deletable;
847    return Success;
848}
849
850int
851ProcXListDeviceProperties(ClientPtr client)
852{
853    Atom *atoms;
854    xListDevicePropertiesReply rep;
855    int natoms;
856    DeviceIntPtr dev;
857    int rc = Success;
858
859    REQUEST(xListDevicePropertiesReq);
860    REQUEST_SIZE_MATCH(xListDevicePropertiesReq);
861
862    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixListPropAccess);
863    if (rc != Success)
864        return rc;
865
866    rc = list_atoms(dev, &natoms, &atoms);
867    if (rc != Success)
868        return rc;
869
870    rep = (xListDevicePropertiesReply) {
871        .repType = X_Reply,
872        .RepType = X_ListDeviceProperties,
873        .sequenceNumber = client->sequence,
874        .length = natoms,
875        .nAtoms = natoms
876    };
877
878    WriteReplyToClient(client, sizeof(xListDevicePropertiesReply), &rep);
879    if (natoms) {
880        client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
881        WriteSwappedDataToClient(client, natoms * sizeof(Atom), atoms);
882        free(atoms);
883    }
884    return rc;
885}
886
887int
888ProcXChangeDeviceProperty(ClientPtr client)
889{
890    REQUEST(xChangeDevicePropertyReq);
891    DeviceIntPtr dev;
892    unsigned long len;
893    int totalSize;
894    int rc;
895
896    REQUEST_AT_LEAST_SIZE(xChangeDevicePropertyReq);
897    UpdateCurrentTime();
898
899    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixSetPropAccess);
900    if (rc != Success)
901        return rc;
902
903    rc = check_change_property(client, stuff->property, stuff->type,
904                               stuff->format, stuff->mode, stuff->nUnits);
905
906    len = stuff->nUnits;
907    if (len > (bytes_to_int32(0xffffffff - sizeof(xChangeDevicePropertyReq))))
908        return BadLength;
909
910    totalSize = len * (stuff->format / 8);
911    REQUEST_FIXED_SIZE(xChangeDevicePropertyReq, totalSize);
912
913    rc = change_property(client, dev, stuff->property, stuff->type,
914                         stuff->format, stuff->mode, len, (void *) &stuff[1]);
915    return rc;
916}
917
918int
919ProcXDeleteDeviceProperty(ClientPtr client)
920{
921    REQUEST(xDeleteDevicePropertyReq);
922    DeviceIntPtr dev;
923    int rc;
924
925    REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq);
926    UpdateCurrentTime();
927    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixSetPropAccess);
928    if (rc != Success)
929        return rc;
930
931    if (!ValidAtom(stuff->property)) {
932        client->errorValue = stuff->property;
933        return BadAtom;
934    }
935
936    rc = XIDeleteDeviceProperty(dev, stuff->property, TRUE);
937    return rc;
938}
939
940int
941ProcXGetDeviceProperty(ClientPtr client)
942{
943    REQUEST(xGetDevicePropertyReq);
944    DeviceIntPtr dev;
945    int length;
946    int rc, format, nitems, bytes_after;
947    char *data;
948    Atom type;
949    xGetDevicePropertyReply reply;
950
951    REQUEST_SIZE_MATCH(xGetDevicePropertyReq);
952    if (stuff->delete)
953        UpdateCurrentTime();
954    rc = dixLookupDevice(&dev, stuff->deviceid, client,
955                         stuff->delete ? DixSetPropAccess : DixGetPropAccess);
956    if (rc != Success)
957        return rc;
958
959    rc = get_property(client, dev, stuff->property, stuff->type,
960                      stuff->delete, stuff->longOffset, stuff->longLength,
961                      &bytes_after, &type, &format, &nitems, &length, &data);
962
963    if (rc != Success)
964        return rc;
965
966    reply = (xGetDevicePropertyReply) {
967        .repType = X_Reply,
968        .RepType = X_GetDeviceProperty,
969        .sequenceNumber = client->sequence,
970        .length = bytes_to_int32(length),
971        .propertyType = type,
972        .bytesAfter = bytes_after,
973        .nItems = nitems,
974        .format = format,
975        .deviceid = dev->id
976    };
977
978    if (stuff->delete && (reply.bytesAfter == 0))
979        send_property_event(dev, stuff->property, XIPropertyDeleted);
980
981    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
982
983    if (length) {
984        switch (reply.format) {
985        case 32:
986            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
987            break;
988        case 16:
989            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
990            break;
991        default:
992            client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
993            break;
994        }
995        WriteSwappedDataToClient(client, length, data);
996    }
997
998    /* delete the Property */
999    if (stuff->delete && (reply.bytesAfter == 0)) {
1000        XIPropertyPtr prop, *prev;
1001
1002        for (prev = &dev->properties.properties; (prop = *prev);
1003             prev = &prop->next) {
1004            if (prop->propertyName == stuff->property) {
1005                *prev = prop->next;
1006                XIDestroyDeviceProperty(prop);
1007                break;
1008            }
1009        }
1010    }
1011    return Success;
1012}
1013
1014int _X_COLD
1015SProcXListDeviceProperties(ClientPtr client)
1016{
1017    REQUEST(xListDevicePropertiesReq);
1018    REQUEST_SIZE_MATCH(xListDevicePropertiesReq);
1019
1020    swaps(&stuff->length);
1021    return (ProcXListDeviceProperties(client));
1022}
1023
1024int _X_COLD
1025SProcXChangeDeviceProperty(ClientPtr client)
1026{
1027    REQUEST(xChangeDevicePropertyReq);
1028
1029    REQUEST_AT_LEAST_SIZE(xChangeDevicePropertyReq);
1030    swaps(&stuff->length);
1031    swapl(&stuff->property);
1032    swapl(&stuff->type);
1033    swapl(&stuff->nUnits);
1034    return (ProcXChangeDeviceProperty(client));
1035}
1036
1037int _X_COLD
1038SProcXDeleteDeviceProperty(ClientPtr client)
1039{
1040    REQUEST(xDeleteDevicePropertyReq);
1041    REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq);
1042
1043    swaps(&stuff->length);
1044    swapl(&stuff->property);
1045    return (ProcXDeleteDeviceProperty(client));
1046}
1047
1048int _X_COLD
1049SProcXGetDeviceProperty(ClientPtr client)
1050{
1051    REQUEST(xGetDevicePropertyReq);
1052    REQUEST_SIZE_MATCH(xGetDevicePropertyReq);
1053
1054    swaps(&stuff->length);
1055    swapl(&stuff->property);
1056    swapl(&stuff->type);
1057    swapl(&stuff->longOffset);
1058    swapl(&stuff->longLength);
1059    return (ProcXGetDeviceProperty(client));
1060}
1061
1062/* Reply swapping */
1063
1064void _X_COLD
1065SRepXListDeviceProperties(ClientPtr client, int size,
1066                          xListDevicePropertiesReply * rep)
1067{
1068    swaps(&rep->sequenceNumber);
1069    swapl(&rep->length);
1070    swaps(&rep->nAtoms);
1071    /* properties will be swapped later, see ProcXListDeviceProperties */
1072    WriteToClient(client, size, rep);
1073}
1074
1075void _X_COLD
1076SRepXGetDeviceProperty(ClientPtr client, int size,
1077                       xGetDevicePropertyReply * rep)
1078{
1079    swaps(&rep->sequenceNumber);
1080    swapl(&rep->length);
1081    swapl(&rep->propertyType);
1082    swapl(&rep->bytesAfter);
1083    swapl(&rep->nItems);
1084    /* data will be swapped, see ProcXGetDeviceProperty */
1085    WriteToClient(client, size, rep);
1086}
1087
1088/* XI2 Request/reply handling */
1089int
1090ProcXIListProperties(ClientPtr client)
1091{
1092    Atom *atoms;
1093    xXIListPropertiesReply rep;
1094    int natoms;
1095    DeviceIntPtr dev;
1096    int rc = Success;
1097
1098    REQUEST(xXIListPropertiesReq);
1099    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1100
1101    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixListPropAccess);
1102    if (rc != Success)
1103        return rc;
1104
1105    rc = list_atoms(dev, &natoms, &atoms);
1106    if (rc != Success)
1107        return rc;
1108
1109    rep = (xXIListPropertiesReply) {
1110        .repType = X_Reply,
1111        .RepType = X_XIListProperties,
1112        .sequenceNumber = client->sequence,
1113        .length = natoms,
1114        .num_properties = natoms
1115    };
1116
1117    WriteReplyToClient(client, sizeof(xXIListPropertiesReply), &rep);
1118    if (natoms) {
1119        client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
1120        WriteSwappedDataToClient(client, natoms * sizeof(Atom), atoms);
1121        free(atoms);
1122    }
1123    return rc;
1124}
1125
1126int
1127ProcXIChangeProperty(ClientPtr client)
1128{
1129    int rc;
1130    DeviceIntPtr dev;
1131    int totalSize;
1132    unsigned long len;
1133
1134    REQUEST(xXIChangePropertyReq);
1135    REQUEST_AT_LEAST_SIZE(xXIChangePropertyReq);
1136    UpdateCurrentTime();
1137
1138    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixSetPropAccess);
1139    if (rc != Success)
1140        return rc;
1141
1142    rc = check_change_property(client, stuff->property, stuff->type,
1143                               stuff->format, stuff->mode, stuff->num_items);
1144    len = stuff->num_items;
1145    if (len > bytes_to_int32(0xffffffff - sizeof(xXIChangePropertyReq)))
1146        return BadLength;
1147
1148    totalSize = len * (stuff->format / 8);
1149    REQUEST_FIXED_SIZE(xXIChangePropertyReq, totalSize);
1150
1151    rc = change_property(client, dev, stuff->property, stuff->type,
1152                         stuff->format, stuff->mode, len, (void *) &stuff[1]);
1153    return rc;
1154}
1155
1156int
1157ProcXIDeleteProperty(ClientPtr client)
1158{
1159    DeviceIntPtr dev;
1160    int rc;
1161
1162    REQUEST(xXIDeletePropertyReq);
1163
1164    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1165    UpdateCurrentTime();
1166    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixSetPropAccess);
1167    if (rc != Success)
1168        return rc;
1169
1170    if (!ValidAtom(stuff->property)) {
1171        client->errorValue = stuff->property;
1172        return BadAtom;
1173    }
1174
1175    rc = XIDeleteDeviceProperty(dev, stuff->property, TRUE);
1176    return rc;
1177}
1178
1179int
1180ProcXIGetProperty(ClientPtr client)
1181{
1182    REQUEST(xXIGetPropertyReq);
1183    DeviceIntPtr dev;
1184    xXIGetPropertyReply reply;
1185    int length;
1186    int rc, format, nitems, bytes_after;
1187    char *data;
1188    Atom type;
1189
1190    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1191    if (stuff->delete)
1192        UpdateCurrentTime();
1193    rc = dixLookupDevice(&dev, stuff->deviceid, client,
1194                         stuff->delete ? DixSetPropAccess : DixGetPropAccess);
1195    if (rc != Success)
1196        return rc;
1197
1198    rc = get_property(client, dev, stuff->property, stuff->type,
1199                      stuff->delete, stuff->offset, stuff->len,
1200                      &bytes_after, &type, &format, &nitems, &length, &data);
1201
1202    if (rc != Success)
1203        return rc;
1204
1205    reply = (xXIGetPropertyReply) {
1206        .repType = X_Reply,
1207        .RepType = X_XIGetProperty,
1208        .sequenceNumber = client->sequence,
1209        .length = bytes_to_int32(length),
1210        .type = type,
1211        .bytes_after = bytes_after,
1212        .num_items = nitems,
1213        .format = format
1214    };
1215
1216    if (length && stuff->delete && (reply.bytes_after == 0))
1217        send_property_event(dev, stuff->property, XIPropertyDeleted);
1218
1219    WriteReplyToClient(client, sizeof(xXIGetPropertyReply), &reply);
1220
1221    if (length) {
1222        switch (reply.format) {
1223        case 32:
1224            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
1225            break;
1226        case 16:
1227            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
1228            break;
1229        default:
1230            client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
1231            break;
1232        }
1233        WriteSwappedDataToClient(client, length, data);
1234    }
1235
1236    /* delete the Property */
1237    if (stuff->delete && (reply.bytes_after == 0)) {
1238        XIPropertyPtr prop, *prev;
1239
1240        for (prev = &dev->properties.properties; (prop = *prev);
1241             prev = &prop->next) {
1242            if (prop->propertyName == stuff->property) {
1243                *prev = prop->next;
1244                XIDestroyDeviceProperty(prop);
1245                break;
1246            }
1247        }
1248    }
1249
1250    return Success;
1251}
1252
1253int _X_COLD
1254SProcXIListProperties(ClientPtr client)
1255{
1256    REQUEST(xXIListPropertiesReq);
1257    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1258
1259    swaps(&stuff->length);
1260    swaps(&stuff->deviceid);
1261    return (ProcXIListProperties(client));
1262}
1263
1264int _X_COLD
1265SProcXIChangeProperty(ClientPtr client)
1266{
1267    REQUEST(xXIChangePropertyReq);
1268
1269    REQUEST_AT_LEAST_SIZE(xXIChangePropertyReq);
1270    swaps(&stuff->length);
1271    swaps(&stuff->deviceid);
1272    swapl(&stuff->property);
1273    swapl(&stuff->type);
1274    swapl(&stuff->num_items);
1275    return (ProcXIChangeProperty(client));
1276}
1277
1278int _X_COLD
1279SProcXIDeleteProperty(ClientPtr client)
1280{
1281    REQUEST(xXIDeletePropertyReq);
1282    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1283
1284    swaps(&stuff->length);
1285    swaps(&stuff->deviceid);
1286    swapl(&stuff->property);
1287    return (ProcXIDeleteProperty(client));
1288}
1289
1290int _X_COLD
1291SProcXIGetProperty(ClientPtr client)
1292{
1293    REQUEST(xXIGetPropertyReq);
1294    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1295
1296    swaps(&stuff->length);
1297    swaps(&stuff->deviceid);
1298    swapl(&stuff->property);
1299    swapl(&stuff->type);
1300    swapl(&stuff->offset);
1301    swapl(&stuff->len);
1302    return (ProcXIGetProperty(client));
1303}
1304
1305void _X_COLD
1306SRepXIListProperties(ClientPtr client, int size, xXIListPropertiesReply * rep)
1307{
1308    swaps(&rep->sequenceNumber);
1309    swapl(&rep->length);
1310    swaps(&rep->num_properties);
1311    /* properties will be swapped later, see ProcXIListProperties */
1312    WriteToClient(client, size, rep);
1313}
1314
1315void _X_COLD
1316SRepXIGetProperty(ClientPtr client, int size, xXIGetPropertyReply * rep)
1317{
1318    swaps(&rep->sequenceNumber);
1319    swapl(&rep->length);
1320    swapl(&rep->type);
1321    swapl(&rep->bytes_after);
1322    swapl(&rep->num_items);
1323    /* data will be swapped, see ProcXIGetProperty */
1324    WriteToClient(client, size, rep);
1325}
1326