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