Add xkb_state_get_map()
[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 _X_EXPORT struct xkb_keymap *
498 xkb_state_get_map(struct xkb_state *state)
499 {
500     return state->xkb;
501 }
502
503 /**
504  * Update the LED state to match the rest of the xkb_state.
505  */
506 static void
507 xkb_state_led_update_all(struct xkb_state *state)
508 {
509     xkb_led_index_t led;
510
511     state->leds = 0;
512
513     for (led = 0; led < XkbNumIndicators; led++) {
514         struct xkb_indicator_map *map = &state->xkb->indicators->maps[led];
515         uint32_t mod_mask = 0;
516         uint32_t group_mask = 0;
517
518         if (!map->which_mods && !map->which_groups && !map->ctrls)
519             continue;
520
521         if (map->which_mods) {
522             if (map->which_mods & XkbIM_UseBase)
523                 mod_mask |= state->base_mods;
524             if (map->which_mods & XkbIM_UseLatched)
525                 mod_mask |= state->latched_mods;
526             if (map->which_mods & XkbIM_UseLocked)
527                 mod_mask |= state->locked_mods;
528             if (map->which_mods & XkbIM_UseEffective)
529                 mod_mask |= state->mods;
530             if ((map->mods.mask & mod_mask))
531                 state->leds |= (1 << led);
532         }
533         else if (map->which_groups) {
534             if (map->which_mods & XkbIM_UseBase)
535                 group_mask |= (1 << state->base_group);
536             if (map->which_mods & XkbIM_UseLatched)
537                 group_mask |= (1 << state->latched_group);
538             if (map->which_mods & XkbIM_UseLocked)
539                 group_mask |= (1 << state->locked_group);
540             if (map->which_mods & XkbIM_UseEffective)
541                 group_mask |= (1 << state->group);
542             if ((map->groups & group_mask))
543                 state->leds |= (1 << led);
544         }
545         else if (map->ctrls) {
546             if ((map->ctrls & state->xkb->ctrls->enabled_ctrls))
547                 state->leds |= (1 << led);
548         }
549     }
550 }
551
552 /**
553  * Calculates the derived state (effective mods/group and LEDs) from an
554  * up-to-date xkb_state.
555  */
556 static void
557 xkb_state_update_derived(struct xkb_state *state)
558 {
559     state->mods = (state->base_mods | state->latched_mods | state->locked_mods);
560     /* FIXME: Clamp/wrap locked_group */
561     state->group = state->locked_group + state->base_group +
562                    state->latched_group;
563     /* FIXME: Clamp/wrap effective group */
564
565     xkb_state_led_update_all(state);
566 }
567
568 /**
569  * Given a particular key event, updates the state structure to reflect the
570  * new modifiers.
571  */
572 _X_EXPORT void
573 xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key,
574                      enum xkb_key_direction direction)
575 {
576     xkb_filter_apply_all(state, key, direction);
577     xkb_state_update_derived(state);
578 }
579
580 /**
581  * Updates the state from a set of explicit masks as gained from
582  * xkb_state_serialise_mods and xkb_state_serialise_groups.  As noted in the
583  * documentation for these functions in xkbcommon.h, this round-trip is
584  * lossy, and should only be used to update a slave state mirroring the
585  * master, e.g. in a client/server window system.
586  */
587 _X_EXPORT void
588 xkb_state_update_mask(struct xkb_state *state,
589                       xkb_mod_mask_t base_mods,
590                       xkb_mod_mask_t latched_mods,
591                       xkb_mod_mask_t locked_mods,
592                       xkb_group_index_t base_group,
593                       xkb_group_index_t latched_group,
594                       xkb_group_index_t locked_group)
595 {
596     xkb_mod_mask_t mod;
597
598     state->base_mods = 0;
599     state->latched_mods = 0;
600     state->locked_mods = 0;
601     for (mod = 0; mod < xkb_map_num_mods(state->xkb); mod++) {
602         xkb_mod_mask_t idx = (1 << mod);
603         if (base_mods & idx)
604             state->base_mods |= idx;
605         if (latched_mods & idx)
606             state->latched_mods |= idx;
607         if (locked_mods & idx)
608             state->locked_mods |= idx;
609     }
610
611     state->base_group = base_group;
612     state->latched_group = latched_group;
613     state->locked_group = locked_group;
614
615     xkb_state_update_derived(state);
616 }
617
618 /**
619  * Serialises the requested modifier state into an xkb_mod_mask_t, with all
620  * the same disclaimers as in xkb_state_update_mask.
621  */
622 _X_EXPORT xkb_mod_mask_t
623 xkb_state_serialise_mods(struct xkb_state *state,
624                          enum xkb_state_component type)
625 {
626     xkb_mod_mask_t ret = 0;
627
628     if (type == XKB_STATE_EFFECTIVE)
629         return state->mods;
630
631     if (type & XKB_STATE_DEPRESSED)
632         ret |= state->base_mods;
633     if (type & XKB_STATE_LATCHED)
634         ret |= state->latched_mods;
635     if (type & XKB_STATE_LOCKED)
636         ret |= state->locked_mods;
637
638     return ret;
639 }
640
641 /**
642  * Serialises the requested group state, with all the same disclaimers as
643  * in xkb_state_update_mask.
644  */
645 _X_EXPORT xkb_group_index_t
646 xkb_state_serialise_group(struct xkb_state *state,
647                           enum xkb_state_component type)
648 {
649     xkb_group_index_t ret = 0;
650
651     if (type == XKB_STATE_EFFECTIVE)
652         return state->group;
653
654     if (type & XKB_STATE_DEPRESSED)
655         ret += state->base_group;
656     if (type & XKB_STATE_LATCHED)
657         ret += state->latched_group;
658     if (type & XKB_STATE_LOCKED)
659         ret += state->locked_group;
660
661     return ret;
662 }
663
664 /**
665  * Returns 1 if the given modifier is active with the specified type(s), 0 if
666  * not, or -1 if the modifier is invalid.
667  */
668 _X_EXPORT int
669 xkb_state_mod_index_is_active(struct xkb_state *state,
670                               xkb_mod_index_t idx,
671                               enum xkb_state_component type)
672 {
673     int ret = 0;
674
675     if (idx >= xkb_map_num_mods(state->xkb))
676         return -1;
677
678     if (type & XKB_STATE_DEPRESSED)
679         ret |= (state->base_mods & (1 << idx));
680     if (type & XKB_STATE_LATCHED)
681         ret |= (state->latched_mods & (1 << idx));
682     if (type & XKB_STATE_LOCKED)
683         ret |= (state->locked_mods & (1 << idx));
684
685     return ret;
686 }
687
688 /**
689  * Returns 1 if the given modifier is active with the specified type(s), 0 if
690  * not, or -1 if the modifier is invalid.
691  */
692 _X_EXPORT int
693 xkb_state_mod_name_is_active(struct xkb_state *state, const char *name,
694                              enum xkb_state_component type)
695 {
696     xkb_mod_index_t idx = xkb_map_mod_get_index(state->xkb, name);
697
698     if (idx == XKB_MOD_INVALID)
699         return -1;
700
701     return xkb_state_mod_index_is_active(state, idx, type);
702 }
703
704 /**
705  * Returns 1 if the given group is active with the specified type(s), 0 if
706  * not, or -1 if the group is invalid.
707  */
708 _X_EXPORT int
709 xkb_state_group_index_is_active(struct xkb_state *state,
710                                 xkb_group_index_t idx,
711                                 enum xkb_state_component type)
712 {
713     int ret = 0;
714
715     if (idx >= xkb_map_num_groups(state->xkb))
716         return -1;
717
718     if (type & XKB_STATE_DEPRESSED)
719         ret |= (state->base_group == idx);
720     if (type & XKB_STATE_LATCHED)
721         ret |= (state->latched_group == idx);
722     if (type & XKB_STATE_LOCKED)
723         ret |= (state->locked_group == idx);
724
725     return ret;
726 }
727
728 /**
729  * Returns 1 if the given modifier is active with the specified type(s), 0 if
730  * not, or -1 if the modifier is invalid.
731  */
732 _X_EXPORT int
733 xkb_state_group_name_is_active(struct xkb_state *state, const char *name,
734                                enum xkb_state_component type)
735 {
736     xkb_group_index_t idx = xkb_map_group_get_index(state->xkb, name);
737
738     if (idx == XKB_GROUP_INVALID)
739         return -1;
740
741     return xkb_state_group_index_is_active(state, idx, type);
742 }
743
744 /**
745  * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid.
746  */
747 _X_EXPORT int
748 xkb_state_led_index_is_active(struct xkb_state *state, xkb_led_index_t idx)
749 {
750     if (idx >= xkb_map_num_leds(state->xkb))
751         return -1;
752
753     return !!(state->leds & (1 << idx));
754 }
755
756 /**
757  * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid.
758  */
759 _X_EXPORT int
760 xkb_state_led_name_is_active(struct xkb_state *state, const char *name)
761 {
762     xkb_led_index_t idx = xkb_map_led_get_index(state->xkb, name);
763
764     if (idx == XKB_LED_INVALID)
765         return -1;
766
767     return xkb_state_led_index_is_active(state, idx);
768 }