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