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