summaryrefslogtreecommitdiff
path: root/tests/wayland/test_keyboard.c
blob: 2f55cf6aaf744e3fe708e951ebd97fe58c2342c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "../../src/platform/wayland/keyboard.c"
#include <assert.h>

static struct yasl_private_event next(struct wayland_surface *w, enum yasl_private_event_type type)
{
    struct yasl_private_event ev;
    assert(input_queue_pop(&w->input, &ev));
    assert(ev.type == type && ev.surface == &w->base);
    return ev;
}

static void empty(struct wayland_surface *w)
{
    struct yasl_private_event ev;
    assert(!input_queue_pop(&w->input, &ev));
}

static void layout(struct seat *s, const char *name, const char *variant)
{
    keyboard_text_reset(s);
    if (s->xkb_state) xkb_state_unref(s->xkb_state);
    if (s->xkb_keymap) xkb_keymap_unref(s->xkb_keymap);
    struct xkb_rule_names names = {.layout = name, .variant = variant};
    s->xkb_keymap = xkb_keymap_new_from_names(s->conn->xkb_ctx, &names, 0);
    assert(s->xkb_keymap);
    s->xkb_state = xkb_state_new(s->xkb_keymap);
    assert(s->xkb_state);
    s->input_mods_ready = false;
    s->mod_alt = xkb_keymap_mod_get_index(s->xkb_keymap, XKB_MOD_NAME_ALT);
}

static void modifiers(struct seat *s, const char *name)
{
    xkb_mod_mask_t mask = name ? 1u << xkb_keymap_mod_get_index(s->xkb_keymap, name) : 0;
    xkb_state_update_mask(s->xkb_state, mask, 0, 0, 0, 0, 0);
}

static void key(struct seat *s, uint32_t code, bool down)
{
    keyboard_listener.key(s, NULL, 1, 0, code, down ?
        WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED);
}

int main(void)
{
    struct wayland_display d = {.base.ops = &yasl_private_wayland_backend};
    d.xkb_ctx = xkb_context_new(0);
    assert(d.xkb_ctx);
    struct wayland_surface w = {.base.display = &d.base, .conn = &d};
    wayland_surface_set_callbacks(&w, NULL, NULL);
    struct seat s = {.conn = &d, .keyboard_focus = &w, .repeat_fd = -1,
        .compose_attempted = true};
    const char compose[] = "<dead_acute> <e> : \"é\" U00E9\n";
    struct xkb_compose_table *table = xkb_compose_table_new_from_buffer(d.xkb_ctx,
        compose, sizeof(compose) - 1, "C.UTF-8", XKB_COMPOSE_FORMAT_TEXT_V1, 0);
    assert(table);
    s.compose = xkb_compose_state_new(table, 0);
    assert(s.compose);
    xkb_compose_table_unref(table);
    layout(&s, "us", NULL);
    key(&s, KEY_A, true);
    struct yasl_private_event ev = next(&w, YASL_PRIVATE_EVENT_KEY);
    assert(ev.key.key == YASL_KEY_A && ev.key.native_code == KEY_A);
    assert(ev.key.codepoint == 'a' && ev.key.shifted == 'A' && !ev.key.mods);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "a") == 0);
    modifiers(&s, XKB_MOD_NAME_SHIFT);
    key(&s, KEY_A, false); /* Modifier changes cannot change physical identity. */
    ev = next(&w, YASL_PRIVATE_EVENT_KEY);
    assert(ev.key.key == YASL_KEY_A && ev.key.action == YASL_KEY_RELEASE);
    assert(ev.key.mods == YASL_PRIVATE_MOD_SHIFT);
    empty(&w);
    key(&s, KEY_A, true);
    next(&w, YASL_PRIVATE_EVENT_KEY);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "A") == 0);
    modifiers(&s, XKB_MOD_NAME_CAPS);
    key(&s, KEY_A, true);
    assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.mods == YASL_PRIVATE_MOD_CAPS);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "A") == 0);
    const char *shortcuts[] = {XKB_MOD_NAME_CTRL, XKB_MOD_NAME_ALT, XKB_MOD_NAME_LOGO};
    for (size_t i = 0; i < sizeof(shortcuts)/sizeof(shortcuts[0]); i++) {
        modifiers(&s, shortcuts[i]);
        key(&s, KEY_A, true);
        next(&w, YASL_PRIVATE_EVENT_KEY);
        empty(&w); /* Shortcut characters never leak into committed text. */
    }
    layout(&s, "fr", NULL);
    key(&s, KEY_A, true);
    ev = next(&w, YASL_PRIVATE_EVENT_KEY);
    assert(ev.key.key == YASL_KEY_A && ev.key.codepoint == 'q');
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "q") == 0);
    layout(&s, "de", NULL);
    modifiers(&s, "Mod5"); /* AltGr is text input, not an Alt shortcut. */
    key(&s, KEY_E, true);
    assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.key == YASL_KEY_E);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "€") == 0);
    layout(&s, "us", "intl");
    key(&s, KEY_APOSTROPHE, true);
    next(&w, YASL_PRIVATE_EVENT_KEY);
    empty(&w);
    key(&s, KEY_E, true);
    next(&w, YASL_PRIVATE_EVENT_KEY);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "é") == 0);
    assert(keyboard_dispatch_key(&s, &w, KEY_E, YASL_KEY_REPEAT));
    assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.action == YASL_KEY_REPEAT);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0);
    key(&s, KEY_APOSTROPHE, true);
    next(&w, YASL_PRIVATE_EVENT_KEY);
    keyboard_reset_keys(&s, &w);
    key(&s, KEY_E, true);
    next(&w, YASL_PRIVATE_EVENT_KEY);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0);
    key(&s, KEY_APOSTROPHE, true);
    next(&w, YASL_PRIVATE_EVENT_KEY);
    s.ime_focus = &w;
    w.base.text_input_active = true;
    text_input_listener.commit_string(&s, NULL, "字");
    text_input_listener.done(&s, NULL, 0);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "字") == 0);
    key(&s, KEY_E, true);
    next(&w, YASL_PRIVATE_EVENT_KEY);
    assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0);
    empty(&w);
    xkb_compose_state_unref(s.compose);
    xkb_state_unref(s.xkb_state);
    xkb_keymap_unref(s.xkb_keymap);
    xkb_context_unref(d.xkb_ctx);
    input_queue_clear(&w.input);
    return 0;
}