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