1/*
2 * Copyright © 2009 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors: Peter Hutterer
24 *
25 */
26
27/**
28 * @file Protocol handling for the XIQueryDevice request/reply.
29 */
30
31#ifdef HAVE_DIX_CONFIG_H
32#include <dix-config.h>
33#endif
34
35#include "inputstr.h"
36#include <X11/X.h>
37#include <X11/Xatom.h>
38#include <X11/extensions/XI2proto.h>
39#include "xkbstr.h"
40#include "xkbsrv.h"
41#include "xserver-properties.h"
42#include "exevents.h"
43#include "xace.h"
44
45#include "xiquerydevice.h"
46
47static Bool ShouldSkipDevice(ClientPtr client, int deviceid, DeviceIntPtr d);
48static int
49ListDeviceInfo(ClientPtr client, DeviceIntPtr dev, xXIDeviceInfo* info);
50static int SizeDeviceInfo(DeviceIntPtr dev);
51static void SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info);
52int
53SProcXIQueryDevice(ClientPtr client)
54{
55    char n;
56
57    REQUEST(xXIQueryDeviceReq);
58    REQUEST_SIZE_MATCH(xXIQueryDeviceReq);
59
60    swaps(&stuff->length, n);
61    swaps(&stuff->deviceid, n);
62
63    return ProcXIQueryDevice(client);
64}
65
66int
67ProcXIQueryDevice(ClientPtr client)
68{
69    xXIQueryDeviceReply rep;
70    DeviceIntPtr dev = NULL;
71    int rc = Success;
72    int i = 0, len = 0;
73    char *info, *ptr;
74    Bool *skip = NULL;
75
76    REQUEST(xXIQueryDeviceReq);
77    REQUEST_SIZE_MATCH(xXIQueryDeviceReq);
78
79    if (stuff->deviceid != XIAllDevices && stuff->deviceid != XIAllMasterDevices)
80    {
81        rc = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess);
82        if (rc != Success)
83        {
84            client->errorValue = stuff->deviceid;
85            return rc;
86        }
87        len += SizeDeviceInfo(dev);
88    }
89    else
90    {
91        skip = calloc(sizeof(Bool), inputInfo.numDevices);
92        if (!skip)
93            return BadAlloc;
94
95        for (dev = inputInfo.devices; dev; dev = dev->next, i++)
96        {
97            skip[i] = ShouldSkipDevice(client, stuff->deviceid, dev);
98            if (!skip[i])
99                len += SizeDeviceInfo(dev);
100        }
101
102        for (dev = inputInfo.off_devices; dev; dev = dev->next, i++)
103        {
104            skip[i] = ShouldSkipDevice(client, stuff->deviceid, dev);
105            if (!skip[i])
106                len += SizeDeviceInfo(dev);
107        }
108    }
109
110    info = calloc(1, len);
111    if (!info) {
112        free(skip);
113        return BadAlloc;
114    }
115
116    memset(&rep, 0, sizeof(xXIQueryDeviceReply));
117    rep.repType = X_Reply;
118    rep.RepType = X_XIQueryDevice;
119    rep.sequenceNumber = client->sequence;
120    rep.length = len/4;
121    rep.num_devices = 0;
122
123    ptr = info;
124    if (dev)
125    {
126        len = ListDeviceInfo(client, dev, (xXIDeviceInfo*)info);
127        if (client->swapped)
128            SwapDeviceInfo(dev, (xXIDeviceInfo*)info);
129        info += len;
130        rep.num_devices = 1;
131    } else
132    {
133        i = 0;
134        for (dev = inputInfo.devices; dev; dev = dev->next, i++)
135        {
136            if (!skip[i])
137            {
138                len = ListDeviceInfo(client, dev, (xXIDeviceInfo*)info);
139                if (client->swapped)
140                    SwapDeviceInfo(dev, (xXIDeviceInfo*)info);
141                info += len;
142                rep.num_devices++;
143            }
144        }
145
146        for (dev = inputInfo.off_devices; dev; dev = dev->next, i++)
147        {
148            if (!skip[i])
149            {
150                len = ListDeviceInfo(client, dev, (xXIDeviceInfo*)info);
151                if (client->swapped)
152                    SwapDeviceInfo(dev, (xXIDeviceInfo*)info);
153                info += len;
154                rep.num_devices++;
155            }
156        }
157    }
158
159    len = rep.length * 4;
160    WriteReplyToClient(client, sizeof(xXIQueryDeviceReply), &rep);
161    WriteToClient(client, len, ptr);
162    free(ptr);
163    free(skip);
164    return rc;
165}
166
167void
168SRepXIQueryDevice(ClientPtr client, int size, xXIQueryDeviceReply *rep)
169{
170    char n;
171
172    swaps(&rep->sequenceNumber, n);
173    swapl(&rep->length, n);
174    swaps(&rep->num_devices, n);
175
176    /* Device info is already swapped, see ProcXIQueryDevice */
177
178    WriteToClient(client, size, (char *)rep);
179}
180
181
182/**
183 * @return Whether the device should be included in the returned list.
184 */
185static Bool
186ShouldSkipDevice(ClientPtr client, int deviceid, DeviceIntPtr dev)
187{
188    /* if all devices are not being queried, only master devices are */
189    if (deviceid == XIAllDevices || IsMaster(dev))
190    {
191        int rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixGetAttrAccess);
192        if (rc == Success)
193            return FALSE;
194    }
195    return TRUE;
196}
197
198/**
199 * @return The number of bytes needed to store this device's xXIDeviceInfo
200 * (and its classes).
201 */
202static int
203SizeDeviceInfo(DeviceIntPtr dev)
204{
205    int len = sizeof(xXIDeviceInfo);
206
207    /* 4-padded name */
208    len += pad_to_int32(strlen(dev->name));
209
210    return len + SizeDeviceClasses(dev);
211
212}
213
214/*
215 * @return The number of bytes needed to store this device's classes.
216 */
217int
218SizeDeviceClasses(DeviceIntPtr dev)
219{
220    int len = 0;
221
222    if (dev->button)
223    {
224        len += sizeof(xXIButtonInfo);
225        len += dev->button->numButtons * sizeof(Atom);
226        len += pad_to_int32(bits_to_bytes(dev->button->numButtons));
227    }
228
229    if (dev->key)
230    {
231        XkbDescPtr xkb = dev->key->xkbInfo->desc;
232        len += sizeof(xXIKeyInfo);
233        len += (xkb->max_key_code - xkb->min_key_code + 1) * sizeof(uint32_t);
234    }
235
236    if (dev->valuator)
237        len += sizeof(xXIValuatorInfo) * dev->valuator->numAxes;
238
239    return len;
240}
241
242
243/**
244 * Write button information into info.
245 * @return Number of bytes written into info.
246 */
247int
248ListButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info, Bool reportState)
249{
250    unsigned char *bits;
251    int mask_len;
252    int i;
253
254    if (!dev || !dev->button)
255	return 0;
256
257    mask_len = bytes_to_int32(bits_to_bytes(dev->button->numButtons));
258
259    info->type = ButtonClass;
260    info->num_buttons = dev->button->numButtons;
261    info->length = bytes_to_int32(sizeof(xXIButtonInfo)) +
262                   info->num_buttons + mask_len;
263    info->sourceid = dev->button->sourceid;
264
265    bits = (unsigned char*)&info[1];
266    memset(bits, 0, mask_len * 4);
267
268    if (reportState)
269	for (i = 0; i < dev->button->numButtons; i++)
270	    if (BitIsOn(dev->button->down, i))
271		SetBit(bits, i);
272
273    bits += mask_len * 4;
274    memcpy(bits, dev->button->labels, dev->button->numButtons * sizeof(Atom));
275
276    return info->length * 4;
277}
278
279static void
280SwapButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info)
281{
282    char n;
283    Atom *btn;
284    int i;
285    swaps(&info->type, n);
286    swaps(&info->length, n);
287    swaps(&info->sourceid, n);
288
289    for (i = 0, btn = (Atom*)&info[1]; i < info->num_buttons; i++, btn++)
290        swaps(btn, n);
291
292    swaps(&info->num_buttons, n);
293}
294
295/**
296 * Write key information into info.
297 * @return Number of bytes written into info.
298 */
299int
300ListKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info)
301{
302    int i;
303    XkbDescPtr xkb = dev->key->xkbInfo->desc;
304    uint32_t *kc;
305
306    info->type = KeyClass;
307    info->num_keycodes = xkb->max_key_code - xkb->min_key_code + 1;
308    info->length = sizeof(xXIKeyInfo)/4 + info->num_keycodes;
309    info->sourceid = dev->key->sourceid;
310
311    kc = (uint32_t*)&info[1];
312    for (i = xkb->min_key_code; i <= xkb->max_key_code; i++, kc++)
313        *kc = i;
314
315    return info->length * 4;
316}
317
318static void
319SwapKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info)
320{
321    char n;
322    uint32_t *key;
323    int i;
324    swaps(&info->type, n);
325    swaps(&info->length, n);
326    swaps(&info->sourceid, n);
327
328    for (i = 0, key = (uint32_t*)&info[1]; i < info->num_keycodes; i++, key++)
329        swapl(key, n);
330
331    swaps(&info->num_keycodes, n);
332}
333
334/**
335 * List axis information for the given axis.
336 *
337 * @return The number of bytes written into info.
338 */
339int
340ListValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info, int axisnumber,
341		 Bool reportState)
342{
343    ValuatorClassPtr v = dev->valuator;
344
345    info->type = ValuatorClass;
346    info->length = sizeof(xXIValuatorInfo)/4;
347    info->label = v->axes[axisnumber].label;
348    info->min.integral = v->axes[axisnumber].min_value;
349    info->min.frac = 0;
350    info->max.integral = v->axes[axisnumber].max_value;
351    info->max.frac = 0;
352    info->value.integral = (int)v->axisVal[axisnumber];
353    info->value.frac = (int)(v->axisVal[axisnumber] * (1 << 16) * (1 << 16));
354    info->resolution = v->axes[axisnumber].resolution;
355    info->number = axisnumber;
356    info->mode = valuator_get_mode(dev, axisnumber);
357    info->sourceid = v->sourceid;
358
359    if (!reportState)
360	info->value = info->min;
361
362    return info->length * 4;
363}
364
365static void
366SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info)
367{
368    char n;
369    swaps(&info->type, n);
370    swaps(&info->length, n);
371    swapl(&info->label, n);
372    swapl(&info->min.integral, n);
373    swapl(&info->min.frac, n);
374    swapl(&info->max.integral, n);
375    swapl(&info->max.frac, n);
376    swaps(&info->number, n);
377    swaps(&info->sourceid, n);
378}
379
380int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment)
381{
382    DeviceIntPtr master = dev->u.master;
383    int use;
384
385    if (IsMaster(dev))
386    {
387        DeviceIntPtr paired = GetPairedDevice(dev);
388        use = IsPointerDevice(dev) ? XIMasterPointer : XIMasterKeyboard;
389        *attachment = (paired ? paired->id : 0);
390    } else if (master)
391    {
392        use = IsPointerDevice(master) ? XISlavePointer : XISlaveKeyboard;
393        *attachment = master->id;
394    } else
395        use = XIFloatingSlave;
396
397    return use;
398}
399
400/**
401 * Write the info for device dev into the buffer pointed to by info.
402 *
403 * @return The number of bytes used.
404 */
405static int
406ListDeviceInfo(ClientPtr client, DeviceIntPtr dev, xXIDeviceInfo* info)
407{
408    char *any = (char*)&info[1];
409    int len = 0, total_len = 0;
410
411    info->deviceid = dev->id;
412    info->use = GetDeviceUse(dev, &info->attachment);
413    info->num_classes = 0;
414    info->name_len = strlen(dev->name);
415    info->enabled = dev->enabled;
416    total_len = sizeof(xXIDeviceInfo);
417
418    len = pad_to_int32(info->name_len);
419    memset(any, 0, len);
420    strncpy(any, dev->name, info->name_len);
421    any += len;
422    total_len += len;
423
424    total_len += ListDeviceClasses(client, dev, any, &info->num_classes);
425    return total_len;
426}
427
428/**
429 * Write the class info of the device into the memory pointed to by any, set
430 * nclasses to the number of classes in total and return the number of bytes
431 * written.
432 */
433int
434ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
435		  char *any, uint16_t *nclasses)
436{
437    int total_len = 0;
438    int len;
439    int i;
440    int rc;
441
442    /* Check if the current device state should be suppressed */
443    rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixReadAccess);
444
445    if (dev->button)
446    {
447        (*nclasses)++;
448        len = ListButtonInfo(dev, (xXIButtonInfo*)any, rc == Success);
449        any += len;
450        total_len += len;
451    }
452
453    if (dev->key)
454    {
455        (*nclasses)++;
456        len = ListKeyInfo(dev, (xXIKeyInfo*)any);
457        any += len;
458        total_len += len;
459    }
460
461    for (i = 0; dev->valuator && i < dev->valuator->numAxes; i++)
462    {
463        (*nclasses)++;
464        len = ListValuatorInfo(dev, (xXIValuatorInfo*)any, i, rc == Success);
465        any += len;
466        total_len += len;
467    }
468
469    return total_len;
470}
471
472static void
473SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info)
474{
475    char n;
476    char *any = (char*)&info[1];
477    int i;
478
479    /* Skip over name */
480    any += pad_to_int32(info->name_len);
481
482    for (i = 0; i < info->num_classes; i++)
483    {
484        int len = ((xXIAnyInfo*)any)->length;
485        switch(((xXIAnyInfo*)any)->type)
486        {
487            case XIButtonClass:
488                SwapButtonInfo(dev, (xXIButtonInfo*)any);
489                break;
490            case XIKeyClass:
491                SwapKeyInfo(dev, (xXIKeyInfo*)any);
492                break;
493            case XIValuatorClass:
494                SwapValuatorInfo(dev, (xXIValuatorInfo*)any);
495                break;
496        }
497
498        any += len * 4;
499    }
500
501    swaps(&info->deviceid, n);
502    swaps(&info->use, n);
503    swaps(&info->attachment, n);
504    swaps(&info->num_classes, n);
505    swaps(&info->name_len, n);
506
507}
508