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;
}
|