1/************************************************************
2
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25Copyright 1989 by Hewlett-Packard Company, Palo Alto, California.
26
27			All Rights Reserved
28
29Permission to use, copy, modify, and distribute this software and its
30documentation for any purpose and without fee is hereby granted,
31provided that the above copyright notice appear in all copies and that
32both that copyright notice and this permission notice appear in
33supporting documentation, and that the name of Hewlett-Packard not be
34used in advertising or publicity pertaining to distribution of the
35software without specific, written prior permission.
36
37HEWLETT-PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39HEWLETT-PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43SOFTWARE.
44
45********************************************************/
46
47/***********************************************************************
48 *
49 * Extension function to list the available input devices.
50 *
51 */
52
53#ifdef HAVE_DIX_CONFIG_H
54#include <dix-config.h>
55#endif
56
57#include <X11/X.h>              /* for inputstr.h    */
58#include <X11/Xproto.h>         /* Request macro     */
59#include "inputstr.h"           /* DeviceIntPtr      */
60#include <X11/extensions/XI.h>
61#include <X11/extensions/XIproto.h>
62#include "XIstubs.h"
63#include "extnsionst.h"
64#include "exevents.h"
65#include "xace.h"
66#include "xkbsrv.h"
67#include "xkbstr.h"
68
69#include "listdev.h"
70
71/***********************************************************************
72 *
73 * This procedure lists the input devices available to the server.
74 *
75 */
76
77int _X_COLD
78SProcXListInputDevices(ClientPtr client)
79{
80    REQUEST(xListInputDevicesReq);
81    swaps(&stuff->length);
82    return (ProcXListInputDevices(client));
83}
84
85/***********************************************************************
86 *
87 * This procedure calculates the size of the information to be returned
88 * for an input device.
89 *
90 */
91
92static void
93SizeDeviceInfo(DeviceIntPtr d, int *namesize, int *size)
94{
95    int chunks;
96
97    *namesize += 1;
98    if (d->name)
99        *namesize += strlen(d->name);
100    if (d->key != NULL)
101        *size += sizeof(xKeyInfo);
102    if (d->button != NULL)
103        *size += sizeof(xButtonInfo);
104    if (d->valuator != NULL) {
105        chunks = ((int) d->valuator->numAxes + 19) / VPC;
106        *size += (chunks * sizeof(xValuatorInfo) +
107                  d->valuator->numAxes * sizeof(xAxisInfo));
108    }
109}
110
111/***********************************************************************
112 *
113 * This procedure copies data to the DeviceInfo struct, swapping if necessary.
114 *
115 * We need the extra byte in the allocated buffer, because the trailing null
116 * hammers one extra byte, which is overwritten by the next name except for
117 * the last name copied.
118 *
119 */
120
121static void
122CopyDeviceName(char **namebuf, const char *name)
123{
124    char *nameptr = *namebuf;
125
126    if (name) {
127        *nameptr++ = strlen(name);
128        strcpy(nameptr, name);
129        *namebuf += (strlen(name) + 1);
130    }
131    else {
132        *nameptr++ = 0;
133        *namebuf += 1;
134    }
135}
136
137/***********************************************************************
138 *
139 * This procedure copies ButtonClass information, swapping if necessary.
140 *
141 */
142
143static void
144CopySwapButtonClass(ClientPtr client, ButtonClassPtr b, char **buf)
145{
146    xButtonInfoPtr b2;
147
148    b2 = (xButtonInfoPtr) * buf;
149    b2->class = ButtonClass;
150    b2->length = sizeof(xButtonInfo);
151    b2->num_buttons = b->numButtons;
152    if (client && client->swapped) {
153        swaps(&b2->num_buttons);
154    }
155    *buf += sizeof(xButtonInfo);
156}
157
158/***********************************************************************
159 *
160 * This procedure copies data to the DeviceInfo struct, swapping if necessary.
161 *
162 */
163
164static void
165CopySwapDevice(ClientPtr client, DeviceIntPtr d, int num_classes, char **buf)
166{
167    xDeviceInfoPtr dev;
168
169    dev = (xDeviceInfoPtr) * buf;
170
171    dev->id = d->id;
172    dev->type = d->xinput_type;
173    dev->num_classes = num_classes;
174    if (IsMaster(d) && IsKeyboardDevice(d))
175        dev->use = IsXKeyboard;
176    else if (IsMaster(d) && IsPointerDevice(d))
177        dev->use = IsXPointer;
178    else if (d->valuator && d->button)
179        dev->use = IsXExtensionPointer;
180    else if (d->key && d->kbdfeed)
181        dev->use = IsXExtensionKeyboard;
182    else
183        dev->use = IsXExtensionDevice;
184
185    if (client->swapped) {
186        swapl(&dev->type);
187    }
188    *buf += sizeof(xDeviceInfo);
189}
190
191/***********************************************************************
192 *
193 * This procedure copies KeyClass information, swapping if necessary.
194 *
195 */
196
197static void
198CopySwapKeyClass(ClientPtr client, KeyClassPtr k, char **buf)
199{
200    xKeyInfoPtr k2;
201
202    k2 = (xKeyInfoPtr) * buf;
203    k2->class = KeyClass;
204    k2->length = sizeof(xKeyInfo);
205    k2->min_keycode = k->xkbInfo->desc->min_key_code;
206    k2->max_keycode = k->xkbInfo->desc->max_key_code;
207    k2->num_keys = k2->max_keycode - k2->min_keycode + 1;
208    if (client && client->swapped) {
209        swaps(&k2->num_keys);
210    }
211    *buf += sizeof(xKeyInfo);
212}
213
214/***********************************************************************
215 *
216 * This procedure copies ValuatorClass information, swapping if necessary.
217 *
218 * Devices may have up to 255 valuators.  The length of a ValuatorClass is
219 * defined to be sizeof(ValuatorClassInfo) + num_axes * sizeof (xAxisInfo).
220 * The maximum length is therefore (8 + 255 * 12) = 3068.  However, the
221 * length field is one byte.  If a device has more than 20 valuators, we
222 * must therefore return multiple valuator classes to the client.
223 *
224 */
225
226static int
227CopySwapValuatorClass(ClientPtr client, DeviceIntPtr dev, char **buf)
228{
229    int i, j, axes, t_axes;
230    ValuatorClassPtr v = dev->valuator;
231    xValuatorInfoPtr v2;
232    AxisInfo *a;
233    xAxisInfoPtr a2;
234
235    for (i = 0, axes = v->numAxes; i < ((v->numAxes + 19) / VPC);
236         i++, axes -= VPC) {
237        t_axes = axes < VPC ? axes : VPC;
238        if (t_axes < 0)
239            t_axes = v->numAxes % VPC;
240        v2 = (xValuatorInfoPtr) * buf;
241        v2->class = ValuatorClass;
242        v2->length = sizeof(xValuatorInfo) + t_axes * sizeof(xAxisInfo);
243        v2->num_axes = t_axes;
244        v2->mode = valuator_get_mode(dev, 0);
245        v2->motion_buffer_size = v->numMotionEvents;
246        if (client && client->swapped) {
247            swapl(&v2->motion_buffer_size);
248        }
249        *buf += sizeof(xValuatorInfo);
250        a = v->axes + (VPC * i);
251        a2 = (xAxisInfoPtr) * buf;
252        for (j = 0; j < t_axes; j++) {
253            a2->min_value = a->min_value;
254            a2->max_value = a->max_value;
255            a2->resolution = a->resolution;
256            if (client && client->swapped) {
257                swapl(&a2->min_value);
258                swapl(&a2->max_value);
259                swapl(&a2->resolution);
260            }
261            a2++;
262            a++;
263            *buf += sizeof(xAxisInfo);
264        }
265    }
266    return i;
267}
268
269static void
270CopySwapClasses(ClientPtr client, DeviceIntPtr dev, CARD8 *num_classes,
271                char **classbuf)
272{
273    if (dev->key != NULL) {
274        CopySwapKeyClass(client, dev->key, classbuf);
275        (*num_classes)++;
276    }
277    if (dev->button != NULL) {
278        CopySwapButtonClass(client, dev->button, classbuf);
279        (*num_classes)++;
280    }
281    if (dev->valuator != NULL) {
282        (*num_classes) += CopySwapValuatorClass(client, dev, classbuf);
283    }
284}
285
286/***********************************************************************
287 *
288 * This procedure lists information to be returned for an input device.
289 *
290 */
291
292static void
293ListDeviceInfo(ClientPtr client, DeviceIntPtr d, xDeviceInfoPtr dev,
294               char **devbuf, char **classbuf, char **namebuf)
295{
296    CopyDeviceName(namebuf, d->name);
297    CopySwapDevice(client, d, 0, devbuf);
298    CopySwapClasses(client, d, &dev->num_classes, classbuf);
299}
300
301/***********************************************************************
302 *
303 * This procedure checks if a device should be left off the list.
304 *
305 */
306
307static Bool
308ShouldSkipDevice(ClientPtr client, DeviceIntPtr d)
309{
310    /* don't send master devices other than VCP/VCK */
311    if (!IsMaster(d) || d == inputInfo.pointer ||d == inputInfo.keyboard) {
312        int rc = XaceHook(XACE_DEVICE_ACCESS, client, d, DixGetAttrAccess);
313
314        if (rc == Success)
315            return FALSE;
316    }
317    return TRUE;
318}
319
320/***********************************************************************
321 *
322 * This procedure lists the input devices available to the server.
323 *
324 * If this request is called by a client that has not issued a
325 * GetExtensionVersion request with major/minor version set, we don't send the
326 * complete device list. Instead, we only send the VCP, the VCK and floating
327 * SDs. This resembles the setup found on XI 1.x machines.
328 */
329
330int
331ProcXListInputDevices(ClientPtr client)
332{
333    xListInputDevicesReply rep;
334    int numdevs = 0;
335    int namesize = 1;           /* need 1 extra byte for strcpy */
336    int i = 0, size = 0;
337    int total_length;
338    char *devbuf, *classbuf, *namebuf, *savbuf;
339    Bool *skip;
340    xDeviceInfo *dev;
341    DeviceIntPtr d;
342
343    REQUEST_SIZE_MATCH(xListInputDevicesReq);
344
345    rep = (xListInputDevicesReply) {
346        .repType = X_Reply,
347        .RepType = X_ListInputDevices,
348        .sequenceNumber = client->sequence,
349        .length = 0
350    };
351
352    /* allocate space for saving skip value */
353    skip = calloc(sizeof(Bool), inputInfo.numDevices);
354    if (!skip)
355        return BadAlloc;
356
357    /* figure out which devices to skip */
358    numdevs = 0;
359    for (d = inputInfo.devices; d; d = d->next, i++) {
360        skip[i] = ShouldSkipDevice(client, d);
361        if (skip[i])
362            continue;
363
364        SizeDeviceInfo(d, &namesize, &size);
365        numdevs++;
366    }
367
368    for (d = inputInfo.off_devices; d; d = d->next, i++) {
369        skip[i] = ShouldSkipDevice(client, d);
370        if (skip[i])
371            continue;
372
373        SizeDeviceInfo(d, &namesize, &size);
374        numdevs++;
375    }
376
377    /* allocate space for reply */
378    total_length = numdevs * sizeof(xDeviceInfo) + size + namesize;
379    devbuf = (char *) calloc(1, total_length);
380    classbuf = devbuf + (numdevs * sizeof(xDeviceInfo));
381    namebuf = classbuf + size;
382    savbuf = devbuf;
383
384    /* fill in and send reply */
385    i = 0;
386    dev = (xDeviceInfoPtr) devbuf;
387    for (d = inputInfo.devices; d; d = d->next, i++) {
388        if (skip[i])
389            continue;
390
391        ListDeviceInfo(client, d, dev++, &devbuf, &classbuf, &namebuf);
392    }
393
394    for (d = inputInfo.off_devices; d; d = d->next, i++) {
395        if (skip[i])
396            continue;
397
398        ListDeviceInfo(client, d, dev++, &devbuf, &classbuf, &namebuf);
399    }
400    rep.ndevices = numdevs;
401    rep.length = bytes_to_int32(total_length);
402    WriteReplyToClient(client, sizeof(xListInputDevicesReply), &rep);
403    WriteToClient(client, total_length, savbuf);
404    free(savbuf);
405    free(skip);
406    return Success;
407}
408
409/***********************************************************************
410 *
411 * This procedure writes the reply for the XListInputDevices function,
412 * if the client and server have a different byte ordering.
413 *
414 */
415
416void _X_COLD
417SRepXListInputDevices(ClientPtr client, int size, xListInputDevicesReply * rep)
418{
419    swaps(&rep->sequenceNumber);
420    swapl(&rep->length);
421    WriteToClient(client, size, rep);
422}
423