1 /* 2 * QEMU graphical console 3 * 4 * Copyright (c) 2004 Fabrice Bellard 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 #include "qemu-common.h" 25 #include "ui/console.h" 26 #include "qemu/timer.h" 27 #include "qmp-commands.h" 28 #include "char/char.h" 29 30 //#define DEBUG_CONSOLE 31 #define DEFAULT_BACKSCROLL 512 32 #define MAX_CONSOLES 12 33 #define CONSOLE_CURSOR_PERIOD 500 34 35 #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) 36 #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff) 37 38 typedef struct TextAttributes { 39 uint8_t fgcol:4; 40 uint8_t bgcol:4; 41 uint8_t bold:1; 42 uint8_t uline:1; 43 uint8_t blink:1; 44 uint8_t invers:1; 45 uint8_t unvisible:1; 46 } TextAttributes; 47 48 typedef struct TextCell { 49 uint8_t ch; 50 TextAttributes t_attrib; 51 } TextCell; 52 53 #define MAX_ESC_PARAMS 3 54 55 enum TTYState { 56 TTY_STATE_NORM, 57 TTY_STATE_ESC, 58 TTY_STATE_CSI, 59 }; 60 61 typedef struct QEMUFIFO { 62 uint8_t *buf; 63 int buf_size; 64 int count, wptr, rptr; 65 } QEMUFIFO; 66 67 static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1) 68 { 69 int l, len; 70 71 l = f->buf_size - f->count; 72 if (len1 > l) 73 len1 = l; 74 len = len1; 75 while (len > 0) { 76 l = f->buf_size - f->wptr; 77 if (l > len) 78 l = len; 79 memcpy(f->buf + f->wptr, buf, l); 80 f->wptr += l; 81 if (f->wptr >= f->buf_size) 82 f->wptr = 0; 83 buf += l; 84 len -= l; 85 } 86 f->count += len1; 87 return len1; 88 } 89 90 static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1) 91 { 92 int l, len; 93 94 if (len1 > f->count) 95 len1 = f->count; 96 len = len1; 97 while (len > 0) { 98 l = f->buf_size - f->rptr; 99 if (l > len) 100 l = len; 101 memcpy(buf, f->buf + f->rptr, l); 102 f->rptr += l; 103 if (f->rptr >= f->buf_size) 104 f->rptr = 0; 105 buf += l; 106 len -= l; 107 } 108 f->count -= len1; 109 return len1; 110 } 111 112 typedef enum { 113 GRAPHIC_CONSOLE, 114 TEXT_CONSOLE, 115 TEXT_CONSOLE_FIXED_SIZE 116 } console_type_t; 117 118 struct QemuConsole { 119 int index; 120 console_type_t console_type; 121 DisplayState *ds; 122 123 /* Graphic console state. */ 124 vga_hw_update_ptr hw_update; 125 vga_hw_invalidate_ptr hw_invalidate; 126 vga_hw_screen_dump_ptr hw_screen_dump; 127 vga_hw_text_update_ptr hw_text_update; 128 void *hw; 129 int g_width, g_height; 130 131 /* Text console state */ 132 int width; 133 int height; 134 int total_height; 135 int backscroll_height; 136 int x, y; 137 int x_saved, y_saved; 138 int y_displayed; 139 int y_base; 140 TextAttributes t_attrib_default; /* default text attributes */ 141 TextAttributes t_attrib; /* currently active text attributes */ 142 TextCell *cells; 143 int text_x[2], text_y[2], cursor_invalidate; 144 int echo; 145 bool cursor_visible_phase; 146 QEMUTimer *cursor_timer; 147 148 int update_x0; 149 int update_y0; 150 int update_x1; 151 int update_y1; 152 153 enum TTYState state; 154 int esc_params[MAX_ESC_PARAMS]; 155 int nb_esc_params; 156 157 CharDriverState *chr; 158 /* fifo for key pressed */ 159 QEMUFIFO out_fifo; 160 uint8_t out_fifo_buf[16]; 161 QEMUTimer *kbd_timer; 162 }; 163 164 static DisplayState *display_state; 165 static QemuConsole *active_console; 166 static QemuConsole *consoles[MAX_CONSOLES]; 167 static int nb_consoles = 0; 168 169 void vga_hw_update(void) 170 { 171 if (active_console && active_console->hw_update) 172 active_console->hw_update(active_console->hw); 173 } 174 175 void vga_hw_invalidate(void) 176 { 177 if (active_console && active_console->hw_invalidate) 178 active_console->hw_invalidate(active_console->hw); 179 } 180 181 void qmp_screendump(const char *filename, Error **errp) 182 { 183 QemuConsole *previous_active_console; 184 bool cswitch; 185 186 previous_active_console = active_console; 187 cswitch = previous_active_console && previous_active_console->index != 0; 188 189 /* There is currently no way of specifying which screen we want to dump, 190 so always dump the first one. */ 191 if (cswitch) { 192 console_select(0); 193 } 194 if (consoles[0] && consoles[0]->hw_screen_dump) { 195 consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp); 196 } else { 197 error_setg(errp, "device doesn't support screendump"); 198 } 199 200 if (cswitch) { 201 console_select(previous_active_console->index); 202 } 203 } 204 205 void vga_hw_text_update(console_ch_t *chardata) 206 { 207 if (active_console && active_console->hw_text_update) 208 active_console->hw_text_update(active_console->hw, chardata); 209 } 210 211 /* convert a RGBA color to a color index usable in graphic primitives */ 212 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) 213 { 214 unsigned int r, g, b, color; 215 216 switch(ds_get_bits_per_pixel(ds)) { 217 #if 0 218 case 8: 219 r = (rgba >> 16) & 0xff; 220 g = (rgba >> 8) & 0xff; 221 b = (rgba) & 0xff; 222 color = (rgb_to_index[r] * 6 * 6) + 223 (rgb_to_index[g] * 6) + 224 (rgb_to_index[b]); 225 break; 226 #endif 227 case 15: 228 r = (rgba >> 16) & 0xff; 229 g = (rgba >> 8) & 0xff; 230 b = (rgba) & 0xff; 231 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); 232 break; 233 case 16: 234 r = (rgba >> 16) & 0xff; 235 g = (rgba >> 8) & 0xff; 236 b = (rgba) & 0xff; 237 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); 238 break; 239 case 32: 240 default: 241 color = rgba; 242 break; 243 } 244 return color; 245 } 246 247 static void vga_fill_rect (DisplayState *ds, 248 int posx, int posy, int width, int height, uint32_t color) 249 { 250 uint8_t *d, *d1; 251 int x, y, bpp; 252 253 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3; 254 d1 = ds_get_data(ds) + 255 ds_get_linesize(ds) * posy + bpp * posx; 256 for (y = 0; y < height; y++) { 257 d = d1; 258 switch(bpp) { 259 case 1: 260 for (x = 0; x < width; x++) { 261 *((uint8_t *)d) = color; 262 d++; 263 } 264 break; 265 case 2: 266 for (x = 0; x < width; x++) { 267 *((uint16_t *)d) = color; 268 d += 2; 269 } 270 break; 271 case 4: 272 for (x = 0; x < width; x++) { 273 *((uint32_t *)d) = color; 274 d += 4; 275 } 276 break; 277 } 278 d1 += ds_get_linesize(ds); 279 } 280 } 281 282 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ 283 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h) 284 { 285 const uint8_t *s; 286 uint8_t *d; 287 int wb, y, bpp; 288 289 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3; 290 wb = w * bpp; 291 if (yd <= ys) { 292 s = ds_get_data(ds) + 293 ds_get_linesize(ds) * ys + bpp * xs; 294 d = ds_get_data(ds) + 295 ds_get_linesize(ds) * yd + bpp * xd; 296 for (y = 0; y < h; y++) { 297 memmove(d, s, wb); 298 d += ds_get_linesize(ds); 299 s += ds_get_linesize(ds); 300 } 301 } else { 302 s = ds_get_data(ds) + 303 ds_get_linesize(ds) * (ys + h - 1) + bpp * xs; 304 d = ds_get_data(ds) + 305 ds_get_linesize(ds) * (yd + h - 1) + bpp * xd; 306 for (y = 0; y < h; y++) { 307 memmove(d, s, wb); 308 d -= ds_get_linesize(ds); 309 s -= ds_get_linesize(ds); 310 } 311 } 312 } 313 314 /***********************************************************/ 315 /* basic char display */ 316 317 #define FONT_HEIGHT 16 318 #define FONT_WIDTH 8 319 320 #include "vgafont.h" 321 322 #define cbswap_32(__x) \ 323 ((uint32_t)( \ 324 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ 325 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ 326 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ 327 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) 328 329 #ifdef HOST_WORDS_BIGENDIAN 330 #define PAT(x) x 331 #else 332 #define PAT(x) cbswap_32(x) 333 #endif 334 335 static const uint32_t dmask16[16] = { 336 PAT(0x00000000), 337 PAT(0x000000ff), 338 PAT(0x0000ff00), 339 PAT(0x0000ffff), 340 PAT(0x00ff0000), 341 PAT(0x00ff00ff), 342 PAT(0x00ffff00), 343 PAT(0x00ffffff), 344 PAT(0xff000000), 345 PAT(0xff0000ff), 346 PAT(0xff00ff00), 347 PAT(0xff00ffff), 348 PAT(0xffff0000), 349 PAT(0xffff00ff), 350 PAT(0xffffff00), 351 PAT(0xffffffff), 352 }; 353 354 static const uint32_t dmask4[4] = { 355 PAT(0x00000000), 356 PAT(0x0000ffff), 357 PAT(0xffff0000), 358 PAT(0xffffffff), 359 }; 360 361 static uint32_t color_table[2][8]; 362 363 #ifndef CONFIG_CURSES 364 enum color_names { 365 COLOR_BLACK = 0, 366 COLOR_RED = 1, 367 COLOR_GREEN = 2, 368 COLOR_YELLOW = 3, 369 COLOR_BLUE = 4, 370 COLOR_MAGENTA = 5, 371 COLOR_CYAN = 6, 372 COLOR_WHITE = 7 373 }; 374 #endif 375 376 static const uint32_t color_table_rgb[2][8] = { 377 { /* dark */ 378 QEMU_RGB(0x00, 0x00, 0x00), /* black */ 379 QEMU_RGB(0xaa, 0x00, 0x00), /* red */ 380 QEMU_RGB(0x00, 0xaa, 0x00), /* green */ 381 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ 382 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ 383 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ 384 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ 385 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ 386 }, 387 { /* bright */ 388 QEMU_RGB(0x00, 0x00, 0x00), /* black */ 389 QEMU_RGB(0xff, 0x00, 0x00), /* red */ 390 QEMU_RGB(0x00, 0xff, 0x00), /* green */ 391 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ 392 QEMU_RGB(0x00, 0x00, 0xff), /* blue */ 393 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ 394 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ 395 QEMU_RGB(0xff, 0xff, 0xff), /* white */ 396 } 397 }; 398 399 static inline unsigned int col_expand(DisplayState *ds, unsigned int col) 400 { 401 switch(ds_get_bits_per_pixel(ds)) { 402 case 8: 403 col |= col << 8; 404 col |= col << 16; 405 break; 406 case 15: 407 case 16: 408 col |= col << 16; 409 break; 410 default: 411 break; 412 } 413 414 return col; 415 } 416 #ifdef DEBUG_CONSOLE 417 static void console_print_text_attributes(TextAttributes *t_attrib, char ch) 418 { 419 if (t_attrib->bold) { 420 printf("b"); 421 } else { 422 printf(" "); 423 } 424 if (t_attrib->uline) { 425 printf("u"); 426 } else { 427 printf(" "); 428 } 429 if (t_attrib->blink) { 430 printf("l"); 431 } else { 432 printf(" "); 433 } 434 if (t_attrib->invers) { 435 printf("i"); 436 } else { 437 printf(" "); 438 } 439 if (t_attrib->unvisible) { 440 printf("n"); 441 } else { 442 printf(" "); 443 } 444 445 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch); 446 } 447 #endif 448 449 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch, 450 TextAttributes *t_attrib) 451 { 452 uint8_t *d; 453 const uint8_t *font_ptr; 454 unsigned int font_data, linesize, xorcol, bpp; 455 int i; 456 unsigned int fgcol, bgcol; 457 458 #ifdef DEBUG_CONSOLE 459 printf("x: %2i y: %2i", x, y); 460 console_print_text_attributes(t_attrib, ch); 461 #endif 462 463 if (t_attrib->invers) { 464 bgcol = color_table[t_attrib->bold][t_attrib->fgcol]; 465 fgcol = color_table[t_attrib->bold][t_attrib->bgcol]; 466 } else { 467 fgcol = color_table[t_attrib->bold][t_attrib->fgcol]; 468 bgcol = color_table[t_attrib->bold][t_attrib->bgcol]; 469 } 470 471 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3; 472 d = ds_get_data(ds) + 473 ds_get_linesize(ds) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH; 474 linesize = ds_get_linesize(ds); 475 font_ptr = vgafont16 + FONT_HEIGHT * ch; 476 xorcol = bgcol ^ fgcol; 477 switch(ds_get_bits_per_pixel(ds)) { 478 case 8: 479 for(i = 0; i < FONT_HEIGHT; i++) { 480 font_data = *font_ptr++; 481 if (t_attrib->uline 482 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) { 483 font_data = 0xFF; 484 } 485 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol; 486 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; 487 d += linesize; 488 } 489 break; 490 case 16: 491 case 15: 492 for(i = 0; i < FONT_HEIGHT; i++) { 493 font_data = *font_ptr++; 494 if (t_attrib->uline 495 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) { 496 font_data = 0xFF; 497 } 498 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol; 499 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol; 500 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol; 501 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; 502 d += linesize; 503 } 504 break; 505 case 32: 506 for(i = 0; i < FONT_HEIGHT; i++) { 507 font_data = *font_ptr++; 508 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) { 509 font_data = 0xFF; 510 } 511 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; 512 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; 513 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; 514 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; 515 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; 516 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; 517 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; 518 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; 519 d += linesize; 520 } 521 break; 522 } 523 } 524 525 static void text_console_resize(QemuConsole *s) 526 { 527 TextCell *cells, *c, *c1; 528 int w1, x, y, last_width; 529 530 last_width = s->width; 531 s->width = s->g_width / FONT_WIDTH; 532 s->height = s->g_height / FONT_HEIGHT; 533 534 w1 = last_width; 535 if (s->width < w1) 536 w1 = s->width; 537 538 cells = g_malloc(s->width * s->total_height * sizeof(TextCell)); 539 for(y = 0; y < s->total_height; y++) { 540 c = &cells[y * s->width]; 541 if (w1 > 0) { 542 c1 = &s->cells[y * last_width]; 543 for(x = 0; x < w1; x++) { 544 *c++ = *c1++; 545 } 546 } 547 for(x = w1; x < s->width; x++) { 548 c->ch = ' '; 549 c->t_attrib = s->t_attrib_default; 550 c++; 551 } 552 } 553 g_free(s->cells); 554 s->cells = cells; 555 } 556 557 static inline void text_update_xy(QemuConsole *s, int x, int y) 558 { 559 s->text_x[0] = MIN(s->text_x[0], x); 560 s->text_x[1] = MAX(s->text_x[1], x); 561 s->text_y[0] = MIN(s->text_y[0], y); 562 s->text_y[1] = MAX(s->text_y[1], y); 563 } 564 565 static void invalidate_xy(QemuConsole *s, int x, int y) 566 { 567 if (s->update_x0 > x * FONT_WIDTH) 568 s->update_x0 = x * FONT_WIDTH; 569 if (s->update_y0 > y * FONT_HEIGHT) 570 s->update_y0 = y * FONT_HEIGHT; 571 if (s->update_x1 < (x + 1) * FONT_WIDTH) 572 s->update_x1 = (x + 1) * FONT_WIDTH; 573 if (s->update_y1 < (y + 1) * FONT_HEIGHT) 574 s->update_y1 = (y + 1) * FONT_HEIGHT; 575 } 576 577 static void update_xy(QemuConsole *s, int x, int y) 578 { 579 TextCell *c; 580 int y1, y2; 581 582 if (s == active_console) { 583 if (!ds_get_bits_per_pixel(s->ds)) { 584 text_update_xy(s, x, y); 585 return; 586 } 587 588 y1 = (s->y_base + y) % s->total_height; 589 y2 = y1 - s->y_displayed; 590 if (y2 < 0) 591 y2 += s->total_height; 592 if (y2 < s->height) { 593 c = &s->cells[y1 * s->width + x]; 594 vga_putcharxy(s->ds, x, y2, c->ch, 595 &(c->t_attrib)); 596 invalidate_xy(s, x, y2); 597 } 598 } 599 } 600 601 static void console_show_cursor(QemuConsole *s, int show) 602 { 603 TextCell *c; 604 int y, y1; 605 606 if (s == active_console) { 607 int x = s->x; 608 609 if (!ds_get_bits_per_pixel(s->ds)) { 610 s->cursor_invalidate = 1; 611 return; 612 } 613 614 if (x >= s->width) { 615 x = s->width - 1; 616 } 617 y1 = (s->y_base + s->y) % s->total_height; 618 y = y1 - s->y_displayed; 619 if (y < 0) 620 y += s->total_height; 621 if (y < s->height) { 622 c = &s->cells[y1 * s->width + x]; 623 if (show && s->cursor_visible_phase) { 624 TextAttributes t_attrib = s->t_attrib_default; 625 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ 626 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib); 627 } else { 628 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib)); 629 } 630 invalidate_xy(s, x, y); 631 } 632 } 633 } 634 635 static void console_refresh(QemuConsole *s) 636 { 637 TextCell *c; 638 int x, y, y1; 639 640 if (s != active_console) 641 return; 642 643 if (s->ds->have_text) { 644 s->text_x[0] = 0; 645 s->text_y[0] = 0; 646 s->text_x[1] = s->width - 1; 647 s->text_y[1] = s->height - 1; 648 s->cursor_invalidate = 1; 649 } 650 651 if (s->ds->have_gfx) { 652 vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds), 653 color_table[0][COLOR_BLACK]); 654 y1 = s->y_displayed; 655 for (y = 0; y < s->height; y++) { 656 c = s->cells + y1 * s->width; 657 for (x = 0; x < s->width; x++) { 658 vga_putcharxy(s->ds, x, y, c->ch, 659 &(c->t_attrib)); 660 c++; 661 } 662 if (++y1 == s->total_height) { 663 y1 = 0; 664 } 665 } 666 console_show_cursor(s, 1); 667 dpy_gfx_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds)); 668 } 669 } 670 671 static void console_scroll(int ydelta) 672 { 673 QemuConsole *s; 674 int i, y1; 675 676 s = active_console; 677 if (!s || (s->console_type == GRAPHIC_CONSOLE)) 678 return; 679 680 if (ydelta > 0) { 681 for(i = 0; i < ydelta; i++) { 682 if (s->y_displayed == s->y_base) 683 break; 684 if (++s->y_displayed == s->total_height) 685 s->y_displayed = 0; 686 } 687 } else { 688 ydelta = -ydelta; 689 i = s->backscroll_height; 690 if (i > s->total_height - s->height) 691 i = s->total_height - s->height; 692 y1 = s->y_base - i; 693 if (y1 < 0) 694 y1 += s->total_height; 695 for(i = 0; i < ydelta; i++) { 696 if (s->y_displayed == y1) 697 break; 698 if (--s->y_displayed < 0) 699 s->y_displayed = s->total_height - 1; 700 } 701 } 702 console_refresh(s); 703 } 704 705 static void console_put_lf(QemuConsole *s) 706 { 707 TextCell *c; 708 int x, y1; 709 710 s->y++; 711 if (s->y >= s->height) { 712 s->y = s->height - 1; 713 714 if (s->y_displayed == s->y_base) { 715 if (++s->y_displayed == s->total_height) 716 s->y_displayed = 0; 717 } 718 if (++s->y_base == s->total_height) 719 s->y_base = 0; 720 if (s->backscroll_height < s->total_height) 721 s->backscroll_height++; 722 y1 = (s->y_base + s->height - 1) % s->total_height; 723 c = &s->cells[y1 * s->width]; 724 for(x = 0; x < s->width; x++) { 725 c->ch = ' '; 726 c->t_attrib = s->t_attrib_default; 727 c++; 728 } 729 if (s == active_console && s->y_displayed == s->y_base) { 730 if (!ds_get_bits_per_pixel(s->ds)) { 731 s->text_x[0] = 0; 732 s->text_y[0] = 0; 733 s->text_x[1] = s->width - 1; 734 s->text_y[1] = s->height - 1; 735 return; 736 } 737 738 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0, 739 s->width * FONT_WIDTH, 740 (s->height - 1) * FONT_HEIGHT); 741 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT, 742 s->width * FONT_WIDTH, FONT_HEIGHT, 743 color_table[0][s->t_attrib_default.bgcol]); 744 s->update_x0 = 0; 745 s->update_y0 = 0; 746 s->update_x1 = s->width * FONT_WIDTH; 747 s->update_y1 = s->height * FONT_HEIGHT; 748 } 749 } 750 } 751 752 /* Set console attributes depending on the current escape codes. 753 * NOTE: I know this code is not very efficient (checking every color for it 754 * self) but it is more readable and better maintainable. 755 */ 756 static void console_handle_escape(QemuConsole *s) 757 { 758 int i; 759 760 for (i=0; i<s->nb_esc_params; i++) { 761 switch (s->esc_params[i]) { 762 case 0: /* reset all console attributes to default */ 763 s->t_attrib = s->t_attrib_default; 764 break; 765 case 1: 766 s->t_attrib.bold = 1; 767 break; 768 case 4: 769 s->t_attrib.uline = 1; 770 break; 771 case 5: 772 s->t_attrib.blink = 1; 773 break; 774 case 7: 775 s->t_attrib.invers = 1; 776 break; 777 case 8: 778 s->t_attrib.unvisible = 1; 779 break; 780 case 22: 781 s->t_attrib.bold = 0; 782 break; 783 case 24: 784 s->t_attrib.uline = 0; 785 break; 786 case 25: 787 s->t_attrib.blink = 0; 788 break; 789 case 27: 790 s->t_attrib.invers = 0; 791 break; 792 case 28: 793 s->t_attrib.unvisible = 0; 794 break; 795 /* set foreground color */ 796 case 30: 797 s->t_attrib.fgcol=COLOR_BLACK; 798 break; 799 case 31: 800 s->t_attrib.fgcol=COLOR_RED; 801 break; 802 case 32: 803 s->t_attrib.fgcol=COLOR_GREEN; 804 break; 805 case 33: 806 s->t_attrib.fgcol=COLOR_YELLOW; 807 break; 808 case 34: 809 s->t_attrib.fgcol=COLOR_BLUE; 810 break; 811 case 35: 812 s->t_attrib.fgcol=COLOR_MAGENTA; 813 break; 814 case 36: 815 s->t_attrib.fgcol=COLOR_CYAN; 816 break; 817 case 37: 818 s->t_attrib.fgcol=COLOR_WHITE; 819 break; 820 /* set background color */ 821 case 40: 822 s->t_attrib.bgcol=COLOR_BLACK; 823 break; 824 case 41: 825 s->t_attrib.bgcol=COLOR_RED; 826 break; 827 case 42: 828 s->t_attrib.bgcol=COLOR_GREEN; 829 break; 830 case 43: 831 s->t_attrib.bgcol=COLOR_YELLOW; 832 break; 833 case 44: 834 s->t_attrib.bgcol=COLOR_BLUE; 835 break; 836 case 45: 837 s->t_attrib.bgcol=COLOR_MAGENTA; 838 break; 839 case 46: 840 s->t_attrib.bgcol=COLOR_CYAN; 841 break; 842 case 47: 843 s->t_attrib.bgcol=COLOR_WHITE; 844 break; 845 } 846 } 847 } 848 849 static void console_clear_xy(QemuConsole *s, int x, int y) 850 { 851 int y1 = (s->y_base + y) % s->total_height; 852 TextCell *c = &s->cells[y1 * s->width + x]; 853 c->ch = ' '; 854 c->t_attrib = s->t_attrib_default; 855 update_xy(s, x, y); 856 } 857 858 /* set cursor, checking bounds */ 859 static void set_cursor(QemuConsole *s, int x, int y) 860 { 861 if (x < 0) { 862 x = 0; 863 } 864 if (y < 0) { 865 y = 0; 866 } 867 if (y >= s->height) { 868 y = s->height - 1; 869 } 870 if (x >= s->width) { 871 x = s->width - 1; 872 } 873 874 s->x = x; 875 s->y = y; 876 } 877 878 static void console_putchar(QemuConsole *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 int *param = &s->esc_params[s->nb_esc_params]; 946 int digit = (ch - '0'); 947 948 *param = (*param <= (INT_MAX - digit) / 10) ? 949 *param * 10 + digit : INT_MAX; 950 } 951 } else { 952 if (s->nb_esc_params < MAX_ESC_PARAMS) 953 s->nb_esc_params++; 954 if (ch == ';') 955 break; 956 #ifdef DEBUG_CONSOLE 957 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n", 958 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params); 959 #endif 960 s->state = TTY_STATE_NORM; 961 switch(ch) { 962 case 'A': 963 /* move cursor up */ 964 if (s->esc_params[0] == 0) { 965 s->esc_params[0] = 1; 966 } 967 set_cursor(s, s->x, s->y - s->esc_params[0]); 968 break; 969 case 'B': 970 /* move cursor down */ 971 if (s->esc_params[0] == 0) { 972 s->esc_params[0] = 1; 973 } 974 set_cursor(s, s->x, s->y + s->esc_params[0]); 975 break; 976 case 'C': 977 /* move cursor right */ 978 if (s->esc_params[0] == 0) { 979 s->esc_params[0] = 1; 980 } 981 set_cursor(s, s->x + s->esc_params[0], s->y); 982 break; 983 case 'D': 984 /* move cursor left */ 985 if (s->esc_params[0] == 0) { 986 s->esc_params[0] = 1; 987 } 988 set_cursor(s, s->x - s->esc_params[0], s->y); 989 break; 990 case 'G': 991 /* move cursor to column */ 992 set_cursor(s, s->esc_params[0] - 1, s->y); 993 break; 994 case 'f': 995 case 'H': 996 /* move cursor to row, column */ 997 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1); 998 break; 999 case 'J': 1000 switch (s->esc_params[0]) { 1001 case 0: 1002 /* clear to end of screen */ 1003 for (y = s->y; y < s->height; y++) { 1004 for (x = 0; x < s->width; x++) { 1005 if (y == s->y && x < s->x) { 1006 continue; 1007 } 1008 console_clear_xy(s, x, y); 1009 } 1010 } 1011 break; 1012 case 1: 1013 /* clear from beginning of screen */ 1014 for (y = 0; y <= s->y; y++) { 1015 for (x = 0; x < s->width; x++) { 1016 if (y == s->y && x > s->x) { 1017 break; 1018 } 1019 console_clear_xy(s, x, y); 1020 } 1021 } 1022 break; 1023 case 2: 1024 /* clear entire screen */ 1025 for (y = 0; y <= s->height; y++) { 1026 for (x = 0; x < s->width; x++) { 1027 console_clear_xy(s, x, y); 1028 } 1029 } 1030 break; 1031 } 1032 break; 1033 case 'K': 1034 switch (s->esc_params[0]) { 1035 case 0: 1036 /* clear to eol */ 1037 for(x = s->x; x < s->width; x++) { 1038 console_clear_xy(s, x, s->y); 1039 } 1040 break; 1041 case 1: 1042 /* clear from beginning of line */ 1043 for (x = 0; x <= s->x; x++) { 1044 console_clear_xy(s, x, s->y); 1045 } 1046 break; 1047 case 2: 1048 /* clear entire line */ 1049 for(x = 0; x < s->width; x++) { 1050 console_clear_xy(s, x, s->y); 1051 } 1052 break; 1053 } 1054 break; 1055 case 'm': 1056 console_handle_escape(s); 1057 break; 1058 case 'n': 1059 /* report cursor position */ 1060 /* TODO: send ESC[row;colR */ 1061 break; 1062 case 's': 1063 /* save cursor position */ 1064 s->x_saved = s->x; 1065 s->y_saved = s->y; 1066 break; 1067 case 'u': 1068 /* restore cursor position */ 1069 s->x = s->x_saved; 1070 s->y = s->y_saved; 1071 break; 1072 default: 1073 #ifdef DEBUG_CONSOLE 1074 fprintf(stderr, "unhandled escape character '%c'\n", ch); 1075 #endif 1076 break; 1077 } 1078 break; 1079 } 1080 } 1081 } 1082 1083 void console_select(unsigned int index) 1084 { 1085 QemuConsole *s; 1086 1087 if (index >= MAX_CONSOLES) 1088 return; 1089 if (active_console) { 1090 active_console->g_width = ds_get_width(active_console->ds); 1091 active_console->g_height = ds_get_height(active_console->ds); 1092 } 1093 s = consoles[index]; 1094 if (s) { 1095 DisplayState *ds = s->ds; 1096 1097 if (active_console && active_console->cursor_timer) { 1098 qemu_del_timer(active_console->cursor_timer); 1099 } 1100 active_console = s; 1101 if (ds->have_gfx) { 1102 ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height); 1103 dpy_gfx_resize(ds); 1104 } 1105 if (ds->have_text) { 1106 dpy_text_resize(ds, s->width, s->height); 1107 } 1108 if (s->cursor_timer) { 1109 qemu_mod_timer(s->cursor_timer, 1110 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2); 1111 } 1112 vga_hw_invalidate(); 1113 } 1114 } 1115 1116 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len) 1117 { 1118 QemuConsole *s = chr->opaque; 1119 int i; 1120 1121 s->update_x0 = s->width * FONT_WIDTH; 1122 s->update_y0 = s->height * FONT_HEIGHT; 1123 s->update_x1 = 0; 1124 s->update_y1 = 0; 1125 console_show_cursor(s, 0); 1126 for(i = 0; i < len; i++) { 1127 console_putchar(s, buf[i]); 1128 } 1129 console_show_cursor(s, 1); 1130 if (s->ds->have_gfx && s->update_x0 < s->update_x1) { 1131 dpy_gfx_update(s->ds, s->update_x0, s->update_y0, 1132 s->update_x1 - s->update_x0, 1133 s->update_y1 - s->update_y0); 1134 } 1135 return len; 1136 } 1137 1138 static void kbd_send_chars(void *opaque) 1139 { 1140 QemuConsole *s = opaque; 1141 int len; 1142 uint8_t buf[16]; 1143 1144 len = qemu_chr_be_can_write(s->chr); 1145 if (len > s->out_fifo.count) 1146 len = s->out_fifo.count; 1147 if (len > 0) { 1148 if (len > sizeof(buf)) 1149 len = sizeof(buf); 1150 qemu_fifo_read(&s->out_fifo, buf, len); 1151 qemu_chr_be_write(s->chr, buf, len); 1152 } 1153 /* characters are pending: we send them a bit later (XXX: 1154 horrible, should change char device API) */ 1155 if (s->out_fifo.count > 0) { 1156 qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1); 1157 } 1158 } 1159 1160 /* called when an ascii key is pressed */ 1161 void kbd_put_keysym(int keysym) 1162 { 1163 QemuConsole *s; 1164 uint8_t buf[16], *q; 1165 int c; 1166 1167 s = active_console; 1168 if (!s || (s->console_type == GRAPHIC_CONSOLE)) 1169 return; 1170 1171 switch(keysym) { 1172 case QEMU_KEY_CTRL_UP: 1173 console_scroll(-1); 1174 break; 1175 case QEMU_KEY_CTRL_DOWN: 1176 console_scroll(1); 1177 break; 1178 case QEMU_KEY_CTRL_PAGEUP: 1179 console_scroll(-10); 1180 break; 1181 case QEMU_KEY_CTRL_PAGEDOWN: 1182 console_scroll(10); 1183 break; 1184 default: 1185 /* convert the QEMU keysym to VT100 key string */ 1186 q = buf; 1187 if (keysym >= 0xe100 && keysym <= 0xe11f) { 1188 *q++ = '\033'; 1189 *q++ = '['; 1190 c = keysym - 0xe100; 1191 if (c >= 10) 1192 *q++ = '0' + (c / 10); 1193 *q++ = '0' + (c % 10); 1194 *q++ = '~'; 1195 } else if (keysym >= 0xe120 && keysym <= 0xe17f) { 1196 *q++ = '\033'; 1197 *q++ = '['; 1198 *q++ = keysym & 0xff; 1199 } else if (s->echo && (keysym == '\r' || keysym == '\n')) { 1200 console_puts(s->chr, (const uint8_t *) "\r", 1); 1201 *q++ = '\n'; 1202 } else { 1203 *q++ = keysym; 1204 } 1205 if (s->echo) { 1206 console_puts(s->chr, buf, q - buf); 1207 } 1208 if (s->chr->chr_read) { 1209 qemu_fifo_write(&s->out_fifo, buf, q - buf); 1210 kbd_send_chars(s); 1211 } 1212 break; 1213 } 1214 } 1215 1216 static void text_console_invalidate(void *opaque) 1217 { 1218 QemuConsole *s = (QemuConsole *) opaque; 1219 if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) { 1220 s->g_width = ds_get_width(s->ds); 1221 s->g_height = ds_get_height(s->ds); 1222 text_console_resize(s); 1223 } 1224 console_refresh(s); 1225 } 1226 1227 static void text_console_update(void *opaque, console_ch_t *chardata) 1228 { 1229 QemuConsole *s = (QemuConsole *) opaque; 1230 int i, j, src; 1231 1232 if (s->text_x[0] <= s->text_x[1]) { 1233 src = (s->y_base + s->text_y[0]) * s->width; 1234 chardata += s->text_y[0] * s->width; 1235 for (i = s->text_y[0]; i <= s->text_y[1]; i ++) 1236 for (j = 0; j < s->width; j ++, src ++) 1237 console_write_ch(chardata ++, s->cells[src].ch | 1238 (s->cells[src].t_attrib.fgcol << 12) | 1239 (s->cells[src].t_attrib.bgcol << 8) | 1240 (s->cells[src].t_attrib.bold << 21)); 1241 dpy_text_update(s->ds, s->text_x[0], s->text_y[0], 1242 s->text_x[1] - s->text_x[0], i - s->text_y[0]); 1243 s->text_x[0] = s->width; 1244 s->text_y[0] = s->height; 1245 s->text_x[1] = 0; 1246 s->text_y[1] = 0; 1247 } 1248 if (s->cursor_invalidate) { 1249 dpy_text_cursor(s->ds, s->x, s->y); 1250 s->cursor_invalidate = 0; 1251 } 1252 } 1253 1254 static QemuConsole *get_graphic_console(DisplayState *ds) 1255 { 1256 int i; 1257 QemuConsole *s; 1258 for (i = 0; i < nb_consoles; i++) { 1259 s = consoles[i]; 1260 if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds) 1261 return s; 1262 } 1263 return NULL; 1264 } 1265 1266 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type) 1267 { 1268 QemuConsole *s; 1269 int i; 1270 1271 if (nb_consoles >= MAX_CONSOLES) 1272 return NULL; 1273 s = g_malloc0(sizeof(QemuConsole)); 1274 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && 1275 (console_type == GRAPHIC_CONSOLE))) { 1276 active_console = s; 1277 } 1278 s->ds = ds; 1279 s->console_type = console_type; 1280 if (console_type != GRAPHIC_CONSOLE) { 1281 s->index = nb_consoles; 1282 consoles[nb_consoles++] = s; 1283 } else { 1284 /* HACK: Put graphical consoles before text consoles. */ 1285 for (i = nb_consoles; i > 0; i--) { 1286 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE) 1287 break; 1288 consoles[i] = consoles[i - 1]; 1289 consoles[i]->index = i; 1290 } 1291 s->index = i; 1292 consoles[i] = s; 1293 nb_consoles++; 1294 } 1295 return s; 1296 } 1297 1298 static void qemu_alloc_display(DisplaySurface *surface, int width, int height, 1299 int linesize, PixelFormat pf, int newflags) 1300 { 1301 surface->pf = pf; 1302 1303 qemu_pixman_image_unref(surface->image); 1304 surface->image = NULL; 1305 1306 surface->format = qemu_pixman_get_format(&pf); 1307 assert(surface->format != 0); 1308 surface->image = pixman_image_create_bits(surface->format, 1309 width, height, 1310 NULL, linesize); 1311 assert(surface->image != NULL); 1312 1313 surface->flags = newflags | QEMU_ALLOCATED_FLAG; 1314 #ifdef HOST_WORDS_BIGENDIAN 1315 surface->flags |= QEMU_BIG_ENDIAN_FLAG; 1316 #endif 1317 } 1318 1319 DisplaySurface *qemu_create_displaysurface(DisplayState *ds, 1320 int width, int height) 1321 { 1322 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1323 1324 int linesize = width * 4; 1325 qemu_alloc_display(surface, width, height, linesize, 1326 qemu_default_pixelformat(32), 0); 1327 return surface; 1328 } 1329 1330 DisplaySurface *qemu_resize_displaysurface(DisplayState *ds, 1331 int width, int height) 1332 { 1333 int linesize = width * 4; 1334 1335 trace_displaysurface_resize(ds, ds->surface, width, height); 1336 qemu_alloc_display(ds->surface, width, height, linesize, 1337 qemu_default_pixelformat(32), 0); 1338 return ds->surface; 1339 } 1340 1341 DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp, 1342 int linesize, uint8_t *data, 1343 bool byteswap) 1344 { 1345 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1346 1347 if (byteswap) { 1348 surface->pf = qemu_different_endianness_pixelformat(bpp); 1349 } else { 1350 surface->pf = qemu_default_pixelformat(bpp); 1351 } 1352 1353 surface->format = qemu_pixman_get_format(&surface->pf); 1354 assert(surface->format != 0); 1355 surface->image = pixman_image_create_bits(surface->format, 1356 width, height, 1357 (void *)data, linesize); 1358 assert(surface->image != NULL); 1359 1360 #ifdef HOST_WORDS_BIGENDIAN 1361 surface->flags = QEMU_BIG_ENDIAN_FLAG; 1362 #endif 1363 1364 return surface; 1365 } 1366 1367 void qemu_free_displaysurface(DisplayState *ds) 1368 { 1369 trace_displaysurface_free(ds, ds->surface); 1370 if (ds->surface == NULL) { 1371 return; 1372 } 1373 qemu_pixman_image_unref(ds->surface->image); 1374 g_free(ds->surface); 1375 } 1376 1377 static void dumb_display_init(void) 1378 { 1379 DisplayState *ds = g_malloc0(sizeof(DisplayState)); 1380 int width = 640; 1381 int height = 480; 1382 1383 if (is_fixedsize_console()) { 1384 width = active_console->g_width; 1385 height = active_console->g_height; 1386 } 1387 ds->surface = qemu_create_displaysurface(ds, width, height); 1388 register_displaystate(ds); 1389 } 1390 1391 /***********************************************************/ 1392 /* register display */ 1393 1394 void register_displaystate(DisplayState *ds) 1395 { 1396 DisplayState **s; 1397 s = &display_state; 1398 while (*s != NULL) 1399 s = &(*s)->next; 1400 ds->next = NULL; 1401 *s = ds; 1402 } 1403 1404 DisplayState *get_displaystate(void) 1405 { 1406 if (!display_state) { 1407 dumb_display_init (); 1408 } 1409 return display_state; 1410 } 1411 1412 DisplayState *graphic_console_init(vga_hw_update_ptr update, 1413 vga_hw_invalidate_ptr invalidate, 1414 vga_hw_screen_dump_ptr screen_dump, 1415 vga_hw_text_update_ptr text_update, 1416 void *opaque) 1417 { 1418 QemuConsole *s; 1419 DisplayState *ds; 1420 1421 ds = (DisplayState *) g_malloc0(sizeof(DisplayState)); 1422 ds->surface = qemu_create_displaysurface(ds, 640, 480); 1423 1424 s = new_console(ds, GRAPHIC_CONSOLE); 1425 if (s == NULL) { 1426 qemu_free_displaysurface(ds); 1427 g_free(ds); 1428 return NULL; 1429 } 1430 s->hw_update = update; 1431 s->hw_invalidate = invalidate; 1432 s->hw_screen_dump = screen_dump; 1433 s->hw_text_update = text_update; 1434 s->hw = opaque; 1435 1436 register_displaystate(ds); 1437 return ds; 1438 } 1439 1440 int is_graphic_console(void) 1441 { 1442 return active_console && active_console->console_type == GRAPHIC_CONSOLE; 1443 } 1444 1445 int is_fixedsize_console(void) 1446 { 1447 return active_console && active_console->console_type != TEXT_CONSOLE; 1448 } 1449 1450 void console_color_init(DisplayState *ds) 1451 { 1452 int i, j; 1453 for (j = 0; j < 2; j++) { 1454 for (i = 0; i < 8; i++) { 1455 color_table[j][i] = col_expand(ds, 1456 vga_get_color(ds, color_table_rgb[j][i])); 1457 } 1458 } 1459 } 1460 1461 static void text_console_set_echo(CharDriverState *chr, bool echo) 1462 { 1463 QemuConsole *s = chr->opaque; 1464 1465 s->echo = echo; 1466 } 1467 1468 static void text_console_update_cursor(void *opaque) 1469 { 1470 QemuConsole *s = opaque; 1471 1472 s->cursor_visible_phase = !s->cursor_visible_phase; 1473 vga_hw_invalidate(); 1474 qemu_mod_timer(s->cursor_timer, 1475 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2); 1476 } 1477 1478 static void text_console_do_init(CharDriverState *chr, DisplayState *ds) 1479 { 1480 QemuConsole *s; 1481 static int color_inited; 1482 1483 s = chr->opaque; 1484 1485 chr->chr_write = console_puts; 1486 1487 s->out_fifo.buf = s->out_fifo_buf; 1488 s->out_fifo.buf_size = sizeof(s->out_fifo_buf); 1489 s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s); 1490 s->ds = ds; 1491 1492 if (!color_inited) { 1493 color_inited = 1; 1494 console_color_init(s->ds); 1495 } 1496 s->y_displayed = 0; 1497 s->y_base = 0; 1498 s->total_height = DEFAULT_BACKSCROLL; 1499 s->x = 0; 1500 s->y = 0; 1501 if (s->console_type == TEXT_CONSOLE) { 1502 s->g_width = ds_get_width(s->ds); 1503 s->g_height = ds_get_height(s->ds); 1504 } 1505 1506 s->cursor_timer = 1507 qemu_new_timer_ms(rt_clock, text_console_update_cursor, s); 1508 1509 s->hw_invalidate = text_console_invalidate; 1510 s->hw_text_update = text_console_update; 1511 s->hw = s; 1512 1513 /* Set text attribute defaults */ 1514 s->t_attrib_default.bold = 0; 1515 s->t_attrib_default.uline = 0; 1516 s->t_attrib_default.blink = 0; 1517 s->t_attrib_default.invers = 0; 1518 s->t_attrib_default.unvisible = 0; 1519 s->t_attrib_default.fgcol = COLOR_WHITE; 1520 s->t_attrib_default.bgcol = COLOR_BLACK; 1521 /* set current text attributes to default */ 1522 s->t_attrib = s->t_attrib_default; 1523 text_console_resize(s); 1524 1525 if (chr->label) { 1526 char msg[128]; 1527 int len; 1528 1529 s->t_attrib.bgcol = COLOR_BLUE; 1530 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label); 1531 console_puts(chr, (uint8_t*)msg, len); 1532 s->t_attrib = s->t_attrib_default; 1533 } 1534 1535 qemu_chr_generic_open(chr); 1536 if (chr->init) 1537 chr->init(chr); 1538 } 1539 1540 static CharDriverState *text_console_init(QemuOpts *opts) 1541 { 1542 CharDriverState *chr; 1543 QemuConsole *s; 1544 unsigned width; 1545 unsigned height; 1546 1547 chr = g_malloc0(sizeof(CharDriverState)); 1548 1549 width = qemu_opt_get_number(opts, "width", 0); 1550 if (width == 0) 1551 width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH; 1552 1553 height = qemu_opt_get_number(opts, "height", 0); 1554 if (height == 0) 1555 height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT; 1556 1557 if (width == 0 || height == 0) { 1558 s = new_console(NULL, TEXT_CONSOLE); 1559 } else { 1560 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE); 1561 } 1562 1563 if (!s) { 1564 g_free(chr); 1565 return NULL; 1566 } 1567 1568 s->chr = chr; 1569 s->g_width = width; 1570 s->g_height = height; 1571 chr->opaque = s; 1572 chr->chr_set_echo = text_console_set_echo; 1573 return chr; 1574 } 1575 1576 static VcHandler *vc_handler = text_console_init; 1577 1578 CharDriverState *vc_init(QemuOpts *opts) 1579 { 1580 return vc_handler(opts); 1581 } 1582 1583 void register_vc_handler(VcHandler *handler) 1584 { 1585 vc_handler = handler; 1586 } 1587 1588 void text_consoles_set_display(DisplayState *ds) 1589 { 1590 int i; 1591 1592 for (i = 0; i < nb_consoles; i++) { 1593 if (consoles[i]->console_type != GRAPHIC_CONSOLE) { 1594 text_console_do_init(consoles[i]->chr, ds); 1595 } 1596 } 1597 } 1598 1599 void qemu_console_resize(DisplayState *ds, int width, int height) 1600 { 1601 QemuConsole *s = get_graphic_console(ds); 1602 if (!s) return; 1603 1604 s->g_width = width; 1605 s->g_height = height; 1606 if (is_graphic_console()) { 1607 ds->surface = qemu_resize_displaysurface(ds, width, height); 1608 dpy_gfx_resize(ds); 1609 } 1610 } 1611 1612 void qemu_console_copy(DisplayState *ds, int src_x, int src_y, 1613 int dst_x, int dst_y, int w, int h) 1614 { 1615 if (is_graphic_console()) { 1616 dpy_gfx_copy(ds, src_x, src_y, dst_x, dst_y, w, h); 1617 } 1618 } 1619 1620 PixelFormat qemu_different_endianness_pixelformat(int bpp) 1621 { 1622 PixelFormat pf; 1623 1624 memset(&pf, 0x00, sizeof(PixelFormat)); 1625 1626 pf.bits_per_pixel = bpp; 1627 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8); 1628 pf.depth = bpp == 32 ? 24 : bpp; 1629 1630 switch (bpp) { 1631 case 24: 1632 pf.rmask = 0x000000FF; 1633 pf.gmask = 0x0000FF00; 1634 pf.bmask = 0x00FF0000; 1635 pf.rmax = 255; 1636 pf.gmax = 255; 1637 pf.bmax = 255; 1638 pf.rshift = 0; 1639 pf.gshift = 8; 1640 pf.bshift = 16; 1641 pf.rbits = 8; 1642 pf.gbits = 8; 1643 pf.bbits = 8; 1644 break; 1645 case 32: 1646 pf.rmask = 0x0000FF00; 1647 pf.gmask = 0x00FF0000; 1648 pf.bmask = 0xFF000000; 1649 pf.amask = 0x00000000; 1650 pf.amax = 255; 1651 pf.rmax = 255; 1652 pf.gmax = 255; 1653 pf.bmax = 255; 1654 pf.ashift = 0; 1655 pf.rshift = 8; 1656 pf.gshift = 16; 1657 pf.bshift = 24; 1658 pf.rbits = 8; 1659 pf.gbits = 8; 1660 pf.bbits = 8; 1661 pf.abits = 8; 1662 break; 1663 default: 1664 break; 1665 } 1666 return pf; 1667 } 1668 1669 PixelFormat qemu_default_pixelformat(int bpp) 1670 { 1671 PixelFormat pf; 1672 1673 memset(&pf, 0x00, sizeof(PixelFormat)); 1674 1675 pf.bits_per_pixel = bpp; 1676 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8); 1677 pf.depth = bpp == 32 ? 24 : bpp; 1678 1679 switch (bpp) { 1680 case 15: 1681 pf.bits_per_pixel = 16; 1682 pf.rmask = 0x00007c00; 1683 pf.gmask = 0x000003E0; 1684 pf.bmask = 0x0000001F; 1685 pf.rmax = 31; 1686 pf.gmax = 31; 1687 pf.bmax = 31; 1688 pf.rshift = 10; 1689 pf.gshift = 5; 1690 pf.bshift = 0; 1691 pf.rbits = 5; 1692 pf.gbits = 5; 1693 pf.bbits = 5; 1694 break; 1695 case 16: 1696 pf.rmask = 0x0000F800; 1697 pf.gmask = 0x000007E0; 1698 pf.bmask = 0x0000001F; 1699 pf.rmax = 31; 1700 pf.gmax = 63; 1701 pf.bmax = 31; 1702 pf.rshift = 11; 1703 pf.gshift = 5; 1704 pf.bshift = 0; 1705 pf.rbits = 5; 1706 pf.gbits = 6; 1707 pf.bbits = 5; 1708 break; 1709 case 24: 1710 pf.rmask = 0x00FF0000; 1711 pf.gmask = 0x0000FF00; 1712 pf.bmask = 0x000000FF; 1713 pf.rmax = 255; 1714 pf.gmax = 255; 1715 pf.bmax = 255; 1716 pf.rshift = 16; 1717 pf.gshift = 8; 1718 pf.bshift = 0; 1719 pf.rbits = 8; 1720 pf.gbits = 8; 1721 pf.bbits = 8; 1722 break; 1723 case 32: 1724 pf.rmask = 0x00FF0000; 1725 pf.gmask = 0x0000FF00; 1726 pf.bmask = 0x000000FF; 1727 pf.rmax = 255; 1728 pf.gmax = 255; 1729 pf.bmax = 255; 1730 pf.rshift = 16; 1731 pf.gshift = 8; 1732 pf.bshift = 0; 1733 pf.rbits = 8; 1734 pf.gbits = 8; 1735 pf.bbits = 8; 1736 break; 1737 default: 1738 break; 1739 } 1740 return pf; 1741 } 1742 1743 static void register_types(void) 1744 { 1745 register_char_driver("vc", text_console_init); 1746 } 1747 1748 type_init(register_types); 1749