static void
print_keycode(struct keyboard *kbd, xkb_keycode_t keycode)
{
    unsigned int i;
    struct xkb_keymap *keymap;
    struct xkb_state *state;

    const xkb_keysym_t *syms;
    unsigned int nsyms;
    char s[16];
    uint32_t unicode;
    xkb_layout_index_t layout;
    xkb_mod_index_t mod;
    xkb_led_index_t led;

    state = kbd->state;
    keymap = xkb_state_get_keymap(state);

    nsyms = xkb_state_key_get_syms(state, keycode, &syms);

    if (nsyms <= 0)
        return;

    printf("keysyms [ ");
    for (i = 0; i < nsyms; i++) {
        xkb_keysym_get_name(syms[i], s, sizeof(s));
        printf("%-*s ", (int)sizeof(s), s);
    }
    printf("] ");

    /*
     * Only do this if wchar_t is UCS-4, so we can be lazy and print
     * with %lc.
     */
#ifdef __STDC_ISO_10646__
    printf("unicode [ ");
    for (i = 0; i < nsyms; i++) {
        unicode = xkb_keysym_to_utf32(syms[i]);
        printf("%lc ", (int)(unicode ? unicode : L' '));
    }
    printf("] ");
#endif

    layout = xkb_state_key_get_layout(state, keycode);
    printf("layout [ %s (%d) ] ",
           xkb_keymap_layout_get_name(keymap, layout), layout);

    printf("level [ %d ] ",
           xkb_state_key_get_level(state, keycode, layout));

    printf("mods [ ");
    for (mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
        if (xkb_state_mod_index_is_active(state, mod,
                                          XKB_STATE_MODS_EFFECTIVE) <= 0)
            continue;
        if (xkb_state_mod_index_is_consumed(state, keycode, mod))
            printf("-%s ", xkb_keymap_mod_get_name(keymap, mod));
        else
            printf("%s ", xkb_keymap_mod_get_name(keymap, mod));
    }
    printf("] ");

    printf("leds [ ");
    for (led = 0; led < xkb_keymap_num_leds(keymap); led++) {
        if (xkb_state_led_index_is_active(state, led) <= 0)
            continue;
        printf("%s ", xkb_keymap_led_get_name(keymap, led));
    }
    printf("] ");

    printf("\n");
}
Пример #2
0
static void
input_method_keyboard_key(void *data,
			  struct wl_keyboard *wl_keyboard,
			  uint32_t serial,
			  uint32_t time,
			  uint32_t key,
			  uint32_t state_w)
{
	struct simple_im *keyboard = data;
	uint32_t code;
	uint32_t num_syms;
	const xkb_keysym_t *syms;
	xkb_keysym_t sym;
	enum wl_keyboard_key_state state = state_w;

	if (!keyboard->state)
		return;

	code = key + 8;
	num_syms = xkb_state_key_get_syms(keyboard->state, code, &syms);

	sym = XKB_KEY_NoSymbol;
	if (num_syms == 1)
		sym = syms[0];

	if (keyboard->key_handler)
		(*keyboard->key_handler)(keyboard, serial, time, key, sym,
					 state);
}
Пример #3
0
static GLFWbool inputChar(_GLFWwindow* window, uint32_t key)
{
    uint32_t code, numSyms;
    long cp;
    const xkb_keysym_t *syms;
    xkb_keysym_t sym;

    code = key + 8;
    numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms);

    if (numSyms == 1)
    {
#ifdef HAVE_XKBCOMMON_COMPOSE_H
        sym = composeSymbol(syms[0]);
#else
        sym = syms[0];
#endif
        cp = _glfwKeySym2Unicode(sym);
        if (cp != -1)
        {
            const int mods = _glfw.wl.xkb.modifiers;
            const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
            _glfwInputChar(window, cp, mods, plain);
        }
    }

    return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, syms[0]);
}
Пример #4
0
static void uxkb_dev_repeat(struct uterm_input_dev *dev, unsigned int state)
{
	struct xkb_keymap *keymap = xkb_state_get_keymap(dev->state);
	unsigned int i;
	int num_keysyms, ret;
	const uint32_t *keysyms;
	struct itimerspec spec;

	if (dev->repeating && dev->repeat_event.keycode == dev->event.keycode) {
		if (state == KEY_RELEASED) {
			dev->repeating = false;
			ev_timer_update(dev->repeat_timer, NULL);
		}
		return;
	}

	if (state == KEY_PRESSED &&
	    xkb_keymap_key_repeats(keymap, dev->event.keycode)) {
		dev->repeat_event.keycode = dev->event.keycode;
		dev->repeat_event.ascii = dev->event.ascii;
		dev->repeat_event.mods = dev->event.mods;
		dev->repeat_event.num_syms = dev->event.num_syms;

		for (i = 0; i < dev->event.num_syms; ++i) {
			dev->repeat_event.keysyms[i] = dev->event.keysyms[i];
			dev->repeat_event.codepoints[i] =
						dev->event.codepoints[i];
		}
	} else if (dev->repeating &&
		   !xkb_keymap_key_repeats(keymap, dev->event.keycode)) {
		num_keysyms = xkb_state_key_get_syms(dev->state,
						     dev->repeat_event.keycode,
						     &keysyms);
		if (num_keysyms <= 0)
			return;

		ret = uxkb_dev_fill_event(dev, &dev->repeat_event,
					  dev->repeat_event.keycode,
					  num_keysyms, keysyms);
		if (ret)
			return;

		return;
	} else {
		return;
	}

	dev->repeating = true;
	spec.it_interval.tv_sec = 0;
	spec.it_interval.tv_nsec = dev->input->repeat_rate * 1000000;
	spec.it_value.tv_sec = 0;
	spec.it_value.tv_nsec = dev->input->repeat_delay * 1000000;
	ev_timer_update(dev->repeat_timer, &spec);
}
Пример #5
0
static void fghKeyboardKey( void* data, struct wl_keyboard* keyboard,
                            uint32_t serial, uint32_t time,
                            uint32_t key, uint32_t state )
{
    SFG_PlatformDisplay* pDisplay = data;
    SFG_Window* win = fgStructure.CurrentWindow;
    const xkb_keysym_t* syms;

    xkb_state_key_get_syms( pDisplay->xkb_state,
                            key + 8, &syms );
    fghKeyboardInterpretKeysym( win, key, syms[0], state );
}
Пример #6
0
/* FIXME: Don't handle composed and dead-keys properly. 
 * Waiting for support in libxkbcommon ... */
