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 #include "qemu-common.h" 25 #include "ui/console.h" 26 #include "hw/qdev-core.h" 27 #include "qemu/timer.h" 28 #include "qmp-commands.h" 29 #include "sysemu/char.h" 30 31 //#define DEBUG_CONSOLE 32 #define DEFAULT_BACKSCROLL 512 33 #define MAX_CONSOLES 12 34 #define CONSOLE_CURSOR_PERIOD 500 35 36 typedef struct TextAttributes { 37 uint8_t fgcol:4; 38 uint8_t bgcol:4; 39 uint8_t bold:1; 40 uint8_t uline:1; 41 uint8_t blink:1; 42 uint8_t invers:1; 43 uint8_t unvisible:1; 44 } TextAttributes; 45 46 typedef struct TextCell { 47 uint8_t ch; 48 TextAttributes t_attrib; 49 } TextCell; 50 51 #define MAX_ESC_PARAMS 3 52 53 enum TTYState { 54 TTY_STATE_NORM, 55 TTY_STATE_ESC, 56 TTY_STATE_CSI, 57 }; 58 59 typedef struct QEMUFIFO { 60 uint8_t *buf; 61 int buf_size; 62 int count, wptr, rptr; 63 } QEMUFIFO; 64 65 static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1) 66 { 67 int l, len; 68 69 l = f->buf_size - f->count; 70 if (len1 > l) 71 len1 = l; 72 len = len1; 73 while (len > 0) { 74 l = f->buf_size - f->wptr; 75 if (l > len) 76 l = len; 77 memcpy(f->buf + f->wptr, buf, l); 78 f->wptr += l; 79 if (f->wptr >= f->buf_size) 80 f->wptr = 0; 81 buf += l; 82 len -= l; 83 } 84 f->count += len1; 85 return len1; 86 } 87 88 static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1) 89 { 90 int l, len; 91 92 if (len1 > f->count) 93 len1 = f->count; 94 len = len1; 95 while (len > 0) { 96 l = f->buf_size - f->rptr; 97 if (l > len) 98 l = len; 99 memcpy(buf, f->buf + f->rptr, l); 100 f->rptr += l; 101 if (f->rptr >= f->buf_size) 102 f->rptr = 0; 103 buf += l; 104 len -= l; 105 } 106 f->count -= len1; 107 return len1; 108 } 109 110 typedef enum { 111 GRAPHIC_CONSOLE, 112 TEXT_CONSOLE, 113 TEXT_CONSOLE_FIXED_SIZE 114 } console_type_t; 115 116 struct QemuConsole { 117 Object parent; 118 119 int index; 120 console_type_t console_type; 121 DisplayState *ds; 122 DisplaySurface *surface; 123 int dcls; 124 125 /* Graphic console state. */ 126 Object *device; 127 const GraphicHwOps *hw_ops; 128 void *hw; 129 130 /* Text console state */ 131 int width; 132 int height; 133 int total_height; 134 int backscroll_height; 135 int x, y; 136 int x_saved, y_saved; 137 int y_displayed; 138 int y_base; 139 TextAttributes t_attrib_default; /* default text attributes */ 140 TextAttributes t_attrib; /* currently active text attributes */ 141 TextCell *cells; 142 int text_x[2], text_y[2], cursor_invalidate; 143 int echo; 144 bool cursor_visible_phase; 145 QEMUTimer *cursor_timer; 146 147 int update_x0; 148 int update_y0; 149 int update_x1; 150 int update_y1; 151 152 enum TTYState state; 153 int esc_params[MAX_ESC_PARAMS]; 154 int nb_esc_params; 155 156 CharDriverState *chr; 157 /* fifo for key pressed */ 158 QEMUFIFO out_fifo; 159 uint8_t out_fifo_buf[16]; 160 QEMUTimer *kbd_timer; 161 }; 162 163 struct DisplayState { 164 struct QEMUTimer *gui_timer; 165 uint64_t last_update; 166 uint64_t update_interval; 167 bool refreshing; 168 bool have_gfx; 169 bool have_text; 170 171 QLIST_HEAD(, DisplayChangeListener) listeners; 172 }; 173 174 static DisplayState *display_state; 175 static QemuConsole *active_console; 176 static QemuConsole *consoles[MAX_CONSOLES]; 177 static int nb_consoles = 0; 178 179 static void text_console_do_init(CharDriverState *chr, DisplayState *ds); 180 static void dpy_refresh(DisplayState *s); 181 182 static void gui_update(void *opaque) 183 { 184 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE; 185 uint64_t dcl_interval; 186 DisplayState *ds = opaque; 187 DisplayChangeListener *dcl; 188 int i; 189 190 ds->refreshing = true; 191 dpy_refresh(ds); 192 ds->refreshing = false; 193 194 QLIST_FOREACH(dcl, &ds->listeners, next) { 195 dcl_interval = dcl->update_interval ? 196 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT; 197 if (interval > dcl_interval) { 198 interval = dcl_interval; 199 } 200 } 201 if (ds->update_interval != interval) { 202 ds->update_interval = interval; 203 for (i = 0; i < nb_consoles; i++) { 204 if (consoles[i]->hw_ops->update_interval) { 205 consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval); 206 } 207 } 208 trace_console_refresh(interval); 209 } 210 ds->last_update = qemu_get_clock_ms(rt_clock); 211 qemu_mod_timer(ds->gui_timer, ds->last_update + interval); 212 } 213 214 static void gui_setup_refresh(DisplayState *ds) 215 { 216 DisplayChangeListener *dcl; 217 bool need_timer = false; 218 bool have_gfx = false; 219 bool have_text = false; 220 221 QLIST_FOREACH(dcl, &ds->listeners, next) { 222 if (dcl->ops->dpy_refresh != NULL) { 223 need_timer = true; 224 } 225 if (dcl->ops->dpy_gfx_update != NULL) { 226 have_gfx = true; 227 } 228 if (dcl->ops->dpy_text_update != NULL) { 229 have_text = true; 230 } 231 } 232 233 if (need_timer && ds->gui_timer == NULL) { 234 ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds); 235 qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock)); 236 } 237 if (!need_timer && ds->gui_timer != NULL) { 238 qemu_del_timer(ds->gui_timer); 239 qemu_free_timer(ds->gui_timer); 240 ds->gui_timer = NULL; 241 } 242 243 ds->have_gfx = have_gfx; 244 ds->have_text = have_text; 245 } 246 247 void graphic_hw_update(QemuConsole *con) 248 { 249 if (!con) { 250 con = active_console; 251 } 252 if (con && con->hw_ops->gfx_update) { 253 con->hw_ops->gfx_update(con->hw); 254 } 255 } 256 257 void graphic_hw_invalidate(QemuConsole *con) 258 { 259 if (!con) { 260 con = active_console; 261 } 262 if (con && con->hw_ops->invalidate) { 263 con->hw_ops->invalidate(con->hw); 264 } 265 } 266 267 static void ppm_save(const char *filename, struct DisplaySurface *ds, 268 Error **errp) 269 { 270 int width = pixman_image_get_width(ds->image); 271 int height = pixman_image_get_height(ds->image); 272 FILE *f; 273 int y; 274 int ret; 275 pixman_image_t *linebuf; 276 277 trace_ppm_save(filename, ds); 278 f = fopen(filename, "wb"); 279 if (!f) { 280 error_setg(errp, "failed to open file '%s': %s", filename, 281 strerror(errno)); 282 return; 283 } 284 ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255); 285 if (ret < 0) { 286 linebuf = NULL; 287 goto write_err; 288 } 289 linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); 290 for (y = 0; y < height; y++) { 291 qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y); 292 clearerr(f); 293 ret = fwrite(pixman_image_get_data(linebuf), 1, 294 pixman_image_get_stride(linebuf), f); 295 (void)ret; 296 if (ferror(f)) { 297 goto write_err; 298 } 299 } 300 301 out: 302 qemu_pixman_image_unref(linebuf); 303 fclose(f); 304 return; 305 306 write_err: 307 error_setg(errp, "failed to write to file '%s': %s", filename, 308 strerror(errno)); 309 unlink(filename); 310 goto out; 311 } 312 313 void qmp_screendump(const char *filename, Error **errp) 314 { 315 QemuConsole *con = qemu_console_lookup_by_index(0); 316 DisplaySurface *surface; 317 318 if (con == NULL) { 319 error_setg(errp, "There is no QemuConsole I can screendump from."); 320 return; 321 } 322 323 graphic_hw_update(con); 324 surface = qemu_console_surface(con); 325 ppm_save(filename, surface, errp); 326 } 327 328 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) 329 { 330 if (!con) { 331 con = active_console; 332 } 333 if (con && con->hw_ops->text_update) { 334 con->hw_ops->text_update(con->hw, chardata); 335 } 336 } 337 338 static void vga_fill_rect(QemuConsole *con, 339 int posx, int posy, int width, int height, 340 pixman_color_t color) 341 { 342 DisplaySurface *surface = qemu_console_surface(con); 343 pixman_rectangle16_t rect = { 344 .x = posx, .y = posy, .width = width, .height = height 345 }; 346 347 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image, 348 &color, 1, &rect); 349 } 350 351 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ 352 static void vga_bitblt(QemuConsole *con, 353 int xs, int ys, int xd, int yd, int w, int h) 354 { 355 DisplaySurface *surface = qemu_console_surface(con); 356 357 pixman_image_composite(PIXMAN_OP_SRC, 358 surface->image, NULL, surface->image, 359 xs, ys, 0, 0, xd, yd, w, h); 360 } 361 362 /***********************************************************/ 363 /* basic char display */ 364 365 #define FONT_HEIGHT 16 366 #define FONT_WIDTH 8 367 368 #include "vgafont.h" 369 370 #ifndef CONFIG_CURSES 371 enum color_names { 372 COLOR_BLACK = 0, 373 COLOR_RED = 1, 374 COLOR_GREEN = 2, 375 COLOR_YELLOW = 3, 376 COLOR_BLUE = 4, 377 COLOR_MAGENTA = 5, 378 COLOR_CYAN = 6, 379 COLOR_WHITE = 7 380 }; 381 #endif 382 383 #define QEMU_RGB(r, g, b) \ 384 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff } 385 386 static const pixman_color_t color_table_rgb[2][8] = { 387 { /* dark */ 388 QEMU_RGB(0x00, 0x00, 0x00), /* black */ 389 QEMU_RGB(0xaa, 0x00, 0x00), /* red */ 390 QEMU_RGB(0x00, 0xaa, 0x00), /* green */ 391 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ 392 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ 393 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ 394 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ 395 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ 396 }, 397 { /* bright */ 398 QEMU_RGB(0x00, 0x00, 0x00), /* black */ 399 QEMU_RGB(0xff, 0x00, 0x00), /* red */ 400 QEMU_RGB(0x00, 0xff, 0x00), /* green */ 401 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ 402 QEMU_RGB(0x00, 0x00, 0xff), /* blue */ 403 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ 404 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ 405 QEMU_RGB(0xff, 0xff, 0xff), /* white */ 406 } 407 }; 408 409 #ifdef DEBUG_CONSOLE 410 static void console_print_text_attributes(TextAttributes *t_attrib, char ch) 411 { 412 if (t_attrib->bold) { 413 printf("b"); 414 } else { 415 printf(" "); 416 } 417 if (t_attrib->uline) { 418 printf("u"); 419 } else { 420 printf(" "); 421 } 422 if (t_attrib->blink) { 423 printf("l"); 424 } else { 425 printf(" "); 426 } 427 if (t_attrib->invers) { 428 printf("i"); 429 } else { 430 printf(" "); 431 } 432 if (t_attrib->unvisible) { 433 printf("n"); 434 } else { 435 printf(" "); 436 } 437 438 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch); 439 } 440 #endif 441 442 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, 443 TextAttributes *t_attrib) 444 { 445 static pixman_image_t *glyphs[256]; 446 DisplaySurface *surface = qemu_console_surface(s); 447 pixman_color_t fgcol, bgcol; 448 449 if (t_attrib->invers) { 450 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; 451 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; 452 } else { 453 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol]; 454 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol]; 455 } 456 457 if (!glyphs[ch]) { 458 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch); 459 } 460 qemu_pixman_glyph_render(glyphs[ch], surface->image, 461 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT); 462 } 463 464 static void text_console_resize(QemuConsole *s) 465 { 466 TextCell *cells, *c, *c1; 467 int w1, x, y, last_width; 468 469 last_width = s->width; 470 s->width = surface_width(s->surface) / FONT_WIDTH; 471 s->height = surface_height(s->surface) / FONT_HEIGHT; 472 473 w1 = last_width; 474 if (s->width < w1) 475 w1 = s->width; 476 477 cells = g_malloc(s->width * s->total_height * sizeof(TextCell)); 478 for(y = 0; y < s->total_height; y++) { 479 c = &cells[y * s->width]; 480 if (w1 > 0) { 481 c1 = &s->cells[y * last_width]; 482 for(x = 0; x < w1; x++) { 483 *c++ = *c1++; 484 } 485 } 486 for(x = w1; x < s->width; x++) { 487 c->ch = ' '; 488 c->t_attrib = s->t_attrib_default; 489 c++; 490 } 491 } 492 g_free(s->cells); 493 s->cells = cells; 494 } 495 496 static inline void text_update_xy(QemuConsole *s, int x, int y) 497 { 498 s->text_x[0] = MIN(s->text_x[0], x); 499 s->text_x[1] = MAX(s->text_x[1], x); 500 s->text_y[0] = MIN(s->text_y[0], y); 501 s->text_y[1] = MAX(s->text_y[1], y); 502 } 503 504 static void invalidate_xy(QemuConsole *s, int x, int y) 505 { 506 if (s->update_x0 > x * FONT_WIDTH) 507 s->update_x0 = x * FONT_WIDTH; 508 if (s->update_y0 > y * FONT_HEIGHT) 509 s->update_y0 = y * FONT_HEIGHT; 510 if (s->update_x1 < (x + 1) * FONT_WIDTH) 511 s->update_x1 = (x + 1) * FONT_WIDTH; 512 if (s->update_y1 < (y + 1) * FONT_HEIGHT) 513 s->update_y1 = (y + 1) * FONT_HEIGHT; 514 } 515 516 static void update_xy(QemuConsole *s, int x, int y) 517 { 518 TextCell *c; 519 int y1, y2; 520 521 if (!qemu_console_is_visible(s)) { 522 return; 523 } 524 525 if (s->ds->have_text) { 526 text_update_xy(s, x, y); 527 } 528 529 if (s->ds->have_gfx) { 530 y1 = (s->y_base + y) % s->total_height; 531 y2 = y1 - s->y_displayed; 532 if (y2 < 0) 533 y2 += s->total_height; 534 if (y2 < s->height) { 535 c = &s->cells[y1 * s->width + x]; 536 vga_putcharxy(s, x, y2, c->ch, 537 &(c->t_attrib)); 538 invalidate_xy(s, x, y2); 539 } 540 } 541 } 542 543 static void console_show_cursor(QemuConsole *s, int show) 544 { 545 TextCell *c; 546 int y, y1; 547 int x = s->x; 548 549 if (!qemu_console_is_visible(s)) { 550 return; 551 } 552 553 if (s->ds->have_text) { 554 s->cursor_invalidate = 1; 555 } 556 557 if (s->ds->have_gfx) { 558 if (x >= s->width) { 559 x = s->width - 1; 560 } 561 y1 = (s->y_base + s->y) % s->total_height; 562 y = y1 - s->y_displayed; 563 if (y < 0) 564 y += s->total_height; 565 if (y < s->height) { 566 c = &s->cells[y1 * s->width + x]; 567 if (show && s->cursor_visible_phase) { 568 TextAttributes t_attrib = s->t_attrib_default; 569 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ 570 vga_putcharxy(s, x, y, c->ch, &t_attrib); 571 } else { 572 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib)); 573 } 574 invalidate_xy(s, x, y); 575 } 576 } 577 } 578 579 static void console_refresh(QemuConsole *s) 580 { 581 DisplaySurface *surface = qemu_console_surface(s); 582 TextCell *c; 583 int x, y, y1; 584 585 if (!qemu_console_is_visible(s)) { 586 return; 587 } 588 589 if (s->ds->have_text) { 590 s->text_x[0] = 0; 591 s->text_y[0] = 0; 592 s->text_x[1] = s->width - 1; 593 s->text_y[1] = s->height - 1; 594 s->cursor_invalidate = 1; 595 } 596 597 if (s->ds->have_gfx) { 598 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), 599 color_table_rgb[0][COLOR_BLACK]); 600 y1 = s->y_displayed; 601 for (y = 0; y < s->height; y++) { 602 c = s->cells + y1 * s->width; 603 for (x = 0; x < s->width; x++) { 604 vga_putcharxy(s, x, y, c->ch, 605 &(c->t_attrib)); 606 c++; 607 } 608 if (++y1 == s->total_height) { 609 y1 = 0; 610 } 611 } 612 console_show_cursor(s, 1); 613 dpy_gfx_update(s, 0, 0, 614 surface_width(surface), surface_height(surface)); 615 } 616 } 617 618 static void console_scroll(QemuConsole *s, int ydelta) 619 { 620 int i, y1; 621 622 if (ydelta > 0) { 623 for(i = 0; i < ydelta; i++) { 624 if (s->y_displayed == s->y_base) 625 break; 626 if (++s->y_displayed == s->total_height) 627 s->y_displayed = 0; 628 } 629 } else { 630 ydelta = -ydelta; 631 i = s->backscroll_height; 632 if (i > s->total_height - s->height) 633 i = s->total_height - s->height; 634 y1 = s->y_base - i; 635 if (y1 < 0) 636 y1 += s->total_height; 637 for(i = 0; i < ydelta; i++) { 638 if (s->y_displayed == y1) 639 break; 640 if (--s->y_displayed < 0) 641 s->y_displayed = s->total_height - 1; 642 } 643 } 644 console_refresh(s); 645 } 646 647 static void console_put_lf(QemuConsole *s) 648 { 649 TextCell *c; 650 int x, y1; 651 652 s->y++; 653 if (s->y >= s->height) { 654 s->y = s->height - 1; 655 656 if (s->y_displayed == s->y_base) { 657 if (++s->y_displayed == s->total_height) 658 s->y_displayed = 0; 659 } 660 if (++s->y_base == s->total_height) 661 s->y_base = 0; 662 if (s->backscroll_height < s->total_height) 663 s->backscroll_height++; 664 y1 = (s->y_base + s->height - 1) % s->total_height; 665 c = &s->cells[y1 * s->width]; 666 for(x = 0; x < s->width; x++) { 667 c->ch = ' '; 668 c->t_attrib = s->t_attrib_default; 669 c++; 670 } 671 if (qemu_console_is_visible(s) && s->y_displayed == s->y_base) { 672 if (s->ds->have_text) { 673 s->text_x[0] = 0; 674 s->text_y[0] = 0; 675 s->text_x[1] = s->width - 1; 676 s->text_y[1] = s->height - 1; 677 } 678 679 if (s->ds->have_gfx) { 680 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, 681 s->width * FONT_WIDTH, 682 (s->height - 1) * FONT_HEIGHT); 683 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, 684 s->width * FONT_WIDTH, FONT_HEIGHT, 685 color_table_rgb[0][s->t_attrib_default.bgcol]); 686 s->update_x0 = 0; 687 s->update_y0 = 0; 688 s->update_x1 = s->width * FONT_WIDTH; 689 s->update_y1 = s->height * FONT_HEIGHT; 690 } 691 } 692 } 693 } 694 695 /* Set console attributes depending on the current escape codes. 696 * NOTE: I know this code is not very efficient (checking every color for it 697 * self) but it is more readable and better maintainable. 698 */ 699 static void console_handle_escape(QemuConsole *s) 700 { 701 int i; 702 703 for (i=0; i<s->nb_esc_params; i++) { 704 switch (s->esc_params[i]) { 705 case 0: /* reset all console attributes to default */ 706 s->t_attrib = s->t_attrib_default; 707 break; 708 case 1: 709 s->t_attrib.bold = 1; 710 break; 711 case 4: 712 s->t_attrib.uline = 1; 713 break; 714 case 5: 715 s->t_attrib.blink = 1; 716 break; 717 case 7: 718 s->t_attrib.invers = 1; 719 break; 720 case 8: 721 s->t_attrib.unvisible = 1; 722 break; 723 case 22: 724 s->t_attrib.bold = 0; 725 break; 726 case 24: 727 s->t_attrib.uline = 0; 728 break; 729 case 25: 730 s->t_attrib.blink = 0; 731 break; 732 case 27: 733 s->t_attrib.invers = 0; 734 break; 735 case 28: 736 s->t_attrib.unvisible = 0; 737 break; 738 /* set foreground color */ 739 case 30: 740 s->t_attrib.fgcol=COLOR_BLACK; 741 break; 742 case 31: 743 s->t_attrib.fgcol=COLOR_RED; 744 break; 745 case 32: 746 s->t_attrib.fgcol=COLOR_GREEN; 747 break; 748 case 33: 749 s->t_attrib.fgcol=COLOR_YELLOW; 750 break; 751 case 34: 752 s->t_attrib.fgcol=COLOR_BLUE; 753 break; 754 case 35: 755 s->t_attrib.fgcol=COLOR_MAGENTA; 756 break; 757 case 36: 758 s->t_attrib.fgcol=COLOR_CYAN; 759 break; 760 case 37: 761 s->t_attrib.fgcol=COLOR_WHITE; 762 break; 763 /* set background color */ 764 case 40: 765 s->t_attrib.bgcol=COLOR_BLACK; 766 break; 767 case 41: 768 s->t_attrib.bgcol=COLOR_RED; 769 break; 770 case 42: 771 s->t_attrib.bgcol=COLOR_GREEN; 772 break; 773 case 43: 774 s->t_attrib.bgcol=COLOR_YELLOW; 775 break; 776 case 44: 777 s->t_attrib.bgcol=COLOR_BLUE; 778 break; 779 case 45: 780 s->t_attrib.bgcol=COLOR_MAGENTA; 781 break; 782 case 46: 783 s->t_attrib.bgcol=COLOR_CYAN; 784 break; 785 case 47: 786 s->t_attrib.bgcol=COLOR_WHITE; 787 break; 788 } 789 } 790 } 791 792 static void console_clear_xy(QemuConsole *s, int x, int y) 793 { 794 int y1 = (s->y_base + y) % s->total_height; 795 TextCell *c = &s->cells[y1 * s->width + x]; 796 c->ch = ' '; 797 c->t_attrib = s->t_attrib_default; 798 update_xy(s, x, y); 799 } 800 801 /* set cursor, checking bounds */ 802 static void set_cursor(QemuConsole *s, int x, int y) 803 { 804 if (x < 0) { 805 x = 0; 806 } 807 if (y < 0) { 808 y = 0; 809 } 810 if (y >= s->height) { 811 y = s->height - 1; 812 } 813 if (x >= s->width) { 814 x = s->width - 1; 815 } 816 817 s->x = x; 818 s->y = y; 819 } 820 821 static void console_putchar(QemuConsole *s, int ch) 822 { 823 TextCell *c; 824 int y1, i; 825 int x, y; 826 827 switch(s->state) { 828 case TTY_STATE_NORM: 829 switch(ch) { 830 case '\r': /* carriage return */ 831 s->x = 0; 832 break; 833 case '\n': /* newline */ 834 console_put_lf(s); 835 break; 836 case '\b': /* backspace */ 837 if (s->x > 0) 838 s->x--; 839 break; 840 case '\t': /* tabspace */ 841 if (s->x + (8 - (s->x % 8)) > s->width) { 842 s->x = 0; 843 console_put_lf(s); 844 } else { 845 s->x = s->x + (8 - (s->x % 8)); 846 } 847 break; 848 case '\a': /* alert aka. bell */ 849 /* TODO: has to be implemented */ 850 break; 851 case 14: 852 /* SI (shift in), character set 0 (ignored) */ 853 break; 854 case 15: 855 /* SO (shift out), character set 1 (ignored) */ 856 break; 857 case 27: /* esc (introducing an escape sequence) */ 858 s->state = TTY_STATE_ESC; 859 break; 860 default: 861 if (s->x >= s->width) { 862 /* line wrap */ 863 s->x = 0; 864 console_put_lf(s); 865 } 866 y1 = (s->y_base + s->y) % s->total_height; 867 c = &s->cells[y1 * s->width + s->x]; 868 c->ch = ch; 869 c->t_attrib = s->t_attrib; 870 update_xy(s, s->x, s->y); 871 s->x++; 872 break; 873 } 874 break; 875 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */ 876 if (ch == '[') { 877 for(i=0;i<MAX_ESC_PARAMS;i++) 878 s->esc_params[i] = 0; 879 s->nb_esc_params = 0; 880 s->state = TTY_STATE_CSI; 881 } else { 882 s->state = TTY_STATE_NORM; 883 } 884 break; 885 case TTY_STATE_CSI: /* handle escape sequence parameters */ 886 if (ch >= '0' && ch <= '9') { 887 if (s->nb_esc_params < MAX_ESC_PARAMS) { 888 int *param = &s->esc_params[s->nb_esc_params]; 889 int digit = (ch - '0'); 890 891 *param = (*param <= (INT_MAX - digit) / 10) ? 892 *param * 10 + digit : INT_MAX; 893 } 894 } else { 895 if (s->nb_esc_params < MAX_ESC_PARAMS) 896 s->nb_esc_params++; 897 if (ch == ';') 898 break; 899 #ifdef DEBUG_CONSOLE 900 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n", 901 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params); 902 #endif 903 s->state = TTY_STATE_NORM; 904 switch(ch) { 905 case 'A': 906 /* move cursor up */ 907 if (s->esc_params[0] == 0) { 908 s->esc_params[0] = 1; 909 } 910 set_cursor(s, s->x, s->y - s->esc_params[0]); 911 break; 912 case 'B': 913 /* move cursor down */ 914 if (s->esc_params[0] == 0) { 915 s->esc_params[0] = 1; 916 } 917 set_cursor(s, s->x, s->y + s->esc_params[0]); 918 break; 919 case 'C': 920 /* move cursor right */ 921 if (s->esc_params[0] == 0) { 922 s->esc_params[0] = 1; 923 } 924 set_cursor(s, s->x + s->esc_params[0], s->y); 925 break; 926 case 'D': 927 /* move cursor left */ 928 if (s->esc_params[0] == 0) { 929 s->esc_params[0] = 1; 930 } 931 set_cursor(s, s->x - s->esc_params[0], s->y); 932 break; 933 case 'G': 934 /* move cursor to column */ 935 set_cursor(s, s->esc_params[0] - 1, s->y); 936 break; 937 case 'f': 938 case 'H': 939 /* move cursor to row, column */ 940 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1); 941 break; 942 case 'J': 943 switch (s->esc_params[0]) { 944 case 0: 945 /* clear to end of screen */ 946 for (y = s->y; y < s->height; y++) { 947 for (x = 0; x < s->width; x++) { 948 if (y == s->y && x < s->x) { 949 continue; 950 } 951 console_clear_xy(s, x, y); 952 } 953 } 954 break; 955 case 1: 956 /* clear from beginning of screen */ 957 for (y = 0; y <= s->y; y++) { 958 for (x = 0; x < s->width; x++) { 959 if (y == s->y && x > s->x) { 960 break; 961 } 962 console_clear_xy(s, x, y); 963 } 964 } 965 break; 966 case 2: 967 /* clear entire screen */ 968 for (y = 0; y <= s->height; y++) { 969 for (x = 0; x < s->width; x++) { 970 console_clear_xy(s, x, y); 971 } 972 } 973 break; 974 } 975 break; 976 case 'K': 977 switch (s->esc_params[0]) { 978 case 0: 979 /* clear to eol */ 980 for(x = s->x; x < s->width; x++) { 981 console_clear_xy(s, x, s->y); 982 } 983 break; 984 case 1: 985 /* clear from beginning of line */ 986 for (x = 0; x <= s->x; x++) { 987 console_clear_xy(s, x, s->y); 988 } 989 break; 990 case 2: 991 /* clear entire line */ 992 for(x = 0; x < s->width; x++) { 993 console_clear_xy(s, x, s->y); 994 } 995 break; 996 } 997 break; 998 case 'm': 999 console_handle_escape(s); 1000 break; 1001 case 'n': 1002 /* report cursor position */ 1003 /* TODO: send ESC[row;colR */ 1004 break; 1005 case 's': 1006 /* save cursor position */ 1007 s->x_saved = s->x; 1008 s->y_saved = s->y; 1009 break; 1010 case 'u': 1011 /* restore cursor position */ 1012 s->x = s->x_saved; 1013 s->y = s->y_saved; 1014 break; 1015 default: 1016 #ifdef DEBUG_CONSOLE 1017 fprintf(stderr, "unhandled escape character '%c'\n", ch); 1018 #endif 1019 break; 1020 } 1021 break; 1022 } 1023 } 1024 } 1025 1026 void console_select(unsigned int index) 1027 { 1028 DisplayChangeListener *dcl; 1029 QemuConsole *s; 1030 1031 if (index >= MAX_CONSOLES) 1032 return; 1033 1034 trace_console_select(index); 1035 s = qemu_console_lookup_by_index(index); 1036 if (s) { 1037 DisplayState *ds = s->ds; 1038 1039 if (active_console && active_console->cursor_timer) { 1040 qemu_del_timer(active_console->cursor_timer); 1041 } 1042 active_console = s; 1043 if (ds->have_gfx) { 1044 QLIST_FOREACH(dcl, &ds->listeners, next) { 1045 if (dcl->con != NULL) { 1046 continue; 1047 } 1048 if (dcl->ops->dpy_gfx_switch) { 1049 dcl->ops->dpy_gfx_switch(dcl, s->surface); 1050 } 1051 } 1052 dpy_gfx_update(s, 0, 0, surface_width(s->surface), 1053 surface_height(s->surface)); 1054 } 1055 if (ds->have_text) { 1056 dpy_text_resize(s, s->width, s->height); 1057 } 1058 if (s->cursor_timer) { 1059 qemu_mod_timer(s->cursor_timer, 1060 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2); 1061 } 1062 } 1063 } 1064 1065 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len) 1066 { 1067 QemuConsole *s = chr->opaque; 1068 int i; 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 qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1); 1106 } 1107 } 1108 1109 /* called when an ascii key is pressed */ 1110 void kbd_put_keysym(int keysym) 1111 { 1112 QemuConsole *s; 1113 uint8_t buf[16], *q; 1114 int c; 1115 1116 s = active_console; 1117 if (!s || (s->console_type == GRAPHIC_CONSOLE)) 1118 return; 1119 1120 switch(keysym) { 1121 case QEMU_KEY_CTRL_UP: 1122 console_scroll(s, -1); 1123 break; 1124 case QEMU_KEY_CTRL_DOWN: 1125 console_scroll(s, 1); 1126 break; 1127 case QEMU_KEY_CTRL_PAGEUP: 1128 console_scroll(s, -10); 1129 break; 1130 case QEMU_KEY_CTRL_PAGEDOWN: 1131 console_scroll(s, 10); 1132 break; 1133 default: 1134 /* convert the QEMU keysym to VT100 key string */ 1135 q = buf; 1136 if (keysym >= 0xe100 && keysym <= 0xe11f) { 1137 *q++ = '\033'; 1138 *q++ = '['; 1139 c = keysym - 0xe100; 1140 if (c >= 10) 1141 *q++ = '0' + (c / 10); 1142 *q++ = '0' + (c % 10); 1143 *q++ = '~'; 1144 } else if (keysym >= 0xe120 && keysym <= 0xe17f) { 1145 *q++ = '\033'; 1146 *q++ = '['; 1147 *q++ = keysym & 0xff; 1148 } else if (s->echo && (keysym == '\r' || keysym == '\n')) { 1149 console_puts(s->chr, (const uint8_t *) "\r", 1); 1150 *q++ = '\n'; 1151 } else { 1152 *q++ = keysym; 1153 } 1154 if (s->echo) { 1155 console_puts(s->chr, buf, q - buf); 1156 } 1157 if (s->chr->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 void text_console_invalidate(void *opaque) 1166 { 1167 QemuConsole *s = (QemuConsole *) opaque; 1168 1169 if (s->ds->have_text && s->console_type == TEXT_CONSOLE) { 1170 text_console_resize(s); 1171 } 1172 console_refresh(s); 1173 } 1174 1175 static void text_console_update(void *opaque, console_ch_t *chardata) 1176 { 1177 QemuConsole *s = (QemuConsole *) opaque; 1178 int i, j, src; 1179 1180 if (s->text_x[0] <= s->text_x[1]) { 1181 src = (s->y_base + s->text_y[0]) * s->width; 1182 chardata += s->text_y[0] * s->width; 1183 for (i = s->text_y[0]; i <= s->text_y[1]; i ++) 1184 for (j = 0; j < s->width; j ++, src ++) 1185 console_write_ch(chardata ++, s->cells[src].ch | 1186 (s->cells[src].t_attrib.fgcol << 12) | 1187 (s->cells[src].t_attrib.bgcol << 8) | 1188 (s->cells[src].t_attrib.bold << 21)); 1189 dpy_text_update(s, s->text_x[0], s->text_y[0], 1190 s->text_x[1] - s->text_x[0], i - s->text_y[0]); 1191 s->text_x[0] = s->width; 1192 s->text_y[0] = s->height; 1193 s->text_x[1] = 0; 1194 s->text_y[1] = 0; 1195 } 1196 if (s->cursor_invalidate) { 1197 dpy_text_cursor(s, s->x, s->y); 1198 s->cursor_invalidate = 0; 1199 } 1200 } 1201 1202 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type) 1203 { 1204 Error *local_err = NULL; 1205 Object *obj; 1206 QemuConsole *s; 1207 int i; 1208 1209 if (nb_consoles >= MAX_CONSOLES) 1210 return NULL; 1211 1212 obj = object_new(TYPE_QEMU_CONSOLE); 1213 s = QEMU_CONSOLE(obj); 1214 object_property_add_link(obj, "device", TYPE_DEVICE, 1215 (Object **)&s->device, &local_err); 1216 1217 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && 1218 (console_type == GRAPHIC_CONSOLE))) { 1219 active_console = s; 1220 } 1221 s->ds = ds; 1222 s->console_type = console_type; 1223 if (console_type != GRAPHIC_CONSOLE) { 1224 s->index = nb_consoles; 1225 consoles[nb_consoles++] = s; 1226 } else { 1227 /* HACK: Put graphical consoles before text consoles. */ 1228 for (i = nb_consoles; i > 0; i--) { 1229 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE) 1230 break; 1231 consoles[i] = consoles[i - 1]; 1232 consoles[i]->index = i; 1233 } 1234 s->index = i; 1235 consoles[i] = s; 1236 nb_consoles++; 1237 } 1238 return s; 1239 } 1240 1241 static void qemu_alloc_display(DisplaySurface *surface, int width, int height, 1242 int linesize, PixelFormat pf, int newflags) 1243 { 1244 surface->pf = pf; 1245 1246 qemu_pixman_image_unref(surface->image); 1247 surface->image = NULL; 1248 1249 surface->format = qemu_pixman_get_format(&pf); 1250 assert(surface->format != 0); 1251 surface->image = pixman_image_create_bits(surface->format, 1252 width, height, 1253 NULL, linesize); 1254 assert(surface->image != NULL); 1255 1256 surface->flags = newflags | QEMU_ALLOCATED_FLAG; 1257 #ifdef HOST_WORDS_BIGENDIAN 1258 surface->flags |= QEMU_BIG_ENDIAN_FLAG; 1259 #endif 1260 } 1261 1262 DisplaySurface *qemu_create_displaysurface(int width, int height) 1263 { 1264 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1265 int linesize = width * 4; 1266 1267 trace_displaysurface_create(surface, width, height); 1268 qemu_alloc_display(surface, width, height, linesize, 1269 qemu_default_pixelformat(32), 0); 1270 return surface; 1271 } 1272 1273 DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp, 1274 int linesize, uint8_t *data, 1275 bool byteswap) 1276 { 1277 DisplaySurface *surface = g_new0(DisplaySurface, 1); 1278 1279 trace_displaysurface_create_from(surface, width, height, bpp, byteswap); 1280 if (byteswap) { 1281 surface->pf = qemu_different_endianness_pixelformat(bpp); 1282 } else { 1283 surface->pf = qemu_default_pixelformat(bpp); 1284 } 1285 1286 surface->format = qemu_pixman_get_format(&surface->pf); 1287 assert(surface->format != 0); 1288 surface->image = pixman_image_create_bits(surface->format, 1289 width, height, 1290 (void *)data, linesize); 1291 assert(surface->image != NULL); 1292 1293 #ifdef HOST_WORDS_BIGENDIAN 1294 surface->flags = QEMU_BIG_ENDIAN_FLAG; 1295 #endif 1296 1297 return surface; 1298 } 1299 1300 void qemu_free_displaysurface(DisplaySurface *surface) 1301 { 1302 if (surface == NULL) { 1303 return; 1304 } 1305 trace_displaysurface_free(surface); 1306 qemu_pixman_image_unref(surface->image); 1307 g_free(surface); 1308 } 1309 1310 void register_displaychangelistener(DisplayState *ds, 1311 DisplayChangeListener *dcl) 1312 { 1313 QemuConsole *con; 1314 1315 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); 1316 dcl->ds = ds; 1317 QLIST_INSERT_HEAD(&ds->listeners, dcl, next); 1318 gui_setup_refresh(ds); 1319 if (dcl->con) { 1320 dcl->con->dcls++; 1321 con = dcl->con; 1322 } else { 1323 con = active_console; 1324 } 1325 if (dcl->ops->dpy_gfx_switch && con) { 1326 dcl->ops->dpy_gfx_switch(dcl, con->surface); 1327 } 1328 } 1329 1330 void update_displaychangelistener(DisplayChangeListener *dcl, 1331 uint64_t interval) 1332 { 1333 DisplayState *ds = dcl->ds; 1334 1335 dcl->update_interval = interval; 1336 if (!ds->refreshing && ds->update_interval > interval) { 1337 qemu_mod_timer(ds->gui_timer, ds->last_update + interval); 1338 } 1339 } 1340 1341 void unregister_displaychangelistener(DisplayChangeListener *dcl) 1342 { 1343 DisplayState *ds = dcl->ds; 1344 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name); 1345 if (dcl->con) { 1346 dcl->con->dcls--; 1347 } 1348 QLIST_REMOVE(dcl, next); 1349 gui_setup_refresh(ds); 1350 } 1351 1352 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 1353 { 1354 DisplayState *s = con->ds; 1355 DisplayChangeListener *dcl; 1356 int width = surface_width(con->surface); 1357 int height = surface_height(con->surface); 1358 1359 x = MAX(x, 0); 1360 y = MAX(y, 0); 1361 x = MIN(x, width); 1362 y = MIN(y, height); 1363 w = MIN(w, width - x); 1364 h = MIN(h, height - y); 1365 1366 if (!qemu_console_is_visible(con)) { 1367 return; 1368 } 1369 QLIST_FOREACH(dcl, &s->listeners, next) { 1370 if (con != (dcl->con ? dcl->con : active_console)) { 1371 continue; 1372 } 1373 if (dcl->ops->dpy_gfx_update) { 1374 dcl->ops->dpy_gfx_update(dcl, x, y, w, h); 1375 } 1376 } 1377 } 1378 1379 void dpy_gfx_replace_surface(QemuConsole *con, 1380 DisplaySurface *surface) 1381 { 1382 DisplayState *s = con->ds; 1383 DisplaySurface *old_surface = con->surface; 1384 DisplayChangeListener *dcl; 1385 1386 con->surface = surface; 1387 QLIST_FOREACH(dcl, &s->listeners, next) { 1388 if (con != (dcl->con ? dcl->con : active_console)) { 1389 continue; 1390 } 1391 if (dcl->ops->dpy_gfx_switch) { 1392 dcl->ops->dpy_gfx_switch(dcl, surface); 1393 } 1394 } 1395 qemu_free_displaysurface(old_surface); 1396 } 1397 1398 void dpy_refresh(DisplayState *s) 1399 { 1400 DisplayChangeListener *dcl; 1401 1402 QLIST_FOREACH(dcl, &s->listeners, next) { 1403 if (dcl->ops->dpy_refresh) { 1404 dcl->ops->dpy_refresh(dcl); 1405 } 1406 } 1407 } 1408 1409 void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y, 1410 int dst_x, int dst_y, int w, int h) 1411 { 1412 DisplayState *s = con->ds; 1413 DisplayChangeListener *dcl; 1414 1415 if (!qemu_console_is_visible(con)) { 1416 return; 1417 } 1418 QLIST_FOREACH(dcl, &s->listeners, next) { 1419 if (con != (dcl->con ? dcl->con : active_console)) { 1420 continue; 1421 } 1422 if (dcl->ops->dpy_gfx_copy) { 1423 dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h); 1424 } else { /* TODO */ 1425 dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h); 1426 } 1427 } 1428 } 1429 1430 void dpy_text_cursor(QemuConsole *con, int x, int y) 1431 { 1432 DisplayState *s = con->ds; 1433 DisplayChangeListener *dcl; 1434 1435 if (!qemu_console_is_visible(con)) { 1436 return; 1437 } 1438 QLIST_FOREACH(dcl, &s->listeners, next) { 1439 if (con != (dcl->con ? dcl->con : active_console)) { 1440 continue; 1441 } 1442 if (dcl->ops->dpy_text_cursor) { 1443 dcl->ops->dpy_text_cursor(dcl, x, y); 1444 } 1445 } 1446 } 1447 1448 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) 1449 { 1450 DisplayState *s = con->ds; 1451 DisplayChangeListener *dcl; 1452 1453 if (!qemu_console_is_visible(con)) { 1454 return; 1455 } 1456 QLIST_FOREACH(dcl, &s->listeners, next) { 1457 if (con != (dcl->con ? dcl->con : active_console)) { 1458 continue; 1459 } 1460 if (dcl->ops->dpy_text_update) { 1461 dcl->ops->dpy_text_update(dcl, x, y, w, h); 1462 } 1463 } 1464 } 1465 1466 void dpy_text_resize(QemuConsole *con, int w, int h) 1467 { 1468 DisplayState *s = con->ds; 1469 struct DisplayChangeListener *dcl; 1470 1471 if (!qemu_console_is_visible(con)) { 1472 return; 1473 } 1474 QLIST_FOREACH(dcl, &s->listeners, next) { 1475 if (con != (dcl->con ? dcl->con : active_console)) { 1476 continue; 1477 } 1478 if (dcl->ops->dpy_text_resize) { 1479 dcl->ops->dpy_text_resize(dcl, w, h); 1480 } 1481 } 1482 } 1483 1484 void dpy_mouse_set(QemuConsole *con, int x, int y, int on) 1485 { 1486 DisplayState *s = con->ds; 1487 DisplayChangeListener *dcl; 1488 1489 if (!qemu_console_is_visible(con)) { 1490 return; 1491 } 1492 QLIST_FOREACH(dcl, &s->listeners, next) { 1493 if (con != (dcl->con ? dcl->con : active_console)) { 1494 continue; 1495 } 1496 if (dcl->ops->dpy_mouse_set) { 1497 dcl->ops->dpy_mouse_set(dcl, x, y, on); 1498 } 1499 } 1500 } 1501 1502 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) 1503 { 1504 DisplayState *s = con->ds; 1505 DisplayChangeListener *dcl; 1506 1507 if (!qemu_console_is_visible(con)) { 1508 return; 1509 } 1510 QLIST_FOREACH(dcl, &s->listeners, next) { 1511 if (con != (dcl->con ? dcl->con : active_console)) { 1512 continue; 1513 } 1514 if (dcl->ops->dpy_cursor_define) { 1515 dcl->ops->dpy_cursor_define(dcl, cursor); 1516 } 1517 } 1518 } 1519 1520 bool dpy_cursor_define_supported(QemuConsole *con) 1521 { 1522 DisplayState *s = con->ds; 1523 DisplayChangeListener *dcl; 1524 1525 QLIST_FOREACH(dcl, &s->listeners, next) { 1526 if (dcl->ops->dpy_cursor_define) { 1527 return true; 1528 } 1529 } 1530 return false; 1531 } 1532 1533 /***********************************************************/ 1534 /* register display */ 1535 1536 /* console.c internal use only */ 1537 static DisplayState *get_alloc_displaystate(void) 1538 { 1539 if (!display_state) { 1540 display_state = g_new0(DisplayState, 1); 1541 } 1542 return display_state; 1543 } 1544 1545 /* 1546 * Called by main(), after creating QemuConsoles 1547 * and before initializing ui (sdl/vnc/...). 1548 */ 1549 DisplayState *init_displaystate(void) 1550 { 1551 int i; 1552 1553 if (!display_state) { 1554 display_state = g_new0(DisplayState, 1); 1555 } 1556 1557 for (i = 0; i < nb_consoles; i++) { 1558 if (consoles[i]->console_type != GRAPHIC_CONSOLE && 1559 consoles[i]->ds == NULL) { 1560 text_console_do_init(consoles[i]->chr, display_state); 1561 } 1562 } 1563 1564 return display_state; 1565 } 1566 1567 QemuConsole *graphic_console_init(DeviceState *dev, 1568 const GraphicHwOps *hw_ops, 1569 void *opaque) 1570 { 1571 Error *local_err = NULL; 1572 int width = 640; 1573 int height = 480; 1574 QemuConsole *s; 1575 DisplayState *ds; 1576 1577 ds = get_alloc_displaystate(); 1578 trace_console_gfx_new(); 1579 s = new_console(ds, GRAPHIC_CONSOLE); 1580 s->hw_ops = hw_ops; 1581 s->hw = opaque; 1582 if (dev) { 1583 object_property_set_link(OBJECT(s), OBJECT(dev), 1584 "device", &local_err); 1585 } 1586 1587 s->surface = qemu_create_displaysurface(width, height); 1588 return s; 1589 } 1590 1591 QemuConsole *qemu_console_lookup_by_index(unsigned int index) 1592 { 1593 if (index >= MAX_CONSOLES) { 1594 return NULL; 1595 } 1596 return consoles[index]; 1597 } 1598 1599 bool qemu_console_is_visible(QemuConsole *con) 1600 { 1601 return (con == active_console) || (con->dcls > 0); 1602 } 1603 1604 bool qemu_console_is_graphic(QemuConsole *con) 1605 { 1606 if (con == NULL) { 1607 con = active_console; 1608 } 1609 return con && (con->console_type == GRAPHIC_CONSOLE); 1610 } 1611 1612 bool qemu_console_is_fixedsize(QemuConsole *con) 1613 { 1614 if (con == NULL) { 1615 con = active_console; 1616 } 1617 return con && (con->console_type != TEXT_CONSOLE); 1618 } 1619 1620 static void text_console_set_echo(CharDriverState *chr, bool echo) 1621 { 1622 QemuConsole *s = chr->opaque; 1623 1624 s->echo = echo; 1625 } 1626 1627 static void text_console_update_cursor(void *opaque) 1628 { 1629 QemuConsole *s = opaque; 1630 1631 s->cursor_visible_phase = !s->cursor_visible_phase; 1632 graphic_hw_invalidate(s); 1633 qemu_mod_timer(s->cursor_timer, 1634 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2); 1635 } 1636 1637 static const GraphicHwOps text_console_ops = { 1638 .invalidate = text_console_invalidate, 1639 .text_update = text_console_update, 1640 }; 1641 1642 static void text_console_do_init(CharDriverState *chr, DisplayState *ds) 1643 { 1644 QemuConsole *s; 1645 int g_width = 80 * FONT_WIDTH; 1646 int g_height = 24 * FONT_HEIGHT; 1647 1648 s = chr->opaque; 1649 1650 chr->chr_write = console_puts; 1651 1652 s->out_fifo.buf = s->out_fifo_buf; 1653 s->out_fifo.buf_size = sizeof(s->out_fifo_buf); 1654 s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s); 1655 s->ds = ds; 1656 1657 s->y_displayed = 0; 1658 s->y_base = 0; 1659 s->total_height = DEFAULT_BACKSCROLL; 1660 s->x = 0; 1661 s->y = 0; 1662 if (!s->surface) { 1663 if (active_console && active_console->surface) { 1664 g_width = surface_width(active_console->surface); 1665 g_height = surface_height(active_console->surface); 1666 } 1667 s->surface = qemu_create_displaysurface(g_width, g_height); 1668 } 1669 1670 s->cursor_timer = 1671 qemu_new_timer_ms(rt_clock, text_console_update_cursor, s); 1672 1673 s->hw_ops = &text_console_ops; 1674 s->hw = s; 1675 1676 /* Set text attribute defaults */ 1677 s->t_attrib_default.bold = 0; 1678 s->t_attrib_default.uline = 0; 1679 s->t_attrib_default.blink = 0; 1680 s->t_attrib_default.invers = 0; 1681 s->t_attrib_default.unvisible = 0; 1682 s->t_attrib_default.fgcol = COLOR_WHITE; 1683 s->t_attrib_default.bgcol = COLOR_BLACK; 1684 /* set current text attributes to default */ 1685 s->t_attrib = s->t_attrib_default; 1686 text_console_resize(s); 1687 1688 if (chr->label) { 1689 char msg[128]; 1690 int len; 1691 1692 s->t_attrib.bgcol = COLOR_BLUE; 1693 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label); 1694 console_puts(chr, (uint8_t*)msg, len); 1695 s->t_attrib = s->t_attrib_default; 1696 } 1697 1698 qemu_chr_be_generic_open(chr); 1699 if (chr->init) 1700 chr->init(chr); 1701 } 1702 1703 static CharDriverState *text_console_init(ChardevVC *vc) 1704 { 1705 CharDriverState *chr; 1706 QemuConsole *s; 1707 unsigned width = 0; 1708 unsigned height = 0; 1709 1710 chr = g_malloc0(sizeof(CharDriverState)); 1711 1712 if (vc->has_width) { 1713 width = vc->width; 1714 } else if (vc->has_cols) { 1715 width = vc->cols * FONT_WIDTH; 1716 } 1717 1718 if (vc->has_height) { 1719 height = vc->height; 1720 } else if (vc->has_rows) { 1721 height = vc->rows * FONT_HEIGHT; 1722 } 1723 1724 trace_console_txt_new(width, height); 1725 if (width == 0 || height == 0) { 1726 s = new_console(NULL, TEXT_CONSOLE); 1727 } else { 1728 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE); 1729 s->surface = qemu_create_displaysurface(width, height); 1730 } 1731 1732 if (!s) { 1733 g_free(chr); 1734 return NULL; 1735 } 1736 1737 s->chr = chr; 1738 chr->opaque = s; 1739 chr->chr_set_echo = text_console_set_echo; 1740 1741 if (display_state) { 1742 text_console_do_init(chr, display_state); 1743 } 1744 return chr; 1745 } 1746 1747 static VcHandler *vc_handler = text_console_init; 1748 1749 CharDriverState *vc_init(ChardevVC *vc) 1750 { 1751 return vc_handler(vc); 1752 } 1753 1754 void register_vc_handler(VcHandler *handler) 1755 { 1756 vc_handler = handler; 1757 } 1758 1759 void qemu_console_resize(QemuConsole *s, int width, int height) 1760 { 1761 DisplaySurface *surface; 1762 1763 assert(s->console_type == GRAPHIC_CONSOLE); 1764 surface = qemu_create_displaysurface(width, height); 1765 dpy_gfx_replace_surface(s, surface); 1766 } 1767 1768 void qemu_console_copy(QemuConsole *con, int src_x, int src_y, 1769 int dst_x, int dst_y, int w, int h) 1770 { 1771 assert(con->console_type == GRAPHIC_CONSOLE); 1772 dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h); 1773 } 1774 1775 DisplaySurface *qemu_console_surface(QemuConsole *console) 1776 { 1777 return console->surface; 1778 } 1779 1780 DisplayState *qemu_console_displaystate(QemuConsole *console) 1781 { 1782 return console->ds; 1783 } 1784 1785 PixelFormat qemu_different_endianness_pixelformat(int bpp) 1786 { 1787 PixelFormat pf; 1788 1789 memset(&pf, 0x00, sizeof(PixelFormat)); 1790 1791 pf.bits_per_pixel = bpp; 1792 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8); 1793 pf.depth = bpp == 32 ? 24 : bpp; 1794 1795 switch (bpp) { 1796 case 24: 1797 pf.rmask = 0x000000FF; 1798 pf.gmask = 0x0000FF00; 1799 pf.bmask = 0x00FF0000; 1800 pf.rmax = 255; 1801 pf.gmax = 255; 1802 pf.bmax = 255; 1803 pf.rshift = 0; 1804 pf.gshift = 8; 1805 pf.bshift = 16; 1806 pf.rbits = 8; 1807 pf.gbits = 8; 1808 pf.bbits = 8; 1809 break; 1810 case 32: 1811 pf.rmask = 0x0000FF00; 1812 pf.gmask = 0x00FF0000; 1813 pf.bmask = 0xFF000000; 1814 pf.amask = 0x00000000; 1815 pf.amax = 255; 1816 pf.rmax = 255; 1817 pf.gmax = 255; 1818 pf.bmax = 255; 1819 pf.ashift = 0; 1820 pf.rshift = 8; 1821 pf.gshift = 16; 1822 pf.bshift = 24; 1823 pf.rbits = 8; 1824 pf.gbits = 8; 1825 pf.bbits = 8; 1826 pf.abits = 8; 1827 break; 1828 default: 1829 break; 1830 } 1831 return pf; 1832 } 1833 1834 PixelFormat qemu_default_pixelformat(int bpp) 1835 { 1836 PixelFormat pf; 1837 1838 memset(&pf, 0x00, sizeof(PixelFormat)); 1839 1840 pf.bits_per_pixel = bpp; 1841 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8); 1842 pf.depth = bpp == 32 ? 24 : bpp; 1843 1844 switch (bpp) { 1845 case 15: 1846 pf.bits_per_pixel = 16; 1847 pf.rmask = 0x00007c00; 1848 pf.gmask = 0x000003E0; 1849 pf.bmask = 0x0000001F; 1850 pf.rmax = 31; 1851 pf.gmax = 31; 1852 pf.bmax = 31; 1853 pf.rshift = 10; 1854 pf.gshift = 5; 1855 pf.bshift = 0; 1856 pf.rbits = 5; 1857 pf.gbits = 5; 1858 pf.bbits = 5; 1859 break; 1860 case 16: 1861 pf.rmask = 0x0000F800; 1862 pf.gmask = 0x000007E0; 1863 pf.bmask = 0x0000001F; 1864 pf.rmax = 31; 1865 pf.gmax = 63; 1866 pf.bmax = 31; 1867 pf.rshift = 11; 1868 pf.gshift = 5; 1869 pf.bshift = 0; 1870 pf.rbits = 5; 1871 pf.gbits = 6; 1872 pf.bbits = 5; 1873 break; 1874 case 24: 1875 pf.rmask = 0x00FF0000; 1876 pf.gmask = 0x0000FF00; 1877 pf.bmask = 0x000000FF; 1878 pf.rmax = 255; 1879 pf.gmax = 255; 1880 pf.bmax = 255; 1881 pf.rshift = 16; 1882 pf.gshift = 8; 1883 pf.bshift = 0; 1884 pf.rbits = 8; 1885 pf.gbits = 8; 1886 pf.bbits = 8; 1887 break; 1888 case 32: 1889 pf.rmask = 0x00FF0000; 1890 pf.gmask = 0x0000FF00; 1891 pf.bmask = 0x000000FF; 1892 pf.rmax = 255; 1893 pf.gmax = 255; 1894 pf.bmax = 255; 1895 pf.rshift = 16; 1896 pf.gshift = 8; 1897 pf.bshift = 0; 1898 pf.rbits = 8; 1899 pf.gbits = 8; 1900 pf.bbits = 8; 1901 break; 1902 default: 1903 break; 1904 } 1905 return pf; 1906 } 1907 1908 static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, 1909 Error **errp) 1910 { 1911 int val; 1912 1913 backend->vc = g_new0(ChardevVC, 1); 1914 1915 val = qemu_opt_get_number(opts, "width", 0); 1916 if (val != 0) { 1917 backend->vc->has_width = true; 1918 backend->vc->width = val; 1919 } 1920 1921 val = qemu_opt_get_number(opts, "height", 0); 1922 if (val != 0) { 1923 backend->vc->has_height = true; 1924 backend->vc->height = val; 1925 } 1926 1927 val = qemu_opt_get_number(opts, "cols", 0); 1928 if (val != 0) { 1929 backend->vc->has_cols = true; 1930 backend->vc->cols = val; 1931 } 1932 1933 val = qemu_opt_get_number(opts, "rows", 0); 1934 if (val != 0) { 1935 backend->vc->has_rows = true; 1936 backend->vc->rows = val; 1937 } 1938 } 1939 1940 static const TypeInfo qemu_console_info = { 1941 .name = TYPE_QEMU_CONSOLE, 1942 .parent = TYPE_OBJECT, 1943 .instance_size = sizeof(QemuConsole), 1944 .class_size = sizeof(QemuConsoleClass), 1945 }; 1946 1947 1948 static void register_types(void) 1949 { 1950 type_register_static(&qemu_console_info); 1951 register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC, 1952 qemu_chr_parse_vc); 1953 } 1954 1955 type_init(register_types); 1956