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 "qemu/option.h" 30 #include "qemu/timer.h" 31 #include "qmp-commands.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, 1764 QemuDmaBuf *dmabuf, 1765 uint32_t pos_x, uint32_t pos_y) 1766 { 1767 assert(con->gl); 1768 1769 if (con->gl->ops->dpy_gl_cursor_dmabuf) { 1770 con->gl->ops->dpy_gl_cursor_dmabuf(con->gl, dmabuf, pos_x, pos_y); 1771 } 1772 } 1773 1774 void dpy_gl_release_dmabuf(QemuConsole *con, 1775 QemuDmaBuf *dmabuf) 1776 { 1777 assert(con->gl); 1778 1779 if (con->gl->ops->dpy_gl_release_dmabuf) { 1780 con->gl->ops->dpy_gl_release_dmabuf(con->gl, dmabuf); 1781 } 1782 } 1783 1784 void dpy_gl_update(QemuConsole *con, 1785 uint32_t x, uint32_t y, uint32_t w, uint32_t h) 1786 { 1787 assert(con->gl); 1788 con->gl->ops->dpy_gl_update(con->gl, x, y, w, h); 1789 } 1790 1791 /***********************************************************/ 1792 /* register display */ 1793 1794 /* console.c internal use only */ 1795 static DisplayState *get_alloc_displaystate(void) 1796 { 1797 if (!display_state) { 1798 display_state = g_new0(DisplayState, 1); 1799 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1800 text_console_update_cursor, NULL); 1801 } 1802 return display_state; 1803 } 1804 1805 /* 1806 * Called by main(), after creating QemuConsoles 1807 * and before initializing ui (sdl/vnc/...). 1808 */ 1809 DisplayState *init_displaystate(void) 1810 { 1811 gchar *name; 1812 int i; 1813 1814 get_alloc_displaystate(); 1815 for (i = 0; i < nb_consoles; i++) { 1816 if (consoles[i]->console_type != GRAPHIC_CONSOLE && 1817 consoles[i]->ds == NULL) { 1818 text_console_do_init(consoles[i]->chr, display_state); 1819 } 1820 1821 /* Hook up into the qom tree here (not in new_console()), once 1822 * all QemuConsoles are created and the order / numbering 1823 * doesn't change any more */ 1824 name = g_strdup_printf("console[%d]", i); 1825 object_property_add_child(container_get(object_get_root(), "/backend"), 1826 name, OBJECT(consoles[i]), &error_abort); 1827 g_free(name); 1828 } 1829 1830 return display_state; 1831 } 1832 1833 void graphic_console_set_hwops(QemuConsole *con, 1834 const GraphicHwOps *hw_ops, 1835 void *opaque) 1836 { 1837 con->hw_ops = hw_ops; 1838 con->hw = opaque; 1839 } 1840 1841 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 1842 const GraphicHwOps *hw_ops, 1843 void *opaque) 1844 { 1845 static const char noinit[] = 1846 "Guest has not initialized the display (yet)."; 1847 int width = 640; 1848 int height = 480; 1849 QemuConsole *s; 1850 DisplayState *ds; 1851 1852 ds = get_alloc_displaystate(); 1853 trace_console_gfx_new(); 1854 s = new_console(ds, GRAPHIC_CONSOLE, head); 1855 s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s); 1856 graphic_console_set_hwops(s, hw_ops, opaque); 1857 if (dev) { 1858 object_property_set_link(OBJECT(s), OBJECT(dev), "device", 1859 &error_abort); 1860 } 1861 1862 s->surface = qemu_create_message_surface(width, height, noinit); 1863 return s; 1864 } 1865 1866 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 1867 { 1868 if (index >= nb_consoles) { 1869 return NULL; 1870 } 1871 return consoles[index]; 1872 } 1873 1874 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) 1875 { 1876 Object *obj; 1877 uint32_t h; 1878 int i; 1879 1880 for (i = 0; i < nb_consoles; i++) { 1881 if (!consoles[i]) { 1882 continue; 1883 } 1884 obj = object_property_get_link(OBJECT(consoles[i]), 1885 "device", &error_abort); 1886 if (DEVICE(obj) != dev) { 1887 continue; 1888 } 1889 h = object_property_get_uint(OBJECT(consoles[i]), 1890 "head", &error_abort); 1891 if (h != head) { 1892 continue; 1893 } 1894 return consoles[i]; 1895 } 1896 return NULL; 1897 } 1898 1899 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, 1900 uint32_t head, Error **errp) 1901 { 1902 DeviceState *dev; 1903 QemuConsole *con; 1904 1905 dev = qdev_find_recursive(sysbus_get_default(), device_id); 1906 if (dev == NULL) { 1907 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 1908 "Device '%s' not found", device_id); 1909 return NULL; 1910 } 1911 1912 con = qemu_console_lookup_by_device(dev, head); 1913 if (con == NULL) { 1914 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole", 1915 device_id, head); 1916 return NULL; 1917 } 1918 1919 return con; 1920 } 1921 1922 bool qemu_console_is_visible(QemuConsole *con) 1923 { 1924 return (con == active_console) || (con->dcls > 0); 1925 } 1926 1927 bool qemu_console_is_graphic(QemuConsole *con) 1928 { 1929 if (con == NULL) { 1930 con = active_console; 1931 } 1932 return con && (con->console_type == GRAPHIC_CONSOLE); 1933 } 1934 1935 bool qemu_console_is_fixedsize(QemuConsole *con) 1936 { 1937 if (con == NULL) { 1938 con = active_console; 1939 } 1940 return con && (con->console_type != TEXT_CONSOLE); 1941 } 1942 1943 bool qemu_console_is_gl_blocked(QemuConsole *con) 1944 { 1945 assert(con != NULL); 1946 return con->gl_block; 1947 } 1948 1949 char *qemu_console_get_label(QemuConsole *con) 1950 { 1951 if (con->console_type == GRAPHIC_CONSOLE) { 1952 if (con->device) { 1953 return g_strdup(object_get_typename(con->device)); 1954 } 1955 return g_strdup("VGA"); 1956 } else { 1957 if (con->chr && con->chr->label) { 1958 return g_strdup(con->chr->label); 1959 } 1960 return g_strdup_printf("vc%d", con->index); 1961 } 1962 } 1963 1964 int qemu_console_get_index(QemuConsole *con) 1965 { 1966 if (con == NULL) { 1967 con = active_console; 1968 } 1969 return con ? con->index : -1; 1970 } 1971 1972 uint32_t qemu_console_get_head(QemuConsole *con) 1973 { 1974 if (con == NULL) { 1975 con = active_console; 1976 } 1977 return con ? con->head : -1; 1978 } 1979 1980 QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con) 1981 { 1982 assert(con != NULL); 1983 return &con->ui_info; 1984 } 1985 1986 int qemu_console_get_width(QemuConsole *con, int fallback) 1987 { 1988 if (con == NULL) { 1989 con = active_console; 1990 } 1991 return con ? surface_width(con->surface) : fallback; 1992 } 1993 1994 int qemu_console_get_height(QemuConsole *con, int fallback) 1995 { 1996 if (con == NULL) { 1997 con = active_console; 1998 } 1999 return con ? surface_height(con->surface) : fallback; 2000 } 2001 2002 static void vc_chr_set_echo(Chardev *chr, bool echo) 2003 { 2004 VCChardev *drv = VC_CHARDEV(chr); 2005 QemuConsole *s = drv->console; 2006 2007 s->echo = echo; 2008 } 2009 2010 static void text_console_update_cursor_timer(void) 2011 { 2012 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) 2013 + CONSOLE_CURSOR_PERIOD / 2); 2014 } 2015 2016 static void text_console_update_cursor(void *opaque) 2017 { 2018 QemuConsole *s; 2019 int i, count = 0; 2020 2021 cursor_visible_phase = !cursor_visible_phase; 2022 2023 for (i = 0; i < nb_consoles; i++) { 2024 s = consoles[i]; 2025 if (qemu_console_is_graphic(s) || 2026 !qemu_console_is_visible(s)) { 2027 continue; 2028 } 2029 count++; 2030 graphic_hw_invalidate(s); 2031 } 2032 2033 if (count) { 2034 text_console_update_cursor_timer(); 2035 } 2036 } 2037 2038 static const GraphicHwOps text_console_ops = { 2039 .invalidate = text_console_invalidate, 2040 .text_update = text_console_update, 2041 }; 2042 2043 static void text_console_do_init(Chardev *chr, DisplayState *ds) 2044 { 2045 VCChardev *drv = VC_CHARDEV(chr); 2046 QemuConsole *s = drv->console; 2047 int g_width = 80 * FONT_WIDTH; 2048 int g_height = 24 * FONT_HEIGHT; 2049 2050 s->out_fifo.buf = s->out_fifo_buf; 2051 s->out_fifo.buf_size = sizeof(s->out_fifo_buf); 2052 s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s); 2053 s->ds = ds; 2054 2055 s->y_displayed = 0; 2056 s->y_base = 0; 2057 s->total_height = DEFAULT_BACKSCROLL; 2058 s->x = 0; 2059 s->y = 0; 2060 if (!s->surface) { 2061 if (active_console && active_console->surface) { 2062 g_width = surface_width(active_console->surface); 2063 g_height = surface_height(active_console->surface); 2064 } 2065 s->surface = qemu_create_displaysurface(g_width, g_height); 2066 } 2067 2068 s->hw_ops = &text_console_ops; 2069 s->hw = s; 2070 2071 /* Set text attribute defaults */ 2072 s->t_attrib_default.bold = 0; 2073 s->t_attrib_default.uline = 0; 2074 s->t_attrib_default.blink = 0; 2075 s->t_attrib_default.invers = 0; 2076 s->t_attrib_default.unvisible = 0; 2077 s->t_attrib_default.fgcol = QEMU_COLOR_WHITE; 2078 s->t_attrib_default.bgcol = QEMU_COLOR_BLACK; 2079 /* set current text attributes to default */ 2080 s->t_attrib = s->t_attrib_default; 2081 text_console_resize(s); 2082 2083 if (chr->label) { 2084 char msg[128]; 2085 int len; 2086 2087 s->t_attrib.bgcol = QEMU_COLOR_BLUE; 2088 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label); 2089 vc_chr_write(chr, (uint8_t *)msg, len); 2090 s->t_attrib = s->t_attrib_default; 2091 } 2092 2093 qemu_chr_be_event(chr, CHR_EVENT_OPENED); 2094 } 2095 2096 static void vc_chr_open(Chardev *chr, 2097 ChardevBackend *backend, 2098 bool *be_opened, 2099 Error **errp) 2100 { 2101 ChardevVC *vc = backend->u.vc.data; 2102 VCChardev *drv = VC_CHARDEV(chr); 2103 QemuConsole *s; 2104 unsigned width = 0; 2105 unsigned height = 0; 2106 2107 if (vc->has_width) { 2108 width = vc->width; 2109 } else if (vc->has_cols) { 2110 width = vc->cols * FONT_WIDTH; 2111 } 2112 2113 if (vc->has_height) { 2114 height = vc->height; 2115 } else if (vc->has_rows) { 2116 height = vc->rows * FONT_HEIGHT; 2117 } 2118 2119 trace_console_txt_new(width, height); 2120 if (width == 0 || height == 0) { 2121 s = new_console(NULL, TEXT_CONSOLE, 0); 2122 } else { 2123 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); 2124 s->surface = qemu_create_displaysurface(width, height); 2125 } 2126 2127 if (!s) { 2128 error_setg(errp, "cannot create text console"); 2129 return; 2130 } 2131 2132 s->chr = chr; 2133 drv->console = s; 2134 2135 if (display_state) { 2136 text_console_do_init(chr, display_state); 2137 } 2138 2139 /* console/chardev init sometimes completes elsewhere in a 2nd 2140 * stage, so defer OPENED events until they are fully initialized 2141 */ 2142 *be_opened = false; 2143 } 2144 2145 void qemu_console_resize(QemuConsole *s, int width, int height) 2146 { 2147 DisplaySurface *surface; 2148 2149 assert(s->console_type == GRAPHIC_CONSOLE); 2150 2151 if (s->surface && (s->surface->flags & QEMU_ALLOCATED_FLAG) && 2152 pixman_image_get_width(s->surface->image) == width && 2153 pixman_image_get_height(s->surface->image) == height) { 2154 return; 2155 } 2156 2157 surface = qemu_create_displaysurface(width, height); 2158 dpy_gfx_replace_surface(s, surface); 2159 } 2160 2161 DisplaySurface *qemu_console_surface(QemuConsole *console) 2162 { 2163 return console->surface; 2164 } 2165 2166 PixelFormat qemu_default_pixelformat(int bpp) 2167 { 2168 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true); 2169 PixelFormat pf = qemu_pixelformat_from_pixman(fmt); 2170 return pf; 2171 } 2172 2173 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) 2174 { 2175 int val; 2176 ChardevVC *vc; 2177 2178 backend->type = CHARDEV_BACKEND_KIND_VC; 2179 vc = backend->u.vc.data = g_new0(ChardevVC, 1); 2180 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc)); 2181 2182 val = qemu_opt_get_number(opts, "width", 0); 2183 if (val != 0) { 2184 vc->has_width = true; 2185 vc->width = val; 2186 } 2187 2188 val = qemu_opt_get_number(opts, "height", 0); 2189 if (val != 0) { 2190 vc->has_height = true; 2191 vc->height = val; 2192 } 2193 2194 val = qemu_opt_get_number(opts, "cols", 0); 2195 if (val != 0) { 2196 vc->has_cols = true; 2197 vc->cols = val; 2198 } 2199 2200 val = qemu_opt_get_number(opts, "rows", 0); 2201 if (val != 0) { 2202 vc->has_rows = true; 2203 vc->rows = val; 2204 } 2205 } 2206 2207 static const TypeInfo qemu_console_info = { 2208 .name = TYPE_QEMU_CONSOLE, 2209 .parent = TYPE_OBJECT, 2210 .instance_size = sizeof(QemuConsole), 2211 .class_size = sizeof(QemuConsoleClass), 2212 }; 2213 2214 static void char_vc_class_init(ObjectClass *oc, void *data) 2215 { 2216 ChardevClass *cc = CHARDEV_CLASS(oc); 2217 2218 cc->parse = qemu_chr_parse_vc; 2219 cc->open = vc_chr_open; 2220 cc->chr_write = vc_chr_write; 2221 cc->chr_set_echo = vc_chr_set_echo; 2222 } 2223 2224 static const TypeInfo char_vc_type_info = { 2225 .name = TYPE_CHARDEV_VC, 2226 .parent = TYPE_CHARDEV, 2227 .instance_size = sizeof(VCChardev), 2228 .class_init = char_vc_class_init, 2229 }; 2230 2231 void qemu_console_early_init(void) 2232 { 2233 /* set the default vc driver */ 2234 if (!object_class_by_name(TYPE_CHARDEV_VC)) { 2235 type_register(&char_vc_type_info); 2236 } 2237 } 2238 2239 static void register_types(void) 2240 { 2241 type_register_static(&qemu_console_info); 2242 } 2243 2244 type_init(register_types); 2245