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