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