2 * QEMU graphical console
4 * Copyright (c) 2004 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
25 #include "qemu/osdep.h"
26 #include "ui/console.h"
27 #include "hw/qdev-core.h"
28 #include "qapi/error.h"
29 #include "qapi/qapi-commands-ui.h"
30 #include "qemu/coroutine.h"
31 #include "qemu/fifo8.h"
32 #include "qemu/error-report.h"
33 #include "qemu/main-loop.h"
34 #include "qemu/module.h"
35 #include "qemu/option.h"
36 #include "qemu/timer.h"
37 #include "chardev/char.h"
39 #include "exec/memory.h"
40 #include "qom/object.h"
42 #define DEFAULT_BACKSCROLL 512
43 #define CONSOLE_CURSOR_PERIOD 500
45 typedef struct TextAttributes {
55 typedef struct TextCell {
57 TextAttributes t_attrib;
60 #define MAX_ESC_PARAMS 3
71 TEXT_CONSOLE_FIXED_SIZE
78 console_type_t console_type;
80 DisplaySurface *surface;
81 DisplayScanout scanout;
85 QEMUTimer *gl_unblock_timer;
88 /* Graphic console state. */
94 int cursor_x, cursor_y, cursor_on;
95 const GraphicHwOps *hw_ops;
98 /* Text console state */
102 int backscroll_height;
104 int x_saved, y_saved;
107 TextAttributes t_attrib_default; /* default text attributes */
108 TextAttributes t_attrib; /* currently active text attributes */
110 int text_x[2], text_y[2], cursor_invalidate;
119 int esc_params[MAX_ESC_PARAMS];
123 /* fifo for key pressed */
127 QTAILQ_ENTRY(QemuConsole) next;
130 struct DisplayState {
131 QEMUTimer *gui_timer;
132 uint64_t last_update;
133 uint64_t update_interval;
137 QLIST_HEAD(, DisplayChangeListener) listeners;
140 static DisplayState *display_state;
141 static QemuConsole *active_console;
142 static QTAILQ_HEAD(, QemuConsole) consoles =
143 QTAILQ_HEAD_INITIALIZER(consoles);
144 static bool cursor_visible_phase;
145 static QEMUTimer *cursor_timer;
147 static void text_console_do_init(Chardev *chr, DisplayState *ds);
148 static void dpy_refresh(DisplayState *s);
149 static DisplayState *get_alloc_displaystate(void);
150 static void text_console_update_cursor_timer(void);
151 static void text_console_update_cursor(void *opaque);
152 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
153 static bool console_compatible_with(QemuConsole *con,
154 DisplayChangeListener *dcl, Error **errp);
156 static void gui_update(void *opaque)
158 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
159 uint64_t dcl_interval;
160 DisplayState *ds = opaque;
161 DisplayChangeListener *dcl;
163 ds->refreshing = true;
165 ds->refreshing = false;
167 QLIST_FOREACH(dcl, &ds->listeners, next) {
168 dcl_interval = dcl->update_interval ?
169 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
170 if (interval > dcl_interval) {
171 interval = dcl_interval;
174 if (ds->update_interval != interval) {
175 ds->update_interval = interval;
176 trace_console_refresh(interval);
178 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
179 timer_mod(ds->gui_timer, ds->last_update + interval);
182 static void gui_setup_refresh(DisplayState *ds)
184 DisplayChangeListener *dcl;
185 bool need_timer = false;
186 bool have_gfx = false;
188 QLIST_FOREACH(dcl, &ds->listeners, next) {
189 if (dcl->ops->dpy_refresh != NULL) {
192 if (dcl->ops->dpy_gfx_update != NULL) {
197 if (need_timer && ds->gui_timer == NULL) {
198 ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
199 timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
201 if (!need_timer && ds->gui_timer != NULL) {
202 timer_free(ds->gui_timer);
203 ds->gui_timer = NULL;
206 ds->have_gfx = have_gfx;
209 void graphic_hw_update_done(QemuConsole *con)
212 qemu_co_enter_all(&con->dump_queue, NULL);
216 void graphic_hw_update(QemuConsole *con)
219 con = con ? con : active_console;
223 if (con->hw_ops->gfx_update) {
224 con->hw_ops->gfx_update(con->hw);
225 async = con->hw_ops->gfx_update_async;
228 graphic_hw_update_done(con);
232 static void graphic_hw_update_bh(void *con)
234 graphic_hw_update(con);
237 void qemu_console_co_wait_update(QemuConsole *con)
239 if (qemu_co_queue_empty(&con->dump_queue)) {
240 /* Defer the update, it will restart the pending coroutines */
241 aio_bh_schedule_oneshot(qemu_get_aio_context(),
242 graphic_hw_update_bh, con);
244 qemu_co_queue_wait(&con->dump_queue, NULL);
248 static void graphic_hw_gl_unblock_timer(void *opaque)
250 warn_report("console: no gl-unblock within one second");
253 void graphic_hw_gl_block(QemuConsole *con, bool block)
263 assert(con->gl_block >= 0);
264 if (!con->hw_ops->gl_block) {
267 if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {
270 con->hw_ops->gl_block(con->hw, block);
273 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
274 timeout += 1000; /* one sec */
275 timer_mod(con->gl_unblock_timer, timeout);
277 timer_del(con->gl_unblock_timer);
281 int qemu_console_get_window_id(QemuConsole *con)
283 return con->window_id;
286 void qemu_console_set_window_id(QemuConsole *con, int window_id)
288 con->window_id = window_id;
291 void graphic_hw_invalidate(QemuConsole *con)
294 con = active_console;
296 if (con && con->hw_ops->invalidate) {
297 con->hw_ops->invalidate(con->hw);
301 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
304 con = active_console;
306 if (con && con->hw_ops->text_update) {
307 con->hw_ops->text_update(con->hw, chardata);
311 static void vga_fill_rect(QemuConsole *con,
312 int posx, int posy, int width, int height,
313 pixman_color_t color)
315 DisplaySurface *surface = qemu_console_surface(con);
316 pixman_rectangle16_t rect = {
317 .x = posx, .y = posy, .width = width, .height = height
320 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
324 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
325 static void vga_bitblt(QemuConsole *con,
326 int xs, int ys, int xd, int yd, int w, int h)
328 DisplaySurface *surface = qemu_console_surface(con);
330 pixman_image_composite(PIXMAN_OP_SRC,
331 surface->image, NULL, surface->image,
332 xs, ys, 0, 0, xd, yd, w, h);
335 /***********************************************************/
336 /* basic char display */
338 #define FONT_HEIGHT 16
343 #define QEMU_RGB(r, g, b) \
344 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
346 static const pixman_color_t color_table_rgb[2][8] = {
348 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
349 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
350 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */
351 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
352 [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */
353 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
354 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
355 [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
358 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
359 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */
360 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */
361 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
362 [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */
363 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
364 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
365 [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */
369 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
370 TextAttributes *t_attrib)
372 static pixman_image_t *glyphs[256];
373 DisplaySurface *surface = qemu_console_surface(s);
374 pixman_color_t fgcol, bgcol;
376 if (t_attrib->invers) {
377 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
378 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
380 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
381 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
385 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
387 qemu_pixman_glyph_render(glyphs[ch], surface->image,
388 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
391 static void text_console_resize(QemuConsole *s)
393 TextCell *cells, *c, *c1;
394 int w1, x, y, last_width;
396 assert(s->scanout.kind == SCANOUT_SURFACE);
398 last_width = s->width;
399 s->width = surface_width(s->surface) / FONT_WIDTH;
400 s->height = surface_height(s->surface) / FONT_HEIGHT;
406 cells = g_new(TextCell, s->width * s->total_height + 1);
407 for(y = 0; y < s->total_height; y++) {
408 c = &cells[y * s->width];
410 c1 = &s->cells[y * last_width];
411 for(x = 0; x < w1; x++) {
415 for(x = w1; x < s->width; x++) {
417 c->t_attrib = s->t_attrib_default;
425 static inline void text_update_xy(QemuConsole *s, int x, int y)
427 s->text_x[0] = MIN(s->text_x[0], x);
428 s->text_x[1] = MAX(s->text_x[1], x);
429 s->text_y[0] = MIN(s->text_y[0], y);
430 s->text_y[1] = MAX(s->text_y[1], y);
433 static void invalidate_xy(QemuConsole *s, int x, int y)
435 if (!qemu_console_is_visible(s)) {
438 if (s->update_x0 > x * FONT_WIDTH)
439 s->update_x0 = x * FONT_WIDTH;
440 if (s->update_y0 > y * FONT_HEIGHT)
441 s->update_y0 = y * FONT_HEIGHT;
442 if (s->update_x1 < (x + 1) * FONT_WIDTH)
443 s->update_x1 = (x + 1) * FONT_WIDTH;
444 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
445 s->update_y1 = (y + 1) * FONT_HEIGHT;
448 static void update_xy(QemuConsole *s, int x, int y)
453 text_update_xy(s, x, y);
455 y1 = (s->y_base + y) % s->total_height;
456 y2 = y1 - s->y_displayed;
458 y2 += s->total_height;
460 if (y2 < s->height) {
464 c = &s->cells[y1 * s->width + x];
465 vga_putcharxy(s, x, y2, c->ch,
467 invalidate_xy(s, x, y2);
471 static void console_show_cursor(QemuConsole *s, int show)
477 s->cursor_invalidate = 1;
482 y1 = (s->y_base + s->y) % s->total_height;
483 y = y1 - s->y_displayed;
485 y += s->total_height;
488 c = &s->cells[y1 * s->width + x];
489 if (show && cursor_visible_phase) {
490 TextAttributes t_attrib = s->t_attrib_default;
491 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
492 vga_putcharxy(s, x, y, c->ch, &t_attrib);
494 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
496 invalidate_xy(s, x, y);
500 static void console_refresh(QemuConsole *s)
502 DisplaySurface *surface = qemu_console_surface(s);
508 s->text_x[1] = s->width - 1;
509 s->text_y[1] = s->height - 1;
510 s->cursor_invalidate = 1;
512 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
513 color_table_rgb[0][QEMU_COLOR_BLACK]);
515 for (y = 0; y < s->height; y++) {
516 c = s->cells + y1 * s->width;
517 for (x = 0; x < s->width; x++) {
518 vga_putcharxy(s, x, y, c->ch,
522 if (++y1 == s->total_height) {
526 console_show_cursor(s, 1);
527 dpy_gfx_update(s, 0, 0,
528 surface_width(surface), surface_height(surface));
531 static void console_scroll(QemuConsole *s, int ydelta)
536 for(i = 0; i < ydelta; i++) {
537 if (s->y_displayed == s->y_base)
539 if (++s->y_displayed == s->total_height)
544 i = s->backscroll_height;
545 if (i > s->total_height - s->height)
546 i = s->total_height - s->height;
549 y1 += s->total_height;
550 for(i = 0; i < ydelta; i++) {
551 if (s->y_displayed == y1)
553 if (--s->y_displayed < 0)
554 s->y_displayed = s->total_height - 1;
560 static void console_put_lf(QemuConsole *s)
566 if (s->y >= s->height) {
567 s->y = s->height - 1;
569 if (s->y_displayed == s->y_base) {
570 if (++s->y_displayed == s->total_height)
573 if (++s->y_base == s->total_height)
575 if (s->backscroll_height < s->total_height)
576 s->backscroll_height++;
577 y1 = (s->y_base + s->height - 1) % s->total_height;
578 c = &s->cells[y1 * s->width];
579 for(x = 0; x < s->width; x++) {
581 c->t_attrib = s->t_attrib_default;
584 if (s->y_displayed == s->y_base) {
587 s->text_x[1] = s->width - 1;
588 s->text_y[1] = s->height - 1;
590 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
591 s->width * FONT_WIDTH,
592 (s->height - 1) * FONT_HEIGHT);
593 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
594 s->width * FONT_WIDTH, FONT_HEIGHT,
595 color_table_rgb[0][s->t_attrib_default.bgcol]);
598 s->update_x1 = s->width * FONT_WIDTH;
599 s->update_y1 = s->height * FONT_HEIGHT;
604 /* Set console attributes depending on the current escape codes.
605 * NOTE: I know this code is not very efficient (checking every color for it
606 * self) but it is more readable and better maintainable.
608 static void console_handle_escape(QemuConsole *s)
612 for (i=0; i<s->nb_esc_params; i++) {
613 switch (s->esc_params[i]) {
614 case 0: /* reset all console attributes to default */
615 s->t_attrib = s->t_attrib_default;
618 s->t_attrib.bold = 1;
621 s->t_attrib.uline = 1;
624 s->t_attrib.blink = 1;
627 s->t_attrib.invers = 1;
630 s->t_attrib.unvisible = 1;
633 s->t_attrib.bold = 0;
636 s->t_attrib.uline = 0;
639 s->t_attrib.blink = 0;
642 s->t_attrib.invers = 0;
645 s->t_attrib.unvisible = 0;
647 /* set foreground color */
649 s->t_attrib.fgcol = QEMU_COLOR_BLACK;
652 s->t_attrib.fgcol = QEMU_COLOR_RED;
655 s->t_attrib.fgcol = QEMU_COLOR_GREEN;
658 s->t_attrib.fgcol = QEMU_COLOR_YELLOW;
661 s->t_attrib.fgcol = QEMU_COLOR_BLUE;
664 s->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
667 s->t_attrib.fgcol = QEMU_COLOR_CYAN;
670 s->t_attrib.fgcol = QEMU_COLOR_WHITE;
672 /* set background color */
674 s->t_attrib.bgcol = QEMU_COLOR_BLACK;
677 s->t_attrib.bgcol = QEMU_COLOR_RED;
680 s->t_attrib.bgcol = QEMU_COLOR_GREEN;
683 s->t_attrib.bgcol = QEMU_COLOR_YELLOW;
686 s->t_attrib.bgcol = QEMU_COLOR_BLUE;
689 s->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
692 s->t_attrib.bgcol = QEMU_COLOR_CYAN;
695 s->t_attrib.bgcol = QEMU_COLOR_WHITE;
701 static void console_clear_xy(QemuConsole *s, int x, int y)
703 int y1 = (s->y_base + y) % s->total_height;
707 TextCell *c = &s->cells[y1 * s->width + x];
709 c->t_attrib = s->t_attrib_default;
713 static void console_put_one(QemuConsole *s, int ch)
717 if (s->x >= s->width) {
722 y1 = (s->y_base + s->y) % s->total_height;
723 c = &s->cells[y1 * s->width + s->x];
725 c->t_attrib = s->t_attrib;
726 update_xy(s, s->x, s->y);
730 static void console_respond_str(QemuConsole *s, const char *buf)
733 console_put_one(s, *buf);
738 /* set cursor, checking bounds */
739 static void set_cursor(QemuConsole *s, int x, int y)
747 if (y >= s->height) {
758 static void console_putchar(QemuConsole *s, int ch)
767 case '\r': /* carriage return */
770 case '\n': /* newline */
773 case '\b': /* backspace */
777 case '\t': /* tabspace */
778 if (s->x + (8 - (s->x % 8)) > s->width) {
782 s->x = s->x + (8 - (s->x % 8));
785 case '\a': /* alert aka. bell */
786 /* TODO: has to be implemented */
789 /* SI (shift in), character set 0 (ignored) */
792 /* SO (shift out), character set 1 (ignored) */
794 case 27: /* esc (introducing an escape sequence) */
795 s->state = TTY_STATE_ESC;
798 console_put_one(s, ch);
802 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
804 for(i=0;i<MAX_ESC_PARAMS;i++)
805 s->esc_params[i] = 0;
806 s->nb_esc_params = 0;
807 s->state = TTY_STATE_CSI;
809 s->state = TTY_STATE_NORM;
812 case TTY_STATE_CSI: /* handle escape sequence parameters */
813 if (ch >= '0' && ch <= '9') {
814 if (s->nb_esc_params < MAX_ESC_PARAMS) {
815 int *param = &s->esc_params[s->nb_esc_params];
816 int digit = (ch - '0');
818 *param = (*param <= (INT_MAX - digit) / 10) ?
819 *param * 10 + digit : INT_MAX;
822 if (s->nb_esc_params < MAX_ESC_PARAMS)
824 if (ch == ';' || ch == '?') {
827 trace_console_putchar_csi(s->esc_params[0], s->esc_params[1],
828 ch, s->nb_esc_params);
829 s->state = TTY_STATE_NORM;
833 if (s->esc_params[0] == 0) {
834 s->esc_params[0] = 1;
836 set_cursor(s, s->x, s->y - s->esc_params[0]);
839 /* move cursor down */
840 if (s->esc_params[0] == 0) {
841 s->esc_params[0] = 1;
843 set_cursor(s, s->x, s->y + s->esc_params[0]);
846 /* move cursor right */
847 if (s->esc_params[0] == 0) {
848 s->esc_params[0] = 1;
850 set_cursor(s, s->x + s->esc_params[0], s->y);
853 /* move cursor left */
854 if (s->esc_params[0] == 0) {
855 s->esc_params[0] = 1;
857 set_cursor(s, s->x - s->esc_params[0], s->y);
860 /* move cursor to column */
861 set_cursor(s, s->esc_params[0] - 1, s->y);
865 /* move cursor to row, column */
866 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
869 switch (s->esc_params[0]) {
871 /* clear to end of screen */
872 for (y = s->y; y < s->height; y++) {
873 for (x = 0; x < s->width; x++) {
874 if (y == s->y && x < s->x) {
877 console_clear_xy(s, x, y);
882 /* clear from beginning of screen */
883 for (y = 0; y <= s->y; y++) {
884 for (x = 0; x < s->width; x++) {
885 if (y == s->y && x > s->x) {
888 console_clear_xy(s, x, y);
893 /* clear entire screen */
894 for (y = 0; y <= s->height; y++) {
895 for (x = 0; x < s->width; x++) {
896 console_clear_xy(s, x, y);
903 switch (s->esc_params[0]) {
906 for(x = s->x; x < s->width; x++) {
907 console_clear_xy(s, x, s->y);
911 /* clear from beginning of line */
912 for (x = 0; x <= s->x && x < s->width; x++) {
913 console_clear_xy(s, x, s->y);
917 /* clear entire line */
918 for(x = 0; x < s->width; x++) {
919 console_clear_xy(s, x, s->y);
925 console_handle_escape(s);
928 switch (s->esc_params[0]) {
930 /* report console status (always succeed)*/
931 console_respond_str(s, "\033[0n");
934 /* report cursor position */
935 sprintf(response, "\033[%d;%dR",
936 (s->y_base + s->y) % s->total_height + 1,
938 console_respond_str(s, response);
943 /* save cursor position */
948 /* restore cursor position */
953 trace_console_putchar_unhandled(ch);
961 static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl,
962 struct DisplaySurface *new_surface,
965 if (dcl->ops->dpy_gfx_switch) {
966 dcl->ops->dpy_gfx_switch(dcl, new_surface);
969 if (update && dcl->ops->dpy_gfx_update) {
970 dcl->ops->dpy_gfx_update(dcl, 0, 0,
971 surface_width(new_surface),
972 surface_height(new_surface));
976 static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface)
978 if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) {
979 con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface);
983 static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface)
985 if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) {
986 con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface);
990 static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface,
991 int x, int y, int w, int h)
993 if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) {
994 con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h);
998 static void displaychangelistener_display_console(DisplayChangeListener *dcl,
1002 static const char nodev[] =
1003 "This VM has no graphic display device.";
1004 static DisplaySurface *dummy;
1006 if (!con || !console_compatible_with(con, dcl, errp)) {
1008 dummy = qemu_create_placeholder_surface(640, 480, nodev);
1011 dpy_gfx_create_texture(con, dummy);
1013 displaychangelistener_gfx_switch(dcl, dummy, TRUE);
1017 dpy_gfx_create_texture(con, con->surface);
1018 displaychangelistener_gfx_switch(dcl, con->surface,
1019 con->scanout.kind == SCANOUT_SURFACE);
1021 if (con->scanout.kind == SCANOUT_DMABUF &&
1022 displaychangelistener_has_dmabuf(dcl)) {
1023 dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
1024 } else if (con->scanout.kind == SCANOUT_TEXTURE &&
1025 dcl->ops->dpy_gl_scanout_texture) {
1026 dcl->ops->dpy_gl_scanout_texture(dcl,
1027 con->scanout.texture.backing_id,
1028 con->scanout.texture.backing_y_0_top,
1029 con->scanout.texture.backing_width,
1030 con->scanout.texture.backing_height,
1031 con->scanout.texture.x,
1032 con->scanout.texture.y,
1033 con->scanout.texture.width,
1034 con->scanout.texture.height,
1035 con->scanout.texture.d3d_tex2d);
1039 void console_select(unsigned int index)
1041 DisplayChangeListener *dcl;
1044 trace_console_select(index);
1045 s = qemu_console_lookup_by_index(index);
1047 DisplayState *ds = s->ds;
1050 QLIST_FOREACH (dcl, &ds->listeners, next) {
1051 if (dcl->con != NULL) {
1054 displaychangelistener_display_console(dcl, s, NULL);
1056 dpy_text_resize(s, s->width, s->height);
1057 text_console_update_cursor(NULL);
1063 QemuConsole *console;
1065 typedef struct VCChardev VCChardev;
1067 #define TYPE_CHARDEV_VC "chardev-vc"
1068 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
1071 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1073 VCChardev *drv = VC_CHARDEV(chr);
1074 QemuConsole *s = drv->console;
1081 s->update_x0 = s->width * FONT_WIDTH;
1082 s->update_y0 = s->height * FONT_HEIGHT;
1085 console_show_cursor(s, 0);
1086 for(i = 0; i < len; i++) {
1087 console_putchar(s, buf[i]);
1089 console_show_cursor(s, 1);
1090 if (s->update_x0 < s->update_x1) {
1091 dpy_gfx_update(s, s->update_x0, s->update_y0,
1092 s->update_x1 - s->update_x0,
1093 s->update_y1 - s->update_y0);
1098 static void kbd_send_chars(QemuConsole *s)
1100 uint32_t len, avail;
1102 len = qemu_chr_be_can_write(s->chr);
1103 avail = fifo8_num_used(&s->out_fifo);
1104 while (len > 0 && avail > 0) {
1108 buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size);
1109 qemu_chr_be_write(s->chr, buf, size);
1110 len = qemu_chr_be_can_write(s->chr);
1115 /* called when an ascii key is pressed */
1116 void kbd_put_keysym_console(QemuConsole *s, int keysym)
1118 uint8_t buf[16], *q;
1122 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1126 case QEMU_KEY_CTRL_UP:
1127 console_scroll(s, -1);
1129 case QEMU_KEY_CTRL_DOWN:
1130 console_scroll(s, 1);
1132 case QEMU_KEY_CTRL_PAGEUP:
1133 console_scroll(s, -10);
1135 case QEMU_KEY_CTRL_PAGEDOWN:
1136 console_scroll(s, 10);
1139 /* convert the QEMU keysym to VT100 key string */
1141 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1144 c = keysym - 0xe100;
1146 *q++ = '0' + (c / 10);
1147 *q++ = '0' + (c % 10);
1149 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1152 *q++ = keysym & 0xff;
1153 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1154 qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
1160 qemu_chr_write(s->chr, buf, q - buf, true);
1162 num_free = fifo8_num_free(&s->out_fifo);
1163 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
1169 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1170 [Q_KEY_CODE_UP] = QEMU_KEY_UP,
1171 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN,
1172 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT,
1173 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT,
1174 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME,
1175 [Q_KEY_CODE_END] = QEMU_KEY_END,
1176 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP,
1177 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN,
1178 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1179 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB,
1180 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1183 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1184 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP,
1185 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN,
1186 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT,
1187 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT,
1188 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME,
1189 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END,
1190 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP,
1191 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN,
1194 bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
1198 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1202 kbd_put_keysym_console(s, keysym);
1206 void kbd_put_string_console(QemuConsole *s, const char *str, int len)
1210 for (i = 0; i < len && str[i]; i++) {
1211 kbd_put_keysym_console(s, str[i]);
1215 void kbd_put_keysym(int keysym)
1217 kbd_put_keysym_console(active_console, keysym);
1220 static void text_console_invalidate(void *opaque)
1222 QemuConsole *s = (QemuConsole *) opaque;
1224 if (s->console_type == TEXT_CONSOLE) {
1225 text_console_resize(s);
1230 static void text_console_update(void *opaque, console_ch_t *chardata)
1232 QemuConsole *s = (QemuConsole *) opaque;
1235 if (s->text_x[0] <= s->text_x[1]) {
1236 src = (s->y_base + s->text_y[0]) * s->width;
1237 chardata += s->text_y[0] * s->width;
1238 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1239 for (j = 0; j < s->width; j++, src++) {
1240 console_write_ch(chardata ++,
1241 ATTR2CHTYPE(s->cells[src].ch,
1242 s->cells[src].t_attrib.fgcol,
1243 s->cells[src].t_attrib.bgcol,
1244 s->cells[src].t_attrib.bold));
1246 dpy_text_update(s, s->text_x[0], s->text_y[0],
1247 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1248 s->text_x[0] = s->width;
1249 s->text_y[0] = s->height;
1253 if (s->cursor_invalidate) {
1254 dpy_text_cursor(s, s->x, s->y);
1255 s->cursor_invalidate = 0;
1259 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
1266 obj = object_new(TYPE_QEMU_CONSOLE);
1267 s = QEMU_CONSOLE(obj);
1268 qemu_co_queue_init(&s->dump_queue);
1270 object_property_add_link(obj, "device", TYPE_DEVICE,
1271 (Object **)&s->device,
1272 object_property_allow_set_link,
1273 OBJ_PROP_LINK_STRONG);
1274 object_property_add_uint32_ptr(obj, "head", &s->head,
1275 OBJ_PROP_FLAG_READ);
1277 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1278 (console_type == GRAPHIC_CONSOLE))) {
1282 s->console_type = console_type;
1285 if (QTAILQ_EMPTY(&consoles)) {
1287 QTAILQ_INSERT_TAIL(&consoles, s, next);
1288 } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) {
1289 QemuConsole *last = QTAILQ_LAST(&consoles);
1290 s->index = last->index + 1;
1291 QTAILQ_INSERT_TAIL(&consoles, s, next);
1294 * HACK: Put graphical consoles before text consoles.
1296 * Only do that for coldplugged devices. After initial device
1297 * initialization we will not renumber the consoles any more.
1299 QemuConsole *c = QTAILQ_FIRST(&consoles);
1301 while (QTAILQ_NEXT(c, next) != NULL &&
1302 c->console_type == GRAPHIC_CONSOLE) {
1303 c = QTAILQ_NEXT(c, next);
1305 if (c->console_type == GRAPHIC_CONSOLE) {
1306 /* have no text consoles */
1307 s->index = c->index + 1;
1308 QTAILQ_INSERT_AFTER(&consoles, c, s, next);
1310 s->index = c->index;
1311 QTAILQ_INSERT_BEFORE(c, s, next);
1312 /* renumber text consoles */
1313 for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) {
1322 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
1323 HANDLE h, uint32_t offset)
1325 assert(!surface->handle);
1327 surface->handle = h;
1328 surface->handle_offset = offset;
1332 win32_pixman_image_destroy(pixman_image_t *image, void *data)
1334 DisplaySurface *surface = data;
1336 if (!surface->handle) {
1340 assert(surface->handle_offset == 0);
1342 qemu_win32_map_free(
1343 pixman_image_get_data(surface->image),
1350 DisplaySurface *qemu_create_displaysurface(int width, int height)
1352 DisplaySurface *surface;
1355 HANDLE handle = NULL;
1358 trace_displaysurface_create(width, height);
1361 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
1364 surface = qemu_create_displaysurface_from(
1369 surface->flags = QEMU_ALLOCATED_FLAG;
1372 qemu_displaysurface_win32_set_handle(surface, handle, 0);
1377 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1378 pixman_format_code_t format,
1379 int linesize, uint8_t *data)
1381 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1383 trace_displaysurface_create_from(surface, width, height, format);
1384 surface->format = format;
1385 surface->image = pixman_image_create_bits(surface->format,
1387 (void *)data, linesize);
1388 assert(surface->image != NULL);
1390 pixman_image_set_destroy_function(surface->image,
1391 win32_pixman_image_destroy, surface);
1397 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1399 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1401 trace_displaysurface_create_pixman(surface);
1402 surface->format = pixman_image_get_format(image);
1403 surface->image = pixman_image_ref(image);
1408 DisplaySurface *qemu_create_placeholder_surface(int w, int h,
1411 DisplaySurface *surface = qemu_create_displaysurface(w, h);
1412 pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
1413 pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
1414 pixman_image_t *glyph;
1418 x = (w / FONT_WIDTH - len) / 2;
1419 y = (h / FONT_HEIGHT - 1) / 2;
1420 for (i = 0; i < len; i++) {
1421 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1422 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1423 x+i, y, FONT_WIDTH, FONT_HEIGHT);
1424 qemu_pixman_image_unref(glyph);
1426 surface->flags |= QEMU_PLACEHOLDER_FLAG;
1430 void qemu_free_displaysurface(DisplaySurface *surface)
1432 if (surface == NULL) {
1435 trace_displaysurface_free(surface);
1436 qemu_pixman_image_unref(surface->image);
1440 bool console_has_gl(QemuConsole *con)
1442 return con->gl != NULL;
1445 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
1447 if (dcl->ops->dpy_has_dmabuf) {
1448 return dcl->ops->dpy_has_dmabuf(dcl);
1451 if (dcl->ops->dpy_gl_scanout_dmabuf) {
1458 static bool console_compatible_with(QemuConsole *con,
1459 DisplayChangeListener *dcl, Error **errp)
1463 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
1465 if (console_has_gl(con) &&
1466 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
1467 error_setg(errp, "Display %s is incompatible with the GL context",
1468 dcl->ops->dpy_name);
1472 if (flags & GRAPHIC_FLAGS_GL &&
1473 !console_has_gl(con)) {
1474 error_setg(errp, "The console requires a GL context.");
1479 if (flags & GRAPHIC_FLAGS_DMABUF &&
1480 !displaychangelistener_has_dmabuf(dcl)) {
1481 error_setg(errp, "The console requires display DMABUF support.");
1488 void console_handle_touch_event(QemuConsole *con,
1489 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
1491 int width, int height,
1493 InputMultiTouchType type,
1496 struct touch_slot *slot;
1497 bool needs_sync = false;
1501 if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
1503 "Unexpected touch slot number: % " PRId64" >= %d",
1504 num_slot, INPUT_EVENT_SLOTS_MAX);
1508 slot = &touch_slots[num_slot];
1512 if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
1513 slot->tracking_id = num_slot;
1516 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
1517 if (i == num_slot) {
1520 update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
1523 slot = &touch_slots[i];
1525 if (slot->tracking_id == -1) {
1529 if (update == INPUT_MULTI_TOUCH_TYPE_END) {
1530 slot->tracking_id = -1;
1531 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1534 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1535 qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
1536 qemu_input_queue_mtt_abs(con,
1537 INPUT_AXIS_X, (int) slot->x,
1539 i, slot->tracking_id);
1540 qemu_input_queue_mtt_abs(con,
1541 INPUT_AXIS_Y, (int) slot->y,
1543 i, slot->tracking_id);
1549 qemu_input_event_sync();
1553 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
1555 /* display has opengl support */
1558 error_report("The console already has an OpenGL context.");
1564 void register_displaychangelistener(DisplayChangeListener *dcl)
1570 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1571 dcl->ds = get_alloc_displaystate();
1572 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1573 gui_setup_refresh(dcl->ds);
1578 con = active_console;
1580 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
1581 if (con && con->cursor && dcl->ops->dpy_cursor_define) {
1582 dcl->ops->dpy_cursor_define(dcl, con->cursor);
1584 if (con && dcl->ops->dpy_mouse_set) {
1585 dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
1587 text_console_update_cursor(NULL);
1590 void update_displaychangelistener(DisplayChangeListener *dcl,
1593 DisplayState *ds = dcl->ds;
1595 dcl->update_interval = interval;
1596 if (!ds->refreshing && ds->update_interval > interval) {
1597 timer_mod(ds->gui_timer, ds->last_update + interval);
1601 void unregister_displaychangelistener(DisplayChangeListener *dcl)
1603 DisplayState *ds = dcl->ds;
1604 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1608 QLIST_REMOVE(dcl, next);
1610 gui_setup_refresh(ds);
1613 static void dpy_set_ui_info_timer(void *opaque)
1615 QemuConsole *con = opaque;
1617 con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
1620 bool dpy_ui_info_supported(QemuConsole *con)
1623 con = active_console;
1626 return con->hw_ops->ui_info != NULL;
1629 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
1632 con = active_console;
1635 return &con->ui_info;
1638 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
1641 con = active_console;
1644 if (!dpy_ui_info_supported(con)) {
1647 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1648 /* nothing changed -- ignore */
1653 * Typically we get a flood of these as the user resizes the window.
1654 * Wait until the dust has settled (one second without updates), then
1655 * go notify the guest.
1657 con->ui_info = *info;
1658 timer_mod(con->ui_timer,
1659 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0));
1663 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1665 DisplayState *s = con->ds;
1666 DisplayChangeListener *dcl;
1667 int width = qemu_console_get_width(con, x + w);
1668 int height = qemu_console_get_height(con, y + h);
1674 w = MIN(w, width - x);
1675 h = MIN(h, height - y);
1677 if (!qemu_console_is_visible(con)) {
1680 dpy_gfx_update_texture(con, con->surface, x, y, w, h);
1681 QLIST_FOREACH(dcl, &s->listeners, next) {
1682 if (con != (dcl->con ? dcl->con : active_console)) {
1685 if (dcl->ops->dpy_gfx_update) {
1686 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1691 void dpy_gfx_update_full(QemuConsole *con)
1693 int w = qemu_console_get_width(con, 0);
1694 int h = qemu_console_get_height(con, 0);
1696 dpy_gfx_update(con, 0, 0, w, h);
1699 void dpy_gfx_replace_surface(QemuConsole *con,
1700 DisplaySurface *surface)
1702 static const char placeholder_msg[] = "Display output is not active.";
1703 DisplayState *s = con->ds;
1704 DisplaySurface *old_surface = con->surface;
1705 DisplaySurface *new_surface = surface;
1706 DisplayChangeListener *dcl;
1712 width = surface_width(old_surface);
1713 height = surface_height(old_surface);
1719 new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
1722 assert(old_surface != new_surface);
1724 con->scanout.kind = SCANOUT_SURFACE;
1725 con->surface = new_surface;
1726 dpy_gfx_create_texture(con, new_surface);
1727 QLIST_FOREACH(dcl, &s->listeners, next) {
1728 if (con != (dcl->con ? dcl->con : active_console)) {
1731 displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
1733 dpy_gfx_destroy_texture(con, old_surface);
1734 qemu_free_displaysurface(old_surface);
1737 bool dpy_gfx_check_format(QemuConsole *con,
1738 pixman_format_code_t format)
1740 DisplayChangeListener *dcl;
1741 DisplayState *s = con->ds;
1743 QLIST_FOREACH(dcl, &s->listeners, next) {
1744 if (dcl->con && dcl->con != con) {
1745 /* dcl bound to another console -> skip */
1748 if (dcl->ops->dpy_gfx_check_format) {
1749 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1753 /* default is to allow native 32 bpp only */
1754 if (format != qemu_default_pixman_format(32, true)) {
1762 static void dpy_refresh(DisplayState *s)
1764 DisplayChangeListener *dcl;
1766 QLIST_FOREACH(dcl, &s->listeners, next) {
1767 if (dcl->ops->dpy_refresh) {
1768 dcl->ops->dpy_refresh(dcl);
1773 void dpy_text_cursor(QemuConsole *con, int x, int y)
1775 DisplayState *s = con->ds;
1776 DisplayChangeListener *dcl;
1778 if (!qemu_console_is_visible(con)) {
1781 QLIST_FOREACH(dcl, &s->listeners, next) {
1782 if (con != (dcl->con ? dcl->con : active_console)) {
1785 if (dcl->ops->dpy_text_cursor) {
1786 dcl->ops->dpy_text_cursor(dcl, x, y);
1791 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1793 DisplayState *s = con->ds;
1794 DisplayChangeListener *dcl;
1796 if (!qemu_console_is_visible(con)) {
1799 QLIST_FOREACH(dcl, &s->listeners, next) {
1800 if (con != (dcl->con ? dcl->con : active_console)) {
1803 if (dcl->ops->dpy_text_update) {
1804 dcl->ops->dpy_text_update(dcl, x, y, w, h);
1809 void dpy_text_resize(QemuConsole *con, int w, int h)
1811 DisplayState *s = con->ds;
1812 DisplayChangeListener *dcl;
1814 if (!qemu_console_is_visible(con)) {
1817 QLIST_FOREACH(dcl, &s->listeners, next) {
1818 if (con != (dcl->con ? dcl->con : active_console)) {
1821 if (dcl->ops->dpy_text_resize) {
1822 dcl->ops->dpy_text_resize(dcl, w, h);
1827 void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1829 DisplayState *s = con->ds;
1830 DisplayChangeListener *dcl;
1834 con->cursor_on = on;
1835 if (!qemu_console_is_visible(con)) {
1838 QLIST_FOREACH(dcl, &s->listeners, next) {
1839 if (con != (dcl->con ? dcl->con : active_console)) {
1842 if (dcl->ops->dpy_mouse_set) {
1843 dcl->ops->dpy_mouse_set(dcl, x, y, on);
1848 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1850 DisplayState *s = con->ds;
1851 DisplayChangeListener *dcl;
1853 cursor_unref(con->cursor);
1854 con->cursor = cursor_ref(cursor);
1855 if (!qemu_console_is_visible(con)) {
1858 QLIST_FOREACH(dcl, &s->listeners, next) {
1859 if (con != (dcl->con ? dcl->con : active_console)) {
1862 if (dcl->ops->dpy_cursor_define) {
1863 dcl->ops->dpy_cursor_define(dcl, cursor);
1868 bool dpy_cursor_define_supported(QemuConsole *con)
1870 DisplayState *s = con->ds;
1871 DisplayChangeListener *dcl;
1873 QLIST_FOREACH(dcl, &s->listeners, next) {
1874 if (dcl->ops->dpy_cursor_define) {
1881 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
1882 struct QEMUGLParams *qparams)
1885 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
1888 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
1891 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
1894 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
1897 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
1900 void dpy_gl_scanout_disable(QemuConsole *con)
1902 DisplayState *s = con->ds;
1903 DisplayChangeListener *dcl;
1905 if (con->scanout.kind != SCANOUT_SURFACE) {
1906 con->scanout.kind = SCANOUT_NONE;
1908 QLIST_FOREACH(dcl, &s->listeners, next) {
1909 if (con != (dcl->con ? dcl->con : active_console)) {
1912 if (dcl->ops->dpy_gl_scanout_disable) {
1913 dcl->ops->dpy_gl_scanout_disable(dcl);
1918 void dpy_gl_scanout_texture(QemuConsole *con,
1919 uint32_t backing_id,
1920 bool backing_y_0_top,
1921 uint32_t backing_width,
1922 uint32_t backing_height,
1923 uint32_t x, uint32_t y,
1924 uint32_t width, uint32_t height,
1927 DisplayState *s = con->ds;
1928 DisplayChangeListener *dcl;
1930 con->scanout.kind = SCANOUT_TEXTURE;
1931 con->scanout.texture = (ScanoutTexture) {
1932 backing_id, backing_y_0_top, backing_width, backing_height,
1933 x, y, width, height, d3d_tex2d,
1935 QLIST_FOREACH(dcl, &s->listeners, next) {
1936 if (con != (dcl->con ? dcl->con : active_console)) {
1939 if (dcl->ops->dpy_gl_scanout_texture) {
1940 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
1942 backing_width, backing_height,
1943 x, y, width, height,
1949 void dpy_gl_scanout_dmabuf(QemuConsole *con,
1952 DisplayState *s = con->ds;
1953 DisplayChangeListener *dcl;
1955 con->scanout.kind = SCANOUT_DMABUF;
1956 con->scanout.dmabuf = dmabuf;
1957 QLIST_FOREACH(dcl, &s->listeners, next) {
1958 if (con != (dcl->con ? dcl->con : active_console)) {
1961 if (dcl->ops->dpy_gl_scanout_dmabuf) {
1962 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
1967 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
1968 bool have_hot, uint32_t hot_x, uint32_t hot_y)
1970 DisplayState *s = con->ds;
1971 DisplayChangeListener *dcl;
1973 QLIST_FOREACH(dcl, &s->listeners, next) {
1974 if (con != (dcl->con ? dcl->con : active_console)) {
1977 if (dcl->ops->dpy_gl_cursor_dmabuf) {
1978 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf,
1979 have_hot, hot_x, hot_y);
1984 void dpy_gl_cursor_position(QemuConsole *con,
1985 uint32_t pos_x, uint32_t pos_y)
1987 DisplayState *s = con->ds;
1988 DisplayChangeListener *dcl;
1990 QLIST_FOREACH(dcl, &s->listeners, next) {
1991 if (con != (dcl->con ? dcl->con : active_console)) {
1994 if (dcl->ops->dpy_gl_cursor_position) {
1995 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
2000 void dpy_gl_release_dmabuf(QemuConsole *con,
2003 DisplayState *s = con->ds;
2004 DisplayChangeListener *dcl;
2006 QLIST_FOREACH(dcl, &s->listeners, next) {
2007 if (con != (dcl->con ? dcl->con : active_console)) {
2010 if (dcl->ops->dpy_gl_release_dmabuf) {
2011 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
2016 void dpy_gl_update(QemuConsole *con,
2017 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
2019 DisplayState *s = con->ds;
2020 DisplayChangeListener *dcl;
2024 graphic_hw_gl_block(con, true);
2025 QLIST_FOREACH(dcl, &s->listeners, next) {
2026 if (con != (dcl->con ? dcl->con : active_console)) {
2029 if (dcl->ops->dpy_gl_update) {
2030 dcl->ops->dpy_gl_update(dcl, x, y, w, h);
2033 graphic_hw_gl_block(con, false);
2036 /***********************************************************/
2037 /* register display */
2039 /* console.c internal use only */
2040 static DisplayState *get_alloc_displaystate(void)
2042 if (!display_state) {
2043 display_state = g_new0(DisplayState, 1);
2044 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2045 text_console_update_cursor, NULL);
2047 return display_state;
2051 * Called by main(), after creating QemuConsoles
2052 * and before initializing ui (sdl/vnc/...).
2054 DisplayState *init_displaystate(void)
2059 get_alloc_displaystate();
2060 QTAILQ_FOREACH(con, &consoles, next) {
2061 if (con->console_type != GRAPHIC_CONSOLE &&
2063 text_console_do_init(con->chr, display_state);
2066 /* Hook up into the qom tree here (not in new_console()), once
2067 * all QemuConsoles are created and the order / numbering
2068 * doesn't change any more */
2069 name = g_strdup_printf("console[%d]", con->index);
2070 object_property_add_child(container_get(object_get_root(), "/backend"),
2075 return display_state;
2078 void graphic_console_set_hwops(QemuConsole *con,
2079 const GraphicHwOps *hw_ops,
2082 con->hw_ops = hw_ops;
2086 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
2087 const GraphicHwOps *hw_ops,
2090 static const char noinit[] =
2091 "Guest has not initialized the display (yet).";
2096 DisplaySurface *surface;
2098 ds = get_alloc_displaystate();
2099 s = qemu_console_lookup_unused();
2101 trace_console_gfx_reuse(s->index);
2102 width = qemu_console_get_width(s, 0);
2103 height = qemu_console_get_height(s, 0);
2105 trace_console_gfx_new();
2106 s = new_console(ds, GRAPHIC_CONSOLE, head);
2107 s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2108 dpy_set_ui_info_timer, s);
2110 graphic_console_set_hwops(s, hw_ops, opaque);
2112 object_property_set_link(OBJECT(s), "device", OBJECT(dev),
2116 surface = qemu_create_placeholder_surface(width, height, noinit);
2117 dpy_gfx_replace_surface(s, surface);
2118 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2119 graphic_hw_gl_unblock_timer, s);
2123 static const GraphicHwOps unused_ops = {
2127 void graphic_console_close(QemuConsole *con)
2129 static const char unplugged[] =
2130 "Guest display has been unplugged";
2131 DisplaySurface *surface;
2132 int width = qemu_console_get_width(con, 640);
2133 int height = qemu_console_get_height(con, 480);
2135 trace_console_gfx_close(con->index);
2136 object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
2137 graphic_console_set_hwops(con, &unused_ops, NULL);
2140 dpy_gl_scanout_disable(con);
2142 surface = qemu_create_placeholder_surface(width, height, unplugged);
2143 dpy_gfx_replace_surface(con, surface);
2146 QemuConsole *qemu_console_lookup_by_index(unsigned int index)
2150 QTAILQ_FOREACH(con, &consoles, next) {
2151 if (con->index == index) {
2158 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
2164 QTAILQ_FOREACH(con, &consoles, next) {
2165 obj = object_property_get_link(OBJECT(con),
2166 "device", &error_abort);
2167 if (DEVICE(obj) != dev) {
2170 h = object_property_get_uint(OBJECT(con),
2171 "head", &error_abort);
2180 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
2181 uint32_t head, Error **errp)
2186 dev = qdev_find_recursive(sysbus_get_default(), device_id);
2188 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
2189 "Device '%s' not found", device_id);
2193 con = qemu_console_lookup_by_device(dev, head);
2195 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
2203 QemuConsole *qemu_console_lookup_unused(void)
2208 QTAILQ_FOREACH(con, &consoles, next) {
2209 if (con->hw_ops != &unused_ops) {
2212 obj = object_property_get_link(OBJECT(con),
2213 "device", &error_abort);
2222 QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
2225 con = active_console;
2227 return con ? con->cursor : NULL;
2230 bool qemu_console_is_visible(QemuConsole *con)
2232 return (con == active_console) || (con->dcls > 0);
2235 bool qemu_console_is_graphic(QemuConsole *con)
2238 con = active_console;
2240 return con && (con->console_type == GRAPHIC_CONSOLE);
2243 bool qemu_console_is_fixedsize(QemuConsole *con)
2246 con = active_console;
2248 return con && (con->console_type != TEXT_CONSOLE);
2251 bool qemu_console_is_gl_blocked(QemuConsole *con)
2253 assert(con != NULL);
2254 return con->gl_block;
2257 bool qemu_console_is_multihead(DeviceState *dev)
2261 uint32_t f = 0xffffffff;
2264 QTAILQ_FOREACH(con, &consoles, next) {
2265 obj = object_property_get_link(OBJECT(con),
2266 "device", &error_abort);
2267 if (DEVICE(obj) != dev) {
2271 h = object_property_get_uint(OBJECT(con),
2272 "head", &error_abort);
2273 if (f == 0xffffffff) {
2275 } else if (h != f) {
2282 char *qemu_console_get_label(QemuConsole *con)
2284 if (con->console_type == GRAPHIC_CONSOLE) {
2289 dev = DEVICE(con->device);
2290 multihead = qemu_console_is_multihead(dev);
2292 return g_strdup_printf("%s.%d", dev->id ?
2294 object_get_typename(con->device),
2297 return g_strdup_printf("%s", dev->id ?
2299 object_get_typename(con->device));
2302 return g_strdup("VGA");
2304 if (con->chr && con->chr->label) {
2305 return g_strdup(con->chr->label);
2307 return g_strdup_printf("vc%d", con->index);
2311 int qemu_console_get_index(QemuConsole *con)
2314 con = active_console;
2316 return con ? con->index : -1;
2319 uint32_t qemu_console_get_head(QemuConsole *con)
2322 con = active_console;
2324 return con ? con->head : -1;
2327 int qemu_console_get_width(QemuConsole *con, int fallback)
2330 con = active_console;
2335 switch (con->scanout.kind) {
2336 case SCANOUT_DMABUF:
2337 return con->scanout.dmabuf->width;
2338 case SCANOUT_TEXTURE:
2339 return con->scanout.texture.width;
2340 case SCANOUT_SURFACE:
2341 return surface_width(con->surface);
2347 int qemu_console_get_height(QemuConsole *con, int fallback)
2350 con = active_console;
2355 switch (con->scanout.kind) {
2356 case SCANOUT_DMABUF:
2357 return con->scanout.dmabuf->height;
2358 case SCANOUT_TEXTURE:
2359 return con->scanout.texture.height;
2360 case SCANOUT_SURFACE:
2361 return surface_height(con->surface);
2367 static void vc_chr_accept_input(Chardev *chr)
2369 VCChardev *drv = VC_CHARDEV(chr);
2370 QemuConsole *s = drv->console;
2375 static void vc_chr_set_echo(Chardev *chr, bool echo)
2377 VCChardev *drv = VC_CHARDEV(chr);
2378 QemuConsole *s = drv->console;
2383 static void text_console_update_cursor_timer(void)
2385 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
2386 + CONSOLE_CURSOR_PERIOD / 2);
2389 static void text_console_update_cursor(void *opaque)
2394 cursor_visible_phase = !cursor_visible_phase;
2396 QTAILQ_FOREACH(s, &consoles, next) {
2397 if (qemu_console_is_graphic(s) ||
2398 !qemu_console_is_visible(s)) {
2402 graphic_hw_invalidate(s);
2406 text_console_update_cursor_timer();
2410 static const GraphicHwOps text_console_ops = {
2411 .invalidate = text_console_invalidate,
2412 .text_update = text_console_update,
2415 static void text_console_do_init(Chardev *chr, DisplayState *ds)
2417 VCChardev *drv = VC_CHARDEV(chr);
2418 QemuConsole *s = drv->console;
2419 int g_width = 80 * FONT_WIDTH;
2420 int g_height = 24 * FONT_HEIGHT;
2422 fifo8_create(&s->out_fifo, 16);
2427 s->total_height = DEFAULT_BACKSCROLL;
2430 if (s->scanout.kind != SCANOUT_SURFACE) {
2431 if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) {
2432 g_width = qemu_console_get_width(active_console, g_width);
2433 g_height = qemu_console_get_height(active_console, g_height);
2435 s->surface = qemu_create_displaysurface(g_width, g_height);
2436 s->scanout.kind = SCANOUT_SURFACE;
2439 s->hw_ops = &text_console_ops;
2442 /* Set text attribute defaults */
2443 s->t_attrib_default.bold = 0;
2444 s->t_attrib_default.uline = 0;
2445 s->t_attrib_default.blink = 0;
2446 s->t_attrib_default.invers = 0;
2447 s->t_attrib_default.unvisible = 0;
2448 s->t_attrib_default.fgcol = QEMU_COLOR_WHITE;
2449 s->t_attrib_default.bgcol = QEMU_COLOR_BLACK;
2450 /* set current text attributes to default */
2451 s->t_attrib = s->t_attrib_default;
2452 text_console_resize(s);
2457 s->t_attrib.bgcol = QEMU_COLOR_BLUE;
2458 msg = g_strdup_printf("%s console\r\n", chr->label);
2459 qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
2461 s->t_attrib = s->t_attrib_default;
2464 qemu_chr_be_event(chr, CHR_EVENT_OPENED);
2467 static void vc_chr_open(Chardev *chr,
2468 ChardevBackend *backend,
2472 ChardevVC *vc = backend->u.vc.data;
2473 VCChardev *drv = VC_CHARDEV(chr);
2476 unsigned height = 0;
2478 if (vc->has_width) {
2480 } else if (vc->has_cols) {
2481 width = vc->cols * FONT_WIDTH;
2484 if (vc->has_height) {
2485 height = vc->height;
2486 } else if (vc->has_rows) {
2487 height = vc->rows * FONT_HEIGHT;
2490 trace_console_txt_new(width, height);
2491 if (width == 0 || height == 0) {
2492 s = new_console(NULL, TEXT_CONSOLE, 0);
2494 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
2495 s->scanout.kind = SCANOUT_SURFACE;
2496 s->surface = qemu_create_displaysurface(width, height);
2500 error_setg(errp, "cannot create text console");
2507 if (display_state) {
2508 text_console_do_init(chr, display_state);
2511 /* console/chardev init sometimes completes elsewhere in a 2nd
2512 * stage, so defer OPENED events until they are fully initialized
2517 void qemu_console_resize(QemuConsole *s, int width, int height)
2519 DisplaySurface *surface = qemu_console_surface(s);
2521 assert(s->console_type == GRAPHIC_CONSOLE);
2523 if ((s->scanout.kind != SCANOUT_SURFACE ||
2524 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) &&
2525 qemu_console_get_width(s, -1) == width &&
2526 qemu_console_get_height(s, -1) == height) {
2530 surface = qemu_create_displaysurface(width, height);
2531 dpy_gfx_replace_surface(s, surface);
2534 DisplaySurface *qemu_console_surface(QemuConsole *console)
2536 switch (console->scanout.kind) {
2537 case SCANOUT_SURFACE:
2538 return console->surface;
2544 PixelFormat qemu_default_pixelformat(int bpp)
2546 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2547 PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2551 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2553 void qemu_display_register(QemuDisplay *ui)
2555 assert(ui->type < DISPLAY_TYPE__MAX);
2556 dpys[ui->type] = ui;
2559 bool qemu_display_find_default(DisplayOptions *opts)
2561 static DisplayType prio[] = {
2562 #if defined(CONFIG_GTK)
2565 #if defined(CONFIG_SDL)
2568 #if defined(CONFIG_COCOA)
2574 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
2575 if (dpys[prio[i]] == NULL) {
2576 Error *local_err = NULL;
2577 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
2579 error_report_err(local_err);
2582 if (dpys[prio[i]] == NULL) {
2585 opts->type = prio[i];
2591 void qemu_display_early_init(DisplayOptions *opts)
2593 assert(opts->type < DISPLAY_TYPE__MAX);
2594 if (opts->type == DISPLAY_TYPE_NONE) {
2597 if (dpys[opts->type] == NULL) {
2598 Error *local_err = NULL;
2599 int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
2601 error_report_err(local_err);
2604 if (dpys[opts->type] == NULL) {
2605 error_report("Display '%s' is not available.",
2606 DisplayType_str(opts->type));
2609 if (dpys[opts->type]->early_init) {
2610 dpys[opts->type]->early_init(opts);
2614 void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2616 assert(opts->type < DISPLAY_TYPE__MAX);
2617 if (opts->type == DISPLAY_TYPE_NONE) {
2620 assert(dpys[opts->type] != NULL);
2621 dpys[opts->type]->init(ds, opts);
2624 void qemu_display_help(void)
2628 printf("Available display backend types:\n");
2630 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
2632 Error *local_err = NULL;
2633 int rv = ui_module_load(DisplayType_str(idx), &local_err);
2635 error_report_err(local_err);
2639 printf("%s\n", DisplayType_str(dpys[idx]->type));
2644 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
2649 backend->type = CHARDEV_BACKEND_KIND_VC;
2650 vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2651 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
2653 val = qemu_opt_get_number(opts, "width", 0);
2655 vc->has_width = true;
2659 val = qemu_opt_get_number(opts, "height", 0);
2661 vc->has_height = true;
2665 val = qemu_opt_get_number(opts, "cols", 0);
2667 vc->has_cols = true;
2671 val = qemu_opt_get_number(opts, "rows", 0);
2673 vc->has_rows = true;
2678 static const TypeInfo qemu_console_info = {
2679 .name = TYPE_QEMU_CONSOLE,
2680 .parent = TYPE_OBJECT,
2681 .instance_size = sizeof(QemuConsole),
2682 .class_size = sizeof(QemuConsoleClass),
2685 static void char_vc_class_init(ObjectClass *oc, void *data)
2687 ChardevClass *cc = CHARDEV_CLASS(oc);
2689 cc->parse = qemu_chr_parse_vc;
2690 cc->open = vc_chr_open;
2691 cc->chr_write = vc_chr_write;
2692 cc->chr_accept_input = vc_chr_accept_input;
2693 cc->chr_set_echo = vc_chr_set_echo;
2696 static const TypeInfo char_vc_type_info = {
2697 .name = TYPE_CHARDEV_VC,
2698 .parent = TYPE_CHARDEV,
2699 .instance_size = sizeof(VCChardev),
2700 .class_init = char_vc_class_init,
2703 void qemu_console_early_init(void)
2705 /* set the default vc driver */
2706 if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2707 type_register(&char_vc_type_info);
2711 static void register_types(void)
2713 type_register_static(&qemu_console_info);
2716 type_init(register_types);