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