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, 1184 object_property_allow_set_link, 1185 OBJ_PROP_LINK_UNREF_ON_RELEASE, 1186 &local_err); 1187 object_property_add_uint32_ptr(obj, "head", 1188 &s->head, &local_err); 1189 1190 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && 1191 (console_type == GRAPHIC_CONSOLE))) { 1192 active_console = s; 1193 } 1194 s->ds = ds; 1195 s->console_type = console_type; 1196 if (console_type != GRAPHIC_CONSOLE) { 1197 s->index = nb_consoles; 1198 consoles[nb_consoles++] = s; 1199 } else { 1200 /* HACK: Put graphical consoles before text consoles. */ 1201 for (i = nb_consoles; i > 0; i--) { 1202 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE) 1203 break; 1204 consoles[i] = consoles[i - 1]; 1205 consoles[i]->index = i; 1206 } 1207 s->index = i; 1208 consoles[i] = s; 1209 nb_consoles++; 1210 } 1211 return s; 1212 } 1213 1214 static void qemu_alloc_display(DisplaySurface *surface, int width, int height, 1215 int linesize, PixelFormat pf, int newflags) 1216 { 1217 surface->pf = pf; 1218 1219 qemu_pixman_image_unref(surface->image); 1220 surface->image = NULL; 1221 1222 surface->format = qemu_pixman_get_format(&pf); 1223 assert(surface->format != 0); 1224 surface->image = pixman_image_create_bits(surface->format, 1225 width, height, 1226 NULL, linesize); 1227 assert(surface->image != NULL); 1228 1229 surface->flags = newflags | QEMU_ALLOCATED_FLAG; 1230 #ifdef HOST_WORDS_BIGENDIAN 1231 surface->flags |= QEMU_BIG_ENDIAN_FLAG; 1232 #endif 1233 } 1234 1235 DisplaySurface *qemu_create_displaysurface(int width, int height) 1236 { 1237 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1238 int linesize = width * 4; 1239 1240 trace_displaysurface_create(surface, width, height); 1241 qemu_alloc_display(surface, width, height, linesize, 1242 qemu_default_pixelformat(32), 0); 1243 return surface; 1244 } 1245 1246 DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp, 1247 int linesize, uint8_t *data, 1248 bool byteswap) 1249 { 1250 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1251 1252 trace_displaysurface_create_from(surface, width, height, bpp, byteswap); 1253 if (byteswap) { 1254 surface->pf = qemu_different_endianness_pixelformat(bpp); 1255 } else { 1256 surface->pf = qemu_default_pixelformat(bpp); 1257 } 1258 1259 surface->format = qemu_pixman_get_format(&surface->pf); 1260 assert(surface->format != 0); 1261 surface->image = pixman_image_create_bits(surface->format, 1262 width, height, 1263 (void *)data, linesize); 1264 assert(surface->image != NULL); 1265 1266 #ifdef HOST_WORDS_BIGENDIAN 1267 surface->flags = QEMU_BIG_ENDIAN_FLAG; 1268 #endif 1269 1270 return surface; 1271 } 1272 1273 static DisplaySurface *qemu_create_dummy_surface(void) 1274 { 1275 static const char msg[] = 1276 "This VM has no graphic display device."; 1277 DisplaySurface *surface = qemu_create_displaysurface(640, 480); 1278 pixman_color_t bg = color_table_rgb[0][COLOR_BLACK]; 1279 pixman_color_t fg = color_table_rgb[0][COLOR_WHITE]; 1280 pixman_image_t *glyph; 1281 int len, x, y, i; 1282 1283 len = strlen(msg); 1284 x = (640/FONT_WIDTH - len) / 2; 1285 y = (480/FONT_HEIGHT - 1) / 2; 1286 for (i = 0; i < len; i++) { 1287 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]); 1288 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg, 1289 x+i, y, FONT_WIDTH, FONT_HEIGHT); 1290 qemu_pixman_image_unref(glyph); 1291 } 1292 return surface; 1293 } 1294 1295 void qemu_free_displaysurface(DisplaySurface *surface) 1296 { 1297 if (surface == NULL) { 1298 return; 1299 } 1300 trace_displaysurface_free(surface); 1301 qemu_pixman_image_unref(surface->image); 1302 g_free(surface); 1303 } 1304 1305 void register_displaychangelistener(DisplayChangeListener *dcl) 1306 { 1307 static DisplaySurface *dummy; 1308 QemuConsole *con; 1309 1310 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); 1311 dcl->ds = get_alloc_displaystate(); 1312 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); 1313 gui_setup_refresh(dcl->ds); 1314 if (dcl->con) { 1315 dcl->con->dcls++; 1316 con = dcl->con; 1317 } else { 1318 con = active_console; 1319 } 1320 if (dcl->ops->dpy_gfx_switch) { 1321 if (con) { 1322 dcl->ops->dpy_gfx_switch(dcl, con->surface); 1323 } else { 1324 if (!dummy) { 1325 dummy = qemu_create_dummy_surface(); 1326 } 1327 dcl->ops->dpy_gfx_switch(dcl, dummy); 1328 } 1329 } 1330 } 1331 1332 void update_displaychangelistener(DisplayChangeListener *dcl, 1333 uint64_t interval) 1334 { 1335 DisplayState *ds = dcl->ds; 1336 1337 dcl->update_interval = interval; 1338 if (!ds->refreshing && ds->update_interval > interval) { 1339 timer_mod(ds->gui_timer, ds->last_update + interval); 1340 } 1341 } 1342 1343 void unregister_displaychangelistener(DisplayChangeListener *dcl) 1344 { 1345 DisplayState *ds = dcl->ds; 1346 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name); 1347 if (dcl->con) { 1348 dcl->con->dcls--; 1349 } 1350 QLIST_REMOVE(dcl, next); 1351 gui_setup_refresh(ds); 1352 } 1353 1354 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info) 1355 { 1356 assert(con != NULL); 1357 con->ui_info = *info; 1358 if (con->hw_ops->ui_info) { 1359 return con->hw_ops->ui_info(con->hw, con->head, info); 1360 } 1361 return -1; 1362 } 1363 1364 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 1365 { 1366 DisplayState *s = con->ds; 1367 DisplayChangeListener *dcl; 1368 int width = surface_width(con->surface); 1369 int height = surface_height(con->surface); 1370 1371 x = MAX(x, 0); 1372 y = MAX(y, 0); 1373 x = MIN(x, width); 1374 y = MIN(y, height); 1375 w = MIN(w, width - x); 1376 h = MIN(h, height - y); 1377 1378 if (!qemu_console_is_visible(con)) { 1379 return; 1380 } 1381 QLIST_FOREACH(dcl, &s->listeners, next) { 1382 if (con != (dcl->con ? dcl->con : active_console)) { 1383 continue; 1384 } 1385 if (dcl->ops->dpy_gfx_update) { 1386 dcl->ops->dpy_gfx_update(dcl, x, y, w, h); 1387 } 1388 } 1389 } 1390 1391 void dpy_gfx_replace_surface(QemuConsole *con, 1392 DisplaySurface *surface) 1393 { 1394 DisplayState *s = con->ds; 1395 DisplaySurface *old_surface = con->surface; 1396 DisplayChangeListener *dcl; 1397 1398 con->surface = surface; 1399 QLIST_FOREACH(dcl, &s->listeners, next) { 1400 if (con != (dcl->con ? dcl->con : active_console)) { 1401 continue; 1402 } 1403 if (dcl->ops->dpy_gfx_switch) { 1404 dcl->ops->dpy_gfx_switch(dcl, surface); 1405 } 1406 } 1407 qemu_free_displaysurface(old_surface); 1408 } 1409 1410 void dpy_refresh(DisplayState *s) 1411 { 1412 DisplayChangeListener *dcl; 1413 1414 QLIST_FOREACH(dcl, &s->listeners, next) { 1415 if (dcl->ops->dpy_refresh) { 1416 dcl->ops->dpy_refresh(dcl); 1417 } 1418 } 1419 } 1420 1421 void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y, 1422 int dst_x, int dst_y, int w, int h) 1423 { 1424 DisplayState *s = con->ds; 1425 DisplayChangeListener *dcl; 1426 1427 if (!qemu_console_is_visible(con)) { 1428 return; 1429 } 1430 QLIST_FOREACH(dcl, &s->listeners, next) { 1431 if (con != (dcl->con ? dcl->con : active_console)) { 1432 continue; 1433 } 1434 if (dcl->ops->dpy_gfx_copy) { 1435 dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h); 1436 } else { /* TODO */ 1437 dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h); 1438 } 1439 } 1440 } 1441 1442 void dpy_text_cursor(QemuConsole *con, int x, int y) 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_text_cursor) { 1455 dcl->ops->dpy_text_cursor(dcl, x, y); 1456 } 1457 } 1458 } 1459 1460 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) 1461 { 1462 DisplayState *s = con->ds; 1463 DisplayChangeListener *dcl; 1464 1465 if (!qemu_console_is_visible(con)) { 1466 return; 1467 } 1468 QLIST_FOREACH(dcl, &s->listeners, next) { 1469 if (con != (dcl->con ? dcl->con : active_console)) { 1470 continue; 1471 } 1472 if (dcl->ops->dpy_text_update) { 1473 dcl->ops->dpy_text_update(dcl, x, y, w, h); 1474 } 1475 } 1476 } 1477 1478 void dpy_text_resize(QemuConsole *con, int w, int h) 1479 { 1480 DisplayState *s = con->ds; 1481 struct DisplayChangeListener *dcl; 1482 1483 if (!qemu_console_is_visible(con)) { 1484 return; 1485 } 1486 QLIST_FOREACH(dcl, &s->listeners, next) { 1487 if (con != (dcl->con ? dcl->con : active_console)) { 1488 continue; 1489 } 1490 if (dcl->ops->dpy_text_resize) { 1491 dcl->ops->dpy_text_resize(dcl, w, h); 1492 } 1493 } 1494 } 1495 1496 void dpy_mouse_set(QemuConsole *con, int x, int y, int on) 1497 { 1498 DisplayState *s = con->ds; 1499 DisplayChangeListener *dcl; 1500 1501 if (!qemu_console_is_visible(con)) { 1502 return; 1503 } 1504 QLIST_FOREACH(dcl, &s->listeners, next) { 1505 if (con != (dcl->con ? dcl->con : active_console)) { 1506 continue; 1507 } 1508 if (dcl->ops->dpy_mouse_set) { 1509 dcl->ops->dpy_mouse_set(dcl, x, y, on); 1510 } 1511 } 1512 } 1513 1514 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) 1515 { 1516 DisplayState *s = con->ds; 1517 DisplayChangeListener *dcl; 1518 1519 if (!qemu_console_is_visible(con)) { 1520 return; 1521 } 1522 QLIST_FOREACH(dcl, &s->listeners, next) { 1523 if (con != (dcl->con ? dcl->con : active_console)) { 1524 continue; 1525 } 1526 if (dcl->ops->dpy_cursor_define) { 1527 dcl->ops->dpy_cursor_define(dcl, cursor); 1528 } 1529 } 1530 } 1531 1532 bool dpy_cursor_define_supported(QemuConsole *con) 1533 { 1534 DisplayState *s = con->ds; 1535 DisplayChangeListener *dcl; 1536 1537 QLIST_FOREACH(dcl, &s->listeners, next) { 1538 if (dcl->ops->dpy_cursor_define) { 1539 return true; 1540 } 1541 } 1542 return false; 1543 } 1544 1545 /***********************************************************/ 1546 /* register display */ 1547 1548 /* console.c internal use only */ 1549 static DisplayState *get_alloc_displaystate(void) 1550 { 1551 if (!display_state) { 1552 display_state = g_new0(DisplayState, 1); 1553 } 1554 return display_state; 1555 } 1556 1557 /* 1558 * Called by main(), after creating QemuConsoles 1559 * and before initializing ui (sdl/vnc/...). 1560 */ 1561 DisplayState *init_displaystate(void) 1562 { 1563 Error *local_err = NULL; 1564 gchar *name; 1565 int i; 1566 1567 if (!display_state) { 1568 display_state = g_new0(DisplayState, 1); 1569 } 1570 1571 for (i = 0; i < nb_consoles; i++) { 1572 if (consoles[i]->console_type != GRAPHIC_CONSOLE && 1573 consoles[i]->ds == NULL) { 1574 text_console_do_init(consoles[i]->chr, display_state); 1575 } 1576 1577 /* Hook up into the qom tree here (not in new_console()), once 1578 * all QemuConsoles are created and the order / numbering 1579 * doesn't change any more */ 1580 name = g_strdup_printf("console[%d]", i); 1581 object_property_add_child(container_get(object_get_root(), "/backend"), 1582 name, OBJECT(consoles[i]), &local_err); 1583 g_free(name); 1584 } 1585 1586 return display_state; 1587 } 1588 1589 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 1590 const GraphicHwOps *hw_ops, 1591 void *opaque) 1592 { 1593 Error *local_err = NULL; 1594 int width = 640; 1595 int height = 480; 1596 QemuConsole *s; 1597 DisplayState *ds; 1598 1599 ds = get_alloc_displaystate(); 1600 trace_console_gfx_new(); 1601 s = new_console(ds, GRAPHIC_CONSOLE); 1602 s->hw_ops = hw_ops; 1603 s->hw = opaque; 1604 if (dev) { 1605 object_property_set_link(OBJECT(s), OBJECT(dev), 1606 "device", &local_err); 1607 object_property_set_int(OBJECT(s), head, 1608 "head", &local_err); 1609 } 1610 1611 s->surface = qemu_create_displaysurface(width, height); 1612 return s; 1613 } 1614 1615 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 1616 { 1617 if (index >= MAX_CONSOLES) { 1618 return NULL; 1619 } 1620 return consoles[index]; 1621 } 1622 1623 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 1624 { 1625 Error *local_err = NULL; 1626 Object *obj; 1627 uint32_t h; 1628 int i; 1629 1630 for (i = 0; i < nb_consoles; i++) { 1631 if (!consoles[i]) { 1632 continue; 1633 } 1634 obj = object_property_get_link(OBJECT(consoles[i]), 1635 "device", &local_err); 1636 if (DEVICE(obj) != dev) { 1637 continue; 1638 } 1639 h = object_property_get_int(OBJECT(consoles[i]), 1640 "head", &local_err); 1641 if (h != head) { 1642 continue; 1643 } 1644 return consoles[i]; 1645 } 1646 return NULL; 1647 } 1648 1649 bool qemu_console_is_visible(QemuConsole *con) 1650 { 1651 return (con == active_console) || (con->dcls > 0); 1652 } 1653 1654 bool qemu_console_is_graphic(QemuConsole *con) 1655 { 1656 if (con == NULL) { 1657 con = active_console; 1658 } 1659 return con && (con->console_type == GRAPHIC_CONSOLE); 1660 } 1661 1662 bool qemu_console_is_fixedsize(QemuConsole *con) 1663 { 1664 if (con == NULL) { 1665 con = active_console; 1666 } 1667 return con && (con->console_type != TEXT_CONSOLE); 1668 } 1669 1670 int qemu_console_get_index(QemuConsole *con) 1671 { 1672 if (con == NULL) { 1673 con = active_console; 1674 } 1675 return con ? con->index : -1; 1676 } 1677 1678 uint32_t qemu_console_get_head(QemuConsole *con) 1679 { 1680 if (con == NULL) { 1681 con = active_console; 1682 } 1683 return con ? con->head : -1; 1684 } 1685 1686 QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con) 1687 { 1688 assert(con != NULL); 1689 return &con->ui_info; 1690 } 1691 1692 int qemu_console_get_width(QemuConsole *con, int fallback) 1693 { 1694 if (con == NULL) { 1695 con = active_console; 1696 } 1697 return con ? surface_width(con->surface) : fallback; 1698 } 1699 1700 int qemu_console_get_height(QemuConsole *con, int fallback) 1701 { 1702 if (con == NULL) { 1703 con = active_console; 1704 } 1705 return con ? surface_height(con->surface) : fallback; 1706 } 1707 1708 static void text_console_set_echo(CharDriverState *chr, bool echo) 1709 { 1710 QemuConsole *s = chr->opaque; 1711 1712 s->echo = echo; 1713 } 1714 1715 static void text_console_update_cursor(void *opaque) 1716 { 1717 QemuConsole *s = opaque; 1718 1719 s->cursor_visible_phase = !s->cursor_visible_phase; 1720 graphic_hw_invalidate(s); 1721 timer_mod(s->cursor_timer, 1722 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2); 1723 } 1724 1725 static const GraphicHwOps text_console_ops = { 1726 .invalidate = text_console_invalidate, 1727 .text_update = text_console_update, 1728 }; 1729 1730 static void text_console_do_init(CharDriverState *chr, DisplayState *ds) 1731 { 1732 QemuConsole *s; 1733 int g_width = 80 * FONT_WIDTH; 1734 int g_height = 24 * FONT_HEIGHT; 1735 1736 s = chr->opaque; 1737 1738 chr->chr_write = console_puts; 1739 1740 s->out_fifo.buf = s->out_fifo_buf; 1741 s->out_fifo.buf_size = sizeof(s->out_fifo_buf); 1742 s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s); 1743 s->ds = ds; 1744 1745 s->y_displayed = 0; 1746 s->y_base = 0; 1747 s->total_height = DEFAULT_BACKSCROLL; 1748 s->x = 0; 1749 s->y = 0; 1750 if (!s->surface) { 1751 if (active_console && active_console->surface) { 1752 g_width = surface_width(active_console->surface); 1753 g_height = surface_height(active_console->surface); 1754 } 1755 s->surface = qemu_create_displaysurface(g_width, g_height); 1756 } 1757 1758 s->cursor_timer = 1759 timer_new_ms(QEMU_CLOCK_REALTIME, text_console_update_cursor, s); 1760 1761 s->hw_ops = &text_console_ops; 1762 s->hw = s; 1763 1764 /* Set text attribute defaults */ 1765 s->t_attrib_default.bold = 0; 1766 s->t_attrib_default.uline = 0; 1767 s->t_attrib_default.blink = 0; 1768 s->t_attrib_default.invers = 0; 1769 s->t_attrib_default.unvisible = 0; 1770 s->t_attrib_default.fgcol = COLOR_WHITE; 1771 s->t_attrib_default.bgcol = COLOR_BLACK; 1772 /* set current text attributes to default */ 1773 s->t_attrib = s->t_attrib_default; 1774 text_console_resize(s); 1775 1776 if (chr->label) { 1777 char msg[128]; 1778 int len; 1779 1780 s->t_attrib.bgcol = COLOR_BLUE; 1781 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label); 1782 console_puts(chr, (uint8_t*)msg, len); 1783 s->t_attrib = s->t_attrib_default; 1784 } 1785 1786 qemu_chr_be_generic_open(chr); 1787 if (chr->init) 1788 chr->init(chr); 1789 } 1790 1791 static CharDriverState *text_console_init(ChardevVC *vc) 1792 { 1793 CharDriverState *chr; 1794 QemuConsole *s; 1795 unsigned width = 0; 1796 unsigned height = 0; 1797 1798 chr = g_malloc0(sizeof(CharDriverState)); 1799 1800 if (vc->has_width) { 1801 width = vc->width; 1802 } else if (vc->has_cols) { 1803 width = vc->cols * FONT_WIDTH; 1804 } 1805 1806 if (vc->has_height) { 1807 height = vc->height; 1808 } else if (vc->has_rows) { 1809 height = vc->rows * FONT_HEIGHT; 1810 } 1811 1812 trace_console_txt_new(width, height); 1813 if (width == 0 || height == 0) { 1814 s = new_console(NULL, TEXT_CONSOLE); 1815 } else { 1816 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE); 1817 s->surface = qemu_create_displaysurface(width, height); 1818 } 1819 1820 if (!s) { 1821 g_free(chr); 1822 return NULL; 1823 } 1824 1825 s->chr = chr; 1826 chr->opaque = s; 1827 chr->chr_set_echo = text_console_set_echo; 1828 /* console/chardev init sometimes completes elsewhere in a 2nd 1829 * stage, so defer OPENED events until they are fully initialized 1830 */ 1831 chr->explicit_be_open = true; 1832 1833 if (display_state) { 1834 text_console_do_init(chr, display_state); 1835 } 1836 return chr; 1837 } 1838 1839 static VcHandler *vc_handler = text_console_init; 1840 1841 CharDriverState *vc_init(ChardevVC *vc) 1842 { 1843 return vc_handler(vc); 1844 } 1845 1846 void register_vc_handler(VcHandler *handler) 1847 { 1848 vc_handler = handler; 1849 } 1850 1851 void qemu_console_resize(QemuConsole *s, int width, int height) 1852 { 1853 DisplaySurface *surface; 1854 1855 assert(s->console_type == GRAPHIC_CONSOLE); 1856 surface = qemu_create_displaysurface(width, height); 1857 dpy_gfx_replace_surface(s, surface); 1858 } 1859 1860 void qemu_console_copy(QemuConsole *con, int src_x, int src_y, 1861 int dst_x, int dst_y, int w, int h) 1862 { 1863 assert(con->console_type == GRAPHIC_CONSOLE); 1864 dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h); 1865 } 1866 1867 DisplaySurface *qemu_console_surface(QemuConsole *console) 1868 { 1869 return console->surface; 1870 } 1871 1872 DisplayState *qemu_console_displaystate(QemuConsole *console) 1873 { 1874 return console->ds; 1875 } 1876 1877 PixelFormat qemu_different_endianness_pixelformat(int bpp) 1878 { 1879 PixelFormat pf; 1880 1881 memset(&pf, 0x00, sizeof(PixelFormat)); 1882 1883 pf.bits_per_pixel = bpp; 1884 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8); 1885 pf.depth = bpp == 32 ? 24 : bpp; 1886 1887 switch (bpp) { 1888 case 24: 1889 pf.rmask = 0x000000FF; 1890 pf.gmask = 0x0000FF00; 1891 pf.bmask = 0x00FF0000; 1892 pf.rmax = 255; 1893 pf.gmax = 255; 1894 pf.bmax = 255; 1895 pf.rshift = 0; 1896 pf.gshift = 8; 1897 pf.bshift = 16; 1898 pf.rbits = 8; 1899 pf.gbits = 8; 1900 pf.bbits = 8; 1901 break; 1902 case 32: 1903 pf.rmask = 0x0000FF00; 1904 pf.gmask = 0x00FF0000; 1905 pf.bmask = 0xFF000000; 1906 pf.amask = 0x00000000; 1907 pf.amax = 255; 1908 pf.rmax = 255; 1909 pf.gmax = 255; 1910 pf.bmax = 255; 1911 pf.ashift = 0; 1912 pf.rshift = 8; 1913 pf.gshift = 16; 1914 pf.bshift = 24; 1915 pf.rbits = 8; 1916 pf.gbits = 8; 1917 pf.bbits = 8; 1918 pf.abits = 8; 1919 break; 1920 default: 1921 break; 1922 } 1923 return pf; 1924 } 1925 1926 PixelFormat qemu_default_pixelformat(int bpp) 1927 { 1928 PixelFormat pf; 1929 1930 memset(&pf, 0x00, sizeof(PixelFormat)); 1931 1932 pf.bits_per_pixel = bpp; 1933 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8); 1934 pf.depth = bpp == 32 ? 24 : bpp; 1935 1936 switch (bpp) { 1937 case 15: 1938 pf.bits_per_pixel = 16; 1939 pf.rmask = 0x00007c00; 1940 pf.gmask = 0x000003E0; 1941 pf.bmask = 0x0000001F; 1942 pf.rmax = 31; 1943 pf.gmax = 31; 1944 pf.bmax = 31; 1945 pf.rshift = 10; 1946 pf.gshift = 5; 1947 pf.bshift = 0; 1948 pf.rbits = 5; 1949 pf.gbits = 5; 1950 pf.bbits = 5; 1951 break; 1952 case 16: 1953 pf.rmask = 0x0000F800; 1954 pf.gmask = 0x000007E0; 1955 pf.bmask = 0x0000001F; 1956 pf.rmax = 31; 1957 pf.gmax = 63; 1958 pf.bmax = 31; 1959 pf.rshift = 11; 1960 pf.gshift = 5; 1961 pf.bshift = 0; 1962 pf.rbits = 5; 1963 pf.gbits = 6; 1964 pf.bbits = 5; 1965 break; 1966 case 24: 1967 pf.rmask = 0x00FF0000; 1968 pf.gmask = 0x0000FF00; 1969 pf.bmask = 0x000000FF; 1970 pf.rmax = 255; 1971 pf.gmax = 255; 1972 pf.bmax = 255; 1973 pf.rshift = 16; 1974 pf.gshift = 8; 1975 pf.bshift = 0; 1976 pf.rbits = 8; 1977 pf.gbits = 8; 1978 pf.bbits = 8; 1979 break; 1980 case 32: 1981 pf.rmask = 0x00FF0000; 1982 pf.gmask = 0x0000FF00; 1983 pf.bmask = 0x000000FF; 1984 pf.rmax = 255; 1985 pf.gmax = 255; 1986 pf.bmax = 255; 1987 pf.rshift = 16; 1988 pf.gshift = 8; 1989 pf.bshift = 0; 1990 pf.rbits = 8; 1991 pf.gbits = 8; 1992 pf.bbits = 8; 1993 break; 1994 default: 1995 break; 1996 } 1997 return pf; 1998 } 1999 2000 static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, 2001 Error **errp) 2002 { 2003 int val; 2004 2005 backend->vc = g_new0(ChardevVC, 1); 2006 2007 val = qemu_opt_get_number(opts, "width", 0); 2008 if (val != 0) { 2009 backend->vc->has_width = true; 2010 backend->vc->width = val; 2011 } 2012 2013 val = qemu_opt_get_number(opts, "height", 0); 2014 if (val != 0) { 2015 backend->vc->has_height = true; 2016 backend->vc->height = val; 2017 } 2018 2019 val = qemu_opt_get_number(opts, "cols", 0); 2020 if (val != 0) { 2021 backend->vc->has_cols = true; 2022 backend->vc->cols = val; 2023 } 2024 2025 val = qemu_opt_get_number(opts, "rows", 0); 2026 if (val != 0) { 2027 backend->vc->has_rows = true; 2028 backend->vc->rows = val; 2029 } 2030 } 2031 2032 static const TypeInfo qemu_console_info = { 2033 .name = TYPE_QEMU_CONSOLE, 2034 .parent = TYPE_OBJECT, 2035 .instance_size = sizeof(QemuConsole), 2036 .class_size = sizeof(QemuConsoleClass), 2037 }; 2038 2039 2040 static void register_types(void) 2041 { 2042 type_register_static(&qemu_console_info); 2043 register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC, 2044 qemu_chr_parse_vc); 2045 } 2046 2047 type_init(register_types); 2048