summaryrefslogtreecommitdiff
path: root/tests/x11/test_input.c
blob: 11dca4fb0ddc058d4c7b7c5475607e1e9e0ce6ab (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
/* An isolated real X server supplies the keymap; disabling this surface's XIC
 * deliberately exercises text insertion when no input method is available. */
#include "../../src/x11/window.c"

static void check_selection_encoding(struct x11_display *d, struct x11_surface *s)
{
    Window requestor = XCreateSimpleWindow(d->native, DefaultRootWindow(d->native),
        0, 0, 1, 1, 0, 0, 0);
    Atom property = XInternAtom(d->native, "YASL_TEST_STRING", False);
    XSelectionRequestEvent request = {.display = d->native, .requestor = requestor,
        .selection = d->clipboard, .target = XA_STRING, .property = property};
    yasl_display_copy(&d->base, YASL_CLIPBOARD, "café £");
    selection_request(d, &request);
    Atom actual;
    int format;
    unsigned long count, left;
    unsigned char *data = NULL;
    assert(XGetWindowProperty(d->native, requestor, property, 0, 100, True,
        AnyPropertyType, &actual, &format, &count, &left, &data) == Success);
    assert(actual == XA_STRING && format == 8 && count == 6);
    assert(memcmp(data, "caf\xe9 \xa3", count) == 0);
    XFree(data);
    yasl_display_copy(&d->base, YASL_CLIPBOARD, "字");
    selection_request(d, &request);
    assert(XGetWindowProperty(d->native, requestor, property, 0, 100, True,
        AnyPropertyType, &actual, &format, &count, &left, &data) == Success);
    assert(actual == None); /* Never mislabel UTF-8 as Latin-1. */
    if (data) XFree(data);
    request.target = d->targets;
    selection_request(d, &request);
    assert(XGetWindowProperty(d->native, requestor, property, 0, 100, True,
        AnyPropertyType, &actual, &format, &count, &left, &data) == Success);
    assert(actual == XA_ATOM && format == 32 && count == 2);
    assert(((Atom *)data)[0] == d->targets && ((Atom *)data)[1] == d->utf8);
    XFree(data);
    XDestroyWindow(d->native, requestor);

    /* Simulate a legacy owner returning STRING to the paste conversion window,
     * then an empty UTF-8 owner and malformed UTF-8. */
    const struct { Atom type; const char *bytes; size_t len; const char *expected; } cases[] = {
        {XA_STRING, "caf\xe9 \xa3", 6, "café £"},
        {d->utf8, "", 0, ""},
        {d->utf8, "\xff", 1, NULL},
    };
    for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
        struct paste_request *pending = calloc(1, sizeof(*pending));
        assert(pending);
        pending->window = XCreateSimpleWindow(d->native, DefaultRootWindow(d->native),
            0, 0, 1, 1, 0, 0, 0);
        pending->target = s;
        pending->next = d->pastes;
        d->pastes = pending;
        XChangeProperty(d->native, pending->window, property, cases[i].type, 8,
            PropModeReplace, (const unsigned char *)cases[i].bytes, (int)cases[i].len);
        XSelectionEvent reply = {.requestor = pending->window, .property = property};
        selection_notify(d, &reply);
        struct yasl_input_event event;
        if (cases[i].expected) {
            assert(yasl_surface_input_poll(&s->base, &event));
            assert(event.kind == YASL_INPUT_PASTE);
            assert(event.paste.len == strlen(cases[i].expected));
            assert(strcmp(event.paste.text, cases[i].expected) == 0);
        }
        assert(!yasl_surface_input_poll(&s->base, &event));
    }
}

int main(void)
{
    struct yasl_display_spec ds = {.backend = YASL_BACKEND_X11};
    struct yasl_display *base = yasl_display_open(NULL, &ds);
    assert(base);
    struct x11_display *d = (struct x11_display *)base;
    struct yasl_surface_spec spec = {.title = "Keyboard fallback", .size = {100, 100}};
    struct yasl_surface *surface = yasl_surface_create_toplevel(base, &spec);
    assert(surface);
    struct x11_surface *s = (struct x11_surface *)surface;
    XIC saved_ic = s->ic;
    s->ic = NULL;
    unsigned code = XKeysymToKeycode(d->native, XK_a);
    int width = 0;
    KeySym *saved = XGetKeyboardMapping(d->native, code, 1, &width);
    assert(saved && width >= 2);
    KeySym *symbols = calloc((size_t)width, sizeof(*symbols));
    assert(symbols);
    const struct { KeySym symbol, shifted; const char *text; uint32_t label; } cases[] = {
        {XK_eacute, XK_Eacute, "é", 0xe9},
        {0x01000416, 0x01000416, "Ж", 0x416},
        {0x0101f600, 0x0101f600, "😀", 0x1f600},
    };
    for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
        symbols[0] = cases[i].symbol;
        symbols[1] = cases[i].shifted;
        XChangeKeyboardMapping(d->native, code, width, symbols, 1);
        XSync(d->native, False);
        dispatch(base);
        input_queue_clear(&s->input);
        XKeyEvent native = {.type = KeyPress, .display = d->native,
            .window = s->window, .keycode = code};
        key_event(s, &native, false);
        struct yasl_input_event event;
        assert(yasl_surface_input_poll(surface, &event));
        assert(event.kind == YASL_INPUT_KEY && event.key.key == YASL_KEY_A);
        assert(event.key.codepoint == cases[i].label);
        assert(yasl_surface_input_poll(surface, &event));
        assert(event.kind == YASL_INPUT_TEXT && event.text.len == strlen(cases[i].text));
        assert(strcmp(event.text.text, cases[i].text) == 0);
        assert(!yasl_surface_input_poll(surface, &event));
        native.type = KeyRelease;
        key_event(s, &native, false);
        assert(yasl_surface_input_poll(surface, &event) && event.kind == YASL_INPUT_KEY);
        assert(!yasl_surface_input_poll(surface, &event));
    }
    XChangeKeyboardMapping(d->native, code, width, saved, 1);
    XSync(d->native, False);
    free(symbols);
    XFree(saved);
    s->ic = saved_ic;
    check_selection_encoding(d, s);
    yasl_surface_destroy(surface);
    yasl_display_close(base);
    return 0;
}