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