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