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