1 /* 2 * QEMU System Emulator 3 * 4 * Copyright (c) 2003-2008 Fabrice Bellard 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include "qapi/error.h" 27 #include "qemu/main-loop.h" 28 #include "qemu/module.h" 29 #include "chardev/char-win.h" 30 #include "chardev/char-win-stdio.h" 31 #include "qom/object.h" 32 33 struct WinStdioChardev { 34 Chardev parent; 35 HANDLE hStdIn; 36 DWORD dwOldMode; 37 HANDLE hInputReadyEvent; 38 HANDLE hInputDoneEvent; 39 HANDLE hInputThread; 40 uint8_t win_stdio_buf; 41 }; 42 typedef struct WinStdioChardev WinStdioChardev; 43 44 DECLARE_INSTANCE_CHECKER(WinStdioChardev, WIN_STDIO_CHARDEV, 45 TYPE_CHARDEV_WIN_STDIO) 46 47 static void win_stdio_wait_func(void *opaque) 48 { 49 Chardev *chr = CHARDEV(opaque); 50 WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque); 51 INPUT_RECORD buf[4]; 52 int ret; 53 DWORD dwSize; 54 int i; 55 56 ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize); 57 58 if (!ret) { 59 /* Avoid error storm */ 60 qemu_del_wait_object(stdio->hStdIn, NULL, NULL); 61 return; 62 } 63 64 for (i = 0; i < dwSize; i++) { 65 KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent; 66 67 if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) { 68 int j; 69 if (kev->uChar.AsciiChar != 0) { 70 for (j = 0; j < kev->wRepeatCount; j++) { 71 if (qemu_chr_be_can_write(chr)) { 72 uint8_t c = kev->uChar.AsciiChar; 73 qemu_chr_be_write(chr, &c, 1); 74 } 75 } 76 } 77 } 78 } 79 } 80 81 static DWORD WINAPI win_stdio_thread(LPVOID param) 82 { 83 WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param); 84 int ret; 85 DWORD dwSize; 86 87 while (1) { 88 89 /* Wait for one byte */ 90 ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL); 91 92 /* Exit in case of error, continue if nothing read */ 93 if (!ret) { 94 break; 95 } 96 if (!dwSize) { 97 continue; 98 } 99 100 /* Some terminal emulator returns \r\n for Enter, just pass \n */ 101 if (stdio->win_stdio_buf == '\r') { 102 continue; 103 } 104 105 /* Signal the main thread and wait until the byte was eaten */ 106 if (!SetEvent(stdio->hInputReadyEvent)) { 107 break; 108 } 109 if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE) 110 != WAIT_OBJECT_0) { 111 break; 112 } 113 } 114 115 qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL); 116 return 0; 117 } 118 119 static void win_stdio_thread_wait_func(void *opaque) 120 { 121 Chardev *chr = CHARDEV(opaque); 122 WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque); 123 124 if (qemu_chr_be_can_write(chr)) { 125 qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1); 126 } 127 128 SetEvent(stdio->hInputDoneEvent); 129 } 130 131 static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo) 132 { 133 WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); 134 DWORD dwMode = 0; 135 136 GetConsoleMode(stdio->hStdIn, &dwMode); 137 138 if (echo) { 139 SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT); 140 } else { 141 SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT); 142 } 143 } 144 145 static void qemu_chr_open_stdio(Chardev *chr, 146 ChardevBackend *backend, 147 bool *be_opened, 148 Error **errp) 149 { 150 ChardevStdio *opts = backend->u.stdio.data; 151 bool stdio_allow_signal = !opts->has_signal || opts->signal; 152 WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); 153 DWORD dwMode; 154 int is_console = 0; 155 156 stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE); 157 if (stdio->hStdIn == INVALID_HANDLE_VALUE) { 158 error_setg(errp, "cannot open stdio: invalid handle"); 159 return; 160 } 161 162 is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0; 163 stdio->dwOldMode = dwMode; 164 165 if (is_console) { 166 if (qemu_add_wait_object(stdio->hStdIn, 167 win_stdio_wait_func, chr)) { 168 error_setg(errp, "qemu_add_wait_object: failed"); 169 goto err1; 170 } 171 } else { 172 DWORD dwId; 173 174 stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 175 stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 176 if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE 177 || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { 178 error_setg(errp, "cannot create event"); 179 goto err2; 180 } 181 if (qemu_add_wait_object(stdio->hInputReadyEvent, 182 win_stdio_thread_wait_func, chr)) { 183 error_setg(errp, "qemu_add_wait_object: failed"); 184 goto err2; 185 } 186 stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, 187 chr, 0, &dwId); 188 189 if (stdio->hInputThread == INVALID_HANDLE_VALUE) { 190 error_setg(errp, "cannot create stdio thread"); 191 goto err3; 192 } 193 } 194 195 dwMode |= ENABLE_LINE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT; 196 197 if (is_console) { 198 /* set the terminal in raw mode */ 199 /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */ 200 if (stdio_allow_signal) { 201 dwMode |= ENABLE_PROCESSED_INPUT; 202 } else { 203 dwMode &= ~ENABLE_PROCESSED_INPUT; 204 } 205 } 206 207 SetConsoleMode(stdio->hStdIn, dwMode); 208 209 qemu_chr_set_echo_win_stdio(chr, false); 210 211 return; 212 213 err3: 214 qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL); 215 err2: 216 CloseHandle(stdio->hInputReadyEvent); 217 CloseHandle(stdio->hInputDoneEvent); 218 err1: 219 qemu_del_wait_object(stdio->hStdIn, NULL, NULL); 220 } 221 222 static void char_win_stdio_finalize(Object *obj) 223 { 224 WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj); 225 226 if (stdio->hStdIn != INVALID_HANDLE_VALUE) { 227 SetConsoleMode(stdio->hStdIn, stdio->dwOldMode); 228 } 229 if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) { 230 CloseHandle(stdio->hInputReadyEvent); 231 } 232 if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) { 233 CloseHandle(stdio->hInputDoneEvent); 234 } 235 if (stdio->hInputThread != INVALID_HANDLE_VALUE) { 236 TerminateThread(stdio->hInputThread, 0); 237 } 238 } 239 240 static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len) 241 { 242 HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 243 DWORD dwSize; 244 int len1; 245 246 len1 = len; 247 248 while (len1 > 0) { 249 if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) { 250 break; 251 } 252 buf += dwSize; 253 len1 -= dwSize; 254 } 255 256 return len - len1; 257 } 258 259 static void char_win_stdio_class_init(ObjectClass *oc, void *data) 260 { 261 ChardevClass *cc = CHARDEV_CLASS(oc); 262 263 cc->open = qemu_chr_open_stdio; 264 cc->chr_write = win_stdio_write; 265 cc->chr_set_echo = qemu_chr_set_echo_win_stdio; 266 } 267 268 static const TypeInfo char_win_stdio_type_info = { 269 .name = TYPE_CHARDEV_WIN_STDIO, 270 .parent = TYPE_CHARDEV, 271 .instance_size = sizeof(WinStdioChardev), 272 .instance_finalize = char_win_stdio_finalize, 273 .class_init = char_win_stdio_class_init, 274 .abstract = true, 275 }; 276 277 static void register_types(void) 278 { 279 type_register_static(&char_win_stdio_type_info); 280 } 281 282 type_init(register_types); 283