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