/* 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/platform/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_window_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); /* Test CSD interactive actions, window menu, and IME caret */ assert(yasl_display_caps(base) & YASL_CAP_CSD); yasl_surface_set_ime_caret(surface, (struct yasl_rect2i){10, 20, 2, 16}); assert(yasl_surface_ime_preedit(surface) == NULL); int begin = -1, end = -1; assert(!yasl_surface_ime_preedit_cursor(surface, &begin, &end)); assert(yasl_surface_show_window_menu(surface, (struct yasl_vector2i){50, 50})); yasl_surface_destroy(surface); yasl_display_close(base); return 0; }