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