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