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