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