blob: a94aecd09e01766eee832a690f6300069ab10fd2 [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
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +020035#ifdef CONFIG_SKINNING
36// Skinning overwrites these functions to put the skin in between
37DisplayState *qemu_graphic_console_init(vga_hw_update_ptr update,
38 vga_hw_invalidate_ptr invalidate,
39 vga_hw_screen_dump_ptr screen_dump,
40 vga_hw_text_update_ptr text_update,
41 void *opaque);
42#undef graphic_console_init
43#define graphic_console_init qemu_graphic_console_init
44
45void original_qemu_console_resize(DisplayState *ds, int width, int height);
46#undef qemu_console_resize
47#define qemu_console_resize original_qemu_console_resize
48#endif
49
50int multitouch_enabled = 0;
51static QEMUDisplayCloseCallback *qemu_display_close_callback = NULL;
52static void *qemu_display_close_callback_opaque = NULL;
53
54void qemu_set_display_close_handler(QEMUDisplayCloseCallback *cb, void *opaque)
55{
56 qemu_display_close_callback = cb;
57 qemu_display_close_callback_opaque = opaque;
58}
59
60int qemu_run_display_close_handler(void)
61{
62 if (qemu_display_close_callback != NULL) {
63 return qemu_display_close_callback(qemu_display_close_callback_opaque);
64 }
65 return 1;
66}
67
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080068typedef struct TextAttributes {
69 uint8_t fgcol:4;
70 uint8_t bgcol:4;
71 uint8_t bold:1;
72 uint8_t uline:1;
73 uint8_t blink:1;
74 uint8_t invers:1;
75 uint8_t unvisible:1;
76} TextAttributes;
77
78typedef struct TextCell {
79 uint8_t ch;
80 TextAttributes t_attrib;
81} TextCell;
82
83#define MAX_ESC_PARAMS 3
84
85enum TTYState {
86 TTY_STATE_NORM,
87 TTY_STATE_ESC,
88 TTY_STATE_CSI,
89};
90
91typedef struct QEMUFIFO {
92 uint8_t *buf;
93 int buf_size;
94 int count, wptr, rptr;
95} QEMUFIFO;
96
97static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
98{
99 int l, len;
100
101 l = f->buf_size - f->count;
102 if (len1 > l)
103 len1 = l;
104 len = len1;
105 while (len > 0) {
106 l = f->buf_size - f->wptr;
107 if (l > len)
108 l = len;
109 memcpy(f->buf + f->wptr, buf, l);
110 f->wptr += l;
111 if (f->wptr >= f->buf_size)
112 f->wptr = 0;
113 buf += l;
114 len -= l;
115 }
116 f->count += len1;
117 return len1;
118}
119
120static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
121{
122 int l, len;
123
124 if (len1 > f->count)
125 len1 = f->count;
126 len = len1;
127 while (len > 0) {
128 l = f->buf_size - f->rptr;
129 if (l > len)
130 l = len;
131 memcpy(buf, f->buf + f->rptr, l);
132 f->rptr += l;
133 if (f->rptr >= f->buf_size)
134 f->rptr = 0;
135 buf += l;
136 len -= l;
137 }
138 f->count -= len1;
139 return len1;
140}
141
142typedef enum {
143 GRAPHIC_CONSOLE,
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700144 TEXT_CONSOLE,
145 TEXT_CONSOLE_FIXED_SIZE
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800146} console_type_t;
147
148/* ??? This is mis-named.
149 It is used for both text and graphical consoles. */
150struct TextConsole {
151 console_type_t console_type;
152 DisplayState *ds;
153 /* Graphic console state. */
154 vga_hw_update_ptr hw_update;
155 vga_hw_invalidate_ptr hw_invalidate;
156 vga_hw_screen_dump_ptr hw_screen_dump;
157 vga_hw_text_update_ptr hw_text_update;
158 void *hw;
159
160 int g_width, g_height;
161 int width;
162 int height;
163 int total_height;
164 int backscroll_height;
165 int x, y;
166 int x_saved, y_saved;
167 int y_displayed;
168 int y_base;
169 TextAttributes t_attrib_default; /* default text attributes */
170 TextAttributes t_attrib; /* currently active text attributes */
171 TextCell *cells;
172 int text_x[2], text_y[2], cursor_invalidate;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +0200173 int echo;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800174
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700175 int update_x0;
176 int update_y0;
177 int update_x1;
178 int update_y1;
179
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800180 enum TTYState state;
181 int esc_params[MAX_ESC_PARAMS];
182 int nb_esc_params;
183
184 CharDriverState *chr;
185 /* fifo for key pressed */
186 QEMUFIFO out_fifo;
187 uint8_t out_fifo_buf[16];
188 QEMUTimer *kbd_timer;
189};
190
David Turnerf52506f2010-09-10 16:11:22 +0200191static DisplayState *display_state;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800192static TextConsole *active_console;
193static TextConsole *consoles[MAX_CONSOLES];
194static int nb_consoles = 0;
195
David Turnerf52506f2010-09-10 16:11:22 +0200196#ifdef CONFIG_ANDROID
Vladimir Chtchetkinedd50f7d2010-07-30 09:16:41 -0700197/* Graphic console width, height and bits per pixel.
198 * These default values can be changed with the "-android-gui" option.
199 */
200int android_display_width = 640;
201int android_display_height = 480;
202int android_display_bpp = 32;
David Turnerf52506f2010-09-10 16:11:22 +0200203#endif
Vladimir Chtchetkinedd50f7d2010-07-30 09:16:41 -0700204
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800205void vga_hw_update(void)
206{
207 if (active_console && active_console->hw_update)
208 active_console->hw_update(active_console->hw);
209}
210
211void vga_hw_invalidate(void)
212{
David Turner9d118822010-09-10 13:02:07 +0200213 if (active_console && active_console->hw_invalidate)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800214 active_console->hw_invalidate(active_console->hw);
215}
216
217void vga_hw_screen_dump(const char *filename)
218{
219 TextConsole *previous_active_console;
220
221 previous_active_console = active_console;
222 active_console = consoles[0];
223 /* There is currently no way of specifying which screen we want to dump,
224 so always dump the first one. */
225 if (consoles[0]->hw_screen_dump)
226 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
227 active_console = previous_active_console;
228}
229
230void vga_hw_text_update(console_ch_t *chardata)
231{
232 if (active_console && active_console->hw_text_update)
233 active_console->hw_text_update(active_console->hw, chardata);
234}
235
236/* convert a RGBA color to a color index usable in graphic primitives */
237static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
238{
239 unsigned int r, g, b, color;
240
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700241 switch(ds_get_bits_per_pixel(ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800242#if 0
243 case 8:
244 r = (rgba >> 16) & 0xff;
245 g = (rgba >> 8) & 0xff;
246 b = (rgba) & 0xff;
247 color = (rgb_to_index[r] * 6 * 6) +
248 (rgb_to_index[g] * 6) +
249 (rgb_to_index[b]);
250 break;
251#endif
252 case 15:
253 r = (rgba >> 16) & 0xff;
254 g = (rgba >> 8) & 0xff;
255 b = (rgba) & 0xff;
256 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
257 break;
258 case 16:
259 r = (rgba >> 16) & 0xff;
260 g = (rgba >> 8) & 0xff;
261 b = (rgba) & 0xff;
262 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
263 break;
264 case 32:
265 default:
266 color = rgba;
267 break;
268 }
269 return color;
270}
271
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +0200272void vga_fill_rect(DisplayState *ds, int posx, int posy,
273 int width, int height, uint32_t color)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800274{
275 uint8_t *d, *d1;
276 int x, y, bpp;
277
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700278 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
279 d1 = ds_get_data(ds) +
280 ds_get_linesize(ds) * posy + bpp * posx;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800281 for (y = 0; y < height; y++) {
282 d = d1;
283 switch(bpp) {
284 case 1:
285 for (x = 0; x < width; x++) {
286 *((uint8_t *)d) = color;
287 d++;
288 }
289 break;
290 case 2:
291 for (x = 0; x < width; x++) {
292 *((uint16_t *)d) = color;
293 d += 2;
294 }
295 break;
296 case 4:
297 for (x = 0; x < width; x++) {
298 *((uint32_t *)d) = color;
299 d += 4;
300 }
301 break;
302 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700303 d1 += ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800304 }
305}
306
307/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
308static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
309{
310 const uint8_t *s;
311 uint8_t *d;
312 int wb, y, bpp;
313
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700314 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800315 wb = w * bpp;
316 if (yd <= ys) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700317 s = ds_get_data(ds) +
318 ds_get_linesize(ds) * ys + bpp * xs;
319 d = ds_get_data(ds) +
320 ds_get_linesize(ds) * yd + bpp * xd;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800321 for (y = 0; y < h; y++) {
322 memmove(d, s, wb);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700323 d += ds_get_linesize(ds);
324 s += ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800325 }
326 } else {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700327 s = ds_get_data(ds) +
328 ds_get_linesize(ds) * (ys + h - 1) + bpp * xs;
329 d = ds_get_data(ds) +
330 ds_get_linesize(ds) * (yd + h - 1) + bpp * xd;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800331 for (y = 0; y < h; y++) {
332 memmove(d, s, wb);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700333 d -= ds_get_linesize(ds);
334 s -= ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800335 }
336 }
337}
338
339/***********************************************************/
340/* basic char display */
341
342#define FONT_HEIGHT 16
343#define FONT_WIDTH 8
344
345#include "vgafont.h"
346
347#define cbswap_32(__x) \
348((uint32_t)( \
Vladimir Chtchetkine90c62352011-01-13 11:24:07 -0800349 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
350 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
351 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
352 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800353
David 'Digit' Turner20894ae2010-05-10 17:07:36 -0700354#ifdef HOST_WORDS_BIGENDIAN
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800355#define PAT(x) x
356#else
357#define PAT(x) cbswap_32(x)
358#endif
359
360static const uint32_t dmask16[16] = {
361 PAT(0x00000000),
362 PAT(0x000000ff),
363 PAT(0x0000ff00),
364 PAT(0x0000ffff),
365 PAT(0x00ff0000),
366 PAT(0x00ff00ff),
367 PAT(0x00ffff00),
368 PAT(0x00ffffff),
369 PAT(0xff000000),
370 PAT(0xff0000ff),
371 PAT(0xff00ff00),
372 PAT(0xff00ffff),
373 PAT(0xffff0000),
374 PAT(0xffff00ff),
375 PAT(0xffffff00),
376 PAT(0xffffffff),
377};
378
379static const uint32_t dmask4[4] = {
380 PAT(0x00000000),
381 PAT(0x0000ffff),
382 PAT(0xffff0000),
383 PAT(0xffffffff),
384};
385
386static uint32_t color_table[2][8];
387
388enum color_names {
389 COLOR_BLACK = 0,
390 COLOR_RED = 1,
391 COLOR_GREEN = 2,
392 COLOR_YELLOW = 3,
393 COLOR_BLUE = 4,
394 COLOR_MAGENTA = 5,
395 COLOR_CYAN = 6,
396 COLOR_WHITE = 7
397};
398
399static const uint32_t color_table_rgb[2][8] = {
400 { /* dark */
401 QEMU_RGB(0x00, 0x00, 0x00), /* black */
402 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
403 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
404 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
405 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
406 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
407 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
408 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
409 },
410 { /* bright */
411 QEMU_RGB(0x00, 0x00, 0x00), /* black */
412 QEMU_RGB(0xff, 0x00, 0x00), /* red */
413 QEMU_RGB(0x00, 0xff, 0x00), /* green */
414 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
415 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
416 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
417 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
418 QEMU_RGB(0xff, 0xff, 0xff), /* white */
419 }
420};
421
422static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
423{
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700424 switch(ds_get_bits_per_pixel(ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800425 case 8:
426 col |= col << 8;
427 col |= col << 16;
428 break;
429 case 15:
430 case 16:
431 col |= col << 16;
432 break;
433 default:
434 break;
435 }
436
437 return col;
438}
439#ifdef DEBUG_CONSOLE
440static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
441{
442 if (t_attrib->bold) {
443 printf("b");
444 } else {
445 printf(" ");
446 }
447 if (t_attrib->uline) {
448 printf("u");
449 } else {
450 printf(" ");
451 }
452 if (t_attrib->blink) {
453 printf("l");
454 } else {
455 printf(" ");
456 }
457 if (t_attrib->invers) {
458 printf("i");
459 } else {
460 printf(" ");
461 }
462 if (t_attrib->unvisible) {
463 printf("n");
464 } else {
465 printf(" ");
466 }
467
468 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
469}
470#endif
471
472static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
473 TextAttributes *t_attrib)
474{
475 uint8_t *d;
476 const uint8_t *font_ptr;
477 unsigned int font_data, linesize, xorcol, bpp;
478 int i;
479 unsigned int fgcol, bgcol;
480
481#ifdef DEBUG_CONSOLE
482 printf("x: %2i y: %2i", x, y);
483 console_print_text_attributes(t_attrib, ch);
484#endif
485
486 if (t_attrib->invers) {
487 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
488 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
489 } else {
490 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
491 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
492 }
493
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700494 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
495 d = ds_get_data(ds) +
496 ds_get_linesize(ds) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
497 linesize = ds_get_linesize(ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800498 font_ptr = vgafont16 + FONT_HEIGHT * ch;
499 xorcol = bgcol ^ fgcol;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700500 switch(ds_get_bits_per_pixel(ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800501 case 8:
502 for(i = 0; i < FONT_HEIGHT; i++) {
503 font_data = *font_ptr++;
504 if (t_attrib->uline
505 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
506 font_data = 0xFFFF;
507 }
508 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
509 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
510 d += linesize;
511 }
512 break;
513 case 16:
514 case 15:
515 for(i = 0; i < FONT_HEIGHT; i++) {
516 font_data = *font_ptr++;
517 if (t_attrib->uline
518 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
519 font_data = 0xFFFF;
520 }
521 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
522 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
523 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
524 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
525 d += linesize;
526 }
527 break;
528 case 32:
529 for(i = 0; i < FONT_HEIGHT; i++) {
530 font_data = *font_ptr++;
531 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
532 font_data = 0xFFFF;
533 }
534 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
535 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
536 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
537 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
538 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
539 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
540 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
541 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
542 d += linesize;
543 }
544 break;
545 }
546}
547
548static void text_console_resize(TextConsole *s)
549{
550 TextCell *cells, *c, *c1;
551 int w1, x, y, last_width;
552
553 last_width = s->width;
554 s->width = s->g_width / FONT_WIDTH;
555 s->height = s->g_height / FONT_HEIGHT;
556
557 w1 = last_width;
558 if (s->width < w1)
559 w1 = s->width;
560
561 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
562 for(y = 0; y < s->total_height; y++) {
563 c = &cells[y * s->width];
564 if (w1 > 0) {
565 c1 = &s->cells[y * last_width];
566 for(x = 0; x < w1; x++) {
567 *c++ = *c1++;
568 }
569 }
570 for(x = w1; x < s->width; x++) {
571 c->ch = ' ';
572 c->t_attrib = s->t_attrib_default;
573 c++;
574 }
575 }
576 qemu_free(s->cells);
577 s->cells = cells;
578}
579
580static inline void text_update_xy(TextConsole *s, int x, int y)
581{
582 s->text_x[0] = MIN(s->text_x[0], x);
583 s->text_x[1] = MAX(s->text_x[1], x);
584 s->text_y[0] = MIN(s->text_y[0], y);
585 s->text_y[1] = MAX(s->text_y[1], y);
586}
587
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700588static void invalidate_xy(TextConsole *s, int x, int y)
589{
590 if (s->update_x0 > x * FONT_WIDTH)
591 s->update_x0 = x * FONT_WIDTH;
592 if (s->update_y0 > y * FONT_HEIGHT)
593 s->update_y0 = y * FONT_HEIGHT;
594 if (s->update_x1 < (x + 1) * FONT_WIDTH)
595 s->update_x1 = (x + 1) * FONT_WIDTH;
596 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
597 s->update_y1 = (y + 1) * FONT_HEIGHT;
598}
599
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800600static void update_xy(TextConsole *s, int x, int y)
601{
602 TextCell *c;
603 int y1, y2;
604
605 if (s == active_console) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700606 if (!ds_get_bits_per_pixel(s->ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800607 text_update_xy(s, x, y);
608 return;
609 }
610
611 y1 = (s->y_base + y) % s->total_height;
612 y2 = y1 - s->y_displayed;
613 if (y2 < 0)
614 y2 += s->total_height;
615 if (y2 < s->height) {
616 c = &s->cells[y1 * s->width + x];
617 vga_putcharxy(s->ds, x, y2, c->ch,
618 &(c->t_attrib));
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700619 invalidate_xy(s, x, y2);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800620 }
621 }
622}
623
624static void console_show_cursor(TextConsole *s, int show)
625{
626 TextCell *c;
627 int y, y1;
628
629 if (s == active_console) {
630 int x = s->x;
631
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->cursor_invalidate = 1;
634 return;
635 }
636
637 if (x >= s->width) {
638 x = s->width - 1;
639 }
640 y1 = (s->y_base + s->y) % s->total_height;
641 y = y1 - s->y_displayed;
642 if (y < 0)
643 y += s->total_height;
644 if (y < s->height) {
645 c = &s->cells[y1 * s->width + x];
646 if (show) {
647 TextAttributes t_attrib = s->t_attrib_default;
648 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
649 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
650 } else {
651 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
652 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700653 invalidate_xy(s, x, y);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800654 }
655 }
656}
657
658static void console_refresh(TextConsole *s)
659{
660 TextCell *c;
661 int x, y, y1;
662
663 if (s != active_console)
664 return;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700665 if (!ds_get_bits_per_pixel(s->ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800666 s->text_x[0] = 0;
667 s->text_y[0] = 0;
668 s->text_x[1] = s->width - 1;
669 s->text_y[1] = s->height - 1;
670 s->cursor_invalidate = 1;
671 return;
672 }
673
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700674 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 -0800675 color_table[0][COLOR_BLACK]);
676 y1 = s->y_displayed;
677 for(y = 0; y < s->height; y++) {
678 c = s->cells + y1 * s->width;
679 for(x = 0; x < s->width; x++) {
680 vga_putcharxy(s->ds, x, y, c->ch,
681 &(c->t_attrib));
682 c++;
683 }
684 if (++y1 == s->total_height)
685 y1 = 0;
686 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800687 console_show_cursor(s, 1);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700688 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 -0800689}
690
691static void console_scroll(int ydelta)
692{
693 TextConsole *s;
694 int i, y1;
695
696 s = active_console;
697 if (!s || (s->console_type == GRAPHIC_CONSOLE))
698 return;
699
700 if (ydelta > 0) {
701 for(i = 0; i < ydelta; i++) {
702 if (s->y_displayed == s->y_base)
703 break;
704 if (++s->y_displayed == s->total_height)
705 s->y_displayed = 0;
706 }
707 } else {
708 ydelta = -ydelta;
709 i = s->backscroll_height;
710 if (i > s->total_height - s->height)
711 i = s->total_height - s->height;
712 y1 = s->y_base - i;
713 if (y1 < 0)
714 y1 += s->total_height;
715 for(i = 0; i < ydelta; i++) {
716 if (s->y_displayed == y1)
717 break;
718 if (--s->y_displayed < 0)
719 s->y_displayed = s->total_height - 1;
720 }
721 }
722 console_refresh(s);
723}
724
725static void console_put_lf(TextConsole *s)
726{
727 TextCell *c;
728 int x, y1;
729
730 s->y++;
731 if (s->y >= s->height) {
732 s->y = s->height - 1;
733
734 if (s->y_displayed == s->y_base) {
735 if (++s->y_displayed == s->total_height)
736 s->y_displayed = 0;
737 }
738 if (++s->y_base == s->total_height)
739 s->y_base = 0;
740 if (s->backscroll_height < s->total_height)
741 s->backscroll_height++;
742 y1 = (s->y_base + s->height - 1) % s->total_height;
743 c = &s->cells[y1 * s->width];
744 for(x = 0; x < s->width; x++) {
745 c->ch = ' ';
746 c->t_attrib = s->t_attrib_default;
747 c++;
748 }
749 if (s == active_console && s->y_displayed == s->y_base) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700750 if (!ds_get_bits_per_pixel(s->ds)) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800751 s->text_x[0] = 0;
752 s->text_y[0] = 0;
753 s->text_x[1] = s->width - 1;
754 s->text_y[1] = s->height - 1;
755 return;
756 }
757
758 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
759 s->width * FONT_WIDTH,
760 (s->height - 1) * FONT_HEIGHT);
761 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
762 s->width * FONT_WIDTH, FONT_HEIGHT,
763 color_table[0][s->t_attrib_default.bgcol]);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700764 s->update_x0 = 0;
765 s->update_y0 = 0;
766 s->update_x1 = s->width * FONT_WIDTH;
767 s->update_y1 = s->height * FONT_HEIGHT;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800768 }
769 }
770}
771
772/* Set console attributes depending on the current escape codes.
773 * NOTE: I know this code is not very efficient (checking every color for it
774 * self) but it is more readable and better maintainable.
775 */
776static void console_handle_escape(TextConsole *s)
777{
778 int i;
779
780 for (i=0; i<s->nb_esc_params; i++) {
781 switch (s->esc_params[i]) {
782 case 0: /* reset all console attributes to default */
783 s->t_attrib = s->t_attrib_default;
784 break;
785 case 1:
786 s->t_attrib.bold = 1;
787 break;
788 case 4:
789 s->t_attrib.uline = 1;
790 break;
791 case 5:
792 s->t_attrib.blink = 1;
793 break;
794 case 7:
795 s->t_attrib.invers = 1;
796 break;
797 case 8:
798 s->t_attrib.unvisible = 1;
799 break;
800 case 22:
801 s->t_attrib.bold = 0;
802 break;
803 case 24:
804 s->t_attrib.uline = 0;
805 break;
806 case 25:
807 s->t_attrib.blink = 0;
808 break;
809 case 27:
810 s->t_attrib.invers = 0;
811 break;
812 case 28:
813 s->t_attrib.unvisible = 0;
814 break;
815 /* set foreground color */
816 case 30:
817 s->t_attrib.fgcol=COLOR_BLACK;
818 break;
819 case 31:
820 s->t_attrib.fgcol=COLOR_RED;
821 break;
822 case 32:
823 s->t_attrib.fgcol=COLOR_GREEN;
824 break;
825 case 33:
826 s->t_attrib.fgcol=COLOR_YELLOW;
827 break;
828 case 34:
829 s->t_attrib.fgcol=COLOR_BLUE;
830 break;
831 case 35:
832 s->t_attrib.fgcol=COLOR_MAGENTA;
833 break;
834 case 36:
835 s->t_attrib.fgcol=COLOR_CYAN;
836 break;
837 case 37:
838 s->t_attrib.fgcol=COLOR_WHITE;
839 break;
840 /* set background color */
841 case 40:
842 s->t_attrib.bgcol=COLOR_BLACK;
843 break;
844 case 41:
845 s->t_attrib.bgcol=COLOR_RED;
846 break;
847 case 42:
848 s->t_attrib.bgcol=COLOR_GREEN;
849 break;
850 case 43:
851 s->t_attrib.bgcol=COLOR_YELLOW;
852 break;
853 case 44:
854 s->t_attrib.bgcol=COLOR_BLUE;
855 break;
856 case 45:
857 s->t_attrib.bgcol=COLOR_MAGENTA;
858 break;
859 case 46:
860 s->t_attrib.bgcol=COLOR_CYAN;
861 break;
862 case 47:
863 s->t_attrib.bgcol=COLOR_WHITE;
864 break;
865 }
866 }
867}
868
869static void console_clear_xy(TextConsole *s, int x, int y)
870{
871 int y1 = (s->y_base + y) % s->total_height;
872 TextCell *c = &s->cells[y1 * s->width + x];
873 c->ch = ' ';
874 c->t_attrib = s->t_attrib_default;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800875 update_xy(s, x, y);
876}
877
878static void console_putchar(TextConsole *s, int ch)
879{
880 TextCell *c;
881 int y1, i;
882 int x, y;
883
884 switch(s->state) {
885 case TTY_STATE_NORM:
886 switch(ch) {
887 case '\r': /* carriage return */
888 s->x = 0;
889 break;
890 case '\n': /* newline */
891 console_put_lf(s);
892 break;
893 case '\b': /* backspace */
894 if (s->x > 0)
895 s->x--;
896 break;
897 case '\t': /* tabspace */
898 if (s->x + (8 - (s->x % 8)) > s->width) {
899 s->x = 0;
900 console_put_lf(s);
901 } else {
902 s->x = s->x + (8 - (s->x % 8));
903 }
904 break;
905 case '\a': /* alert aka. bell */
906 /* TODO: has to be implemented */
907 break;
908 case 14:
909 /* SI (shift in), character set 0 (ignored) */
910 break;
911 case 15:
912 /* SO (shift out), character set 1 (ignored) */
913 break;
914 case 27: /* esc (introducing an escape sequence) */
915 s->state = TTY_STATE_ESC;
916 break;
917 default:
918 if (s->x >= s->width) {
919 /* line wrap */
920 s->x = 0;
921 console_put_lf(s);
922 }
923 y1 = (s->y_base + s->y) % s->total_height;
924 c = &s->cells[y1 * s->width + s->x];
925 c->ch = ch;
926 c->t_attrib = s->t_attrib;
927 update_xy(s, s->x, s->y);
928 s->x++;
929 break;
930 }
931 break;
932 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
933 if (ch == '[') {
934 for(i=0;i<MAX_ESC_PARAMS;i++)
935 s->esc_params[i] = 0;
936 s->nb_esc_params = 0;
937 s->state = TTY_STATE_CSI;
938 } else {
939 s->state = TTY_STATE_NORM;
940 }
941 break;
942 case TTY_STATE_CSI: /* handle escape sequence parameters */
943 if (ch >= '0' && ch <= '9') {
944 if (s->nb_esc_params < MAX_ESC_PARAMS) {
945 s->esc_params[s->nb_esc_params] =
946 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
947 }
948 } else {
949 s->nb_esc_params++;
950 if (ch == ';')
951 break;
952#ifdef DEBUG_CONSOLE
953 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
954 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
955#endif
956 s->state = TTY_STATE_NORM;
957 switch(ch) {
958 case 'A':
959 /* move cursor up */
960 if (s->esc_params[0] == 0) {
961 s->esc_params[0] = 1;
962 }
963 s->y -= s->esc_params[0];
964 if (s->y < 0) {
965 s->y = 0;
966 }
967 break;
968 case 'B':
969 /* move cursor down */
970 if (s->esc_params[0] == 0) {
971 s->esc_params[0] = 1;
972 }
973 s->y += s->esc_params[0];
974 if (s->y >= s->height) {
975 s->y = s->height - 1;
976 }
977 break;
978 case 'C':
979 /* move cursor right */
980 if (s->esc_params[0] == 0) {
981 s->esc_params[0] = 1;
982 }
983 s->x += s->esc_params[0];
984 if (s->x >= s->width) {
985 s->x = s->width - 1;
986 }
987 break;
988 case 'D':
989 /* move cursor left */
990 if (s->esc_params[0] == 0) {
991 s->esc_params[0] = 1;
992 }
993 s->x -= s->esc_params[0];
994 if (s->x < 0) {
995 s->x = 0;
996 }
997 break;
998 case 'G':
999 /* move cursor to column */
1000 s->x = s->esc_params[0] - 1;
1001 if (s->x < 0) {
1002 s->x = 0;
1003 }
1004 break;
1005 case 'f':
1006 case 'H':
1007 /* move cursor to row, column */
1008 s->x = s->esc_params[1] - 1;
1009 if (s->x < 0) {
1010 s->x = 0;
1011 }
1012 s->y = s->esc_params[0] - 1;
1013 if (s->y < 0) {
1014 s->y = 0;
1015 }
1016 break;
1017 case 'J':
1018 switch (s->esc_params[0]) {
1019 case 0:
1020 /* clear to end of screen */
1021 for (y = s->y; y < s->height; y++) {
1022 for (x = 0; x < s->width; x++) {
1023 if (y == s->y && x < s->x) {
1024 continue;
1025 }
1026 console_clear_xy(s, x, y);
1027 }
1028 }
1029 break;
1030 case 1:
1031 /* clear from beginning of screen */
1032 for (y = 0; y <= s->y; y++) {
1033 for (x = 0; x < s->width; x++) {
1034 if (y == s->y && x > s->x) {
1035 break;
1036 }
1037 console_clear_xy(s, x, y);
1038 }
1039 }
1040 break;
1041 case 2:
1042 /* clear entire screen */
1043 for (y = 0; y <= s->height; y++) {
1044 for (x = 0; x < s->width; x++) {
1045 console_clear_xy(s, x, y);
1046 }
1047 }
1048 break;
1049 }
1050 case 'K':
1051 switch (s->esc_params[0]) {
1052 case 0:
1053 /* clear to eol */
1054 for(x = s->x; x < s->width; x++) {
1055 console_clear_xy(s, x, s->y);
1056 }
1057 break;
1058 case 1:
1059 /* clear from beginning of line */
1060 for (x = 0; x <= s->x; x++) {
1061 console_clear_xy(s, x, s->y);
1062 }
1063 break;
1064 case 2:
1065 /* clear entire line */
1066 for(x = 0; x < s->width; x++) {
1067 console_clear_xy(s, x, s->y);
1068 }
1069 break;
1070 }
1071 break;
1072 case 'm':
1073 console_handle_escape(s);
1074 break;
1075 case 'n':
1076 /* report cursor position */
1077 /* TODO: send ESC[row;colR */
1078 break;
1079 case 's':
1080 /* save cursor position */
1081 s->x_saved = s->x;
1082 s->y_saved = s->y;
1083 break;
1084 case 'u':
1085 /* restore cursor position */
1086 s->x = s->x_saved;
1087 s->y = s->y_saved;
1088 break;
1089 default:
1090#ifdef DEBUG_CONSOLE
1091 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1092#endif
1093 break;
1094 }
1095 break;
1096 }
1097 }
1098}
1099
1100void console_select(unsigned int index)
1101{
1102 TextConsole *s;
1103
1104 if (index >= MAX_CONSOLES)
1105 return;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001106 if (active_console) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001107 active_console->g_width = ds_get_width(active_console->ds);
1108 active_console->g_height = ds_get_height(active_console->ds);
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001109 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001110 s = consoles[index];
1111 if (s) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001112 DisplayState *ds = s->ds;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001113 active_console = s;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001114 if (ds_get_bits_per_pixel(s->ds)) {
1115 ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height);
1116 } else {
1117 s->ds->surface->width = s->width;
1118 s->ds->surface->height = s->height;
1119 }
1120 dpy_resize(s->ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001121 vga_hw_invalidate();
1122 }
1123}
1124
1125static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1126{
1127 TextConsole *s = chr->opaque;
1128 int i;
1129
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001130 s->update_x0 = s->width * FONT_WIDTH;
1131 s->update_y0 = s->height * FONT_HEIGHT;
1132 s->update_x1 = 0;
1133 s->update_y1 = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001134 console_show_cursor(s, 0);
1135 for(i = 0; i < len; i++) {
1136 console_putchar(s, buf[i]);
1137 }
1138 console_show_cursor(s, 1);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001139 if (ds_get_bits_per_pixel(s->ds) && s->update_x0 < s->update_x1) {
1140 dpy_update(s->ds, s->update_x0, s->update_y0,
1141 s->update_x1 - s->update_x0,
1142 s->update_y1 - s->update_y0);
1143 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001144 return len;
1145}
1146
1147static void console_send_event(CharDriverState *chr, int event)
1148{
1149 TextConsole *s = chr->opaque;
1150 int i;
1151
1152 if (event == CHR_EVENT_FOCUS) {
1153 for(i = 0; i < nb_consoles; i++) {
1154 if (consoles[i] == s) {
1155 console_select(i);
1156 break;
1157 }
1158 }
1159 }
1160}
1161
1162static void kbd_send_chars(void *opaque)
1163{
1164 TextConsole *s = opaque;
1165 int len;
1166 uint8_t buf[16];
1167
1168 len = qemu_chr_can_read(s->chr);
1169 if (len > s->out_fifo.count)
1170 len = s->out_fifo.count;
1171 if (len > 0) {
1172 if (len > sizeof(buf))
1173 len = sizeof(buf);
1174 qemu_fifo_read(&s->out_fifo, buf, len);
1175 qemu_chr_read(s->chr, buf, len);
1176 }
1177 /* characters are pending: we send them a bit later (XXX:
1178 horrible, should change char device API) */
1179 if (s->out_fifo.count > 0) {
David 'Digit' Turner5973c772011-05-10 07:06:00 +02001180 qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001181 }
1182}
1183
1184/* called when an ascii key is pressed */
1185void kbd_put_keysym(int keysym)
1186{
1187 TextConsole *s;
1188 uint8_t buf[16], *q;
1189 int c;
1190
1191 s = active_console;
1192 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1193 return;
1194
1195 switch(keysym) {
1196 case QEMU_KEY_CTRL_UP:
1197 console_scroll(-1);
1198 break;
1199 case QEMU_KEY_CTRL_DOWN:
1200 console_scroll(1);
1201 break;
1202 case QEMU_KEY_CTRL_PAGEUP:
1203 console_scroll(-10);
1204 break;
1205 case QEMU_KEY_CTRL_PAGEDOWN:
1206 console_scroll(10);
1207 break;
1208 default:
1209 /* convert the QEMU keysym to VT100 key string */
1210 q = buf;
1211 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1212 *q++ = '\033';
1213 *q++ = '[';
1214 c = keysym - 0xe100;
1215 if (c >= 10)
1216 *q++ = '0' + (c / 10);
1217 *q++ = '0' + (c % 10);
1218 *q++ = '~';
1219 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1220 *q++ = '\033';
1221 *q++ = '[';
1222 *q++ = keysym & 0xff;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001223 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1224 console_puts(s->chr, (const uint8_t *) "\r", 1);
1225 *q++ = '\n';
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001226 } else {
1227 *q++ = keysym;
1228 }
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001229 if (s->echo) {
1230 console_puts(s->chr, buf, q - buf);
1231 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001232 if (s->chr->chr_read) {
1233 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1234 kbd_send_chars(s);
1235 }
1236 break;
1237 }
1238}
1239
1240static void text_console_invalidate(void *opaque)
1241{
1242 TextConsole *s = (TextConsole *) opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001243 if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) {
1244 s->g_width = ds_get_width(s->ds);
1245 s->g_height = ds_get_height(s->ds);
1246 text_console_resize(s);
1247 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001248 console_refresh(s);
1249}
1250
1251static void text_console_update(void *opaque, console_ch_t *chardata)
1252{
1253 TextConsole *s = (TextConsole *) opaque;
1254 int i, j, src;
1255
1256 if (s->text_x[0] <= s->text_x[1]) {
1257 src = (s->y_base + s->text_y[0]) * s->width;
1258 chardata += s->text_y[0] * s->width;
1259 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1260 for (j = 0; j < s->width; j ++, src ++)
1261 console_write_ch(chardata ++, s->cells[src].ch |
1262 (s->cells[src].t_attrib.fgcol << 12) |
1263 (s->cells[src].t_attrib.bgcol << 8) |
1264 (s->cells[src].t_attrib.bold << 21));
1265 dpy_update(s->ds, s->text_x[0], s->text_y[0],
1266 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1267 s->text_x[0] = s->width;
1268 s->text_y[0] = s->height;
1269 s->text_x[1] = 0;
1270 s->text_y[1] = 0;
1271 }
1272 if (s->cursor_invalidate) {
1273 dpy_cursor(s->ds, s->x, s->y);
1274 s->cursor_invalidate = 0;
1275 }
1276}
1277
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001278static TextConsole *get_graphic_console(DisplayState *ds)
1279{
1280 int i;
1281 TextConsole *s;
1282 for (i = 0; i < nb_consoles; i++) {
1283 s = consoles[i];
1284 if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
1285 return s;
1286 }
1287 return NULL;
1288}
1289
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001290static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1291{
1292 TextConsole *s;
1293 int i;
1294
1295 if (nb_consoles >= MAX_CONSOLES)
1296 return NULL;
1297 s = qemu_mallocz(sizeof(TextConsole));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001298 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1299 (console_type == GRAPHIC_CONSOLE))) {
1300 active_console = s;
1301 }
1302 s->ds = ds;
1303 s->console_type = console_type;
1304 if (console_type != GRAPHIC_CONSOLE) {
1305 consoles[nb_consoles++] = s;
1306 } else {
1307 /* HACK: Put graphical consoles before text consoles. */
1308 for (i = nb_consoles; i > 0; i--) {
1309 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1310 break;
1311 consoles[i] = consoles[i - 1];
1312 }
1313 consoles[i] = s;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001314 nb_consoles++;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001315 }
1316 return s;
1317}
1318
David Turnerf52506f2010-09-10 16:11:22 +02001319static DisplaySurface* defaultallocator_create_displaysurface(int width, int height)
1320{
1321 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1322
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001323 int linesize = width * 4;
1324 qemu_alloc_display(surface, width, height, linesize,
1325 qemu_default_pixelformat(32), 0);
David Turnerf52506f2010-09-10 16:11:22 +02001326 return surface;
1327}
1328
1329static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
1330 int width, int height)
1331{
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001332 int linesize = width * 4;
1333 qemu_alloc_display(surface, width, height, linesize,
1334 qemu_default_pixelformat(32), 0);
1335 return surface;
1336}
1337
1338void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1339 int linesize, PixelFormat pf, int newflags)
1340{
1341 void *data;
David Turnerf52506f2010-09-10 16:11:22 +02001342 surface->width = width;
1343 surface->height = height;
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001344 surface->linesize = linesize;
1345 surface->pf = pf;
1346 if (surface->flags & QEMU_ALLOCATED_FLAG) {
1347 data = qemu_realloc(surface->data,
1348 surface->linesize * surface->height);
1349 } else {
1350 data = qemu_malloc(surface->linesize * surface->height);
1351 }
1352 surface->data = (uint8_t *)data;
1353 surface->flags = newflags | QEMU_ALLOCATED_FLAG;
David Turnerf52506f2010-09-10 16:11:22 +02001354#ifdef HOST_WORDS_BIGENDIAN
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001355 surface->flags |= QEMU_BIG_ENDIAN_FLAG;
David Turnerf52506f2010-09-10 16:11:22 +02001356#endif
David Turnerf52506f2010-09-10 16:11:22 +02001357}
1358
1359DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
1360 int linesize, uint8_t *data)
1361{
1362 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1363
1364 surface->width = width;
1365 surface->height = height;
1366 surface->linesize = linesize;
1367 surface->pf = qemu_default_pixelformat(bpp);
1368#ifdef HOST_WORDS_BIGENDIAN
1369 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1370#endif
1371 surface->data = data;
1372
1373 return surface;
1374}
1375
1376static void defaultallocator_free_displaysurface(DisplaySurface *surface)
1377{
1378 if (surface == NULL)
1379 return;
1380 if (surface->flags & QEMU_ALLOCATED_FLAG)
1381 qemu_free(surface->data);
1382 qemu_free(surface);
1383}
1384
1385static struct DisplayAllocator default_allocator = {
1386 defaultallocator_create_displaysurface,
1387 defaultallocator_resize_displaysurface,
1388 defaultallocator_free_displaysurface
1389};
1390
1391static void dumb_display_init(void)
1392{
1393 DisplayState *ds = qemu_mallocz(sizeof(DisplayState));
1394 ds->allocator = &default_allocator;
1395 ds->surface = qemu_create_displaysurface(ds, 640, 480);
1396 register_displaystate(ds);
1397}
1398
1399/***********************************************************/
1400/* register display */
1401
1402void register_displaystate(DisplayState *ds)
1403{
1404 DisplayState **s;
1405 s = &display_state;
1406 while (*s != NULL)
1407 s = &(*s)->next;
1408 ds->next = NULL;
1409 *s = ds;
1410}
1411
1412DisplayState *get_displaystate(void)
1413{
1414 if (!display_state) {
1415 dumb_display_init ();
1416 }
1417 return display_state;
1418}
1419
1420DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da)
1421{
1422 if(ds->allocator == &default_allocator) {
1423 DisplaySurface *surf;
1424 surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds));
1425 defaultallocator_free_displaysurface(ds->surface);
1426 ds->surface = surf;
1427 ds->allocator = da;
1428 }
1429 return ds->allocator;
1430}
1431
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001432DisplayState *graphic_console_init(vga_hw_update_ptr update,
1433 vga_hw_invalidate_ptr invalidate,
1434 vga_hw_screen_dump_ptr screen_dump,
1435 vga_hw_text_update_ptr text_update,
1436 void *opaque)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001437{
1438 TextConsole *s;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001439 DisplayState *ds;
1440
1441 ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
Vladimir Chtchetkine90c62352011-01-13 11:24:07 -08001442 ds->allocator = &default_allocator;
David Turnerf52506f2010-09-10 16:11:22 +02001443#ifdef CONFIG_ANDROID
Vladimir Chtchetkinedd50f7d2010-07-30 09:16:41 -07001444 ds->surface = qemu_create_displaysurface(ds, android_display_width, android_display_height);
David Turnerf52506f2010-09-10 16:11:22 +02001445#else
1446 ds->surface = qemu_create_displaysurface(ds, 640, 480);
1447#endif
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001448
1449 s = new_console(ds, GRAPHIC_CONSOLE);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001450 if (s == NULL) {
1451 qemu_free_displaysurface(ds);
1452 qemu_free(ds);
1453 return NULL;
1454 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001455 s->hw_update = update;
1456 s->hw_invalidate = invalidate;
1457 s->hw_screen_dump = screen_dump;
1458 s->hw_text_update = text_update;
1459 s->hw = opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001460
1461 register_displaystate(ds);
1462 return ds;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001463}
1464
1465int is_graphic_console(void)
1466{
1467 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1468}
1469
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001470int is_fixedsize_console(void)
1471{
1472 return active_console && active_console->console_type != TEXT_CONSOLE;
1473}
1474
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001475void console_color_init(DisplayState *ds)
1476{
1477 int i, j;
1478 for (j = 0; j < 2; j++) {
1479 for (i = 0; i < 8; i++) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001480 color_table[j][i] = col_expand(ds,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001481 vga_get_color(ds, color_table_rgb[j][i]));
1482 }
1483 }
1484}
1485
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001486static int n_text_consoles;
1487static CharDriverState *text_consoles[128];
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001488
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001489static void text_console_set_echo(CharDriverState *chr, bool echo)
1490{
1491 TextConsole *s = chr->opaque;
1492
1493 s->echo = echo;
1494}
1495
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001496static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001497{
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001498 TextConsole *s;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001499 static int color_inited;
1500
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001501 s = chr->opaque;
David Turnerf52506f2010-09-10 16:11:22 +02001502
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001503 chr->chr_write = console_puts;
1504 chr->chr_send_event = console_send_event;
1505
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001506 s->out_fifo.buf = s->out_fifo_buf;
1507 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
David 'Digit' Turner5973c772011-05-10 07:06:00 +02001508 s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001509 s->ds = ds;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001510
1511 if (!color_inited) {
1512 color_inited = 1;
1513 console_color_init(s->ds);
1514 }
1515 s->y_displayed = 0;
1516 s->y_base = 0;
1517 s->total_height = DEFAULT_BACKSCROLL;
1518 s->x = 0;
1519 s->y = 0;
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001520 if (s->console_type == TEXT_CONSOLE) {
1521 s->g_width = ds_get_width(s->ds);
1522 s->g_height = ds_get_height(s->ds);
1523 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001524
1525 s->hw_invalidate = text_console_invalidate;
1526 s->hw_text_update = text_console_update;
1527 s->hw = s;
1528
1529 /* Set text attribute defaults */
1530 s->t_attrib_default.bold = 0;
1531 s->t_attrib_default.uline = 0;
1532 s->t_attrib_default.blink = 0;
1533 s->t_attrib_default.invers = 0;
1534 s->t_attrib_default.unvisible = 0;
1535 s->t_attrib_default.fgcol = COLOR_WHITE;
1536 s->t_attrib_default.bgcol = COLOR_BLACK;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001537 /* set current text attributes to default */
1538 s->t_attrib = s->t_attrib_default;
1539 text_console_resize(s);
1540
David Turnerf52506f2010-09-10 16:11:22 +02001541 if (chr->label) {
1542 char msg[128];
1543 int len;
1544
1545 s->t_attrib.bgcol = COLOR_BLUE;
1546 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1547 console_puts(chr, (uint8_t*)msg, len);
1548 s->t_attrib = s->t_attrib_default;
1549 }
1550
David Turner9d118822010-09-10 13:02:07 +02001551 qemu_chr_generic_open(chr);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001552 if (chr->init)
1553 chr->init(chr);
1554}
1555
David Turnerf52506f2010-09-10 16:11:22 +02001556CharDriverState *text_console_init(QemuOpts *opts)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001557{
1558 CharDriverState *chr;
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001559 TextConsole *s;
1560 unsigned width;
1561 unsigned height;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001562
1563 chr = qemu_mallocz(sizeof(CharDriverState));
1564
1565 if (n_text_consoles == 128) {
1566 fprintf(stderr, "Too many text consoles\n");
1567 exit(1);
1568 }
1569 text_consoles[n_text_consoles] = chr;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001570 n_text_consoles++;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001571
David 'Digit' Turner17410ee2011-05-10 10:55:21 +02001572 width = qemu_opt_get_number(opts, "width", 0);
1573 if (width == 0)
1574 width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH;
1575
1576 height = qemu_opt_get_number(opts, "height", 0);
1577 if (height == 0)
1578 height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT;
1579
1580 if (width == 0 || height == 0) {
1581 s = new_console(NULL, TEXT_CONSOLE);
1582 } else {
1583 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1584 }
1585
1586 if (!s) {
1587 free(chr);
1588 return NULL;
1589 }
1590
1591 s->chr = chr;
1592 s->g_width = width;
1593 s->g_height = height;
1594 chr->opaque = s;
1595 chr->chr_set_echo = text_console_set_echo;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001596 return chr;
1597}
1598
David Turnerf52506f2010-09-10 16:11:22 +02001599CharDriverState* text_console_init_compat(const char *label, const char *p)
1600{
1601 QemuOpts *opts;
1602 int width, height;
1603 char temp[32];
1604
1605 opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1);
1606 if (NULL == opts)
1607 return NULL;
1608
1609 if (p != NULL) {
1610 width = strtoul(p, (char **)&p, 10);
1611 if (*p == 'C') {
1612 p++;
1613 width *= FONT_WIDTH;
1614 }
1615 snprintf(temp, sizeof temp, "%d", width);
1616 qemu_opt_set(opts, "width", temp);
1617 if (*p == 'x') {
1618 p++;
1619 height = strtoul(p, (char **)&p, 10);
1620 if (*p == 'C') {
1621 p++;
1622 height *= FONT_HEIGHT;
1623 }
1624 snprintf(temp, sizeof temp, "%d", height);
1625 qemu_opt_set(opts, "height", temp);
1626 }
1627 }
1628 return text_console_init(opts);
1629}
1630
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001631void text_consoles_set_display(DisplayState *ds)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001632{
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001633 int i;
1634
1635 for (i = 0; i < n_text_consoles; i++) {
David 'Digit' Turner3b2846a2011-05-11 01:34:40 +02001636 text_console_do_init(text_consoles[i], ds);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001637 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001638
1639 n_text_consoles = 0;
1640}
1641
1642void qemu_console_resize(DisplayState *ds, int width, int height)
1643{
1644 TextConsole *s = get_graphic_console(ds);
1645 if (!s) return;
1646
1647 s->g_width = width;
1648 s->g_height = height;
1649 if (is_graphic_console()) {
1650 ds->surface = qemu_resize_displaysurface(ds, width, height);
1651 dpy_resize(ds);
1652 }
1653}
1654
1655void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
1656 int dst_x, int dst_y, int w, int h)
1657{
1658 if (is_graphic_console()) {
1659 dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
1660 }
1661}
1662
1663PixelFormat qemu_different_endianness_pixelformat(int bpp)
1664{
1665 PixelFormat pf;
1666
1667 memset(&pf, 0x00, sizeof(PixelFormat));
1668
1669 pf.bits_per_pixel = bpp;
1670 pf.bytes_per_pixel = bpp / 8;
1671 pf.depth = bpp == 32 ? 24 : bpp;
1672
1673 switch (bpp) {
1674 case 24:
1675 pf.rmask = 0x000000FF;
1676 pf.gmask = 0x0000FF00;
1677 pf.bmask = 0x00FF0000;
1678 pf.rmax = 255;
1679 pf.gmax = 255;
1680 pf.bmax = 255;
1681 pf.rshift = 0;
1682 pf.gshift = 8;
1683 pf.bshift = 16;
1684 pf.rbits = 8;
1685 pf.gbits = 8;
1686 pf.bbits = 8;
1687 break;
1688 case 32:
1689 pf.rmask = 0x0000FF00;
1690 pf.gmask = 0x00FF0000;
1691 pf.bmask = 0xFF000000;
1692 pf.amask = 0x00000000;
1693 pf.amax = 255;
1694 pf.rmax = 255;
1695 pf.gmax = 255;
1696 pf.bmax = 255;
1697 pf.ashift = 0;
1698 pf.rshift = 8;
1699 pf.gshift = 16;
1700 pf.bshift = 24;
1701 pf.rbits = 8;
1702 pf.gbits = 8;
1703 pf.bbits = 8;
1704 pf.abits = 8;
1705 break;
1706 default:
1707 break;
1708 }
1709 return pf;
1710}
1711
1712PixelFormat qemu_default_pixelformat(int bpp)
1713{
1714 PixelFormat pf;
1715
1716 memset(&pf, 0x00, sizeof(PixelFormat));
1717
1718 pf.bits_per_pixel = bpp;
1719 pf.bytes_per_pixel = bpp / 8;
1720 pf.depth = bpp == 32 ? 24 : bpp;
1721
1722 switch (bpp) {
David Turnerf52506f2010-09-10 16:11:22 +02001723 case 15:
1724 pf.bits_per_pixel = 16;
1725 pf.bytes_per_pixel = 2;
1726 pf.rmask = 0x00007c00;
1727 pf.gmask = 0x000003E0;
1728 pf.bmask = 0x0000001F;
1729 pf.rmax = 31;
1730 pf.gmax = 31;
1731 pf.bmax = 31;
1732 pf.rshift = 10;
1733 pf.gshift = 5;
1734 pf.bshift = 0;
1735 pf.rbits = 5;
1736 pf.gbits = 5;
1737 pf.bbits = 5;
1738 break;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001739 case 16:
1740 pf.rmask = 0x0000F800;
1741 pf.gmask = 0x000007E0;
1742 pf.bmask = 0x0000001F;
1743 pf.rmax = 31;
1744 pf.gmax = 63;
1745 pf.bmax = 31;
1746 pf.rshift = 11;
1747 pf.gshift = 5;
1748 pf.bshift = 0;
1749 pf.rbits = 5;
1750 pf.gbits = 6;
1751 pf.bbits = 5;
1752 break;
1753 case 24:
1754 pf.rmask = 0x00FF0000;
1755 pf.gmask = 0x0000FF00;
1756 pf.bmask = 0x000000FF;
1757 pf.rmax = 255;
1758 pf.gmax = 255;
1759 pf.bmax = 255;
1760 pf.rshift = 16;
1761 pf.gshift = 8;
1762 pf.bshift = 0;
1763 pf.rbits = 8;
1764 pf.gbits = 8;
1765 pf.bbits = 8;
1766 case 32:
1767 pf.rmask = 0x00FF0000;
1768 pf.gmask = 0x0000FF00;
1769 pf.bmask = 0x000000FF;
1770 pf.amax = 255;
1771 pf.rmax = 255;
1772 pf.gmax = 255;
1773 pf.bmax = 255;
1774 pf.ashift = 24;
1775 pf.rshift = 16;
1776 pf.gshift = 8;
1777 pf.bshift = 0;
1778 pf.rbits = 8;
1779 pf.gbits = 8;
1780 pf.bbits = 8;
1781 pf.abits = 8;
1782 break;
1783 default:
1784 break;
1785 }
1786 return pf;
1787}
1788
David Turnerf52506f2010-09-10 16:11:22 +02001789#ifdef CONFIG_ANDROID
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01001790
1791void
1792unregister_displayupdatelistener(DisplayState *ds, DisplayUpdateListener *dul)
1793{
1794 DisplayUpdateListener **pnode = &ds->update_listeners;
1795 for (;;) {
1796 if (*pnode == NULL)
1797 break;
1798 if (*pnode == dul) {
1799 *pnode = dul->next;
1800 break;
1801 }
1802 pnode = &(*pnode)->next;
1803 }
1804 dul->next = NULL;
1805}
1806
David Turnerf52506f2010-09-10 16:11:22 +02001807void
David 'Digit' Turner94702b02011-01-20 02:46:33 +01001808android_display_reset(DisplayState* ds, int width, int height, int bitspp)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001809{
David 'Digit' Turner94702b02011-01-20 02:46:33 +01001810 DisplaySurface* surface;
1811 int bytespp = (bitspp+7)/8;
1812 int pitch = (bytespp*width + 3) & ~3;
1813
1814 qemu_free_displaysurface(ds);
1815
1816 surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1817
1818 surface->width = width;
1819 surface->height = height;
1820 surface->linesize = pitch;
1821 surface->pf = qemu_default_pixelformat(bitspp);
1822 surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
1823#ifdef HOST_WORDS_BIGENDIAN
1824 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1825#else
1826 surface->flags = QEMU_ALLOCATED_FLAG;
1827#endif
1828
1829 ds->surface = surface;
David Turnerf52506f2010-09-10 16:11:22 +02001830}
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001831#endif