| 1 | /************************************************************ |
| 2 | Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc. |
| 3 | |
| 4 | Permission to use, copy, modify, and distribute this |
| 5 | software and its documentation for any purpose and without |
| 6 | fee is hereby granted, provided that the above copyright |
| 7 | notice appear in all copies and that both that copyright |
| 8 | notice and this permission notice appear in supporting |
| 9 | documentation, and that the name of Silicon Graphics not be |
| 10 | used in advertising or publicity pertaining to distribution |
| 11 | of the software without specific prior written permission. |
| 12 | Silicon Graphics makes no representation about the suitability |
| 13 | of this software for any purpose. It is provided "as is" |
| 14 | without any express or implied warranty. |
| 15 | |
| 16 | SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS |
| 17 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| 18 | AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON |
| 19 | GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL |
| 20 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| 21 | DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE |
| 22 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH |
| 23 | THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 24 | |
| 25 | ********************************************************/ |
| 26 | |
| 27 | #ifdef HAVE_DIX_CONFIG_H |
| 28 | #include <dix-config.h> |
| 29 | #endif |
| 30 | |
| 31 | #include <stdio.h> |
| 32 | #include <ctype.h> |
| 33 | #include <math.h> |
| 34 | #include <X11/X.h> |
| 35 | #include <X11/Xproto.h> |
| 36 | #include "misc.h" |
| 37 | #include "inputstr.h" |
| 38 | |
| 39 | #include <X11/extensions/XI.h> |
| 40 | #include <xkbsrv.h> |
| 41 | #include "xkb.h" |
| 42 | |
| 43 | /***====================================================================***/ |
| 44 | |
| 45 | /* |
| 46 | * unsigned |
| 47 | * XkbIndicatorsToUpdate(dev,changed,check_devs_rtrn) |
| 48 | * |
| 49 | * Given a keyboard and a set of state components that have changed, |
| 50 | * this function returns the indicators on the default keyboard |
| 51 | * feedback that might be affected. It also reports whether or not |
| 52 | * any extension devices might be affected in check_devs_rtrn. |
| 53 | */ |
| 54 | |
| 55 | unsigned |
| 56 | XkbIndicatorsToUpdate(DeviceIntPtr dev, |
| 57 | unsigned long state_changes, Bool enable_changes) |
| 58 | { |
| 59 | register unsigned update = 0; |
| 60 | XkbSrvLedInfoPtr sli; |
| 61 | |
| 62 | sli = XkbFindSrvLedInfo(dev, XkbDfltXIClass, XkbDfltXIId, 0); |
| 63 | |
| 64 | if (!sli) |
| 65 | return update; |
| 66 | |
| 67 | if (state_changes & (XkbModifierStateMask | XkbGroupStateMask)) |
| 68 | update |= sli->usesEffective; |
| 69 | if (state_changes & (XkbModifierBaseMask | XkbGroupBaseMask)) |
| 70 | update |= sli->usesBase; |
| 71 | if (state_changes & (XkbModifierLatchMask | XkbGroupLatchMask)) |
| 72 | update |= sli->usesLatched; |
| 73 | if (state_changes & (XkbModifierLockMask | XkbGroupLockMask)) |
| 74 | update |= sli->usesLocked; |
| 75 | if (state_changes & XkbCompatStateMask) |
| 76 | update |= sli->usesCompat; |
| 77 | if (enable_changes) |
| 78 | update |= sli->usesControls; |
| 79 | return update; |
| 80 | } |
| 81 | |
| 82 | /***====================================================================***/ |
| 83 | |
| 84 | /* |
| 85 | * Bool |
| 86 | *XkbApplyLEDChangeToKeyboard(xkbi,map,on,change) |
| 87 | * |
| 88 | * Some indicators "drive" the keyboard when their state is explicitly |
| 89 | * changed, as described in section 9.2.1 of the XKB protocol spec. |
| 90 | * This function updates the state and controls for the keyboard |
| 91 | * specified by 'xkbi' to reflect any changes that are required |
| 92 | * when the indicator described by 'map' is turned on or off. The |
| 93 | * extent of the changes is reported in change, which must be defined. |
| 94 | */ |
| 95 | static Bool |
| 96 | XkbApplyLEDChangeToKeyboard(XkbSrvInfoPtr xkbi, |
| 97 | XkbIndicatorMapPtr map, |
| 98 | Bool on, XkbChangesPtr change) |
| 99 | { |
| 100 | Bool ctrlChange, stateChange; |
| 101 | XkbStatePtr state; |
| 102 | |
| 103 | if ((map->flags & XkbIM_NoExplicit) || |
| 104 | ((map->flags & XkbIM_LEDDrivesKB) == 0)) |
| 105 | return FALSE; |
| 106 | ctrlChange = stateChange = FALSE; |
| 107 | if (map->ctrls) { |
| 108 | XkbControlsPtr ctrls = xkbi->desc->ctrls; |
| 109 | unsigned old; |
| 110 | |
| 111 | old = ctrls->enabled_ctrls; |
| 112 | if (on) |
| 113 | ctrls->enabled_ctrls |= map->ctrls; |
| 114 | else |
| 115 | ctrls->enabled_ctrls &= ~map->ctrls; |
| 116 | if (old != ctrls->enabled_ctrls) { |
| 117 | change->ctrls.changed_ctrls = XkbControlsEnabledMask; |
| 118 | change->ctrls.enabled_ctrls_changes = old ^ ctrls->enabled_ctrls; |
| 119 | ctrlChange = TRUE; |
| 120 | } |
| 121 | } |
| 122 | state = &xkbi->state; |
| 123 | if ((map->groups) && ((map->which_groups & (~XkbIM_UseBase)) != 0)) { |
| 124 | register int i; |
| 125 | register unsigned bit, match; |
| 126 | |
| 127 | if (on) |
| 128 | match = (map->groups) & XkbAllGroupsMask; |
| 129 | else |
| 130 | match = (~map->groups) & XkbAllGroupsMask; |
| 131 | if (map->which_groups & (XkbIM_UseLocked | XkbIM_UseEffective)) { |
| 132 | for (i = 0, bit = 1; i < XkbNumKbdGroups; i++, bit <<= 1) { |
| 133 | if (bit & match) |
| 134 | break; |
| 135 | } |
| 136 | if (map->which_groups & XkbIM_UseLatched) |
| 137 | XkbLatchGroup(xkbi->device, 0); /* unlatch group */ |
| 138 | state->locked_group = i; |
| 139 | stateChange = TRUE; |
| 140 | } |
| 141 | else if (map->which_groups & (XkbIM_UseLatched | XkbIM_UseEffective)) { |
| 142 | for (i = 0, bit = 1; i < XkbNumKbdGroups; i++, bit <<= 1) { |
| 143 | if (bit & match) |
| 144 | break; |
| 145 | } |
| 146 | state->locked_group = 0; |
| 147 | XkbLatchGroup(xkbi->device, i); |
| 148 | stateChange = TRUE; |
| 149 | } |
| 150 | } |
| 151 | if ((map->mods.mask) && ((map->which_mods & (~XkbIM_UseBase)) != 0)) { |
| 152 | if (map->which_mods & (XkbIM_UseLocked | XkbIM_UseEffective)) { |
| 153 | register unsigned long old; |
| 154 | |
| 155 | old = state->locked_mods; |
| 156 | if (on) |
| 157 | state->locked_mods |= map->mods.mask; |
| 158 | else |
| 159 | state->locked_mods &= ~map->mods.mask; |
| 160 | if (state->locked_mods != old) |
| 161 | stateChange = TRUE; |
| 162 | } |
| 163 | if (map->which_mods & (XkbIM_UseLatched | XkbIM_UseEffective)) { |
| 164 | register unsigned long newmods; |
| 165 | |
| 166 | newmods = state->latched_mods; |
| 167 | if (on) |
| 168 | newmods |= map->mods.mask; |
| 169 | else |
| 170 | newmods &= ~map->mods.mask; |
| 171 | if (newmods != state->locked_mods) { |
| 172 | newmods &= map->mods.mask; |
| 173 | XkbLatchModifiers(xkbi->device, map->mods.mask, newmods); |
| 174 | stateChange = TRUE; |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | return stateChange || ctrlChange; |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | * Bool |
| 183 | * ComputeAutoState(map,state,ctrls) |
| 184 | * |
| 185 | * This function reports the effect of applying the specified |
| 186 | * indicator map given the specified state and controls, as |
| 187 | * described in section 9.2 of the XKB protocol specification. |
| 188 | */ |
| 189 | |
| 190 | static Bool |
| 191 | ComputeAutoState(XkbIndicatorMapPtr map, |
| 192 | XkbStatePtr state, XkbControlsPtr ctrls) |
| 193 | { |
| 194 | Bool on; |
| 195 | CARD8 mods, group; |
| 196 | |
| 197 | on = FALSE; |
| 198 | mods = group = 0; |
| 199 | if (map->which_mods & XkbIM_UseAnyMods) { |
| 200 | if (map->which_mods & XkbIM_UseBase) |
| 201 | mods |= state->base_mods; |
| 202 | if (map->which_mods & XkbIM_UseLatched) |
| 203 | mods |= state->latched_mods; |
| 204 | if (map->which_mods & XkbIM_UseLocked) |
| 205 | mods |= state->locked_mods; |
| 206 | if (map->which_mods & XkbIM_UseEffective) |
| 207 | mods |= state->mods; |
| 208 | if (map->which_mods & XkbIM_UseCompat) |
| 209 | mods |= state->compat_state; |
| 210 | on = ((map->mods.mask & mods) != 0); |
| 211 | on = on || ((mods == 0) && (map->mods.mask == 0) && |
| 212 | (map->mods.vmods == 0)); |
| 213 | } |
| 214 | if (map->which_groups & XkbIM_UseAnyGroup) { |
| 215 | if (map->which_groups & XkbIM_UseBase) |
| 216 | group |= (1L << state->base_group); |
| 217 | if (map->which_groups & XkbIM_UseLatched) |
| 218 | group |= (1L << state->latched_group); |
| 219 | if (map->which_groups & XkbIM_UseLocked) |
| 220 | group |= (1L << state->locked_group); |
| 221 | if (map->which_groups & XkbIM_UseEffective) |
| 222 | group |= (1L << state->group); |
| 223 | on = on || (((map->groups & group) != 0) || (map->groups == 0)); |
| 224 | } |
| 225 | if (map->ctrls) |
| 226 | on = on || (ctrls->enabled_ctrls & map->ctrls); |
| 227 | return on; |
| 228 | } |
| 229 | |
| 230 | static void |
| 231 | XkbUpdateLedAutoState(DeviceIntPtr dev, |
| 232 | XkbSrvLedInfoPtr sli, |
| 233 | unsigned maps_to_check, |
| 234 | xkbExtensionDeviceNotify * ed, |
| 235 | XkbChangesPtr changes, XkbEventCausePtr cause) |
| 236 | { |
| 237 | DeviceIntPtr kbd; |
| 238 | XkbStatePtr state; |
| 239 | XkbControlsPtr ctrls; |
| 240 | XkbChangesRec my_changes; |
| 241 | xkbExtensionDeviceNotify my_ed; |
| 242 | register unsigned i, bit, affected; |
| 243 | register XkbIndicatorMapPtr map; |
| 244 | unsigned oldState; |
| 245 | |
| 246 | if ((maps_to_check == 0) || (sli->maps == NULL) || (sli->mapsPresent == 0)) |
| 247 | return; |
| 248 | |
| 249 | if (dev->key && dev->key->xkbInfo) |
| 250 | kbd = dev; |
| 251 | else |
| 252 | kbd = inputInfo.keyboard; |
| 253 | |
| 254 | state = &kbd->key->xkbInfo->state; |
| 255 | ctrls = kbd->key->xkbInfo->desc->ctrls; |
| 256 | affected = maps_to_check; |
| 257 | oldState = sli->effectiveState; |
| 258 | sli->autoState &= ~affected; |
| 259 | for (i = 0, bit = 1; (i < XkbNumIndicators) && (affected); i++, bit <<= 1) { |
| 260 | if ((affected & bit) == 0) |
| 261 | continue; |
| 262 | affected &= ~bit; |
| 263 | map = &sli->maps[i]; |
| 264 | if ((!(map->flags & XkbIM_NoAutomatic)) && |
| 265 | ComputeAutoState(map, state, ctrls)) |
| 266 | sli->autoState |= bit; |
| 267 | } |
| 268 | sli->effectiveState = (sli->autoState | sli->explicitState); |
| 269 | affected = sli->effectiveState ^ oldState; |
| 270 | if (affected == 0) |
| 271 | return; |
| 272 | |
| 273 | if (ed == NULL) { |
| 274 | ed = &my_ed; |
| 275 | memset((char *) ed, 0, sizeof(xkbExtensionDeviceNotify)); |
| 276 | } |
| 277 | else if ((ed->reason & XkbXI_IndicatorsMask) && |
| 278 | ((ed->ledClass != sli->class) || (ed->ledID != sli->id))) { |
| 279 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 280 | } |
| 281 | |
| 282 | if ((kbd == dev) && (sli->flags & XkbSLI_IsDefault)) { |
| 283 | if (changes == NULL) { |
| 284 | changes = &my_changes; |
| 285 | memset((char *) changes, 0, sizeof(XkbChangesRec)); |
| 286 | } |
| 287 | changes->indicators.state_changes |= affected; |
| 288 | } |
| 289 | |
| 290 | ed->reason |= XkbXI_IndicatorStateMask; |
| 291 | ed->ledClass = sli->class; |
| 292 | ed->ledID = sli->id; |
| 293 | ed->ledsDefined = sli->namesPresent | sli->mapsPresent; |
| 294 | ed->ledState = sli->effectiveState; |
| 295 | ed->unsupported = 0; |
| 296 | ed->supported = XkbXI_AllFeaturesMask; |
| 297 | |
| 298 | if (changes != &my_changes) |
| 299 | changes = NULL; |
| 300 | if (ed != &my_ed) |
| 301 | ed = NULL; |
| 302 | if (changes || ed) |
| 303 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 304 | return; |
| 305 | } |
| 306 | |
| 307 | static void |
| 308 | XkbUpdateAllDeviceIndicators(XkbChangesPtr changes, XkbEventCausePtr cause) |
| 309 | { |
| 310 | DeviceIntPtr edev; |
| 311 | XkbSrvLedInfoPtr sli; |
| 312 | |
| 313 | for (edev = inputInfo.devices; edev != NULL; edev = edev->next) { |
| 314 | if (edev->kbdfeed) { |
| 315 | KbdFeedbackPtr kf; |
| 316 | |
| 317 | for (kf = edev->kbdfeed; kf != NULL; kf = kf->next) { |
| 318 | if ((kf->xkb_sli == NULL) || (kf->xkb_sli->maps == NULL)) |
| 319 | continue; |
| 320 | sli = kf->xkb_sli; |
| 321 | XkbUpdateLedAutoState(edev, sli, sli->mapsPresent, NULL, |
| 322 | changes, cause); |
| 323 | |
| 324 | } |
| 325 | } |
| 326 | if (edev->leds) { |
| 327 | LedFeedbackPtr lf; |
| 328 | |
| 329 | for (lf = edev->leds; lf != NULL; lf = lf->next) { |
| 330 | if ((lf->xkb_sli == NULL) || (lf->xkb_sli->maps == NULL)) |
| 331 | continue; |
| 332 | sli = lf->xkb_sli; |
| 333 | XkbUpdateLedAutoState(edev, sli, sli->mapsPresent, NULL, |
| 334 | changes, cause); |
| 335 | |
| 336 | } |
| 337 | } |
| 338 | } |
| 339 | return; |
| 340 | } |
| 341 | |
| 342 | /***====================================================================***/ |
| 343 | |
| 344 | /* |
| 345 | * void |
| 346 | * XkbSetIndicators(dev,affect,values,cause) |
| 347 | * |
| 348 | * Attempts to change the indicators specified in 'affect' to the |
| 349 | * states specified in 'values' for the default keyboard feedback |
| 350 | * on the keyboard specified by 'dev.' Attempts to change indicator |
| 351 | * state might be ignored or have no affect, depending on the XKB |
| 352 | * indicator map for any affected indicators, as described in section |
| 353 | * 9.2 of the XKB protocol specification. |
| 354 | * |
| 355 | * If 'changes' is non-NULL, this function notes any changes to the |
| 356 | * keyboard state, controls, or indicator state that result from this |
| 357 | * attempted change. If 'changes' is NULL, this function generates |
| 358 | * XKB events to report any such changes to interested clients. |
| 359 | * |
| 360 | * If 'cause' is non-NULL, it specifies the reason for the change, |
| 361 | * as reported in some XKB events. If it is NULL, this function |
| 362 | * assumes that the change is the result of a core protocol |
| 363 | * ChangeKeyboardMapping request. |
| 364 | */ |
| 365 | |
| 366 | void |
| 367 | XkbSetIndicators(DeviceIntPtr dev, |
| 368 | CARD32 affect, CARD32 values, XkbEventCausePtr cause) |
| 369 | { |
| 370 | XkbSrvLedInfoPtr sli; |
| 371 | XkbChangesRec changes; |
| 372 | xkbExtensionDeviceNotify ed; |
| 373 | unsigned side_affected; |
| 374 | |
| 375 | memset((char *) &changes, 0, sizeof(XkbChangesRec)); |
| 376 | memset((char *) &ed, 0, sizeof(xkbExtensionDeviceNotify)); |
| 377 | sli = XkbFindSrvLedInfo(dev, XkbDfltXIClass, XkbDfltXIId, 0); |
| 378 | sli->explicitState &= ~affect; |
| 379 | sli->explicitState |= (affect & values); |
| 380 | XkbApplyLedStateChanges(dev, sli, affect, &ed, &changes, cause); |
| 381 | |
| 382 | side_affected = 0; |
| 383 | if (changes.state_changes != 0) |
| 384 | side_affected |= |
| 385 | XkbIndicatorsToUpdate(dev, changes.state_changes, FALSE); |
| 386 | if (changes.ctrls.enabled_ctrls_changes) |
| 387 | side_affected |= sli->usesControls; |
| 388 | |
| 389 | if (side_affected) { |
| 390 | XkbUpdateLedAutoState(dev, sli, side_affected, &ed, &changes, cause); |
| 391 | affect |= side_affected; |
| 392 | } |
| 393 | if (changes.state_changes || changes.ctrls.enabled_ctrls_changes) |
| 394 | XkbUpdateAllDeviceIndicators(NULL, cause); |
| 395 | |
| 396 | XkbFlushLedEvents(dev, dev, sli, &ed, &changes, cause); |
| 397 | return; |
| 398 | } |
| 399 | |
| 400 | /***====================================================================***/ |
| 401 | |
| 402 | /***====================================================================***/ |
| 403 | |
| 404 | /* |
| 405 | * void |
| 406 | * XkbUpdateIndicators(dev,update,check_edevs,changes,cause) |
| 407 | * |
| 408 | * Applies the indicator maps for any indicators specified in |
| 409 | * 'update' from the default keyboard feedback on the device |
| 410 | * specified by 'dev.' |
| 411 | * |
| 412 | * If 'changes' is NULL, this function generates and XKB events |
| 413 | * required to report the necessary changes, otherwise it simply |
| 414 | * notes the indicators with changed state. |
| 415 | * |
| 416 | * If 'check_edevs' is TRUE, this function also checks the indicator |
| 417 | * maps for any open extension devices that have them, and updates |
| 418 | * the state of any extension device indicators as necessary. |
| 419 | */ |
| 420 | |
| 421 | void |
| 422 | XkbUpdateIndicators(DeviceIntPtr dev, |
| 423 | register CARD32 update, |
| 424 | Bool check_edevs, |
| 425 | XkbChangesPtr changes, XkbEventCausePtr cause) |
| 426 | { |
| 427 | XkbSrvLedInfoPtr sli; |
| 428 | |
| 429 | sli = XkbFindSrvLedInfo(dev, XkbDfltXIClass, XkbDfltXIId, 0); |
| 430 | XkbUpdateLedAutoState(dev, sli, update, NULL, changes, cause); |
| 431 | if (check_edevs) |
| 432 | XkbUpdateAllDeviceIndicators(changes, cause); |
| 433 | return; |
| 434 | } |
| 435 | |
| 436 | /***====================================================================***/ |
| 437 | |
| 438 | /***====================================================================***/ |
| 439 | |
| 440 | /* |
| 441 | * void |
| 442 | * XkbCheckIndicatorMaps(dev,sli,which) |
| 443 | * |
| 444 | * Updates the 'indicator accelerators' for the indicators specified |
| 445 | * by 'which' in the feedback specified by 'sli.' The indicator |
| 446 | * accelerators are internal to the server and are used to simplify |
| 447 | * and speed up the process of figuring out which indicators might |
| 448 | * be affected by a particular change in keyboard state or controls. |
| 449 | */ |
| 450 | |
| 451 | void |
| 452 | XkbCheckIndicatorMaps(DeviceIntPtr dev, XkbSrvLedInfoPtr sli, unsigned which) |
| 453 | { |
| 454 | register unsigned i, bit; |
| 455 | XkbIndicatorMapPtr map; |
| 456 | XkbDescPtr xkb; |
| 457 | |
| 458 | if ((sli->flags & XkbSLI_HasOwnState) == 0) |
| 459 | return; |
| 460 | |
| 461 | sli->usesBase &= ~which; |
| 462 | sli->usesLatched &= ~which; |
| 463 | sli->usesLocked &= ~which; |
| 464 | sli->usesEffective &= ~which; |
| 465 | sli->usesCompat &= ~which; |
| 466 | sli->usesControls &= ~which; |
| 467 | sli->mapsPresent &= ~which; |
| 468 | |
| 469 | xkb = dev->key->xkbInfo->desc; |
| 470 | for (i = 0, bit = 1, map = sli->maps; i < XkbNumIndicators; |
| 471 | i++, bit <<= 1, map++) { |
| 472 | if (which & bit) { |
| 473 | CARD8 what; |
| 474 | |
| 475 | if (!map || !XkbIM_InUse(map)) |
| 476 | continue; |
| 477 | sli->mapsPresent |= bit; |
| 478 | |
| 479 | what = (map->which_mods | map->which_groups); |
| 480 | if (what & XkbIM_UseBase) |
| 481 | sli->usesBase |= bit; |
| 482 | if (what & XkbIM_UseLatched) |
| 483 | sli->usesLatched |= bit; |
| 484 | if (what & XkbIM_UseLocked) |
| 485 | sli->usesLocked |= bit; |
| 486 | if (what & XkbIM_UseEffective) |
| 487 | sli->usesEffective |= bit; |
| 488 | if (what & XkbIM_UseCompat) |
| 489 | sli->usesCompat |= bit; |
| 490 | if (map->ctrls) |
| 491 | sli->usesControls |= bit; |
| 492 | |
| 493 | map->mods.mask = map->mods.real_mods; |
| 494 | if (map->mods.vmods != 0) { |
| 495 | map->mods.mask |= XkbMaskForVMask(xkb, map->mods.vmods); |
| 496 | } |
| 497 | } |
| 498 | } |
| 499 | sli->usedComponents = 0; |
| 500 | if (sli->usesBase) |
| 501 | sli->usedComponents |= XkbModifierBaseMask | XkbGroupBaseMask; |
| 502 | if (sli->usesLatched) |
| 503 | sli->usedComponents |= XkbModifierLatchMask | XkbGroupLatchMask; |
| 504 | if (sli->usesLocked) |
| 505 | sli->usedComponents |= XkbModifierLockMask | XkbGroupLockMask; |
| 506 | if (sli->usesEffective) |
| 507 | sli->usedComponents |= XkbModifierStateMask | XkbGroupStateMask; |
| 508 | if (sli->usesCompat) |
| 509 | sli->usedComponents |= XkbCompatStateMask; |
| 510 | return; |
| 511 | } |
| 512 | |
| 513 | /***====================================================================***/ |
| 514 | |
| 515 | /* |
| 516 | * XkbSrvLedInfoPtr |
| 517 | * XkbAllocSrvLedInfo(dev,kf,lf,needed_parts) |
| 518 | * |
| 519 | * Allocates an XkbSrvLedInfoPtr for the feedback specified by either |
| 520 | * 'kf' or 'lf' on the keyboard specified by 'dev.' |
| 521 | * |
| 522 | * If 'needed_parts' is non-zero, this function makes sure that any |
| 523 | * of the parts speicified therein are allocated. |
| 524 | */ |
| 525 | XkbSrvLedInfoPtr |
| 526 | XkbAllocSrvLedInfo(DeviceIntPtr dev, |
| 527 | KbdFeedbackPtr kf, LedFeedbackPtr lf, unsigned needed_parts) |
| 528 | { |
| 529 | XkbSrvLedInfoPtr sli; |
| 530 | Bool checkAccel; |
| 531 | Bool checkNames; |
| 532 | |
| 533 | sli = NULL; |
| 534 | checkAccel = checkNames = FALSE; |
| 535 | if ((kf != NULL) && (kf->xkb_sli == NULL)) { |
| 536 | kf->xkb_sli = sli = calloc(1, sizeof(XkbSrvLedInfoRec)); |
| 537 | if (sli == NULL) |
| 538 | return NULL; /* ALLOCATION ERROR */ |
| 539 | if (dev->key && dev->key->xkbInfo) |
| 540 | sli->flags = XkbSLI_HasOwnState; |
| 541 | else |
| 542 | sli->flags = 0; |
| 543 | sli->class = KbdFeedbackClass; |
| 544 | sli->id = kf->ctrl.id; |
| 545 | sli->fb.kf = kf; |
| 546 | |
| 547 | sli->autoState = 0; |
| 548 | sli->explicitState = kf->ctrl.leds; |
| 549 | sli->effectiveState = kf->ctrl.leds; |
| 550 | |
| 551 | if ((kf == dev->kbdfeed) && (dev->key) && (dev->key->xkbInfo)) { |
| 552 | XkbDescPtr xkb; |
| 553 | |
| 554 | xkb = dev->key->xkbInfo->desc; |
| 555 | sli->flags |= XkbSLI_IsDefault; |
| 556 | sli->physIndicators = xkb->indicators->phys_indicators; |
| 557 | sli->names = xkb->names->indicators; |
| 558 | sli->maps = xkb->indicators->maps; |
| 559 | checkNames = checkAccel = TRUE; |
| 560 | } |
| 561 | else { |
| 562 | sli->physIndicators = XkbAllIndicatorsMask; |
| 563 | sli->names = NULL; |
| 564 | sli->maps = NULL; |
| 565 | } |
| 566 | } |
| 567 | else if ((kf != NULL) && ((kf->xkb_sli->flags & XkbSLI_IsDefault) != 0)) { |
| 568 | XkbDescPtr xkb; |
| 569 | |
| 570 | xkb = dev->key->xkbInfo->desc; |
| 571 | sli = kf->xkb_sli; |
| 572 | sli->physIndicators = xkb->indicators->phys_indicators; |
| 573 | if (xkb->names->indicators != sli->names) { |
| 574 | checkNames = TRUE; |
| 575 | sli->names = xkb->names->indicators; |
| 576 | } |
| 577 | if (xkb->indicators->maps != sli->maps) { |
| 578 | checkAccel = TRUE; |
| 579 | sli->maps = xkb->indicators->maps; |
| 580 | } |
| 581 | } |
| 582 | else if ((lf != NULL) && (lf->xkb_sli == NULL)) { |
| 583 | lf->xkb_sli = sli = calloc(1, sizeof(XkbSrvLedInfoRec)); |
| 584 | if (sli == NULL) |
| 585 | return NULL; /* ALLOCATION ERROR */ |
| 586 | if (dev->key && dev->key->xkbInfo) |
| 587 | sli->flags = XkbSLI_HasOwnState; |
| 588 | else |
| 589 | sli->flags = 0; |
| 590 | sli->class = LedFeedbackClass; |
| 591 | sli->id = lf->ctrl.id; |
| 592 | sli->fb.lf = lf; |
| 593 | |
| 594 | sli->physIndicators = lf->ctrl.led_mask; |
| 595 | sli->autoState = 0; |
| 596 | sli->explicitState = lf->ctrl.led_values; |
| 597 | sli->effectiveState = lf->ctrl.led_values; |
| 598 | sli->maps = NULL; |
| 599 | sli->names = NULL; |
| 600 | } |
| 601 | else |
| 602 | return NULL; |
| 603 | if ((sli->names == NULL) && (needed_parts & XkbXI_IndicatorNamesMask)) |
| 604 | sli->names = calloc(XkbNumIndicators, sizeof(Atom)); |
| 605 | if ((sli->maps == NULL) && (needed_parts & XkbXI_IndicatorMapsMask)) |
| 606 | sli->maps = calloc(XkbNumIndicators, sizeof(XkbIndicatorMapRec)); |
| 607 | if (checkNames) { |
| 608 | register unsigned i, bit; |
| 609 | |
| 610 | sli->namesPresent = 0; |
| 611 | for (i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1) { |
| 612 | if (sli->names[i] != None) |
| 613 | sli->namesPresent |= bit; |
| 614 | } |
| 615 | } |
| 616 | if (checkAccel) |
| 617 | XkbCheckIndicatorMaps(dev, sli, XkbAllIndicatorsMask); |
| 618 | return sli; |
| 619 | } |
| 620 | |
| 621 | void |
| 622 | XkbFreeSrvLedInfo(XkbSrvLedInfoPtr sli) |
| 623 | { |
| 624 | if ((sli->flags & XkbSLI_IsDefault) == 0) { |
| 625 | free(sli->maps); |
| 626 | free(sli->names); |
| 627 | } |
| 628 | sli->maps = NULL; |
| 629 | sli->names = NULL; |
| 630 | free(sli); |
| 631 | return; |
| 632 | } |
| 633 | |
| 634 | /* |
| 635 | * XkbSrvLedInfoPtr |
| 636 | * XkbCopySrvLedInfo(dev,src,kf,lf) |
| 637 | * |
| 638 | * Takes the given XkbSrvLedInfoPtr and duplicates it. A deep copy is made, |
| 639 | * thus the new copy behaves like the original one and can be freed with |
| 640 | * XkbFreeSrvLedInfo. |
| 641 | */ |
| 642 | XkbSrvLedInfoPtr |
| 643 | XkbCopySrvLedInfo(DeviceIntPtr from, |
| 644 | XkbSrvLedInfoPtr src, KbdFeedbackPtr kf, LedFeedbackPtr lf) |
| 645 | { |
| 646 | XkbSrvLedInfoPtr sli_new = NULL; |
| 647 | |
| 648 | if (!src) |
| 649 | goto finish; |
| 650 | |
| 651 | sli_new = calloc(1, sizeof(XkbSrvLedInfoRec)); |
| 652 | if (!sli_new) |
| 653 | goto finish; |
| 654 | |
| 655 | memcpy(sli_new, src, sizeof(XkbSrvLedInfoRec)); |
| 656 | if (sli_new->class == KbdFeedbackClass) |
| 657 | sli_new->fb.kf = kf; |
| 658 | else |
| 659 | sli_new->fb.lf = lf; |
| 660 | |
| 661 | if (!(sli_new->flags & XkbSLI_IsDefault)) { |
| 662 | sli_new->names = calloc(XkbNumIndicators, sizeof(Atom)); |
| 663 | sli_new->maps = calloc(XkbNumIndicators, sizeof(XkbIndicatorMapRec)); |
| 664 | } /* else sli_new->names/maps is pointing to |
| 665 | dev->key->xkbInfo->desc->names->indicators; |
| 666 | dev->key->xkbInfo->desc->names->indicators; */ |
| 667 | |
| 668 | finish: |
| 669 | return sli_new; |
| 670 | } |
| 671 | |
| 672 | /***====================================================================***/ |
| 673 | |
| 674 | /* |
| 675 | * XkbSrvLedInfoPtr |
| 676 | * XkbFindSrvLedInfo(dev,class,id,needed_parts) |
| 677 | * |
| 678 | * Finds the XkbSrvLedInfoPtr for the specified 'class' and 'id' |
| 679 | * on the device specified by 'dev.' If the class and id specify |
| 680 | * a valid device feedback, this function returns the existing |
| 681 | * feedback or allocates a new one. |
| 682 | * |
| 683 | */ |
| 684 | |
| 685 | XkbSrvLedInfoPtr |
| 686 | XkbFindSrvLedInfo(DeviceIntPtr dev, |
| 687 | unsigned class, unsigned id, unsigned needed_parts) |
| 688 | { |
| 689 | XkbSrvLedInfoPtr sli; |
| 690 | |
| 691 | /* optimization to check for most common case */ |
| 692 | if (((class == XkbDfltXIClass) && (id == XkbDfltXIId)) && (dev->kbdfeed)) { |
| 693 | if (dev->kbdfeed->xkb_sli == NULL) { |
| 694 | dev->kbdfeed->xkb_sli = |
| 695 | XkbAllocSrvLedInfo(dev, dev->kbdfeed, NULL, needed_parts); |
| 696 | } |
| 697 | return dev->kbdfeed->xkb_sli; |
| 698 | } |
| 699 | |
| 700 | sli = NULL; |
| 701 | if (class == XkbDfltXIClass) { |
| 702 | if (dev->kbdfeed) |
| 703 | class = KbdFeedbackClass; |
| 704 | else if (dev->leds) |
| 705 | class = LedFeedbackClass; |
| 706 | else |
| 707 | return NULL; |
| 708 | } |
| 709 | if (class == KbdFeedbackClass) { |
| 710 | KbdFeedbackPtr kf; |
| 711 | |
| 712 | for (kf = dev->kbdfeed; kf != NULL; kf = kf->next) { |
| 713 | if ((id == XkbDfltXIId) || (id == kf->ctrl.id)) { |
| 714 | if (kf->xkb_sli == NULL) |
| 715 | kf->xkb_sli = |
| 716 | XkbAllocSrvLedInfo(dev, kf, NULL, needed_parts); |
| 717 | sli = kf->xkb_sli; |
| 718 | break; |
| 719 | } |
| 720 | } |
| 721 | } |
| 722 | else if (class == LedFeedbackClass) { |
| 723 | LedFeedbackPtr lf; |
| 724 | |
| 725 | for (lf = dev->leds; lf != NULL; lf = lf->next) { |
| 726 | if ((id == XkbDfltXIId) || (id == lf->ctrl.id)) { |
| 727 | if (lf->xkb_sli == NULL) |
| 728 | lf->xkb_sli = |
| 729 | XkbAllocSrvLedInfo(dev, NULL, lf, needed_parts); |
| 730 | sli = lf->xkb_sli; |
| 731 | break; |
| 732 | } |
| 733 | } |
| 734 | } |
| 735 | if (sli) { |
| 736 | if ((sli->names == NULL) && (needed_parts & XkbXI_IndicatorNamesMask)) |
| 737 | sli->names = calloc(XkbNumIndicators, sizeof(Atom)); |
| 738 | if ((sli->maps == NULL) && (needed_parts & XkbXI_IndicatorMapsMask)) |
| 739 | sli->maps = calloc(XkbNumIndicators, sizeof(XkbIndicatorMapRec)); |
| 740 | } |
| 741 | return sli; |
| 742 | } |
| 743 | |
| 744 | /***====================================================================***/ |
| 745 | |
| 746 | void |
| 747 | XkbFlushLedEvents(DeviceIntPtr dev, |
| 748 | DeviceIntPtr kbd, |
| 749 | XkbSrvLedInfoPtr sli, |
| 750 | xkbExtensionDeviceNotify * ed, |
| 751 | XkbChangesPtr changes, XkbEventCausePtr cause) |
| 752 | { |
| 753 | if (changes) { |
| 754 | if (changes->indicators.state_changes) |
| 755 | XkbDDXUpdateDeviceIndicators(dev, sli, sli->effectiveState); |
| 756 | XkbSendNotification(kbd, changes, cause); |
| 757 | memset((char *) changes, 0, sizeof(XkbChangesRec)); |
| 758 | |
| 759 | if (XkbAX_NeedFeedback |
| 760 | (kbd->key->xkbInfo->desc->ctrls, XkbAX_IndicatorFBMask)) { |
| 761 | if (sli->effectiveState) |
| 762 | /* it appears that the which parameter is not used */ |
| 763 | XkbDDXAccessXBeep(dev, _BEEP_LED_ON, XkbAccessXFeedbackMask); |
| 764 | else |
| 765 | XkbDDXAccessXBeep(dev, _BEEP_LED_OFF, XkbAccessXFeedbackMask); |
| 766 | } |
| 767 | } |
| 768 | if (ed) { |
| 769 | if (ed->reason) { |
| 770 | if ((dev != kbd) && (ed->reason & XkbXI_IndicatorStateMask)) |
| 771 | XkbDDXUpdateDeviceIndicators(dev, sli, sli->effectiveState); |
| 772 | XkbSendExtensionDeviceNotify(dev, cause->client, ed); |
| 773 | } |
| 774 | memset((char *) ed, 0, sizeof(XkbExtensionDeviceNotify)); |
| 775 | } |
| 776 | return; |
| 777 | } |
| 778 | |
| 779 | /***====================================================================***/ |
| 780 | |
| 781 | void |
| 782 | XkbApplyLedNameChanges(DeviceIntPtr dev, |
| 783 | XkbSrvLedInfoPtr sli, |
| 784 | unsigned changed_names, |
| 785 | xkbExtensionDeviceNotify * ed, |
| 786 | XkbChangesPtr changes, XkbEventCausePtr cause) |
| 787 | { |
| 788 | DeviceIntPtr kbd; |
| 789 | XkbChangesRec my_changes; |
| 790 | xkbExtensionDeviceNotify my_ed; |
| 791 | |
| 792 | if (changed_names == 0) |
| 793 | return; |
| 794 | if (dev->key && dev->key->xkbInfo) |
| 795 | kbd = dev; |
| 796 | else |
| 797 | kbd = inputInfo.keyboard; |
| 798 | |
| 799 | if (ed == NULL) { |
| 800 | ed = &my_ed; |
| 801 | memset((char *) ed, 0, sizeof(xkbExtensionDeviceNotify)); |
| 802 | } |
| 803 | else if ((ed->reason & XkbXI_IndicatorsMask) && |
| 804 | ((ed->ledClass != sli->class) || (ed->ledID != sli->id))) { |
| 805 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 806 | } |
| 807 | |
| 808 | if ((kbd == dev) && (sli->flags & XkbSLI_IsDefault)) { |
| 809 | if (changes == NULL) { |
| 810 | changes = &my_changes; |
| 811 | memset((char *) changes, 0, sizeof(XkbChangesRec)); |
| 812 | } |
| 813 | changes->names.changed |= XkbIndicatorNamesMask; |
| 814 | changes->names.changed_indicators |= changed_names; |
| 815 | } |
| 816 | |
| 817 | ed->reason |= XkbXI_IndicatorNamesMask; |
| 818 | ed->ledClass = sli->class; |
| 819 | ed->ledID = sli->id; |
| 820 | ed->ledsDefined = sli->namesPresent | sli->mapsPresent; |
| 821 | ed->ledState = sli->effectiveState; |
| 822 | ed->unsupported = 0; |
| 823 | ed->supported = XkbXI_AllFeaturesMask; |
| 824 | |
| 825 | if (changes != &my_changes) |
| 826 | changes = NULL; |
| 827 | if (ed != &my_ed) |
| 828 | ed = NULL; |
| 829 | if (changes || ed) |
| 830 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 831 | return; |
| 832 | } |
| 833 | |
| 834 | /***====================================================================***/ |
| 835 | |
| 836 | /* |
| 837 | * void |
| 838 | * XkbApplyLedMapChanges(dev,sli,changed_maps,changes,cause) |
| 839 | * |
| 840 | * Handles all of the secondary effects of the changes to the |
| 841 | * feedback specified by 'sli' on the device specified by 'dev.' |
| 842 | * |
| 843 | * If 'changed_maps' specifies any indicators, this function generates |
| 844 | * XkbExtensionDeviceNotify events and possibly IndicatorMapNotify |
| 845 | * events to report the changes, and recalculates the effective |
| 846 | * state of each indicator with a changed map. If any indicators |
| 847 | * change state, the server generates XkbExtensionDeviceNotify and |
| 848 | * XkbIndicatorStateNotify events as appropriate. |
| 849 | * |
| 850 | * If 'changes' is non-NULL, this function updates it to reflect |
| 851 | * any changes to the keyboard state or controls or to the 'core' |
| 852 | * indicator names, maps, or state. If 'changes' is NULL, this |
| 853 | * function generates XKB events as needed to report the changes. |
| 854 | * If 'dev' is not a keyboard device, any changes are reported |
| 855 | * for the core keyboard. |
| 856 | * |
| 857 | * The 'cause' specifies the reason for the event (key event or |
| 858 | * request) for the change, as reported in some XKB events. |
| 859 | */ |
| 860 | |
| 861 | void |
| 862 | XkbApplyLedMapChanges(DeviceIntPtr dev, |
| 863 | XkbSrvLedInfoPtr sli, |
| 864 | unsigned changed_maps, |
| 865 | xkbExtensionDeviceNotify * ed, |
| 866 | XkbChangesPtr changes, XkbEventCausePtr cause) |
| 867 | { |
| 868 | DeviceIntPtr kbd; |
| 869 | XkbChangesRec my_changes; |
| 870 | xkbExtensionDeviceNotify my_ed; |
| 871 | |
| 872 | if (changed_maps == 0) |
| 873 | return; |
| 874 | if (dev->key && dev->key->xkbInfo) |
| 875 | kbd = dev; |
| 876 | else |
| 877 | kbd = inputInfo.keyboard; |
| 878 | |
| 879 | if (ed == NULL) { |
| 880 | ed = &my_ed; |
| 881 | memset((char *) ed, 0, sizeof(xkbExtensionDeviceNotify)); |
| 882 | } |
| 883 | else if ((ed->reason & XkbXI_IndicatorsMask) && |
| 884 | ((ed->ledClass != sli->class) || (ed->ledID != sli->id))) { |
| 885 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 886 | } |
| 887 | |
| 888 | if ((kbd == dev) && (sli->flags & XkbSLI_IsDefault)) { |
| 889 | if (changes == NULL) { |
| 890 | changes = &my_changes; |
| 891 | memset((char *) changes, 0, sizeof(XkbChangesRec)); |
| 892 | } |
| 893 | changes->indicators.map_changes |= changed_maps; |
| 894 | } |
| 895 | |
| 896 | XkbCheckIndicatorMaps(dev, sli, changed_maps); |
| 897 | |
| 898 | ed->reason |= XkbXI_IndicatorMapsMask; |
| 899 | ed->ledClass = sli->class; |
| 900 | ed->ledID = sli->id; |
| 901 | ed->ledsDefined = sli->namesPresent | sli->mapsPresent; |
| 902 | ed->ledState = sli->effectiveState; |
| 903 | ed->unsupported = 0; |
| 904 | ed->supported = XkbXI_AllFeaturesMask; |
| 905 | |
| 906 | XkbUpdateLedAutoState(dev, sli, changed_maps, ed, changes, cause); |
| 907 | |
| 908 | if (changes != &my_changes) |
| 909 | changes = NULL; |
| 910 | if (ed != &my_ed) |
| 911 | ed = NULL; |
| 912 | if (changes || ed) |
| 913 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 914 | return; |
| 915 | } |
| 916 | |
| 917 | /***====================================================================***/ |
| 918 | |
| 919 | void |
| 920 | XkbApplyLedStateChanges(DeviceIntPtr dev, |
| 921 | XkbSrvLedInfoPtr sli, |
| 922 | unsigned changed_leds, |
| 923 | xkbExtensionDeviceNotify * ed, |
| 924 | XkbChangesPtr changes, XkbEventCausePtr cause) |
| 925 | { |
| 926 | XkbSrvInfoPtr xkbi; |
| 927 | DeviceIntPtr kbd; |
| 928 | XkbChangesRec my_changes; |
| 929 | xkbExtensionDeviceNotify my_ed; |
| 930 | register unsigned i, bit, affected; |
| 931 | XkbIndicatorMapPtr map; |
| 932 | unsigned oldState; |
| 933 | Bool kb_changed; |
| 934 | |
| 935 | if (changed_leds == 0) |
| 936 | return; |
| 937 | if (dev->key && dev->key->xkbInfo) |
| 938 | kbd = dev; |
| 939 | else |
| 940 | kbd = inputInfo.keyboard; |
| 941 | xkbi = kbd->key->xkbInfo; |
| 942 | |
| 943 | if (changes == NULL) { |
| 944 | changes = &my_changes; |
| 945 | memset((char *) changes, 0, sizeof(XkbChangesRec)); |
| 946 | } |
| 947 | |
| 948 | kb_changed = FALSE; |
| 949 | affected = changed_leds; |
| 950 | oldState = sli->effectiveState; |
| 951 | for (i = 0, bit = 1; (i < XkbNumIndicators) && (affected); i++, bit <<= 1) { |
| 952 | if ((affected & bit) == 0) |
| 953 | continue; |
| 954 | affected &= ~bit; |
| 955 | map = &sli->maps[i]; |
| 956 | if (map->flags & XkbIM_NoExplicit) { |
| 957 | sli->explicitState &= ~bit; |
| 958 | continue; |
| 959 | } |
| 960 | if (map->flags & XkbIM_LEDDrivesKB) { |
| 961 | Bool on = ((sli->explicitState & bit) != 0); |
| 962 | |
| 963 | if (XkbApplyLEDChangeToKeyboard(xkbi, map, on, changes)) |
| 964 | kb_changed = TRUE; |
| 965 | } |
| 966 | } |
| 967 | sli->effectiveState = (sli->autoState | sli->explicitState); |
| 968 | affected = sli->effectiveState ^ oldState; |
| 969 | |
| 970 | if (ed == NULL) { |
| 971 | ed = &my_ed; |
| 972 | memset((char *) ed, 0, sizeof(xkbExtensionDeviceNotify)); |
| 973 | } |
| 974 | else if (affected && (ed->reason & XkbXI_IndicatorsMask) && |
| 975 | ((ed->ledClass != sli->class) || (ed->ledID != sli->id))) { |
| 976 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 977 | } |
| 978 | |
| 979 | if ((kbd == dev) && (sli->flags & XkbSLI_IsDefault)) |
| 980 | changes->indicators.state_changes |= affected; |
| 981 | if (affected) { |
| 982 | ed->reason |= XkbXI_IndicatorStateMask; |
| 983 | ed->ledClass = sli->class; |
| 984 | ed->ledID = sli->id; |
| 985 | ed->ledsDefined = sli->namesPresent | sli->mapsPresent; |
| 986 | ed->ledState = sli->effectiveState; |
| 987 | ed->unsupported = 0; |
| 988 | ed->supported = XkbXI_AllFeaturesMask; |
| 989 | } |
| 990 | |
| 991 | if (kb_changed) { |
| 992 | XkbComputeDerivedState(kbd->key->xkbInfo); |
| 993 | XkbUpdateLedAutoState(dev, sli, sli->mapsPresent, ed, changes, cause); |
| 994 | } |
| 995 | |
| 996 | if (changes != &my_changes) |
| 997 | changes = NULL; |
| 998 | if (ed != &my_ed) |
| 999 | ed = NULL; |
| 1000 | if (changes || ed) |
| 1001 | XkbFlushLedEvents(dev, kbd, sli, ed, changes, cause); |
| 1002 | if (kb_changed) |
| 1003 | XkbUpdateAllDeviceIndicators(NULL, cause); |
| 1004 | return; |
| 1005 | } |