inpututils.c revision 6747b715
1/*
2 * Copyright © 2008 Daniel Stone
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 * Author: Daniel Stone <daniel@fooishbar.org>
24 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include "dix-config.h"
28#endif
29
30#include "exevents.h"
31#include "exglobals.h"
32#include "misc.h"
33#include "input.h"
34#include "inputstr.h"
35#include "xace.h"
36#include "xkbsrv.h"
37#include "xkbstr.h"
38
39/* Check if a button map change is okay with the device.
40 * Returns -1 for BadValue, as it collides with MappingBusy. */
41static int
42check_butmap_change(DeviceIntPtr dev, CARD8 *map, int len, CARD32 *errval_out,
43                    ClientPtr client)
44{
45    int i, ret;
46
47    if (!dev || !dev->button)
48    {
49        client->errorValue = (dev) ? dev->id : 0;
50        return BadDevice;
51    }
52
53    ret = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixManageAccess);
54    if (ret != Success)
55    {
56        client->errorValue = dev->id;
57        return ret;
58    }
59
60    for (i = 0; i < len; i++) {
61        if (dev->button->map[i + 1] != map[i] && dev->button->down[i + 1])
62            return MappingBusy;
63    }
64
65    return Success;
66}
67
68static void
69do_butmap_change(DeviceIntPtr dev, CARD8 *map, int len, ClientPtr client)
70{
71    int i;
72    xEvent core_mn;
73    deviceMappingNotify xi_mn;
74
75    /* The map in ButtonClassRec refers to button numbers, whereas the
76     * protocol is zero-indexed.  Sigh. */
77    memcpy(&(dev->button->map[1]), map, len);
78
79    core_mn.u.u.type = MappingNotify;
80    core_mn.u.mappingNotify.request = MappingPointer;
81
82    /* 0 is the server client. */
83    for (i = 1; i < currentMaxClients; i++) {
84        /* Don't send irrelevant events to naïve clients. */
85        if (!clients[i] || clients[i]->clientState != ClientStateRunning)
86            continue;
87
88        if (!XIShouldNotify(clients[i], dev))
89            continue;
90
91        WriteEventsToClient(clients[i], 1, &core_mn);
92    }
93
94    xi_mn.type = DeviceMappingNotify;
95    xi_mn.request = MappingPointer;
96    xi_mn.deviceid = dev->id;
97    xi_mn.time = GetTimeInMillis();
98
99    SendEventToAllWindows(dev, DeviceMappingNotifyMask, (xEvent *) &xi_mn, 1);
100}
101
102/*
103 * Does what it says on the box, both for core and Xi.
104 *
105 * Faithfully reports any errors encountered while trying to apply the map
106 * to the requested device, faithfully ignores any errors encountered while
107 * trying to apply the map to its master/slaves.
108 */
109int
110ApplyPointerMapping(DeviceIntPtr dev, CARD8 *map, int len, ClientPtr client)
111{
112    int ret;
113
114    /* If we can't perform the change on the requested device, bail out. */
115    ret = check_butmap_change(dev, map, len, &client->errorValue, client);
116    if (ret != Success)
117        return ret;
118    do_butmap_change(dev, map, len, client);
119
120    return Success;
121}
122
123/* Check if a modifier map change is okay with the device.
124 * Returns -1 for BadValue, as it collides with MappingBusy; this particular
125 * caveat can be removed with LegalModifier, as we have no other reason to
126 * set MappingFailed.  Sigh. */
127static int
128check_modmap_change(ClientPtr client, DeviceIntPtr dev, KeyCode *modmap)
129{
130    int ret, i;
131    XkbDescPtr xkb;
132
133    ret = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixManageAccess);
134    if (ret != Success)
135        return ret;
136
137    if (!dev->key)
138        return BadMatch;
139    xkb = dev->key->xkbInfo->desc;
140
141    for (i = 0; i < MAP_LENGTH; i++) {
142        if (!modmap[i])
143            continue;
144
145        /* Check that all the new modifiers fall within the advertised
146         * keycode range. */
147        if (i < xkb->min_key_code || i > xkb->max_key_code) {
148            client->errorValue = i;
149            return -1;
150        }
151
152        /* Make sure the mapping is okay with the DDX. */
153        if (!LegalModifier(i, dev)) {
154            client->errorValue = i;
155            return MappingFailed;
156        }
157
158        /* None of the new modifiers may be down while we change the
159         * map. */
160        if (key_is_down(dev, i, KEY_POSTED | KEY_PROCESSED)) {
161            client->errorValue = i;
162            return MappingBusy;
163        }
164    }
165
166    /* None of the old modifiers may be down while we change the map,
167     * either. */
168    for (i = xkb->min_key_code; i < xkb->max_key_code; i++) {
169        if (!xkb->map->modmap[i])
170            continue;
171        if (key_is_down(dev, i, KEY_POSTED | KEY_PROCESSED)) {
172            client->errorValue = i;
173            return MappingBusy;
174        }
175    }
176
177    return Success;
178}
179
180static int
181check_modmap_change_slave(ClientPtr client, DeviceIntPtr master,
182                          DeviceIntPtr slave, CARD8 *modmap)
183{
184    XkbDescPtr master_xkb, slave_xkb;
185    int i, j;
186
187    if (!slave->key || !master->key)
188        return 0;
189
190    master_xkb = master->key->xkbInfo->desc;
191    slave_xkb = slave->key->xkbInfo->desc;
192
193    /* Ignore devices with a clearly different keymap. */
194    if (slave_xkb->min_key_code != master_xkb->min_key_code ||
195        slave_xkb->max_key_code != master_xkb->max_key_code)
196        return 0;
197
198    for (i = 0; i < MAP_LENGTH; i++) {
199        if (!modmap[i])
200            continue;
201
202        /* If we have different symbols for any modifier on an
203         * extended keyboard, ignore the whole remap request. */
204        for (j = 0;
205             j < XkbKeyNumSyms(slave_xkb, i) &&
206              j < XkbKeyNumSyms(master_xkb, i);
207             j++)
208            if (XkbKeySymsPtr(slave_xkb, i)[j] != XkbKeySymsPtr(master_xkb, i)[j])
209                return 0;
210    }
211
212    if (check_modmap_change(client, slave, modmap) != Success)
213        return 0;
214
215    return 1;
216}
217
218/* Actually change the modifier map, and send notifications.  Cannot fail. */
219static void
220do_modmap_change(ClientPtr client, DeviceIntPtr dev, CARD8 *modmap)
221{
222    XkbApplyMappingChange(dev, NULL, 0, 0, modmap, serverClient);
223}
224
225/* Rebuild modmap (key -> mod) from map (mod -> key). */
226static int build_modmap_from_modkeymap(CARD8 *modmap, KeyCode *modkeymap,
227                                       int max_keys_per_mod)
228{
229    int i, len = max_keys_per_mod * 8;
230
231    memset(modmap, 0, MAP_LENGTH);
232
233    for (i = 0; i < len; i++) {
234        if (!modkeymap[i])
235            continue;
236
237        if (modkeymap[i] >= MAP_LENGTH)
238            return BadValue;
239
240        if (modmap[modkeymap[i]])
241            return BadValue;
242
243        modmap[modkeymap[i]] = 1 << (i / max_keys_per_mod);
244    }
245
246    return Success;
247}
248
249int
250change_modmap(ClientPtr client, DeviceIntPtr dev, KeyCode *modkeymap,
251              int max_keys_per_mod)
252{
253    int ret;
254    CARD8 modmap[MAP_LENGTH];
255    DeviceIntPtr tmp;
256
257    ret = build_modmap_from_modkeymap(modmap, modkeymap, max_keys_per_mod);
258    if (ret != Success)
259        return ret;
260
261    /* If we can't perform the change on the requested device, bail out. */
262    ret = check_modmap_change(client, dev, modmap);
263    if (ret != Success)
264        return ret;
265    do_modmap_change(client, dev, modmap);
266
267    /* Change any attached masters/slaves. */
268    if (IsMaster(dev)) {
269        for (tmp = inputInfo.devices; tmp; tmp = tmp->next) {
270            if (!IsMaster(tmp) && tmp->u.master == dev)
271                if (check_modmap_change_slave(client, dev, tmp, modmap))
272                    do_modmap_change(client, tmp, modmap);
273        }
274    }
275    else if (dev->u.master && dev->u.master->u.lastSlave == dev) {
276        /* If this fails, expect the results to be weird. */
277        if (check_modmap_change(client, dev->u.master, modmap))
278            do_modmap_change(client, dev->u.master, modmap);
279    }
280
281    return Success;
282}
283
284int generate_modkeymap(ClientPtr client, DeviceIntPtr dev,
285                       KeyCode **modkeymap_out, int *max_keys_per_mod_out)
286{
287    CARD8 keys_per_mod[8];
288    int max_keys_per_mod;
289    KeyCode *modkeymap;
290    int i, j, ret;
291
292    ret = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixGetAttrAccess);
293    if (ret != Success)
294        return ret;
295
296    if (!dev->key)
297        return BadMatch;
298
299    /* Count the number of keys per modifier to determine how wide we
300     * should make the map. */
301    max_keys_per_mod = 0;
302    for (i = 0; i < 8; i++)
303        keys_per_mod[i] = 0;
304    for (i = 8; i < MAP_LENGTH; i++) {
305        for (j = 0; j < 8; j++) {
306            if (dev->key->xkbInfo->desc->map->modmap[i] & (1 << j)) {
307                if (++keys_per_mod[j] > max_keys_per_mod)
308                    max_keys_per_mod = keys_per_mod[j];
309            }
310        }
311    }
312
313    modkeymap = calloc(max_keys_per_mod * 8, sizeof(KeyCode));
314    if (!modkeymap)
315        return BadAlloc;
316
317    for (i = 0; i < 8; i++)
318        keys_per_mod[i] = 0;
319
320    for (i = 8; i < MAP_LENGTH; i++) {
321        for (j = 0; j < 8; j++) {
322            if (dev->key->xkbInfo->desc->map->modmap[i] & (1 << j)) {
323                modkeymap[(j * max_keys_per_mod) + keys_per_mod[j]] = i;
324                keys_per_mod[j]++;
325            }
326        }
327    }
328
329    *max_keys_per_mod_out = max_keys_per_mod;
330    *modkeymap_out = modkeymap;
331
332    return Success;
333}
334
335/**
336 * Duplicate the InputAttributes in the most obvious way.
337 * No special memory handling is used to give drivers the maximum
338 * flexibility with the data. Drivers should be able to call realloc on the
339 * product string if needed and perform similar operations.
340 */
341InputAttributes*
342DuplicateInputAttributes(InputAttributes *attrs)
343{
344    InputAttributes *new_attr;
345    int ntags = 0;
346    char **tags, **new_tags;
347
348    if (!attrs)
349        return NULL;
350
351    if (!(new_attr = calloc(1, sizeof(InputAttributes))))
352        goto unwind;
353
354    if (attrs->product && !(new_attr->product = strdup(attrs->product)))
355        goto unwind;
356    if (attrs->vendor && !(new_attr->vendor = strdup(attrs->vendor)))
357        goto unwind;
358    if (attrs->device && !(new_attr->device = strdup(attrs->device)))
359        goto unwind;
360    if (attrs->pnp_id && !(new_attr->pnp_id = strdup(attrs->pnp_id)))
361        goto unwind;
362    if (attrs->usb_id && !(new_attr->usb_id = strdup(attrs->usb_id)))
363        goto unwind;
364
365    new_attr->flags = attrs->flags;
366
367    if ((tags = attrs->tags))
368    {
369        while(*tags++)
370            ntags++;
371
372        new_attr->tags = calloc(ntags + 1, sizeof(char*));
373        if (!new_attr->tags)
374            goto unwind;
375
376        tags = attrs->tags;
377        new_tags = new_attr->tags;
378
379        while(*tags)
380        {
381            *new_tags = strdup(*tags);
382            if (!*new_tags)
383                goto unwind;
384
385            tags++;
386            new_tags++;
387        }
388    }
389
390    return new_attr;
391
392unwind:
393    FreeInputAttributes(new_attr);
394    return NULL;
395}
396
397void
398FreeInputAttributes(InputAttributes *attrs)
399{
400    char **tags;
401
402    if (!attrs)
403        return;
404
405    free(attrs->product);
406    free(attrs->vendor);
407    free(attrs->device);
408    free(attrs->pnp_id);
409    free(attrs->usb_id);
410
411    if ((tags = attrs->tags))
412        while(*tags)
413            free(*tags++);
414
415    free(attrs->tags);
416    free(attrs);
417}
418
419