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 png_struct *png_ptr; 308 png_info *info_ptr; 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 350 png_write_end(png_ptr, NULL); 351 352 png_destroy_write_struct(&png_ptr, &info_ptr); 353 354 if (fclose(f) != 0) { 355 error_setg_errno(errp, errno, 356 "PNG creation failed. Unable to close file"); 357 return false; 358 } 359 360 return true; 361 } 362 363 #else /* no png support */ 364 365 static bool png_save(int fd, pixman_image_t *image, Error **errp) 366 { 367 error_setg(errp, "Enable PNG support with libpng for screendump"); 368 return false; 369 } 370 371 #endif /* CONFIG_PNG */ 372 373 static bool ppm_save(int fd, pixman_image_t *image, Error **errp) 374 { 375 int width = pixman_image_get_width(image); 376 int height = pixman_image_get_height(image); 377 g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); 378 g_autofree char *header = NULL; 379 g_autoptr(pixman_image_t) linebuf = NULL; 380 int y; 381 382 trace_ppm_save(fd, image); 383 384 header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); 385 if (qio_channel_write_all(QIO_CHANNEL(ioc), 386 header, strlen(header), errp) < 0) { 387 return false; 388 } 389 390 linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); 391 for (y = 0; y < height; y++) { 392 qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); 393 if (qio_channel_write_all(QIO_CHANNEL(ioc), 394 (char *)pixman_image_get_data(linebuf), 395 pixman_image_get_stride(linebuf), errp) < 0) { 396 return false; 397 } 398 } 399 400 return true; 401 } 402 403 static void graphic_hw_update_bh(void *con) 404 { 405 graphic_hw_update(con); 406 } 407 408 /* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ 409 void coroutine_fn 410 qmp_screendump(const char *filename, bool has_device, const char *device, 411 bool has_head, int64_t head, 412 bool has_format, ImageFormat format, Error **errp) 413 { 414 g_autoptr(pixman_image_t) image = NULL; 415 QemuConsole *con; 416 DisplaySurface *surface; 417 int fd; 418 419 if (has_device) { 420 con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, 421 errp); 422 if (!con) { 423 return; 424 } 425 } else { 426 if (has_head) { 427 error_setg(errp, "'head' must be specified together with 'device'"); 428 return; 429 } 430 con = qemu_console_lookup_by_index(0); 431 if (!con) { 432 error_setg(errp, "There is no console to take a screendump from"); 433 return; 434 } 435 } 436 437 if (qemu_co_queue_empty(&con->dump_queue)) { 438 /* Defer the update, it will restart the pending coroutines */ 439 aio_bh_schedule_oneshot(qemu_get_aio_context(), 440 graphic_hw_update_bh, con); 441 } 442 qemu_co_queue_wait(&con->dump_queue, NULL); 443 444 /* 445 * All pending coroutines are woken up, while the BQL is held. No 446 * further graphic update are possible until it is released. Take 447 * an image ref before that. 448 */ 449 surface = qemu_console_surface(con); 450 if (!surface) { 451 error_setg(errp, "no surface"); 452 return; 453 } 454 image = pixman_image_ref(surface->image); 455 456 fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); 457 if (fd == -1) { 458 error_setg(errp, "failed to open file '%s': %s", filename, 459 strerror(errno)); 460 return; 461 } 462 463 /* 464 * The image content could potentially be updated as the coroutine 465 * yields and releases the BQL. It could produce corrupted dump, but 466 * it should be otherwise safe. 467 */ 468 if (has_format && format == IMAGE_FORMAT_PNG) { 469 /* PNG format specified for screendump */ 470 if (!png_save(fd, image, errp)) { 471 qemu_unlink(filename); 472 } 473 } else { 474 /* PPM format specified/default for screendump */ 475 if (!ppm_save(fd, image, errp)) { 476 qemu_unlink(filename); 477 } 478 } 479 } 480 481 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) 482 { 483 if (!con) { 484 con = active_console; 485 } 486 if (con && con->hw_ops->text_update) { 487 con->hw_ops->text_update(con->hw, chardata); 488 } 489 } 490 491 static void vga_fill_rect(QemuConsole *con, 492 int posx, int posy, int width, int height, 493 pixman_color_t color) 494 { 495 DisplaySurface *surface = qemu_console_surface(con); 496 pixman_rectangle16_t rect = { 497 .x = posx, .y = posy, .width = width, .height = height 498 }; 499 500 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image, 501 &color, 1, &rect); 502 } 503 504 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ 505 static void vga_bitblt(QemuConsole *con, 506 int xs, int ys, int xd, int yd, int w, int h) 507 { 508 DisplaySurface *surface = qemu_console_surface(con); 509 510 pixman_image_composite(PIXMAN_OP_SRC, 511 surface->image, NULL, surface->image, 512 xs, ys, 0, 0, xd, yd, w, h); 513 } 514 515 /***********************************************************/ 516 /* basic char display */ 517 518 #define FONT_HEIGHT 16 519 #define FONT_WIDTH 8 520 521 #include "vgafont.h" 522 523 #define QEMU_RGB(r, g, b) \ 524 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } 525 526 static const pixman_color_t color_table_rgb[2][8] = { 527 { /* dark */ 528 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ 529 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ 530 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */ 531 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ 532 [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */ 533 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ 534 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ 535 [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ 536 }, 537 { /* bright */ 538 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */ 539 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */ 540 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */ 541 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ 542 [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */ 543 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ 544 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ 545 [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */ 546 } 547 }; 548 549 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, 550 TextAttributes *t_attrib) 551 { 552 static pixman_image_t *glyphs[256]; 553 DisplaySurface *surface = qemu_console_surface(s); 554 pixman_color_t fgcol, bgcol; 555 556 if (t_attrib->invers) { 557 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; 558 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; 559 } else { 560 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; 561 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; 562 } 563 564 if (!glyphs[ch]) { 565 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch); 566 } 567 qemu_pixman_glyph_render(glyphs[ch], surface->image, 568 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT); 569 } 570 571 static void text_console_resize(QemuConsole *s) 572 { 573 TextCell *cells, *c, *c1; 574 int w1, x, y, last_width; 575 576 assert(s->scanout.kind == SCANOUT_SURFACE); 577 578 last_width = s->width; 579 s->width = surface_width(s->surface) / FONT_WIDTH; 580 s->height = surface_height(s->surface) / FONT_HEIGHT; 581 582 w1 = last_width; 583 if (s->width < w1) 584 w1 = s->width; 585 586 cells = g_new(TextCell, s->width * s->total_height + 1); 587 for(y = 0; y < s->total_height; y++) { 588 c = &cells[y * s->width]; 589 if (w1 > 0) { 590 c1 = &s->cells[y * last_width]; 591 for(x = 0; x < w1; x++) { 592 *c++ = *c1++; 593 } 594 } 595 for(x = w1; x < s->width; x++) { 596 c->ch = ' '; 597 c->t_attrib = s->t_attrib_default; 598 c++; 599 } 600 } 601 g_free(s->cells); 602 s->cells = cells; 603 } 604 605 static inline void text_update_xy(QemuConsole *s, int x, int y) 606 { 607 s->text_x[0] = MIN(s->text_x[0], x); 608 s->text_x[1] = MAX(s->text_x[1], x); 609 s->text_y[0] = MIN(s->text_y[0], y); 610 s->text_y[1] = MAX(s->text_y[1], y); 611 } 612 613 static void invalidate_xy(QemuConsole *s, int x, int y) 614 { 615 if (!qemu_console_is_visible(s)) { 616 return; 617 } 618 if (s->update_x0 > x * FONT_WIDTH) 619 s->update_x0 = x * FONT_WIDTH; 620 if (s->update_y0 > y * FONT_HEIGHT) 621 s->update_y0 = y * FONT_HEIGHT; 622 if (s->update_x1 < (x + 1) * FONT_WIDTH) 623 s->update_x1 = (x + 1) * FONT_WIDTH; 624 if (s->update_y1 < (y + 1) * FONT_HEIGHT) 625 s->update_y1 = (y + 1) * FONT_HEIGHT; 626 } 627 628 static void update_xy(QemuConsole *s, int x, int y) 629 { 630 TextCell *c; 631 int y1, y2; 632 633 if (s->ds->have_text) { 634 text_update_xy(s, x, y); 635 } 636 637 y1 = (s->y_base + y) % s->total_height; 638 y2 = y1 - s->y_displayed; 639 if (y2 < 0) { 640 y2 += s->total_height; 641 } 642 if (y2 < s->height) { 643 if (x >= s->width) { 644 x = s->width - 1; 645 } 646 c = &s->cells[y1 * s->width + x]; 647 vga_putcharxy(s, x, y2, c->ch, 648 &(c->t_attrib)); 649 invalidate_xy(s, x, y2); 650 } 651 } 652 653 static void console_show_cursor(QemuConsole *s, int show) 654 { 655 TextCell *c; 656 int y, y1; 657 int x = s->x; 658 659 if (s->ds->have_text) { 660 s->cursor_invalidate = 1; 661 } 662 663 if (x >= s->width) { 664 x = s->width - 1; 665 } 666 y1 = (s->y_base + s->y) % s->total_height; 667 y = y1 - s->y_displayed; 668 if (y < 0) { 669 y += s->total_height; 670 } 671 if (y < s->height) { 672 c = &s->cells[y1 * s->width + x]; 673 if (show && cursor_visible_phase) { 674 TextAttributes t_attrib = s->t_attrib_default; 675 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ 676 vga_putcharxy(s, x, y, c->ch, &t_attrib); 677 } else { 678 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib)); 679 } 680 invalidate_xy(s, x, y); 681 } 682 } 683 684 static void console_refresh(QemuConsole *s) 685 { 686 DisplaySurface *surface = qemu_console_surface(s); 687 TextCell *c; 688 int x, y, y1; 689 690 if (s->ds->have_text) { 691 s->text_x[0] = 0; 692 s->text_y[0] = 0; 693 s->text_x[1] = s->width - 1; 694 s->text_y[1] = s->height - 1; 695 s->cursor_invalidate = 1; 696 } 697 698 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), 699 color_table_rgb[0][QEMU_COLOR_BLACK]); 700 y1 = s->y_displayed; 701 for (y = 0; y < s->height; y++) { 702 c = s->cells + y1 * s->width; 703 for (x = 0; x < s->width; x++) { 704 vga_putcharxy(s, x, y, c->ch, 705 &(c->t_attrib)); 706 c++; 707 } 708 if (++y1 == s->total_height) { 709 y1 = 0; 710 } 711 } 712 console_show_cursor(s, 1); 713 dpy_gfx_update(s, 0, 0, 714 surface_width(surface), surface_height(surface)); 715 } 716 717 static void console_scroll(QemuConsole *s, int ydelta) 718 { 719 int i, y1; 720 721 if (ydelta > 0) { 722 for(i = 0; i < ydelta; i++) { 723 if (s->y_displayed == s->y_base) 724 break; 725 if (++s->y_displayed == s->total_height) 726 s->y_displayed = 0; 727 } 728 } else { 729 ydelta = -ydelta; 730 i = s->backscroll_height; 731 if (i > s->total_height - s->height) 732 i = s->total_height - s->height; 733 y1 = s->y_base - i; 734 if (y1 < 0) 735 y1 += s->total_height; 736 for(i = 0; i < ydelta; i++) { 737 if (s->y_displayed == y1) 738 break; 739 if (--s->y_displayed < 0) 740 s->y_displayed = s->total_height - 1; 741 } 742 } 743 console_refresh(s); 744 } 745 746 static void console_put_lf(QemuConsole *s) 747 { 748 TextCell *c; 749 int x, y1; 750 751 s->y++; 752 if (s->y >= s->height) { 753 s->y = s->height - 1; 754 755 if (s->y_displayed == s->y_base) { 756 if (++s->y_displayed == s->total_height) 757 s->y_displayed = 0; 758 } 759 if (++s->y_base == s->total_height) 760 s->y_base = 0; 761 if (s->backscroll_height < s->total_height) 762 s->backscroll_height++; 763 y1 = (s->y_base + s->height - 1) % s->total_height; 764 c = &s->cells[y1 * s->width]; 765 for(x = 0; x < s->width; x++) { 766 c->ch = ' '; 767 c->t_attrib = s->t_attrib_default; 768 c++; 769 } 770 if (s->y_displayed == s->y_base) { 771 if (s->ds->have_text) { 772 s->text_x[0] = 0; 773 s->text_y[0] = 0; 774 s->text_x[1] = s->width - 1; 775 s->text_y[1] = s->height - 1; 776 } 777 778 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, 779 s->width * FONT_WIDTH, 780 (s->height - 1) * FONT_HEIGHT); 781 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, 782 s->width * FONT_WIDTH, FONT_HEIGHT, 783 color_table_rgb[0][s->t_attrib_default.bgcol]); 784 s->update_x0 = 0; 785 s->update_y0 = 0; 786 s->update_x1 = s->width * FONT_WIDTH; 787 s->update_y1 = s->height * FONT_HEIGHT; 788 } 789 } 790 } 791 792 /* Set console attributes depending on the current escape codes. 793 * NOTE: I know this code is not very efficient (checking every color for it 794 * self) but it is more readable and better maintainable. 795 */ 796 static void console_handle_escape(QemuConsole *s) 797 { 798 int i; 799 800 for (i=0; i<s->nb_esc_params; i++) { 801 switch (s->esc_params[i]) { 802 case 0: /* reset all console attributes to default */ 803 s->t_attrib = s->t_attrib_default; 804 break; 805 case 1: 806 s->t_attrib.bold = 1; 807 break; 808 case 4: 809 s->t_attrib.uline = 1; 810 break; 811 case 5: 812 s->t_attrib.blink = 1; 813 break; 814 case 7: 815 s->t_attrib.invers = 1; 816 break; 817 case 8: 818 s->t_attrib.unvisible = 1; 819 break; 820 case 22: 821 s->t_attrib.bold = 0; 822 break; 823 case 24: 824 s->t_attrib.uline = 0; 825 break; 826 case 25: 827 s->t_attrib.blink = 0; 828 break; 829 case 27: 830 s->t_attrib.invers = 0; 831 break; 832 case 28: 833 s->t_attrib.unvisible = 0; 834 break; 835 /* set foreground color */ 836 case 30: 837 s->t_attrib.fgcol = QEMU_COLOR_BLACK; 838 break; 839 case 31: 840 s->t_attrib.fgcol = QEMU_COLOR_RED; 841 break; 842 case 32: 843 s->t_attrib.fgcol = QEMU_COLOR_GREEN; 844 break; 845 case 33: 846 s->t_attrib.fgcol = QEMU_COLOR_YELLOW; 847 break; 848 case 34: 849 s->t_attrib.fgcol = QEMU_COLOR_BLUE; 850 break; 851 case 35: 852 s->t_attrib.fgcol = QEMU_COLOR_MAGENTA; 853 break; 854 case 36: 855 s->t_attrib.fgcol = QEMU_COLOR_CYAN; 856 break; 857 case 37: 858 s->t_attrib.fgcol = QEMU_COLOR_WHITE; 859 break; 860 /* set background color */ 861 case 40: 862 s->t_attrib.bgcol = QEMU_COLOR_BLACK; 863 break; 864 case 41: 865 s->t_attrib.bgcol = QEMU_COLOR_RED; 866 break; 867 case 42: 868 s->t_attrib.bgcol = QEMU_COLOR_GREEN; 869 break; 870 case 43: 871 s->t_attrib.bgcol = QEMU_COLOR_YELLOW; 872 break; 873 case 44: 874 s->t_attrib.bgcol = QEMU_COLOR_BLUE; 875 break; 876 case 45: 877 s->t_attrib.bgcol = QEMU_COLOR_MAGENTA; 878 break; 879 case 46: 880 s->t_attrib.bgcol = QEMU_COLOR_CYAN; 881 break; 882 case 47: 883 s->t_attrib.bgcol = QEMU_COLOR_WHITE; 884 break; 885 } 886 } 887 } 888 889 static void console_clear_xy(QemuConsole *s, int x, int y) 890 { 891 int y1 = (s->y_base + y) % s->total_height; 892 if (x >= s->width) { 893 x = s->width - 1; 894 } 895 TextCell *c = &s->cells[y1 * s->width + x]; 896 c->ch = ' '; 897 c->t_attrib = s->t_attrib_default; 898 update_xy(s, x, y); 899 } 900 901 static void console_put_one(QemuConsole *s, int ch) 902 { 903 TextCell *c; 904 int y1; 905 if (s->x >= s->width) { 906 /* line wrap */ 907 s->x = 0; 908 console_put_lf(s); 909 } 910 y1 = (s->y_base + s->y) % s->total_height; 911 c = &s->cells[y1 * s->width + s->x]; 912 c->ch = ch; 913 c->t_attrib = s->t_attrib; 914 update_xy(s, s->x, s->y); 915 s->x++; 916 } 917 918 static void console_respond_str(QemuConsole *s, const char *buf) 919 { 920 while (*buf) { 921 console_put_one(s, *buf); 922 buf++; 923 } 924 } 925 926 /* set cursor, checking bounds */ 927 static void set_cursor(QemuConsole *s, int x, int y) 928 { 929 if (x < 0) { 930 x = 0; 931 } 932 if (y < 0) { 933 y = 0; 934 } 935 if (y >= s->height) { 936 y = s->height - 1; 937 } 938 if (x >= s->width) { 939 x = s->width - 1; 940 } 941 942 s->x = x; 943 s->y = y; 944 } 945 946 static void console_putchar(QemuConsole *s, int ch) 947 { 948 int i; 949 int x, y; 950 char response[40]; 951 952 switch(s->state) { 953 case TTY_STATE_NORM: 954 switch(ch) { 955 case '\r': /* carriage return */ 956 s->x = 0; 957 break; 958 case '\n': /* newline */ 959 console_put_lf(s); 960 break; 961 case '\b': /* backspace */ 962 if (s->x > 0) 963 s->x--; 964 break; 965 case '\t': /* tabspace */ 966 if (s->x + (8 - (s->x % 8)) > s->width) { 967 s->x = 0; 968 console_put_lf(s); 969 } else { 970 s->x = s->x + (8 - (s->x % 8)); 971 } 972 break; 973 case '\a': /* alert aka. bell */ 974 /* TODO: has to be implemented */ 975 break; 976 case 14: 977 /* SI (shift in), character set 0 (ignored) */ 978 break; 979 case 15: 980 /* SO (shift out), character set 1 (ignored) */ 981 break; 982 case 27: /* esc (introducing an escape sequence) */ 983 s->state = TTY_STATE_ESC; 984 break; 985 default: 986 console_put_one(s, ch); 987 break; 988 } 989 break; 990 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */ 991 if (ch == '[') { 992 for(i=0;i<MAX_ESC_PARAMS;i++) 993 s->esc_params[i] = 0; 994 s->nb_esc_params = 0; 995 s->state = TTY_STATE_CSI; 996 } else { 997 s->state = TTY_STATE_NORM; 998 } 999 break; 1000 case TTY_STATE_CSI: /* handle escape sequence parameters */ 1001 if (ch >= '0' && ch <= '9') { 1002 if (s->nb_esc_params < MAX_ESC_PARAMS) { 1003 int *param = &s->esc_params[s->nb_esc_params]; 1004 int digit = (ch - '0'); 1005 1006 *param = (*param <= (INT_MAX - digit) / 10) ? 1007 *param * 10 + digit : INT_MAX; 1008 } 1009 } else { 1010 if (s->nb_esc_params < MAX_ESC_PARAMS) 1011 s->nb_esc_params++; 1012 if (ch == ';' || ch == '?') { 1013 break; 1014 } 1015 trace_console_putchar_csi(s->esc_params[0], s->esc_params[1], 1016 ch, s->nb_esc_params); 1017 s->state = TTY_STATE_NORM; 1018 switch(ch) { 1019 case 'A': 1020 /* move cursor up */ 1021 if (s->esc_params[0] == 0) { 1022 s->esc_params[0] = 1; 1023 } 1024 set_cursor(s, s->x, s->y - s->esc_params[0]); 1025 break; 1026 case 'B': 1027 /* move cursor down */ 1028 if (s->esc_params[0] == 0) { 1029 s->esc_params[0] = 1; 1030 } 1031 set_cursor(s, s->x, s->y + s->esc_params[0]); 1032 break; 1033 case 'C': 1034 /* move cursor right */ 1035 if (s->esc_params[0] == 0) { 1036 s->esc_params[0] = 1; 1037 } 1038 set_cursor(s, s->x + s->esc_params[0], s->y); 1039 break; 1040 case 'D': 1041 /* move cursor left */ 1042 if (s->esc_params[0] == 0) { 1043 s->esc_params[0] = 1; 1044 } 1045 set_cursor(s, s->x - s->esc_params[0], s->y); 1046 break; 1047 case 'G': 1048 /* move cursor to column */ 1049 set_cursor(s, s->esc_params[0] - 1, s->y); 1050 break; 1051 case 'f': 1052 case 'H': 1053 /* move cursor to row, column */ 1054 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1); 1055 break; 1056 case 'J': 1057 switch (s->esc_params[0]) { 1058 case 0: 1059 /* clear to end of screen */ 1060 for (y = s->y; y < s->height; y++) { 1061 for (x = 0; x < s->width; x++) { 1062 if (y == s->y && x < s->x) { 1063 continue; 1064 } 1065 console_clear_xy(s, x, y); 1066 } 1067 } 1068 break; 1069 case 1: 1070 /* clear from beginning of screen */ 1071 for (y = 0; y <= s->y; y++) { 1072 for (x = 0; x < s->width; x++) { 1073 if (y == s->y && x > s->x) { 1074 break; 1075 } 1076 console_clear_xy(s, x, y); 1077 } 1078 } 1079 break; 1080 case 2: 1081 /* clear entire screen */ 1082 for (y = 0; y <= s->height; y++) { 1083 for (x = 0; x < s->width; x++) { 1084 console_clear_xy(s, x, y); 1085 } 1086 } 1087 break; 1088 } 1089 break; 1090 case 'K': 1091 switch (s->esc_params[0]) { 1092 case 0: 1093 /* clear to eol */ 1094 for(x = s->x; x < s->width; x++) { 1095 console_clear_xy(s, x, s->y); 1096 } 1097 break; 1098 case 1: 1099 /* clear from beginning of line */ 1100 for (x = 0; x <= s->x && x < s->width; x++) { 1101 console_clear_xy(s, x, s->y); 1102 } 1103 break; 1104 case 2: 1105 /* clear entire line */ 1106 for(x = 0; x < s->width; x++) { 1107 console_clear_xy(s, x, s->y); 1108 } 1109 break; 1110 } 1111 break; 1112 case 'm': 1113 console_handle_escape(s); 1114 break; 1115 case 'n': 1116 switch (s->esc_params[0]) { 1117 case 5: 1118 /* report console status (always succeed)*/ 1119 console_respond_str(s, "\033[0n"); 1120 break; 1121 case 6: 1122 /* report cursor position */ 1123 sprintf(response, "\033[%d;%dR", 1124 (s->y_base + s->y) % s->total_height + 1, 1125 s->x + 1); 1126 console_respond_str(s, response); 1127 break; 1128 } 1129 break; 1130 case 's': 1131 /* save cursor position */ 1132 s->x_saved = s->x; 1133 s->y_saved = s->y; 1134 break; 1135 case 'u': 1136 /* restore cursor position */ 1137 s->x = s->x_saved; 1138 s->y = s->y_saved; 1139 break; 1140 default: 1141 trace_console_putchar_unhandled(ch); 1142 break; 1143 } 1144 break; 1145 } 1146 } 1147 } 1148 1149 static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl, 1150 struct DisplaySurface *new_surface, 1151 bool update) 1152 { 1153 if (dcl->ops->dpy_gfx_switch) { 1154 dcl->ops->dpy_gfx_switch(dcl, new_surface); 1155 } 1156 1157 if (update && dcl->ops->dpy_gfx_update) { 1158 dcl->ops->dpy_gfx_update(dcl, 0, 0, 1159 surface_width(new_surface), 1160 surface_height(new_surface)); 1161 } 1162 } 1163 1164 static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface) 1165 { 1166 if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) { 1167 con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface); 1168 } 1169 } 1170 1171 static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface) 1172 { 1173 if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) { 1174 con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface); 1175 } 1176 } 1177 1178 static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface, 1179 int x, int y, int w, int h) 1180 { 1181 if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) { 1182 con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h); 1183 } 1184 } 1185 1186 static void displaychangelistener_display_console(DisplayChangeListener *dcl, 1187 QemuConsole *con, 1188 Error **errp) 1189 { 1190 static const char nodev[] = 1191 "This VM has no graphic display device."; 1192 static DisplaySurface *dummy; 1193 1194 if (!con || !console_compatible_with(con, dcl, errp)) { 1195 if (!dummy) { 1196 dummy = qemu_create_placeholder_surface(640, 480, nodev); 1197 } 1198 if (con) { 1199 dpy_gfx_create_texture(con, dummy); 1200 } 1201 displaychangelistener_gfx_switch(dcl, dummy, TRUE); 1202 return; 1203 } 1204 1205 dpy_gfx_create_texture(con, con->surface); 1206 displaychangelistener_gfx_switch(dcl, con->surface, 1207 con->scanout.kind == SCANOUT_SURFACE); 1208 1209 if (con->scanout.kind == SCANOUT_DMABUF && 1210 displaychangelistener_has_dmabuf(dcl)) { 1211 dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf); 1212 } else if (con->scanout.kind == SCANOUT_TEXTURE && 1213 dcl->ops->dpy_gl_scanout_texture) { 1214 dcl->ops->dpy_gl_scanout_texture(dcl, 1215 con->scanout.texture.backing_id, 1216 con->scanout.texture.backing_y_0_top, 1217 con->scanout.texture.backing_width, 1218 con->scanout.texture.backing_height, 1219 con->scanout.texture.x, 1220 con->scanout.texture.y, 1221 con->scanout.texture.width, 1222 con->scanout.texture.height); 1223 } 1224 } 1225 1226 void console_select(unsigned int index) 1227 { 1228 DisplayChangeListener *dcl; 1229 QemuConsole *s; 1230 1231 trace_console_select(index); 1232 s = qemu_console_lookup_by_index(index); 1233 if (s) { 1234 DisplayState *ds = s->ds; 1235 1236 active_console = s; 1237 if (ds->have_gfx) { 1238 QLIST_FOREACH(dcl, &ds->listeners, next) { 1239 if (dcl->con != NULL) { 1240 continue; 1241 } 1242 displaychangelistener_display_console(dcl, s, NULL); 1243 } 1244 } 1245 if (ds->have_text) { 1246 dpy_text_resize(s, s->width, s->height); 1247 } 1248 text_console_update_cursor(NULL); 1249 } 1250 } 1251 1252 struct VCChardev { 1253 Chardev parent; 1254 QemuConsole *console; 1255 }; 1256 typedef struct VCChardev VCChardev; 1257 1258 #define TYPE_CHARDEV_VC "chardev-vc" 1259 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV, 1260 TYPE_CHARDEV_VC) 1261 1262 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) 1263 { 1264 VCChardev *drv = VC_CHARDEV(chr); 1265 QemuConsole *s = drv->console; 1266 int i; 1267 1268 if (!s->ds) { 1269 return 0; 1270 } 1271 1272 s->update_x0 = s->width * FONT_WIDTH; 1273 s->update_y0 = s->height * FONT_HEIGHT; 1274 s->update_x1 = 0; 1275 s->update_y1 = 0; 1276 console_show_cursor(s, 0); 1277 for(i = 0; i < len; i++) { 1278 console_putchar(s, buf[i]); 1279 } 1280 console_show_cursor(s, 1); 1281 if (s->ds->have_gfx && s->update_x0 < s->update_x1) { 1282 dpy_gfx_update(s, s->update_x0, s->update_y0, 1283 s->update_x1 - s->update_x0, 1284 s->update_y1 - s->update_y0); 1285 } 1286 return len; 1287 } 1288 1289 static void kbd_send_chars(QemuConsole *s) 1290 { 1291 uint32_t len, avail; 1292 1293 len = qemu_chr_be_can_write(s->chr); 1294 avail = fifo8_num_used(&s->out_fifo); 1295 while (len > 0 && avail > 0) { 1296 const uint8_t *buf; 1297 uint32_t size; 1298 1299 buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size); 1300 qemu_chr_be_write(s->chr, buf, size); 1301 len = qemu_chr_be_can_write(s->chr); 1302 avail -= size; 1303 } 1304 } 1305 1306 /* called when an ascii key is pressed */ 1307 void kbd_put_keysym_console(QemuConsole *s, int keysym) 1308 { 1309 uint8_t buf[16], *q; 1310 int c; 1311 uint32_t num_free; 1312 1313 if (!s || (s->console_type == GRAPHIC_CONSOLE)) 1314 return; 1315 1316 switch(keysym) { 1317 case QEMU_KEY_CTRL_UP: 1318 console_scroll(s, -1); 1319 break; 1320 case QEMU_KEY_CTRL_DOWN: 1321 console_scroll(s, 1); 1322 break; 1323 case QEMU_KEY_CTRL_PAGEUP: 1324 console_scroll(s, -10); 1325 break; 1326 case QEMU_KEY_CTRL_PAGEDOWN: 1327 console_scroll(s, 10); 1328 break; 1329 default: 1330 /* convert the QEMU keysym to VT100 key string */ 1331 q = buf; 1332 if (keysym >= 0xe100 && keysym <= 0xe11f) { 1333 *q++ = '\033'; 1334 *q++ = '['; 1335 c = keysym - 0xe100; 1336 if (c >= 10) 1337 *q++ = '0' + (c / 10); 1338 *q++ = '0' + (c % 10); 1339 *q++ = '~'; 1340 } else if (keysym >= 0xe120 && keysym <= 0xe17f) { 1341 *q++ = '\033'; 1342 *q++ = '['; 1343 *q++ = keysym & 0xff; 1344 } else if (s->echo && (keysym == '\r' || keysym == '\n')) { 1345 vc_chr_write(s->chr, (const uint8_t *) "\r", 1); 1346 *q++ = '\n'; 1347 } else { 1348 *q++ = keysym; 1349 } 1350 if (s->echo) { 1351 vc_chr_write(s->chr, buf, q - buf); 1352 } 1353 num_free = fifo8_num_free(&s->out_fifo); 1354 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); 1355 kbd_send_chars(s); 1356 break; 1357 } 1358 } 1359 1360 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = { 1361 [Q_KEY_CODE_UP] = QEMU_KEY_UP, 1362 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN, 1363 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT, 1364 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT, 1365 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME, 1366 [Q_KEY_CODE_END] = QEMU_KEY_END, 1367 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP, 1368 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN, 1369 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE, 1370 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB, 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 bool qemu_console_is_multihead(DeviceState *dev) 2317 { 2318 QemuConsole *con; 2319 Object *obj; 2320 uint32_t f = 0xffffffff; 2321 uint32_t h; 2322 2323 QTAILQ_FOREACH(con, &consoles, next) { 2324 obj = object_property_get_link(OBJECT(con), 2325 "device", &error_abort); 2326 if (DEVICE(obj) != dev) { 2327 continue; 2328 } 2329 2330 h = object_property_get_uint(OBJECT(con), 2331 "head", &error_abort); 2332 if (f == 0xffffffff) { 2333 f = h; 2334 } else if (h != f) { 2335 return true; 2336 } 2337 } 2338 return false; 2339 } 2340 2341 char *qemu_console_get_label(QemuConsole *con) 2342 { 2343 if (con->console_type == GRAPHIC_CONSOLE) { 2344 if (con->device) { 2345 DeviceState *dev; 2346 bool multihead; 2347 2348 dev = DEVICE(con->device); 2349 multihead = qemu_console_is_multihead(dev); 2350 if (multihead) { 2351 return g_strdup_printf("%s.%d", dev->id ? 2352 dev->id : 2353 object_get_typename(con->device), 2354 con->head); 2355 } else { 2356 return g_strdup_printf("%s", dev->id ? 2357 dev->id : 2358 object_get_typename(con->device)); 2359 } 2360 } 2361 return g_strdup("VGA"); 2362 } else { 2363 if (con->chr && con->chr->label) { 2364 return g_strdup(con->chr->label); 2365 } 2366 return g_strdup_printf("vc%d", con->index); 2367 } 2368 } 2369 2370 int qemu_console_get_index(QemuConsole *con) 2371 { 2372 if (con == NULL) { 2373 con = active_console; 2374 } 2375 return con ? con->index : -1; 2376 } 2377 2378 uint32_t qemu_console_get_head(QemuConsole *con) 2379 { 2380 if (con == NULL) { 2381 con = active_console; 2382 } 2383 return con ? con->head : -1; 2384 } 2385 2386 int qemu_console_get_width(QemuConsole *con, int fallback) 2387 { 2388 if (con == NULL) { 2389 con = active_console; 2390 } 2391 if (con == NULL) { 2392 return fallback; 2393 } 2394 switch (con->scanout.kind) { 2395 case SCANOUT_DMABUF: 2396 return con->scanout.dmabuf->width; 2397 case SCANOUT_TEXTURE: 2398 return con->scanout.texture.width; 2399 case SCANOUT_SURFACE: 2400 return surface_width(con->surface); 2401 default: 2402 return fallback; 2403 } 2404 } 2405 2406 int qemu_console_get_height(QemuConsole *con, int fallback) 2407 { 2408 if (con == NULL) { 2409 con = active_console; 2410 } 2411 if (con == NULL) { 2412 return fallback; 2413 } 2414 switch (con->scanout.kind) { 2415 case SCANOUT_DMABUF: 2416 return con->scanout.dmabuf->height; 2417 case SCANOUT_TEXTURE: 2418 return con->scanout.texture.height; 2419 case SCANOUT_SURFACE: 2420 return surface_height(con->surface); 2421 default: 2422 return fallback; 2423 } 2424 } 2425 2426 static void vc_chr_accept_input(Chardev *chr) 2427 { 2428 VCChardev *drv = VC_CHARDEV(chr); 2429 QemuConsole *s = drv->console; 2430 2431 kbd_send_chars(s); 2432 } 2433 2434 static void vc_chr_set_echo(Chardev *chr, bool echo) 2435 { 2436 VCChardev *drv = VC_CHARDEV(chr); 2437 QemuConsole *s = drv->console; 2438 2439 s->echo = echo; 2440 } 2441 2442 static void text_console_update_cursor_timer(void) 2443 { 2444 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) 2445 + CONSOLE_CURSOR_PERIOD / 2); 2446 } 2447 2448 static void text_console_update_cursor(void *opaque) 2449 { 2450 QemuConsole *s; 2451 int count = 0; 2452 2453 cursor_visible_phase = !cursor_visible_phase; 2454 2455 QTAILQ_FOREACH(s, &consoles, next) { 2456 if (qemu_console_is_graphic(s) || 2457 !qemu_console_is_visible(s)) { 2458 continue; 2459 } 2460 count++; 2461 graphic_hw_invalidate(s); 2462 } 2463 2464 if (count) { 2465 text_console_update_cursor_timer(); 2466 } 2467 } 2468 2469 static const GraphicHwOps text_console_ops = { 2470 .invalidate = text_console_invalidate, 2471 .text_update = text_console_update, 2472 }; 2473 2474 static void text_console_do_init(Chardev *chr, DisplayState *ds) 2475 { 2476 VCChardev *drv = VC_CHARDEV(chr); 2477 QemuConsole *s = drv->console; 2478 int g_width = 80 * FONT_WIDTH; 2479 int g_height = 24 * FONT_HEIGHT; 2480 2481 fifo8_create(&s->out_fifo, 16); 2482 s->ds = ds; 2483 2484 s->y_displayed = 0; 2485 s->y_base = 0; 2486 s->total_height = DEFAULT_BACKSCROLL; 2487 s->x = 0; 2488 s->y = 0; 2489 if (s->scanout.kind != SCANOUT_SURFACE) { 2490 if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { 2491 g_width = qemu_console_get_width(active_console, g_width); 2492 g_height = qemu_console_get_height(active_console, g_height); 2493 } 2494 s->surface = qemu_create_displaysurface(g_width, g_height); 2495 s->scanout.kind = SCANOUT_SURFACE; 2496 } 2497 2498 s->hw_ops = &text_console_ops; 2499 s->hw = s; 2500 2501 /* Set text attribute defaults */ 2502 s->t_attrib_default.bold = 0; 2503 s->t_attrib_default.uline = 0; 2504 s->t_attrib_default.blink = 0; 2505 s->t_attrib_default.invers = 0; 2506 s->t_attrib_default.unvisible = 0; 2507 s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; 2508 s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; 2509 /* set current text attributes to default */ 2510 s->t_attrib = s->t_attrib_default; 2511 text_console_resize(s); 2512 2513 if (chr->label) { 2514 char *msg; 2515 2516 s->t_attrib.bgcol = QEMU_COLOR_BLUE; 2517 msg = g_strdup_printf("%s console\r\n", chr->label); 2518 vc_chr_write(chr, (uint8_t *)msg, strlen(msg)); 2519 g_free(msg); 2520 s->t_attrib = s->t_attrib_default; 2521 } 2522 2523 qemu_chr_be_event(chr, CHR_EVENT_OPENED); 2524 } 2525 2526 static void vc_chr_open(Chardev *chr, 2527 ChardevBackend *backend, 2528 bool *be_opened, 2529 Error **errp) 2530 { 2531 ChardevVC *vc = backend->u.vc.data; 2532 VCChardev *drv = VC_CHARDEV(chr); 2533 QemuConsole *s; 2534 unsigned width = 0; 2535 unsigned height = 0; 2536 2537 if (vc->has_width) { 2538 width = vc->width; 2539 } else if (vc->has_cols) { 2540 width = vc->cols * FONT_WIDTH; 2541 } 2542 2543 if (vc->has_height) { 2544 height = vc->height; 2545 } else if (vc->has_rows) { 2546 height = vc->rows * FONT_HEIGHT; 2547 } 2548 2549 trace_console_txt_new(width, height); 2550 if (width == 0 || height == 0) { 2551 s = new_console(NULL, TEXT_CONSOLE, 0); 2552 } else { 2553 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); 2554 s->scanout.kind = SCANOUT_SURFACE; 2555 s->surface = qemu_create_displaysurface(width, height); 2556 } 2557 2558 if (!s) { 2559 error_setg(errp, "cannot create text console"); 2560 return; 2561 } 2562 2563 s->chr = chr; 2564 drv->console = s; 2565 2566 if (display_state) { 2567 text_console_do_init(chr, display_state); 2568 } 2569 2570 /* console/chardev init sometimes completes elsewhere in a 2nd 2571 * stage, so defer OPENED events until they are fully initialized 2572 */ 2573 *be_opened = false; 2574 } 2575 2576 void qemu_console_resize(QemuConsole *s, int width, int height) 2577 { 2578 DisplaySurface *surface = qemu_console_surface(s); 2579 2580 assert(s->console_type == GRAPHIC_CONSOLE); 2581 2582 if ((s->scanout.kind != SCANOUT_SURFACE || 2583 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) && 2584 qemu_console_get_width(s, -1) == width && 2585 qemu_console_get_height(s, -1) == height) { 2586 return; 2587 } 2588 2589 surface = qemu_create_displaysurface(width, height); 2590 dpy_gfx_replace_surface(s, surface); 2591 } 2592 2593 DisplaySurface *qemu_console_surface(QemuConsole *console) 2594 { 2595 switch (console->scanout.kind) { 2596 case SCANOUT_SURFACE: 2597 return console->surface; 2598 default: 2599 return NULL; 2600 } 2601 } 2602 2603 PixelFormat qemu_default_pixelformat(int bpp) 2604 { 2605 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 2606 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 2607 return pf; 2608 } 2609 2610 static QemuDisplay *dpys[DISPLAY_TYPE__MAX]; 2611 2612 void qemu_display_register(QemuDisplay *ui) 2613 { 2614 assert(ui->type < DISPLAY_TYPE__MAX); 2615 dpys[ui->type] = ui; 2616 } 2617 2618 bool qemu_display_find_default(DisplayOptions *opts) 2619 { 2620 static DisplayType prio[] = { 2621 #if defined(CONFIG_GTK) 2622 DISPLAY_TYPE_GTK, 2623 #endif 2624 #if defined(CONFIG_SDL) 2625 DISPLAY_TYPE_SDL, 2626 #endif 2627 #if defined(CONFIG_COCOA) 2628 DISPLAY_TYPE_COCOA 2629 #endif 2630 }; 2631 int i; 2632 2633 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { 2634 if (dpys[prio[i]] == NULL) { 2635 Error *local_err = NULL; 2636 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err); 2637 if (rv < 0) { 2638 error_report_err(local_err); 2639 } 2640 } 2641 if (dpys[prio[i]] == NULL) { 2642 continue; 2643 } 2644 opts->type = prio[i]; 2645 return true; 2646 } 2647 return false; 2648 } 2649 2650 void qemu_display_early_init(DisplayOptions *opts) 2651 { 2652 assert(opts->type < DISPLAY_TYPE__MAX); 2653 if (opts->type == DISPLAY_TYPE_NONE) { 2654 return; 2655 } 2656 if (dpys[opts->type] == NULL) { 2657 Error *local_err = NULL; 2658 int rv = ui_module_load(DisplayType_str(opts->type), &local_err); 2659 if (rv < 0) { 2660 error_report_err(local_err); 2661 } 2662 } 2663 if (dpys[opts->type] == NULL) { 2664 error_report("Display '%s' is not available.", 2665 DisplayType_str(opts->type)); 2666 exit(1); 2667 } 2668 if (dpys[opts->type]->early_init) { 2669 dpys[opts->type]->early_init(opts); 2670 } 2671 } 2672 2673 void qemu_display_init(DisplayState *ds, DisplayOptions *opts) 2674 { 2675 assert(opts->type < DISPLAY_TYPE__MAX); 2676 if (opts->type == DISPLAY_TYPE_NONE) { 2677 return; 2678 } 2679 assert(dpys[opts->type] != NULL); 2680 dpys[opts->type]->init(ds, opts); 2681 } 2682 2683 void qemu_display_help(void) 2684 { 2685 int idx; 2686 2687 printf("Available display backend types:\n"); 2688 printf("none\n"); 2689 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { 2690 if (!dpys[idx]) { 2691 Error *local_err = NULL; 2692 int rv = ui_module_load(DisplayType_str(idx), &local_err); 2693 if (rv < 0) { 2694 error_report_err(local_err); 2695 } 2696 } 2697 if (dpys[idx]) { 2698 printf("%s\n", DisplayType_str(dpys[idx]->type)); 2699 } 2700 } 2701 } 2702 2703 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) 2704 { 2705 int val; 2706 ChardevVC *vc; 2707 2708 backend->type = CHARDEV_BACKEND_KIND_VC; 2709 vc = backend->u.vc.data = g_new0(ChardevVC, 1); 2710 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); 2711 2712 val = qemu_opt_get_number(opts, "width", 0); 2713 if (val != 0) { 2714 vc->has_width = true; 2715 vc->width = val; 2716 } 2717 2718 val = qemu_opt_get_number(opts, "height", 0); 2719 if (val != 0) { 2720 vc->has_height = true; 2721 vc->height = val; 2722 } 2723 2724 val = qemu_opt_get_number(opts, "cols", 0); 2725 if (val != 0) { 2726 vc->has_cols = true; 2727 vc->cols = val; 2728 } 2729 2730 val = qemu_opt_get_number(opts, "rows", 0); 2731 if (val != 0) { 2732 vc->has_rows = true; 2733 vc->rows = val; 2734 } 2735 } 2736 2737 static const TypeInfo qemu_console_info = { 2738 .name = TYPE_QEMU_CONSOLE, 2739 .parent = TYPE_OBJECT, 2740 .instance_size = sizeof(QemuConsole), 2741 .class_size = sizeof(QemuConsoleClass), 2742 }; 2743 2744 static void char_vc_class_init(ObjectClass *oc, void *data) 2745 { 2746 ChardevClass *cc = CHARDEV_CLASS(oc); 2747 2748 cc->parse = qemu_chr_parse_vc; 2749 cc->open = vc_chr_open; 2750 cc->chr_write = vc_chr_write; 2751 cc->chr_accept_input = vc_chr_accept_input; 2752 cc->chr_set_echo = vc_chr_set_echo; 2753 } 2754 2755 static const TypeInfo char_vc_type_info = { 2756 .name = TYPE_CHARDEV_VC, 2757 .parent = TYPE_CHARDEV, 2758 .instance_size = sizeof(VCChardev), 2759 .class_init = char_vc_class_init, 2760 }; 2761 2762 void qemu_console_early_init(void) 2763 { 2764 /* set the default vc driver */ 2765 if (!object_class_by_name(TYPE_CHARDEV_VC)) { 2766 type_register(&char_vc_type_info); 2767 } 2768 } 2769 2770 static void register_types(void) 2771 { 2772 type_register_static(&qemu_console_info); 2773 } 2774 2775 type_init(register_types); 2776