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