int handle_xkb(int code, int value)
{
   unsigned i;
   const xkb_keysym_t *syms = NULL;
   unsigned num_syms        = 0;
   uint16_t mod             = 0;
   /* Convert Linux evdev to X11 (xkbcommon docs say so at least ...) */
   int xk_code              = code + 8;

   if (!xkb_state)
      return -1;

   if (value == 2) /* Repeat, release first explicitly. */
      xkb_state_update_key(xkb_state, xk_code, XKB_KEY_UP);

   if (value)
      num_syms = xkb_state_key_get_syms(xkb_state, xk_code, &syms);

   if (value > 0)
      xkb_state_update_key(xkb_state, xk_code, XKB_KEY_DOWN);
   else
      xkb_state_update_key(xkb_state, xk_code, XKB_KEY_UP);

   if (!syms)
      return -1;

   /* Build mod state. */
   for (i = 0; i < MOD_MAP_SIZE; i++)
   {
      xkb_mod_index_t *map_idx = (xkb_mod_index_t*)&mod_map_idx[i];
      uint16_t        *map_bit = (uint16_t       *)&mod_map_bit[i];

      if (*map_idx != XKB_MOD_INVALID)
         mod |= xkb_state_mod_index_is_active(
               xkb_state, *map_idx,
               (enum xkb_state_component)
               ((XKB_STATE_MODS_EFFECTIVE) > 0)) ? *map_bit : 0;
   }

   input_keyboard_event(value, input_keymaps_translate_keysym_to_rk(code),
         num_syms ? xkb_keysym_to_utf32(syms[0]) : 0, mod, RETRO_DEVICE_KEYBOARD);

   for (i = 1; i < num_syms; i++)
      input_keyboard_event(value, RETROK_UNKNOWN,
            xkb_keysym_to_utf32(syms[i]), mod, RETRO_DEVICE_KEYBOARD);

   return 0;
}
Пример #7
0
int uxkb_dev_process(struct uterm_input_dev *dev,
		     uint16_t key_state, uint16_t code)
{
	struct xkb_state *state;
	xkb_keycode_t keycode;
	const xkb_keysym_t *keysyms;
	int num_keysyms, ret;
	enum xkb_state_component changed;

	if (key_state == KEY_REPEATED)
		return -ENOKEY;

	state = dev->state;
	keycode = code + EVDEV_KEYCODE_OFFSET;

	num_keysyms = xkb_state_key_get_syms(state, keycode, &keysyms);

	changed = 0;
	if (key_state == KEY_PRESSED)
		changed = xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
	else if (key_state == KEY_RELEASED)
		changed = xkb_state_update_key(state, keycode, XKB_KEY_UP);

	if (changed & XKB_STATE_LEDS)
		uxkb_dev_update_keyboard_leds(dev);

	if (num_keysyms <= 0)
		return -ENOKEY;

	ret = uxkb_dev_fill_event(dev, &dev->event, keycode, num_keysyms,
				  keysyms);
	if (ret)
		return -ENOKEY;

	uxkb_dev_repeat(dev, key_state);

	if (key_state == KEY_RELEASED)
		return -ENOKEY;

	dev->event.handled = false;
	shl_hook_call(dev->input->hook, dev->input, &dev->event);

	return 0;
}
Пример #8
0
static void inputChar(_GLFWwindow* window, uint32_t key)
{
    uint32_t code, numSyms;
    long cp;
    const xkb_keysym_t *syms;

    code = key + 8;
    numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms);

    if (numSyms == 1)
    {
        cp = _glfwKeySym2Unicode(syms[0]);
        if (cp != -1)
        {
            const int mods = _glfw.wl.xkb.modifiers;
            const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
            _glfwInputChar(window, cp, mods, plain);
        }
    }
}
Пример #9
0
static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
        struct input_event *ev = &data->evdev.event;
        enum xkb_state_component compch;
        enum xkb_compose_status cstatus;
        const xkb_keysym_t *keysyms;
        idev_device *d = &k->device;
        int num, r;

        if (ev->type != EV_KEY || ev->value > KBDKEY_DOWN)
                return 0;

        /* TODO: We should audit xkb-actions, whether they need @resync as
         * flag. Most actions should just be executed, however, there might
         * be actions that depend on modifier-orders. Those should be
         * suppressed. */

        num = xkb_state_key_get_syms(k->xkb_state, ev->code + KBDXKB_SHIFT, &keysyms);
        compch = xkb_state_update_key(k->xkb_state, ev->code + KBDXKB_SHIFT, ev->value);

        if (compch & XKB_STATE_LEDS) {
                /* TODO: update LEDs */
        }

        if (num < 0) {
                r = num;
                goto error;
        }

        if (k->xkb_compose && ev->value == KBDKEY_DOWN) {
                if (num == 1 && !data->resync) {
                        xkb_compose_state_feed(k->xkb_compose, keysyms[0]);
                        cstatus = xkb_compose_state_get_status(k->xkb_compose);
                } else {
                        cstatus = XKB_COMPOSE_CANCELLED;
                }

                switch (cstatus) {
                case XKB_COMPOSE_NOTHING:
                        /* keep produced keysyms and forward unchanged */
                        break;
                case XKB_COMPOSE_COMPOSING:
                        /* consumed by compose-state, drop keysym */
                        keysyms = NULL;
                        num = 0;
                        break;
                case XKB_COMPOSE_COMPOSED:
                        /* compose-state produced sth, replace keysym */
                        num = keyboard_read_compose(k, &keysyms);
                        xkb_compose_state_reset(k->xkb_compose);
                        break;
                case XKB_COMPOSE_CANCELLED:
                        /* canceled compose, reset, forward cancellation sym */
                        xkb_compose_state_reset(k->xkb_compose);
                        break;
                }
        } else if (k->xkb_compose &&
                   num == 1 &&
                   keysyms[0] == XKB_KEY_Multi_key &&
                   !data->resync &&
                   ev->value == KBDKEY_UP) {
                /* Reset compose state on Multi-Key UP events. This effectively
                 * requires you to hold the key during the whole sequence. I
                 * think it's pretty handy to avoid accidental
                 * Compose-sequences, but this may break Compose for disabled
                 * people. We really need to make this opional! (TODO) */
                xkb_compose_state_reset(k->xkb_compose);
        }

        if (ev->value == KBDKEY_UP) {
                /* never produce keysyms for UP */
                keysyms = NULL;
                num = 0;
        }

        r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms);
        if (r < 0)
                goto error;

        keyboard_repeat(k);
        return keyboard_raise_data(k, &k->evdata);

