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