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 DisplayGLCtx *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, DisplayGLCtx *gl) 1462 { 1463 /* display has opengl support */ 1464 assert(con); 1465 if (con->gl) { 1466 error_report("The console already has an OpenGL context."); 1467 exit(1); 1468 } 1469 con->gl = gl; 1470 } 1471 1472 static bool dpy_gl_compatible_with(QemuConsole *con, DisplayChangeListener *dcl) 1473 { 1474 if (!con->gl) { 1475 return true; 1476 } 1477 1478 return con->gl->ops->compatible_dcl == dcl->ops; 1479 } 1480 1481 void register_displaychangelistener(DisplayChangeListener *dcl) 1482 { 1483 static const char nodev[] = 1484 "This VM has no graphic display device."; 1485 static DisplaySurface *dummy; 1486 QemuConsole *con; 1487 1488 assert(!dcl->ds); 1489 1490 if (dcl->con && !dpy_gl_compatible_with(dcl->con, dcl)) { 1491 error_report("Display %s is incompatible with the GL context", 1492 dcl->ops->dpy_name); 1493 exit(1); 1494 } 1495 1496 if (dcl->con) { 1497 dpy_compatible_with(dcl->con, dcl, &error_fatal); 1498 } 1499 1500 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); 1501 dcl->ds = get_alloc_displaystate(); 1502 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); 1503 gui_setup_refresh(dcl->ds); 1504 if (dcl->con) { 1505 dcl->con->dcls++; 1506 con = dcl->con; 1507 } else { 1508 con = active_console; 1509 } 1510 if (dcl->ops->dpy_gfx_switch) { 1511 if (con) { 1512 dcl->ops->dpy_gfx_switch(dcl, con->surface); 1513 } else { 1514 if (!dummy) { 1515 dummy = qemu_create_placeholder_surface(640, 480, nodev); 1516 } 1517 dcl->ops->dpy_gfx_switch(dcl, dummy); 1518 } 1519 } 1520 text_console_update_cursor(NULL); 1521 } 1522 1523 void update_displaychangelistener(DisplayChangeListener *dcl, 1524 uint64_t interval) 1525 { 1526 DisplayState *ds = dcl->ds; 1527 1528 dcl->update_interval = interval; 1529 if (!ds->refreshing && ds->update_interval > interval) { 1530 timer_mod(ds->gui_timer, ds->last_update + interval); 1531 } 1532 } 1533 1534 void unregister_displaychangelistener(DisplayChangeListener *dcl) 1535 { 1536 DisplayState *ds = dcl->ds; 1537 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name); 1538 if (dcl->con) { 1539 dcl->con->dcls--; 1540 } 1541 QLIST_REMOVE(dcl, next); 1542 dcl->ds = NULL; 1543 gui_setup_refresh(ds); 1544 } 1545 1546 static void dpy_set_ui_info_timer(void *opaque) 1547 { 1548 QemuConsole *con = opaque; 1549 1550 con->hw_ops->ui_info(con->hw, con->head, &con->ui_info); 1551 } 1552 1553 bool dpy_ui_info_supported(QemuConsole *con) 1554 { 1555 if (con == NULL) { 1556 con = active_console; 1557 } 1558 1559 return con->hw_ops->ui_info != NULL; 1560 } 1561 1562 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con) 1563 { 1564 if (con == NULL) { 1565 con = active_console; 1566 } 1567 1568 return &con->ui_info; 1569 } 1570 1571 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay) 1572 { 1573 if (con == NULL) { 1574 con = active_console; 1575 } 1576 1577 if (!dpy_ui_info_supported(con)) { 1578 return -1; 1579 } 1580 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) { 1581 /* nothing changed -- ignore */ 1582 return 0; 1583 } 1584 1585 /* 1586 * Typically we get a flood of these as the user resizes the window. 1587 * Wait until the dust has settled (one second without updates), then 1588 * go notify the guest. 1589 */ 1590 con->ui_info = *info; 1591 timer_mod(con->ui_timer, 1592 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0)); 1593 return 0; 1594 } 1595 1596 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 1597 { 1598 DisplayState *s = con->ds; 1599 DisplayChangeListener *dcl; 1600 int width = w; 1601 int height = h; 1602 1603 if (con->surface) { 1604 width = surface_width(con->surface); 1605 height = surface_height(con->surface); 1606 } 1607 x = MAX(x, 0); 1608 y = MAX(y, 0); 1609 x = MIN(x, width); 1610 y = MIN(y, height); 1611 w = MIN(w, width - x); 1612 h = MIN(h, height - y); 1613 1614 if (!qemu_console_is_visible(con)) { 1615 return; 1616 } 1617 QLIST_FOREACH(dcl, &s->listeners, next) { 1618 if (con != (dcl->con ? dcl->con : active_console)) { 1619 continue; 1620 } 1621 if (dcl->ops->dpy_gfx_update) { 1622 dcl->ops->dpy_gfx_update(dcl, x, y, w, h); 1623 } 1624 } 1625 } 1626 1627 void dpy_gfx_update_full(QemuConsole *con) 1628 { 1629 if (!con->surface) { 1630 return; 1631 } 1632 dpy_gfx_update(con, 0, 0, 1633 surface_width(con->surface), 1634 surface_height(con->surface)); 1635 } 1636 1637 void dpy_gfx_replace_surface(QemuConsole *con, 1638 DisplaySurface *surface) 1639 { 1640 static const char placeholder_msg[] = "Display output is not active."; 1641 DisplayState *s = con->ds; 1642 DisplaySurface *old_surface = con->surface; 1643 DisplayChangeListener *dcl; 1644 int width; 1645 int height; 1646 1647 if (!surface) { 1648 if (old_surface) { 1649 width = surface_width(old_surface); 1650 height = surface_height(old_surface); 1651 } else { 1652 width = 640; 1653 height = 480; 1654 } 1655 1656 surface = qemu_create_placeholder_surface(width, height, placeholder_msg); 1657 } 1658 1659 assert(old_surface != surface); 1660 1661 con->surface = surface; 1662 QLIST_FOREACH(dcl, &s->listeners, next) { 1663 if (con != (dcl->con ? dcl->con : active_console)) { 1664 continue; 1665 } 1666 if (dcl->ops->dpy_gfx_switch) { 1667 dcl->ops->dpy_gfx_switch(dcl, surface); 1668 } 1669 } 1670 qemu_free_displaysurface(old_surface); 1671 } 1672 1673 bool dpy_gfx_check_format(QemuConsole *con, 1674 pixman_format_code_t format) 1675 { 1676 DisplayChangeListener *dcl; 1677 DisplayState *s = con->ds; 1678 1679 QLIST_FOREACH(dcl, &s->listeners, next) { 1680 if (dcl->con && dcl->con != con) { 1681 /* dcl bound to another console -> skip */ 1682 continue; 1683 } 1684 if (dcl->ops->dpy_gfx_check_format) { 1685 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) { 1686 return false; 1687 } 1688 } else { 1689 /* default is to allow native 32 bpp only */ 1690 if (format != qemu_default_pixman_format(32, true)) { 1691 return false; 1692 } 1693 } 1694 } 1695 return true; 1696 } 1697 1698 static void dpy_refresh(DisplayState *s) 1699 { 1700 DisplayChangeListener *dcl; 1701 1702 QLIST_FOREACH(dcl, &s->listeners, next) { 1703 if (dcl->ops->dpy_refresh) { 1704 dcl->ops->dpy_refresh(dcl); 1705 } 1706 } 1707 } 1708 1709 void dpy_text_cursor(QemuConsole *con, int x, int y) 1710 { 1711 DisplayState *s = con->ds; 1712 DisplayChangeListener *dcl; 1713 1714 if (!qemu_console_is_visible(con)) { 1715 return; 1716 } 1717 QLIST_FOREACH(dcl, &s->listeners, next) { 1718 if (con != (dcl->con ? dcl->con : active_console)) { 1719 continue; 1720 } 1721 if (dcl->ops->dpy_text_cursor) { 1722 dcl->ops->dpy_text_cursor(dcl, x, y); 1723 } 1724 } 1725 } 1726 1727 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) 1728 { 1729 DisplayState *s = con->ds; 1730 DisplayChangeListener *dcl; 1731 1732 if (!qemu_console_is_visible(con)) { 1733 return; 1734 } 1735 QLIST_FOREACH(dcl, &s->listeners, next) { 1736 if (con != (dcl->con ? dcl->con : active_console)) { 1737 continue; 1738 } 1739 if (dcl->ops->dpy_text_update) { 1740 dcl->ops->dpy_text_update(dcl, x, y, w, h); 1741 } 1742 } 1743 } 1744 1745 void dpy_text_resize(QemuConsole *con, int w, int h) 1746 { 1747 DisplayState *s = con->ds; 1748 DisplayChangeListener *dcl; 1749 1750 if (!qemu_console_is_visible(con)) { 1751 return; 1752 } 1753 QLIST_FOREACH(dcl, &s->listeners, next) { 1754 if (con != (dcl->con ? dcl->con : active_console)) { 1755 continue; 1756 } 1757 if (dcl->ops->dpy_text_resize) { 1758 dcl->ops->dpy_text_resize(dcl, w, h); 1759 } 1760 } 1761 } 1762 1763 void dpy_mouse_set(QemuConsole *con, int x, int y, int on) 1764 { 1765 DisplayState *s = con->ds; 1766 DisplayChangeListener *dcl; 1767 1768 if (!qemu_console_is_visible(con)) { 1769 return; 1770 } 1771 QLIST_FOREACH(dcl, &s->listeners, next) { 1772 if (con != (dcl->con ? dcl->con : active_console)) { 1773 continue; 1774 } 1775 if (dcl->ops->dpy_mouse_set) { 1776 dcl->ops->dpy_mouse_set(dcl, x, y, on); 1777 } 1778 } 1779 } 1780 1781 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) 1782 { 1783 DisplayState *s = con->ds; 1784 DisplayChangeListener *dcl; 1785 1786 if (!qemu_console_is_visible(con)) { 1787 return; 1788 } 1789 QLIST_FOREACH(dcl, &s->listeners, next) { 1790 if (con != (dcl->con ? dcl->con : active_console)) { 1791 continue; 1792 } 1793 if (dcl->ops->dpy_cursor_define) { 1794 dcl->ops->dpy_cursor_define(dcl, cursor); 1795 } 1796 } 1797 } 1798 1799 bool dpy_cursor_define_supported(QemuConsole *con) 1800 { 1801 DisplayState *s = con->ds; 1802 DisplayChangeListener *dcl; 1803 1804 QLIST_FOREACH(dcl, &s->listeners, next) { 1805 if (dcl->ops->dpy_cursor_define) { 1806 return true; 1807 } 1808 } 1809 return false; 1810 } 1811 1812 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, 1813 struct QEMUGLParams *qparams) 1814 { 1815 assert(con->gl); 1816 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams); 1817 } 1818 1819 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx) 1820 { 1821 assert(con->gl); 1822 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx); 1823 } 1824 1825 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) 1826 { 1827 assert(con->gl); 1828 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); 1829 } 1830 1831 void dpy_gl_scanout_disable(QemuConsole *con) 1832 { 1833 DisplayState *s = con->ds; 1834 DisplayChangeListener *dcl; 1835 1836 QLIST_FOREACH(dcl, &s->listeners, next) { 1837 dcl->ops->dpy_gl_scanout_disable(dcl); 1838 } 1839 } 1840 1841 void dpy_gl_scanout_texture(QemuConsole *con, 1842 uint32_t backing_id, 1843 bool backing_y_0_top, 1844 uint32_t backing_width, 1845 uint32_t backing_height, 1846 uint32_t x, uint32_t y, 1847 uint32_t width, uint32_t height) 1848 { 1849 DisplayState *s = con->ds; 1850 DisplayChangeListener *dcl; 1851 1852 QLIST_FOREACH(dcl, &s->listeners, next) { 1853 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, 1854 backing_y_0_top, 1855 backing_width, backing_height, 1856 x, y, width, height); 1857 } 1858 } 1859 1860 void dpy_gl_scanout_dmabuf(QemuConsole *con, 1861 QemuDmaBuf *dmabuf) 1862 { 1863 DisplayState *s = con->ds; 1864 DisplayChangeListener *dcl; 1865 1866 QLIST_FOREACH(dcl, &s->listeners, next) { 1867 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); 1868 } 1869 } 1870 1871 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, 1872 bool have_hot, uint32_t hot_x, uint32_t hot_y) 1873 { 1874 DisplayState *s = con->ds; 1875 DisplayChangeListener *dcl; 1876 1877 QLIST_FOREACH(dcl, &s->listeners, next) { 1878 if (dcl->ops->dpy_gl_cursor_dmabuf) { 1879 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf, 1880 have_hot, hot_x, hot_y); 1881 } 1882 } 1883 } 1884 1885 void dpy_gl_cursor_position(QemuConsole *con, 1886 uint32_t pos_x, uint32_t pos_y) 1887 { 1888 DisplayState *s = con->ds; 1889 DisplayChangeListener *dcl; 1890 1891 QLIST_FOREACH(dcl, &s->listeners, next) { 1892 if (dcl->ops->dpy_gl_cursor_position) { 1893 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y); 1894 } 1895 } 1896 } 1897 1898 void dpy_gl_release_dmabuf(QemuConsole *con, 1899 QemuDmaBuf *dmabuf) 1900 { 1901 DisplayState *s = con->ds; 1902 DisplayChangeListener *dcl; 1903 1904 QLIST_FOREACH(dcl, &s->listeners, next) { 1905 if (dcl->ops->dpy_gl_release_dmabuf) { 1906 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf); 1907 } 1908 } 1909 } 1910 1911 void dpy_gl_update(QemuConsole *con, 1912 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 1913 { 1914 DisplayState *s = con->ds; 1915 DisplayChangeListener *dcl; 1916 1917 assert(con->gl); 1918 1919 graphic_hw_gl_block(con, true); 1920 QLIST_FOREACH(dcl, &s->listeners, next) { 1921 dcl->ops->dpy_gl_update(dcl, x, y, w, h); 1922 } 1923 graphic_hw_gl_block(con, false); 1924 } 1925 1926 /***********************************************************/ 1927 /* register display */ 1928 1929 /* console.c internal use only */ 1930 static DisplayState *get_alloc_displaystate(void) 1931 { 1932 if (!display_state) { 1933 display_state = g_new0(DisplayState, 1); 1934 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1935 text_console_update_cursor, NULL); 1936 } 1937 return display_state; 1938 } 1939 1940 /* 1941 * Called by main(), after creating QemuConsoles 1942 * and before initializing ui (sdl/vnc/...). 1943 */ 1944 DisplayState *init_displaystate(void) 1945 { 1946 gchar *name; 1947 QemuConsole *con; 1948 1949 get_alloc_displaystate(); 1950 QTAILQ_FOREACH(con, &consoles, next) { 1951 if (con->console_type != GRAPHIC_CONSOLE && 1952 con->ds == NULL) { 1953 text_console_do_init(con->chr, display_state); 1954 } 1955 1956 /* Hook up into the qom tree here (not in new_console()), once 1957 * all QemuConsoles are created and the order / numbering 1958 * doesn't change any more */ 1959 name = g_strdup_printf("console[%d]", con->index); 1960 object_property_add_child(container_get(object_get_root(), "/backend"), 1961 name, OBJECT(con)); 1962 g_free(name); 1963 } 1964 1965 return display_state; 1966 } 1967 1968 void graphic_console_set_hwops(QemuConsole *con, 1969 const GraphicHwOps *hw_ops, 1970 void *opaque) 1971 { 1972 con->hw_ops = hw_ops; 1973 con->hw = opaque; 1974 } 1975 1976 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 1977 const GraphicHwOps *hw_ops, 1978 void *opaque) 1979 { 1980 static const char noinit[] = 1981 "Guest has not initialized the display (yet)."; 1982 int width = 640; 1983 int height = 480; 1984 QemuConsole *s; 1985 DisplayState *ds; 1986 DisplaySurface *surface; 1987 1988 ds = get_alloc_displaystate(); 1989 s = qemu_console_lookup_unused(); 1990 if (s) { 1991 trace_console_gfx_reuse(s->index); 1992 if (s->surface) { 1993 width = surface_width(s->surface); 1994 height = surface_height(s->surface); 1995 } 1996 } else { 1997 trace_console_gfx_new(); 1998 s = new_console(ds, GRAPHIC_CONSOLE, head); 1999 s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2000 dpy_set_ui_info_timer, s); 2001 } 2002 graphic_console_set_hwops(s, hw_ops, opaque); 2003 if (dev) { 2004 object_property_set_link(OBJECT(s), "device", OBJECT(dev), 2005 &error_abort); 2006 } 2007 2008 surface = qemu_create_placeholder_surface(width, height, noinit); 2009 dpy_gfx_replace_surface(s, surface); 2010 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2011 graphic_hw_gl_unblock_timer, s); 2012 return s; 2013 } 2014 2015 static const GraphicHwOps unused_ops = { 2016 /* no callbacks */ 2017 }; 2018 2019 void graphic_console_close(QemuConsole *con) 2020 { 2021 static const char unplugged[] = 2022 "Guest display has been unplugged"; 2023 DisplaySurface *surface; 2024 int width = 640; 2025 int height = 480; 2026 2027 if (con->surface) { 2028 width = surface_width(con->surface); 2029 height = surface_height(con->surface); 2030 } 2031 2032 trace_console_gfx_close(con->index); 2033 object_property_set_link(OBJECT(con), "device", NULL, &error_abort); 2034 graphic_console_set_hwops(con, &unused_ops, NULL); 2035 2036 if (con->gl) { 2037 dpy_gl_scanout_disable(con); 2038 } 2039 surface = qemu_create_placeholder_surface(width, height, unplugged); 2040 dpy_gfx_replace_surface(con, surface); 2041 } 2042 2043 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 2044 { 2045 QemuConsole *con; 2046 2047 QTAILQ_FOREACH(con, &consoles, next) { 2048 if (con->index == index) { 2049 return con; 2050 } 2051 } 2052 return NULL; 2053 } 2054 2055 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 2056 { 2057 QemuConsole *con; 2058 Object *obj; 2059 uint32_t h; 2060 2061 QTAILQ_FOREACH(con, &consoles, next) { 2062 obj = object_property_get_link(OBJECT(con), 2063 "device", &error_abort); 2064 if (DEVICE(obj) != dev) { 2065 continue; 2066 } 2067 h = object_property_get_uint(OBJECT(con), 2068 "head", &error_abort); 2069 if (h != head) { 2070 continue; 2071 } 2072 return con; 2073 } 2074 return NULL; 2075 } 2076 2077 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, 2078 uint32_t head, Error **errp) 2079 { 2080 DeviceState *dev; 2081 QemuConsole *con; 2082 2083 dev = qdev_find_recursive(sysbus_get_default(), device_id); 2084 if (dev == NULL) { 2085 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 2086 "Device '%s' not found", device_id); 2087 return NULL; 2088 } 2089 2090 con = qemu_console_lookup_by_device(dev, head); 2091 if (con == NULL) { 2092 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", 2093 device_id, head); 2094 return NULL; 2095 } 2096 2097 return con; 2098 } 2099 2100 QemuConsole *qemu_console_lookup_unused(void) 2101 { 2102 QemuConsole *con; 2103 Object *obj; 2104 2105 QTAILQ_FOREACH(con, &consoles, next) { 2106 if (con->hw_ops != &unused_ops) { 2107 continue; 2108 } 2109 obj = object_property_get_link(OBJECT(con), 2110 "device", &error_abort); 2111 if (obj != NULL) { 2112 continue; 2113 } 2114 return con; 2115 } 2116 return NULL; 2117 } 2118 2119 bool qemu_console_is_visible(QemuConsole *con) 2120 { 2121 return (con == active_console) || (con->dcls > 0); 2122 } 2123 2124 bool qemu_console_is_graphic(QemuConsole *con) 2125 { 2126 if (con == NULL) { 2127 con = active_console; 2128 } 2129 return con && (con->console_type == GRAPHIC_CONSOLE); 2130 } 2131 2132 bool qemu_console_is_fixedsize(QemuConsole *con) 2133 { 2134 if (con == NULL) { 2135 con = active_console; 2136 } 2137 return con && (con->console_type != TEXT_CONSOLE); 2138 } 2139 2140 bool qemu_console_is_gl_blocked(QemuConsole *con) 2141 { 2142 assert(con != NULL); 2143 return con->gl_block; 2144 } 2145 2146 char *qemu_console_get_label(QemuConsole *con) 2147 { 2148 if (con->console_type == GRAPHIC_CONSOLE) { 2149 if (con->device) { 2150 return g_strdup(object_get_typename(con->device)); 2151 } 2152 return g_strdup("VGA"); 2153 } else { 2154 if (con->chr && con->chr->label) { 2155 return g_strdup(con->chr->label); 2156 } 2157 return g_strdup_printf("vc%d", con->index); 2158 } 2159 } 2160 2161 int qemu_console_get_index(QemuConsole *con) 2162 { 2163 if (con == NULL) { 2164 con = active_console; 2165 } 2166 return con ? con->index : -1; 2167 } 2168 2169 uint32_t qemu_console_get_head(QemuConsole *con) 2170 { 2171 if (con == NULL) { 2172 con = active_console; 2173 } 2174 return con ? con->head : -1; 2175 } 2176 2177 int qemu_console_get_width(QemuConsole *con, int fallback) 2178 { 2179 if (con == NULL) { 2180 con = active_console; 2181 } 2182 return con ? surface_width(con->surface) : fallback; 2183 } 2184 2185 int qemu_console_get_height(QemuConsole *con, int fallback) 2186 { 2187 if (con == NULL) { 2188 con = active_console; 2189 } 2190 return con ? surface_height(con->surface) : fallback; 2191 } 2192 2193 static void vc_chr_accept_input(Chardev *chr) 2194 { 2195 VCChardev *drv = VC_CHARDEV(chr); 2196 QemuConsole *s = drv->console; 2197 2198 kbd_send_chars(s); 2199 } 2200 2201 static void vc_chr_set_echo(Chardev *chr, bool echo) 2202 { 2203 VCChardev *drv = VC_CHARDEV(chr); 2204 QemuConsole *s = drv->console; 2205 2206 s->echo = echo; 2207 } 2208 2209 static void text_console_update_cursor_timer(void) 2210 { 2211 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) 2212 + CONSOLE_CURSOR_PERIOD / 2); 2213 } 2214 2215 static void text_console_update_cursor(void *opaque) 2216 { 2217 QemuConsole *s; 2218 int count = 0; 2219 2220 cursor_visible_phase = !cursor_visible_phase; 2221 2222 QTAILQ_FOREACH(s, &consoles, next) { 2223 if (qemu_console_is_graphic(s) || 2224 !qemu_console_is_visible(s)) { 2225 continue; 2226 } 2227 count++; 2228 graphic_hw_invalidate(s); 2229 } 2230 2231 if (count) { 2232 text_console_update_cursor_timer(); 2233 } 2234 } 2235 2236 static const GraphicHwOps text_console_ops = { 2237 .invalidate = text_console_invalidate, 2238 .text_update = text_console_update, 2239 }; 2240 2241 static void text_console_do_init(Chardev *chr, DisplayState *ds) 2242 { 2243 VCChardev *drv = VC_CHARDEV(chr); 2244 QemuConsole *s = drv->console; 2245 int g_width = 80 * FONT_WIDTH; 2246 int g_height = 24 * FONT_HEIGHT; 2247 2248 fifo8_create(&s->out_fifo, 16); 2249 s->ds = ds; 2250 2251 s->y_displayed = 0; 2252 s->y_base = 0; 2253 s->total_height = DEFAULT_BACKSCROLL; 2254 s->x = 0; 2255 s->y = 0; 2256 if (!s->surface) { 2257 if (active_console && active_console->surface) { 2258 g_width = surface_width(active_console->surface); 2259 g_height = surface_height(active_console->surface); 2260 } 2261 s->surface = qemu_create_displaysurface(g_width, g_height); 2262 } 2263 2264 s->hw_ops = &text_console_ops; 2265 s->hw = s; 2266 2267 /* Set text attribute defaults */ 2268 s->t_attrib_default.bold = 0; 2269 s->t_attrib_default.uline = 0; 2270 s->t_attrib_default.blink = 0; 2271 s->t_attrib_default.invers = 0; 2272 s->t_attrib_default.unvisible = 0; 2273 s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; 2274 s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; 2275 /* set current text attributes to default */ 2276 s->t_attrib = s->t_attrib_default; 2277 text_console_resize(s); 2278 2279 if (chr->label) { 2280 char *msg; 2281 2282 s->t_attrib.bgcol = QEMU_COLOR_BLUE; 2283 msg = g_strdup_printf("%s console\r\n", chr->label); 2284 vc_chr_write(chr, (uint8_t *)msg, strlen(msg)); 2285 g_free(msg); 2286 s->t_attrib = s->t_attrib_default; 2287 } 2288 2289 qemu_chr_be_event(chr, CHR_EVENT_OPENED); 2290 } 2291 2292 static void vc_chr_open(Chardev *chr, 2293 ChardevBackend *backend, 2294 bool *be_opened, 2295 Error **errp) 2296 { 2297 ChardevVC *vc = backend->u.vc.data; 2298 VCChardev *drv = VC_CHARDEV(chr); 2299 QemuConsole *s; 2300 unsigned width = 0; 2301 unsigned height = 0; 2302 2303 if (vc->has_width) { 2304 width = vc->width; 2305 } else if (vc->has_cols) { 2306 width = vc->cols * FONT_WIDTH; 2307 } 2308 2309 if (vc->has_height) { 2310 height = vc->height; 2311 } else if (vc->has_rows) { 2312 height = vc->rows * FONT_HEIGHT; 2313 } 2314 2315 trace_console_txt_new(width, height); 2316 if (width == 0 || height == 0) { 2317 s = new_console(NULL, TEXT_CONSOLE, 0); 2318 } else { 2319 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); 2320 s->surface = qemu_create_displaysurface(width, height); 2321 } 2322 2323 if (!s) { 2324 error_setg(errp, "cannot create text console"); 2325 return; 2326 } 2327 2328 s->chr = chr; 2329 drv->console = s; 2330 2331 if (display_state) { 2332 text_console_do_init(chr, display_state); 2333 } 2334 2335 /* console/chardev init sometimes completes elsewhere in a 2nd 2336 * stage, so defer OPENED events until they are fully initialized 2337 */ 2338 *be_opened = false; 2339 } 2340 2341 void qemu_console_resize(QemuConsole *s, int width, int height) 2342 { 2343 DisplaySurface *surface; 2344 2345 assert(s->console_type == GRAPHIC_CONSOLE); 2346 2347 if (s->surface && (s->surface->flags & QEMU_ALLOCATED_FLAG) && 2348 pixman_image_get_width(s->surface->image) == width && 2349 pixman_image_get_height(s->surface->image) == height) { 2350 return; 2351 } 2352 2353 surface = qemu_create_displaysurface(width, height); 2354 dpy_gfx_replace_surface(s, surface); 2355 } 2356 2357 DisplaySurface *qemu_console_surface(QemuConsole *console) 2358 { 2359 return console->surface; 2360 } 2361 2362 PixelFormat qemu_default_pixelformat(int bpp) 2363 { 2364 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 2365 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 2366 return pf; 2367 } 2368 2369 static QemuDisplay *dpys[DISPLAY_TYPE__MAX]; 2370 2371 void qemu_display_register(QemuDisplay *ui) 2372 { 2373 assert(ui->type < DISPLAY_TYPE__MAX); 2374 dpys[ui->type] = ui; 2375 } 2376 2377 bool qemu_display_find_default(DisplayOptions *opts) 2378 { 2379 static DisplayType prio[] = { 2380 #if defined(CONFIG_GTK) 2381 DISPLAY_TYPE_GTK, 2382 #endif 2383 #if defined(CONFIG_SDL) 2384 DISPLAY_TYPE_SDL, 2385 #endif 2386 #if defined(CONFIG_COCOA) 2387 DISPLAY_TYPE_COCOA 2388 #endif 2389 }; 2390 int i; 2391 2392 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { 2393 if (dpys[prio[i]] == NULL) { 2394 ui_module_load_one(DisplayType_str(prio[i])); 2395 } 2396 if (dpys[prio[i]] == NULL) { 2397 continue; 2398 } 2399 opts->type = prio[i]; 2400 return true; 2401 } 2402 return false; 2403 } 2404 2405 void qemu_display_early_init(DisplayOptions *opts) 2406 { 2407 assert(opts->type < DISPLAY_TYPE__MAX); 2408 if (opts->type == DISPLAY_TYPE_NONE) { 2409 return; 2410 } 2411 if (dpys[opts->type] == NULL) { 2412 ui_module_load_one(DisplayType_str(opts->type)); 2413 } 2414 if (dpys[opts->type] == NULL) { 2415 error_report("Display '%s' is not available.", 2416 DisplayType_str(opts->type)); 2417 exit(1); 2418 } 2419 if (dpys[opts->type]->early_init) { 2420 dpys[opts->type]->early_init(opts); 2421 } 2422 } 2423 2424 void qemu_display_init(DisplayState *ds, DisplayOptions *opts) 2425 { 2426 assert(opts->type < DISPLAY_TYPE__MAX); 2427 if (opts->type == DISPLAY_TYPE_NONE) { 2428 return; 2429 } 2430 assert(dpys[opts->type] != NULL); 2431 dpys[opts->type]->init(ds, opts); 2432 } 2433 2434 void qemu_display_help(void) 2435 { 2436 int idx; 2437 2438 printf("Available display backend types:\n"); 2439 printf("none\n"); 2440 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { 2441 if (!dpys[idx]) { 2442 ui_module_load_one(DisplayType_str(idx)); 2443 } 2444 if (dpys[idx]) { 2445 printf("%s\n", DisplayType_str(dpys[idx]->type)); 2446 } 2447 } 2448 } 2449 2450 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) 2451 { 2452 int val; 2453 ChardevVC *vc; 2454 2455 backend->type = CHARDEV_BACKEND_KIND_VC; 2456 vc = backend->u.vc.data = g_new0(ChardevVC, 1); 2457 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); 2458 2459 val = qemu_opt_get_number(opts, "width", 0); 2460 if (val != 0) { 2461 vc->has_width = true; 2462 vc->width = val; 2463 } 2464 2465 val = qemu_opt_get_number(opts, "height", 0); 2466 if (val != 0) { 2467 vc->has_height = true; 2468 vc->height = val; 2469 } 2470 2471 val = qemu_opt_get_number(opts, "cols", 0); 2472 if (val != 0) { 2473 vc->has_cols = true; 2474 vc->cols = val; 2475 } 2476 2477 val = qemu_opt_get_number(opts, "rows", 0); 2478 if (val != 0) { 2479 vc->has_rows = true; 2480 vc->rows = val; 2481 } 2482 } 2483 2484 static const TypeInfo qemu_console_info = { 2485 .name = TYPE_QEMU_CONSOLE, 2486 .parent = TYPE_OBJECT, 2487 .instance_size = sizeof(QemuConsole), 2488 .class_size = sizeof(QemuConsoleClass), 2489 }; 2490 2491 static void char_vc_class_init(ObjectClass *oc, void *data) 2492 { 2493 ChardevClass *cc = CHARDEV_CLASS(oc); 2494 2495 cc->parse = qemu_chr_parse_vc; 2496 cc->open = vc_chr_open; 2497 cc->chr_write = vc_chr_write; 2498 cc->chr_accept_input = vc_chr_accept_input; 2499 cc->chr_set_echo = vc_chr_set_echo; 2500 } 2501 2502 static const TypeInfo char_vc_type_info = { 2503 .name = TYPE_CHARDEV_VC, 2504 .parent = TYPE_CHARDEV, 2505 .instance_size = sizeof(VCChardev), 2506 .class_init = char_vc_class_init, 2507 }; 2508 2509 void qemu_console_early_init(void) 2510 { 2511 /* set the default vc driver */ 2512 if (!object_class_by_name(TYPE_CHARDEV_VC)) { 2513 type_register(&char_vc_type_info); 2514 } 2515 } 2516 2517 static void register_types(void) 2518 { 2519 type_register_static(&qemu_console_info); 2520 } 2521 2522 type_init(register_types); 2523