error:
        log_debug_errno(r, "idev-keyboard: %s/%s: cannot handle event: %m",
                        d->session->name, d->name);
        k->repeating = false;
        keyboard_arm(k, 0);
        return 0;
}
Пример #10
0
static void keyboard_repeat(idev_keyboard *k) {
        idev_data *evdata = &k->evdata;
        idev_data *repdata = &k->repdata;
        idev_data_keyboard *evkbd = &evdata->keyboard;
        idev_data_keyboard *repkbd = &repdata->keyboard;
        const xkb_keysym_t *keysyms;
        idev_device *d = &k->device;
        bool repeats;
        int r, num;

        if (evdata->resync) {
                /*
                 * We received a re-sync event. During re-sync, any number of
                 * key-events may have been lost and sync-events may be
                 * re-ordered. Always disable key-repeat for those events. Any
                 * following event will trigger it again.
                 */

                k->repeating = false;
                keyboard_arm(k, 0);
                return;
        }

        repeats = xkb_keymap_key_repeats(k->kbdmap->xkb_keymap, evkbd->keycode + KBDXKB_SHIFT);

        if (k->repeating && repkbd->keycode == evkbd->keycode) {
                /*
                 * We received an event for the key we currently repeat. If it
                 * was released, stop key-repeat. Otherwise, ignore the event.
                 */

                if (evkbd->value == KBDKEY_UP) {
                        k->repeating = false;
                        keyboard_arm(k, 0);
                }
        } else if (evkbd->value == KBDKEY_DOWN && repeats) {
                /*
                 * We received a key-down event for a key that repeats. The
                 * previous condition caught keys we already repeat, so we know
                 * this is a different key or no key-repeat is running. Start
                 * new key-repeat.
                 */

                errno = 0;
                num = xkb_state_key_get_syms(k->xkb_state, evkbd->keycode + KBDXKB_SHIFT, &keysyms);
                if (num < 0)
                        r = errno > 0 ? errno : -EFAULT;
                else
                        r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms);

                if (r < 0) {
                        log_debug_errno(r, "idev-keyboard: %s/%s: cannot set key-repeat: %m",
                                        d->session->name, d->name);
                        k->repeating = false;
                        keyboard_arm(k, 0);
                } else {
                        k->repeating = true;
                        keyboard_arm(k, k->repeat_delay);
                }
        } else if (k->repeating && !repeats) {
                /*
                 * We received an event for a key that does not repeat, but we
                 * currently repeat a previously received key. The new key is
                 * usually a modifier, but might be any kind of key. In this
                 * case, we continue repeating the old key, but update the
                 * symbols according to the new state.
                 */

                errno = 0;
                num = xkb_state_key_get_syms(k->xkb_state, repkbd->keycode + KBDXKB_SHIFT, &keysyms);
                if (num < 0)
                        r = errno > 0 ? errno : -EFAULT;
                else
                        r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms);

                if (r < 0) {
                        log_debug_errno(r, "idev-keyboard: %s/%s: cannot update key-repeat: %m",
                                        d->session->name, d->name);
                        k->repeating = false;
                        keyboard_arm(k, 0);
                }
        }
}
Пример #11
0
/*
 * Test a sequence of keysyms, resulting from a sequence of key presses,
 * against the keysyms they're supposed to generate.
 *
 * - Each test runs with a clean state.
 * - Each line in the test is made up of:
 *   + A keycode, given as a KEY_* from linux/input.h.
 *   + A direction - DOWN for press, UP for release, BOTH for
 *     immediate press + release, REPEAT to just get the syms.
 *   + A sequence of keysyms that should result from this keypress.
 *
 * The vararg format is:
 * <KEY_*>  <DOWN | UP | BOTH>  <XKB_KEY_* (zero or more)>  <NEXT | FINISH>
 *
 * See below for examples.
 */
