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