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