int
test_key_seq_va(struct xkb_keymap *keymap, va_list ap)
{
    struct xkb_state *state;

    xkb_keycode_t kc;
    int op;
    xkb_keysym_t keysym;

    const xkb_keysym_t *syms;
    xkb_keysym_t sym;
    unsigned int nsyms, i;
    char ksbuf[64];

    fprintf(stderr, "----\n");

    state = xkb_state_new(keymap);
    assert(state);

    for (;;) {
        kc = va_arg(ap, int) + EVDEV_OFFSET;
        op = va_arg(ap, int);

        nsyms = xkb_state_key_get_syms(state, kc, &syms);
        if (nsyms == 1) {
            sym = xkb_state_key_get_one_sym(state, kc);
            syms = &sym;
        }

        fprintf(stderr, "got %u syms for keycode %u: [", nsyms, kc);

        if (op == DOWN || op == BOTH)
            xkb_state_update_key(state, kc, XKB_KEY_DOWN);
        if (op == UP || op == BOTH)
            xkb_state_update_key(state, kc, XKB_KEY_UP);

        for (i = 0; i < nsyms; i++) {
            keysym = va_arg(ap, int);
            xkb_keysym_get_name(syms[i], ksbuf, sizeof(ksbuf));
            fprintf(stderr, "%s%s", (i != 0) ? ", " : "", ksbuf);

            if (keysym == FINISH || keysym == NEXT) {
                xkb_keysym_get_name(syms[i], ksbuf, sizeof(ksbuf));
                fprintf(stderr, "Did not expect keysym: %s.\n", ksbuf);
                goto fail;
            }

            if (keysym != syms[i]) {
                xkb_keysym_get_name(keysym, ksbuf, sizeof(ksbuf));
                fprintf(stderr, "Expected keysym: %s. ", ksbuf);;
                xkb_keysym_get_name(syms[i], ksbuf, sizeof(ksbuf));
                fprintf(stderr, "Got keysym: %s.\n", ksbuf);;
                goto fail;
            }
        }

        if (nsyms == 0) {
            keysym = va_arg(ap, int);
            if (keysym != XKB_KEY_NoSymbol) {
                xkb_keysym_get_name(keysym, ksbuf, sizeof(ksbuf));
                fprintf(stderr, "Expected %s, but got no keysyms.\n", ksbuf);
                goto fail;
            }
        }

        fprintf(stderr, "]\n");

        keysym = va_arg(ap, int);
        if (keysym == NEXT)
            continue;
        if (keysym == FINISH)
            break;

        xkb_keysym_get_name(keysym, ksbuf, sizeof(ksbuf));
        fprintf(stderr, "Expected keysym: %s. Didn't get it.\n", ksbuf);
        goto fail;
    }
Пример #12
0
static void
test_caps_keysym_transformation(struct xkb_keymap *keymap)
{
    struct xkb_state *state = xkb_state_new(keymap);
    xkb_mod_index_t caps, shift;
    int nsyms;
    xkb_keysym_t sym;
    const xkb_keysym_t *syms;

    assert(state);

    /* See xkb_state_key_get_one_sym() for what's this all about. */

    caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
    shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
    assert(caps != XKB_MOD_INVALID && shift != XKB_MOD_INVALID);

    assert(xkb_state_key_get_layout(state, KEY_A + 8) == 0);
    assert(xkb_state_key_get_layout(state, KEY_SEMICOLON + 8) == 0);

    /* Without caps, no transformation. */
    assert(xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) == 0);
    assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);
    assert(xkb_state_key_get_level(state, KEY_A + 8, 0) == 0);
    sym = xkb_state_key_get_one_sym(state, KEY_A + 8);
    assert(sym == XKB_KEY_a);
    assert(xkb_state_key_get_level(state, KEY_SEMICOLON + 8, 0) == 0);
    sym = xkb_state_key_get_one_sym(state, KEY_SEMICOLON + 8);
    assert(sym == XKB_KEY_eacute);
    nsyms = xkb_state_key_get_syms(state, KEY_SEMICOLON + 8, &syms);
    assert(nsyms == 1 && syms[0] == XKB_KEY_eacute);

    /* With shift, no transformation (only different level). */
    xkb_state_update_key(state, KEY_LEFTSHIFT + 8, XKB_KEY_DOWN);
    assert(xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) == 0);
    assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) > 0);
    assert(xkb_state_key_get_level(state, KEY_A + 8, 0) == 1);
    sym = xkb_state_key_get_one_sym(state, KEY_A + 8);
    assert(sym == XKB_KEY_A);
    sym = xkb_state_key_get_one_sym(state, KEY_SEMICOLON + 8);
    assert(sym == XKB_KEY_odiaeresis);
    nsyms = xkb_state_key_get_syms(state, KEY_SEMICOLON + 8, &syms);
    assert(nsyms == 1 && syms[0] == XKB_KEY_odiaeresis);
    xkb_state_update_key(state, KEY_LEFTSHIFT + 8, XKB_KEY_UP);
    assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);

    /* With caps, transform in same level, only with _get_one_sym(). */
    xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_UP);
    assert(xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) > 0);
    assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);
    assert(xkb_state_key_get_level(state, KEY_A + 8, 0) == 1);
    sym = xkb_state_key_get_one_sym(state, KEY_A + 8);
    assert(sym == XKB_KEY_A);
    assert(xkb_state_key_get_level(state, KEY_SEMICOLON + 8, 0) == 0);
    sym = xkb_state_key_get_one_sym(state, KEY_SEMICOLON + 8);
    assert(sym == XKB_KEY_Eacute);
    nsyms = xkb_state_key_get_syms(state, KEY_SEMICOLON + 8, &syms);
    assert(nsyms == 1 && syms[0] == XKB_KEY_eacute);
    xkb_state_update_key(state, KEY_LEFTSHIFT + 8, XKB_KEY_UP);
    assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);
    xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_UP);

    xkb_state_unref(state);
}
Пример #13
0
static void
test_update_key(struct xkb_keymap *keymap)
{
    struct xkb_state *state = xkb_state_new(keymap);
    const xkb_keysym_t *syms;
    xkb_keysym_t one_sym;
    int num_syms;

    assert(state);

    /* LCtrl down */
    xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, XKB_KEY_DOWN);
    fprintf(stderr, "dumping state for LCtrl down:\n");
    print_state(state);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
                                        XKB_STATE_MODS_DEPRESSED) > 0);

    /* LCtrl + RAlt down */
    xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, XKB_KEY_DOWN);
    fprintf(stderr, "dumping state for LCtrl + RAlt down:\n");
    print_state(state);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
                                        XKB_STATE_MODS_DEPRESSED) > 0);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
                                        XKB_STATE_MODS_DEPRESSED) > 0);
    assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
                                          XKB_STATE_MATCH_ALL,
                                          XKB_MOD_NAME_CTRL,
                                          XKB_MOD_NAME_ALT,
                                          NULL) > 0);
    assert(xkb_state_mod_indices_are_active(state, XKB_STATE_MODS_DEPRESSED,
                                            XKB_STATE_MATCH_ALL,
                                            xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL),
                                            xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT),
                                            XKB_MOD_INVALID) > 0);
    assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
                                          XKB_STATE_MATCH_ALL,
                                          XKB_MOD_NAME_ALT,
                                          NULL) == 0);
    assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
                                          XKB_STATE_MATCH_ALL |
                                          XKB_STATE_MATCH_NON_EXCLUSIVE,
                                          XKB_MOD_NAME_ALT,
                                          NULL) > 0);
    assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
                                          (XKB_STATE_MATCH_ANY |
                                           XKB_STATE_MATCH_NON_EXCLUSIVE),
                                          XKB_MOD_NAME_ALT,
                                          NULL) > 0);

    /* RAlt down */
    xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, XKB_KEY_UP);
    fprintf(stderr, "dumping state for RAlt down:\n");
    print_state(state);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
                                        XKB_STATE_MODS_EFFECTIVE) == 0);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
                                        XKB_STATE_MODS_DEPRESSED) > 0);
    assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
                                          XKB_STATE_MATCH_ANY,
                                          XKB_MOD_NAME_CTRL,
                                          XKB_MOD_NAME_ALT,
                                          NULL) > 0);
    assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_LATCHED,
                                          XKB_STATE_MATCH_ANY,
                                          XKB_MOD_NAME_CTRL,
                                          XKB_MOD_NAME_ALT,
                                          NULL) == 0);

    /* none down */
    xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, XKB_KEY_UP);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
                                        XKB_STATE_MODS_EFFECTIVE) == 0);

    /* Caps locked */
    xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, XKB_KEY_DOWN);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
                                        XKB_STATE_MODS_DEPRESSED) > 0);
    xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, XKB_KEY_UP);
    fprintf(stderr, "dumping state for Caps Lock:\n");
    print_state(state);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
                                        XKB_STATE_MODS_DEPRESSED) == 0);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
                                        XKB_STATE_MODS_LOCKED) > 0);
    assert(xkb_state_led_name_is_active(state, XKB_LED_NAME_CAPS) > 0);
    num_syms = xkb_state_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms);
    assert(num_syms == 1 && syms[0] == XKB_KEY_Q);

    /* Num Lock locked */
    xkb_state_update_key(state, KEY_NUMLOCK + EVDEV_OFFSET, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_NUMLOCK + EVDEV_OFFSET, XKB_KEY_UP);
    fprintf(stderr, "dumping state for Caps Lock + Num Lock:\n");
    print_state(state);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
                                        XKB_STATE_MODS_LOCKED) > 0);
    assert(xkb_state_mod_name_is_active(state, "Mod2",
                                        XKB_STATE_MODS_LOCKED) > 0);
    num_syms = xkb_state_key_get_syms(state, KEY_KP1 + EVDEV_OFFSET, &syms);
    assert(num_syms == 1 && syms[0] == XKB_KEY_KP_1);
    assert(xkb_state_led_name_is_active(state, XKB_LED_NAME_NUM) > 0);

    /* Num Lock unlocked */
    xkb_state_update_key(state, KEY_NUMLOCK + EVDEV_OFFSET, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_NUMLOCK + EVDEV_OFFSET, XKB_KEY_UP);

    /* Switch to group 2 */
    xkb_state_update_key(state, KEY_COMPOSE + EVDEV_OFFSET, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_COMPOSE + EVDEV_OFFSET, XKB_KEY_UP);
    assert(xkb_state_led_name_is_active(state, "Group 2") > 0);
    assert(xkb_state_led_name_is_active(state, XKB_LED_NAME_NUM) == 0);

    /* Switch back to group 1. */
    xkb_state_update_key(state, KEY_COMPOSE + EVDEV_OFFSET, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_COMPOSE + EVDEV_OFFSET, XKB_KEY_UP);

    /* Caps unlocked */
    xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, XKB_KEY_UP);
    assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
                                        XKB_STATE_MODS_EFFECTIVE) == 0);
    assert(xkb_state_led_name_is_active(state, XKB_LED_NAME_CAPS) == 0);
    num_syms = xkb_state_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms);
    assert(num_syms == 1 && syms[0] == XKB_KEY_q);

    /* Multiple symbols */
    num_syms = xkb_state_key_get_syms(state, KEY_6 + EVDEV_OFFSET, &syms);
    assert(num_syms == 5 &&
           syms[0] == XKB_KEY_H && syms[1] == XKB_KEY_E &&
           syms[2] == XKB_KEY_L && syms[3] == XKB_KEY_L &&
           syms[4] == XKB_KEY_O);
    one_sym = xkb_state_key_get_one_sym(state, KEY_6 + EVDEV_OFFSET);
    assert(one_sym == XKB_KEY_NoSymbol);
    xkb_state_update_key(state, KEY_6 + EVDEV_OFFSET, XKB_KEY_DOWN);
    xkb_state_update_key(state, KEY_6 + EVDEV_OFFSET, XKB_KEY_UP);

    one_sym = xkb_state_key_get_one_sym(state, KEY_5 + EVDEV_OFFSET);
    assert(one_sym == XKB_KEY_5);

    xkb_state_unref(state);
}