xiproperty.c revision e383896c
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    uint64_t 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    if (rc != Success)
906        return rc;
907
908    len = stuff->nUnits;
909    if (len > (bytes_to_int32(0xffffffff - sizeof(xChangeDevicePropertyReq))))
910        return BadLength;
911
912    totalSize = len * (stuff->format / 8);
913    REQUEST_FIXED_SIZE(xChangeDevicePropertyReq, totalSize);
914
915    rc = change_property(client, dev, stuff->property, stuff->type,
916                         stuff->format, stuff->mode, len, (void *) &stuff[1]);
917    return rc;
918}
919
920int
921ProcXDeleteDeviceProperty(ClientPtr client)
922{
923    REQUEST(xDeleteDevicePropertyReq);
924    DeviceIntPtr dev;
925    int rc;
926
927    REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq);
928    UpdateCurrentTime();
929    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixSetPropAccess);
930    if (rc != Success)
931        return rc;
932
933    if (!ValidAtom(stuff->property)) {
934        client->errorValue = stuff->property;
935        return BadAtom;
936    }
937
938    rc = XIDeleteDeviceProperty(dev, stuff->property, TRUE);
939    return rc;
940}
941
942int
943ProcXGetDeviceProperty(ClientPtr client)
944{
945    REQUEST(xGetDevicePropertyReq);
946    DeviceIntPtr dev;
947    int length;
948    int rc, format, nitems, bytes_after;
949    char *data;
950    Atom type;
951    xGetDevicePropertyReply reply;
952
953    REQUEST_SIZE_MATCH(xGetDevicePropertyReq);
954    if (stuff->delete)
955        UpdateCurrentTime();
956    rc = dixLookupDevice(&dev, stuff->deviceid, client,
957                         stuff->delete ? DixSetPropAccess : DixGetPropAccess);
958    if (rc != Success)
959        return rc;
960
961    rc = get_property(client, dev, stuff->property, stuff->type,
962                      stuff->delete, stuff->longOffset, stuff->longLength,
963                      &bytes_after, &type, &format, &nitems, &length, &data);
964
965    if (rc != Success)
966        return rc;
967
968    reply = (xGetDevicePropertyReply) {
969        .repType = X_Reply,
970        .RepType = X_GetDeviceProperty,
971        .sequenceNumber = client->sequence,
972        .length = bytes_to_int32(length),
973        .propertyType = type,
974        .bytesAfter = bytes_after,
975        .nItems = nitems,
976        .format = format,
977        .deviceid = dev->id
978    };
979
980    if (stuff->delete && (reply.bytesAfter == 0))
981        send_property_event(dev, stuff->property, XIPropertyDeleted);
982
983    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
984
985    if (length) {
986        switch (reply.format) {
987        case 32:
988            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
989            break;
990        case 16:
991            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
992            break;
993        default:
994            client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
995            break;
996        }
997        WriteSwappedDataToClient(client, length, data);
998    }
999
1000    /* delete the Property */
1001    if (stuff->delete && (reply.bytesAfter == 0)) {
1002        XIPropertyPtr prop, *prev;
1003
1004        for (prev = &dev->properties.properties; (prop = *prev);
1005             prev = &prop->next) {
1006            if (prop->propertyName == stuff->property) {
1007                *prev = prop->next;
1008                XIDestroyDeviceProperty(prop);
1009                break;
1010            }
1011        }
1012    }
1013    return Success;
1014}
1015
1016int _X_COLD
1017SProcXListDeviceProperties(ClientPtr client)
1018{
1019    REQUEST(xListDevicePropertiesReq);
1020    REQUEST_SIZE_MATCH(xListDevicePropertiesReq);
1021
1022    swaps(&stuff->length);
1023    return (ProcXListDeviceProperties(client));
1024}
1025
1026int _X_COLD
1027SProcXChangeDeviceProperty(ClientPtr client)
1028{
1029    REQUEST(xChangeDevicePropertyReq);
1030
1031    REQUEST_AT_LEAST_SIZE(xChangeDevicePropertyReq);
1032    swaps(&stuff->length);
1033    swapl(&stuff->property);
1034    swapl(&stuff->type);
1035    swapl(&stuff->nUnits);
1036    return (ProcXChangeDeviceProperty(client));
1037}
1038
1039int _X_COLD
1040SProcXDeleteDeviceProperty(ClientPtr client)
1041{
1042    REQUEST(xDeleteDevicePropertyReq);
1043    REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq);
1044
1045    swaps(&stuff->length);
1046    swapl(&stuff->property);
1047    return (ProcXDeleteDeviceProperty(client));
1048}
1049
1050int _X_COLD
1051SProcXGetDeviceProperty(ClientPtr client)
1052{
1053    REQUEST(xGetDevicePropertyReq);
1054    REQUEST_SIZE_MATCH(xGetDevicePropertyReq);
1055
1056    swaps(&stuff->length);
1057    swapl(&stuff->property);
1058    swapl(&stuff->type);
1059    swapl(&stuff->longOffset);
1060    swapl(&stuff->longLength);
1061    return (ProcXGetDeviceProperty(client));
1062}
1063
1064/* Reply swapping */
1065
1066void _X_COLD
1067SRepXListDeviceProperties(ClientPtr client, int size,
1068                          xListDevicePropertiesReply * rep)
1069{
1070    swaps(&rep->sequenceNumber);
1071    swapl(&rep->length);
1072    swaps(&rep->nAtoms);
1073    /* properties will be swapped later, see ProcXListDeviceProperties */
1074    WriteToClient(client, size, rep);
1075}
1076
1077void _X_COLD
1078SRepXGetDeviceProperty(ClientPtr client, int size,
1079                       xGetDevicePropertyReply * rep)
1080{
1081    swaps(&rep->sequenceNumber);
1082    swapl(&rep->length);
1083    swapl(&rep->propertyType);
1084    swapl(&rep->bytesAfter);
1085    swapl(&rep->nItems);
1086    /* data will be swapped, see ProcXGetDeviceProperty */
1087    WriteToClient(client, size, rep);
1088}
1089
1090/* XI2 Request/reply handling */
1091int
1092ProcXIListProperties(ClientPtr client)
1093{
1094    Atom *atoms;
1095    xXIListPropertiesReply rep;
1096    int natoms;
1097    DeviceIntPtr dev;
1098    int rc = Success;
1099
1100    REQUEST(xXIListPropertiesReq);
1101    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1102
1103    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixListPropAccess);
1104    if (rc != Success)
1105        return rc;
1106
1107    rc = list_atoms(dev, &natoms, &atoms);
1108    if (rc != Success)
1109        return rc;
1110
1111    rep = (xXIListPropertiesReply) {
1112        .repType = X_Reply,
1113        .RepType = X_XIListProperties,
1114        .sequenceNumber = client->sequence,
1115        .length = natoms,
1116        .num_properties = natoms
1117    };
1118
1119    WriteReplyToClient(client, sizeof(xXIListPropertiesReply), &rep);
1120    if (natoms) {
1121        client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
1122        WriteSwappedDataToClient(client, natoms * sizeof(Atom), atoms);
1123        free(atoms);
1124    }
1125    return rc;
1126}
1127
1128int
1129ProcXIChangeProperty(ClientPtr client)
1130{
1131    int rc;
1132    DeviceIntPtr dev;
1133    uint64_t totalSize;
1134    unsigned long len;
1135
1136    REQUEST(xXIChangePropertyReq);
1137    REQUEST_AT_LEAST_SIZE(xXIChangePropertyReq);
1138    UpdateCurrentTime();
1139
1140    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixSetPropAccess);
1141    if (rc != Success)
1142        return rc;
1143
1144    rc = check_change_property(client, stuff->property, stuff->type,
1145                               stuff->format, stuff->mode, stuff->num_items);
1146    if (rc != Success)
1147        return rc;
1148
1149    len = stuff->num_items;
1150    if (len > bytes_to_int32(0xffffffff - sizeof(xXIChangePropertyReq)))
1151        return BadLength;
1152
1153    totalSize = len * (stuff->format / 8);
1154    REQUEST_FIXED_SIZE(xXIChangePropertyReq, totalSize);
1155
1156    rc = change_property(client, dev, stuff->property, stuff->type,
1157                         stuff->format, stuff->mode, len, (void *) &stuff[1]);
1158    return rc;
1159}
1160
1161int
1162ProcXIDeleteProperty(ClientPtr client)
1163{
1164    DeviceIntPtr dev;
1165    int rc;
1166
1167    REQUEST(xXIDeletePropertyReq);
1168
1169    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1170    UpdateCurrentTime();
1171    rc = dixLookupDevice(&dev, stuff->deviceid, client, DixSetPropAccess);
1172    if (rc != Success)
1173        return rc;
1174
1175    if (!ValidAtom(stuff->property)) {
1176        client->errorValue = stuff->property;
1177        return BadAtom;
1178    }
1179
1180    rc = XIDeleteDeviceProperty(dev, stuff->property, TRUE);
1181    return rc;
1182}
1183
1184int
1185ProcXIGetProperty(ClientPtr client)
1186{
1187    REQUEST(xXIGetPropertyReq);
1188    DeviceIntPtr dev;
1189    xXIGetPropertyReply reply;
1190    int length;
1191    int rc, format, nitems, bytes_after;
1192    char *data;
1193    Atom type;
1194
1195    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1196    if (stuff->delete)
1197        UpdateCurrentTime();
1198    rc = dixLookupDevice(&dev, stuff->deviceid, client,
1199                         stuff->delete ? DixSetPropAccess : DixGetPropAccess);
1200    if (rc != Success)
1201        return rc;
1202
1203    rc = get_property(client, dev, stuff->property, stuff->type,
1204                      stuff->delete, stuff->offset, stuff->len,
1205                      &bytes_after, &type, &format, &nitems, &length, &data);
1206
1207    if (rc != Success)
1208        return rc;
1209
1210    reply = (xXIGetPropertyReply) {
1211        .repType = X_Reply,
1212        .RepType = X_XIGetProperty,
1213        .sequenceNumber = client->sequence,
1214        .length = bytes_to_int32(length),
1215        .type = type,
1216        .bytes_after = bytes_after,
1217        .num_items = nitems,
1218        .format = format
1219    };
1220
1221    if (length && stuff->delete && (reply.bytes_after == 0))
1222        send_property_event(dev, stuff->property, XIPropertyDeleted);
1223
1224    WriteReplyToClient(client, sizeof(xXIGetPropertyReply), &reply);
1225
1226    if (length) {
1227        switch (reply.format) {
1228        case 32:
1229            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
1230            break;
1231        case 16:
1232            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
1233            break;
1234        default:
1235            client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
1236            break;
1237        }
1238        WriteSwappedDataToClient(client, length, data);
1239    }
1240
1241    /* delete the Property */
1242    if (stuff->delete && (reply.bytes_after == 0)) {
1243        XIPropertyPtr prop, *prev;
1244
1245        for (prev = &dev->properties.properties; (prop = *prev);
1246             prev = &prop->next) {
1247            if (prop->propertyName == stuff->property) {
1248                *prev = prop->next;
1249                XIDestroyDeviceProperty(prop);
1250                break;
1251            }
1252        }
1253    }
1254
1255    return Success;
1256}
1257
1258int _X_COLD
1259SProcXIListProperties(ClientPtr client)
1260{
1261    REQUEST(xXIListPropertiesReq);
1262    REQUEST_SIZE_MATCH(xXIListPropertiesReq);
1263
1264    swaps(&stuff->length);
1265    swaps(&stuff->deviceid);
1266    return (ProcXIListProperties(client));
1267}
1268
1269int _X_COLD
1270SProcXIChangeProperty(ClientPtr client)
1271{
1272    REQUEST(xXIChangePropertyReq);
1273
1274    REQUEST_AT_LEAST_SIZE(xXIChangePropertyReq);
1275    swaps(&stuff->length);
1276    swaps(&stuff->deviceid);
1277    swapl(&stuff->property);
1278    swapl(&stuff->type);
1279    swapl(&stuff->num_items);
1280    return (ProcXIChangeProperty(client));
1281}
1282
1283int _X_COLD
1284SProcXIDeleteProperty(ClientPtr client)
1285{
1286    REQUEST(xXIDeletePropertyReq);
1287    REQUEST_SIZE_MATCH(xXIDeletePropertyReq);
1288
1289    swaps(&stuff->length);
1290    swaps(&stuff->deviceid);
1291    swapl(&stuff->property);
1292    return (ProcXIDeleteProperty(client));
1293}
1294
1295int _X_COLD
1296SProcXIGetProperty(ClientPtr client)
1297{
1298    REQUEST(xXIGetPropertyReq);
1299    REQUEST_SIZE_MATCH(xXIGetPropertyReq);
1300
1301    swaps(&stuff->length);
1302    swaps(&stuff->deviceid);
1303    swapl(&stuff->property);
1304    swapl(&stuff->type);
1305    swapl(&stuff->offset);
1306    swapl(&stuff->len);
1307    return (ProcXIGetProperty(client));
1308}
1309
1310void _X_COLD
1311SRepXIListProperties(ClientPtr client, int size, xXIListPropertiesReply * rep)
1312{
1313    swaps(&rep->sequenceNumber);
1314    swapl(&rep->length);
1315    swaps(&rep->num_properties);
1316    /* properties will be swapped later, see ProcXIListProperties */
1317    WriteToClient(client, size, rep);
1318}
1319
1320void _X_COLD
1321SRepXIGetProperty(ClientPtr client, int size, xXIGetPropertyReply * rep)
1322{
1323    swaps(&rep->sequenceNumber);
1324    swapl(&rep->length);
1325    swapl(&rep->type);
1326    swapl(&rep->bytes_after);
1327    swapl(&rep->num_items);
1328    /* data will be swapped, see ProcXIGetProperty */
1329    WriteToClient(client, size, rep);
1330}
1331