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