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(QemuConsole *con, int keysym) 1139 { 1140 QemuTextConsole *s = (QemuTextConsole *)object_dynamic_cast(OBJECT(con), TYPE_QEMU_TEXT_CONSOLE); 1141 uint8_t buf[16], *q; 1142 int c; 1143 uint32_t num_free; 1144 1145 if (!s) 1146 return; 1147 1148 switch(keysym) { 1149 case QEMU_KEY_CTRL_UP: 1150 console_scroll(s, -1); 1151 break; 1152 case QEMU_KEY_CTRL_DOWN: 1153 console_scroll(s, 1); 1154 break; 1155 case QEMU_KEY_CTRL_PAGEUP: 1156 console_scroll(s, -10); 1157 break; 1158 case QEMU_KEY_CTRL_PAGEDOWN: 1159 console_scroll(s, 10); 1160 break; 1161 default: 1162 /* convert the QEMU keysym to VT100 key string */ 1163 q = buf; 1164 if (keysym >= 0xe100 && keysym <= 0xe11f) { 1165 *q++ = '\033'; 1166 *q++ = '['; 1167 c = keysym - 0xe100; 1168 if (c >= 10) 1169 *q++ = '0' + (c / 10); 1170 *q++ = '0' + (c % 10); 1171 *q++ = '~'; 1172 } else if (keysym >= 0xe120 && keysym <= 0xe17f) { 1173 *q++ = '\033'; 1174 *q++ = '['; 1175 *q++ = keysym & 0xff; 1176 } else if (s->echo && (keysym == '\r' || keysym == '\n')) { 1177 qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true); 1178 *q++ = '\n'; 1179 } else { 1180 *q++ = keysym; 1181 } 1182 if (s->echo) { 1183 qemu_chr_write(s->chr, buf, q - buf, true); 1184 } 1185 num_free = fifo8_num_free(&s->out_fifo); 1186 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf)); 1187 kbd_send_chars(s); 1188 break; 1189 } 1190 } 1191 1192 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = { 1193 [Q_KEY_CODE_UP] = QEMU_KEY_UP, 1194 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN, 1195 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT, 1196 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT, 1197 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME, 1198 [Q_KEY_CODE_END] = QEMU_KEY_END, 1199 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP, 1200 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN, 1201 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE, 1202 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB, 1203 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE, 1204 }; 1205 1206 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = { 1207 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP, 1208 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN, 1209 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT, 1210 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT, 1211 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME, 1212 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END, 1213 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP, 1214 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN, 1215 }; 1216 1217 bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl) 1218 { 1219 int keysym; 1220 1221 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode]; 1222 if (keysym == 0) { 1223 return false; 1224 } 1225 kbd_put_keysym_console(s, keysym); 1226 return true; 1227 } 1228 1229 void kbd_put_string_console(QemuConsole *s, const char *str, int len) 1230 { 1231 int i; 1232 1233 for (i = 0; i < len && str[i]; i++) { 1234 kbd_put_keysym_console(s, str[i]); 1235 } 1236 } 1237 1238 void kbd_put_keysym(int keysym) 1239 { 1240 kbd_put_keysym_console(active_console, keysym); 1241 } 1242 1243 static void text_console_invalidate(void *opaque) 1244 { 1245 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque); 1246 1247 if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) { 1248 text_console_resize(QEMU_TEXT_CONSOLE(s)); 1249 } 1250 console_refresh(s); 1251 } 1252 1253 static void text_console_update(void *opaque, console_ch_t *chardata) 1254 { 1255 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque); 1256 int i, j, src; 1257 1258 if (s->text_x[0] <= s->text_x[1]) { 1259 src = (s->y_base + s->text_y[0]) * s->width; 1260 chardata += s->text_y[0] * s->width; 1261 for (i = s->text_y[0]; i <= s->text_y[1]; i ++) 1262 for (j = 0; j < s->width; j++, src++) { 1263 console_write_ch(chardata ++, 1264 ATTR2CHTYPE(s->cells[src].ch, 1265 s->cells[src].t_attrib.fgcol, 1266 s->cells[src].t_attrib.bgcol, 1267 s->cells[src].t_attrib.bold)); 1268 } 1269 dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0], 1270 s->text_x[1] - s->text_x[0], i - s->text_y[0]); 1271 s->text_x[0] = s->width; 1272 s->text_y[0] = s->height; 1273 s->text_x[1] = 0; 1274 s->text_y[1] = 0; 1275 } 1276 if (s->cursor_invalidate) { 1277 dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y); 1278 s->cursor_invalidate = 0; 1279 } 1280 } 1281 1282 static void 1283 qemu_console_register(QemuConsole *c) 1284 { 1285 int i; 1286 1287 if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) && 1288 QEMU_IS_GRAPHIC_CONSOLE(c))) { 1289 active_console = c; 1290 } 1291 1292 if (QTAILQ_EMPTY(&consoles)) { 1293 c->index = 0; 1294 QTAILQ_INSERT_TAIL(&consoles, c, next); 1295 } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) { 1296 QemuConsole *last = QTAILQ_LAST(&consoles); 1297 c->index = last->index + 1; 1298 QTAILQ_INSERT_TAIL(&consoles, c, next); 1299 } else { 1300 /* 1301 * HACK: Put graphical consoles before text consoles. 1302 * 1303 * Only do that for coldplugged devices. After initial device 1304 * initialization we will not renumber the consoles any more. 1305 */ 1306 QemuConsole *it = QTAILQ_FIRST(&consoles); 1307 1308 while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) { 1309 it = QTAILQ_NEXT(it, next); 1310 } 1311 if (QEMU_IS_GRAPHIC_CONSOLE(it)) { 1312 /* have no text consoles */ 1313 c->index = it->index + 1; 1314 QTAILQ_INSERT_AFTER(&consoles, it, c, next); 1315 } else { 1316 c->index = it->index; 1317 QTAILQ_INSERT_BEFORE(it, c, next); 1318 /* renumber text consoles */ 1319 for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) { 1320 it->index = i; 1321 } 1322 } 1323 } 1324 } 1325 1326 static void 1327 qemu_console_finalize(Object *obj) 1328 { 1329 QemuConsole *c = QEMU_CONSOLE(obj); 1330 1331 /* TODO: check this code path, and unregister from consoles */ 1332 g_clear_pointer(&c->surface, qemu_free_displaysurface); 1333 g_clear_pointer(&c->gl_unblock_timer, timer_free); 1334 g_clear_pointer(&c->ui_timer, timer_free); 1335 } 1336 1337 static void 1338 qemu_console_class_init(ObjectClass *oc, void *data) 1339 { 1340 } 1341 1342 static void 1343 qemu_console_init(Object *obj) 1344 { 1345 QemuConsole *c = QEMU_CONSOLE(obj); 1346 DisplayState *ds = get_alloc_displaystate(); 1347 1348 qemu_co_queue_init(&c->dump_queue); 1349 c->ds = ds; 1350 c->window_id = -1; 1351 c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1352 dpy_set_ui_info_timer, c); 1353 qemu_console_register(c); 1354 } 1355 1356 static void 1357 qemu_graphic_console_finalize(Object *obj) 1358 { 1359 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj); 1360 1361 g_clear_pointer(&c->device, object_unref); 1362 } 1363 1364 static void 1365 qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name, 1366 void *opaque, Error **errp) 1367 { 1368 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj); 1369 1370 visit_type_uint32(v, name, &c->head, errp); 1371 } 1372 1373 static void 1374 qemu_graphic_console_class_init(ObjectClass *oc, void *data) 1375 { 1376 object_class_property_add_link(oc, "device", TYPE_DEVICE, 1377 offsetof(QemuGraphicConsole, device), 1378 object_property_allow_set_link, 1379 OBJ_PROP_LINK_STRONG); 1380 object_class_property_add(oc, "head", "uint32", 1381 qemu_graphic_console_prop_get_head, 1382 NULL, NULL, NULL); 1383 } 1384 1385 static void 1386 qemu_graphic_console_init(Object *obj) 1387 { 1388 } 1389 1390 static void 1391 qemu_text_console_finalize(Object *obj) 1392 { 1393 } 1394 1395 static void 1396 qemu_text_console_class_init(ObjectClass *oc, void *data) 1397 { 1398 if (!cursor_timer) { 1399 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1400 text_console_update_cursor, NULL); 1401 } 1402 } 1403 1404 static const GraphicHwOps text_console_ops = { 1405 .invalidate = text_console_invalidate, 1406 .text_update = text_console_update, 1407 }; 1408 1409 static void 1410 qemu_text_console_init(Object *obj) 1411 { 1412 QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj); 1413 1414 fifo8_create(&c->out_fifo, 16); 1415 c->total_height = DEFAULT_BACKSCROLL; 1416 QEMU_CONSOLE(c)->hw_ops = &text_console_ops; 1417 QEMU_CONSOLE(c)->hw = c; 1418 } 1419 1420 static void 1421 qemu_fixed_text_console_finalize(Object *obj) 1422 { 1423 } 1424 1425 static void 1426 qemu_fixed_text_console_class_init(ObjectClass *oc, void *data) 1427 { 1428 } 1429 1430 static void 1431 qemu_fixed_text_console_init(Object *obj) 1432 { 1433 } 1434 1435 #ifdef WIN32 1436 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, 1437 HANDLE h, uint32_t offset) 1438 { 1439 assert(!surface->handle); 1440 1441 surface->handle = h; 1442 surface->handle_offset = offset; 1443 } 1444 1445 static void 1446 win32_pixman_image_destroy(pixman_image_t *image, void *data) 1447 { 1448 DisplaySurface *surface = data; 1449 1450 if (!surface->handle) { 1451 return; 1452 } 1453 1454 assert(surface->handle_offset == 0); 1455 1456 qemu_win32_map_free( 1457 pixman_image_get_data(surface->image), 1458 surface->handle, 1459 &error_warn 1460 ); 1461 } 1462 #endif 1463 1464 DisplaySurface *qemu_create_displaysurface(int width, int height) 1465 { 1466 DisplaySurface *surface; 1467 void *bits = NULL; 1468 #ifdef WIN32 1469 HANDLE handle = NULL; 1470 #endif 1471 1472 trace_displaysurface_create(width, height); 1473 1474 #ifdef WIN32 1475 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort); 1476 #endif 1477 1478 surface = qemu_create_displaysurface_from( 1479 width, height, 1480 PIXMAN_x8r8g8b8, 1481 width * 4, bits 1482 ); 1483 surface->flags = QEMU_ALLOCATED_FLAG; 1484 1485 #ifdef WIN32 1486 qemu_displaysurface_win32_set_handle(surface, handle, 0); 1487 #endif 1488 return surface; 1489 } 1490 1491 DisplaySurface *qemu_create_displaysurface_from(int width, int height, 1492 pixman_format_code_t format, 1493 int linesize, uint8_t *data) 1494 { 1495 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1496 1497 trace_displaysurface_create_from(surface, width, height, format); 1498 surface->format = format; 1499 surface->image = pixman_image_create_bits(surface->format, 1500 width, height, 1501 (void *)data, linesize); 1502 assert(surface->image != NULL); 1503 #ifdef WIN32 1504 pixman_image_set_destroy_function(surface->image, 1505 win32_pixman_image_destroy, surface); 1506 #endif 1507 1508 return surface; 1509 } 1510 1511 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) 1512 { 1513 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1514 1515 trace_displaysurface_create_pixman(surface); 1516 surface->format = pixman_image_get_format(image); 1517 surface->image = pixman_image_ref(image); 1518 1519 return surface; 1520 } 1521 1522 DisplaySurface *qemu_create_placeholder_surface(int w, int h, 1523 const char *msg) 1524 { 1525 DisplaySurface *surface = qemu_create_displaysurface(w, h); 1526 pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK; 1527 pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY; 1528 pixman_image_t *glyph; 1529 int len, x, y, i; 1530 1531 len = strlen(msg); 1532 x = (w / FONT_WIDTH - len) / 2; 1533 y = (h / FONT_HEIGHT - 1) / 2; 1534 for (i = 0; i < len; i++) { 1535 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]); 1536 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg, 1537 x+i, y, FONT_WIDTH, FONT_HEIGHT); 1538 qemu_pixman_image_unref(glyph); 1539 } 1540 surface->flags |= QEMU_PLACEHOLDER_FLAG; 1541 return surface; 1542 } 1543 1544 void qemu_free_displaysurface(DisplaySurface *surface) 1545 { 1546 if (surface == NULL) { 1547 return; 1548 } 1549 trace_displaysurface_free(surface); 1550 qemu_pixman_image_unref(surface->image); 1551 g_free(surface); 1552 } 1553 1554 bool console_has_gl(QemuConsole *con) 1555 { 1556 return con->gl != NULL; 1557 } 1558 1559 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) 1560 { 1561 if (dcl->ops->dpy_has_dmabuf) { 1562 return dcl->ops->dpy_has_dmabuf(dcl); 1563 } 1564 1565 if (dcl->ops->dpy_gl_scanout_dmabuf) { 1566 return true; 1567 } 1568 1569 return false; 1570 } 1571 1572 static bool console_compatible_with(QemuConsole *con, 1573 DisplayChangeListener *dcl, Error **errp) 1574 { 1575 int flags; 1576 1577 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; 1578 1579 if (console_has_gl(con) && 1580 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) { 1581 error_setg(errp, "Display %s is incompatible with the GL context", 1582 dcl->ops->dpy_name); 1583 return false; 1584 } 1585 1586 if (flags & GRAPHIC_FLAGS_GL && 1587 !console_has_gl(con)) { 1588 error_setg(errp, "The console requires a GL context."); 1589 return false; 1590 1591 } 1592 1593 if (flags & GRAPHIC_FLAGS_DMABUF && 1594 !displaychangelistener_has_dmabuf(dcl)) { 1595 error_setg(errp, "The console requires display DMABUF support."); 1596 return false; 1597 } 1598 1599 return true; 1600 } 1601 1602 void console_handle_touch_event(QemuConsole *con, 1603 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX], 1604 uint64_t num_slot, 1605 int width, int height, 1606 double x, double y, 1607 InputMultiTouchType type, 1608 Error **errp) 1609 { 1610 struct touch_slot *slot; 1611 bool needs_sync = false; 1612 int update; 1613 int i; 1614 1615 if (num_slot >= INPUT_EVENT_SLOTS_MAX) { 1616 error_setg(errp, 1617 "Unexpected touch slot number: % " PRId64" >= %d", 1618 num_slot, INPUT_EVENT_SLOTS_MAX); 1619 return; 1620 } 1621 1622 slot = &touch_slots[num_slot]; 1623 slot->x = x; 1624 slot->y = y; 1625 1626 if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) { 1627 slot->tracking_id = num_slot; 1628 } 1629 1630 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) { 1631 if (i == num_slot) { 1632 update = type; 1633 } else { 1634 update = INPUT_MULTI_TOUCH_TYPE_UPDATE; 1635 } 1636 1637 slot = &touch_slots[i]; 1638 1639 if (slot->tracking_id == -1) { 1640 continue; 1641 } 1642 1643 if (update == INPUT_MULTI_TOUCH_TYPE_END) { 1644 slot->tracking_id = -1; 1645 qemu_input_queue_mtt(con, update, i, slot->tracking_id); 1646 needs_sync = true; 1647 } else { 1648 qemu_input_queue_mtt(con, update, i, slot->tracking_id); 1649 qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true); 1650 qemu_input_queue_mtt_abs(con, 1651 INPUT_AXIS_X, (int) slot->x, 1652 0, width, 1653 i, slot->tracking_id); 1654 qemu_input_queue_mtt_abs(con, 1655 INPUT_AXIS_Y, (int) slot->y, 1656 0, height, 1657 i, slot->tracking_id); 1658 needs_sync = true; 1659 } 1660 } 1661 1662 if (needs_sync) { 1663 qemu_input_event_sync(); 1664 } 1665 } 1666 1667 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl) 1668 { 1669 /* display has opengl support */ 1670 assert(con); 1671 if (con->gl) { 1672 error_report("The console already has an OpenGL context."); 1673 exit(1); 1674 } 1675 con->gl = gl; 1676 } 1677 1678 static void 1679 dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con) 1680 { 1681 if (con && con->cursor && dcl->ops->dpy_cursor_define) { 1682 dcl->ops->dpy_cursor_define(dcl, con->cursor); 1683 } 1684 if (con && dcl->ops->dpy_mouse_set) { 1685 dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on); 1686 } 1687 } 1688 void register_displaychangelistener(DisplayChangeListener *dcl) 1689 { 1690 QemuConsole *con; 1691 1692 assert(!dcl->ds); 1693 1694 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); 1695 dcl->ds = get_alloc_displaystate(); 1696 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); 1697 gui_setup_refresh(dcl->ds); 1698 if (dcl->con) { 1699 dcl->con->dcls++; 1700 con = dcl->con; 1701 } else { 1702 con = active_console; 1703 } 1704 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL); 1705 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 1706 dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(con)); 1707 } 1708 text_console_update_cursor(NULL); 1709 } 1710 1711 void update_displaychangelistener(DisplayChangeListener *dcl, 1712 uint64_t interval) 1713 { 1714 DisplayState *ds = dcl->ds; 1715 1716 dcl->update_interval = interval; 1717 if (!ds->refreshing && ds->update_interval > interval) { 1718 timer_mod(ds->gui_timer, ds->last_update + interval); 1719 } 1720 } 1721 1722 void unregister_displaychangelistener(DisplayChangeListener *dcl) 1723 { 1724 DisplayState *ds = dcl->ds; 1725 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name); 1726 if (dcl->con) { 1727 dcl->con->dcls--; 1728 } 1729 QLIST_REMOVE(dcl, next); 1730 dcl->ds = NULL; 1731 gui_setup_refresh(ds); 1732 } 1733 1734 static void dpy_set_ui_info_timer(void *opaque) 1735 { 1736 QemuConsole *con = opaque; 1737 uint32_t head = qemu_console_get_head(con); 1738 1739 con->hw_ops->ui_info(con->hw, head, &con->ui_info); 1740 } 1741 1742 bool dpy_ui_info_supported(QemuConsole *con) 1743 { 1744 if (con == NULL) { 1745 con = active_console; 1746 } 1747 1748 return con->hw_ops->ui_info != NULL; 1749 } 1750 1751 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con) 1752 { 1753 if (con == NULL) { 1754 con = active_console; 1755 } 1756 1757 return &con->ui_info; 1758 } 1759 1760 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay) 1761 { 1762 if (con == NULL) { 1763 con = active_console; 1764 } 1765 1766 if (!dpy_ui_info_supported(con)) { 1767 return -1; 1768 } 1769 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) { 1770 /* nothing changed -- ignore */ 1771 return 0; 1772 } 1773 1774 /* 1775 * Typically we get a flood of these as the user resizes the window. 1776 * Wait until the dust has settled (one second without updates), then 1777 * go notify the guest. 1778 */ 1779 con->ui_info = *info; 1780 timer_mod(con->ui_timer, 1781 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0)); 1782 return 0; 1783 } 1784 1785 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 1786 { 1787 DisplayState *s = con->ds; 1788 DisplayChangeListener *dcl; 1789 int width = qemu_console_get_width(con, x + w); 1790 int height = qemu_console_get_height(con, y + h); 1791 1792 x = MAX(x, 0); 1793 y = MAX(y, 0); 1794 x = MIN(x, width); 1795 y = MIN(y, height); 1796 w = MIN(w, width - x); 1797 h = MIN(h, height - y); 1798 1799 if (!qemu_console_is_visible(con)) { 1800 return; 1801 } 1802 dpy_gfx_update_texture(con, con->surface, x, y, w, h); 1803 QLIST_FOREACH(dcl, &s->listeners, next) { 1804 if (con != (dcl->con ? dcl->con : active_console)) { 1805 continue; 1806 } 1807 if (dcl->ops->dpy_gfx_update) { 1808 dcl->ops->dpy_gfx_update(dcl, x, y, w, h); 1809 } 1810 } 1811 } 1812 1813 void dpy_gfx_update_full(QemuConsole *con) 1814 { 1815 int w = qemu_console_get_width(con, 0); 1816 int h = qemu_console_get_height(con, 0); 1817 1818 dpy_gfx_update(con, 0, 0, w, h); 1819 } 1820 1821 void dpy_gfx_replace_surface(QemuConsole *con, 1822 DisplaySurface *surface) 1823 { 1824 static const char placeholder_msg[] = "Display output is not active."; 1825 DisplayState *s = con->ds; 1826 DisplaySurface *old_surface = con->surface; 1827 DisplaySurface *new_surface = surface; 1828 DisplayChangeListener *dcl; 1829 int width; 1830 int height; 1831 1832 if (!surface) { 1833 if (old_surface) { 1834 width = surface_width(old_surface); 1835 height = surface_height(old_surface); 1836 } else { 1837 width = 640; 1838 height = 480; 1839 } 1840 1841 new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg); 1842 } 1843 1844 assert(old_surface != new_surface); 1845 1846 con->scanout.kind = SCANOUT_SURFACE; 1847 con->surface = new_surface; 1848 dpy_gfx_create_texture(con, new_surface); 1849 QLIST_FOREACH(dcl, &s->listeners, next) { 1850 if (con != (dcl->con ? dcl->con : active_console)) { 1851 continue; 1852 } 1853 displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE); 1854 } 1855 dpy_gfx_destroy_texture(con, old_surface); 1856 qemu_free_displaysurface(old_surface); 1857 } 1858 1859 bool dpy_gfx_check_format(QemuConsole *con, 1860 pixman_format_code_t format) 1861 { 1862 DisplayChangeListener *dcl; 1863 DisplayState *s = con->ds; 1864 1865 QLIST_FOREACH(dcl, &s->listeners, next) { 1866 if (dcl->con && dcl->con != con) { 1867 /* dcl bound to another console -> skip */ 1868 continue; 1869 } 1870 if (dcl->ops->dpy_gfx_check_format) { 1871 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) { 1872 return false; 1873 } 1874 } else { 1875 /* default is to allow native 32 bpp only */ 1876 if (format != qemu_default_pixman_format(32, true)) { 1877 return false; 1878 } 1879 } 1880 } 1881 return true; 1882 } 1883 1884 static void dpy_refresh(DisplayState *s) 1885 { 1886 DisplayChangeListener *dcl; 1887 1888 QLIST_FOREACH(dcl, &s->listeners, next) { 1889 if (dcl->ops->dpy_refresh) { 1890 dcl->ops->dpy_refresh(dcl); 1891 } 1892 } 1893 } 1894 1895 void dpy_text_cursor(QemuConsole *con, int x, int y) 1896 { 1897 DisplayState *s = con->ds; 1898 DisplayChangeListener *dcl; 1899 1900 if (!qemu_console_is_visible(con)) { 1901 return; 1902 } 1903 QLIST_FOREACH(dcl, &s->listeners, next) { 1904 if (con != (dcl->con ? dcl->con : active_console)) { 1905 continue; 1906 } 1907 if (dcl->ops->dpy_text_cursor) { 1908 dcl->ops->dpy_text_cursor(dcl, x, y); 1909 } 1910 } 1911 } 1912 1913 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) 1914 { 1915 DisplayState *s = con->ds; 1916 DisplayChangeListener *dcl; 1917 1918 if (!qemu_console_is_visible(con)) { 1919 return; 1920 } 1921 QLIST_FOREACH(dcl, &s->listeners, next) { 1922 if (con != (dcl->con ? dcl->con : active_console)) { 1923 continue; 1924 } 1925 if (dcl->ops->dpy_text_update) { 1926 dcl->ops->dpy_text_update(dcl, x, y, w, h); 1927 } 1928 } 1929 } 1930 1931 void dpy_text_resize(QemuConsole *con, int w, int h) 1932 { 1933 DisplayState *s = con->ds; 1934 DisplayChangeListener *dcl; 1935 1936 if (!qemu_console_is_visible(con)) { 1937 return; 1938 } 1939 QLIST_FOREACH(dcl, &s->listeners, next) { 1940 if (con != (dcl->con ? dcl->con : active_console)) { 1941 continue; 1942 } 1943 if (dcl->ops->dpy_text_resize) { 1944 dcl->ops->dpy_text_resize(dcl, w, h); 1945 } 1946 } 1947 } 1948 1949 void dpy_mouse_set(QemuConsole *c, int x, int y, int on) 1950 { 1951 QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); 1952 DisplayState *s = c->ds; 1953 DisplayChangeListener *dcl; 1954 1955 con->cursor_x = x; 1956 con->cursor_y = y; 1957 con->cursor_on = on; 1958 if (!qemu_console_is_visible(c)) { 1959 return; 1960 } 1961 QLIST_FOREACH(dcl, &s->listeners, next) { 1962 if (c != (dcl->con ? dcl->con : active_console)) { 1963 continue; 1964 } 1965 if (dcl->ops->dpy_mouse_set) { 1966 dcl->ops->dpy_mouse_set(dcl, x, y, on); 1967 } 1968 } 1969 } 1970 1971 void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor) 1972 { 1973 QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); 1974 DisplayState *s = c->ds; 1975 DisplayChangeListener *dcl; 1976 1977 cursor_unref(con->cursor); 1978 con->cursor = cursor_ref(cursor); 1979 if (!qemu_console_is_visible(c)) { 1980 return; 1981 } 1982 QLIST_FOREACH(dcl, &s->listeners, next) { 1983 if (c != (dcl->con ? dcl->con : active_console)) { 1984 continue; 1985 } 1986 if (dcl->ops->dpy_cursor_define) { 1987 dcl->ops->dpy_cursor_define(dcl, cursor); 1988 } 1989 } 1990 } 1991 1992 bool dpy_cursor_define_supported(QemuConsole *con) 1993 { 1994 DisplayState *s = con->ds; 1995 DisplayChangeListener *dcl; 1996 1997 QLIST_FOREACH(dcl, &s->listeners, next) { 1998 if (dcl->ops->dpy_cursor_define) { 1999 return true; 2000 } 2001 } 2002 return false; 2003 } 2004 2005 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, 2006 struct QEMUGLParams *qparams) 2007 { 2008 assert(con->gl); 2009 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams); 2010 } 2011 2012 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx) 2013 { 2014 assert(con->gl); 2015 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx); 2016 } 2017 2018 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) 2019 { 2020 assert(con->gl); 2021 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); 2022 } 2023 2024 void dpy_gl_scanout_disable(QemuConsole *con) 2025 { 2026 DisplayState *s = con->ds; 2027 DisplayChangeListener *dcl; 2028 2029 if (con->scanout.kind != SCANOUT_SURFACE) { 2030 con->scanout.kind = SCANOUT_NONE; 2031 } 2032 QLIST_FOREACH(dcl, &s->listeners, next) { 2033 if (con != (dcl->con ? dcl->con : active_console)) { 2034 continue; 2035 } 2036 if (dcl->ops->dpy_gl_scanout_disable) { 2037 dcl->ops->dpy_gl_scanout_disable(dcl); 2038 } 2039 } 2040 } 2041 2042 void dpy_gl_scanout_texture(QemuConsole *con, 2043 uint32_t backing_id, 2044 bool backing_y_0_top, 2045 uint32_t backing_width, 2046 uint32_t backing_height, 2047 uint32_t x, uint32_t y, 2048 uint32_t width, uint32_t height, 2049 void *d3d_tex2d) 2050 { 2051 DisplayState *s = con->ds; 2052 DisplayChangeListener *dcl; 2053 2054 con->scanout.kind = SCANOUT_TEXTURE; 2055 con->scanout.texture = (ScanoutTexture) { 2056 backing_id, backing_y_0_top, backing_width, backing_height, 2057 x, y, width, height, d3d_tex2d, 2058 }; 2059 QLIST_FOREACH(dcl, &s->listeners, next) { 2060 if (con != (dcl->con ? dcl->con : active_console)) { 2061 continue; 2062 } 2063 if (dcl->ops->dpy_gl_scanout_texture) { 2064 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, 2065 backing_y_0_top, 2066 backing_width, backing_height, 2067 x, y, width, height, 2068 d3d_tex2d); 2069 } 2070 } 2071 } 2072 2073 void dpy_gl_scanout_dmabuf(QemuConsole *con, 2074 QemuDmaBuf *dmabuf) 2075 { 2076 DisplayState *s = con->ds; 2077 DisplayChangeListener *dcl; 2078 2079 con->scanout.kind = SCANOUT_DMABUF; 2080 con->scanout.dmabuf = dmabuf; 2081 QLIST_FOREACH(dcl, &s->listeners, next) { 2082 if (con != (dcl->con ? dcl->con : active_console)) { 2083 continue; 2084 } 2085 if (dcl->ops->dpy_gl_scanout_dmabuf) { 2086 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); 2087 } 2088 } 2089 } 2090 2091 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, 2092 bool have_hot, uint32_t hot_x, uint32_t hot_y) 2093 { 2094 DisplayState *s = con->ds; 2095 DisplayChangeListener *dcl; 2096 2097 QLIST_FOREACH(dcl, &s->listeners, next) { 2098 if (con != (dcl->con ? dcl->con : active_console)) { 2099 continue; 2100 } 2101 if (dcl->ops->dpy_gl_cursor_dmabuf) { 2102 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf, 2103 have_hot, hot_x, hot_y); 2104 } 2105 } 2106 } 2107 2108 void dpy_gl_cursor_position(QemuConsole *con, 2109 uint32_t pos_x, uint32_t pos_y) 2110 { 2111 DisplayState *s = con->ds; 2112 DisplayChangeListener *dcl; 2113 2114 QLIST_FOREACH(dcl, &s->listeners, next) { 2115 if (con != (dcl->con ? dcl->con : active_console)) { 2116 continue; 2117 } 2118 if (dcl->ops->dpy_gl_cursor_position) { 2119 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y); 2120 } 2121 } 2122 } 2123 2124 void dpy_gl_release_dmabuf(QemuConsole *con, 2125 QemuDmaBuf *dmabuf) 2126 { 2127 DisplayState *s = con->ds; 2128 DisplayChangeListener *dcl; 2129 2130 QLIST_FOREACH(dcl, &s->listeners, next) { 2131 if (con != (dcl->con ? dcl->con : active_console)) { 2132 continue; 2133 } 2134 if (dcl->ops->dpy_gl_release_dmabuf) { 2135 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf); 2136 } 2137 } 2138 } 2139 2140 void dpy_gl_update(QemuConsole *con, 2141 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 2142 { 2143 DisplayState *s = con->ds; 2144 DisplayChangeListener *dcl; 2145 2146 assert(con->gl); 2147 2148 graphic_hw_gl_block(con, true); 2149 QLIST_FOREACH(dcl, &s->listeners, next) { 2150 if (con != (dcl->con ? dcl->con : active_console)) { 2151 continue; 2152 } 2153 if (dcl->ops->dpy_gl_update) { 2154 dcl->ops->dpy_gl_update(dcl, x, y, w, h); 2155 } 2156 } 2157 graphic_hw_gl_block(con, false); 2158 } 2159 2160 /***********************************************************/ 2161 /* register display */ 2162 2163 /* console.c internal use only */ 2164 static DisplayState *get_alloc_displaystate(void) 2165 { 2166 if (!display_state) { 2167 display_state = g_new0(DisplayState, 1); 2168 } 2169 return display_state; 2170 } 2171 2172 /* 2173 * Called by main(), after creating QemuConsoles 2174 * and before initializing ui (sdl/vnc/...). 2175 */ 2176 DisplayState *init_displaystate(void) 2177 { 2178 gchar *name; 2179 QemuConsole *con; 2180 2181 QTAILQ_FOREACH(con, &consoles, next) { 2182 /* Hook up into the qom tree here (not in object_new()), once 2183 * all QemuConsoles are created and the order / numbering 2184 * doesn't change any more */ 2185 name = g_strdup_printf("console[%d]", con->index); 2186 object_property_add_child(container_get(object_get_root(), "/backend"), 2187 name, OBJECT(con)); 2188 g_free(name); 2189 } 2190 2191 return display_state; 2192 } 2193 2194 void graphic_console_set_hwops(QemuConsole *con, 2195 const GraphicHwOps *hw_ops, 2196 void *opaque) 2197 { 2198 con->hw_ops = hw_ops; 2199 con->hw = opaque; 2200 } 2201 2202 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 2203 const GraphicHwOps *hw_ops, 2204 void *opaque) 2205 { 2206 static const char noinit[] = 2207 "Guest has not initialized the display (yet)."; 2208 int width = 640; 2209 int height = 480; 2210 QemuConsole *s; 2211 DisplaySurface *surface; 2212 2213 s = qemu_graphic_console_lookup_unused(); 2214 if (s) { 2215 trace_console_gfx_reuse(s->index); 2216 width = qemu_console_get_width(s, 0); 2217 height = qemu_console_get_height(s, 0); 2218 } else { 2219 trace_console_gfx_new(); 2220 s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE); 2221 } 2222 QEMU_GRAPHIC_CONSOLE(s)->head = head; 2223 graphic_console_set_hwops(s, hw_ops, opaque); 2224 if (dev) { 2225 object_property_set_link(OBJECT(s), "device", OBJECT(dev), 2226 &error_abort); 2227 } 2228 2229 surface = qemu_create_placeholder_surface(width, height, noinit); 2230 dpy_gfx_replace_surface(s, surface); 2231 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 2232 graphic_hw_gl_unblock_timer, s); 2233 return s; 2234 } 2235 2236 static const GraphicHwOps unused_ops = { 2237 /* no callbacks */ 2238 }; 2239 2240 void graphic_console_close(QemuConsole *con) 2241 { 2242 static const char unplugged[] = 2243 "Guest display has been unplugged"; 2244 DisplaySurface *surface; 2245 int width = qemu_console_get_width(con, 640); 2246 int height = qemu_console_get_height(con, 480); 2247 2248 trace_console_gfx_close(con->index); 2249 object_property_set_link(OBJECT(con), "device", NULL, &error_abort); 2250 graphic_console_set_hwops(con, &unused_ops, NULL); 2251 2252 if (con->gl) { 2253 dpy_gl_scanout_disable(con); 2254 } 2255 surface = qemu_create_placeholder_surface(width, height, unplugged); 2256 dpy_gfx_replace_surface(con, surface); 2257 } 2258 2259 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 2260 { 2261 QemuConsole *con; 2262 2263 QTAILQ_FOREACH(con, &consoles, next) { 2264 if (con->index == index) { 2265 return con; 2266 } 2267 } 2268 return NULL; 2269 } 2270 2271 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 2272 { 2273 QemuConsole *con; 2274 Object *obj; 2275 uint32_t h; 2276 2277 QTAILQ_FOREACH(con, &consoles, next) { 2278 obj = object_property_get_link(OBJECT(con), 2279 "device", &error_abort); 2280 if (DEVICE(obj) != dev) { 2281 continue; 2282 } 2283 h = object_property_get_uint(OBJECT(con), 2284 "head", &error_abort); 2285 if (h != head) { 2286 continue; 2287 } 2288 return con; 2289 } 2290 return NULL; 2291 } 2292 2293 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, 2294 uint32_t head, Error **errp) 2295 { 2296 DeviceState *dev; 2297 QemuConsole *con; 2298 2299 dev = qdev_find_recursive(sysbus_get_default(), device_id); 2300 if (dev == NULL) { 2301 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 2302 "Device '%s' not found", device_id); 2303 return NULL; 2304 } 2305 2306 con = qemu_console_lookup_by_device(dev, head); 2307 if (con == NULL) { 2308 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", 2309 device_id, head); 2310 return NULL; 2311 } 2312 2313 return con; 2314 } 2315 2316 static QemuConsole *qemu_graphic_console_lookup_unused(void) 2317 { 2318 QemuConsole *con; 2319 Object *obj; 2320 2321 QTAILQ_FOREACH(con, &consoles, next) { 2322 if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) { 2323 continue; 2324 } 2325 obj = object_property_get_link(OBJECT(con), 2326 "device", &error_abort); 2327 if (obj != NULL) { 2328 continue; 2329 } 2330 return con; 2331 } 2332 return NULL; 2333 } 2334 2335 QEMUCursor *qemu_console_get_cursor(QemuConsole *con) 2336 { 2337 if (con == NULL) { 2338 con = active_console; 2339 } 2340 return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL; 2341 } 2342 2343 bool qemu_console_is_visible(QemuConsole *con) 2344 { 2345 return (con == active_console) || (con->dcls > 0); 2346 } 2347 2348 bool qemu_console_is_graphic(QemuConsole *con) 2349 { 2350 if (con == NULL) { 2351 con = active_console; 2352 } 2353 return con && QEMU_IS_GRAPHIC_CONSOLE(con); 2354 } 2355 2356 bool qemu_console_is_fixedsize(QemuConsole *con) 2357 { 2358 if (con == NULL) { 2359 con = active_console; 2360 } 2361 return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con)); 2362 } 2363 2364 bool qemu_console_is_gl_blocked(QemuConsole *con) 2365 { 2366 assert(con != NULL); 2367 return con->gl_block; 2368 } 2369 2370 bool qemu_console_is_multihead(DeviceState *dev) 2371 { 2372 QemuConsole *con; 2373 Object *obj; 2374 uint32_t f = 0xffffffff; 2375 uint32_t h; 2376 2377 QTAILQ_FOREACH(con, &consoles, next) { 2378 obj = object_property_get_link(OBJECT(con), 2379 "device", &error_abort); 2380 if (DEVICE(obj) != dev) { 2381 continue; 2382 } 2383 2384 h = object_property_get_uint(OBJECT(con), 2385 "head", &error_abort); 2386 if (f == 0xffffffff) { 2387 f = h; 2388 } else if (h != f) { 2389 return true; 2390 } 2391 } 2392 return false; 2393 } 2394 2395 char *qemu_console_get_label(QemuConsole *con) 2396 { 2397 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 2398 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con); 2399 if (c->device) { 2400 DeviceState *dev; 2401 bool multihead; 2402 2403 dev = DEVICE(c->device); 2404 multihead = qemu_console_is_multihead(dev); 2405 if (multihead) { 2406 return g_strdup_printf("%s.%d", dev->id ? 2407 dev->id : 2408 object_get_typename(c->device), 2409 c->head); 2410 } else { 2411 return g_strdup_printf("%s", dev->id ? 2412 dev->id : 2413 object_get_typename(c->device)); 2414 } 2415 } 2416 return g_strdup("VGA"); 2417 } else if (QEMU_IS_TEXT_CONSOLE(con)) { 2418 QemuTextConsole *c = QEMU_TEXT_CONSOLE(con); 2419 if (c->chr && c->chr->label) { 2420 return g_strdup(c->chr->label); 2421 } 2422 } 2423 2424 return g_strdup_printf("vc%d", con->index); 2425 } 2426 2427 int qemu_console_get_index(QemuConsole *con) 2428 { 2429 if (con == NULL) { 2430 con = active_console; 2431 } 2432 return con ? con->index : -1; 2433 } 2434 2435 uint32_t qemu_console_get_head(QemuConsole *con) 2436 { 2437 if (con == NULL) { 2438 con = active_console; 2439 } 2440 if (con == NULL) { 2441 return -1; 2442 } 2443 if (QEMU_IS_GRAPHIC_CONSOLE(con)) { 2444 return QEMU_GRAPHIC_CONSOLE(con)->head; 2445 } 2446 return 0; 2447 } 2448 2449 int qemu_console_get_width(QemuConsole *con, int fallback) 2450 { 2451 if (con == NULL) { 2452 con = active_console; 2453 } 2454 if (con == NULL) { 2455 return fallback; 2456 } 2457 switch (con->scanout.kind) { 2458 case SCANOUT_DMABUF: 2459 return con->scanout.dmabuf->width; 2460 case SCANOUT_TEXTURE: 2461 return con->scanout.texture.width; 2462 case SCANOUT_SURFACE: 2463 return surface_width(con->surface); 2464 default: 2465 return fallback; 2466 } 2467 } 2468 2469 int qemu_console_get_height(QemuConsole *con, int fallback) 2470 { 2471 if (con == NULL) { 2472 con = active_console; 2473 } 2474 if (con == NULL) { 2475 return fallback; 2476 } 2477 switch (con->scanout.kind) { 2478 case SCANOUT_DMABUF: 2479 return con->scanout.dmabuf->height; 2480 case SCANOUT_TEXTURE: 2481 return con->scanout.texture.height; 2482 case SCANOUT_SURFACE: 2483 return surface_height(con->surface); 2484 default: 2485 return fallback; 2486 } 2487 } 2488 2489 static void vc_chr_accept_input(Chardev *chr) 2490 { 2491 VCChardev *drv = VC_CHARDEV(chr); 2492 2493 kbd_send_chars(drv->console); 2494 } 2495 2496 static void vc_chr_set_echo(Chardev *chr, bool echo) 2497 { 2498 VCChardev *drv = VC_CHARDEV(chr); 2499 2500 drv->console->echo = echo; 2501 } 2502 2503 int qemu_invalidate_text_consoles(void) 2504 { 2505 QemuConsole *s; 2506 int count = 0; 2507 2508 QTAILQ_FOREACH(s, &consoles, next) { 2509 if (qemu_console_is_graphic(s) || 2510 !qemu_console_is_visible(s)) { 2511 continue; 2512 } 2513 count++; 2514 graphic_hw_invalidate(s); 2515 } 2516 2517 return count; 2518 } 2519 2520 static void text_console_update_cursor(void *opaque) 2521 { 2522 cursor_visible_phase = !cursor_visible_phase; 2523 2524 if (qemu_invalidate_text_consoles()) { 2525 timer_mod(cursor_timer, 2526 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2); 2527 } 2528 } 2529 2530 static void vc_chr_open(Chardev *chr, 2531 ChardevBackend *backend, 2532 bool *be_opened, 2533 Error **errp) 2534 { 2535 ChardevVC *vc = backend->u.vc.data; 2536 VCChardev *drv = VC_CHARDEV(chr); 2537 QemuTextConsole *s; 2538 unsigned width = 0; 2539 unsigned height = 0; 2540 2541 if (vc->has_width) { 2542 width = vc->width; 2543 } else if (vc->has_cols) { 2544 width = vc->cols * FONT_WIDTH; 2545 } 2546 2547 if (vc->has_height) { 2548 height = vc->height; 2549 } else if (vc->has_rows) { 2550 height = vc->rows * FONT_HEIGHT; 2551 } 2552 2553 trace_console_txt_new(width, height); 2554 if (width == 0 || height == 0) { 2555 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE)); 2556 width = qemu_console_get_width(NULL, 80 * FONT_WIDTH); 2557 height = qemu_console_get_height(NULL, 24 * FONT_HEIGHT); 2558 } else { 2559 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); 2560 } 2561 2562 dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height)); 2563 2564 s->chr = chr; 2565 drv->console = s; 2566 2567 /* set current text attributes to default */ 2568 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; 2569 text_console_resize(s); 2570 2571 if (chr->label) { 2572 char *msg; 2573 2574 drv->t_attrib.bgcol = QEMU_COLOR_BLUE; 2575 msg = g_strdup_printf("%s console\r\n", chr->label); 2576 qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true); 2577 g_free(msg); 2578 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT; 2579 } 2580 2581 *be_opened = true; 2582 } 2583 2584 void qemu_console_resize(QemuConsole *s, int width, int height) 2585 { 2586 DisplaySurface *surface = qemu_console_surface(s); 2587 2588 assert(QEMU_IS_GRAPHIC_CONSOLE(s)); 2589 2590 if ((s->scanout.kind != SCANOUT_SURFACE || 2591 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) && 2592 qemu_console_get_width(s, -1) == width && 2593 qemu_console_get_height(s, -1) == height) { 2594 return; 2595 } 2596 2597 surface = qemu_create_displaysurface(width, height); 2598 dpy_gfx_replace_surface(s, surface); 2599 } 2600 2601 DisplaySurface *qemu_console_surface(QemuConsole *console) 2602 { 2603 switch (console->scanout.kind) { 2604 case SCANOUT_SURFACE: 2605 return console->surface; 2606 default: 2607 return NULL; 2608 } 2609 } 2610 2611 PixelFormat qemu_default_pixelformat(int bpp) 2612 { 2613 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 2614 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 2615 return pf; 2616 } 2617 2618 static QemuDisplay *dpys[DISPLAY_TYPE__MAX]; 2619 2620 void qemu_display_register(QemuDisplay *ui) 2621 { 2622 assert(ui->type < DISPLAY_TYPE__MAX); 2623 dpys[ui->type] = ui; 2624 } 2625 2626 bool qemu_display_find_default(DisplayOptions *opts) 2627 { 2628 static DisplayType prio[] = { 2629 #if defined(CONFIG_GTK) 2630 DISPLAY_TYPE_GTK, 2631 #endif 2632 #if defined(CONFIG_SDL) 2633 DISPLAY_TYPE_SDL, 2634 #endif 2635 #if defined(CONFIG_COCOA) 2636 DISPLAY_TYPE_COCOA 2637 #endif 2638 }; 2639 int i; 2640 2641 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) { 2642 if (dpys[prio[i]] == NULL) { 2643 Error *local_err = NULL; 2644 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err); 2645 if (rv < 0) { 2646 error_report_err(local_err); 2647 } 2648 } 2649 if (dpys[prio[i]] == NULL) { 2650 continue; 2651 } 2652 opts->type = prio[i]; 2653 return true; 2654 } 2655 return false; 2656 } 2657 2658 void qemu_display_early_init(DisplayOptions *opts) 2659 { 2660 assert(opts->type < DISPLAY_TYPE__MAX); 2661 if (opts->type == DISPLAY_TYPE_NONE) { 2662 return; 2663 } 2664 if (dpys[opts->type] == NULL) { 2665 Error *local_err = NULL; 2666 int rv = ui_module_load(DisplayType_str(opts->type), &local_err); 2667 if (rv < 0) { 2668 error_report_err(local_err); 2669 } 2670 } 2671 if (dpys[opts->type] == NULL) { 2672 error_report("Display '%s' is not available.", 2673 DisplayType_str(opts->type)); 2674 exit(1); 2675 } 2676 if (dpys[opts->type]->early_init) { 2677 dpys[opts->type]->early_init(opts); 2678 } 2679 } 2680 2681 void qemu_display_init(DisplayState *ds, DisplayOptions *opts) 2682 { 2683 assert(opts->type < DISPLAY_TYPE__MAX); 2684 if (opts->type == DISPLAY_TYPE_NONE) { 2685 return; 2686 } 2687 assert(dpys[opts->type] != NULL); 2688 dpys[opts->type]->init(ds, opts); 2689 } 2690 2691 void qemu_display_help(void) 2692 { 2693 int idx; 2694 2695 printf("Available display backend types:\n"); 2696 printf("none\n"); 2697 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { 2698 if (!dpys[idx]) { 2699 Error *local_err = NULL; 2700 int rv = ui_module_load(DisplayType_str(idx), &local_err); 2701 if (rv < 0) { 2702 error_report_err(local_err); 2703 } 2704 } 2705 if (dpys[idx]) { 2706 printf("%s\n", DisplayType_str(dpys[idx]->type)); 2707 } 2708 } 2709 } 2710 2711 static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) 2712 { 2713 int val; 2714 ChardevVC *vc; 2715 2716 backend->type = CHARDEV_BACKEND_KIND_VC; 2717 vc = backend->u.vc.data = g_new0(ChardevVC, 1); 2718 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); 2719 2720 val = qemu_opt_get_number(opts, "width", 0); 2721 if (val != 0) { 2722 vc->has_width = true; 2723 vc->width = val; 2724 } 2725 2726 val = qemu_opt_get_number(opts, "height", 0); 2727 if (val != 0) { 2728 vc->has_height = true; 2729 vc->height = val; 2730 } 2731 2732 val = qemu_opt_get_number(opts, "cols", 0); 2733 if (val != 0) { 2734 vc->has_cols = true; 2735 vc->cols = val; 2736 } 2737 2738 val = qemu_opt_get_number(opts, "rows", 0); 2739 if (val != 0) { 2740 vc->has_rows = true; 2741 vc->rows = val; 2742 } 2743 } 2744 2745 static void char_vc_class_init(ObjectClass *oc, void *data) 2746 { 2747 ChardevClass *cc = CHARDEV_CLASS(oc); 2748 2749 cc->parse = vc_chr_parse; 2750 cc->open = vc_chr_open; 2751 cc->chr_write = vc_chr_write; 2752 cc->chr_accept_input = vc_chr_accept_input; 2753 cc->chr_set_echo = vc_chr_set_echo; 2754 } 2755 2756 static const TypeInfo char_vc_type_info = { 2757 .name = TYPE_CHARDEV_VC, 2758 .parent = TYPE_CHARDEV, 2759 .instance_size = sizeof(VCChardev), 2760 .class_init = char_vc_class_init, 2761 }; 2762 2763 void qemu_console_early_init(void) 2764 { 2765 /* set the default vc driver */ 2766 if (!object_class_by_name(TYPE_CHARDEV_VC)) { 2767 type_register(&char_vc_type_info); 2768 } 2769 } 2770