blob: 18d1dc2233063b37a64f9f34f1cb4e3ba0cbaaaf [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/*
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 "qemu-common.h"
25#include "console.h"
26#include "qemu-timer.h"
27
28//#define DEBUG_CONSOLE
29#define DEFAULT_BACKSCROLL 512
30#define MAX_CONSOLES 12
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080031
32#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
33#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
34
35typedef struct TextAttributes {
36 uint8_t fgcol:4;
37 uint8_t bgcol:4;
38 uint8_t bold:1;
39 uint8_t uline:1;
40 uint8_t blink:1;
41 uint8_t invers:1;
42 uint8_t unvisible:1;
43} TextAttributes;
44
45typedef struct TextCell {
46 uint8_t ch;
47 TextAttributes t_attrib;
48} TextCell;
49
50#define MAX_ESC_PARAMS 3
51
52enum TTYState {
53 TTY_STATE_NORM,
54 TTY_STATE_ESC,
55 TTY_STATE_CSI,
56};
57
58typedef struct QEMUFIFO {
59 uint8_t *buf;
60 int buf_size;
61 int count, wptr, rptr;
62} QEMUFIFO;
63
64static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
65{
66 int l, len;
67
68 l = f->buf_size - f->count;
69 if (len1 > l)
70 len1 = l;
71 len = len1;
72 while (len > 0) {
73 l = f->buf_size - f->wptr;
74 if (l > len)
75 l = len;
76 memcpy(f->buf + f->wptr, buf, l);
77 f->wptr += l;
78 if (f->wptr >= f->buf_size)
79 f->wptr = 0;
80 buf += l;
81 len -= l;
82 }
83 f->count += len1;
84 return len1;
85}
86
87static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
88{
89 int l, len;
90
91 if (len1 > f->count)
92 len1 = f->count;
93 len = len1;
94 while (len > 0) {
95 l = f->buf_size - f->rptr;
96 if (l > len)
97 l = len;
98 memcpy(buf, f->buf + f->rptr, l);
99 f->rptr += l;
100 if (f->rptr >= f->buf_size)
101 f->rptr = 0;
102 buf += l;
103 len -= l;
104 }
105 f->count -= len1;
106 return len1;
107}
108
109typedef enum {
110 GRAPHIC_CONSOLE,
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700111 TEXT_CONSOLE,
112 TEXT_CONSOLE_FIXED_SIZE
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800113} console_type_t;
114
115/* ??? This is mis-named.
116 It is used for both text and graphical consoles. */
117struct TextConsole {
118 console_type_t console_type;
119 DisplayState *ds;
120 /* Graphic console state. */
121 vga_hw_update_ptr hw_update;
122 vga_hw_invalidate_ptr hw_invalidate;
123 vga_hw_screen_dump_ptr hw_screen_dump;
124 vga_hw_text_update_ptr hw_text_update;
125 void *hw;
126
127 int g_width, g_height;
128 int width;
129 int height;
130 int total_height;
131 int backscroll_height;
132 int x, y;
133 int x_saved, y_saved;
134 int y_displayed;
135 int y_base;
136 TextAttributes t_attrib_default; /* default text attributes */
137 TextAttributes t_attrib; /* currently active text attributes */
138 TextCell *cells;
139 int text_x[2], text_y[2], cursor_invalidate;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +0200140 int echo;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800141
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700142 int update_x0;
143 int update_y0;
144 int update_x1;
145 int update_y1;
146
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800147 enum TTYState state;
148 int esc_params[MAX_ESC_PARAMS];
149 int nb_esc_params;
150
151 CharDriverState *chr;
152 /* fifo for key pressed */
153 QEMUFIFO out_fifo;
154 uint8_t out_fifo_buf[16];
155 QEMUTimer *kbd_timer;
156};
157
David Turnerf52506f2010-09-10 16:11:22 +0200158static DisplayState *display_state;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800159static TextConsole *active_console;
160static TextConsole *consoles[MAX_CONSOLES];
161static int nb_consoles = 0;
162
David Turnerf52506f2010-09-10 16:11:22 +0200163#ifdef CONFIG_ANDROID
Vladimir Chtchetkinedd50f7d2010-07-30 09:16:41 -0700164/* Graphic console width, height and bits per pixel.
165 * These default values can be changed with the "-android-gui" option.
166 */
167int android_display_width = 640;
168int android_display_height = 480;
169int android_display_bpp = 32;
David Turnerf52506f2010-09-10 16:11:22 +0200170#endif
Vladimir Chtchetkinedd50f7d2010-07-30 09:16:41 -0700171
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800172void vga_hw_update(void)
173{
174 if (active_console && active_console->hw_update)
175 active_console->hw_update(active_console->hw);
176}
177
178void vga_hw_invalidate(void)
179{
David Turner9d118822010-09-10 13:02:07 +0200180 if (active_console && active_console->hw_invalidate)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800181 active_console->hw_invalidate(active_console->hw);
182}
183
184void vga_hw_screen_dump(const char *filename)
185{
186 TextConsole *previous_active_console;
187
188 previous_active_console = active_console;
189 active_console = consoles[0];
190 /* There is currently no way of specifying which screen we want to dump,
191 so always dump the first one. */
192 if (consoles[0]->hw_screen_dump)
193 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
194 active_console = previous_active_console;
195}
196
197void vga_hw_text_update(console_ch_t *chardata)
198{
199 if (active_console && active_console->hw_text_update)
200 active_console->hw_text_update(active_console->hw, chardata);
201}
202
203/* convert a RGBA color to a color index usable in graphic primitives */
204static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
205{
206 unsigned int r, g, b, color;
207
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700208 switch(ds_get_bits_per_pixel(ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800209#if 0
210 case 8:
211 r = (rgba >> 16) & 0xff;
212 g = (rgba >> 8) & 0xff;
213 b = (rgba) & 0xff;
214 color = (rgb_to_index[r] * 6 * 6) +
215 (rgb_to_index[g] * 6) +
216 (rgb_to_index[b]);
217 break;
218#endif
219 case 15:
220 r = (rgba >> 16) & 0xff;
221 g = (rgba >> 8) & 0xff;
222 b = (rgba) & 0xff;
223 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
224 break;
225 case 16:
226 r = (rgba >> 16) & 0xff;
227 g = (rgba >> 8) & 0xff;
228 b = (rgba) & 0xff;
229 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
230 break;
231 case 32:
232 default:
233 color = rgba;
234 break;
235 }
236 return color;
237}
238
239static void vga_fill_rect (DisplayState *ds,
240 int posx, int posy, int width, int height, uint32_t color)
241{
242 uint8_t *d, *d1;
243 int x, y, bpp;
244
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700245 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
246 d1 = ds_get_data(ds) +
247 ds_get_linesize(ds) * posy + bpp * posx;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800248 for (y = 0; y < height; y++) {
249 d = d1;
250 switch(bpp) {
251 case 1:
252 for (x = 0; x < width; x++) {
253 *((uint8_t *)d) = color;
254 d++;
255 }
256 break;
257 case 2:
258 for (x = 0; x < width; x++) {
259 *((uint16_t *)d) = color;
260 d += 2;
261 }
262 break;
263 case 4:
264 for (x = 0; x < width; x++) {
265 *((uint32_t *)d) = color;
266 d += 4;
267 }
268 break;
269 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700270 d1 += ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800271 }
272}
273
274/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
275static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
276{
277 const uint8_t *s;
278 uint8_t *d;
279 int wb, y, bpp;
280
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700281 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800282 wb = w * bpp;
283 if (yd <= ys) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700284 s = ds_get_data(ds) +
285 ds_get_linesize(ds) * ys + bpp * xs;
286 d = ds_get_data(ds) +
287 ds_get_linesize(ds) * yd + bpp * xd;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800288 for (y = 0; y < h; y++) {
289 memmove(d, s, wb);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700290 d += ds_get_linesize(ds);
291 s += ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800292 }
293 } else {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700294 s = ds_get_data(ds) +
295 ds_get_linesize(ds) * (ys + h - 1) + bpp * xs;
296 d = ds_get_data(ds) +
297 ds_get_linesize(ds) * (yd + h - 1) + bpp * xd;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800298 for (y = 0; y < h; y++) {
299 memmove(d, s, wb);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700300 d -= ds_get_linesize(ds);
301 s -= ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800302 }
303 }
304}
305
306/***********************************************************/
307/* basic char display */
308
309#define FONT_HEIGHT 16
310#define FONT_WIDTH 8
311
312#include "vgafont.h"
313
314#define cbswap_32(__x) \
315((uint32_t)( \
Vladimir Chtchetkine90c62352011-01-13 11:24:07 -0800316 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
317 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
318 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
319 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800320
David 'Digit' Turner20894ae2010-05-10 17:07:36 -0700321#ifdef HOST_WORDS_BIGENDIAN
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800322#define PAT(x) x
323#else
324#define PAT(x) cbswap_32(x)
325#endif
326
327static const uint32_t dmask16[16] = {
328 PAT(0x00000000),
329 PAT(0x000000ff),
330 PAT(0x0000ff00),
331 PAT(0x0000ffff),
332 PAT(0x00ff0000),
333 PAT(0x00ff00ff),
334 PAT(0x00ffff00),
335 PAT(0x00ffffff),
336 PAT(0xff000000),
337 PAT(0xff0000ff),
338 PAT(0xff00ff00),
339 PAT(0xff00ffff),
340 PAT(0xffff0000),
341 PAT(0xffff00ff),
342 PAT(0xffffff00),
343 PAT(0xffffffff),
344};
345
346static const uint32_t dmask4[4] = {
347 PAT(0x00000000),
348 PAT(0x0000ffff),
349 PAT(0xffff0000),
350 PAT(0xffffffff),
351};
352
353static uint32_t color_table[2][8];
354
355enum color_names {
356 COLOR_BLACK = 0,
357 COLOR_RED = 1,
358 COLOR_GREEN = 2,
359 COLOR_YELLOW = 3,
360 COLOR_BLUE = 4,
361 COLOR_MAGENTA = 5,
362 COLOR_CYAN = 6,
363 COLOR_WHITE = 7
364};
365
366static const uint32_t color_table_rgb[2][8] = {
367 { /* dark */
368 QEMU_RGB(0x00, 0x00, 0x00), /* black */
369 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
370 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
371 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
372 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
373 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
374 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
375 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
376 },
377 { /* bright */
378 QEMU_RGB(0x00, 0x00, 0x00), /* black */
379 QEMU_RGB(0xff, 0x00, 0x00), /* red */
380 QEMU_RGB(0x00, 0xff, 0x00), /* green */
381 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
382 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
383 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
384 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
385 QEMU_RGB(0xff, 0xff, 0xff), /* white */
386 }
387};
388
389static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
390{
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700391 switch(ds_get_bits_per_pixel(ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800392 case 8:
393 col |= col << 8;
394 col |= col << 16;
395 break;
396 case 15:
397 case 16:
398 col |= col << 16;
399 break;
400 default:
401 break;
402 }
403
404 return col;
405}
406#ifdef DEBUG_CONSOLE
407static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
408{
409 if (t_attrib->bold) {
410 printf("b");
411 } else {
412 printf(" ");
413 }
414 if (t_attrib->uline) {
415 printf("u");
416 } else {
417 printf(" ");
418 }
419 if (t_attrib->blink) {
420 printf("l");
421 } else {
422 printf(" ");
423 }
424 if (t_attrib->invers) {
425 printf("i");
426 } else {
427 printf(" ");
428 }
429 if (t_attrib->unvisible) {
430 printf("n");
431 } else {
432 printf(" ");
433 }
434
435 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
436}
437#endif
438
439static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
440 TextAttributes *t_attrib)
441{
442 uint8_t *d;
443 const uint8_t *font_ptr;
444 unsigned int font_data, linesize, xorcol, bpp;
445 int i;
446 unsigned int fgcol, bgcol;
447
448#ifdef DEBUG_CONSOLE
449 printf("x: %2i y: %2i", x, y);
450 console_print_text_attributes(t_attrib, ch);
451#endif
452
453 if (t_attrib->invers) {
454 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
455 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
456 } else {
457 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
458 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
459 }
460
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700461 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
462 d = ds_get_data(ds) +
463 ds_get_linesize(ds) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
464 linesize = ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800465 font_ptr = vgafont16 + FONT_HEIGHT * ch;
466 xorcol = bgcol ^ fgcol;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700467 switch(ds_get_bits_per_pixel(ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800468 case 8:
469 for(i = 0; i < FONT_HEIGHT; i++) {
470 font_data = *font_ptr++;
471 if (t_attrib->uline
472 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
473 font_data = 0xFFFF;
474 }
475 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
476 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
477 d += linesize;
478 }
479 break;
480 case 16:
481 case 15:
482 for(i = 0; i < FONT_HEIGHT; i++) {
483 font_data = *font_ptr++;
484 if (t_attrib->uline
485 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
486 font_data = 0xFFFF;
487 }
488 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
489 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
490 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
491 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
492 d += linesize;
493 }
494 break;
495 case 32:
496 for(i = 0; i < FONT_HEIGHT; i++) {
497 font_data = *font_ptr++;
498 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
499 font_data = 0xFFFF;
500 }
501 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
502 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
503 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
504 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
505 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
506 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
507 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
508 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
509 d += linesize;
510 }
511 break;
512 }
513}
514
515static void text_console_resize(TextConsole *s)
516{
517 TextCell *cells, *c, *c1;
518 int w1, x, y, last_width;
519
520 last_width = s->width;
521 s->width = s->g_width / FONT_WIDTH;
522 s->height = s->g_height / FONT_HEIGHT;
523
524 w1 = last_width;
525 if (s->width < w1)
526 w1 = s->width;
527
528 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
529 for(y = 0; y < s->total_height; y++) {
530 c = &cells[y * s->width];
531 if (w1 > 0) {
532 c1 = &s->cells[y * last_width];
533 for(x = 0; x < w1; x++) {
534 *c++ = *c1++;
535 }
536 }
537 for(x = w1; x < s->width; x++) {
538 c->ch = ' ';
539 c->t_attrib = s->t_attrib_default;
540 c++;
541 }
542 }
543 qemu_free(s->cells);
544 s->cells = cells;
545}
546
547static inline void text_update_xy(TextConsole *s, int x, int y)
548{
549 s->text_x[0] = MIN(s->text_x[0], x);
550 s->text_x[1] = MAX(s->text_x[1], x);
551 s->text_y[0] = MIN(s->text_y[0], y);
552 s->text_y[1] = MAX(s->text_y[1], y);
553}
554
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700555static void invalidate_xy(TextConsole *s, int x, int y)
556{
557 if (s->update_x0 > x * FONT_WIDTH)
558 s->update_x0 = x * FONT_WIDTH;
559 if (s->update_y0 > y * FONT_HEIGHT)
560 s->update_y0 = y * FONT_HEIGHT;
561 if (s->update_x1 < (x + 1) * FONT_WIDTH)
562 s->update_x1 = (x + 1) * FONT_WIDTH;
563 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
564 s->update_y1 = (y + 1) * FONT_HEIGHT;
565}
566
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800567static void update_xy(TextConsole *s, int x, int y)
568{
569 TextCell *c;
570 int y1, y2;
571
572 if (s == active_console) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700573 if (!ds_get_bits_per_pixel(s->ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800574 text_update_xy(s, x, y);
575 return;
576 }
577
578 y1 = (s->y_base + y) % s->total_height;
579 y2 = y1 - s->y_displayed;
580 if (y2 < 0)
581 y2 += s->total_height;
582 if (y2 < s->height) {
583 c = &s->cells[y1 * s->width + x];
584 vga_putcharxy(s->ds, x, y2, c->ch,
585 &(c->t_attrib));
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700586 invalidate_xy(s, x, y2);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800587 }
588 }
589}
590
591static void console_show_cursor(TextConsole *s, int show)
592{
593 TextCell *c;
594 int y, y1;
595
596 if (s == active_console) {
597 int x = s->x;
598
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700599 if (!ds_get_bits_per_pixel(s->ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800600 s->cursor_invalidate = 1;
601 return;
602 }
603
604 if (x >= s->width) {
605 x = s->width - 1;
606 }
607 y1 = (s->y_base + s->y) % s->total_height;
608 y = y1 - s->y_displayed;
609 if (y < 0)
610 y += s->total_height;
611 if (y < s->height) {
612 c = &s->cells[y1 * s->width + x];
613 if (show) {
614 TextAttributes t_attrib = s->t_attrib_default;
615 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
616 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
617 } else {
618 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
619 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700620 invalidate_xy(s, x, y);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800621 }
622 }
623}
624
625static void console_refresh(TextConsole *s)
626{
627 TextCell *c;
628 int x, y, y1;
629
630 if (s != active_console)
631 return;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700632 if (!ds_get_bits_per_pixel(s->ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800633 s->text_x[0] = 0;
634 s->text_y[0] = 0;
635 s->text_x[1] = s->width - 1;
636 s->text_y[1] = s->height - 1;
637 s->cursor_invalidate = 1;
638 return;
639 }
640
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700641 vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800642 color_table[0][COLOR_BLACK]);
643 y1 = s->y_displayed;
644 for(y = 0; y < s->height; y++) {
645 c = s->cells + y1 * s->width;
646 for(x = 0; x < s->width; x++) {
647 vga_putcharxy(s->ds, x, y, c->ch,
648 &(c->t_attrib));
649 c++;
650 }
651 if (++y1 == s->total_height)
652 y1 = 0;
653 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800654 console_show_cursor(s, 1);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700655 dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800656}
657
658static void console_scroll(int ydelta)
659{
660 TextConsole *s;
661 int i, y1;
662
663 s = active_console;
664 if (!s || (s->console_type == GRAPHIC_CONSOLE))
665 return;
666
667 if (ydelta > 0) {
668 for(i = 0; i < ydelta; i++) {
669 if (s->y_displayed == s->y_base)
670 break;
671 if (++s->y_displayed == s->total_height)
672 s->y_displayed = 0;
673 }
674 } else {
675 ydelta = -ydelta;
676 i = s->backscroll_height;
677 if (i > s->total_height - s->height)
678 i = s->total_height - s->height;
679 y1 = s->y_base - i;
680 if (y1 < 0)
681 y1 += s->total_height;
682 for(i = 0; i < ydelta; i++) {
683 if (s->y_displayed == y1)
684 break;
685 if (--s->y_displayed < 0)
686 s->y_displayed = s->total_height - 1;
687 }
688 }
689 console_refresh(s);
690}
691
692static void console_put_lf(TextConsole *s)
693{
694 TextCell *c;
695 int x, y1;
696
697 s->y++;
698 if (s->y >= s->height) {
699 s->y = s->height - 1;
700
701 if (s->y_displayed == s->y_base) {
702 if (++s->y_displayed == s->total_height)
703 s->y_displayed = 0;
704 }
705 if (++s->y_base == s->total_height)
706 s->y_base = 0;
707 if (s->backscroll_height < s->total_height)
708 s->backscroll_height++;
709 y1 = (s->y_base + s->height - 1) % s->total_height;
710 c = &s->cells[y1 * s->width];
711 for(x = 0; x < s->width; x++) {
712 c->ch = ' ';
713 c->t_attrib = s->t_attrib_default;
714 c++;
715 }
716 if (s == active_console && s->y_displayed == s->y_base) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700717 if (!ds_get_bits_per_pixel(s->ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800718 s->text_x[0] = 0;
719 s->text_y[0] = 0;
720 s->text_x[1] = s->width - 1;
721 s->text_y[1] = s->height - 1;
722 return;
723 }
724
725 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
726 s->width * FONT_WIDTH,
727 (s->height - 1) * FONT_HEIGHT);
728 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
729 s->width * FONT_WIDTH, FONT_HEIGHT,
730 color_table[0][s->t_attrib_default.bgcol]);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700731 s->update_x0 = 0;
732 s->update_y0 = 0;
733 s->update_x1 = s->width * FONT_WIDTH;
734 s->update_y1 = s->height * FONT_HEIGHT;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800735 }
736 }
737}
738
739/* Set console attributes depending on the current escape codes.
740 * NOTE: I know this code is not very efficient (checking every color for it
741 * self) but it is more readable and better maintainable.
742 */
743static void console_handle_escape(TextConsole *s)
744{
745 int i;
746
747 for (i=0; i<s->nb_esc_params; i++) {
748 switch (s->esc_params[i]) {
749 case 0: /* reset all console attributes to default */
750 s->t_attrib = s->t_attrib_default;
751 break;
752 case 1:
753 s->t_attrib.bold = 1;
754 break;
755 case 4:
756 s->t_attrib.uline = 1;
757 break;
758 case 5:
759 s->t_attrib.blink = 1;
760 break;
761 case 7:
762 s->t_attrib.invers = 1;
763 break;
764 case 8:
765 s->t_attrib.unvisible = 1;
766 break;
767 case 22:
768 s->t_attrib.bold = 0;
769 break;
770 case 24:
771 s->t_attrib.uline = 0;
772 break;
773 case 25:
774 s->t_attrib.blink = 0;
775 break;
776 case 27:
777 s->t_attrib.invers = 0;
778 break;
779 case 28:
780 s->t_attrib.unvisible = 0;
781 break;
782 /* set foreground color */
783 case 30:
784 s->t_attrib.fgcol=COLOR_BLACK;
785 break;
786 case 31:
787 s->t_attrib.fgcol=COLOR_RED;
788 break;
789 case 32:
790 s->t_attrib.fgcol=COLOR_GREEN;
791 break;
792 case 33:
793 s->t_attrib.fgcol=COLOR_YELLOW;
794 break;
795 case 34:
796 s->t_attrib.fgcol=COLOR_BLUE;
797 break;
798 case 35:
799 s->t_attrib.fgcol=COLOR_MAGENTA;
800 break;
801 case 36:
802 s->t_attrib.fgcol=COLOR_CYAN;
803 break;
804 case 37:
805 s->t_attrib.fgcol=COLOR_WHITE;
806 break;
807 /* set background color */
808 case 40:
809 s->t_attrib.bgcol=COLOR_BLACK;
810 break;
811 case 41:
812 s->t_attrib.bgcol=COLOR_RED;
813 break;
814 case 42:
815 s->t_attrib.bgcol=COLOR_GREEN;
816 break;
817 case 43:
818 s->t_attrib.bgcol=COLOR_YELLOW;
819 break;
820 case 44:
821 s->t_attrib.bgcol=COLOR_BLUE;
822 break;
823 case 45:
824 s->t_attrib.bgcol=COLOR_MAGENTA;
825 break;
826 case 46:
827 s->t_attrib.bgcol=COLOR_CYAN;
828 break;
829 case 47:
830 s->t_attrib.bgcol=COLOR_WHITE;
831 break;
832 }
833 }
834}
835
836static void console_clear_xy(TextConsole *s, int x, int y)
837{
838 int y1 = (s->y_base + y) % s->total_height;
839 TextCell *c = &s->cells[y1 * s->width + x];
840 c->ch = ' ';
841 c->t_attrib = s->t_attrib_default;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800842 update_xy(s, x, y);
843}
844
845static void console_putchar(TextConsole *s, int ch)
846{
847 TextCell *c;
848 int y1, i;
849 int x, y;
850
851 switch(s->state) {
852 case TTY_STATE_NORM:
853 switch(ch) {
854 case '\r': /* carriage return */
855 s->x = 0;
856 break;
857 case '\n': /* newline */
858 console_put_lf(s);
859 break;
860 case '\b': /* backspace */
861 if (s->x > 0)
862 s->x--;
863 break;
864 case '\t': /* tabspace */
865 if (s->x + (8 - (s->x % 8)) > s->width) {
866 s->x = 0;
867 console_put_lf(s);
868 } else {
869 s->x = s->x + (8 - (s->x % 8));
870 }
871 break;
872 case '\a': /* alert aka. bell */
873 /* TODO: has to be implemented */
874 break;
875 case 14:
876 /* SI (shift in), character set 0 (ignored) */
877 break;
878 case 15:
879 /* SO (shift out), character set 1 (ignored) */
880 break;
881 case 27: /* esc (introducing an escape sequence) */
882 s->state = TTY_STATE_ESC;
883 break;
884 default:
885 if (s->x >= s->width) {
886 /* line wrap */
887 s->x = 0;
888 console_put_lf(s);
889 }
890 y1 = (s->y_base + s->y) % s->total_height;
891 c = &s->cells[y1 * s->width + s->x];
892 c->ch = ch;
893 c->t_attrib = s->t_attrib;
894 update_xy(s, s->x, s->y);
895 s->x++;
896 break;
897 }
898 break;
899 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
900 if (ch == '[') {
901 for(i=0;i<MAX_ESC_PARAMS;i++)
902 s->esc_params[i] = 0;
903 s->nb_esc_params = 0;
904 s->state = TTY_STATE_CSI;
905 } else {
906 s->state = TTY_STATE_NORM;
907 }
908 break;
909 case TTY_STATE_CSI: /* handle escape sequence parameters */
910 if (ch >= '0' && ch <= '9') {
911 if (s->nb_esc_params < MAX_ESC_PARAMS) {
912 s->esc_params[s->nb_esc_params] =
913 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
914 }
915 } else {
916 s->nb_esc_params++;
917 if (ch == ';')
918 break;
919#ifdef DEBUG_CONSOLE
920 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
921 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
922#endif
923 s->state = TTY_STATE_NORM;
924 switch(ch) {
925 case 'A':
926 /* move cursor up */
927 if (s->esc_params[0] == 0) {
928 s->esc_params[0] = 1;
929 }
930 s->y -= s->esc_params[0];
931 if (s->y < 0) {
932 s->y = 0;
933 }
934 break;
935 case 'B':
936 /* move cursor down */
937 if (s->esc_params[0] == 0) {
938 s->esc_params[0] = 1;
939 }
940 s->y += s->esc_params[0];
941 if (s->y >= s->height) {
942 s->y = s->height - 1;
943 }
944 break;
945 case 'C':
946 /* move cursor right */
947 if (s->esc_params[0] == 0) {
948 s->esc_params[0] = 1;
949 }
950 s->x += s->esc_params[0];
951 if (s->x >= s->width) {
952 s->x = s->width - 1;
953 }
954 break;
955 case 'D':
956 /* move cursor left */
957 if (s->esc_params[0] == 0) {
958 s->esc_params[0] = 1;
959 }
960 s->x -= s->esc_params[0];
961 if (s->x < 0) {
962 s->x = 0;
963 }
964 break;
965 case 'G':
966 /* move cursor to column */
967 s->x = s->esc_params[0] - 1;
968 if (s->x < 0) {
969 s->x = 0;
970 }
971 break;
972 case 'f':
973 case 'H':
974 /* move cursor to row, column */
975 s->x = s->esc_params[1] - 1;
976 if (s->x < 0) {
977 s->x = 0;
978 }
979 s->y = s->esc_params[0] - 1;
980 if (s->y < 0) {
981 s->y = 0;
982 }
983 break;
984 case 'J':
985 switch (s->esc_params[0]) {
986 case 0:
987 /* clear to end of screen */
988 for (y = s->y; y < s->height; y++) {
989 for (x = 0; x < s->width; x++) {
990 if (y == s->y && x < s->x) {
991 continue;
992 }
993 console_clear_xy(s, x, y);
994 }
995 }
996 break;
997 case 1:
998 /* clear from beginning of screen */
999 for (y = 0; y <= s->y; y++) {
1000 for (x = 0; x < s->width; x++) {
1001 if (y == s->y && x > s->x) {
1002 break;
1003 }
1004 console_clear_xy(s, x, y);
1005 }
1006 }
1007 break;
1008 case 2:
1009 /* clear entire screen */
1010 for (y = 0; y <= s->height; y++) {
1011 for (x = 0; x < s->width; x++) {
1012 console_clear_xy(s, x, y);
1013 }
1014 }
1015 break;
1016 }
1017 case 'K':
1018 switch (s->esc_params[0]) {
1019 case 0:
1020 /* clear to eol */
1021 for(x = s->x; x < s->width; x++) {
1022 console_clear_xy(s, x, s->y);
1023 }
1024 break;
1025 case 1:
1026 /* clear from beginning of line */
1027 for (x = 0; x <= s->x; x++) {
1028 console_clear_xy(s, x, s->y);
1029 }
1030 break;
1031 case 2:
1032 /* clear entire line */
1033 for(x = 0; x < s->width; x++) {
1034 console_clear_xy(s, x, s->y);
1035 }
1036 break;
1037 }
1038 break;
1039 case 'm':
1040 console_handle_escape(s);
1041 break;
1042 case 'n':
1043 /* report cursor position */
1044 /* TODO: send ESC[row;colR */
1045 break;
1046 case 's':
1047 /* save cursor position */
1048 s->x_saved = s->x;
1049 s->y_saved = s->y;
1050 break;
1051 case 'u':
1052 /* restore cursor position */
1053 s->x = s->x_saved;
1054 s->y = s->y_saved;
1055 break;
1056 default:
1057#ifdef DEBUG_CONSOLE
1058 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1059#endif
1060 break;
1061 }
1062 break;
1063 }
1064 }
1065}
1066
1067void console_select(unsigned int index)
1068{
1069 TextConsole *s;
1070
1071 if (index >= MAX_CONSOLES)
1072 return;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001073 if (active_console) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001074 active_console->g_width = ds_get_width(active_console->ds);
1075 active_console->g_height = ds_get_height(active_console->ds);
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001076 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001077 s = consoles[index];
1078 if (s) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001079 DisplayState *ds = s->ds;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001080 active_console = s;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001081 if (ds_get_bits_per_pixel(s->ds)) {
1082 ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height);
1083 } else {
1084 s->ds->surface->width = s->width;
1085 s->ds->surface->height = s->height;
1086 }
1087 dpy_resize(s->ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001088 vga_hw_invalidate();
1089 }
1090}
1091
1092static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1093{
1094 TextConsole *s = chr->opaque;
1095 int i;
1096
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001097 s->update_x0 = s->width * FONT_WIDTH;
1098 s->update_y0 = s->height * FONT_HEIGHT;
1099 s->update_x1 = 0;
1100 s->update_y1 = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001101 console_show_cursor(s, 0);
1102 for(i = 0; i < len; i++) {
1103 console_putchar(s, buf[i]);
1104 }
1105 console_show_cursor(s, 1);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001106 if (ds_get_bits_per_pixel(s->ds) && s->update_x0 < s->update_x1) {
1107 dpy_update(s->ds, s->update_x0, s->update_y0,
1108 s->update_x1 - s->update_x0,
1109 s->update_y1 - s->update_y0);
1110 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001111 return len;
1112}
1113
1114static void console_send_event(CharDriverState *chr, int event)
1115{
1116 TextConsole *s = chr->opaque;
1117 int i;
1118
1119 if (event == CHR_EVENT_FOCUS) {
1120 for(i = 0; i < nb_consoles; i++) {
1121 if (consoles[i] == s) {
1122 console_select(i);
1123 break;
1124 }
1125 }
1126 }
1127}
1128
1129static void kbd_send_chars(void *opaque)
1130{
1131 TextConsole *s = opaque;
1132 int len;
1133 uint8_t buf[16];
1134
1135 len = qemu_chr_can_read(s->chr);
1136 if (len > s->out_fifo.count)
1137 len = s->out_fifo.count;
1138 if (len > 0) {
1139 if (len > sizeof(buf))
1140 len = sizeof(buf);
1141 qemu_fifo_read(&s->out_fifo, buf, len);
1142 qemu_chr_read(s->chr, buf, len);
1143 }
1144 /* characters are pending: we send them a bit later (XXX:
1145 horrible, should change char device API) */
1146 if (s->out_fifo.count > 0) {
David 'Digit' Turner5973c772011-05-10 07:06:00 +02001147 qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001148 }
1149}
1150
1151/* called when an ascii key is pressed */
1152void kbd_put_keysym(int keysym)
1153{
1154 TextConsole *s;
1155 uint8_t buf[16], *q;
1156 int c;
1157
1158 s = active_console;
1159 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1160 return;
1161
1162 switch(keysym) {
1163 case QEMU_KEY_CTRL_UP:
1164 console_scroll(-1);
1165 break;
1166 case QEMU_KEY_CTRL_DOWN:
1167 console_scroll(1);
1168 break;
1169 case QEMU_KEY_CTRL_PAGEUP:
1170 console_scroll(-10);
1171 break;
1172 case QEMU_KEY_CTRL_PAGEDOWN:
1173 console_scroll(10);
1174 break;
1175 default:
1176 /* convert the QEMU keysym to VT100 key string */
1177 q = buf;
1178 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1179 *q++ = '\033';
1180 *q++ = '[';
1181 c = keysym - 0xe100;
1182 if (c >= 10)
1183 *q++ = '0' + (c / 10);
1184 *q++ = '0' + (c % 10);
1185 *q++ = '~';
1186 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1187 *q++ = '\033';
1188 *q++ = '[';
1189 *q++ = keysym & 0xff;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001190 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1191 console_puts(s->chr, (const uint8_t *) "\r", 1);
1192 *q++ = '\n';
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001193 } else {
1194 *q++ = keysym;
1195 }
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001196 if (s->echo) {
1197 console_puts(s->chr, buf, q - buf);
1198 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001199 if (s->chr->chr_read) {
1200 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1201 kbd_send_chars(s);
1202 }
1203 break;
1204 }
1205}
1206
1207static void text_console_invalidate(void *opaque)
1208{
1209 TextConsole *s = (TextConsole *) opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001210 if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) {
1211 s->g_width = ds_get_width(s->ds);
1212 s->g_height = ds_get_height(s->ds);
1213 text_console_resize(s);
1214 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001215 console_refresh(s);
1216}
1217
1218static void text_console_update(void *opaque, console_ch_t *chardata)
1219{
1220 TextConsole *s = (TextConsole *) opaque;
1221 int i, j, src;
1222
1223 if (s->text_x[0] <= s->text_x[1]) {
1224 src = (s->y_base + s->text_y[0]) * s->width;
1225 chardata += s->text_y[0] * s->width;
1226 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1227 for (j = 0; j < s->width; j ++, src ++)
1228 console_write_ch(chardata ++, s->cells[src].ch |
1229 (s->cells[src].t_attrib.fgcol << 12) |
1230 (s->cells[src].t_attrib.bgcol << 8) |
1231 (s->cells[src].t_attrib.bold << 21));
1232 dpy_update(s->ds, s->text_x[0], s->text_y[0],
1233 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1234 s->text_x[0] = s->width;
1235 s->text_y[0] = s->height;
1236 s->text_x[1] = 0;
1237 s->text_y[1] = 0;
1238 }
1239 if (s->cursor_invalidate) {
1240 dpy_cursor(s->ds, s->x, s->y);
1241 s->cursor_invalidate = 0;
1242 }
1243}
1244
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001245static TextConsole *get_graphic_console(DisplayState *ds)
1246{
1247 int i;
1248 TextConsole *s;
1249 for (i = 0; i < nb_consoles; i++) {
1250 s = consoles[i];
1251 if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
1252 return s;
1253 }
1254 return NULL;
1255}
1256
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001257static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1258{
1259 TextConsole *s;
1260 int i;
1261
1262 if (nb_consoles >= MAX_CONSOLES)
1263 return NULL;
1264 s = qemu_mallocz(sizeof(TextConsole));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001265 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1266 (console_type == GRAPHIC_CONSOLE))) {
1267 active_console = s;
1268 }
1269 s->ds = ds;
1270 s->console_type = console_type;
1271 if (console_type != GRAPHIC_CONSOLE) {
1272 consoles[nb_consoles++] = s;
1273 } else {
1274 /* HACK: Put graphical consoles before text consoles. */
1275 for (i = nb_consoles; i > 0; i--) {
1276 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1277 break;
1278 consoles[i] = consoles[i - 1];
1279 }
1280 consoles[i] = s;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001281 nb_consoles++;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001282 }
1283 return s;
1284}
1285
David Turnerf52506f2010-09-10 16:11:22 +02001286static DisplaySurface* defaultallocator_create_displaysurface(int width, int height)
1287{
1288 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1289
1290 surface->width = width;
1291 surface->height = height;
1292 surface->linesize = width * 4;
1293 surface->pf = qemu_default_pixelformat(32);
1294#ifdef HOST_WORDS_BIGENDIAN
1295 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1296#else
1297 surface->flags = QEMU_ALLOCATED_FLAG;
1298#endif
1299 surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
1300
1301 return surface;
1302}
1303
1304static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
1305 int width, int height)
1306{
1307 surface->width = width;
1308 surface->height = height;
1309 surface->linesize = width * 4;
1310 surface->pf = qemu_default_pixelformat(32);
1311 if (surface->flags & QEMU_ALLOCATED_FLAG)
1312 surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height);
1313 else
1314 surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
1315#ifdef HOST_WORDS_BIGENDIAN
1316 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1317#else
1318 surface->flags = QEMU_ALLOCATED_FLAG;
1319#endif
1320
1321 return surface;
1322}
1323
1324DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
1325 int linesize, uint8_t *data)
1326{
1327 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1328
1329 surface->width = width;
1330 surface->height = height;
1331 surface->linesize = linesize;
1332 surface->pf = qemu_default_pixelformat(bpp);
1333#ifdef HOST_WORDS_BIGENDIAN
1334 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1335#endif
1336 surface->data = data;
1337
1338 return surface;
1339}
1340
1341static void defaultallocator_free_displaysurface(DisplaySurface *surface)
1342{
1343 if (surface == NULL)
1344 return;
1345 if (surface->flags & QEMU_ALLOCATED_FLAG)
1346 qemu_free(surface->data);
1347 qemu_free(surface);
1348}
1349
1350static struct DisplayAllocator default_allocator = {
1351 defaultallocator_create_displaysurface,
1352 defaultallocator_resize_displaysurface,
1353 defaultallocator_free_displaysurface
1354};
1355
1356static void dumb_display_init(void)
1357{
1358 DisplayState *ds = qemu_mallocz(sizeof(DisplayState));
1359 ds->allocator = &default_allocator;
1360 ds->surface = qemu_create_displaysurface(ds, 640, 480);
1361 register_displaystate(ds);
1362}
1363
1364/***********************************************************/
1365/* register display */
1366
1367void register_displaystate(DisplayState *ds)
1368{
1369 DisplayState **s;
1370 s = &display_state;
1371 while (*s != NULL)
1372 s = &(*s)->next;
1373 ds->next = NULL;
1374 *s = ds;
1375}
1376
1377DisplayState *get_displaystate(void)
1378{
1379 if (!display_state) {
1380 dumb_display_init ();
1381 }
1382 return display_state;
1383}
1384
1385DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da)
1386{
1387 if(ds->allocator == &default_allocator) {
1388 DisplaySurface *surf;
1389 surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds));
1390 defaultallocator_free_displaysurface(ds->surface);
1391 ds->surface = surf;
1392 ds->allocator = da;
1393 }
1394 return ds->allocator;
1395}
1396
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001397DisplayState *graphic_console_init(vga_hw_update_ptr update,
1398 vga_hw_invalidate_ptr invalidate,
1399 vga_hw_screen_dump_ptr screen_dump,
1400 vga_hw_text_update_ptr text_update,
1401 void *opaque)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001402{
1403 TextConsole *s;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001404 DisplayState *ds;
1405
1406 ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
Vladimir Chtchetkine90c62352011-01-13 11:24:07 -08001407 ds->allocator = &default_allocator;
David Turnerf52506f2010-09-10 16:11:22 +02001408#ifdef CONFIG_ANDROID
Vladimir Chtchetkinedd50f7d2010-07-30 09:16:41 -07001409 ds->surface = qemu_create_displaysurface(ds, android_display_width, android_display_height);
David Turnerf52506f2010-09-10 16:11:22 +02001410#else
1411 ds->surface = qemu_create_displaysurface(ds, 640, 480);
1412#endif
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001413
1414 s = new_console(ds, GRAPHIC_CONSOLE);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001415 if (s == NULL) {
1416 qemu_free_displaysurface(ds);
1417 qemu_free(ds);
1418 return NULL;
1419 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001420 s->hw_update = update;
1421 s->hw_invalidate = invalidate;
1422 s->hw_screen_dump = screen_dump;
1423 s->hw_text_update = text_update;
1424 s->hw = opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001425
1426 register_displaystate(ds);
1427 return ds;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001428}
1429
1430int is_graphic_console(void)
1431{
1432 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1433}
1434
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001435int is_fixedsize_console(void)
1436{
1437 return active_console && active_console->console_type != TEXT_CONSOLE;
1438}
1439
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001440void console_color_init(DisplayState *ds)
1441{
1442 int i, j;
1443 for (j = 0; j < 2; j++) {
1444 for (i = 0; i < 8; i++) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001445 color_table[j][i] = col_expand(ds,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001446 vga_get_color(ds, color_table_rgb[j][i]));
1447 }
1448 }
1449}
1450
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001451static int n_text_consoles;
1452static CharDriverState *text_consoles[128];
David Turnerf52506f2010-09-10 16:11:22 +02001453static QemuOpts *text_console_opts[128];
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001454
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001455static void text_console_set_echo(CharDriverState *chr, bool echo)
1456{
1457 TextConsole *s = chr->opaque;
1458
1459 s->echo = echo;
1460}
1461
David Turnerf52506f2010-09-10 16:11:22 +02001462static void text_console_do_init(CharDriverState *chr, DisplayState *ds, QemuOpts *opts)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001463{
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001464 TextConsole *s;
1465 unsigned width;
1466 unsigned height;
1467 static int color_inited;
1468
David Turnerf52506f2010-09-10 16:11:22 +02001469 width = qemu_opt_get_number(opts, "width", 0);
1470 if (width == 0)
1471 width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH;
1472
1473 height = qemu_opt_get_number(opts, "height", 0);
1474 if (height == 0)
1475 height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT;
1476
1477 if (width == 0 || height == 0) {
1478 s = new_console(ds, TEXT_CONSOLE);
1479 width = ds_get_width(s->ds);
1480 height = ds_get_height(s->ds);
1481 } else {
1482 s = new_console(ds, TEXT_CONSOLE_FIXED_SIZE);
1483 }
1484
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001485 if (!s) {
1486 free(chr);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001487 return;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001488 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001489 chr->opaque = s;
1490 chr->chr_write = console_puts;
1491 chr->chr_send_event = console_send_event;
1492
1493 s->chr = chr;
1494 s->out_fifo.buf = s->out_fifo_buf;
1495 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
David 'Digit' Turner5973c772011-05-10 07:06:00 +02001496 s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001497 s->ds = ds;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001498
1499 if (!color_inited) {
1500 color_inited = 1;
1501 console_color_init(s->ds);
1502 }
1503 s->y_displayed = 0;
1504 s->y_base = 0;
1505 s->total_height = DEFAULT_BACKSCROLL;
1506 s->x = 0;
1507 s->y = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001508 s->g_width = width;
1509 s->g_height = height;
1510
1511 s->hw_invalidate = text_console_invalidate;
1512 s->hw_text_update = text_console_update;
1513 s->hw = s;
1514
1515 /* Set text attribute defaults */
1516 s->t_attrib_default.bold = 0;
1517 s->t_attrib_default.uline = 0;
1518 s->t_attrib_default.blink = 0;
1519 s->t_attrib_default.invers = 0;
1520 s->t_attrib_default.unvisible = 0;
1521 s->t_attrib_default.fgcol = COLOR_WHITE;
1522 s->t_attrib_default.bgcol = COLOR_BLACK;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001523 /* set current text attributes to default */
1524 s->t_attrib = s->t_attrib_default;
1525 text_console_resize(s);
1526
David Turnerf52506f2010-09-10 16:11:22 +02001527 if (chr->label) {
1528 char msg[128];
1529 int len;
1530
1531 s->t_attrib.bgcol = COLOR_BLUE;
1532 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1533 console_puts(chr, (uint8_t*)msg, len);
1534 s->t_attrib = s->t_attrib_default;
1535 }
1536
David Turner9d118822010-09-10 13:02:07 +02001537 qemu_chr_generic_open(chr);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001538 if (chr->init)
1539 chr->init(chr);
1540}
1541
David Turnerf52506f2010-09-10 16:11:22 +02001542CharDriverState *text_console_init(QemuOpts *opts)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001543{
1544 CharDriverState *chr;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001545 TextConsole *s;
1546 unsigned width;
1547 unsigned height;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001548
1549 chr = qemu_mallocz(sizeof(CharDriverState));
1550
1551 if (n_text_consoles == 128) {
1552 fprintf(stderr, "Too many text consoles\n");
1553 exit(1);
1554 }
1555 text_consoles[n_text_consoles] = chr;
David Turnerf52506f2010-09-10 16:11:22 +02001556 text_console_opts[n_text_consoles] = opts;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001557 n_text_consoles++;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001558
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001559 width = qemu_opt_get_number(opts, "width", 0);
1560 if (width == 0)
1561 width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH;
1562
1563 height = qemu_opt_get_number(opts, "height", 0);
1564 if (height == 0)
1565 height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT;
1566
1567 if (width == 0 || height == 0) {
1568 s = new_console(NULL, TEXT_CONSOLE);
1569 } else {
1570 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1571 }
1572
1573 if (!s) {
1574 free(chr);
1575 return NULL;
1576 }
1577
1578 s->chr = chr;
1579 s->g_width = width;
1580 s->g_height = height;
1581 chr->opaque = s;
1582 chr->chr_set_echo = text_console_set_echo;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001583 return chr;
1584}
1585
David Turnerf52506f2010-09-10 16:11:22 +02001586CharDriverState* text_console_init_compat(const char *label, const char *p)
1587{
1588 QemuOpts *opts;
1589 int width, height;
1590 char temp[32];
1591
1592 opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1);
1593 if (NULL == opts)
1594 return NULL;
1595
1596 if (p != NULL) {
1597 width = strtoul(p, (char **)&p, 10);
1598 if (*p == 'C') {
1599 p++;
1600 width *= FONT_WIDTH;
1601 }
1602 snprintf(temp, sizeof temp, "%d", width);
1603 qemu_opt_set(opts, "width", temp);
1604 if (*p == 'x') {
1605 p++;
1606 height = strtoul(p, (char **)&p, 10);
1607 if (*p == 'C') {
1608 p++;
1609 height *= FONT_HEIGHT;
1610 }
1611 snprintf(temp, sizeof temp, "%d", height);
1612 qemu_opt_set(opts, "height", temp);
1613 }
1614 }
1615 return text_console_init(opts);
1616}
1617
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001618void text_consoles_set_display(DisplayState *ds)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001619{
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001620 int i;
1621
1622 for (i = 0; i < n_text_consoles; i++) {
David Turnerf52506f2010-09-10 16:11:22 +02001623 text_console_do_init(text_consoles[i], ds, text_console_opts[i]);
1624 qemu_opts_del(text_console_opts[i]);
1625 text_console_opts[i] = NULL;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001626 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001627
1628 n_text_consoles = 0;
1629}
1630
1631void qemu_console_resize(DisplayState *ds, int width, int height)
1632{
1633 TextConsole *s = get_graphic_console(ds);
1634 if (!s) return;
1635
1636 s->g_width = width;
1637 s->g_height = height;
1638 if (is_graphic_console()) {
1639 ds->surface = qemu_resize_displaysurface(ds, width, height);
1640 dpy_resize(ds);
1641 }
1642}
1643
1644void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
1645 int dst_x, int dst_y, int w, int h)
1646{
1647 if (is_graphic_console()) {
1648 dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
1649 }
1650}
1651
1652PixelFormat qemu_different_endianness_pixelformat(int bpp)
1653{
1654 PixelFormat pf;
1655
1656 memset(&pf, 0x00, sizeof(PixelFormat));
1657
1658 pf.bits_per_pixel = bpp;
1659 pf.bytes_per_pixel = bpp / 8;
1660 pf.depth = bpp == 32 ? 24 : bpp;
1661
1662 switch (bpp) {
1663 case 24:
1664 pf.rmask = 0x000000FF;
1665 pf.gmask = 0x0000FF00;
1666 pf.bmask = 0x00FF0000;
1667 pf.rmax = 255;
1668 pf.gmax = 255;
1669 pf.bmax = 255;
1670 pf.rshift = 0;
1671 pf.gshift = 8;
1672 pf.bshift = 16;
1673 pf.rbits = 8;
1674 pf.gbits = 8;
1675 pf.bbits = 8;
1676 break;
1677 case 32:
1678 pf.rmask = 0x0000FF00;
1679 pf.gmask = 0x00FF0000;
1680 pf.bmask = 0xFF000000;
1681 pf.amask = 0x00000000;
1682 pf.amax = 255;
1683 pf.rmax = 255;
1684 pf.gmax = 255;
1685 pf.bmax = 255;
1686 pf.ashift = 0;
1687 pf.rshift = 8;
1688 pf.gshift = 16;
1689 pf.bshift = 24;
1690 pf.rbits = 8;
1691 pf.gbits = 8;
1692 pf.bbits = 8;
1693 pf.abits = 8;
1694 break;
1695 default:
1696 break;
1697 }
1698 return pf;
1699}
1700
1701PixelFormat qemu_default_pixelformat(int bpp)
1702{
1703 PixelFormat pf;
1704
1705 memset(&pf, 0x00, sizeof(PixelFormat));
1706
1707 pf.bits_per_pixel = bpp;
1708 pf.bytes_per_pixel = bpp / 8;
1709 pf.depth = bpp == 32 ? 24 : bpp;
1710
1711 switch (bpp) {
David Turnerf52506f2010-09-10 16:11:22 +02001712 case 15:
1713 pf.bits_per_pixel = 16;
1714 pf.bytes_per_pixel = 2;
1715 pf.rmask = 0x00007c00;
1716 pf.gmask = 0x000003E0;
1717 pf.bmask = 0x0000001F;
1718 pf.rmax = 31;
1719 pf.gmax = 31;
1720 pf.bmax = 31;
1721 pf.rshift = 10;
1722 pf.gshift = 5;
1723 pf.bshift = 0;
1724 pf.rbits = 5;
1725 pf.gbits = 5;
1726 pf.bbits = 5;
1727 break;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001728 case 16:
1729 pf.rmask = 0x0000F800;
1730 pf.gmask = 0x000007E0;
1731 pf.bmask = 0x0000001F;
1732 pf.rmax = 31;
1733 pf.gmax = 63;
1734 pf.bmax = 31;
1735 pf.rshift = 11;
1736 pf.gshift = 5;
1737 pf.bshift = 0;
1738 pf.rbits = 5;
1739 pf.gbits = 6;
1740 pf.bbits = 5;
1741 break;
1742 case 24:
1743 pf.rmask = 0x00FF0000;
1744 pf.gmask = 0x0000FF00;
1745 pf.bmask = 0x000000FF;
1746 pf.rmax = 255;
1747 pf.gmax = 255;
1748 pf.bmax = 255;
1749 pf.rshift = 16;
1750 pf.gshift = 8;
1751 pf.bshift = 0;
1752 pf.rbits = 8;
1753 pf.gbits = 8;
1754 pf.bbits = 8;
1755 case 32:
1756 pf.rmask = 0x00FF0000;
1757 pf.gmask = 0x0000FF00;
1758 pf.bmask = 0x000000FF;
1759 pf.amax = 255;
1760 pf.rmax = 255;
1761 pf.gmax = 255;
1762 pf.bmax = 255;
1763 pf.ashift = 24;
1764 pf.rshift = 16;
1765 pf.gshift = 8;
1766 pf.bshift = 0;
1767 pf.rbits = 8;
1768 pf.gbits = 8;
1769 pf.bbits = 8;
1770 pf.abits = 8;
1771 break;
1772 default:
1773 break;
1774 }
1775 return pf;
1776}
1777
David Turnerf52506f2010-09-10 16:11:22 +02001778#ifdef CONFIG_ANDROID
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01001779
1780void
1781unregister_displayupdatelistener(DisplayState *ds, DisplayUpdateListener *dul)
1782{
1783 DisplayUpdateListener **pnode = &ds->update_listeners;
1784 for (;;) {
1785 if (*pnode == NULL)
1786 break;
1787 if (*pnode == dul) {
1788 *pnode = dul->next;
1789 break;
1790 }
1791 pnode = &(*pnode)->next;
1792 }
1793 dul->next = NULL;
1794}
1795
David Turnerf52506f2010-09-10 16:11:22 +02001796void
David 'Digit' Turner94702b02011-01-20 02:46:33 +01001797android_display_reset(DisplayState* ds, int width, int height, int bitspp)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001798{
David 'Digit' Turner94702b02011-01-20 02:46:33 +01001799 DisplaySurface* surface;
1800 int bytespp = (bitspp+7)/8;
1801 int pitch = (bytespp*width + 3) & ~3;
1802
1803 qemu_free_displaysurface(ds);
1804
1805 surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1806
1807 surface->width = width;
1808 surface->height = height;
1809 surface->linesize = pitch;
1810 surface->pf = qemu_default_pixelformat(bitspp);
1811 surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
1812#ifdef HOST_WORDS_BIGENDIAN
1813 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1814#else
1815 surface->flags = QEMU_ALLOCATED_FLAG;
1816#endif
1817
1818 ds->surface = surface;
David Turnerf52506f2010-09-10 16:11:22 +02001819}
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001820#endif