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 25 #include "qemu/osdep.h" 26 #include "ui/console.h" 27 #include "hw/qdev-core.h" 28 #include "qapi/error.h" 29 #include "qapi/qapi-commands-ui.h" 30 #include "qemu/coroutine.h" 31 #include "qemu/error-report.h" 32 #include "qemu/fifo8.h" 33 #include "qemu/main-loop.h" 34 #include "qemu/module.h" 35 #include "qemu/option.h" 36 #include "qemu/timer.h" 37 #include "chardev/char.h" 38 #include "trace.h" 39 #include "exec/memory.h" 40 #include "io/channel-file.h" 41 #include "qom/object.h" 42 #ifdef CONFIG_PNG 43 #include <png.h> 44 #endif 45 46 #define DEFAULT_BACKSCROLL 512 47 #define CONSOLE_CURSOR_PERIOD 500 48 49 typedef struct TextAttributes { 50 uint8_t fgcol:4; 51 uint8_t bgcol:4; 52 uint8_t bold:1; 53 uint8_t uline:1; 54 uint8_t blink:1; 55 uint8_t invers:1; 56 uint8_t unvisible:1; 57 } TextAttributes; 58 59 typedef struct TextCell { 60 uint8_t ch; 61 TextAttributes t_attrib; 62 } TextCell; 63 64 #define MAX_ESC_PARAMS 3 65 66 enum TTYState { 67 TTY_STATE_NORM, 68 TTY_STATE_ESC, 69 TTY_STATE_CSI, 70 }; 71 72 typedef enum { 73 GRAPHIC_CONSOLE, 74 TEXT_CONSOLE, 75 TEXT_CONSOLE_FIXED_SIZE 76 } console_type_t; 77 78 struct QemuConsole { 79 Object parent; 80 81 int index; 82 console_type_t console_type; 83 DisplayState *ds; 84 DisplaySurface *surface; 85 DisplayScanout scanout; 86 int dcls; 87 DisplayGLCtx *gl; 88 int gl_block; 89 QEMUTimer *gl_unblock_timer; 90 int window_id; 91 92 /* Graphic console state. */ 93 Object *device; 94 uint32_t head; 95 QemuUIInfo ui_info; 96 QEMUTimer *ui_timer; 97 const GraphicHwOps *hw_ops; 98 void *hw; 99 100 /* Text console state */ 101 int width; 102 int height; 103 int total_height; 104 int backscroll_height; 105 int x, y; 106 int x_saved, y_saved; 107 int y_displayed; 108 int y_base; 109 TextAttributes t_attrib_default; /* default text attributes */ 110 TextAttributes t_attrib; /* currently active text attributes */ 111 TextCell *cells; 112 int text_x[2], text_y[2], cursor_invalidate; 113 int echo; 114 115 int update_x0; 116 int update_y0; 117 int update_x1; 118 int update_y1; 119 120 enum TTYState state; 121 int esc_params[MAX_ESC_PARAMS]; 122 int nb_esc_params; 123 124 Chardev *chr; 125 /* fifo for key pressed */ 126 Fifo8 out_fifo; 127 CoQueue dump_queue; 128 129 QTAILQ_ENTRY(QemuConsole) next; 130 }; 131 132 struct DisplayState { 133 QEMUTimer *gui_timer; 134 uint64_t last_update; 135 uint64_t update_interval; 136 bool refreshing; 137 bool have_gfx; 138 bool have_text; 139 140 QLIST_HEAD(, DisplayChangeListener) listeners; 141 }; 142 143 static DisplayState *display_state; 144 static QemuConsole *active_console; 145 static QTAILQ_HEAD(, QemuConsole) consoles = 146 QTAILQ_HEAD_INITIALIZER(consoles); 147 static bool cursor_visible_phase; 148 static QEMUTimer *cursor_timer; 149 150 static void text_console_do_init(Chardev *chr, DisplayState *ds); 151 static void dpy_refresh(DisplayState *s); 152 static DisplayState *get_alloc_displaystate(void); 153 static void text_console_update_cursor_timer(void); 154 static void text_console_update_cursor(void *opaque); 155 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); 156 static bool console_compatible_with(QemuConsole *con, 157 DisplayChangeListener *dcl, Error **errp); 158 159 static void gui_update(void *opaque) 160 { 161 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE; 162 uint64_t dcl_interval; 163 DisplayState *ds = opaque; 164 DisplayChangeListener *dcl; 165 166 ds->refreshing = true; 167 dpy_refresh(ds); 168 ds->refreshing = false; 169 170 QLIST_FOREACH(dcl, &ds->listeners, next) { 171 dcl_interval = dcl->update_interval ? 172 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT; 173 if (interval > dcl_interval) { 174 interval = dcl_interval; 175 } 176 } 177 if (ds->update_interval != interval) { 178 ds->update_interval = interval; 179 trace_console_refresh(interval); 180 } 181 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 182 timer_mod(ds->gui_timer, ds->last_update + interval); 183 } 184 185 static void gui_setup_refresh(DisplayState *ds) 186 { 187 DisplayChangeListener *dcl; 188 bool need_timer = false; 189 bool have_gfx = false; 190 bool have_text = false; 191 192 QLIST_FOREACH(dcl, &ds->listeners, next) { 193 if (dcl->ops->dpy_refresh != NULL) { 194 need_timer = true; 195 } 196 if (dcl->ops->dpy_gfx_update != NULL) { 197 have_gfx = true; 198 } 199 if (dcl->ops->dpy_text_update != NULL) { 200 have_text = true; 201 } 202 } 203 204 if (need_timer && ds->gui_timer == NULL) { 205 ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds); 206 timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); 207 } 208 if (!need_timer && ds->gui_timer != NULL) { 209 timer_free(ds->gui_timer); 210 ds->gui_timer = NULL; 211 } 212 213 ds->have_gfx = have_gfx; 214 ds->have_text = have_text; 215 } 216 217 void graphic_hw_update_done(QemuConsole *con) 218 { 219 if (con) { 220 qemu_co_enter_all(&con->dump_queue, NULL); 221 } 222 } 223 224 void graphic_hw_update(QemuConsole *con) 225 { 226 bool async = false; 227 con = con ? con : active_console; 228 if (!con) { 229 return; 230 } 231 if (con->hw_ops->gfx_update) { 232 con->hw_ops->gfx_update(con->hw); 233 async = con->hw_ops->gfx_update_async; 234 } 235 if (!async) { 236 graphic_hw_update_done(con); 237 } 238 } 239 240 static void graphic_hw_gl_unblock_timer(void *opaque) 241 { 242 warn_report("console: no gl-unblock within one second"); 243 } 244 245 void graphic_hw_gl_block(QemuConsole *con, bool block) 246 { 247 uint64_t timeout; 248 assert(con != NULL); 249 250 if (block) { 251 con->gl_block++; 252 } else { 253 con->gl_block--; 254 } 255 assert(con->gl_block >= 0); 256 if (!con->hw_ops->gl_block) { 257 return; 258 } 259 if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) { 260 return; 261 } 262 con->hw_ops->gl_block(con->hw, block); 263 264 if (block) { 265 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 266 timeout += 1000; /* one sec */ 267 timer_mod(con->gl_unblock_timer, timeout); 268 } else { 269 timer_del(con->gl_unblock_timer); 270 } 271 } 272 273 int qemu_console_get_window_id(QemuConsole *con) 274 { 275 return con->window_id; 276 } 277 278 void qemu_console_set_window_id(QemuConsole *con, int window_id) 279 { 280 con->window_id = window_id; 281 } 282 283 void graphic_hw_invalidate(QemuConsole *con) 284 { 285 if (!con) { 286 con = active_console; 287 } 288 if (con && con->hw_ops->invalidate) { 289 con->hw_ops->invalidate(con->hw); 290 } 291 } 292 293 #ifdef CONFIG_PNG 294 /** 295 * png_save: Take a screenshot as PNG 296 * 297 * Saves screendump as a PNG file 298 * 299 * Returns true for success or false for error. 300 * 301 * @fd: File descriptor for PNG file. 302 * @image: Image data in pixman format. 303 * @errp: Pointer to an error. 304 */ 305 static bool png_save(int fd, pixman_image_t *image, Error **errp) 306 { 307 int width = pixman_image_get_width(image); 308 int height = pixman_image_get_height(image); 309 png_struct *png_ptr; 310 png_info *info_ptr; 311 g_autoptr(pixman_image_t) linebuf = 312 qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, width); 313 uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); 314 FILE *f = fdopen(fd, "wb"); 315 int y; 316 if (!f) { 317 error_setg_errno(errp, errno, 318 "Failed to create file from file descriptor"); 319 return false; 320 } 321 322 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 323 NULL, NULL); 324 if (!png_ptr) { 325 error_setg(errp, "PNG creation failed. Unable to write struct"); 326 fclose(f); 327 return false; 328 } 329 330 info_ptr = png_create_info_struct(png_ptr); 331 332 if (!info_ptr) { 333 error_setg(errp, "PNG creation failed. Unable to write info"); 334 fclose(f); 335 png_destroy_write_struct(&png_ptr, &info_ptr); 336 return false; 337 } 338 339 png_init_io(png_ptr, f); 340 341 png_set_IHDR(png_ptr, info_ptr, width, height, 8, 342 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, 343 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 344 345 png_write_info(png_ptr, info_ptr); 346 347 for (y = 0; y < height; ++y) { 348 qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); 349 png_write_row(png_ptr, buf); 350 } 351 352 png_write_end(png_ptr, NULL); 353 354 png_destroy_write_struct(&png_ptr, &info_ptr); 355 356 if (fclose(f) != 0) { 357 error_setg_errno(errp, errno, 358 "PNG creation failed. Unable to close file"); 359 return false; 360 } 361 362 return true; 363 } 364 365 #else /* no png support */ 366 367 static bool png_save(int fd, pixman_image_t *image, Error **errp) 368 { 369 error_setg(errp, "Enable PNG support with libpng for screendump"); 370 return false; 371 } 372 373 #endif /* CONFIG_PNG */ 374 375 static bool ppm_save(int fd, pixman_image_t *image, Error **errp) 376 { 377 int width = pixman_image_get_width(image); 378 int height = pixman_image_get_height(image); 379 g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); 380 g_autofree char *header = NULL; 381 g_autoptr(pixman_image_t) linebuf = NULL; 382 int y; 383 384 trace_ppm_save(fd, image); 385 386 header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); 387 if (qio_channel_write_all(QIO_CHANNEL(ioc), 388 header, strlen(header), errp) < 0) { 389 return false; 390 } 391 392 linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); 393 for (y = 0; y < height; y++) { 394 qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); 395 if (qio_channel_write_all(QIO_CHANNEL(ioc), 396 (char *)pixman_image_get_data(linebuf), 397 pixman_image_get_stride(linebuf), errp) < 0) { 398 return false; 399 } 400 } 401 402 return true; 403 } 404 405 static void graphic_hw_update_bh(void *con) 406 { 407 graphic_hw_update(con); 408 } 409 410 /* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ 411 void coroutine_fn 412 qmp_screendump(const char *filename, const char *device, 413 bool has_head, int64_t head, 414 bool has_format, ImageFormat format, Error **errp) 415 { 416 g_autoptr(pixman_image_t) image = NULL; 417 QemuConsole *con; 418 DisplaySurface *surface; 419 int fd; 420 421 if (device) { 422 con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, 423 errp); 424 if (!con) { 425 return; 426 } 427 } else { 428 if (has_head) { 429 error_setg(errp, "'head' must be specified together with 'device'"); 430 return; 431 } 432 con = qemu_console_lookup_by_index(0); 433 if (!con) { 434 error_setg(errp, "There is no console to take a screendump from"); 435 return; 436 } 437 } 438 439 if (qemu_co_queue_empty(&con->dump_queue)) { 440 /* Defer the update, it will restart the pending coroutines */ 441 aio_bh_schedule_oneshot(qemu_get_aio_context(), 442 graphic_hw_update_bh, con); 443 } 444 qemu_co_queue_wait(&con->dump_queue, NULL); 445 446 /* 447 * All pending coroutines are woken up, while the BQL is held. No 448 * further graphic update are possible until it is released. Take 449 * an image ref before that. 450 */ 451 surface = qemu_console_surface(con); 452 if (!surface) { 453 error_setg(errp, "no surface"); 454 return; 455 } 456 image = pixman_image_ref(surface->image); 457 458 fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); 459 if (fd == -1) { 460 error_setg(errp, "failed to open file '%s': %s", filename, 461 strerror(errno)); 462 return; 463 } 464 465 /* 466 * The image content could potentially be updated as the coroutine 467 * yields and releases the BQL. It could produce corrupted dump, but 468 * it should be otherwise safe. 469 */ 470 if (has_format && format == IMAGE_FORMAT_PNG) { 471 /* PNG format specified for screendump */ 472 if (!png_save(fd, image, errp)) { 473 qemu_unlink(filename); 474 } 475 } else { 476 /* PPM format specified/default for screendump */ 477 if (!ppm_save(fd, image, errp)) { 478 qemu_unlink(filename); 479 } 480 } 481 } 482 483 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) 484 { 485 if (!con) { 486 con = active_console; 487 } 488 if (con && con->hw_ops->text_update) { 489 con->hw_ops->text_update(con->hw, chardata); 490 } 491 } 492 493 static void vga_fill_rect(QemuConsole *con, 494 int posx, int posy, int width, int height, 495 pixman_color_t color) 496 { 497 DisplaySurface *surface = qemu_console_surface(con); 498 pixman_rectangle16_t rect = { 499 .x = posx, .y = posy, .width = width, .height = height 500 }; 501 502 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image, 503 &color, 1, &rect); 504 } 505 506 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ 507 static void vga_bitblt(QemuConsole *con, 508 int xs, int ys, int xd, int yd, int w, int h) 509 { 510 DisplaySurface *surface = qemu_console_surface(con); 511 512 pixman_image_composite(PIXMAN_OP_SRC, 513 surface->image, NULL, surface->image, 514 xs, ys, 0, 0, xd, yd, w, h); 515 } 516 517 /***********************************************************/ 518 /* basic char display */ 519 520 #define FONT_HEIGHT 16 521 #define FONT_WIDTH 8 522 523 #include "vgafont.h" 524 525 #define QEMU_RGB(r, g, b) \ 526 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } 527 528 static const pixman_color_t color_table_rgb[2][8] = { 529 { /* dark */ 530 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ 531 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ 532 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */ 533 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ 534 [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */ 535 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ 536 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ 537 [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ 538 }, 539 { /* bright */ 540 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ 541 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */ 542 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */ 543 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ 544 [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */ 545 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ 546 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ 547 [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */ 548 } 549 }; 550 551 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, 552 TextAttributes *t_attrib) 553 { 554 static pixman_image_t *glyphs[256]; 555 DisplaySurface *surface = qemu_console_surface(s); 556 pixman_color_t fgcol, bgcol; 557 558 if (t_attrib->invers) { 559 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; 560 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; 561 } else { 562 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; 563 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; 564 } 565 566 if (!glyphs[ch]) { 567 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch); 568 } 569 qemu_pixman_glyph_render(glyphs[ch], surface->image, 570 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT); 571 } 572 573 static void text_console_resize(QemuConsole *s) 574 { 575 TextCell *cells, *c, *c1; 576 int w1, x, y, last_width; 577 578 assert(s->scanout.kind == SCANOUT_SURFACE); 579 580 last_width = s->width; 581 s->width = surface_width(s->surface) / FONT_WIDTH; 582 s->height = surface_height(s->surface) / FONT_HEIGHT; 583 584 w1 = last_width; 585 if (s->width < w1) 586 w1 = s->width; 587 588 cells = g_new(TextCell, s->width * s->total_height + 1); 589 for(y = 0; y < s->total_height; y++) { 590 c = &cells[y * s->width]; 591 if (w1 > 0) { 592 c1 = &s->cells[y * last_width]; 593 for(x = 0; x < w1; x++) { 594 *c++ = *c1++; 595 } 596 } 597 for(x = w1; x < s->width; x++) { 598 c->ch = ' '; 599 c->t_attrib = s->t_attrib_default; 600 c++; 601 } 602 } 603 g_free(s->cells); 604 s->cells = cells; 605 } 606 607 static inline void text_update_xy(QemuConsole *s, int x, int y) 608 { 609 s->text_x[0] = MIN(s->text_x[0], x); 610 s->text_x[1] = MAX(s->text_x[1], x); 611 s->text_y[0] = MIN(s->text_y[0], y); 612 s->text_y[1] = MAX(s->text_y[1], y); 613 } 614 615 static void invalidate_xy(QemuConsole *s, int x, int y) 616 { 617 if (!qemu_console_is_visible(s)) { 618 return; 619 } 620 if (s->update_x0 > x * FONT_WIDTH) 621 s->update_x0 = x * FONT_WIDTH; 622 if (s->update_y0 > y * FONT_HEIGHT) 623 s->update_y0 = y * FONT_HEIGHT; 624 if (s->update_x1 < (x + 1) * FONT_WIDTH) 625 s->update_x1 = (x + 1) * FONT_WIDTH; 626 if (s->update_y1 < (y + 1) * FONT_HEIGHT) 627 s->update_y1 = (y + 1) * FONT_HEIGHT; 628 } 629 630 static void update_xy(QemuConsole *s, int x, int y) 631 { 632 TextCell *c; 633 int y1, y2; 634 635 if (s->ds->have_text) { 636 text_update_xy(s, x, y); 637 } 638 639 y1 = (s->y_base + y) % s->total_height; 640 y2 = y1 - s->y_displayed; 641 if (y2 < 0) { 642 y2 += s->total_height; 643 } 644 if (y2 < s->height) { 645 if (x >= s->width) { 646 x = s->width - 1; 647 } 648 c = &s->cells[y1 * s->width + x]; 649 vga_putcharxy(s, x, y2, c->ch, 650 &(c->t_attrib)); 651 invalidate_xy(s, x, y2); 652 } 653 } 654 655 static void console_show_cursor(QemuConsole *s, int show) 656 { 657 TextCell *c; 658 int y, y1; 659 int x = s->x; 660 661 if (s->ds->have_text) { 662 s->cursor_invalidate = 1; 663 } 664 665 if (x >= s->width) { 666 x = s->width - 1; 667 } 668 y1 = (s->y_base + s->y) % s->total_height; 669 y = y1 - s->y_displayed; 670 if (y < 0) { 671 y += s->total_height; 672 } 673 if (y < s->height) { 674 c = &s->cells[y1 * s->width + x]; 675 if (show && cursor_visible_phase) { 676 TextAttributes t_attrib = s->t_attrib_default; 677 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ 678 vga_putcharxy(s, x, y, c->ch, &t_attrib); 679 } else { 680 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib)); 681 } 682 invalidate_xy(s, x, y); 683 } 684 } 685 686 static void console_refresh(QemuConsole *s) 687 { 688 DisplaySurface *surface = qemu_console_surface(s); 689 TextCell *c; 690 int x, y, y1; 691 692 if (s->ds->have_text) { 693 s->text_x[0] = 0; 694 s->text_y[0] = 0; 695 s->text_x[1] = s->width - 1; 696 s->text_y[1] = s->height - 1; 697 s->cursor_invalidate = 1; 698 } 699 700 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), 701 color_table_rgb[0][QEMU_COLOR_BLACK]); 702 y1 = s->y_displayed; 703 for (y = 0; y < s->height; y++) { 704 c = s->cells + y1 * s->width; 705 for (x = 0; x < s->width; x++) { 706 vga_putcharxy(s, x, y, c->ch, 707 &(c->t_attrib)); 708 c++; 709 } 710 if (++y1 == s->total_height) { 711 y1 = 0; 712 } 713 } 714 console_show_cursor(s, 1); 715 dpy_gfx_update(s, 0, 0, 716 surface_width(surface), surface_height(surface)); 717 } 718 719 static void console_scroll(QemuConsole *s, int ydelta) 720 { 721 int i, y1; 722 723 if (ydelta > 0) { 724 for(i = 0; i < ydelta; i++) { 725 if (s->y_displayed == s->y_base) 726 break; 727 if (++s->y_displayed == s->total_height) 728 s->y_displayed = 0; 729 } 730 } else { 731 ydelta = -ydelta; 732 i = s->backscroll_height; 733 if (i > s->total_height - s->height) 734 i = s->total_height - s->height; 735 y1 = s->y_base - i; 736 if (y1 < 0) 737 y1 += s->total_height; 738 for(i = 0; i < ydelta; i++) { 739 if (s->y_displayed == y1) 740 break; 741 if (--s->y_displayed < 0) 742 s->y_displayed = s->total_height - 1; 743 } 744 } 745 console_refresh(s); 746 } 747 748 static void console_put_lf(QemuConsole *s) 749 { 750 TextCell *c; 751 int x, y1; 752 753 s->y++; 754 if (s->y >= s->height) { 755 s->y = s->height - 1; 756 757 if (s->y_displayed == s->y_base) { 758 if (++s->y_displayed == s->total_height) 759 s->y_displayed = 0; 760 } 761 if (++s->y_base == s->total_height) 762 s->y_base = 0; 763 if (s->backscroll_height < s->total_height) 764 s->backscroll_height++; 765 y1 = (s->y_base + s->height - 1) % s->total_height; 766 c = &s->cells[y1 * s->width]; 767 for(x = 0; x < s->width; x++) { 768 c->ch = ' '; 769 c->t_attrib = s->t_attrib_default; 770 c++; 771 } 772 if (s->y_displayed == s->y_base) { 773 if (s->ds->have_text) { 774 s->text_x[0] = 0; 775 s->text_y[0] = 0; 776 s->text_x[1] = s->width - 1; 777 s->text_y[1] = s->height - 1; 778 } 779 780 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, 781 s->width * FONT_WIDTH, 782 (s->height - 1) * FONT_HEIGHT); 783 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, 784 s->width * FONT_WIDTH, FONT_HEIGHT, 785 color_table_rgb[0][s->t_attrib_default.bgcol]); 786 s->update_x0 = 0; 787 s->update_y0 = 0; 788 s->update_x1 = s->width * FONT_WIDTH; 789 s->update_y1 = s->height * FONT_HEIGHT; 790 } 791 } 792 } 793 794 /* Set console attributes depending on the current escape codes. 795 * NOTE: I know this code is not very efficient (checking every color for it 796 * self) but it is more readable and better maintainable. 797 */ 798 static void console_handle_escape(QemuConsole *s) 799 { 800 int i; 801 802 for (i=0; i<s->nb_esc_params; i++) { 803 switch (s->esc_params[i]) { 804 case 0: /* reset all console attributes to default */ 805 s->t_attrib = s->t_attrib_default; 806 break; 807 case 1: 808 s->t_attrib.bold = 1; 809 break; 810 case 4: 811 s->t_attrib.uline = 1; 812 break; 813 case 5: 814 s->t_attrib.blink = 1; 815 break; 816 case 7: 817 s->t_attrib.invers = 1; 818 break; 819 case 8: 820 s->t_attrib.unvisible = 1; 821 break; 822 case 22: 823 s->t_attrib.bold = 0; 824 break; 825 case 24: 826 s->t_attrib.uline = 0; 827 break; 828 case 25: 829 s->t_attrib.blink = 0; 830 break; 831 case 27: 832 s->t_attrib.invers = 0; 833 break; 834 case 28: 835 s->t_attrib.unvisible = 0; 836 break; 837 /* set foreground color */ 838 case 30: 839 s->t_attrib.fgcol = QEMU_COLOR_BLACK; 840 break; 841 case 31: 842 s->t_attrib.fgcol = QEMU_COLOR_RED; 843 break; 844 case 32: 845 s->t_attrib.fgcol = QEMU_COLOR_GREEN; 846 break; 847 case 33: 848 s->t_attrib.fgcol = QEMU_COLOR_YELLOW; 849 break; 850 case 34: 851 s->t_attrib.fgcol = QEMU_COLOR_BLUE; 852 break; 853 case 35: 854 s->t_attrib.fgcol = QEMU_COLOR_MAGENTA; 855 break; 856 case 36: 857 s->t_attrib.fgcol = QEMU_COLOR_CYAN; 858 break; 859 case 37: 860 s->t_attrib.fgcol = QEMU_COLOR_WHITE; 861 break; 862 /* set background color */ 863 case 40: 864 s->t_attrib.bgcol = QEMU_COLOR_BLACK; 865 break; 866 case 41: 867 s->t_attrib.bgcol = QEMU_COLOR_RED; 868 break; 869 case 42: 870 s->t_attrib.bgcol = QEMU_COLOR_GREEN; 871 break; 872 case 43: 873 s->t_attrib.bgcol = QEMU_COLOR_YELLOW; 874 break; 875 case 44: 876 s->t_attrib.bgcol = QEMU_COLOR_BLUE; 877 break; 878 case 45: 879 s->t_attrib.bgcol = QEMU_COLOR_MAGENTA; 880 break; 881 case 46: 882 s->t_attrib.bgcol = QEMU_COLOR_CYAN; 883 break; 884 case 47: 885 s->t_attrib.bgcol = QEMU_COLOR_WHITE; 886 break; 887 } 888 } 889 } 890 891 static void console_clear_xy(QemuConsole *s, int x, int y) 892 { 893 int y1 = (s->y_base + y) % s->total_height; 894 if (x >= s->width) { 895 x = s->width - 1; 896 } 897 TextCell *c = &s->cells[y1 * s->width + x]; 898 c->ch = ' '; 899 c->t_attrib = s->t_attrib_default; 900 update_xy(s, x, y); 901 } 902 903 static void console_put_one(QemuConsole *s, int ch) 904 { 905 TextCell *c; 906 int y1; 907 if (s->x >= s->width) { 908 /* line wrap */ 909 s->x = 0; 910 console_put_lf(s); 911 } 912 y1 = (s->y_base + s->y) % s->total_height; 913 c = &s->cells[y1 * s->width + s->x]; 914 c->ch = ch; 915 c->t_attrib = s->t_attrib; 916 update_xy(s, s->x, s->y); 917 s->x++; 918 } 919 920 static void console_respond_str(QemuConsole *s, const char *buf) 921 { 922 while (*buf) { 923 console_put_one(s, *buf); 924 buf++; 925 } 926 } 927 928 /* set cursor, checking bounds */ 929 static void set_cursor(QemuConsole *s, int x, int y) 930 { 931 if (x < 0) { 932 x = 0; 933 } 934 if (y < 0) { 935 y = 0; 936 } 937 if (y >= s->height) { 938 y = s->height - 1; 939 } 940 if (x >= s->width) { 941 x = s->width - 1; 942 } 943 944 s->x = x; 945 s->y = y; 946 } 947 948 static void console_putchar(QemuConsole *s, int ch) 949 { 950 int i; 951 int x, y; 952 char response[40]; 953 954 switch(s->state) { 955 case TTY_STATE_NORM: 956 switch(ch) { 957 case '\r': /* carriage return */ 958 s->x = 0; 959 break; 960 case '\n': /* newline */ 961 console_put_lf(s); 962 break; 963 case '\b': /* backspace */ 964 if (s->x > 0) 965 s->x--; 966 break; 967 case '\t': /* tabspace */ 968 if (s->x + (8 - (s->x % 8)) > s->width) { 969 s->x = 0; 970 console_put_lf(s); 971 } else { 972 s->x = s->x + (8 - (s->x % 8)); 973 } 974 break; 975 case '\a': /* alert aka. bell */ 976 /* TODO: has to be implemented */ 977 break; 978 case 14: 979 /* SI (shift in), character set 0 (ignored) */ 980 break; 981 case 15: 982 /* SO (shift out), character set 1 (ignored) */ 983 break; 984 case 27: /* esc (introducing an escape sequence) */ 985 s->state = TTY_STATE_ESC; 986 break; 987 default: 988 console_put_one(s, ch); 989 break; 990 } 991 break; 992 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */ 993 if (ch == '[') { 994 for(i=0;i<MAX_ESC_PARAMS;i++) 995 s->esc_params[i] = 0; 996 s->nb_esc_params = 0; 997 s->state = TTY_STATE_CSI; 998 } else { 999 s->state = TTY_STATE_NORM; 1000 } 1001 break; 1002 case TTY_STATE_CSI: /* handle escape sequence parameters */ 1003 if (ch >= '0' && ch <= '9') { 1004 if (s->nb_esc_params < MAX_ESC_PARAMS) { 1005 int *param = &s->esc_params[s->nb_esc_params]; 1006 int digit = (ch - '0'); 1007 1008 *param = (*param <= (INT_MAX - digit) / 10) ? 1009 *param * 10 + digit : INT_MAX; 1010 } 1011 } else { 1012 if (s->nb_esc_params < MAX_ESC_PARAMS) 1013 s->nb_esc_params++; 1014 if (ch == ';' || ch == '?') { 1015 break; 1016 } 1017 trace_console_putchar_csi(s->esc_params[0], s->esc_params[1], 1018 ch, s->nb_esc_params); 1019 s->state = TTY_STATE_NORM; 1020 switch(ch) { 1021 case 'A': 1022 /* move cursor up */ 1023 if (s->esc_params[0] == 0) { 1024 s->esc_params[0] = 1; 1025 } 1026 set_cursor(s, s->x, s->y - s->esc_params[0]); 1027 break; 1028 case 'B': 1029 /* move cursor down */ 1030 if (s->esc_params[0] == 0) { 1031 s->esc_params[0] = 1; 1032 } 1033 set_cursor(s, s->x, s->y + s->esc_params[0]); 1034 break; 1035 case 'C': 1036 /* move cursor right */ 1037 if (s->esc_params[0] == 0) { 1038 s->esc_params[0] = 1; 1039 } 1040 set_cursor(s, s->x + s->esc_params[0], s->y); 1041 break; 1042 case 'D': 1043 /* move cursor left */ 1044 if (s->esc_params[0] == 0) { 1045 s->esc_params[0] = 1; 1046 } 1047 set_cursor(s, s->x - s->esc_params[0], s->y); 1048 break; 1049 case 'G': 1050 /* move cursor to column */ 1051 set_cursor(s, s->esc_params[0] - 1, s->y); 1052 break; 1053 case 'f': 1054 case 'H': 1055 /* move cursor to row, column */ 1056 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1); 1057 break; 1058 case 'J': 1059 switch (s->esc_params[0]) { 1060 case 0: 1061 /* clear to end of screen */ 1062 for (y = s->y; y < s->height; y++) { 1063 for (x = 0; x < s->width; x++) { 1064 if (y == s->y && x < s->x) { 1065 continue; 1066 } 1067 console_clear_xy(s, x, y); 1068 } 1069 } 1070 break; 1071 case 1: 1072 /* clear from beginning of screen */ 1073 for (y = 0; y <= s->y; y++) { 1074 for (x = 0; x < s->width; x++) { 1075 if (y == s->y && x > s->x) { 1076 break; 1077 } 1078 console_clear_xy(s, x, y); 1079 } 1080 } 1081 break; 1082 case 2: 1083 /* clear entire screen */ 1084 for (y = 0; y <= s->height; y++) { 1085 for (x = 0; x < s->width; x++) { 1086 console_clear_xy(s, x, y); 1087 } 1088 } 1089 break; 1090 } 1091 break; 1092 case 'K': 1093 switch (s->esc_params[0]) { 1094 case 0: 1095 /* clear to eol */ 1096 for(x = s->x; x < s->width; x++) { 1097 console_clear_xy(s, x, s->y); 1098 } 1099 break; 1100 case 1: 1101 /* clear from beginning of line */ 1102 for (x = 0; x <= s->x && x < s->width; x++) { 1103 console_clear_xy(s, x, s->y); 1104 } 1105 break; 1106 case 2: 1107 /* clear entire line */ 1108 for(x = 0; x < s->width; x++) { 1109 console_clear_xy(s, x, s->y); 1110 } 1111 break; 1112 } 1113 break; 1114 case 'm': 1115 console_handle_escape(s); 1116 break; 1117 case 'n': 1118 switch (s->esc_params[0]) { 1119 case 5: 1120 /* report console status (always succeed)*/ 1121 console_respond_str(s, "\033[0n"); 1122 break; 1123 case 6: 1124 /* report cursor position */ 1125 sprintf(response, "\033[%d;%dR", 1126 (s->y_base + s->y) % s->total_height + 1, 1127 s->x + 1); 1128 console_respond_str(s, response); 1129 break; 1130 } 1131 break; 1132 case 's': 1133 /* save cursor position */ 1134 s->x_saved = s->x; 1135 s->y_saved = s->y; 1136 break; 1137 case 'u': 1138 /* restore cursor position */ 1139 s->x = s->x_saved; 1140 s->y = s->y_saved; 1141 break; 1142 default: 1143 trace_console_putchar_unhandled(ch); 1144 break; 1145 } 1146 break; 1147 } 1148 } 1149 } 1150 1151 static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl, 1152 struct DisplaySurface *new_surface, 1153 bool update) 1154 { 1155 if (dcl->ops->dpy_gfx_switch) { 1156 dcl->ops->dpy_gfx_switch(dcl, new_surface); 1157 } 1158 1159 if (update && dcl->ops->dpy_gfx_update) { 1160 dcl->ops->dpy_gfx_update(dcl, 0, 0, 1161 surface_width(new_surface), 1162 surface_height(new_surface)); 1163 } 1164 } 1165 1166 static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface) 1167 { 1168 if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) { 1169 con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface); 1170 } 1171 } 1172 1173 static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface) 1174 { 1175 if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) { 1176 con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface); 1177 } 1178 } 1179 1180 static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface, 1181 int x, int y, int w, int h) 1182 { 1183 if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) { 1184 con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h); 1185 } 1186 } 1187 1188 static void displaychangelistener_display_console(DisplayChangeListener *dcl, 1189 QemuConsole *con, 1190 Error **errp) 1191 { 1192 static const char nodev[] = 1193 "This VM has no graphic display device."; 1194 static DisplaySurface *dummy; 1195 1196 if (!con || !console_compatible_with(con, dcl, errp)) { 1197 if (!dummy) { 1198 dummy = qemu_create_placeholder_surface(640, 480, nodev); 1199 } 1200 if (con) { 1201 dpy_gfx_create_texture(con, dummy); 1202 } 1203 displaychangelistener_gfx_switch(dcl, dummy, TRUE); 1204 return; 1205 } 1206 1207 dpy_gfx_create_texture(con, con->surface); 1208 displaychangelistener_gfx_switch(dcl, con->surface, 1209 con->scanout.kind == SCANOUT_SURFACE); 1210 1211 if (con->scanout.kind == SCANOUT_DMABUF && 1212 displaychangelistener_has_dmabuf(dcl)) { 1213 dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf); 1214 } else if (con->scanout.kind == SCANOUT_TEXTURE && 1215 dcl->ops->dpy_gl_scanout_texture) { 1216 dcl->ops->dpy_gl_scanout_texture(dcl, 1217 con->scanout.texture.backing_id, 1218 con->scanout.texture.backing_y_0_top, 1219 con->scanout.texture.backing_width, 1220 con->scanout.texture.backing_height, 1221 con->scanout.texture.x, 1222 con->scanout.texture.y, 1223 con->scanout.texture.width, 1224 con->scanout.texture.height); 1225 } 1226 } 1227 1228 void console_select(unsigned int index) 1229 { 1230 DisplayChangeListener *dcl; 1231 QemuConsole *s; 1232 1233 trace_console_select(index); 1234 s = qemu_console_lookup_by_index(index); 1235 if (s) { 1236 DisplayState *ds = s->ds; 1237 1238 active_console = s; 1239 if (ds->have_gfx) { 1240 QLIST_FOREACH(dcl, &ds->listeners, next) { 1241 if (dcl->con != NULL) { 1242 continue; 1243 } 1244 displaychangelistener_display_console(dcl, s, NULL); 1245 } 1246 } 1247 if (ds->have_text) { 1248 dpy_text_resize(s, s->width, s->height); 1249 } 1250 text_console_update_cursor(NULL); 1251 } 1252 } 1253 1254 struct VCChardev { 1255 Chardev parent; 1256 QemuConsole *console; 1257 }; 1258 typedef struct VCChardev VCChardev; 1259 1260 #define TYPE_CHARDEV_VC "chardev-vc" 1261 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, 1262 TYPE_CHARDEV_VC) 1263 1264 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) 1265 { 1266 VCChardev *drv = VC_CHARDEV(chr); 1267 QemuConsole *s = drv->console; 1268 int i; 1269 1270 if (!s->ds) { 1271 return 0; 1272 } 1273 1274 s->update_x0 = s->width * FONT_WIDTH; 1275 s->update_y0 = s->height * FONT_HEIGHT; 1276 s->update_x1 = 0; 1277 s->update_y1 = 0; 1278 console_show_cursor(s, 0); 1279 for(i = 0; i < len; i++) { 1280 console_putchar(s, buf[i]); 1281 } 1282 console_show_cursor(s, 1); 1283 if (s->ds->have_gfx && s->update_x0 < s->update_x1) { 1284 dpy_gfx_update(s, s->update_x0, s->update_y0, 1285 s->update_x1 - s->update_x0, 1286 s->update_y1 - s->update_y0); 1287 } 1288 return len; 1289 } 1290 1291 static void kbd_send_chars(QemuConsole *s) 1292 { 1293 uint32_t len, avail; 1294 1295 len = qemu_chr_be_can_write(s->chr); 1296 avail = fifo8_num_used(&s->out_fifo); 1297 while (len > 0 && avail > 0) { 1298 const uint8_t *buf; 1299 uint32_t size; 1300 1301 buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size); 1302 qemu_chr_be_write(s->chr, buf, size); 1303 len = qemu_chr_be_can_write(s->chr); 1304 avail -= size; 1305 } 1306 } 1307 1308 /* called when an ascii key is pressed */ 1309 void kbd_put_keysym_console(QemuConsole *s, int keysym) 1310 { 1311 uint8_t buf[16], *q; 1312 int c; 1313 uint32_t num_free; 1314 1315 if (!s || (s->console_type == GRAPHIC_CONSOLE)) 1316 return; 1317 1318 switch(keysym) { 1319 case QEMU_KEY_CTRL_UP: 1320 console_scroll(s, -1); 1321 break; 1322 case QEMU_KEY_CTRL_DOWN: 1323 console_scroll(s, 1); 1324 break; 1325 case QEMU_KEY_CTRL_PAGEUP: 1326 console_scroll(s, -10); 1327 break; 1328 case QEMU_KEY_CTRL_PAGEDOWN: 1329 console_scroll(s, 10); 1330 break; 1331 default: 1332 /* convert the QEMU keysym to VT100 key string */ 1333 q = buf; 1334 if (keysym >= 0xe100 && keysym <= 0xe11f) { 1335 *q++ = '\033'; 1336 *q++ = '['; 1337 c = keysym - 0xe100; 1338 if (c >= 10) 1339 *q++ = '0' + (c / 10); 1340 *q++ = '0' + (c % 10); 1341 *q++ = '~'; 1342 } else if (keysym >= 0xe120 && keysym <= 0xe17f) { 1343 *q++ = '\033'; 1344 *q++ = '['; 1345 *q++ = keysym & 0xff; 1346 } else if (s->echo && (keysym == '\r' || keysym == '\n')) { 1347 vc_chr_write(s->chr, (const uint8_t *) "\r", 1); 1348 *q++ = '\n'; 1349 } else { 1350 *q++ = keysym; 1351 } 1352 if (s->echo) { 1353 vc_chr_write(s->chr, buf, q - buf); 1354 } 1355 num_free = fifo8_num_free(&s->out_fifo); 1356 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); 1357 kbd_send_chars(s); 1358 break; 1359 } 1360 } 1361 1362 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = { 1363 [Q_KEY_CODE_UP] = QEMU_KEY_UP, 1364 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN, 1365 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT, 1366 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT, 1367 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME, 1368 [Q_KEY_CODE_END] = QEMU_KEY_END, 1369 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP, 1370 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN, 1371 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE, 1372 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB, 1373 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE, 1374 }; 1375 1376 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = { 1377 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP, 1378 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN, 1379 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT, 1380 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT, 1381 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME, 1382 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END, 1383 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP, 1384 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN, 1385 }; 1386 1387 bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl) 1388 { 1389 int keysym; 1390 1391 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode]; 1392 if (keysym == 0) { 1393 return false; 1394 } 1395 kbd_put_keysym_console(s, keysym); 1396 return true; 1397 } 1398 1399 void kbd_put_string_console(QemuConsole *s, const char *str, int len) 1400 { 1401 int i; 1402 1403 for (i = 0; i < len && str[i]; i++) { 1404 kbd_put_keysym_console(s, str[i]); 1405 } 1406 } 1407 1408 void kbd_put_keysym(int keysym) 1409 { 1410 kbd_put_keysym_console(active_console, keysym); 1411 } 1412 1413 static void text_console_invalidate(void *opaque) 1414 { 1415 QemuConsole *s = (QemuConsole *) opaque; 1416 1417 if (s->ds->have_text && s->console_type == TEXT_CONSOLE) { 1418 text_console_resize(s); 1419 } 1420 console_refresh(s); 1421 } 1422 1423 static void text_console_update(void *opaque, console_ch_t *chardata) 1424 { 1425 QemuConsole *s = (QemuConsole *) opaque; 1426 int i, j, src; 1427 1428 if (s->text_x[0] <= s->text_x[1]) { 1429 src = (s->y_base + s->text_y[0]) * s->width; 1430 chardata += s->text_y[0] * s->width; 1431 for (i = s->text_y[0]; i <= s->text_y[1]; i ++) 1432 for (j = 0; j < s->width; j++, src++) { 1433 console_write_ch(chardata ++, 1434 ATTR2CHTYPE(s->cells[src].ch, 1435 s->cells[src].t_attrib.fgcol, 1436 s->cells[src].t_attrib.bgcol, 1437 s->cells[src].t_attrib.bold)); 1438 } 1439 dpy_text_update(s, s->text_x[0], s->text_y[0], 1440 s->text_x[1] - s->text_x[0], i - s->text_y[0]); 1441 s->text_x[0] = s->width; 1442 s->text_y[0] = s->height; 1443 s->text_x[1] = 0; 1444 s->text_y[1] = 0; 1445 } 1446 if (s->cursor_invalidate) { 1447 dpy_text_cursor(s, s->x, s->y); 1448 s->cursor_invalidate = 0; 1449 } 1450 } 1451 1452 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, 1453 uint32_t head) 1454 { 1455 Object *obj; 1456 QemuConsole *s; 1457 int i; 1458 1459 obj = object_new(TYPE_QEMU_CONSOLE); 1460 s = QEMU_CONSOLE(obj); 1461 qemu_co_queue_init(&s->dump_queue); 1462 s->head = head; 1463 object_property_add_link(obj, "device", TYPE_DEVICE, 1464 (Object **)&s->device, 1465 object_property_allow_set_link, 1466 OBJ_PROP_LINK_STRONG); 1467 object_property_add_uint32_ptr(obj, "head", &s->head, 1468 OBJ_PROP_FLAG_READ); 1469 1470 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && 1471 (console_type == GRAPHIC_CONSOLE))) { 1472 active_console = s; 1473 } 1474 s->ds = ds; 1475 s->console_type = console_type; 1476 s->window_id = -1; 1477 1478 if (QTAILQ_EMPTY(&consoles)) { 1479 s->index = 0; 1480 QTAILQ_INSERT_TAIL(&consoles, s, next); 1481 } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) { 1482 QemuConsole *last = QTAILQ_LAST(&consoles); 1483 s->index = last->index + 1; 1484 QTAILQ_INSERT_TAIL(&consoles, s, next); 1485 } else { 1486 /* 1487 * HACK: Put graphical consoles before text consoles. 1488 * 1489 * Only do that for coldplugged devices. After initial device 1490 * initialization we will not renumber the consoles any more. 1491 */ 1492 QemuConsole *c = QTAILQ_FIRST(&consoles); 1493 1494 while (QTAILQ_NEXT(c, next) != NULL && 1495 c->console_type == GRAPHIC_CONSOLE) { 1496 c = QTAILQ_NEXT(c, next); 1497 } 1498 if (c->console_type == GRAPHIC_CONSOLE) { 1499 /* have no text consoles */ 1500 s->index = c->index + 1; 1501 QTAILQ_INSERT_AFTER(&consoles, c, s, next); 1502 } else { 1503 s->index = c->index; 1504 QTAILQ_INSERT_BEFORE(c, s, next); 1505 /* renumber text consoles */ 1506 for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) { 1507 c->index = i; 1508 } 1509 } 1510 } 1511 return s; 1512 } 1513 1514 DisplaySurface *qemu_create_displaysurface(int width, int height) 1515 { 1516 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1517 1518 trace_displaysurface_create(surface, width, height); 1519 surface->format = PIXMAN_x8r8g8b8; 1520 surface->image = pixman_image_create_bits(surface->format, 1521 width, height, 1522 NULL, width * 4); 1523 assert(surface->image != NULL); 1524 surface->flags = QEMU_ALLOCATED_FLAG; 1525 1526 return surface; 1527 } 1528 1529 DisplaySurface *qemu_create_displaysurface_from(int width, int height, 1530 pixman_format_code_t format, 1531 int linesize, uint8_t *data) 1532 { 1533 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1534 1535 trace_displaysurface_create_from(surface, width, height, format); 1536 surface->format = format; 1537 surface->image = pixman_image_create_bits(surface->format, 1538 width, height, 1539 (void *)data, linesize); 1540 assert(surface->image != NULL); 1541 1542 return surface; 1543 } 1544 1545 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) 1546 { 1547 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1548 1549 trace_displaysurface_create_pixman(surface); 1550 surface->format = pixman_image_get_format(image); 1551 surface->image = pixman_image_ref(image); 1552 1553 return surface; 1554 } 1555 1556 DisplaySurface *qemu_create_placeholder_surface(int w, int h, 1557 const char *msg) 1558 { 1559 DisplaySurface *surface = qemu_create_displaysurface(w, h); 1560 pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK]; 1561 pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE]; 1562 pixman_image_t *glyph; 1563 int len, x, y, i; 1564 1565 len = strlen(msg); 1566 x = (w / FONT_WIDTH - len) / 2; 1567 y = (h / FONT_HEIGHT - 1) / 2; 1568 for (i = 0; i < len; i++) { 1569 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]); 1570 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg, 1571 x+i, y, FONT_WIDTH, FONT_HEIGHT); 1572 qemu_pixman_image_unref(glyph); 1573 } 1574 surface->flags |= QEMU_PLACEHOLDER_FLAG; 1575 return surface; 1576 } 1577 1578 void qemu_free_displaysurface(DisplaySurface *surface) 1579 { 1580 if (surface == NULL) { 1581 return; 1582 } 1583 trace_displaysurface_free(surface); 1584 qemu_pixman_image_unref(surface->image); 1585 g_free(surface); 1586 } 1587 1588 bool console_has_gl(QemuConsole *con) 1589 { 1590 return con->gl != NULL; 1591 } 1592 1593 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) 1594 { 1595 if (dcl->ops->dpy_has_dmabuf) { 1596 return dcl->ops->dpy_has_dmabuf(dcl); 1597 } 1598 1599 if (dcl->ops->dpy_gl_scanout_dmabuf) { 1600 return true; 1601 } 1602 1603 return false; 1604 } 1605 1606 static bool console_compatible_with(QemuConsole *con, 1607 DisplayChangeListener *dcl, Error **errp) 1608 { 1609 int flags; 1610 1611 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; 1612 1613 if (console_has_gl(con) && 1614 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) { 1615 error_setg(errp, "Display %s is incompatible with the GL context", 1616 dcl->ops->dpy_name); 1617 return false; 1618 } 1619 1620 if (flags & GRAPHIC_FLAGS_GL && 1621 !console_has_gl(con)) { 1622 error_setg(errp, "The console requires a GL context."); 1623 return false; 1624 1625 } 1626 1627 if (flags & GRAPHIC_FLAGS_DMABUF && 1628 !displaychangelistener_has_dmabuf(dcl)) { 1629 error_setg(errp, "The console requires display DMABUF support."); 1630 return false; 1631 } 1632 1633 return true; 1634 } 1635 1636 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) 1637 { 1638 /* display has opengl support */ 1639 assert(con); 1640 if (con->gl) { 1641 error_report("The console already has an OpenGL context."); 1642 exit(1); 1643 } 1644 con->gl = gl; 1645 } 1646 1647 void register_displaychangelistener(DisplayChangeListener *dcl) 1648 { 1649 QemuConsole *con; 1650 1651 assert(!dcl->ds); 1652 1653 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); 1654 dcl->ds = get_alloc_displaystate(); 1655 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); 1656 gui_setup_refresh(dcl->ds); 1657 if (dcl->con) { 1658 dcl->con->dcls++; 1659 con = dcl->con; 1660 } else { 1661 con = active_console; 1662 } 1663 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL); 1664 text_console_update_cursor(NULL); 1665 } 1666 1667 void update_displaychangelistener(DisplayChangeListener *dcl, 1668 uint64_t interval) 1669 { 1670 DisplayState *ds = dcl->ds; 1671 1672 dcl->update_interval = interval; 1673 if (!ds->refreshing && ds->update_interval > interval) { 1674 timer_mod(ds->gui_timer, ds->last_update + interval); 1675 } 1676 } 1677 1678 void unregister_displaychangelistener(DisplayChangeListener *dcl) 1679 { 1680 DisplayState *ds = dcl->ds; 1681 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name); 1682 if (dcl->con) { 1683 dcl->con->dcls--; 1684 } 1685 QLIST_REMOVE(dcl, next); 1686 dcl->ds = NULL; 1687 gui_setup_refresh(ds); 1688 } 1689 1690 static void dpy_set_ui_info_timer(void *opaque) 1691 { 1692 QemuConsole *con = opaque; 1693 1694 con->hw_ops->ui_info(con->hw, con->head, &con->ui_info); 1695 } 1696 1697 bool dpy_ui_info_supported(QemuConsole *con) 1698 { 1699 if (con == NULL) { 1700 con = active_console; 1701 } 1702 1703 return con->hw_ops->ui_info != NULL; 1704 } 1705 1706 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con) 1707 { 1708 if (con == NULL) { 1709 con = active_console; 1710 } 1711 1712 return &con->ui_info; 1713 } 1714 1715 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay) 1716 { 1717 if (con == NULL) { 1718 con = active_console; 1719 } 1720 1721 if (!dpy_ui_info_supported(con)) { 1722 return -1; 1723 } 1724 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) { 1725 /* nothing changed -- ignore */ 1726 return 0; 1727 } 1728 1729 /* 1730 * Typically we get a flood of these as the user resizes the window. 1731 * Wait until the dust has settled (one second without updates), then 1732 * go notify the guest. 1733 */ 1734 con->ui_info = *info; 1735 timer_mod(con->ui_timer, 1736 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0)); 1737 return 0; 1738 } 1739 1740 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 1741 { 1742 DisplayState *s = con->ds; 1743 DisplayChangeListener *dcl; 1744 int width = qemu_console_get_width(con, x + w); 1745 int height = qemu_console_get_height(con, y + h); 1746 1747 x = MAX(x, 0); 1748 y = MAX(y, 0); 1749 x = MIN(x, width); 1750 y = MIN(y, height); 1751 w = MIN(w, width - x); 1752 h = MIN(h, height - y); 1753 1754 if (!qemu_console_is_visible(con)) { 1755 return; 1756 } 1757 dpy_gfx_update_texture(con, con->surface, x, y, w, h); 1758 QLIST_FOREACH(dcl, &s->listeners, next) { 1759 if (con != (dcl->con ? dcl->con : active_console)) { 1760 continue; 1761 } 1762 if (dcl->ops->dpy_gfx_update) { 1763 dcl->ops->dpy_gfx_update(dcl, x, y, w, h); 1764 } 1765 } 1766 } 1767 1768 void dpy_gfx_update_full(QemuConsole *con) 1769 { 1770 int w = qemu_console_get_width(con, 0); 1771 int h = qemu_console_get_height(con, 0); 1772 1773 dpy_gfx_update(con, 0, 0, w, h); 1774 } 1775 1776 void dpy_gfx_replace_surface(QemuConsole *con, 1777 DisplaySurface *surface) 1778 { 1779 static const char placeholder_msg[] = "Display output is not active."; 1780 DisplayState *s = con->ds; 1781 DisplaySurface *old_surface = con->surface; 1782 DisplayChangeListener *dcl; 1783 int width; 1784 int height; 1785 1786 if (!surface) { 1787 if (old_surface) { 1788 width = surface_width(old_surface); 1789 height = surface_height(old_surface); 1790 } else { 1791 width = 640; 1792 height = 480; 1793 } 1794 1795 surface = qemu_create_placeholder_surface(width, height, placeholder_msg); 1796 } 1797 1798 assert(old_surface != surface); 1799 1800 con->scanout.kind = SCANOUT_SURFACE; 1801 con->surface = surface; 1802 dpy_gfx_create_texture(con, surface); 1803 QLIST_FOREACH(dcl, &s->listeners, next) { 1804 if (con != (dcl->con ? dcl->con : active_console)) { 1805 continue; 1806 } 1807 displaychangelistener_gfx_switch(dcl, surface, FALSE); 1808 } 1809 dpy_gfx_destroy_texture(con, old_surface); 1810 qemu_free_displaysurface(old_surface); 1811 } 1812 1813 bool dpy_gfx_check_format(QemuConsole *con, 1814 pixman_format_code_t format) 1815 { 1816 DisplayChangeListener *dcl; 1817 DisplayState *s = con->ds; 1818 1819 QLIST_FOREACH(dcl, &s->listeners, next) { 1820 if (dcl->con && dcl->con != con) { 1821 /* dcl bound to another console -> skip */ 1822 continue; 1823 } 1824 if (dcl->ops->dpy_gfx_check_format) { 1825 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) { 1826 return false; 1827 } 1828 } else { 1829 /* default is to allow native 32 bpp only */ 1830 if (format != qemu_default_pixman_format(32, true)) { 1831 return false; 1832 } 1833 } 1834 } 1835 return true; 1836 } 1837 1838 static void dpy_refresh(DisplayState *s) 1839 { 1840 DisplayChangeListener *dcl; 1841 1842 QLIST_FOREACH(dcl, &s->listeners, next) { 1843 if (dcl->ops->dpy_refresh) { 1844 dcl->ops->dpy_refresh(dcl); 1845 } 1846 } 1847 } 1848 1849 void dpy_text_cursor(QemuConsole *con, int x, int y) 1850 { 1851 DisplayState *s = con->ds; 1852 DisplayChangeListener *dcl; 1853 1854 if (!qemu_console_is_visible(con)) { 1855 return; 1856 } 1857 QLIST_FOREACH(dcl, &s->listeners, next) { 1858 if (con != (dcl->con ? dcl->con : active_console)) { 1859 continue; 1860 } 1861 if (dcl->ops->dpy_text_cursor) { 1862 dcl->ops->dpy_text_cursor(dcl, x, y); 1863 } 1864 } 1865 } 1866 1867 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) 1868 { 1869 DisplayState *s = con->ds; 1870 DisplayChangeListener *dcl; 1871 1872 if (!qemu_console_is_visible(con)) { 1873 return; 1874 } 1875 QLIST_FOREACH(dcl, &s->listeners, next) { 1876 if (con != (dcl->con ? dcl->con : active_console)) { 1877 continue; 1878 } 1879 if (dcl->ops->dpy_text_update) { 1880 dcl->ops->dpy_text_update(dcl, x, y, w, h); 1881 } 1882 } 1883 } 1884 1885 void dpy_text_resize(QemuConsole *con, int w, int h) 1886 { 1887 DisplayState *s = con->ds; 1888 DisplayChangeListener *dcl; 1889 1890 if (!qemu_console_is_visible(con)) { 1891 return; 1892 } 1893 QLIST_FOREACH(dcl, &s->listeners, next) { 1894 if (con != (dcl->con ? dcl->con : active_console)) { 1895 continue; 1896 } 1897 if (dcl->ops->dpy_text_resize) { 1898 dcl->ops->dpy_text_resize(dcl, w, h); 1899 } 1900 } 1901 } 1902 1903 void dpy_mouse_set(QemuConsole *con, int x, int y, int on) 1904 { 1905 DisplayState *s = con->ds; 1906 DisplayChangeListener *dcl; 1907 1908 if (!qemu_console_is_visible(con)) { 1909 return; 1910 } 1911 QLIST_FOREACH(dcl, &s->listeners, next) { 1912 if (con != (dcl->con ? dcl->con : active_console)) { 1913 continue; 1914 } 1915 if (dcl->ops->dpy_mouse_set) { 1916 dcl->ops->dpy_mouse_set(dcl, x, y, on); 1917 } 1918 } 1919 } 1920 1921 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) 1922 { 1923 DisplayState *s = con->ds; 1924 DisplayChangeListener *dcl; 1925 1926 if (!qemu_console_is_visible(con)) { 1927 return; 1928 } 1929 QLIST_FOREACH(dcl, &s->listeners, next) { 1930 if (con != (dcl->con ? dcl->con : active_console)) { 1931 continue; 1932 } 1933 if (dcl->ops->dpy_cursor_define) { 1934 dcl->ops->dpy_cursor_define(dcl, cursor); 1935 } 1936 } 1937 } 1938 1939 bool dpy_cursor_define_supported(QemuConsole *con) 1940 { 1941 DisplayState *s = con->ds; 1942 DisplayChangeListener *dcl; 1943 1944 QLIST_FOREACH(dcl, &s->listeners, next) { 1945 if (dcl->ops->dpy_cursor_define) { 1946 return true; 1947 } 1948 } 1949 return false; 1950 } 1951 1952 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, 1953 struct QEMUGLParams *qparams) 1954 { 1955 assert(con->gl); 1956 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams); 1957 } 1958 1959 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx) 1960 { 1961 assert(con->gl); 1962 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx); 1963 } 1964 1965 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) 1966 { 1967 assert(con->gl); 1968 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); 1969 } 1970 1971 void dpy_gl_scanout_disable(QemuConsole *con) 1972 { 1973 DisplayState *s = con->ds; 1974 DisplayChangeListener *dcl; 1975 1976 if (con->scanout.kind != SCANOUT_SURFACE) { 1977 con->scanout.kind = SCANOUT_NONE; 1978 } 1979 QLIST_FOREACH(dcl, &s->listeners, next) { 1980 if (con != (dcl->con ? dcl->con : active_console)) { 1981 continue; 1982 } 1983 if (dcl->ops->dpy_gl_scanout_disable) { 1984 dcl->ops->dpy_gl_scanout_disable(dcl); 1985 } 1986 } 1987 } 1988 1989 void dpy_gl_scanout_texture(QemuConsole *con, 1990 uint32_t backing_id, 1991 bool backing_y_0_top, 1992 uint32_t backing_width, 1993 uint32_t backing_height, 1994 uint32_t x, uint32_t y, 1995 uint32_t width, uint32_t height) 1996 { 1997 DisplayState *s = con->ds; 1998 DisplayChangeListener *dcl; 1999 2000 con->scanout.kind = SCANOUT_TEXTURE; 2001 con->scanout.texture = (ScanoutTexture) { 2002 backing_id, backing_y_0_top, backing_width, backing_height, 2003 x, y, width, height 2004 }; 2005 QLIST_FOREACH(dcl, &s->listeners, next) { 2006 if (con != (dcl->con ? dcl->con : active_console)) { 2007 continue; 2008 } 2009 if (dcl->ops->dpy_gl_scanout_texture) { 2010 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, 2011 backing_y_0_top, 2012 backing_width, backing_height, 2013 x, y, width, height); 2014 } 2015 } 2016 } 2017 2018 void dpy_gl_scanout_dmabuf(QemuConsole *con, 2019 QemuDmaBuf *dmabuf) 2020 { 2021 DisplayState *s = con->ds; 2022 DisplayChangeListener *dcl; 2023 2024 con->scanout.kind = SCANOUT_DMABUF; 2025 con->scanout.dmabuf = dmabuf; 2026 QLIST_FOREACH(dcl, &s->listeners, next) { 2027 if (con != (dcl->con ? dcl->con : active_console)) { 2028 continue; 2029 } 2030 if (dcl->ops->dpy_gl_scanout_dmabuf) { 2031 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); 2032 } 2033 } 2034 } 2035 2036 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, 2037 bool have_hot, uint32_t hot_x, uint32_t hot_y) 2038 { 2039 DisplayState *s = con->ds; 2040 DisplayChangeListener *dcl; 2041 2042 QLIST_FOREACH(dcl, &s->listeners, next) { 2043 if (con != (dcl->con ? dcl->con : active_console)) { 2044 continue; 2045 } 2046 if (dcl->ops->dpy_gl_cursor_dmabuf) { 2047 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf, 2048 have_hot, hot_x, hot_y); 2049 } 2050 } 2051 } 2052 2053 void dpy_gl_cursor_position(QemuConsole *con, 2054 uint32_t pos_x, uint32_t pos_y) 2055 { 2056 DisplayState *s = con->ds; 2057 DisplayChangeListener *dcl; 2058 2059 QLIST_FOREACH(dcl, &s->listeners, next) { 2060 if (con != (dcl->con ? dcl->con : active_console)) { 2061 continue; 2062 } 2063 if (dcl->ops->dpy_gl_cursor_position) { 2064 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y); 2065 } 2066 } 2067 } 2068 2069 void dpy_gl_release_dmabuf(QemuConsole *con, 2070 QemuDmaBuf *dmabuf) 2071 { 2072 DisplayState *s = con->ds; 2073 DisplayChangeListener *dcl; 2074 2075 QLIST_FOREACH(dcl, &s->listeners, next) { 2076 if (con != (dcl->con ? dcl->con : active_console)) { 2077 continue; 2078 } 2079 if (dcl->ops->dpy_gl_release_dmabuf) { 2080 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf); 2081 } 2082 } 2083 } 2084 2085 void dpy_gl_update(QemuConsole *con, 2086 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 2087 { 2088 DisplayState *s = con->ds; 2089 DisplayChangeListener *dcl; 2090 2091 assert(con->gl); 2092 2093 graphic_hw_gl_block(con, true); 2094 QLIST_FOREACH(dcl, &s->listeners, next) { 2095 if (con != (dcl->con ? dcl->con : active_console)) { 2096 continue; 2097 } 2098 if (dcl->ops->dpy_gl_update) { 2099 dcl->ops->dpy_gl_update(dcl, x, y, w, h); 2100 } 2101 } 2102 graphic_hw_gl_block(con, false); 2103 } 2104 2105 /***********************************************************/ 2106 /* register display */ 2107 2108 /* console.c internal use only */ 2109 static DisplayState *get_alloc_displaystate(void) 2110 { 2111 if (!display_state) { 2112 display_state = g_new0(DisplayState, 1); 2113 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2114 text_console_update_cursor, NULL); 2115 } 2116 return display_state; 2117 } 2118 2119 /* 2120 * Called by main(), after creating QemuConsoles 2121 * and before initializing ui (sdl/vnc/...). 2122 */ 2123 DisplayState *init_displaystate(void) 2124 { 2125 gchar *name; 2126 QemuConsole *con; 2127 2128 get_alloc_displaystate(); 2129 QTAILQ_FOREACH(con, &consoles, next) { 2130 if (con->console_type != GRAPHIC_CONSOLE && 2131 con->ds == NULL) { 2132 text_console_do_init(con->chr, display_state); 2133 } 2134 2135 /* Hook up into the qom tree here (not in new_console()), once 2136 * all QemuConsoles are created and the order / numbering 2137 * doesn't change any more */ 2138 name = g_strdup_printf("console[%d]", con->index); 2139 object_property_add_child(container_get(object_get_root(), "/backend"), 2140 name, OBJECT(con)); 2141 g_free(name); 2142 } 2143 2144 return display_state; 2145 } 2146 2147 void graphic_console_set_hwops(QemuConsole *con, 2148 const GraphicHwOps *hw_ops, 2149 void *opaque) 2150 { 2151 con->hw_ops = hw_ops; 2152 con->hw = opaque; 2153 } 2154 2155 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 2156 const GraphicHwOps *hw_ops, 2157 void *opaque) 2158 { 2159 static const char noinit[] = 2160 "Guest has not initialized the display (yet)."; 2161 int width = 640; 2162 int height = 480; 2163 QemuConsole *s; 2164 DisplayState *ds; 2165 DisplaySurface *surface; 2166 2167 ds = get_alloc_displaystate(); 2168 s = qemu_console_lookup_unused(); 2169 if (s) { 2170 trace_console_gfx_reuse(s->index); 2171 width = qemu_console_get_width(s, 0); 2172 height = qemu_console_get_height(s, 0); 2173 } else { 2174 trace_console_gfx_new(); 2175 s = new_console(ds, GRAPHIC_CONSOLE, head); 2176 s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2177 dpy_set_ui_info_timer, s); 2178 } 2179 graphic_console_set_hwops(s, hw_ops, opaque); 2180 if (dev) { 2181 object_property_set_link(OBJECT(s), "device", OBJECT(dev), 2182 &error_abort); 2183 } 2184 2185 surface = qemu_create_placeholder_surface(width, height, noinit); 2186 dpy_gfx_replace_surface(s, surface); 2187 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2188 graphic_hw_gl_unblock_timer, s); 2189 return s; 2190 } 2191 2192 static const GraphicHwOps unused_ops = { 2193 /* no callbacks */ 2194 }; 2195 2196 void graphic_console_close(QemuConsole *con) 2197 { 2198 static const char unplugged[] = 2199 "Guest display has been unplugged"; 2200 DisplaySurface *surface; 2201 int width = qemu_console_get_width(con, 640); 2202 int height = qemu_console_get_height(con, 480); 2203 2204 trace_console_gfx_close(con->index); 2205 object_property_set_link(OBJECT(con), "device", NULL, &error_abort); 2206 graphic_console_set_hwops(con, &unused_ops, NULL); 2207 2208 if (con->gl) { 2209 dpy_gl_scanout_disable(con); 2210 } 2211 surface = qemu_create_placeholder_surface(width, height, unplugged); 2212 dpy_gfx_replace_surface(con, surface); 2213 } 2214 2215 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 2216 { 2217 QemuConsole *con; 2218 2219 QTAILQ_FOREACH(con, &consoles, next) { 2220 if (con->index == index) { 2221 return con; 2222 } 2223 } 2224 return NULL; 2225 } 2226 2227 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 2228 { 2229 QemuConsole *con; 2230 Object *obj; 2231 uint32_t h; 2232 2233 QTAILQ_FOREACH(con, &consoles, next) { 2234 obj = object_property_get_link(OBJECT(con), 2235 "device", &error_abort); 2236 if (DEVICE(obj) != dev) { 2237 continue; 2238 } 2239 h = object_property_get_uint(OBJECT(con), 2240 "head", &error_abort); 2241 if (h != head) { 2242 continue; 2243 } 2244 return con; 2245 } 2246 return NULL; 2247 } 2248 2249 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, 2250 uint32_t head, Error **errp) 2251 { 2252 DeviceState *dev; 2253 QemuConsole *con; 2254 2255 dev = qdev_find_recursive(sysbus_get_default(), device_id); 2256 if (dev == NULL) { 2257 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 2258 "Device '%s' not found", device_id); 2259 return NULL; 2260 } 2261 2262 con = qemu_console_lookup_by_device(dev, head); 2263 if (con == NULL) { 2264 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", 2265 device_id, head); 2266 return NULL; 2267 } 2268 2269 return con; 2270 } 2271 2272 QemuConsole *qemu_console_lookup_unused(void) 2273 { 2274 QemuConsole *con; 2275 Object *obj; 2276 2277 QTAILQ_FOREACH(con, &consoles, next) { 2278 if (con->hw_ops != &unused_ops) { 2279 continue; 2280 } 2281 obj = object_property_get_link(OBJECT(con), 2282 "device", &error_abort); 2283 if (obj != NULL) { 2284 continue; 2285 } 2286 return con; 2287 } 2288 return NULL; 2289 } 2290 2291 bool qemu_console_is_visible(QemuConsole *con) 2292 { 2293 return (con == active_console) || (con->dcls > 0); 2294 } 2295 2296 bool qemu_console_is_graphic(QemuConsole *con) 2297 { 2298 if (con == NULL) { 2299 con = active_console; 2300 } 2301 return con && (con->console_type == GRAPHIC_CONSOLE); 2302 } 2303 2304 bool qemu_console_is_fixedsize(QemuConsole *con) 2305 { 2306 if (con == NULL) { 2307 con = active_console; 2308 } 2309 return con && (con->console_type != TEXT_CONSOLE); 2310 } 2311 2312 bool qemu_console_is_gl_blocked(QemuConsole *con) 2313 { 2314 assert(con != NULL); 2315 return con->gl_block; 2316 } 2317 2318 bool qemu_console_is_multihead(DeviceState *dev) 2319 { 2320 QemuConsole *con; 2321 Object *obj; 2322 uint32_t f = 0xffffffff; 2323 uint32_t h; 2324 2325 QTAILQ_FOREACH(con, &consoles, next) { 2326 obj = object_property_get_link(OBJECT(con), 2327 "device", &error_abort); 2328 if (DEVICE(obj) != dev) { 2329 continue; 2330 } 2331 2332 h = object_property_get_uint(OBJECT(con), 2333 "head", &error_abort); 2334 if (f == 0xffffffff) { 2335 f = h; 2336 } else if (h != f) { 2337 return true; 2338 } 2339 } 2340 return false; 2341 } 2342 2343 char *qemu_console_get_label(QemuConsole *con) 2344 { 2345 if (con->console_type == GRAPHIC_CONSOLE) { 2346 if (con->device) { 2347 DeviceState *dev; 2348 bool multihead; 2349 2350 dev = DEVICE(con->device); 2351 multihead = qemu_console_is_multihead(dev); 2352 if (multihead) { 2353 return g_strdup_printf("%s.%d", dev->id ? 2354 dev->id : 2355 object_get_typename(con->device), 2356 con->head); 2357 } else { 2358 return g_strdup_printf("%s", dev->id ? 2359 dev->id : 2360 object_get_typename(con->device)); 2361 } 2362 } 2363 return g_strdup("VGA"); 2364 } else { 2365 if (con->chr && con->chr->label) { 2366 return g_strdup(con->chr->label); 2367 } 2368 return g_strdup_printf("vc%d", con->index); 2369 } 2370 } 2371 2372 int qemu_console_get_index(QemuConsole *con) 2373 { 2374 if (con == NULL) { 2375 con = active_console; 2376 } 2377 return con ? con->index : -1; 2378 } 2379 2380 uint32_t qemu_console_get_head(QemuConsole *con) 2381 { 2382 if (con == NULL) { 2383 con = active_console; 2384 } 2385 return con ? con->head : -1; 2386 } 2387 2388 int qemu_console_get_width(QemuConsole *con, int fallback) 2389 { 2390 if (con == NULL) { 2391 con = active_console; 2392 } 2393 if (con == NULL) { 2394 return fallback; 2395 } 2396 switch (con->scanout.kind) { 2397 case SCANOUT_DMABUF: 2398 return con->scanout.dmabuf->width; 2399 case SCANOUT_TEXTURE: 2400 return con->scanout.texture.width; 2401 case SCANOUT_SURFACE: 2402 return surface_width(con->surface); 2403 default: 2404 return fallback; 2405 } 2406 } 2407 2408 int qemu_console_get_height(QemuConsole *con, int fallback) 2409 { 2410 if (con == NULL) { 2411 con = active_console; 2412 } 2413 if (con == NULL) { 2414 return fallback; 2415 } 2416 switch (con->scanout.kind) { 2417 case SCANOUT_DMABUF: 2418 return con->scanout.dmabuf->height; 2419 case SCANOUT_TEXTURE: 2420 return con->scanout.texture.height; 2421 case SCANOUT_SURFACE: 2422 return surface_height(con->surface); 2423 default: 2424 return fallback; 2425 } 2426 } 2427 2428 static void vc_chr_accept_input(Chardev *chr) 2429 { 2430 VCChardev *drv = VC_CHARDEV(chr); 2431 QemuConsole *s = drv->console; 2432 2433 kbd_send_chars(s); 2434 } 2435 2436 static void vc_chr_set_echo(Chardev *chr, bool echo) 2437 { 2438 VCChardev *drv = VC_CHARDEV(chr); 2439 QemuConsole *s = drv->console; 2440 2441 s->echo = echo; 2442 } 2443 2444 static void text_console_update_cursor_timer(void) 2445 { 2446 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) 2447 + CONSOLE_CURSOR_PERIOD / 2); 2448 } 2449 2450 static void text_console_update_cursor(void *opaque) 2451 { 2452 QemuConsole *s; 2453 int count = 0; 2454 2455 cursor_visible_phase = !cursor_visible_phase; 2456 2457 QTAILQ_FOREACH(s, &consoles, next) { 2458 if (qemu_console_is_graphic(s) || 2459 !qemu_console_is_visible(s)) { 2460 continue; 2461 } 2462 count++; 2463 graphic_hw_invalidate(s); 2464 } 2465 2466 if (count) { 2467 text_console_update_cursor_timer(); 2468 } 2469 } 2470 2471 static const GraphicHwOps text_console_ops = { 2472 .invalidate = text_console_invalidate, 2473 .text_update = text_console_update, 2474 }; 2475 2476 static void text_console_do_init(Chardev *chr, DisplayState *ds) 2477 { 2478 VCChardev *drv = VC_CHARDEV(chr); 2479 QemuConsole *s = drv->console; 2480 int g_width = 80 * FONT_WIDTH; 2481 int g_height = 24 * FONT_HEIGHT; 2482 2483 fifo8_create(&s->out_fifo, 16); 2484 s->ds = ds; 2485 2486 s->y_displayed = 0; 2487 s->y_base = 0; 2488 s->total_height = DEFAULT_BACKSCROLL; 2489 s->x = 0; 2490 s->y = 0; 2491 if (s->scanout.kind != SCANOUT_SURFACE) { 2492 if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { 2493 g_width = qemu_console_get_width(active_console, g_width); 2494 g_height = qemu_console_get_height(active_console, g_height); 2495 } 2496 s->surface = qemu_create_displaysurface(g_width, g_height); 2497 s->scanout.kind = SCANOUT_SURFACE; 2498 } 2499 2500 s->hw_ops = &text_console_ops; 2501 s->hw = s; 2502 2503 /* Set text attribute defaults */ 2504 s->t_attrib_default.bold = 0; 2505 s->t_attrib_default.uline = 0; 2506 s->t_attrib_default.blink = 0; 2507 s->t_attrib_default.invers = 0; 2508 s->t_attrib_default.unvisible = 0; 2509 s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; 2510 s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; 2511 /* set current text attributes to default */ 2512 s->t_attrib = s->t_attrib_default; 2513 text_console_resize(s); 2514 2515 if (chr->label) { 2516 char *msg; 2517 2518 s->t_attrib.bgcol = QEMU_COLOR_BLUE; 2519 msg = g_strdup_printf("%s console\r\n", chr->label); 2520 vc_chr_write(chr, (uint8_t *)msg, strlen(msg)); 2521 g_free(msg); 2522 s->t_attrib = s->t_attrib_default; 2523 } 2524 2525 qemu_chr_be_event(chr, CHR_EVENT_OPENED); 2526 } 2527 2528 static void vc_chr_open(Chardev *chr, 2529 ChardevBackend *backend, 2530 bool *be_opened, 2531 Error **errp) 2532 { 2533 ChardevVC *vc = backend->u.vc.data; 2534 VCChardev *drv = VC_CHARDEV(chr); 2535 QemuConsole *s; 2536 unsigned width = 0; 2537 unsigned height = 0; 2538 2539 if (vc->has_width) { 2540 width = vc->width; 2541 } else if (vc->has_cols) { 2542 width = vc->cols * FONT_WIDTH; 2543 } 2544 2545 if (vc->has_height) { 2546 height = vc->height; 2547 } else if (vc->has_rows) { 2548 height = vc->rows * FONT_HEIGHT; 2549 } 2550 2551 trace_console_txt_new(width, height); 2552 if (width == 0 || height == 0) { 2553 s = new_console(NULL, TEXT_CONSOLE, 0); 2554 } else { 2555 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); 2556 s->scanout.kind = SCANOUT_SURFACE; 2557 s->surface = qemu_create_displaysurface(width, height); 2558 } 2559 2560 if (!s) { 2561 error_setg(errp, "cannot create text console"); 2562 return; 2563 } 2564 2565 s->chr = chr; 2566 drv->console = s; 2567 2568 if (display_state) { 2569 text_console_do_init(chr, display_state); 2570 } 2571 2572 /* console/chardev init sometimes completes elsewhere in a 2nd 2573 * stage, so defer OPENED events until they are fully initialized 2574 */ 2575 *be_opened = false; 2576 } 2577 2578 void qemu_console_resize(QemuConsole *s, int width, int height) 2579 { 2580 DisplaySurface *surface = qemu_console_surface(s); 2581 2582 assert(s->console_type == GRAPHIC_CONSOLE); 2583 2584 if ((s->scanout.kind != SCANOUT_SURFACE || 2585 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) && 2586 qemu_console_get_width(s, -1) == width && 2587 qemu_console_get_height(s, -1) == height) { 2588 return; 2589 } 2590 2591 surface = qemu_create_displaysurface(width, height); 2592 dpy_gfx_replace_surface(s, surface); 2593 } 2594 2595 DisplaySurface *qemu_console_surface(QemuConsole *console) 2596 { 2597 switch (console->scanout.kind) { 2598 case SCANOUT_SURFACE: 2599 return console->surface; 2600 default: 2601 return NULL; 2602 } 2603 } 2604 2605 PixelFormat qemu_default_pixelformat(int bpp) 2606 { 2607 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 2608 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 2609 return pf; 2610 } 2611 2612 static QemuDisplay *dpys[DISPLAY_TYPE__MAX]; 2613 2614 void qemu_display_register(QemuDisplay *ui) 2615 { 2616 assert(ui->type < DISPLAY_TYPE__MAX); 2617 dpys[ui->type] = ui; 2618 } 2619 2620 bool qemu_display_find_default(DisplayOptions *opts) 2621 { 2622 static DisplayType prio[] = { 2623 #if defined(CONFIG_GTK) 2624 DISPLAY_TYPE_GTK, 2625 #endif 2626 #if defined(CONFIG_SDL) 2627 DISPLAY_TYPE_SDL, 2628 #endif 2629 #if defined(CONFIG_COCOA) 2630 DISPLAY_TYPE_COCOA 2631 #endif 2632 }; 2633 int i; 2634 2635 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { 2636 if (dpys[prio[i]] == NULL) { 2637 Error *local_err = NULL; 2638 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err); 2639 if (rv < 0) { 2640 error_report_err(local_err); 2641 } 2642 } 2643 if (dpys[prio[i]] == NULL) { 2644 continue; 2645 } 2646 opts->type = prio[i]; 2647 return true; 2648 } 2649 return false; 2650 } 2651 2652 void qemu_display_early_init(DisplayOptions *opts) 2653 { 2654 assert(opts->type < DISPLAY_TYPE__MAX); 2655 if (opts->type == DISPLAY_TYPE_NONE) { 2656 return; 2657 } 2658 if (dpys[opts->type] == NULL) { 2659 Error *local_err = NULL; 2660 int rv = ui_module_load(DisplayType_str(opts->type), &local_err); 2661 if (rv < 0) { 2662 error_report_err(local_err); 2663 } 2664 } 2665 if (dpys[opts->type] == NULL) { 2666 error_report("Display '%s' is not available.", 2667 DisplayType_str(opts->type)); 2668 exit(1); 2669 } 2670 if (dpys[opts->type]->early_init) { 2671 dpys[opts->type]->early_init(opts); 2672 } 2673 } 2674 2675 void qemu_display_init(DisplayState *ds, DisplayOptions *opts) 2676 { 2677 assert(opts->type < DISPLAY_TYPE__MAX); 2678 if (opts->type == DISPLAY_TYPE_NONE) { 2679 return; 2680 } 2681 assert(dpys[opts->type] != NULL); 2682 dpys[opts->type]->init(ds, opts); 2683 } 2684 2685 void qemu_display_help(void) 2686 { 2687 int idx; 2688 2689 printf("Available display backend types:\n"); 2690 printf("none\n"); 2691 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { 2692 if (!dpys[idx]) { 2693 Error *local_err = NULL; 2694 int rv = ui_module_load(DisplayType_str(idx), &local_err); 2695 if (rv < 0) { 2696 error_report_err(local_err); 2697 } 2698 } 2699 if (dpys[idx]) { 2700 printf("%s\n", DisplayType_str(dpys[idx]->type)); 2701 } 2702 } 2703 } 2704 2705 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) 2706 { 2707 int val; 2708 ChardevVC *vc; 2709 2710 backend->type = CHARDEV_BACKEND_KIND_VC; 2711 vc = backend->u.vc.data = g_new0(ChardevVC, 1); 2712 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); 2713 2714 val = qemu_opt_get_number(opts, "width", 0); 2715 if (val != 0) { 2716 vc->has_width = true; 2717 vc->width = val; 2718 } 2719 2720 val = qemu_opt_get_number(opts, "height", 0); 2721 if (val != 0) { 2722 vc->has_height = true; 2723 vc->height = val; 2724 } 2725 2726 val = qemu_opt_get_number(opts, "cols", 0); 2727 if (val != 0) { 2728 vc->has_cols = true; 2729 vc->cols = val; 2730 } 2731 2732 val = qemu_opt_get_number(opts, "rows", 0); 2733 if (val != 0) { 2734 vc->has_rows = true; 2735 vc->rows = val; 2736 } 2737 } 2738 2739 static const TypeInfo qemu_console_info = { 2740 .name = TYPE_QEMU_CONSOLE, 2741 .parent = TYPE_OBJECT, 2742 .instance_size = sizeof(QemuConsole), 2743 .class_size = sizeof(QemuConsoleClass), 2744 }; 2745 2746 static void char_vc_class_init(ObjectClass *oc, void *data) 2747 { 2748 ChardevClass *cc = CHARDEV_CLASS(oc); 2749 2750 cc->parse = qemu_chr_parse_vc; 2751 cc->open = vc_chr_open; 2752 cc->chr_write = vc_chr_write; 2753 cc->chr_accept_input = vc_chr_accept_input; 2754 cc->chr_set_echo = vc_chr_set_echo; 2755 } 2756 2757 static const TypeInfo char_vc_type_info = { 2758 .name = TYPE_CHARDEV_VC, 2759 .parent = TYPE_CHARDEV, 2760 .instance_size = sizeof(VCChardev), 2761 .class_init = char_vc_class_init, 2762 }; 2763 2764 void qemu_console_early_init(void) 2765 { 2766 /* set the default vc driver */ 2767 if (!object_class_by_name(TYPE_CHARDEV_VC)) { 2768 type_register(&char_vc_type_info); 2769 } 2770 } 2771 2772 static void register_types(void) 2773 { 2774 type_register_static(&qemu_console_info); 2775 } 2776 2777 type_init(register_types); 2778