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