Remove Xfuncproto.h and XKB.h from xkbcommon/xkbcommon.h
[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 #ifdef HAVE_CONFIG_H
62 #include <config.h>
63 #endif
64
65 #include <assert.h>
66
67 #include "xkbcommon/xkbcommon.h"
68 #include "XKBcommonint.h"
69 #include "xkbmisc.h"
70
71 struct xkb_filter {
72     struct xkb_state *state;
73     union xkb_action action;
74     xkb_keycode_t keycode;
75     uint32_t priv;
76     int (*func)(struct xkb_filter *filter, xkb_keycode_t key,
77                 enum xkb_key_direction direction);
78     int refcnt;
79     struct xkb_filter *next;
80 };
81
82 static union xkb_action *
83 xkb_key_get_action(struct xkb_state *state, xkb_keycode_t key)
84 {
85     int group, level;
86
87     if (!XkbKeyHasActions(state->xkb, key) ||
88         !XkbKeycodeInRange(state->xkb, key)) {
89         static union xkb_action fake;
90         memset(&fake, 0, sizeof(fake));
91         fake.type = XkbSA_NoAction;
92         return &fake;
93     }
94
95     group = xkb_key_get_group(state, key);
96     level = xkb_key_get_level(state, key, group);
97
98     return XkbKeyActionEntry(state->xkb, key, level, group);
99 }
100
101 static struct xkb_filter *
102 xkb_filter_new(struct xkb_state *state)
103 {
104     int i;
105     int old_size = state->num_filters;
106     struct xkb_filter *filters = state->filters;
107
108     for (i = 0; i < state->num_filters; i++) {
109         if (filters[i].func)
110             continue;
111         filters[i].state = state;
112         filters[i].refcnt = 1;
113         return &filters[i];
114     }
115
116     if (state->num_filters > 0)
117         state->num_filters *= 2;
118     else
119         state->num_filters = 4;
120     filters = realloc(filters, state->num_filters * sizeof(*filters));
121     if (!filters) { /* WSGO */
122         state->num_filters = old_size;
123         return NULL;
124     }
125     state->filters = filters;
126     memset(&filters[old_size], 0,
127            (state->num_filters - old_size) * sizeof(*filters));
128
129     filters[old_size].state = state;
130     filters[old_size].refcnt = 1;
131
132     return &filters[old_size];
133 }
134
135 /***====================================================================***/
136
137 static int
138 xkb_filter_group_set_func(struct xkb_filter *filter, xkb_keycode_t keycode,
139                           enum xkb_key_direction direction)
140 {
141     if (keycode != filter->keycode) {
142         filter->action.group.flags &= ~XkbSA_ClearLocks;
143         return 1;
144     }
145
146     if (direction == XKB_KEY_DOWN) {
147         filter->refcnt++;
148         return 0;
149     }
150     else if (--filter->refcnt > 0) {
151         return 0;
152     }
153
154     if (filter->action.group.flags & XkbSA_GroupAbsolute)
155         filter->state->base_group = filter->action.group.group;
156     else
157         filter->state->base_group = -filter->action.group.group;
158     if (filter->action.group.flags & XkbSA_ClearLocks)
159         filter->state->locked_group = 0;
160
161     filter->func = NULL;
162
163     return 1;
164 }
165
166 static int
167 xkb_filter_group_set_new(struct xkb_state *state, xkb_keycode_t keycode,
168                          union xkb_action *action)
169 {
170     struct xkb_filter *filter = xkb_filter_new(state);
171
172     if (!filter) /* WSGO */
173         return -1;
174     filter->keycode = keycode;
175     filter->func = xkb_filter_group_set_func;
176     filter->action = *action;
177
178     if (action->group.flags & XkbSA_GroupAbsolute) {
179         filter->action.group.group = filter->state->base_group;
180         filter->state->base_group = action->group.group;
181     }
182     else {
183         filter->state->base_group += action->group.group;
184     }
185
186     return 1;
187 }
188
189 static int
190 xkb_filter_group_lock_func(struct xkb_filter *filter, xkb_keycode_t keycode,
191                            enum xkb_key_direction direction)
192 {
193     if (keycode != filter->keycode)
194         return 1;
195
196     if (direction == XKB_KEY_DOWN) {
197         filter->refcnt++;
198         return 0;
199     }
200     if (--filter->refcnt > 0)
201         return 0;
202
203     filter->func = NULL;
204     return 1;
205 }
206
207 static int
208 xkb_filter_group_lock_new(struct xkb_state *state, xkb_keycode_t keycode,
209                           union xkb_action *action)
210 {
211     struct xkb_filter *filter = xkb_filter_new(state);
212
213     if (!filter)
214         return 0;
215
216     filter->keycode = keycode;
217     filter->func = xkb_filter_group_lock_func;
218     filter->action = *action;
219
220     if (action->group.flags & XkbSA_GroupAbsolute)
221         filter->state->locked_group = action->group.group;
222     else
223         filter->state->locked_group += action->group.group;
224
225     return 1;
226 }
227
228 static int
229 xkb_filter_mod_set_func(struct xkb_filter *filter, xkb_keycode_t keycode,
230                         enum xkb_key_direction direction)
231 {
232     if (keycode != filter->keycode) {
233         filter->action.mods.flags &= ~XkbSA_ClearLocks;
234         return 1;
235     }
236
237     if (direction == XKB_KEY_DOWN) {
238         filter->refcnt++;
239         return 0;
240     }
241     else if (--filter->refcnt > 0) {
242         return 0;
243     }
244
245     filter->state->base_mods &= ~(filter->action.mods.mask);
246     if (filter->action.mods.flags & XkbSA_ClearLocks)
247         filter->state->locked_mods &= ~filter->action.mods.mask;
248
249     filter->func = NULL;
250
251     return 1;
252 }
253
254 static int
255 xkb_filter_mod_set_new(struct xkb_state *state, xkb_keycode_t keycode,
256                        union xkb_action *action)
257 {
258     struct xkb_filter *filter = xkb_filter_new(state);
259
260     if (!filter) /* WSGO */
261         return -1;
262     filter->keycode = keycode;
263     filter->func = xkb_filter_mod_set_func;
264     filter->action = *action;
265
266     filter->state->base_mods |= action->mods.mask;
267
268     return 1;
269 }
270
271 static int
272 xkb_filter_mod_lock_func(struct xkb_filter *filter, xkb_keycode_t keycode,
273                          enum xkb_key_direction direction)
274 {
275     if (keycode != filter->keycode)
276         return 1;
277
278     if (direction == XKB_KEY_DOWN) {
279         filter->refcnt++;
280         return 0;
281     }
282     if (--filter->refcnt > 0)
283         return 0;
284
285     filter->state->locked_mods &= ~filter->priv;
286     filter->func = NULL;
287     return 1;
288 }
289
290 static int
291 xkb_filter_mod_lock_new(struct xkb_state *state, xkb_keycode_t keycode,
292                         union xkb_action *action)
293 {
294     struct xkb_filter *filter = xkb_filter_new(state);
295
296     if (!filter) /* WSGO */
297         return 0;
298
299     filter->keycode = keycode;
300     filter->func = xkb_filter_mod_lock_func;
301     filter->action = *action;
302     filter->priv = state->locked_mods & action->mods.mask;
303     state->locked_mods |= action->mods.mask;
304
305     return 1;
306 }
307
308 enum xkb_key_latch_state {
309     NO_LATCH,
310     LATCH_KEY_DOWN,
311     LATCH_PENDING,
312 };
313
314 static int
315 xkb_filter_mod_latch_func(struct xkb_filter *filter, xkb_keycode_t keycode,
316                           enum xkb_key_direction direction)
317 {
318     enum xkb_key_latch_state latch = filter->priv;
319
320     if (direction == XKB_KEY_DOWN && latch == LATCH_PENDING) {
321         /* If this is a new keypress and we're awaiting our single latched
322          * keypress, then either break the latch if any random key is pressed,
323          * or promote it to a lock or plain base set if it's the same
324          * modifier. */
325         union xkb_action *action = xkb_key_get_action(filter->state, keycode);
326         if (action->type == XkbSA_LatchMods &&
327             action->mods.flags == filter->action.mods.flags &&
328             action->mods.mask == filter->action.mods.mask) {
329             filter->action = *action;
330             if (filter->action.mods.flags & XkbSA_LatchToLock) {
331                 filter->action.type = XkbSA_LockMods;
332                 filter->func = xkb_filter_mod_lock_func;
333                 filter->state->locked_mods |= filter->action.mods.mask;
334             }
335             else {
336                 filter->action.type = XkbSA_SetMods;
337                 filter->func = xkb_filter_mod_set_func;
338                 filter->state->base_mods |= filter->action.mods.mask;
339             }
340             filter->keycode = keycode;
341             filter->state->latched_mods &= ~filter->action.mods.mask;
342             /* XXX beep beep! */
343             return 0;
344         }
345         else if (((1 << action->type) & XkbSA_BreakLatch)) {
346             /* XXX: This may be totally broken, we might need to break the
347              *      latch in the next run after this press? */
348             filter->state->latched_mods &= ~filter->action.mods.mask;
349             filter->func = NULL;
350             return 1;
351         }
352     }
353     else if (direction == XKB_KEY_UP && keycode == filter->keycode) {
354         /* Our key got released.  If we've set it to clear locks, and we
355          * currently have the same modifiers locked, then release them and
356          * don't actually latch.  Else we've actually hit the latching
357          * stage, so set PENDING and move our modifier from base to
358          * latched. */
359         if (latch == NO_LATCH ||
360             ((filter->action.mods.flags & XkbSA_ClearLocks) &&
361              (filter->state->locked_mods & filter->action.mods.mask) ==
362              filter->action.mods.mask)) {
363             /* XXX: We might be a bit overenthusiastic about clearing
364              *      mods other filters have set here? */
365             if (latch == LATCH_PENDING)
366                 filter->state->latched_mods &= ~filter->action.mods.mask;
367             else
368                 filter->state->base_mods &= ~filter->action.mods.mask;
369             filter->state->locked_mods &= ~filter->action.mods.mask;
370             filter->func = NULL;
371         }
372         else {
373             latch = LATCH_PENDING;
374             filter->state->base_mods &= ~filter->action.mods.mask;
375             filter->state->latched_mods |= filter->action.mods.mask;
376             /* XXX beep beep! */
377         }
378     }
379     else if (direction == XKB_KEY_DOWN && latch == LATCH_KEY_DOWN) {
380         /* Someone's pressed another key while we've still got the latching
381          * key held down, so keep the base modifier state active (from
382          * xkb_filter_mod_latch_new), but don't trip the latch, just clear
383          * it as soon as the modifier gets released. */
384         latch = NO_LATCH;
385     }
386
387     filter->priv = latch;
388
389     return 1;
390 }
391
392 static int
393 xkb_filter_mod_latch_new(struct xkb_state *state, xkb_keycode_t keycode,
394                          union xkb_action *action)
395 {
396     struct xkb_filter *filter = xkb_filter_new(state);
397     enum xkb_key_latch_state latch = LATCH_KEY_DOWN;
398
399     if (!filter) /* WSGO */
400         return -1;
401     filter->keycode = keycode;
402     filter->priv = latch;
403     filter->func = xkb_filter_mod_latch_func;
404     filter->action = *action;
405
406     filter->state->base_mods |= action->mods.mask;
407
408     return 1;
409 }
410
411 /**
412  * Applies any relevant filters to the key, first from the list of filters
413  * that are currently active, then if no filter has claimed the key, possibly
414  * apply a new filter from the key action.
415  */
416 static void
417 xkb_filter_apply_all(struct xkb_state *state, xkb_keycode_t key,
418                      enum xkb_key_direction direction)
419 {
420     struct xkb_filter *filters = state->filters;
421     union xkb_action *act = NULL;
422     int send = 1;
423     int i;
424
425     /* First run through all the currently active filters and see if any of
426      * them have claimed this event. */
427     for (i = 0; i < state->num_filters; i++) {
428         if (!filters[i].func)
429             continue;
430         send &= (*filters[i].func)(&filters[i], key, direction);
431     }
432
433     if (!send || direction == XKB_KEY_UP)
434         return;
435
436     act = xkb_key_get_action(state, key);
437     switch (act->type) {
438     case XkbSA_SetMods:
439         send = xkb_filter_mod_set_new(state, key, act);
440         break;
441     case XkbSA_LatchMods:
442         send = xkb_filter_mod_latch_new(state, key, act);
443         break;
444     case XkbSA_LockMods:
445         send = xkb_filter_mod_lock_new(state, key, act);
446         break;
447     case XkbSA_SetGroup:
448         send = xkb_filter_group_set_new(state, key, act);
449         break;
450 #if 0
451     case XkbSA_LatchGroup:
452         send = xkb_filter_mod_latch_new(state, key, act);
453         break;
454 #endif
455     case XkbSA_LockGroup:
456         send = xkb_filter_group_lock_new(state, key, act);
457         break;
458     }
459
460     return;
461 }
462
463 _X_EXPORT struct xkb_state *
464 xkb_state_new(struct xkb_keymap *xkb)
465 {
466     struct xkb_state *ret;
467
468     if (!xkb)
469         return NULL;
470
471     ret = calloc(sizeof(*ret), 1);
472     if (!ret)
473         return NULL;
474
475     ret->refcnt = 1;
476     ret->xkb = xkb_map_ref(xkb);
477
478     return ret;
479 }
480
481 _X_EXPORT struct xkb_state *
482 xkb_state_ref(struct xkb_state *state)
483 {
484     state->refcnt++;
485     return state;
486 }
487
488 _X_EXPORT void
489 xkb_state_unref(struct xkb_state *state)
490 {
491     state->refcnt--;
492     assert(state->refcnt >= 0);
493     if (state->refcnt > 0)
494         return;
495
496     xkb_map_unref(state->xkb);
497     free(state->filters);
498     free(state);
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 }