1/************************************************************
2Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc.
3
4Permission to use, copy, modify, and distribute this
5software and its documentation for any purpose and without
6fee is hereby granted, provided that the above copyright
7notice appear in all copies and that both that copyright
8notice and this permission notice appear in supporting
9documentation, and that the name of Silicon Graphics not be
10used in advertising or publicity pertaining to distribution
11of the software without specific prior written permission.
12Silicon Graphics makes no representation about the suitability
13of this software for any purpose. It is provided "as is"
14without any express or implied warranty.
15
16SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23THE 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
55unsigned
56XkbIndicatorsToUpdate(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         */
95static Bool
96XkbApplyLEDChangeToKeyboard(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
190static Bool
191ComputeAutoState(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
230static void
231XkbUpdateLedAutoState(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
307void
308XkbUpdateAllDeviceIndicators(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
366void
367XkbSetIndicators(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
421void
422XkbUpdateIndicators(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
451void
452XkbCheckIndicatorMaps(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         */
525XkbSrvLedInfoPtr
526XkbAllocSrvLedInfo(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
621void
622XkbFreeSrvLedInfo(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 */
642XkbSrvLedInfoPtr
643XkbCopySrvLedInfo(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
685XkbSrvLedInfoPtr
686XkbFindSrvLedInfo(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
746void
747XkbFlushLedEvents(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
781void
782XkbApplyLedNameChanges(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
861void
862XkbApplyLedMapChanges(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
919void
920XkbApplyLedStateChanges(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}
1006