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