1 /* 2 * QEMU graphical console 3 * 4 * Copyright (c) 2004 Fabrice Bellard 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include "ui/console.h" 27 #include "hw/qdev-core.h" 28 #include "qapi/error.h" 29 #include "qapi/qapi-commands-ui.h" 30 #include "qemu/coroutine.h" 31 #include "qemu/fifo8.h" 32 #include "qemu/main-loop.h" 33 #include "qemu/module.h" 34 #include "qemu/option.h" 35 #include "qemu/timer.h" 36 #include "chardev/char.h" 37 #include "trace.h" 38 #include "exec/memory.h" 39 #include "io/channel-file.h" 40 #include "qom/object.h" 41 #ifdef CONFIG_PNG 42 #include <png.h> 43 #endif 44 45 #define DEFAULT_BACKSCROLL 512 46 #define CONSOLE_CURSOR_PERIOD 500 47 48 typedef struct TextAttributes { 49 uint8_t fgcol:4; 50 uint8_t bgcol:4; 51 uint8_t bold:1; 52 uint8_t uline:1; 53 uint8_t blink:1; 54 uint8_t invers:1; 55 uint8_t unvisible:1; 56 } TextAttributes; 57 58 typedef struct TextCell { 59 uint8_t ch; 60 TextAttributes t_attrib; 61 } TextCell; 62 63 #define MAX_ESC_PARAMS 3 64 65 enum TTYState { 66 TTY_STATE_NORM, 67 TTY_STATE_ESC, 68 TTY_STATE_CSI, 69 }; 70 71 typedef enum { 72 GRAPHIC_CONSOLE, 73 TEXT_CONSOLE, 74 TEXT_CONSOLE_FIXED_SIZE 75 } console_type_t; 76 77 struct QemuConsole { 78 Object parent; 79 80 int index; 81 console_type_t console_type; 82 DisplayState *ds; 83 DisplaySurface *surface; 84 DisplayScanout scanout; 85 int dcls; 86 DisplayGLCtx *gl; 87 int gl_block; 88 QEMUTimer *gl_unblock_timer; 89 int window_id; 90 91 /* Graphic console state. */ 92 Object *device; 93 uint32_t head; 94 QemuUIInfo ui_info; 95 QEMUTimer *ui_timer; 96 const GraphicHwOps *hw_ops; 97 void *hw; 98 99 /* Text console state */ 100 int width; 101 int height; 102 int total_height; 103 int backscroll_height; 104 int x, y; 105 int x_saved, y_saved; 106 int y_displayed; 107 int y_base; 108 TextAttributes t_attrib_default; /* default text attributes */ 109 TextAttributes t_attrib; /* currently active text attributes */ 110 TextCell *cells; 111 int text_x[2], text_y[2], cursor_invalidate; 112 int echo; 113 114 int update_x0; 115 int update_y0; 116 int update_x1; 117 int update_y1; 118 119 enum TTYState state; 120 int esc_params[MAX_ESC_PARAMS]; 121 int nb_esc_params; 122 123 Chardev *chr; 124 /* fifo for key pressed */ 125 Fifo8 out_fifo; 126 CoQueue dump_queue; 127 128 QTAILQ_ENTRY(QemuConsole) next; 129 }; 130 131 struct DisplayState { 132 QEMUTimer *gui_timer; 133 uint64_t last_update; 134 uint64_t update_interval; 135 bool refreshing; 136 bool have_gfx; 137 bool have_text; 138 139 QLIST_HEAD(, DisplayChangeListener) listeners; 140 }; 141 142 static DisplayState *display_state; 143 static QemuConsole *active_console; 144 static QTAILQ_HEAD(, QemuConsole) consoles = 145 QTAILQ_HEAD_INITIALIZER(consoles); 146 static bool cursor_visible_phase; 147 static QEMUTimer *cursor_timer; 148 149 static void text_console_do_init(Chardev *chr, DisplayState *ds); 150 static void dpy_refresh(DisplayState *s); 151 static DisplayState *get_alloc_displaystate(void); 152 static void text_console_update_cursor_timer(void); 153 static void text_console_update_cursor(void *opaque); 154 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl); 155 static bool console_compatible_with(QemuConsole *con, 156 DisplayChangeListener *dcl, Error **errp); 157 158 static void gui_update(void *opaque) 159 { 160 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE; 161 uint64_t dcl_interval; 162 DisplayState *ds = opaque; 163 DisplayChangeListener *dcl; 164 165 ds->refreshing = true; 166 dpy_refresh(ds); 167 ds->refreshing = false; 168 169 QLIST_FOREACH(dcl, &ds->listeners, next) { 170 dcl_interval = dcl->update_interval ? 171 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT; 172 if (interval > dcl_interval) { 173 interval = dcl_interval; 174 } 175 } 176 if (ds->update_interval != interval) { 177 ds->update_interval = interval; 178 trace_console_refresh(interval); 179 } 180 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 181 timer_mod(ds->gui_timer, ds->last_update + interval); 182 } 183 184 static void gui_setup_refresh(DisplayState *ds) 185 { 186 DisplayChangeListener *dcl; 187 bool need_timer = false; 188 bool have_gfx = false; 189 bool have_text = false; 190 191 QLIST_FOREACH(dcl, &ds->listeners, next) { 192 if (dcl->ops->dpy_refresh != NULL) { 193 need_timer = true; 194 } 195 if (dcl->ops->dpy_gfx_update != NULL) { 196 have_gfx = true; 197 } 198 if (dcl->ops->dpy_text_update != NULL) { 199 have_text = true; 200 } 201 } 202 203 if (need_timer && ds->gui_timer == NULL) { 204 ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds); 205 timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); 206 } 207 if (!need_timer && ds->gui_timer != NULL) { 208 timer_free(ds->gui_timer); 209 ds->gui_timer = NULL; 210 } 211 212 ds->have_gfx = have_gfx; 213 ds->have_text = have_text; 214 } 215 216 void graphic_hw_update_done(QemuConsole *con) 217 { 218 if (con) { 219 qemu_co_enter_all(&con->dump_queue, NULL); 220 } 221 } 222 223 void graphic_hw_update(QemuConsole *con) 224 { 225 bool async = false; 226 con = con ? con : active_console; 227 if (!con) { 228 return; 229 } 230 if (con->hw_ops->gfx_update) { 231 con->hw_ops->gfx_update(con->hw); 232 async = con->hw_ops->gfx_update_async; 233 } 234 if (!async) { 235 graphic_hw_update_done(con); 236 } 237 } 238 239 static void graphic_hw_gl_unblock_timer(void *opaque) 240 { 241 warn_report("console: no gl-unblock within one second"); 242 } 243 244 void graphic_hw_gl_block(QemuConsole *con, bool block) 245 { 246 uint64_t timeout; 247 assert(con != NULL); 248 249 if (block) { 250 con->gl_block++; 251 } else { 252 con->gl_block--; 253 } 254 assert(con->gl_block >= 0); 255 if (!con->hw_ops->gl_block) { 256 return; 257 } 258 if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) { 259 return; 260 } 261 con->hw_ops->gl_block(con->hw, block); 262 263 if (block) { 264 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 265 timeout += 1000; /* one sec */ 266 timer_mod(con->gl_unblock_timer, timeout); 267 } else { 268 timer_del(con->gl_unblock_timer); 269 } 270 } 271 272 int qemu_console_get_window_id(QemuConsole *con) 273 { 274 return con->window_id; 275 } 276 277 void qemu_console_set_window_id(QemuConsole *con, int window_id) 278 { 279 con->window_id = window_id; 280 } 281 282 void graphic_hw_invalidate(QemuConsole *con) 283 { 284 if (!con) { 285 con = active_console; 286 } 287 if (con && con->hw_ops->invalidate) { 288 con->hw_ops->invalidate(con->hw); 289 } 290 } 291 292 #ifdef CONFIG_PNG 293 /** 294 * png_save: Take a screenshot as PNG 295 * 296 * Saves screendump as a PNG file 297 * 298 * Returns true for success or false for error. 299 * 300 * @fd: File descriptor for PNG file. 301 * @image: Image data in pixman format. 302 * @errp: Pointer to an error. 303 */ 304 static bool png_save(int fd, pixman_image_t *image, Error **errp) 305 { 306 int width = pixman_image_get_width(image); 307 int height = pixman_image_get_height(image); 308 png_struct *png_ptr; 309 png_info *info_ptr; 310 g_autoptr(pixman_image_t) linebuf = 311 qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, width); 312 uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); 313 FILE *f = fdopen(fd, "wb"); 314 int y; 315 if (!f) { 316 error_setg_errno(errp, errno, 317 "Failed to create file from file descriptor"); 318 return false; 319 } 320 321 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 322 NULL, NULL); 323 if (!png_ptr) { 324 error_setg(errp, "PNG creation failed. Unable to write struct"); 325 fclose(f); 326 return false; 327 } 328 329 info_ptr = png_create_info_struct(png_ptr); 330 331 if (!info_ptr) { 332 error_setg(errp, "PNG creation failed. Unable to write info"); 333 fclose(f); 334 png_destroy_write_struct(&png_ptr, &info_ptr); 335 return false; 336 } 337 338 png_init_io(png_ptr, f); 339 340 png_set_IHDR(png_ptr, info_ptr, width, height, 8, 341 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, 342 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 343 344 png_write_info(png_ptr, info_ptr); 345 346 for (y = 0; y < height; ++y) { 347 qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); 348 png_write_row(png_ptr, buf); 349 } 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, 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 (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, 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_TAB] = QEMU_KEY_TAB, 1372 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE, 1373 }; 1374 1375 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = { 1376 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP, 1377 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN, 1378 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT, 1379 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT, 1380 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME, 1381 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END, 1382 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP, 1383 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN, 1384 }; 1385 1386 bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl) 1387 { 1388 int keysym; 1389 1390 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode]; 1391 if (keysym == 0) { 1392 return false; 1393 } 1394 kbd_put_keysym_console(s, keysym); 1395 return true; 1396 } 1397 1398 void kbd_put_string_console(QemuConsole *s, const char *str, int len) 1399 { 1400 int i; 1401 1402 for (i = 0; i < len && str[i]; i++) { 1403 kbd_put_keysym_console(s, str[i]); 1404 } 1405 } 1406 1407 void kbd_put_keysym(int keysym) 1408 { 1409 kbd_put_keysym_console(active_console, keysym); 1410 } 1411 1412 static void text_console_invalidate(void *opaque) 1413 { 1414 QemuConsole *s = (QemuConsole *) opaque; 1415 1416 if (s->ds->have_text && s->console_type == TEXT_CONSOLE) { 1417 text_console_resize(s); 1418 } 1419 console_refresh(s); 1420 } 1421 1422 static void text_console_update(void *opaque, console_ch_t *chardata) 1423 { 1424 QemuConsole *s = (QemuConsole *) opaque; 1425 int i, j, src; 1426 1427 if (s->text_x[0] <= s->text_x[1]) { 1428 src = (s->y_base + s->text_y[0]) * s->width; 1429 chardata += s->text_y[0] * s->width; 1430 for (i = s->text_y[0]; i <= s->text_y[1]; i ++) 1431 for (j = 0; j < s->width; j++, src++) { 1432 console_write_ch(chardata ++, 1433 ATTR2CHTYPE(s->cells[src].ch, 1434 s->cells[src].t_attrib.fgcol, 1435 s->cells[src].t_attrib.bgcol, 1436 s->cells[src].t_attrib.bold)); 1437 } 1438 dpy_text_update(s, s->text_x[0], s->text_y[0], 1439 s->text_x[1] - s->text_x[0], i - s->text_y[0]); 1440 s->text_x[0] = s->width; 1441 s->text_y[0] = s->height; 1442 s->text_x[1] = 0; 1443 s->text_y[1] = 0; 1444 } 1445 if (s->cursor_invalidate) { 1446 dpy_text_cursor(s, s->x, s->y); 1447 s->cursor_invalidate = 0; 1448 } 1449 } 1450 1451 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, 1452 uint32_t head) 1453 { 1454 Object *obj; 1455 QemuConsole *s; 1456 int i; 1457 1458 obj = object_new(TYPE_QEMU_CONSOLE); 1459 s = QEMU_CONSOLE(obj); 1460 qemu_co_queue_init(&s->dump_queue); 1461 s->head = head; 1462 object_property_add_link(obj, "device", TYPE_DEVICE, 1463 (Object **)&s->device, 1464 object_property_allow_set_link, 1465 OBJ_PROP_LINK_STRONG); 1466 object_property_add_uint32_ptr(obj, "head", &s->head, 1467 OBJ_PROP_FLAG_READ); 1468 1469 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && 1470 (console_type == GRAPHIC_CONSOLE))) { 1471 active_console = s; 1472 } 1473 s->ds = ds; 1474 s->console_type = console_type; 1475 s->window_id = -1; 1476 1477 if (QTAILQ_EMPTY(&consoles)) { 1478 s->index = 0; 1479 QTAILQ_INSERT_TAIL(&consoles, s, next); 1480 } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) { 1481 QemuConsole *last = QTAILQ_LAST(&consoles); 1482 s->index = last->index + 1; 1483 QTAILQ_INSERT_TAIL(&consoles, s, next); 1484 } else { 1485 /* 1486 * HACK: Put graphical consoles before text consoles. 1487 * 1488 * Only do that for coldplugged devices. After initial device 1489 * initialization we will not renumber the consoles any more. 1490 */ 1491 QemuConsole *c = QTAILQ_FIRST(&consoles); 1492 1493 while (QTAILQ_NEXT(c, next) != NULL && 1494 c->console_type == GRAPHIC_CONSOLE) { 1495 c = QTAILQ_NEXT(c, next); 1496 } 1497 if (c->console_type == GRAPHIC_CONSOLE) { 1498 /* have no text consoles */ 1499 s->index = c->index + 1; 1500 QTAILQ_INSERT_AFTER(&consoles, c, s, next); 1501 } else { 1502 s->index = c->index; 1503 QTAILQ_INSERT_BEFORE(c, s, next); 1504 /* renumber text consoles */ 1505 for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) { 1506 c->index = i; 1507 } 1508 } 1509 } 1510 return s; 1511 } 1512 1513 DisplaySurface *qemu_create_displaysurface(int width, int height) 1514 { 1515 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1516 1517 trace_displaysurface_create(surface, width, height); 1518 surface->format = PIXMAN_x8r8g8b8; 1519 surface->image = pixman_image_create_bits(surface->format, 1520 width, height, 1521 NULL, width * 4); 1522 assert(surface->image != NULL); 1523 surface->flags = QEMU_ALLOCATED_FLAG; 1524 1525 return surface; 1526 } 1527 1528 DisplaySurface *qemu_create_displaysurface_from(int width, int height, 1529 pixman_format_code_t format, 1530 int linesize, uint8_t *data) 1531 { 1532 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1533 1534 trace_displaysurface_create_from(surface, width, height, format); 1535 surface->format = format; 1536 surface->image = pixman_image_create_bits(surface->format, 1537 width, height, 1538 (void *)data, linesize); 1539 assert(surface->image != NULL); 1540 1541 return surface; 1542 } 1543 1544 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) 1545 { 1546 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1547 1548 trace_displaysurface_create_pixman(surface); 1549 surface->format = pixman_image_get_format(image); 1550 surface->image = pixman_image_ref(image); 1551 1552 return surface; 1553 } 1554 1555 DisplaySurface *qemu_create_placeholder_surface(int w, int h, 1556 const char *msg) 1557 { 1558 DisplaySurface *surface = qemu_create_displaysurface(w, h); 1559 pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK]; 1560 pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE]; 1561 pixman_image_t *glyph; 1562 int len, x, y, i; 1563 1564 len = strlen(msg); 1565 x = (w / FONT_WIDTH - len) / 2; 1566 y = (h / FONT_HEIGHT - 1) / 2; 1567 for (i = 0; i < len; i++) { 1568 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]); 1569 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg, 1570 x+i, y, FONT_WIDTH, FONT_HEIGHT); 1571 qemu_pixman_image_unref(glyph); 1572 } 1573 surface->flags |= QEMU_PLACEHOLDER_FLAG; 1574 return surface; 1575 } 1576 1577 void qemu_free_displaysurface(DisplaySurface *surface) 1578 { 1579 if (surface == NULL) { 1580 return; 1581 } 1582 trace_displaysurface_free(surface); 1583 qemu_pixman_image_unref(surface->image); 1584 g_free(surface); 1585 } 1586 1587 bool console_has_gl(QemuConsole *con) 1588 { 1589 return con->gl != NULL; 1590 } 1591 1592 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) 1593 { 1594 if (dcl->ops->dpy_has_dmabuf) { 1595 return dcl->ops->dpy_has_dmabuf(dcl); 1596 } 1597 1598 if (dcl->ops->dpy_gl_scanout_dmabuf) { 1599 return true; 1600 } 1601 1602 return false; 1603 } 1604 1605 static bool console_compatible_with(QemuConsole *con, 1606 DisplayChangeListener *dcl, Error **errp) 1607 { 1608 int flags; 1609 1610 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; 1611 1612 if (console_has_gl(con) && 1613 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) { 1614 error_setg(errp, "Display %s is incompatible with the GL context", 1615 dcl->ops->dpy_name); 1616 return false; 1617 } 1618 1619 if (flags & GRAPHIC_FLAGS_GL && 1620 !console_has_gl(con)) { 1621 error_setg(errp, "The console requires a GL context."); 1622 return false; 1623 1624 } 1625 1626 if (flags & GRAPHIC_FLAGS_DMABUF && 1627 !displaychangelistener_has_dmabuf(dcl)) { 1628 error_setg(errp, "The console requires display DMABUF support."); 1629 return false; 1630 } 1631 1632 return true; 1633 } 1634 1635 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) 1636 { 1637 /* display has opengl support */ 1638 assert(con); 1639 if (con->gl) { 1640 error_report("The console already has an OpenGL context."); 1641 exit(1); 1642 } 1643 con->gl = gl; 1644 } 1645 1646 void register_displaychangelistener(DisplayChangeListener *dcl) 1647 { 1648 QemuConsole *con; 1649 1650 assert(!dcl->ds); 1651 1652 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); 1653 dcl->ds = get_alloc_displaystate(); 1654 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); 1655 gui_setup_refresh(dcl->ds); 1656 if (dcl->con) { 1657 dcl->con->dcls++; 1658 con = dcl->con; 1659 } else { 1660 con = active_console; 1661 } 1662 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL); 1663 text_console_update_cursor(NULL); 1664 } 1665 1666 void update_displaychangelistener(DisplayChangeListener *dcl, 1667 uint64_t interval) 1668 { 1669 DisplayState *ds = dcl->ds; 1670 1671 dcl->update_interval = interval; 1672 if (!ds->refreshing && ds->update_interval > interval) { 1673 timer_mod(ds->gui_timer, ds->last_update + interval); 1674 } 1675 } 1676 1677 void unregister_displaychangelistener(DisplayChangeListener *dcl) 1678 { 1679 DisplayState *ds = dcl->ds; 1680 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name); 1681 if (dcl->con) { 1682 dcl->con->dcls--; 1683 } 1684 QLIST_REMOVE(dcl, next); 1685 dcl->ds = NULL; 1686 gui_setup_refresh(ds); 1687 } 1688 1689 static void dpy_set_ui_info_timer(void *opaque) 1690 { 1691 QemuConsole *con = opaque; 1692 1693 con->hw_ops->ui_info(con->hw, con->head, &con->ui_info); 1694 } 1695 1696 bool dpy_ui_info_supported(QemuConsole *con) 1697 { 1698 if (con == NULL) { 1699 con = active_console; 1700 } 1701 1702 return con->hw_ops->ui_info != NULL; 1703 } 1704 1705 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con) 1706 { 1707 if (con == NULL) { 1708 con = active_console; 1709 } 1710 1711 return &con->ui_info; 1712 } 1713 1714 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay) 1715 { 1716 if (con == NULL) { 1717 con = active_console; 1718 } 1719 1720 if (!dpy_ui_info_supported(con)) { 1721 return -1; 1722 } 1723 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) { 1724 /* nothing changed -- ignore */ 1725 return 0; 1726 } 1727 1728 /* 1729 * Typically we get a flood of these as the user resizes the window. 1730 * Wait until the dust has settled (one second without updates), then 1731 * go notify the guest. 1732 */ 1733 con->ui_info = *info; 1734 timer_mod(con->ui_timer, 1735 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0)); 1736 return 0; 1737 } 1738 1739 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 1740 { 1741 DisplayState *s = con->ds; 1742 DisplayChangeListener *dcl; 1743 int width = qemu_console_get_width(con, x + w); 1744 int height = qemu_console_get_height(con, y + h); 1745 1746 x = MAX(x, 0); 1747 y = MAX(y, 0); 1748 x = MIN(x, width); 1749 y = MIN(y, height); 1750 w = MIN(w, width - x); 1751 h = MIN(h, height - y); 1752 1753 if (!qemu_console_is_visible(con)) { 1754 return; 1755 } 1756 dpy_gfx_update_texture(con, con->surface, x, y, w, h); 1757 QLIST_FOREACH(dcl, &s->listeners, next) { 1758 if (con != (dcl->con ? dcl->con : active_console)) { 1759 continue; 1760 } 1761 if (dcl->ops->dpy_gfx_update) { 1762 dcl->ops->dpy_gfx_update(dcl, x, y, w, h); 1763 } 1764 } 1765 } 1766 1767 void dpy_gfx_update_full(QemuConsole *con) 1768 { 1769 int w = qemu_console_get_width(con, 0); 1770 int h = qemu_console_get_height(con, 0); 1771 1772 dpy_gfx_update(con, 0, 0, w, h); 1773 } 1774 1775 void dpy_gfx_replace_surface(QemuConsole *con, 1776 DisplaySurface *surface) 1777 { 1778 static const char placeholder_msg[] = "Display output is not active."; 1779 DisplayState *s = con->ds; 1780 DisplaySurface *old_surface = con->surface; 1781 DisplayChangeListener *dcl; 1782 int width; 1783 int height; 1784 1785 if (!surface) { 1786 if (old_surface) { 1787 width = surface_width(old_surface); 1788 height = surface_height(old_surface); 1789 } else { 1790 width = 640; 1791 height = 480; 1792 } 1793 1794 surface = qemu_create_placeholder_surface(width, height, placeholder_msg); 1795 } 1796 1797 assert(old_surface != surface); 1798 1799 con->scanout.kind = SCANOUT_SURFACE; 1800 con->surface = surface; 1801 dpy_gfx_create_texture(con, surface); 1802 QLIST_FOREACH(dcl, &s->listeners, next) { 1803 if (con != (dcl->con ? dcl->con : active_console)) { 1804 continue; 1805 } 1806 displaychangelistener_gfx_switch(dcl, surface, FALSE); 1807 } 1808 dpy_gfx_destroy_texture(con, old_surface); 1809 qemu_free_displaysurface(old_surface); 1810 } 1811 1812 bool dpy_gfx_check_format(QemuConsole *con, 1813 pixman_format_code_t format) 1814 { 1815 DisplayChangeListener *dcl; 1816 DisplayState *s = con->ds; 1817 1818 QLIST_FOREACH(dcl, &s->listeners, next) { 1819 if (dcl->con && dcl->con != con) { 1820 /* dcl bound to another console -> skip */ 1821 continue; 1822 } 1823 if (dcl->ops->dpy_gfx_check_format) { 1824 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) { 1825 return false; 1826 } 1827 } else { 1828 /* default is to allow native 32 bpp only */ 1829 if (format != qemu_default_pixman_format(32, true)) { 1830 return false; 1831 } 1832 } 1833 } 1834 return true; 1835 } 1836 1837 static void dpy_refresh(DisplayState *s) 1838 { 1839 DisplayChangeListener *dcl; 1840 1841 QLIST_FOREACH(dcl, &s->listeners, next) { 1842 if (dcl->ops->dpy_refresh) { 1843 dcl->ops->dpy_refresh(dcl); 1844 } 1845 } 1846 } 1847 1848 void dpy_text_cursor(QemuConsole *con, int x, int y) 1849 { 1850 DisplayState *s = con->ds; 1851 DisplayChangeListener *dcl; 1852 1853 if (!qemu_console_is_visible(con)) { 1854 return; 1855 } 1856 QLIST_FOREACH(dcl, &s->listeners, next) { 1857 if (con != (dcl->con ? dcl->con : active_console)) { 1858 continue; 1859 } 1860 if (dcl->ops->dpy_text_cursor) { 1861 dcl->ops->dpy_text_cursor(dcl, x, y); 1862 } 1863 } 1864 } 1865 1866 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) 1867 { 1868 DisplayState *s = con->ds; 1869 DisplayChangeListener *dcl; 1870 1871 if (!qemu_console_is_visible(con)) { 1872 return; 1873 } 1874 QLIST_FOREACH(dcl, &s->listeners, next) { 1875 if (con != (dcl->con ? dcl->con : active_console)) { 1876 continue; 1877 } 1878 if (dcl->ops->dpy_text_update) { 1879 dcl->ops->dpy_text_update(dcl, x, y, w, h); 1880 } 1881 } 1882 } 1883 1884 void dpy_text_resize(QemuConsole *con, int w, int h) 1885 { 1886 DisplayState *s = con->ds; 1887 DisplayChangeListener *dcl; 1888 1889 if (!qemu_console_is_visible(con)) { 1890 return; 1891 } 1892 QLIST_FOREACH(dcl, &s->listeners, next) { 1893 if (con != (dcl->con ? dcl->con : active_console)) { 1894 continue; 1895 } 1896 if (dcl->ops->dpy_text_resize) { 1897 dcl->ops->dpy_text_resize(dcl, w, h); 1898 } 1899 } 1900 } 1901 1902 void dpy_mouse_set(QemuConsole *con, int x, int y, int on) 1903 { 1904 DisplayState *s = con->ds; 1905 DisplayChangeListener *dcl; 1906 1907 if (!qemu_console_is_visible(con)) { 1908 return; 1909 } 1910 QLIST_FOREACH(dcl, &s->listeners, next) { 1911 if (con != (dcl->con ? dcl->con : active_console)) { 1912 continue; 1913 } 1914 if (dcl->ops->dpy_mouse_set) { 1915 dcl->ops->dpy_mouse_set(dcl, x, y, on); 1916 } 1917 } 1918 } 1919 1920 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) 1921 { 1922 DisplayState *s = con->ds; 1923 DisplayChangeListener *dcl; 1924 1925 if (!qemu_console_is_visible(con)) { 1926 return; 1927 } 1928 QLIST_FOREACH(dcl, &s->listeners, next) { 1929 if (con != (dcl->con ? dcl->con : active_console)) { 1930 continue; 1931 } 1932 if (dcl->ops->dpy_cursor_define) { 1933 dcl->ops->dpy_cursor_define(dcl, cursor); 1934 } 1935 } 1936 } 1937 1938 bool dpy_cursor_define_supported(QemuConsole *con) 1939 { 1940 DisplayState *s = con->ds; 1941 DisplayChangeListener *dcl; 1942 1943 QLIST_FOREACH(dcl, &s->listeners, next) { 1944 if (dcl->ops->dpy_cursor_define) { 1945 return true; 1946 } 1947 } 1948 return false; 1949 } 1950 1951 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, 1952 struct QEMUGLParams *qparams) 1953 { 1954 assert(con->gl); 1955 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams); 1956 } 1957 1958 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx) 1959 { 1960 assert(con->gl); 1961 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx); 1962 } 1963 1964 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) 1965 { 1966 assert(con->gl); 1967 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); 1968 } 1969 1970 void dpy_gl_scanout_disable(QemuConsole *con) 1971 { 1972 DisplayState *s = con->ds; 1973 DisplayChangeListener *dcl; 1974 1975 if (con->scanout.kind != SCANOUT_SURFACE) { 1976 con->scanout.kind = SCANOUT_NONE; 1977 } 1978 QLIST_FOREACH(dcl, &s->listeners, next) { 1979 if (con != (dcl->con ? dcl->con : active_console)) { 1980 continue; 1981 } 1982 if (dcl->ops->dpy_gl_scanout_disable) { 1983 dcl->ops->dpy_gl_scanout_disable(dcl); 1984 } 1985 } 1986 } 1987 1988 void dpy_gl_scanout_texture(QemuConsole *con, 1989 uint32_t backing_id, 1990 bool backing_y_0_top, 1991 uint32_t backing_width, 1992 uint32_t backing_height, 1993 uint32_t x, uint32_t y, 1994 uint32_t width, uint32_t height) 1995 { 1996 DisplayState *s = con->ds; 1997 DisplayChangeListener *dcl; 1998 1999 con->scanout.kind = SCANOUT_TEXTURE; 2000 con->scanout.texture = (ScanoutTexture) { 2001 backing_id, backing_y_0_top, backing_width, backing_height, 2002 x, y, width, height 2003 }; 2004 QLIST_FOREACH(dcl, &s->listeners, next) { 2005 if (con != (dcl->con ? dcl->con : active_console)) { 2006 continue; 2007 } 2008 if (dcl->ops->dpy_gl_scanout_texture) { 2009 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, 2010 backing_y_0_top, 2011 backing_width, backing_height, 2012 x, y, width, height); 2013 } 2014 } 2015 } 2016 2017 void dpy_gl_scanout_dmabuf(QemuConsole *con, 2018 QemuDmaBuf *dmabuf) 2019 { 2020 DisplayState *s = con->ds; 2021 DisplayChangeListener *dcl; 2022 2023 con->scanout.kind = SCANOUT_DMABUF; 2024 con->scanout.dmabuf = dmabuf; 2025 QLIST_FOREACH(dcl, &s->listeners, next) { 2026 if (con != (dcl->con ? dcl->con : active_console)) { 2027 continue; 2028 } 2029 if (dcl->ops->dpy_gl_scanout_dmabuf) { 2030 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); 2031 } 2032 } 2033 } 2034 2035 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, 2036 bool have_hot, uint32_t hot_x, uint32_t hot_y) 2037 { 2038 DisplayState *s = con->ds; 2039 DisplayChangeListener *dcl; 2040 2041 QLIST_FOREACH(dcl, &s->listeners, next) { 2042 if (con != (dcl->con ? dcl->con : active_console)) { 2043 continue; 2044 } 2045 if (dcl->ops->dpy_gl_cursor_dmabuf) { 2046 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf, 2047 have_hot, hot_x, hot_y); 2048 } 2049 } 2050 } 2051 2052 void dpy_gl_cursor_position(QemuConsole *con, 2053 uint32_t pos_x, uint32_t pos_y) 2054 { 2055 DisplayState *s = con->ds; 2056 DisplayChangeListener *dcl; 2057 2058 QLIST_FOREACH(dcl, &s->listeners, next) { 2059 if (con != (dcl->con ? dcl->con : active_console)) { 2060 continue; 2061 } 2062 if (dcl->ops->dpy_gl_cursor_position) { 2063 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y); 2064 } 2065 } 2066 } 2067 2068 void dpy_gl_release_dmabuf(QemuConsole *con, 2069 QemuDmaBuf *dmabuf) 2070 { 2071 DisplayState *s = con->ds; 2072 DisplayChangeListener *dcl; 2073 2074 QLIST_FOREACH(dcl, &s->listeners, next) { 2075 if (con != (dcl->con ? dcl->con : active_console)) { 2076 continue; 2077 } 2078 if (dcl->ops->dpy_gl_release_dmabuf) { 2079 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf); 2080 } 2081 } 2082 } 2083 2084 void dpy_gl_update(QemuConsole *con, 2085 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 2086 { 2087 DisplayState *s = con->ds; 2088 DisplayChangeListener *dcl; 2089 2090 assert(con->gl); 2091 2092 graphic_hw_gl_block(con, true); 2093 QLIST_FOREACH(dcl, &s->listeners, next) { 2094 if (con != (dcl->con ? dcl->con : active_console)) { 2095 continue; 2096 } 2097 if (dcl->ops->dpy_gl_update) { 2098 dcl->ops->dpy_gl_update(dcl, x, y, w, h); 2099 } 2100 } 2101 graphic_hw_gl_block(con, false); 2102 } 2103 2104 /***********************************************************/ 2105 /* register display */ 2106 2107 /* console.c internal use only */ 2108 static DisplayState *get_alloc_displaystate(void) 2109 { 2110 if (!display_state) { 2111 display_state = g_new0(DisplayState, 1); 2112 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2113 text_console_update_cursor, NULL); 2114 } 2115 return display_state; 2116 } 2117 2118 /* 2119 * Called by main(), after creating QemuConsoles 2120 * and before initializing ui (sdl/vnc/...). 2121 */ 2122 DisplayState *init_displaystate(void) 2123 { 2124 gchar *name; 2125 QemuConsole *con; 2126 2127 get_alloc_displaystate(); 2128 QTAILQ_FOREACH(con, &consoles, next) { 2129 if (con->console_type != GRAPHIC_CONSOLE && 2130 con->ds == NULL) { 2131 text_console_do_init(con->chr, display_state); 2132 } 2133 2134 /* Hook up into the qom tree here (not in new_console()), once 2135 * all QemuConsoles are created and the order / numbering 2136 * doesn't change any more */ 2137 name = g_strdup_printf("console[%d]", con->index); 2138 object_property_add_child(container_get(object_get_root(), "/backend"), 2139 name, OBJECT(con)); 2140 g_free(name); 2141 } 2142 2143 return display_state; 2144 } 2145 2146 void graphic_console_set_hwops(QemuConsole *con, 2147 const GraphicHwOps *hw_ops, 2148 void *opaque) 2149 { 2150 con->hw_ops = hw_ops; 2151 con->hw = opaque; 2152 } 2153 2154 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 2155 const GraphicHwOps *hw_ops, 2156 void *opaque) 2157 { 2158 static const char noinit[] = 2159 "Guest has not initialized the display (yet)."; 2160 int width = 640; 2161 int height = 480; 2162 QemuConsole *s; 2163 DisplayState *ds; 2164 DisplaySurface *surface; 2165 2166 ds = get_alloc_displaystate(); 2167 s = qemu_console_lookup_unused(); 2168 if (s) { 2169 trace_console_gfx_reuse(s->index); 2170 width = qemu_console_get_width(s, 0); 2171 height = qemu_console_get_height(s, 0); 2172 } else { 2173 trace_console_gfx_new(); 2174 s = new_console(ds, GRAPHIC_CONSOLE, head); 2175 s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2176 dpy_set_ui_info_timer, s); 2177 } 2178 graphic_console_set_hwops(s, hw_ops, opaque); 2179 if (dev) { 2180 object_property_set_link(OBJECT(s), "device", OBJECT(dev), 2181 &error_abort); 2182 } 2183 2184 surface = qemu_create_placeholder_surface(width, height, noinit); 2185 dpy_gfx_replace_surface(s, surface); 2186 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2187 graphic_hw_gl_unblock_timer, s); 2188 return s; 2189 } 2190 2191 static const GraphicHwOps unused_ops = { 2192 /* no callbacks */ 2193 }; 2194 2195 void graphic_console_close(QemuConsole *con) 2196 { 2197 static const char unplugged[] = 2198 "Guest display has been unplugged"; 2199 DisplaySurface *surface; 2200 int width = qemu_console_get_width(con, 640); 2201 int height = qemu_console_get_height(con, 480); 2202 2203 trace_console_gfx_close(con->index); 2204 object_property_set_link(OBJECT(con), "device", NULL, &error_abort); 2205 graphic_console_set_hwops(con, &unused_ops, NULL); 2206 2207 if (con->gl) { 2208 dpy_gl_scanout_disable(con); 2209 } 2210 surface = qemu_create_placeholder_surface(width, height, unplugged); 2211 dpy_gfx_replace_surface(con, surface); 2212 } 2213 2214 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 2215 { 2216 QemuConsole *con; 2217 2218 QTAILQ_FOREACH(con, &consoles, next) { 2219 if (con->index == index) { 2220 return con; 2221 } 2222 } 2223 return NULL; 2224 } 2225 2226 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 2227 { 2228 QemuConsole *con; 2229 Object *obj; 2230 uint32_t h; 2231 2232 QTAILQ_FOREACH(con, &consoles, next) { 2233 obj = object_property_get_link(OBJECT(con), 2234 "device", &error_abort); 2235 if (DEVICE(obj) != dev) { 2236 continue; 2237 } 2238 h = object_property_get_uint(OBJECT(con), 2239 "head", &error_abort); 2240 if (h != head) { 2241 continue; 2242 } 2243 return con; 2244 } 2245 return NULL; 2246 } 2247 2248 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, 2249 uint32_t head, Error **errp) 2250 { 2251 DeviceState *dev; 2252 QemuConsole *con; 2253 2254 dev = qdev_find_recursive(sysbus_get_default(), device_id); 2255 if (dev == NULL) { 2256 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 2257 "Device '%s' not found", device_id); 2258 return NULL; 2259 } 2260 2261 con = qemu_console_lookup_by_device(dev, head); 2262 if (con == NULL) { 2263 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", 2264 device_id, head); 2265 return NULL; 2266 } 2267 2268 return con; 2269 } 2270 2271 QemuConsole *qemu_console_lookup_unused(void) 2272 { 2273 QemuConsole *con; 2274 Object *obj; 2275 2276 QTAILQ_FOREACH(con, &consoles, next) { 2277 if (con->hw_ops != &unused_ops) { 2278 continue; 2279 } 2280 obj = object_property_get_link(OBJECT(con), 2281 "device", &error_abort); 2282 if (obj != NULL) { 2283 continue; 2284 } 2285 return con; 2286 } 2287 return NULL; 2288 } 2289 2290 bool qemu_console_is_visible(QemuConsole *con) 2291 { 2292 return (con == active_console) || (con->dcls > 0); 2293 } 2294 2295 bool qemu_console_is_graphic(QemuConsole *con) 2296 { 2297 if (con == NULL) { 2298 con = active_console; 2299 } 2300 return con && (con->console_type == GRAPHIC_CONSOLE); 2301 } 2302 2303 bool qemu_console_is_fixedsize(QemuConsole *con) 2304 { 2305 if (con == NULL) { 2306 con = active_console; 2307 } 2308 return con && (con->console_type != TEXT_CONSOLE); 2309 } 2310 2311 bool qemu_console_is_gl_blocked(QemuConsole *con) 2312 { 2313 assert(con != NULL); 2314 return con->gl_block; 2315 } 2316 2317 bool qemu_console_is_multihead(DeviceState *dev) 2318 { 2319 QemuConsole *con; 2320 Object *obj; 2321 uint32_t f = 0xffffffff; 2322 uint32_t h; 2323 2324 QTAILQ_FOREACH(con, &consoles, next) { 2325 obj = object_property_get_link(OBJECT(con), 2326 "device", &error_abort); 2327 if (DEVICE(obj) != dev) { 2328 continue; 2329 } 2330 2331 h = object_property_get_uint(OBJECT(con), 2332 "head", &error_abort); 2333 if (f == 0xffffffff) { 2334 f = h; 2335 } else if (h != f) { 2336 return true; 2337 } 2338 } 2339 return false; 2340 } 2341 2342 char *qemu_console_get_label(QemuConsole *con) 2343 { 2344 if (con->console_type == GRAPHIC_CONSOLE) { 2345 if (con->device) { 2346 DeviceState *dev; 2347 bool multihead; 2348 2349 dev = DEVICE(con->device); 2350 multihead = qemu_console_is_multihead(dev); 2351 if (multihead) { 2352 return g_strdup_printf("%s.%d", dev->id ? 2353 dev->id : 2354 object_get_typename(con->device), 2355 con->head); 2356 } else { 2357 return g_strdup_printf("%s", dev->id ? 2358 dev->id : 2359 object_get_typename(con->device)); 2360 } 2361 } 2362 return g_strdup("VGA"); 2363 } else { 2364 if (con->chr && con->chr->label) { 2365 return g_strdup(con->chr->label); 2366 } 2367 return g_strdup_printf("vc%d", con->index); 2368 } 2369 } 2370 2371 int qemu_console_get_index(QemuConsole *con) 2372 { 2373 if (con == NULL) { 2374 con = active_console; 2375 } 2376 return con ? con->index : -1; 2377 } 2378 2379 uint32_t qemu_console_get_head(QemuConsole *con) 2380 { 2381 if (con == NULL) { 2382 con = active_console; 2383 } 2384 return con ? con->head : -1; 2385 } 2386 2387 int qemu_console_get_width(QemuConsole *con, int fallback) 2388 { 2389 if (con == NULL) { 2390 con = active_console; 2391 } 2392 if (con == NULL) { 2393 return fallback; 2394 } 2395 switch (con->scanout.kind) { 2396 case SCANOUT_DMABUF: 2397 return con->scanout.dmabuf->width; 2398 case SCANOUT_TEXTURE: 2399 return con->scanout.texture.width; 2400 case SCANOUT_SURFACE: 2401 return surface_width(con->surface); 2402 default: 2403 return fallback; 2404 } 2405 } 2406 2407 int qemu_console_get_height(QemuConsole *con, int fallback) 2408 { 2409 if (con == NULL) { 2410 con = active_console; 2411 } 2412 if (con == NULL) { 2413 return fallback; 2414 } 2415 switch (con->scanout.kind) { 2416 case SCANOUT_DMABUF: 2417 return con->scanout.dmabuf->height; 2418 case SCANOUT_TEXTURE: 2419 return con->scanout.texture.height; 2420 case SCANOUT_SURFACE: 2421 return surface_height(con->surface); 2422 default: 2423 return fallback; 2424 } 2425 } 2426 2427 static void vc_chr_accept_input(Chardev *chr) 2428 { 2429 VCChardev *drv = VC_CHARDEV(chr); 2430 QemuConsole *s = drv->console; 2431 2432 kbd_send_chars(s); 2433 } 2434 2435 static void vc_chr_set_echo(Chardev *chr, bool echo) 2436 { 2437 VCChardev *drv = VC_CHARDEV(chr); 2438 QemuConsole *s = drv->console; 2439 2440 s->echo = echo; 2441 } 2442 2443 static void text_console_update_cursor_timer(void) 2444 { 2445 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) 2446 + CONSOLE_CURSOR_PERIOD / 2); 2447 } 2448 2449 static void text_console_update_cursor(void *opaque) 2450 { 2451 QemuConsole *s; 2452 int count = 0; 2453 2454 cursor_visible_phase = !cursor_visible_phase; 2455 2456 QTAILQ_FOREACH(s, &consoles, next) { 2457 if (qemu_console_is_graphic(s) || 2458 !qemu_console_is_visible(s)) { 2459 continue; 2460 } 2461 count++; 2462 graphic_hw_invalidate(s); 2463 } 2464 2465 if (count) { 2466 text_console_update_cursor_timer(); 2467 } 2468 } 2469 2470 static const GraphicHwOps text_console_ops = { 2471 .invalidate = text_console_invalidate, 2472 .text_update = text_console_update, 2473 }; 2474 2475 static void text_console_do_init(Chardev *chr, DisplayState *ds) 2476 { 2477 VCChardev *drv = VC_CHARDEV(chr); 2478 QemuConsole *s = drv->console; 2479 int g_width = 80 * FONT_WIDTH; 2480 int g_height = 24 * FONT_HEIGHT; 2481 2482 fifo8_create(&s->out_fifo, 16); 2483 s->ds = ds; 2484 2485 s->y_displayed = 0; 2486 s->y_base = 0; 2487 s->total_height = DEFAULT_BACKSCROLL; 2488 s->x = 0; 2489 s->y = 0; 2490 if (s->scanout.kind != SCANOUT_SURFACE) { 2491 if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) { 2492 g_width = qemu_console_get_width(active_console, g_width); 2493 g_height = qemu_console_get_height(active_console, g_height); 2494 } 2495 s->surface = qemu_create_displaysurface(g_width, g_height); 2496 s->scanout.kind = SCANOUT_SURFACE; 2497 } 2498 2499 s->hw_ops = &text_console_ops; 2500 s->hw = s; 2501 2502 /* Set text attribute defaults */ 2503 s->t_attrib_default.bold = 0; 2504 s->t_attrib_default.uline = 0; 2505 s->t_attrib_default.blink = 0; 2506 s->t_attrib_default.invers = 0; 2507 s->t_attrib_default.unvisible = 0; 2508 s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; 2509 s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; 2510 /* set current text attributes to default */ 2511 s->t_attrib = s->t_attrib_default; 2512 text_console_resize(s); 2513 2514 if (chr->label) { 2515 char *msg; 2516 2517 s->t_attrib.bgcol = QEMU_COLOR_BLUE; 2518 msg = g_strdup_printf("%s console\r\n", chr->label); 2519 vc_chr_write(chr, (uint8_t *)msg, strlen(msg)); 2520 g_free(msg); 2521 s->t_attrib = s->t_attrib_default; 2522 } 2523 2524 qemu_chr_be_event(chr, CHR_EVENT_OPENED); 2525 } 2526 2527 static void vc_chr_open(Chardev *chr, 2528 ChardevBackend *backend, 2529 bool *be_opened, 2530 Error **errp) 2531 { 2532 ChardevVC *vc = backend->u.vc.data; 2533 VCChardev *drv = VC_CHARDEV(chr); 2534 QemuConsole *s; 2535 unsigned width = 0; 2536 unsigned height = 0; 2537 2538 if (vc->has_width) { 2539 width = vc->width; 2540 } else if (vc->has_cols) { 2541 width = vc->cols * FONT_WIDTH; 2542 } 2543 2544 if (vc->has_height) { 2545 height = vc->height; 2546 } else if (vc->has_rows) { 2547 height = vc->rows * FONT_HEIGHT; 2548 } 2549 2550 trace_console_txt_new(width, height); 2551 if (width == 0 || height == 0) { 2552 s = new_console(NULL, TEXT_CONSOLE, 0); 2553 } else { 2554 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); 2555 s->scanout.kind = SCANOUT_SURFACE; 2556 s->surface = qemu_create_displaysurface(width, height); 2557 } 2558 2559 if (!s) { 2560 error_setg(errp, "cannot create text console"); 2561 return; 2562 } 2563 2564 s->chr = chr; 2565 drv->console = s; 2566 2567 if (display_state) { 2568 text_console_do_init(chr, display_state); 2569 } 2570 2571 /* console/chardev init sometimes completes elsewhere in a 2nd 2572 * stage, so defer OPENED events until they are fully initialized 2573 */ 2574 *be_opened = false; 2575 } 2576 2577 void qemu_console_resize(QemuConsole *s, int width, int height) 2578 { 2579 DisplaySurface *surface = qemu_console_surface(s); 2580 2581 assert(s->console_type == GRAPHIC_CONSOLE); 2582 2583 if ((s->scanout.kind != SCANOUT_SURFACE || 2584 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) && 2585 qemu_console_get_width(s, -1) == width && 2586 qemu_console_get_height(s, -1) == height) { 2587 return; 2588 } 2589 2590 surface = qemu_create_displaysurface(width, height); 2591 dpy_gfx_replace_surface(s, surface); 2592 } 2593 2594 DisplaySurface *qemu_console_surface(QemuConsole *console) 2595 { 2596 switch (console->scanout.kind) { 2597 case SCANOUT_SURFACE: 2598 return console->surface; 2599 default: 2600 return NULL; 2601 } 2602 } 2603 2604 PixelFormat qemu_default_pixelformat(int bpp) 2605 { 2606 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 2607 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 2608 return pf; 2609 } 2610 2611 static QemuDisplay *dpys[DISPLAY_TYPE__MAX]; 2612 2613 void qemu_display_register(QemuDisplay *ui) 2614 { 2615 assert(ui->type < DISPLAY_TYPE__MAX); 2616 dpys[ui->type] = ui; 2617 } 2618 2619 bool qemu_display_find_default(DisplayOptions *opts) 2620 { 2621 static DisplayType prio[] = { 2622 #if defined(CONFIG_GTK) 2623 DISPLAY_TYPE_GTK, 2624 #endif 2625 #if defined(CONFIG_SDL) 2626 DISPLAY_TYPE_SDL, 2627 #endif 2628 #if defined(CONFIG_COCOA) 2629 DISPLAY_TYPE_COCOA 2630 #endif 2631 }; 2632 int i; 2633 2634 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { 2635 if (dpys[prio[i]] == NULL) { 2636 Error *local_err = NULL; 2637 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err); 2638 if (rv < 0) { 2639 error_report_err(local_err); 2640 } 2641 } 2642 if (dpys[prio[i]] == NULL) { 2643 continue; 2644 } 2645 opts->type = prio[i]; 2646 return true; 2647 } 2648 return false; 2649 } 2650 2651 void qemu_display_early_init(DisplayOptions *opts) 2652 { 2653 assert(opts->type < DISPLAY_TYPE__MAX); 2654 if (opts->type == DISPLAY_TYPE_NONE) { 2655 return; 2656 } 2657 if (dpys[opts->type] == NULL) { 2658 Error *local_err = NULL; 2659 int rv = ui_module_load(DisplayType_str(opts->type), &local_err); 2660 if (rv < 0) { 2661 error_report_err(local_err); 2662 } 2663 } 2664 if (dpys[opts->type] == NULL) { 2665 error_report("Display '%s' is not available.", 2666 DisplayType_str(opts->type)); 2667 exit(1); 2668 } 2669 if (dpys[opts->type]->early_init) { 2670 dpys[opts->type]->early_init(opts); 2671 } 2672 } 2673 2674 void qemu_display_init(DisplayState *ds, DisplayOptions *opts) 2675 { 2676 assert(opts->type < DISPLAY_TYPE__MAX); 2677 if (opts->type == DISPLAY_TYPE_NONE) { 2678 return; 2679 } 2680 assert(dpys[opts->type] != NULL); 2681 dpys[opts->type]->init(ds, opts); 2682 } 2683 2684 void qemu_display_help(void) 2685 { 2686 int idx; 2687 2688 printf("Available display backend types:\n"); 2689 printf("none\n"); 2690 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { 2691 if (!dpys[idx]) { 2692 Error *local_err = NULL; 2693 int rv = ui_module_load(DisplayType_str(idx), &local_err); 2694 if (rv < 0) { 2695 error_report_err(local_err); 2696 } 2697 } 2698 if (dpys[idx]) { 2699 printf("%s\n", DisplayType_str(dpys[idx]->type)); 2700 } 2701 } 2702 } 2703 2704 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) 2705 { 2706 int val; 2707 ChardevVC *vc; 2708 2709 backend->type = CHARDEV_BACKEND_KIND_VC; 2710 vc = backend->u.vc.data = g_new0(ChardevVC, 1); 2711 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); 2712 2713 val = qemu_opt_get_number(opts, "width", 0); 2714 if (val != 0) { 2715 vc->has_width = true; 2716 vc->width = val; 2717 } 2718 2719 val = qemu_opt_get_number(opts, "height", 0); 2720 if (val != 0) { 2721 vc->has_height = true; 2722 vc->height = val; 2723 } 2724 2725 val = qemu_opt_get_number(opts, "cols", 0); 2726 if (val != 0) { 2727 vc->has_cols = true; 2728 vc->cols = val; 2729 } 2730 2731 val = qemu_opt_get_number(opts, "rows", 0); 2732 if (val != 0) { 2733 vc->has_rows = true; 2734 vc->rows = val; 2735 } 2736 } 2737 2738 static const TypeInfo qemu_console_info = { 2739 .name = TYPE_QEMU_CONSOLE, 2740 .parent = TYPE_OBJECT, 2741 .instance_size = sizeof(QemuConsole), 2742 .class_size = sizeof(QemuConsoleClass), 2743 }; 2744 2745 static void char_vc_class_init(ObjectClass *oc, void *data) 2746 { 2747 ChardevClass *cc = CHARDEV_CLASS(oc); 2748 2749 cc->parse = qemu_chr_parse_vc; 2750 cc->open = vc_chr_open; 2751 cc->chr_write = vc_chr_write; 2752 cc->chr_accept_input = vc_chr_accept_input; 2753 cc->chr_set_echo = vc_chr_set_echo; 2754 } 2755 2756 static const TypeInfo char_vc_type_info = { 2757 .name = TYPE_CHARDEV_VC, 2758 .parent = TYPE_CHARDEV, 2759 .instance_size = sizeof(VCChardev), 2760 .class_init = char_vc_class_init, 2761 }; 2762 2763 void qemu_console_early_init(void) 2764 { 2765 /* set the default vc driver */ 2766 if (!object_class_by_name(TYPE_CHARDEV_VC)) { 2767 type_register(&char_vc_type_info); 2768 } 2769 } 2770 2771 static void register_types(void) 2772 { 2773 type_register_static(&qemu_console_info); 2774 } 2775 2776 type_init(register_types); 2777