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