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