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