]>
Commit | Line | Data |
---|---|---|
e7f0ad58 FB |
1 | /* |
2 | * QEMU graphical console | |
3 | * | |
4 | * Copyright (c) 2004 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 "vl.h" | |
25 | ||
26 | #define DEFAULT_BACKSCROLL 512 | |
27 | #define MAX_CONSOLES 12 | |
28 | ||
29 | #define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) | |
30 | #define RGB(r, g, b) RGBA(r, g, b, 0xff) | |
31 | ||
32 | typedef struct TextCell { | |
33 | uint8_t ch; | |
34 | uint8_t bgcol:4; | |
35 | uint8_t fgcol:4; | |
36 | } TextCell; | |
37 | ||
38 | #define MAX_ESC_PARAMS 3 | |
39 | ||
40 | enum TTYState { | |
41 | TTY_STATE_NORM, | |
42 | TTY_STATE_ESC, | |
43 | TTY_STATE_CSI, | |
44 | }; | |
45 | ||
46 | struct TextConsole { | |
47 | int text_console; /* true if text console */ | |
48 | DisplayState *ds; | |
49 | int g_width, g_height; | |
50 | int width; | |
51 | int height; | |
52 | int total_height; | |
53 | int backscroll_height; | |
54 | int fgcol; | |
55 | int bgcol; | |
56 | int x, y; | |
57 | int y_displayed; | |
58 | int y_base; | |
59 | TextCell *cells; | |
60 | ||
61 | enum TTYState state; | |
62 | int esc_params[MAX_ESC_PARAMS]; | |
63 | int nb_esc_params; | |
64 | ||
65 | /* kbd read handler */ | |
66 | IOReadHandler *fd_read; | |
67 | void *fd_opaque; | |
68 | }; | |
69 | ||
70 | static TextConsole *active_console; | |
71 | static TextConsole *consoles[MAX_CONSOLES]; | |
72 | static int nb_consoles = 0; | |
73 | ||
74 | /* convert a RGBA color to a color index usable in graphic primitives */ | |
75 | static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) | |
76 | { | |
77 | unsigned int r, g, b, color; | |
78 | ||
79 | switch(ds->depth) { | |
80 | #if 0 | |
81 | case 8: | |
82 | r = (rgba >> 16) & 0xff; | |
83 | g = (rgba >> 8) & 0xff; | |
84 | b = (rgba) & 0xff; | |
85 | color = (rgb_to_index[r] * 6 * 6) + | |
86 | (rgb_to_index[g] * 6) + | |
87 | (rgb_to_index[b]); | |
88 | break; | |
89 | #endif | |
90 | case 15: | |
91 | r = (rgba >> 16) & 0xff; | |
92 | g = (rgba >> 8) & 0xff; | |
93 | b = (rgba) & 0xff; | |
94 | color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); | |
95 | break; | |
96 | case 16: | |
97 | r = (rgba >> 16) & 0xff; | |
98 | g = (rgba >> 8) & 0xff; | |
99 | b = (rgba) & 0xff; | |
100 | color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); | |
101 | break; | |
102 | case 32: | |
103 | default: | |
104 | color = rgba; | |
105 | break; | |
106 | } | |
107 | return color; | |
108 | } | |
109 | ||
110 | static void vga_fill_rect (DisplayState *ds, | |
111 | int posx, int posy, int width, int height, uint32_t color) | |
112 | { | |
113 | uint8_t *d, *d1; | |
114 | int x, y, bpp; | |
115 | ||
116 | bpp = (ds->depth + 7) >> 3; | |
117 | d1 = ds->data + | |
118 | ds->linesize * posy + bpp * posx; | |
119 | for (y = 0; y < height; y++) { | |
120 | d = d1; | |
121 | switch(bpp) { | |
122 | case 1: | |
123 | for (x = 0; x < width; x++) { | |
124 | *((uint8_t *)d) = color; | |
125 | d++; | |
126 | } | |
127 | break; | |
128 | case 2: | |
129 | for (x = 0; x < width; x++) { | |
130 | *((uint16_t *)d) = color; | |
131 | d += 2; | |
132 | } | |
133 | break; | |
134 | case 4: | |
135 | for (x = 0; x < width; x++) { | |
136 | *((uint32_t *)d) = color; | |
137 | d += 4; | |
138 | } | |
139 | break; | |
140 | } | |
141 | d1 += ds->linesize; | |
142 | } | |
143 | } | |
144 | ||
145 | /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ | |
146 | static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h) | |
147 | { | |
148 | const uint8_t *s; | |
149 | uint8_t *d; | |
150 | int wb, y, bpp; | |
151 | ||
152 | bpp = (ds->depth + 7) >> 3; | |
153 | wb = w * bpp; | |
154 | if (yd <= ys) { | |
155 | s = ds->data + | |
156 | ds->linesize * ys + bpp * xs; | |
157 | d = ds->data + | |
158 | ds->linesize * yd + bpp * xd; | |
159 | for (y = 0; y < h; y++) { | |
160 | memmove(d, s, wb); | |
161 | d += ds->linesize; | |
162 | s += ds->linesize; | |
163 | } | |
164 | } else { | |
165 | s = ds->data + | |
166 | ds->linesize * (ys + h - 1) + bpp * xs; | |
167 | d = ds->data + | |
168 | ds->linesize * (yd + h - 1) + bpp * xd; | |
169 | for (y = 0; y < h; y++) { | |
170 | memmove(d, s, wb); | |
171 | d -= ds->linesize; | |
172 | s -= ds->linesize; | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | /***********************************************************/ | |
178 | /* basic char display */ | |
179 | ||
180 | #define FONT_HEIGHT 16 | |
181 | #define FONT_WIDTH 8 | |
182 | ||
183 | #include "vgafont.h" | |
184 | ||
185 | #define cbswap_32(__x) \ | |
186 | ((uint32_t)( \ | |
187 | (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ | |
188 | (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ | |
189 | (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ | |
190 | (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) | |
191 | ||
192 | #ifdef WORDS_BIGENDIAN | |
193 | #define PAT(x) x | |
194 | #else | |
195 | #define PAT(x) cbswap_32(x) | |
196 | #endif | |
197 | ||
198 | static const uint32_t dmask16[16] = { | |
199 | PAT(0x00000000), | |
200 | PAT(0x000000ff), | |
201 | PAT(0x0000ff00), | |
202 | PAT(0x0000ffff), | |
203 | PAT(0x00ff0000), | |
204 | PAT(0x00ff00ff), | |
205 | PAT(0x00ffff00), | |
206 | PAT(0x00ffffff), | |
207 | PAT(0xff000000), | |
208 | PAT(0xff0000ff), | |
209 | PAT(0xff00ff00), | |
210 | PAT(0xff00ffff), | |
211 | PAT(0xffff0000), | |
212 | PAT(0xffff00ff), | |
213 | PAT(0xffffff00), | |
214 | PAT(0xffffffff), | |
215 | }; | |
216 | ||
217 | static const uint32_t dmask4[4] = { | |
218 | PAT(0x00000000), | |
219 | PAT(0x0000ffff), | |
220 | PAT(0xffff0000), | |
221 | PAT(0xffffffff), | |
222 | }; | |
223 | ||
224 | static uint32_t color_table[8]; | |
225 | ||
226 | static const uint32_t color_table_rgb[8] = { | |
227 | RGB(0x00, 0x00, 0x00), | |
228 | RGB(0xff, 0x00, 0x00), | |
229 | RGB(0x00, 0xff, 0x00), | |
230 | RGB(0xff, 0xff, 0x00), | |
231 | RGB(0x00, 0x00, 0xff), | |
232 | RGB(0xff, 0x00, 0xff), | |
233 | RGB(0x00, 0xff, 0xff), | |
234 | RGB(0xff, 0xff, 0xff), | |
235 | }; | |
236 | ||
237 | static inline unsigned int col_expand(DisplayState *ds, unsigned int col) | |
238 | { | |
239 | switch(ds->depth) { | |
240 | case 8: | |
241 | col |= col << 8; | |
242 | col |= col << 16; | |
243 | break; | |
244 | case 15: | |
245 | case 16: | |
246 | col |= col << 16; | |
247 | break; | |
248 | default: | |
249 | break; | |
250 | } | |
251 | ||
252 | return col; | |
253 | } | |
254 | ||
255 | static void vga_putcharxy(DisplayState *ds, int x, int y, int ch, | |
256 | unsigned int fgcol, unsigned int bgcol) | |
257 | { | |
258 | uint8_t *d; | |
259 | const uint8_t *font_ptr; | |
260 | unsigned int font_data, linesize, xorcol, bpp; | |
261 | int i; | |
262 | ||
263 | bpp = (ds->depth + 7) >> 3; | |
264 | d = ds->data + | |
265 | ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH; | |
266 | linesize = ds->linesize; | |
267 | font_ptr = vgafont16 + FONT_HEIGHT * ch; | |
268 | xorcol = bgcol ^ fgcol; | |
269 | switch(ds->depth) { | |
270 | case 8: | |
271 | for(i = 0; i < FONT_HEIGHT; i++) { | |
272 | font_data = *font_ptr++; | |
273 | ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol; | |
274 | ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; | |
275 | d += linesize; | |
276 | } | |
277 | break; | |
278 | case 16: | |
279 | case 15: | |
280 | for(i = 0; i < FONT_HEIGHT; i++) { | |
281 | font_data = *font_ptr++; | |
282 | ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol; | |
283 | ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol; | |
284 | ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol; | |
285 | ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; | |
286 | d += linesize; | |
287 | } | |
288 | break; | |
289 | case 32: | |
290 | for(i = 0; i < FONT_HEIGHT; i++) { | |
291 | font_data = *font_ptr++; | |
292 | ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; | |
293 | ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; | |
294 | ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; | |
295 | ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; | |
296 | ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; | |
297 | ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; | |
298 | ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; | |
299 | ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; | |
300 | d += linesize; | |
301 | } | |
302 | break; | |
303 | } | |
304 | } | |
305 | ||
306 | static void text_console_resize(TextConsole *s) | |
307 | { | |
308 | TextCell *cells, *c, *c1; | |
309 | int w1, x, y, last_width; | |
310 | ||
311 | last_width = s->width; | |
312 | s->width = s->g_width / FONT_WIDTH; | |
313 | s->height = s->g_height / FONT_HEIGHT; | |
314 | ||
315 | w1 = last_width; | |
316 | if (s->width < w1) | |
317 | w1 = s->width; | |
318 | ||
319 | cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell)); | |
320 | for(y = 0; y < s->total_height; y++) { | |
321 | c = &cells[y * s->width]; | |
322 | if (w1 > 0) { | |
323 | c1 = &s->cells[y * last_width]; | |
324 | for(x = 0; x < w1; x++) { | |
325 | *c++ = *c1++; | |
326 | } | |
327 | } | |
328 | for(x = w1; x < s->width; x++) { | |
329 | c->ch = ' '; | |
330 | c->fgcol = 7; | |
331 | c->bgcol = 0; | |
332 | c++; | |
333 | } | |
334 | } | |
335 | free(s->cells); | |
336 | s->cells = cells; | |
337 | } | |
338 | ||
339 | static void update_xy(TextConsole *s, int x, int y) | |
340 | { | |
341 | TextCell *c; | |
342 | int y1, y2; | |
343 | ||
344 | if (s == active_console) { | |
345 | y1 = (s->y_base + y) % s->total_height; | |
346 | y2 = y1 - s->y_displayed; | |
347 | if (y2 < 0) | |
348 | y2 += s->total_height; | |
349 | if (y2 < s->height) { | |
350 | c = &s->cells[y1 * s->width + x]; | |
351 | vga_putcharxy(s->ds, x, y2, c->ch, | |
352 | color_table[c->fgcol], color_table[c->bgcol]); | |
353 | dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT, | |
354 | FONT_WIDTH, FONT_HEIGHT); | |
355 | } | |
356 | } | |
357 | } | |
358 | ||
359 | static void console_show_cursor(TextConsole *s, int show) | |
360 | { | |
361 | TextCell *c; | |
362 | int y, y1; | |
363 | ||
364 | if (s == active_console) { | |
365 | y1 = (s->y_base + s->y) % s->total_height; | |
366 | y = y1 - s->y_displayed; | |
367 | if (y < 0) | |
368 | y += s->total_height; | |
369 | if (y < s->height) { | |
370 | c = &s->cells[y1 * s->width + s->x]; | |
371 | if (show) { | |
372 | vga_putcharxy(s->ds, s->x, y, c->ch, | |
373 | color_table[0], color_table[7]); | |
374 | } else { | |
375 | vga_putcharxy(s->ds, s->x, y, c->ch, | |
376 | color_table[c->fgcol], color_table[c->bgcol]); | |
377 | } | |
378 | dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT, | |
379 | FONT_WIDTH, FONT_HEIGHT); | |
380 | } | |
381 | } | |
382 | } | |
383 | ||
384 | static void console_refresh(TextConsole *s) | |
385 | { | |
386 | TextCell *c; | |
387 | int x, y, y1; | |
388 | ||
389 | if (s != active_console) | |
390 | return; | |
391 | ||
392 | vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height, | |
393 | color_table[0]); | |
394 | y1 = s->y_displayed; | |
395 | for(y = 0; y < s->height; y++) { | |
396 | c = s->cells + y1 * s->width; | |
397 | for(x = 0; x < s->width; x++) { | |
398 | vga_putcharxy(s->ds, x, y, c->ch, | |
399 | color_table[c->fgcol], color_table[c->bgcol]); | |
400 | c++; | |
401 | } | |
402 | if (++y1 == s->total_height) | |
403 | y1 = 0; | |
404 | } | |
405 | dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height); | |
406 | console_show_cursor(s, 1); | |
407 | } | |
408 | ||
409 | static void console_scroll(int ydelta) | |
410 | { | |
411 | TextConsole *s; | |
412 | int i, y1; | |
413 | ||
414 | s = active_console; | |
415 | if (!s || !s->text_console) | |
416 | return; | |
417 | ||
418 | if (ydelta > 0) { | |
419 | for(i = 0; i < ydelta; i++) { | |
420 | if (s->y_displayed == s->y_base) | |
421 | break; | |
422 | if (++s->y_displayed == s->total_height) | |
423 | s->y_displayed = 0; | |
424 | } | |
425 | } else { | |
426 | ydelta = -ydelta; | |
427 | i = s->backscroll_height; | |
428 | if (i > s->total_height - s->height) | |
429 | i = s->total_height - s->height; | |
430 | y1 = s->y_base - i; | |
431 | if (y1 < 0) | |
432 | y1 += s->total_height; | |
433 | for(i = 0; i < ydelta; i++) { | |
434 | if (s->y_displayed == y1) | |
435 | break; | |
436 | if (--s->y_displayed < 0) | |
437 | s->y_displayed = s->total_height - 1; | |
438 | } | |
439 | } | |
440 | console_refresh(s); | |
441 | } | |
442 | ||
443 | static void console_put_lf(TextConsole *s) | |
444 | { | |
445 | TextCell *c; | |
446 | int x, y1; | |
447 | ||
448 | s->x = 0; | |
449 | s->y++; | |
450 | if (s->y >= s->height) { | |
451 | s->y = s->height - 1; | |
452 | ||
453 | if (s->y_displayed == s->y_base) { | |
454 | if (++s->y_displayed == s->total_height) | |
455 | s->y_displayed = 0; | |
456 | } | |
457 | if (++s->y_base == s->total_height) | |
458 | s->y_base = 0; | |
459 | if (s->backscroll_height < s->total_height) | |
460 | s->backscroll_height++; | |
461 | y1 = (s->y_base + s->height - 1) % s->total_height; | |
462 | c = &s->cells[y1 * s->width]; | |
463 | for(x = 0; x < s->width; x++) { | |
464 | c->ch = ' '; | |
465 | c->fgcol = s->fgcol; | |
466 | c->bgcol = s->bgcol; | |
467 | c++; | |
468 | } | |
469 | if (s == active_console && s->y_displayed == s->y_base) { | |
470 | vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0, | |
471 | s->width * FONT_WIDTH, | |
472 | (s->height - 1) * FONT_HEIGHT); | |
473 | vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT, | |
474 | s->width * FONT_WIDTH, FONT_HEIGHT, | |
475 | color_table[s->bgcol]); | |
476 | dpy_update(s->ds, 0, 0, | |
477 | s->width * FONT_WIDTH, s->height * FONT_HEIGHT); | |
478 | } | |
479 | } | |
480 | } | |
481 | ||
482 | static void console_putchar(TextConsole *s, int ch) | |
483 | { | |
484 | TextCell *c; | |
485 | int y1, i, x; | |
486 | ||
487 | switch(s->state) { | |
488 | case TTY_STATE_NORM: | |
489 | switch(ch) { | |
490 | case '\r': | |
491 | s->x = 0; | |
492 | break; | |
493 | case '\n': | |
494 | console_put_lf(s); | |
495 | break; | |
496 | case 27: | |
497 | s->state = TTY_STATE_ESC; | |
498 | break; | |
499 | default: | |
500 | y1 = (s->y_base + s->y) % s->total_height; | |
501 | c = &s->cells[y1 * s->width + s->x]; | |
502 | c->ch = ch; | |
503 | c->fgcol = s->fgcol; | |
504 | c->bgcol = s->bgcol; | |
505 | update_xy(s, s->x, s->y); | |
506 | s->x++; | |
507 | if (s->x >= s->width) | |
508 | console_put_lf(s); | |
509 | break; | |
510 | } | |
511 | break; | |
512 | case TTY_STATE_ESC: | |
513 | if (ch == '[') { | |
514 | for(i=0;i<MAX_ESC_PARAMS;i++) | |
515 | s->esc_params[i] = 0; | |
516 | s->nb_esc_params = 0; | |
517 | s->state = TTY_STATE_CSI; | |
518 | } else { | |
519 | s->state = TTY_STATE_NORM; | |
520 | } | |
521 | break; | |
522 | case TTY_STATE_CSI: | |
523 | if (ch >= '0' && ch <= '9') { | |
524 | if (s->nb_esc_params < MAX_ESC_PARAMS) { | |
525 | s->esc_params[s->nb_esc_params] = | |
526 | s->esc_params[s->nb_esc_params] * 10 + ch - '0'; | |
527 | } | |
528 | } else { | |
529 | s->nb_esc_params++; | |
530 | if (ch == ';') | |
531 | break; | |
532 | s->state = TTY_STATE_NORM; | |
533 | switch(ch) { | |
534 | case 'D': | |
535 | if (s->x > 0) | |
536 | s->x--; | |
537 | break; | |
538 | case 'C': | |
539 | if (s->x < (s->width - 1)) | |
540 | s->x++; | |
541 | break; | |
542 | case 'K': | |
543 | /* clear to eol */ | |
544 | y1 = (s->y_base + s->y) % s->total_height; | |
545 | for(x = s->x; x < s->width; x++) { | |
546 | c = &s->cells[y1 * s->width + x]; | |
547 | c->ch = ' '; | |
548 | c->fgcol = s->fgcol; | |
549 | c->bgcol = s->bgcol; | |
550 | c++; | |
551 | update_xy(s, x, s->y); | |
552 | } | |
553 | break; | |
554 | default: | |
555 | break; | |
556 | } | |
557 | break; | |
558 | } | |
559 | } | |
560 | } | |
561 | ||
562 | void console_select(unsigned int index) | |
563 | { | |
564 | TextConsole *s; | |
565 | ||
566 | if (index >= MAX_CONSOLES) | |
567 | return; | |
568 | s = consoles[index]; | |
569 | if (s) { | |
570 | active_console = s; | |
571 | if (s->text_console) { | |
572 | if (s->g_width != s->ds->width || | |
8e3a9fd2 FB |
573 | s->g_height != s->ds->height) { |
574 | s->g_width = s->ds->width; | |
575 | s->g_height = s->ds->height; | |
e7f0ad58 | 576 | text_console_resize(s); |
8e3a9fd2 | 577 | } |
e7f0ad58 FB |
578 | console_refresh(s); |
579 | } | |
580 | } | |
581 | } | |
582 | ||
583 | static int console_puts(CharDriverState *chr, const uint8_t *buf, int len) | |
584 | { | |
585 | TextConsole *s = chr->opaque; | |
586 | int i; | |
587 | ||
588 | console_show_cursor(s, 0); | |
589 | for(i = 0; i < len; i++) { | |
590 | console_putchar(s, buf[i]); | |
591 | } | |
592 | console_show_cursor(s, 1); | |
593 | return len; | |
594 | } | |
595 | ||
596 | static void console_chr_add_read_handler(CharDriverState *chr, | |
597 | IOCanRWHandler *fd_can_read, | |
598 | IOReadHandler *fd_read, void *opaque) | |
599 | { | |
600 | TextConsole *s = chr->opaque; | |
601 | s->fd_read = fd_read; | |
602 | s->fd_opaque = opaque; | |
603 | } | |
604 | ||
6fcfafb7 FB |
605 | static void console_send_event(CharDriverState *chr, int event) |
606 | { | |
607 | TextConsole *s = chr->opaque; | |
608 | int i; | |
609 | ||
610 | if (event == CHR_EVENT_FOCUS) { | |
611 | for(i = 0; i < nb_consoles; i++) { | |
612 | if (consoles[i] == s) { | |
613 | console_select(i); | |
614 | break; | |
615 | } | |
616 | } | |
617 | } | |
618 | } | |
619 | ||
e7f0ad58 FB |
620 | /* called when an ascii key is pressed */ |
621 | void kbd_put_keysym(int keysym) | |
622 | { | |
623 | TextConsole *s; | |
624 | uint8_t buf[16], *q; | |
625 | int c; | |
626 | ||
627 | s = active_console; | |
628 | if (!s || !s->text_console) | |
629 | return; | |
630 | ||
631 | switch(keysym) { | |
632 | case QEMU_KEY_CTRL_UP: | |
633 | console_scroll(-1); | |
634 | break; | |
635 | case QEMU_KEY_CTRL_DOWN: | |
636 | console_scroll(1); | |
637 | break; | |
638 | case QEMU_KEY_CTRL_PAGEUP: | |
639 | console_scroll(-10); | |
640 | break; | |
641 | case QEMU_KEY_CTRL_PAGEDOWN: | |
642 | console_scroll(10); | |
643 | break; | |
644 | default: | |
645 | if (s->fd_read) { | |
646 | /* convert the QEMU keysym to VT100 key string */ | |
647 | q = buf; | |
648 | if (keysym >= 0xe100 && keysym <= 0xe11f) { | |
649 | *q++ = '\033'; | |
650 | *q++ = '['; | |
651 | c = keysym - 0xe100; | |
652 | if (c >= 10) | |
653 | *q++ = '0' + (c / 10); | |
654 | *q++ = '0' + (c % 10); | |
655 | *q++ = '~'; | |
656 | } else if (keysym >= 0xe120 && keysym <= 0xe17f) { | |
657 | *q++ = '\033'; | |
658 | *q++ = '['; | |
659 | *q++ = keysym & 0xff; | |
660 | } else { | |
661 | *q++ = keysym; | |
662 | } | |
663 | s->fd_read(s->fd_opaque, buf, q - buf); | |
664 | } | |
665 | break; | |
666 | } | |
667 | } | |
668 | ||
669 | TextConsole *graphic_console_init(DisplayState *ds) | |
670 | { | |
671 | TextConsole *s; | |
672 | ||
673 | if (nb_consoles >= MAX_CONSOLES) | |
674 | return NULL; | |
675 | s = qemu_mallocz(sizeof(TextConsole)); | |
676 | if (!s) { | |
677 | return NULL; | |
678 | } | |
679 | if (!active_console) | |
680 | active_console = s; | |
681 | s->ds = ds; | |
682 | consoles[nb_consoles++] = s; | |
683 | return s; | |
684 | } | |
685 | ||
686 | int is_active_console(TextConsole *s) | |
687 | { | |
688 | return s == active_console; | |
689 | } | |
690 | ||
691 | CharDriverState *text_console_init(DisplayState *ds) | |
692 | { | |
693 | CharDriverState *chr; | |
694 | TextConsole *s; | |
695 | int i; | |
696 | static int color_inited; | |
697 | ||
698 | chr = qemu_mallocz(sizeof(CharDriverState)); | |
699 | if (!chr) | |
700 | return NULL; | |
701 | s = graphic_console_init(ds); | |
702 | if (!s) { | |
703 | free(chr); | |
704 | return NULL; | |
705 | } | |
706 | s->text_console = 1; | |
707 | chr->opaque = s; | |
708 | chr->chr_write = console_puts; | |
709 | chr->chr_add_read_handler = console_chr_add_read_handler; | |
6fcfafb7 FB |
710 | chr->chr_send_event = console_send_event; |
711 | ||
e7f0ad58 FB |
712 | if (!color_inited) { |
713 | color_inited = 1; | |
714 | for(i = 0; i < 8; i++) { | |
715 | color_table[i] = col_expand(s->ds, | |
716 | vga_get_color(s->ds, color_table_rgb[i])); | |
717 | } | |
718 | } | |
719 | s->y_displayed = 0; | |
720 | s->y_base = 0; | |
721 | s->total_height = DEFAULT_BACKSCROLL; | |
722 | s->x = 0; | |
723 | s->y = 0; | |
724 | s->fgcol = 7; | |
725 | s->bgcol = 0; | |
726 | s->g_width = s->ds->width; | |
727 | s->g_height = s->ds->height; | |
728 | text_console_resize(s); | |
729 | ||
730 | return chr; | |
731 | } |