]>
Commit | Line | Data |
---|---|---|
0e58f177 MAL |
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 | #include "qemu/osdep.h" | |
25 | #include "qapi/error.h" | |
8228e353 MAL |
26 | #include "chardev/char-win.h" |
27 | #include "chardev/char-win-stdio.h" | |
0e58f177 MAL |
28 | |
29 | typedef struct { | |
30 | Chardev parent; | |
31 | HANDLE hStdIn; | |
32 | HANDLE hInputReadyEvent; | |
33 | HANDLE hInputDoneEvent; | |
34 | HANDLE hInputThread; | |
35 | uint8_t win_stdio_buf; | |
36 | } WinStdioChardev; | |
37 | ||
38 | #define WIN_STDIO_CHARDEV(obj) \ | |
39 | OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO) | |
40 | ||
41 | static void win_stdio_wait_func(void *opaque) | |
42 | { | |
43 | Chardev *chr = CHARDEV(opaque); | |
44 | WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque); | |
45 | INPUT_RECORD buf[4]; | |
46 | int ret; | |
47 | DWORD dwSize; | |
48 | int i; | |
49 | ||
50 | ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize); | |
51 | ||
52 | if (!ret) { | |
53 | /* Avoid error storm */ | |
54 | qemu_del_wait_object(stdio->hStdIn, NULL, NULL); | |
55 | return; | |
56 | } | |
57 | ||
58 | for (i = 0; i < dwSize; i++) { | |
59 | KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent; | |
60 | ||
61 | if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) { | |
62 | int j; | |
63 | if (kev->uChar.AsciiChar != 0) { | |
64 | for (j = 0; j < kev->wRepeatCount; j++) { | |
65 | if (qemu_chr_be_can_write(chr)) { | |
66 | uint8_t c = kev->uChar.AsciiChar; | |
67 | qemu_chr_be_write(chr, &c, 1); | |
68 | } | |
69 | } | |
70 | } | |
71 | } | |
72 | } | |
73 | } | |
74 | ||
75 | static DWORD WINAPI win_stdio_thread(LPVOID param) | |
76 | { | |
77 | WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param); | |
78 | int ret; | |
79 | DWORD dwSize; | |
80 | ||
81 | while (1) { | |
82 | ||
83 | /* Wait for one byte */ | |
84 | ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL); | |
85 | ||
86 | /* Exit in case of error, continue if nothing read */ | |
87 | if (!ret) { | |
88 | break; | |
89 | } | |
90 | if (!dwSize) { | |
91 | continue; | |
92 | } | |
93 | ||
94 | /* Some terminal emulator returns \r\n for Enter, just pass \n */ | |
95 | if (stdio->win_stdio_buf == '\r') { | |
96 | continue; | |
97 | } | |
98 | ||
99 | /* Signal the main thread and wait until the byte was eaten */ | |
100 | if (!SetEvent(stdio->hInputReadyEvent)) { | |
101 | break; | |
102 | } | |
103 | if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE) | |
104 | != WAIT_OBJECT_0) { | |
105 | break; | |
106 | } | |
107 | } | |
108 | ||
109 | qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL); | |
110 | return 0; | |
111 | } | |
112 | ||
113 | static void win_stdio_thread_wait_func(void *opaque) | |
114 | { | |
115 | Chardev *chr = CHARDEV(opaque); | |
116 | WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque); | |
117 | ||
118 | if (qemu_chr_be_can_write(chr)) { | |
119 | qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1); | |
120 | } | |
121 | ||
122 | SetEvent(stdio->hInputDoneEvent); | |
123 | } | |
124 | ||
125 | static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo) | |
126 | { | |
127 | WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); | |
128 | DWORD dwMode = 0; | |
129 | ||
130 | GetConsoleMode(stdio->hStdIn, &dwMode); | |
131 | ||
132 | if (echo) { | |
133 | SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT); | |
134 | } else { | |
135 | SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT); | |
136 | } | |
137 | } | |
138 | ||
139 | static void qemu_chr_open_stdio(Chardev *chr, | |
140 | ChardevBackend *backend, | |
141 | bool *be_opened, | |
142 | Error **errp) | |
143 | { | |
144 | WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); | |
145 | DWORD dwMode; | |
146 | int is_console = 0; | |
147 | ||
148 | stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE); | |
149 | if (stdio->hStdIn == INVALID_HANDLE_VALUE) { | |
150 | error_setg(errp, "cannot open stdio: invalid handle"); | |
151 | return; | |
152 | } | |
153 | ||
154 | is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0; | |
155 | ||
156 | if (is_console) { | |
157 | if (qemu_add_wait_object(stdio->hStdIn, | |
158 | win_stdio_wait_func, chr)) { | |
159 | error_setg(errp, "qemu_add_wait_object: failed"); | |
160 | goto err1; | |
161 | } | |
162 | } else { | |
163 | DWORD dwId; | |
164 | ||
165 | stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | |
166 | stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); | |
167 | if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE | |
168 | || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { | |
169 | error_setg(errp, "cannot create event"); | |
170 | goto err2; | |
171 | } | |
172 | if (qemu_add_wait_object(stdio->hInputReadyEvent, | |
173 | win_stdio_thread_wait_func, chr)) { | |
174 | error_setg(errp, "qemu_add_wait_object: failed"); | |
175 | goto err2; | |
176 | } | |
177 | stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, | |
178 | chr, 0, &dwId); | |
179 | ||
180 | if (stdio->hInputThread == INVALID_HANDLE_VALUE) { | |
181 | error_setg(errp, "cannot create stdio thread"); | |
182 | goto err3; | |
183 | } | |
184 | } | |
185 | ||
186 | dwMode |= ENABLE_LINE_INPUT; | |
187 | ||
188 | if (is_console) { | |
189 | /* set the terminal in raw mode */ | |
190 | /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */ | |
191 | dwMode |= ENABLE_PROCESSED_INPUT; | |
192 | } | |
193 | ||
194 | SetConsoleMode(stdio->hStdIn, dwMode); | |
195 | ||
196 | qemu_chr_set_echo_win_stdio(chr, false); | |
197 | ||
198 | return; | |
199 | ||
200 | err3: | |
201 | qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL); | |
202 | err2: | |
203 | CloseHandle(stdio->hInputReadyEvent); | |
204 | CloseHandle(stdio->hInputDoneEvent); | |
205 | err1: | |
206 | qemu_del_wait_object(stdio->hStdIn, NULL, NULL); | |
207 | } | |
208 | ||
209 | static void char_win_stdio_finalize(Object *obj) | |
210 | { | |
211 | WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj); | |
212 | ||
213 | if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) { | |
214 | CloseHandle(stdio->hInputReadyEvent); | |
215 | } | |
216 | if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) { | |
217 | CloseHandle(stdio->hInputDoneEvent); | |
218 | } | |
219 | if (stdio->hInputThread != INVALID_HANDLE_VALUE) { | |
220 | TerminateThread(stdio->hInputThread, 0); | |
221 | } | |
222 | } | |
223 | ||
224 | static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len) | |
225 | { | |
226 | HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); | |
227 | DWORD dwSize; | |
228 | int len1; | |
229 | ||
230 | len1 = len; | |
231 | ||
232 | while (len1 > 0) { | |
233 | if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) { | |
234 | break; | |
235 | } | |
236 | buf += dwSize; | |
237 | len1 -= dwSize; | |
238 | } | |
239 | ||
240 | return len - len1; | |
241 | } | |
242 | ||
243 | static void char_win_stdio_class_init(ObjectClass *oc, void *data) | |
244 | { | |
245 | ChardevClass *cc = CHARDEV_CLASS(oc); | |
246 | ||
247 | cc->open = qemu_chr_open_stdio; | |
248 | cc->chr_write = win_stdio_write; | |
249 | cc->chr_set_echo = qemu_chr_set_echo_win_stdio; | |
250 | } | |
251 | ||
252 | static const TypeInfo char_win_stdio_type_info = { | |
253 | .name = TYPE_CHARDEV_WIN_STDIO, | |
254 | .parent = TYPE_CHARDEV, | |
255 | .instance_size = sizeof(WinStdioChardev), | |
256 | .instance_finalize = char_win_stdio_finalize, | |
257 | .class_init = char_win_stdio_class_init, | |
258 | .abstract = true, | |
259 | }; | |
260 | ||
261 | static void register_types(void) | |
262 | { | |
263 | type_register_static(&char_win_stdio_type_info); | |
264 | } | |
265 | ||
266 | type_init(register_types); |