1 /* 2 * Copyright 2014 Ran Benita <ran234 (at) gmail.com> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 #include <stdio.h> 25 #include <spawn.h> 26 #include <unistd.h> 27 #include <assert.h> 28 #include <signal.h> 29 #include <sys/types.h> 30 #include <sys/wait.h> 31 32 #include "test.h" 33 #include "xkbcommon/xkbcommon-x11.h" 34 35 int 36 main(void) 37 { 38 struct xkb_context *ctx = test_get_context(0); 39 struct xkb_keymap *keymap; 40 xcb_connection_t *conn; 41 int32_t device_id; 42 int pipefds[2]; 43 int ret, status; 44 char displayfd[128], display[128]; 45 char *search_path, *search_path_arg, *xkb_path; 46 char *original, *dump; 47 char *envp[] = { NULL }; 48 char *xvfb_argv[] = { "Xvfb", "-displayfd", displayfd, NULL }; 49 pid_t xvfb_pid; 50 char *xkbcomp_argv[] = { "xkbcomp", "-I", NULL /* search_path_arg */, 51 NULL /* xkb_path */, display, NULL }; 52 pid_t xkbcomp_pid; 53 54 /* 55 * What all of this mess does is: 56 * 1. Launch Xvfb on the next available DISPLAY. Xvfb reports the 57 * display number to an fd passed with -displayfd once it's 58 * initialized. We pass a pipe there to read it. 59 * 2. Make an xcb connection to this display. 60 * 3. Launch xkbcomp to change the keymap of the new display (doing 61 * this programmatically is major work [which we may yet do some 62 * day for xkbcommon-x11] so we use xkbcomp for now). 63 * 4. Download the keymap back from the display using xkbcommon-x11. 64 * 5. Compare received keymap to the uploaded keymap. 65 * 6. Kill the server & clean up. 66 */ 67 68 ret = pipe(pipefds); 69 assert(ret == 0); 70 71 ret = snprintf(displayfd, sizeof(displayfd), "%d", pipefds[1]); 72 assert(ret >= 0 && ret < sizeof(displayfd)); 73 74 ret = posix_spawnp(&xvfb_pid, "Xvfb", NULL, NULL, xvfb_argv, envp); 75 if (ret != 0) { 76 ret = SKIP_TEST; 77 goto err_ctx; 78 } 79 80 close(pipefds[1]); 81 82 display[0] = ':'; 83 ret = read(pipefds[0], display + 1, sizeof(display) - 1); 84 if (ret <= 0 || 1 + ret >= sizeof(display) - 1) { 85 ret = SKIP_TEST; 86 goto err_xvfd; 87 } 88 if (display[ret] == '\n') 89 display[ret] = '\0'; 90 display[1 + ret] = '\0'; 91 close(pipefds[0]); 92 93 conn = xcb_connect(display, NULL); 94 if (xcb_connection_has_error(conn)) { 95 ret = SKIP_TEST; 96 goto err_xvfd; 97 } 98 ret = xkb_x11_setup_xkb_extension(conn, 99 XKB_X11_MIN_MAJOR_XKB_VERSION, 100 XKB_X11_MIN_MINOR_XKB_VERSION, 101 XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, 102 NULL, NULL, NULL, NULL); 103 if (!ret) { 104 ret = SKIP_TEST; 105 goto err_xcb; 106 } 107 device_id = xkb_x11_get_core_keyboard_device_id(conn); 108 assert(device_id != -1); 109 110 xkb_path = test_get_path("keymaps/host.xkb"); 111 search_path = test_get_path(""); 112 assert(search_path); 113 ret = asprintf(&search_path_arg, "-I%s", search_path); 114 assert(ret >= 0); 115 xkbcomp_argv[2] = search_path_arg; 116 xkbcomp_argv[3] = xkb_path; 117 ret = posix_spawnp(&xkbcomp_pid, "xkbcomp", NULL, NULL, xkbcomp_argv, envp); 118 free(search_path_arg); 119 free(search_path); 120 free(xkb_path); 121 if (ret != 0) { 122 ret = SKIP_TEST; 123 goto err_xcb; 124 } 125 ret = waitpid(xkbcomp_pid, &status, 0); 126 if (ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { 127 ret = SKIP_TEST; 128 goto err_xcb; 129 } 130 131 keymap = xkb_x11_keymap_new_from_device(ctx, conn, device_id, 132 XKB_KEYMAP_COMPILE_NO_FLAGS); 133 assert(keymap); 134 135 original = test_read_file("keymaps/host.xkb"); 136 assert(original); 137 138 dump = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT); 139 assert(dump); 140 141 ret = streq(original, dump); 142 if (!ret) { 143 fprintf(stderr, 144 "round-trip test failed: dumped map differs from original\n"); 145 fprintf(stderr, "length: dumped %lu, original %lu\n", 146 (unsigned long) strlen(dump), 147 (unsigned long) strlen(original)); 148 fprintf(stderr, "dumped map:\n"); 149 fprintf(stderr, "%s\n", dump); 150 ret = 1; 151 goto err_dump; 152 } 153 154 ret = 0; 155 err_dump: 156 free(original); 157 free(dump); 158 xkb_keymap_unref(keymap); 159 err_xcb: 160 xcb_disconnect(conn); 161 err_xvfd: 162 kill(xvfb_pid, SIGTERM); 163 err_ctx: 164 xkb_context_unref(ctx); 165 return ret; 166 } 167