Rename XKBcommonint.h to xkb-priv.h and use it
[platform/upstream/libxkbcommon.git] / src / state.c
1 /************************************************************
2 Copyright (c) 1993 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 /*
28  * Copyright © 2012 Intel Corporation
29  *
30  * Permission is hereby granted, free of charge, to any person obtaining a
31  * copy of this software and associated documentation files (the "Software"),
32  * to deal in the Software without restriction, including without limitation
33  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34  * and/or sell copies of the Software, and to permit persons to whom the
35  * Software is furnished to do so, subject to the following conditions:
36  *
37  * The above copyright notice and this permission notice (including the next
38  * paragraph) shall be included in all copies or substantial portions of the
39  * Software.
40  *
41  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47  * DEALINGS IN THE SOFTWARE.
48  *
49  * Author: Daniel Stone <daniel@fooishbar.org>
50  */
51
52 /*
53  * This is a bastardised version of xkbActions.c from the X server which
54  * does not support, for the moment:
55  *   - AccessX sticky/debounce/etc (will come later)
56  *   - pointer keys (may come later)
57  *   - key redirects (unlikely)
58  *   - messages (very unlikely)
59  */
60
61 #include <assert.h>
62
63 #include "xkb-priv.h"
64
65 struct xkb_filter {
66     struct xkb_state *state;
67     union xkb_action action;
68     xkb_keycode_t keycode;
69     uint32_t priv;
70     int (*func)(struct xkb_filter *filter, xkb_keycode_t key,
71                 enum xkb_key_direction direction);
72     int refcnt;
73     struct xkb_filter *next;
74 };
75
76 static union xkb_action *
77 xkb_key_get_action(struct xkb_state *state, xkb_keycode_t key)
78 {
79     int group, level;
80
81     if (!XkbKeyHasActions(state->xkb, key) ||
82         !XkbKeycodeInRange(state->xkb, key)) {
83         static union xkb_action fake;
84         memset(&fake, 0, sizeof(fake));
85         fake.type = XkbSA_NoAction;
86         return &fake;
87     }
88
89     group = xkb_key_get_group(state, key);
90     level = xkb_key_get_level(state, key, group);
91
92     return XkbKeyActionEntry(state->xkb, key, level, group);
93 }
94
95 static struct xkb_filter *
96 xkb_filter_new(struct xkb_state *state)
97 {
98     int i;
99     int old_size = state->num_filters;
100     struct xkb_filter *filters = state->filters;
101
102     for (i = 0; i < state->num_filters; i++) {
103         if (filters[i].func)
104             continue;
105         filters[i].state = state;
106         filters[i].refcnt = 1;
107         return &filters[i];
108     }
109
110     if (state->num_filters > 0)
111         state->num_filters *= 2;
112     else
113         state->num_filters = 4;
114     filters = realloc(filters, state->num_filters * sizeof(*filters));
115     if (!filters) { /* WSGO */
116         state->num_filters = old_size;
117         return NULL;
118     }
119     state->filters = filters;
120     memset(&filters[old_size], 0,
121            (state->num_filters - old_size) * sizeof(*filters));
122
123     filters[old_size].state = state;
124     filters[old_size].refcnt = 1;
125
126     return &filters[old_size];
127 }
128
129 /***====================================================================***/
130
131 static int
132 xkb_filter_group_set_func(struct xkb_filter *filter, xkb_keycode_t keycode,
133                           enum xkb_key_direction direction)
134 {
135     if (keycode != filter->keycode) {
136         filter->action.group.flags &= ~XkbSA_ClearLocks;
137         return 1;
138     }
139
140     if (direction == XKB_KEY_DOWN) {
141         filter->refcnt++;
142         return 0;
143     }
144     else if (--filter->refcnt > 0) {
145         return 0;
146     }
147
148     if (filter->action.group.flags & XkbSA_GroupAbsolute)
149         filter->state->base_group = filter->action.group.group;
150     else
151         filter->state->base_group = -filter->action.group.group;
152     if (filter->action.group.flags & XkbSA_ClearLocks)
153         filter->state->locked_group = 0;
154
155     filter->func = NULL;
156
157     return 1;
158 }
159
160 static int
161 xkb_filter_group_set_new(struct xkb_state *state, xkb_keycode_t keycode,
162                          union xkb_action *action)
163 {
164     struct xkb_filter *filter = xkb_filter_new(state);
165
166     if (!filter) /* WSGO */
167         return -1;
168     filter->keycode = keycode;
169     filter->func = xkb_filter_group_set_func;
170     filter->action = *action;
171
172     if (action->group.flags & XkbSA_GroupAbsolute) {
173         filter->action.group.group = filter->state->base_group;
174         filter->state->base_group = action->group.group;
175     }
176     else {
177         filter->state->base_group += action->group.group;
178     }
179
180     return 1;
181 }
182
183 static int
184 xkb_filter_group_lock_func(struct xkb_filter *filter, xkb_keycode_t keycode,
185                            enum xkb_key_direction direction)
186 {
187     if (keycode != filter->keycode)
188         return 1;
189
190     if (direction == XKB_KEY_DOWN) {
191         filter->refcnt++;
192         return 0;
193     }
194     if (--filter->refcnt > 0)
195         return 0;
196
197     filter->func = NULL;
198     return 1;
199 }
200
201 static int
202 xkb_filter_group_lock_new(struct xkb_state *state, xkb_keycode_t keycode,
203                           union xkb_action *action)
204 {
205     struct xkb_filter *filter = xkb_filter_new(state);
206
207     if (!filter)
208         return 0;
209
210     filter->keycode = keycode;
211     filter->func = xkb_filter_group_lock_func;
212     filter->action = *action;
213
214     if (action->group.flags & XkbSA_GroupAbsolute)
215         filter->state->locked_group = action->group.group;
216     else
217         filter->state->locked_group += action->group.group;
218
219     return 1;
220 }
221
222 static int
223 xkb_filter_mod_set_func(struct xkb_filter *filter, xkb_keycode_t keycode,
224                         enum xkb_key_direction direction)
225 {
226     if (keycode != filter->keycode) {
227         filter->action.mods.flags &= ~XkbSA_ClearLocks;
228         return 1;
229     }
230
231     if (direction == XKB_KEY_DOWN) {
232         filter->refcnt++;
233         return 0;
234     }
235     else if (--filter->refcnt > 0) {
236         return 0;
237     }
238
239     filter->state->base_mods &= ~(filter->action.mods.mask);
240     if (filter->action.mods.flags & XkbSA_ClearLocks)
241         filter->state->locked_mods &= ~filter->action.mods.mask;
242
243     filter->func = NULL;
244
245     return 1;
246 }
247
248 static int
249 xkb_filter_mod_set_new(struct xkb_state *state, xkb_keycode_t keycode,
250                        union xkb_action *action)
251 {
252     struct xkb_filter *filter = xkb_filter_new(state);
253
254     if (!filter) /* WSGO */
255         return -1;
256     filter->keycode = keycode;
257     filter->func = xkb_filter_mod_set_func;
258     filter->action = *action;
259
260     filter->state->base_mods |= action->mods.mask;
261
262     return 1;
263 }
264
265 static int
266 xkb_filter_mod_lock_func(struct xkb_filter *filter, xkb_keycode_t keycode,
267                          enum xkb_key_direction direction)
268 {
269     if (keycode != filter->keycode)
270         return 1;
271
272     if (direction == XKB_KEY_DOWN) {
273         filter->refcnt++;
274         return 0;
275     }
276     if (--filter->refcnt > 0)
277         return 0;
278
279     filter->state->locked_mods &= ~filter->priv;
280     filter->func = NULL;
281     return 1;
282 }
283
284 static int
285 xkb_filter_mod_lock_new(struct xkb_state *state, xkb_keycode_t keycode,
286                         union xkb_action *action)
287 {
288     struct xkb_filter *filter = xkb_filter_new(state);
289
290     if (!filter) /* WSGO */
291         return 0;
292
293     filter->keycode = keycode;
294     filter->func = xkb_filter_mod_lock_func;
295     filter->action = *action;
296     filter->priv = state->locked_mods & action->mods.mask;
297     state->locked_mods |= action->mods.mask;
298
299     return 1;
300 }
301
302 enum xkb_key_latch_state {
303     NO_LATCH,
304     LATCH_KEY_DOWN,
305     LATCH_PENDING,
306 };
307
308 static int
309 xkb_filter_mod_latch_func(struct xkb_filter *filter, xkb_keycode_t keycode,
310                           enum xkb_key_direction direction)
311 {
312     enum xkb_key_latch_state latch = filter->priv;
313
314     if (direction == XKB_KEY_DOWN && latch == LATCH_PENDING) {
315         /* If this is a new keypress and we're awaiting our single latched
316          * keypress, then either break the latch if any random key is pressed,
317          * or promote it to a lock or plain base set if it's the same
318          * modifier. */
319         union xkb_action *action = xkb_key_get_action(filter->state, keycode);
320         if (action->type == XkbSA_LatchMods &&
321             action->mods.flags == filter->action.mods.flags &&
322             action->mods.mask == filter->action.mods.mask) {
323             filter->action = *action;
324             if (filter->action.mods.flags & XkbSA_LatchToLock) {
325                 filter->action.type = XkbSA_LockMods;
326                 filter->func = xkb_filter_mod_lock_func;
327                 filter->state->locked_mods |= filter->action.mods.mask;
328             }
329             else {
330                 filter->action.type = XkbSA_SetMods;
331                 filter->func = xkb_filter_mod_set_func;
332                 filter->state->base_mods |= filter->action.mods.mask;
333             }
334             filter->keycode = keycode;
335             filter->state->latched_mods &= ~filter->action.mods.mask;
336             /* XXX beep beep! */
337             return 0;
338         }
339         else if (((1 << action->type) & XkbSA_BreakLatch)) {
340             /* XXX: This may be totally broken, we might need to break the
341              *      latch in the next run after this press? */
342             filter->state->latched_mods &= ~filter->action.mods.mask;
343             filter->func = NULL;
344             return 1;
345         }
346     }
347     else if (direction == XKB_KEY_UP && keycode == filter->keycode) {
348         /* Our key got released.  If we've set it to clear locks, and we
349          * currently have the same modifiers locked, then release them and
350          * don't actually latch.  Else we've actually hit the latching
351          * stage, so set PENDING and move our modifier from base to
352          * latched. */
353         if (latch == NO_LATCH ||
354             ((filter->action.mods.flags & XkbSA_ClearLocks) &&
355              (filter->state->locked_mods & filter->action.mods.mask) ==
356              filter->action.mods.mask)) {
357             /* XXX: We might be a bit overenthusiastic about clearing
358              *      mods other filters have set here? */
359             if (latch == LATCH_PENDING)
360                 filter->state->latched_mods &= ~filter->action.mods.mask;
361             else
362                 filter->state->base_mods &= ~filter->action.mods.mask;
363             filter->state->locked_mods &= ~filter->action.mods.mask;
364             filter->func = NULL;
365         }
366         else {
367             latch = LATCH_PENDING;
368             filter->state->base_mods &= ~filter->action.mods.mask;
369             filter->state->latched_mods |= filter->action.mods.mask;
370             /* XXX beep beep! */
371         }
372     }
373     else if (direction == XKB_KEY_DOWN && latch == LATCH_KEY_DOWN) {
374         /* Someone's pressed another key while we've still got the latching
375          * key held down, so keep the base modifier state active (from
376          * xkb_filter_mod_latch_new), but don't trip the latch, just clear
377          * it as soon as the modifier gets released. */
378         latch = NO_LATCH;
379     }
380
381     filter->priv = latch;
382
383     return 1;
384 }
385
386 static int
387 xkb_filter_mod_latch_new(struct xkb_state *state, xkb_keycode_t keycode,
388                          union xkb_action *action)
389 {
390     struct xkb_filter *filter = xkb_filter_new(state);
391     enum xkb_key_latch_state latch = LATCH_KEY_DOWN;
392
393     if (!filter) /* WSGO */
394         return -1;
395     filter->keycode = keycode;
396     filter->priv = latch;
397     filter->func = xkb_filter_mod_latch_func;
398     filter->action = *action;
399
400     filter->state->base_mods |= action->mods.mask;
401
402     return 1;
403 }
404
405 /**
406  * Applies any relevant filters to the key, first from the list of filters
407  * that are currently active, then if no filter has claimed the key, possibly
408  * apply a new filter from the key action.
409  */
410 static void
411 xkb_filter_apply_all(struct xkb_state *state, xkb_keycode_t key,
412                      enum xkb_key_direction direction)
413 {
414     struct xkb_filter *filters = state->filters;
415     union xkb_action *act = NULL;
416     int send = 1;
417     int i;
418
419     /* First run through all the currently active filters and see if any of
420      * them have claimed this event. */
421     for (i = 0; i < state->num_filters; i++) {
422         if (!filters[i].func)
423             continue;
424         send &= (*filters[i].func)(&filters[i], key, direction);
425     }
426
427     if (!send || direction == XKB_KEY_UP)
428         return;
429
430     act = xkb_key_get_action(state, key);
431     switch (act->type) {
432     case XkbSA_SetMods:
433         send = xkb_filter_mod_set_new(state, key, act);
434         break;
435     case XkbSA_LatchMods:
436         send = xkb_filter_mod_latch_new(state, key, act);
437         break;
438     case XkbSA_LockMods:
439         send = xkb_filter_mod_lock_new(state, key, act);
440         break;
441     case XkbSA_SetGroup:
442         send = xkb_filter_group_set_new(state, key, act);
443         break;
444 #if 0
445     case XkbSA_LatchGroup:
446         send = xkb_filter_mod_latch_new(state, key, act);
447         break;
448 #endif
449     case XkbSA_LockGroup:
450         send = xkb_filter_group_lock_new(state, key, act);
451         break;
452     }
453
454     return;
455 }
456
457 _X_EXPORT struct xkb_state *
458 xkb_state_new(struct xkb_keymap *xkb)
459 {
460     struct xkb_state *ret;
461
462     if (!xkb)
463         return NULL;
464
465     ret = calloc(sizeof(*ret), 1);
466     if (!ret)
467         return NULL;
468
469     ret->refcnt = 1;
470     ret->xkb = xkb_map_ref(xkb);
471
472     return ret;
473 }
474
475 _X_EXPORT struct xkb_state *
476 xkb_state_ref(struct xkb_state *state)
477 {
478     state->refcnt++;
479     return state;
480 }
481
482 _X_EXPORT void
483 xkb_state_unref(struct xkb_state *state)
484 {
485     state->refcnt--;
486     assert(state->refcnt >= 0);
487     if (state->refcnt > 0)
488         return;
489
490     xkb_map_unref(state->xkb);
491     free(state->filters);
492     free(state);
493 }
494
495 _X_EXPORT struct xkb_keymap *
496 xkb_state_get_map(struct xkb_state *state)
497 {
498     return state->xkb;
499 }
500
501 /**
502  * Update the LED state to match the rest of the xkb_state.
503  */
504 static void
505 xkb_state_led_update_all(struct xkb_state *state)
506 {
507     xkb_led_index_t led;
508
509     state->leds = 0;
510
511     for (led = 0; led < XkbNumIndicators; led++) {
512         struct xkb_indicator_map *map = &state->xkb->indicators->maps[led];
513         uint32_t mod_mask = 0;
514         uint32_t group_mask = 0;
515
516         if (!map->which_mods && !map->which_groups && !map->ctrls)
517             continue;
518
519         if (map->which_mods) {
520             if (map->which_mods & XkbIM_UseBase)
521                 mod_mask |= state->base_mods;
522             if (map->which_mods & XkbIM_UseLatched)
523                 mod_mask |= state->latched_mods;
524             if (map->which_mods & XkbIM_UseLocked)
525                 mod_mask |= state->locked_mods;
526             if (map->which_mods & XkbIM_UseEffective)
527                 mod_mask |= state->mods;
528             if ((map->mods.mask & mod_mask))
529                 state->leds |= (1 << led);
530         }
531         else if (map->which_groups) {
532             if (map->which_mods & XkbIM_UseBase)
533                 group_mask |= (1 << state->base_group);
534             if (map->which_mods & XkbIM_UseLatched)
535                 group_mask |= (1 << state->latched_group);
536             if (map->which_mods & XkbIM_UseLocked)
537                 group_mask |= (1 << state->locked_group);
538             if (map->which_mods & XkbIM_UseEffective)
539                 group_mask |= (1 << state->group);
540             if ((map->groups & group_mask))
541                 state->leds |= (1 << led);
542         }
543         else if (map->ctrls) {
544             if ((map->ctrls & state->xkb->ctrls->enabled_ctrls))
545                 state->leds |= (1 << led);
546         }
547     }
548 }
549
550 /**
551  * Calculates the derived state (effective mods/group and LEDs) from an
552  * up-to-date xkb_state.
553  */
554 static void
555 xkb_state_update_derived(struct xkb_state *state)
556 {
557     state->mods = (state->base_mods | state->latched_mods | state->locked_mods);
558     /* FIXME: Clamp/wrap locked_group */
559     state->group = state->locked_group + state->base_group +
560                    state->latched_group;
561     /* FIXME: Clamp/wrap effective group */
562
563     xkb_state_led_update_all(state);
564 }
565
566 /**
567  * Given a particular key event, updates the state structure to reflect the
568  * new modifiers.
569  */
570 _X_EXPORT void
571 xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key,
572                      enum xkb_key_direction direction)
573 {
574     xkb_filter_apply_all(state, key, direction);
575     xkb_state_update_derived(state);
576 }
577
578 /**
579  * Updates the state from a set of explicit masks as gained from
580  * xkb_state_serialise_mods and xkb_state_serialise_groups.  As noted in the
581  * documentation for these functions in xkbcommon.h, this round-trip is
582  * lossy, and should only be used to update a slave state mirroring the
583  * master, e.g. in a client/server window system.
584  */
585 _X_EXPORT void
586 xkb_state_update_mask(struct xkb_state *state,
587                       xkb_mod_mask_t base_mods,
588                       xkb_mod_mask_t latched_mods,
589                       xkb_mod_mask_t locked_mods,
590                       xkb_group_index_t base_group,
591                       xkb_group_index_t latched_group,
592                       xkb_group_index_t locked_group)
593 {
594     xkb_mod_mask_t mod;
595
596     state->base_mods = 0;
597     state->latched_mods = 0;
598     state->locked_mods = 0;
599     for (mod = 0; mod < xkb_map_num_mods(state->xkb); mod++) {
600         xkb_mod_mask_t idx = (1 << mod);
601         if (base_mods & idx)
602             state->base_mods |= idx;
603         if (latched_mods & idx)
604             state->latched_mods |= idx;
605         if (locked_mods & idx)
606             state->locked_mods |= idx;
607     }
608
609     state->base_group = base_group;
610     state->latched_group = latched_group;
611     state->locked_group = locked_group;
612
613     xkb_state_update_derived(state);
614 }
615
616 /**
617  * Serialises the requested modifier state into an xkb_mod_mask_t, with all
618  * the same disclaimers as in xkb_state_update_mask.
619  */
620 _X_EXPORT xkb_mod_mask_t
621 xkb_state_serialise_mods(struct xkb_state *state,
622                          enum xkb_state_component type)
623 {
624     xkb_mod_mask_t ret = 0;
625
626     if (type == XKB_STATE_EFFECTIVE)
627         return state->mods;
628
629     if (type & XKB_STATE_DEPRESSED)
630         ret |= state->base_mods;
631     if (type & XKB_STATE_LATCHED)
632         ret |= state->latched_mods;
633     if (type & XKB_STATE_LOCKED)
634         ret |= state->locked_mods;
635
636     return ret;
637 }
638
639 /**
640  * Serialises the requested group state, with all the same disclaimers as
641  * in xkb_state_update_mask.
642  */
643 _X_EXPORT xkb_group_index_t
644 xkb_state_serialise_group(struct xkb_state *state,
645                           enum xkb_state_component type)
646 {
647     xkb_group_index_t ret = 0;
648
649     if (type == XKB_STATE_EFFECTIVE)
650         return state->group;
651
652     if (type & XKB_STATE_DEPRESSED)
653         ret += state->base_group;
654     if (type & XKB_STATE_LATCHED)
655         ret += state->latched_group;
656     if (type & XKB_STATE_LOCKED)
657         ret += state->locked_group;
658
659     return ret;
660 }
661
662 /**
663  * Returns 1 if the given modifier is active with the specified type(s), 0 if
664  * not, or -1 if the modifier is invalid.
665  */
666 _X_EXPORT int
667 xkb_state_mod_index_is_active(struct xkb_state *state,
668                               xkb_mod_index_t idx,
669                               enum xkb_state_component type)
670 {
671     int ret = 0;
672
673     if (idx >= xkb_map_num_mods(state->xkb))
674         return -1;
675
676     if (type & XKB_STATE_DEPRESSED)
677         ret |= (state->base_mods & (1 << idx));
678     if (type & XKB_STATE_LATCHED)
679         ret |= (state->latched_mods & (1 << idx));
680     if (type & XKB_STATE_LOCKED)
681         ret |= (state->locked_mods & (1 << idx));
682
683     return ret;
684 }
685
686 /**
687  * Returns 1 if the given modifier is active with the specified type(s), 0 if
688  * not, or -1 if the modifier is invalid.
689  */
690 _X_EXPORT int
691 xkb_state_mod_name_is_active(struct xkb_state *state, const char *name,
692                              enum xkb_state_component type)
693 {
694     xkb_mod_index_t idx = xkb_map_mod_get_index(state->xkb, name);
695
696     if (idx == XKB_MOD_INVALID)
697         return -1;
698
699     return xkb_state_mod_index_is_active(state, idx, type);
700 }
701
702 /**
703  * Returns 1 if the given group is active with the specified type(s), 0 if
704  * not, or -1 if the group is invalid.
705  */
706 _X_EXPORT int
707 xkb_state_group_index_is_active(struct xkb_state *state,
708                                 xkb_group_index_t idx,
709                                 enum xkb_state_component type)
710 {
711     int ret = 0;
712
713     if (idx >= xkb_map_num_groups(state->xkb))
714         return -1;
715
716     if (type & XKB_STATE_DEPRESSED)
717         ret |= (state->base_group == idx);
718     if (type & XKB_STATE_LATCHED)
719         ret |= (state->latched_group == idx);
720     if (type & XKB_STATE_LOCKED)
721         ret |= (state->locked_group == idx);
722
723     return ret;
724 }
725
726 /**
727  * Returns 1 if the given modifier is active with the specified type(s), 0 if
728  * not, or -1 if the modifier is invalid.
729  */
730 _X_EXPORT int
731 xkb_state_group_name_is_active(struct xkb_state *state, const char *name,
732                                enum xkb_state_component type)
733 {
734     xkb_group_index_t idx = xkb_map_group_get_index(state->xkb, name);
735
736     if (idx == XKB_GROUP_INVALID)
737         return -1;
738
739     return xkb_state_group_index_is_active(state, idx, type);
740 }
741
742 /**
743  * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid.
744  */
745 _X_EXPORT int
746 xkb_state_led_index_is_active(struct xkb_state *state, xkb_led_index_t idx)
747 {
748     if (idx >= xkb_map_num_leds(state->xkb))
749         return -1;
750
751     return !!(state->leds & (1 << idx));
752 }
753
754 /**
755  * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid.
756  */
757 _X_EXPORT int
758 xkb_state_led_name_is_active(struct xkb_state *state, const char *name)
759 {
760     xkb_led_index_t idx = xkb_map_led_get_index(state->xkb, name);
761
762     if (idx == XKB_LED_INVALID)
763         return -1;
764
765     return xkb_state_led_index_is_active(state, idx);
766 }