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