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;
136 QLIST_HEAD(, DisplayChangeListener) listeners;
139 static DisplayState *display_state;
140 static QemuConsole *active_console;
141 static QTAILQ_HEAD(, QemuConsole) consoles =
142 QTAILQ_HEAD_INITIALIZER(consoles);
143 static bool cursor_visible_phase;
144 static QEMUTimer *cursor_timer;
146 static void text_console_do_init(Chardev *chr, DisplayState *ds);
147 static void dpy_refresh(DisplayState *s);
148 static DisplayState *get_alloc_displaystate(void);
149 static void text_console_update_cursor_timer(void);
150 static void text_console_update_cursor(void *opaque);
151 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
152 static bool console_compatible_with(QemuConsole *con,
153 DisplayChangeListener *dcl, Error **errp);
155 static void gui_update(void *opaque)
157 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
158 uint64_t dcl_interval;
159 DisplayState *ds = opaque;
160 DisplayChangeListener *dcl;
162 ds->refreshing = true;
164 ds->refreshing = false;
166 QLIST_FOREACH(dcl, &ds->listeners, next) {
167 dcl_interval = dcl->update_interval ?
168 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
169 if (interval > dcl_interval) {
170 interval = dcl_interval;
173 if (ds->update_interval != interval) {
174 ds->update_interval = interval;
175 trace_console_refresh(interval);
177 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
178 timer_mod(ds->gui_timer, ds->last_update + interval);
181 static void gui_setup_refresh(DisplayState *ds)
183 DisplayChangeListener *dcl;
184 bool need_timer = false;
186 QLIST_FOREACH(dcl, &ds->listeners, next) {
187 if (dcl->ops->dpy_refresh != NULL) {
192 if (need_timer && ds->gui_timer == NULL) {
193 ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
194 timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
196 if (!need_timer && ds->gui_timer != NULL) {
197 timer_free(ds->gui_timer);
198 ds->gui_timer = NULL;
202 void graphic_hw_update_done(QemuConsole *con)
205 qemu_co_enter_all(&con->dump_queue, NULL);
209 void graphic_hw_update(QemuConsole *con)
212 con = con ? con : active_console;
216 if (con->hw_ops->gfx_update) {
217 con->hw_ops->gfx_update(con->hw);
218 async = con->hw_ops->gfx_update_async;
221 graphic_hw_update_done(con);
225 static void graphic_hw_update_bh(void *con)
227 graphic_hw_update(con);
230 void qemu_console_co_wait_update(QemuConsole *con)
232 if (qemu_co_queue_empty(&con->dump_queue)) {
233 /* Defer the update, it will restart the pending coroutines */
234 aio_bh_schedule_oneshot(qemu_get_aio_context(),
235 graphic_hw_update_bh, con);
237 qemu_co_queue_wait(&con->dump_queue, NULL);
241 static void graphic_hw_gl_unblock_timer(void *opaque)
243 warn_report("console: no gl-unblock within one second");
246 void graphic_hw_gl_block(QemuConsole *con, bool block)
256 assert(con->gl_block >= 0);
257 if (!con->hw_ops->gl_block) {
260 if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {
263 con->hw_ops->gl_block(con->hw, block);
266 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
267 timeout += 1000; /* one sec */
268 timer_mod(con->gl_unblock_timer, timeout);
270 timer_del(con->gl_unblock_timer);
274 int qemu_console_get_window_id(QemuConsole *con)
276 return con->window_id;
279 void qemu_console_set_window_id(QemuConsole *con, int window_id)
281 con->window_id = window_id;
284 void graphic_hw_invalidate(QemuConsole *con)
287 con = active_console;
289 if (con && con->hw_ops->invalidate) {
290 con->hw_ops->invalidate(con->hw);
294 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
297 con = active_console;
299 if (con && con->hw_ops->text_update) {
300 con->hw_ops->text_update(con->hw, chardata);
304 static void vga_fill_rect(QemuConsole *con,
305 int posx, int posy, int width, int height,
306 pixman_color_t color)
308 DisplaySurface *surface = qemu_console_surface(con);
309 pixman_rectangle16_t rect = {
310 .x = posx, .y = posy, .width = width, .height = height
313 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
317 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
318 static void vga_bitblt(QemuConsole *con,
319 int xs, int ys, int xd, int yd, int w, int h)
321 DisplaySurface *surface = qemu_console_surface(con);
323 pixman_image_composite(PIXMAN_OP_SRC,
324 surface->image, NULL, surface->image,
325 xs, ys, 0, 0, xd, yd, w, h);
328 /***********************************************************/
329 /* basic char display */
331 #define FONT_HEIGHT 16
336 #define QEMU_RGB(r, g, b) \
337 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
339 static const pixman_color_t color_table_rgb[2][8] = {
341 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
342 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
343 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */
344 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
345 [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */
346 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
347 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
348 [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
351 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
352 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */
353 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */
354 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
355 [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */
356 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
357 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
358 [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */
362 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
363 TextAttributes *t_attrib)
365 static pixman_image_t *glyphs[256];
366 DisplaySurface *surface = qemu_console_surface(s);
367 pixman_color_t fgcol, bgcol;
369 if (t_attrib->invers) {
370 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
371 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
373 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
374 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
378 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
380 qemu_pixman_glyph_render(glyphs[ch], surface->image,
381 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
384 static void text_console_resize(QemuConsole *s)
386 TextCell *cells, *c, *c1;
387 int w1, x, y, last_width;
389 assert(s->scanout.kind == SCANOUT_SURFACE);
391 last_width = s->width;
392 s->width = surface_width(s->surface) / FONT_WIDTH;
393 s->height = surface_height(s->surface) / FONT_HEIGHT;
399 cells = g_new(TextCell, s->width * s->total_height + 1);
400 for(y = 0; y < s->total_height; y++) {
401 c = &cells[y * s->width];
403 c1 = &s->cells[y * last_width];
404 for(x = 0; x < w1; x++) {
408 for(x = w1; x < s->width; x++) {
410 c->t_attrib = s->t_attrib_default;
418 static inline void text_update_xy(QemuConsole *s, int x, int y)
420 s->text_x[0] = MIN(s->text_x[0], x);
421 s->text_x[1] = MAX(s->text_x[1], x);
422 s->text_y[0] = MIN(s->text_y[0], y);
423 s->text_y[1] = MAX(s->text_y[1], y);
426 static void invalidate_xy(QemuConsole *s, int x, int y)
428 if (!qemu_console_is_visible(s)) {
431 if (s->update_x0 > x * FONT_WIDTH)
432 s->update_x0 = x * FONT_WIDTH;
433 if (s->update_y0 > y * FONT_HEIGHT)
434 s->update_y0 = y * FONT_HEIGHT;
435 if (s->update_x1 < (x + 1) * FONT_WIDTH)
436 s->update_x1 = (x + 1) * FONT_WIDTH;
437 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
438 s->update_y1 = (y + 1) * FONT_HEIGHT;
441 static void update_xy(QemuConsole *s, int x, int y)
446 text_update_xy(s, x, y);
448 y1 = (s->y_base + y) % s->total_height;
449 y2 = y1 - s->y_displayed;
451 y2 += s->total_height;
453 if (y2 < s->height) {
457 c = &s->cells[y1 * s->width + x];
458 vga_putcharxy(s, x, y2, c->ch,
460 invalidate_xy(s, x, y2);
464 static void console_show_cursor(QemuConsole *s, int show)
470 s->cursor_invalidate = 1;
475 y1 = (s->y_base + s->y) % s->total_height;
476 y = y1 - s->y_displayed;
478 y += s->total_height;
481 c = &s->cells[y1 * s->width + x];
482 if (show && cursor_visible_phase) {
483 TextAttributes t_attrib = s->t_attrib_default;
484 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
485 vga_putcharxy(s, x, y, c->ch, &t_attrib);
487 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
489 invalidate_xy(s, x, y);
493 static void console_refresh(QemuConsole *s)
495 DisplaySurface *surface = qemu_console_surface(s);
501 s->text_x[1] = s->width - 1;
502 s->text_y[1] = s->height - 1;
503 s->cursor_invalidate = 1;
505 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
506 color_table_rgb[0][QEMU_COLOR_BLACK]);
508 for (y = 0; y < s->height; y++) {
509 c = s->cells + y1 * s->width;
510 for (x = 0; x < s->width; x++) {
511 vga_putcharxy(s, x, y, c->ch,
515 if (++y1 == s->total_height) {
519 console_show_cursor(s, 1);
520 dpy_gfx_update(s, 0, 0,
521 surface_width(surface), surface_height(surface));
524 static void console_scroll(QemuConsole *s, int ydelta)
529 for(i = 0; i < ydelta; i++) {
530 if (s->y_displayed == s->y_base)
532 if (++s->y_displayed == s->total_height)
537 i = s->backscroll_height;
538 if (i > s->total_height - s->height)
539 i = s->total_height - s->height;
542 y1 += s->total_height;
543 for(i = 0; i < ydelta; i++) {
544 if (s->y_displayed == y1)
546 if (--s->y_displayed < 0)
547 s->y_displayed = s->total_height - 1;
553 static void console_put_lf(QemuConsole *s)
559 if (s->y >= s->height) {
560 s->y = s->height - 1;
562 if (s->y_displayed == s->y_base) {
563 if (++s->y_displayed == s->total_height)
566 if (++s->y_base == s->total_height)
568 if (s->backscroll_height < s->total_height)
569 s->backscroll_height++;
570 y1 = (s->y_base + s->height - 1) % s->total_height;
571 c = &s->cells[y1 * s->width];
572 for(x = 0; x < s->width; x++) {
574 c->t_attrib = s->t_attrib_default;
577 if (s->y_displayed == s->y_base) {
580 s->text_x[1] = s->width - 1;
581 s->text_y[1] = s->height - 1;
583 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
584 s->width * FONT_WIDTH,
585 (s->height - 1) * FONT_HEIGHT);
586 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
587 s->width * FONT_WIDTH, FONT_HEIGHT,
588 color_table_rgb[0][s->t_attrib_default.bgcol]);
591 s->update_x1 = s->width * FONT_WIDTH;
592 s->update_y1 = s->height * FONT_HEIGHT;
597 /* Set console attributes depending on the current escape codes.
598 * NOTE: I know this code is not very efficient (checking every color for it
599 * self) but it is more readable and better maintainable.
601 static void console_handle_escape(QemuConsole *s)
605 for (i=0; i<s->nb_esc_params; i++) {
606 switch (s->esc_params[i]) {
607 case 0: /* reset all console attributes to default */
608 s->t_attrib = s->t_attrib_default;
611 s->t_attrib.bold = 1;
614 s->t_attrib.uline = 1;
617 s->t_attrib.blink = 1;
620 s->t_attrib.invers = 1;
623 s->t_attrib.unvisible = 1;
626 s->t_attrib.bold = 0;
629 s->t_attrib.uline = 0;
632 s->t_attrib.blink = 0;
635 s->t_attrib.invers = 0;
638 s->t_attrib.unvisible = 0;
640 /* set foreground color */
642 s->t_attrib.fgcol = QEMU_COLOR_BLACK;
645 s->t_attrib.fgcol = QEMU_COLOR_RED;
648 s->t_attrib.fgcol = QEMU_COLOR_GREEN;
651 s->t_attrib.fgcol = QEMU_COLOR_YELLOW;
654 s->t_attrib.fgcol = QEMU_COLOR_BLUE;
657 s->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
660 s->t_attrib.fgcol = QEMU_COLOR_CYAN;
663 s->t_attrib.fgcol = QEMU_COLOR_WHITE;
665 /* set background color */
667 s->t_attrib.bgcol = QEMU_COLOR_BLACK;
670 s->t_attrib.bgcol = QEMU_COLOR_RED;
673 s->t_attrib.bgcol = QEMU_COLOR_GREEN;
676 s->t_attrib.bgcol = QEMU_COLOR_YELLOW;
679 s->t_attrib.bgcol = QEMU_COLOR_BLUE;
682 s->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
685 s->t_attrib.bgcol = QEMU_COLOR_CYAN;
688 s->t_attrib.bgcol = QEMU_COLOR_WHITE;
694 static void console_clear_xy(QemuConsole *s, int x, int y)
696 int y1 = (s->y_base + y) % s->total_height;
700 TextCell *c = &s->cells[y1 * s->width + x];
702 c->t_attrib = s->t_attrib_default;
706 static void console_put_one(QemuConsole *s, int ch)
710 if (s->x >= s->width) {
715 y1 = (s->y_base + s->y) % s->total_height;
716 c = &s->cells[y1 * s->width + s->x];
718 c->t_attrib = s->t_attrib;
719 update_xy(s, s->x, s->y);
723 static void console_respond_str(QemuConsole *s, const char *buf)
726 console_put_one(s, *buf);
731 /* set cursor, checking bounds */
732 static void set_cursor(QemuConsole *s, int x, int y)
740 if (y >= s->height) {
751 static void console_putchar(QemuConsole *s, int ch)
760 case '\r': /* carriage return */
763 case '\n': /* newline */
766 case '\b': /* backspace */
770 case '\t': /* tabspace */
771 if (s->x + (8 - (s->x % 8)) > s->width) {
775 s->x = s->x + (8 - (s->x % 8));
778 case '\a': /* alert aka. bell */
779 /* TODO: has to be implemented */
782 /* SI (shift in), character set 0 (ignored) */
785 /* SO (shift out), character set 1 (ignored) */
787 case 27: /* esc (introducing an escape sequence) */
788 s->state = TTY_STATE_ESC;
791 console_put_one(s, ch);
795 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
797 for(i=0;i<MAX_ESC_PARAMS;i++)
798 s->esc_params[i] = 0;
799 s->nb_esc_params = 0;
800 s->state = TTY_STATE_CSI;
802 s->state = TTY_STATE_NORM;
805 case TTY_STATE_CSI: /* handle escape sequence parameters */
806 if (ch >= '0' && ch <= '9') {
807 if (s->nb_esc_params < MAX_ESC_PARAMS) {
808 int *param = &s->esc_params[s->nb_esc_params];
809 int digit = (ch - '0');
811 *param = (*param <= (INT_MAX - digit) / 10) ?
812 *param * 10 + digit : INT_MAX;
815 if (s->nb_esc_params < MAX_ESC_PARAMS)
817 if (ch == ';' || ch == '?') {
820 trace_console_putchar_csi(s->esc_params[0], s->esc_params[1],
821 ch, s->nb_esc_params);
822 s->state = TTY_STATE_NORM;
826 if (s->esc_params[0] == 0) {
827 s->esc_params[0] = 1;
829 set_cursor(s, s->x, s->y - s->esc_params[0]);
832 /* move cursor down */
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 right */
840 if (s->esc_params[0] == 0) {
841 s->esc_params[0] = 1;
843 set_cursor(s, s->x + s->esc_params[0], s->y);
846 /* move cursor left */
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 to column */
854 set_cursor(s, s->esc_params[0] - 1, s->y);
858 /* move cursor to row, column */
859 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
862 switch (s->esc_params[0]) {
864 /* clear to end of screen */
865 for (y = s->y; y < s->height; y++) {
866 for (x = 0; x < s->width; x++) {
867 if (y == s->y && x < s->x) {
870 console_clear_xy(s, x, y);
875 /* clear from beginning of screen */
876 for (y = 0; y <= s->y; y++) {
877 for (x = 0; x < s->width; x++) {
878 if (y == s->y && x > s->x) {
881 console_clear_xy(s, x, y);
886 /* clear entire screen */
887 for (y = 0; y <= s->height; y++) {
888 for (x = 0; x < s->width; x++) {
889 console_clear_xy(s, x, y);
896 switch (s->esc_params[0]) {
899 for(x = s->x; x < s->width; x++) {
900 console_clear_xy(s, x, s->y);
904 /* clear from beginning of line */
905 for (x = 0; x <= s->x && x < s->width; x++) {
906 console_clear_xy(s, x, s->y);
910 /* clear entire line */
911 for(x = 0; x < s->width; x++) {
912 console_clear_xy(s, x, s->y);
918 console_handle_escape(s);
921 switch (s->esc_params[0]) {
923 /* report console status (always succeed)*/
924 console_respond_str(s, "\033[0n");
927 /* report cursor position */
928 sprintf(response, "\033[%d;%dR",
929 (s->y_base + s->y) % s->total_height + 1,
931 console_respond_str(s, response);
936 /* save cursor position */
941 /* restore cursor position */
946 trace_console_putchar_unhandled(ch);
954 static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl,
955 struct DisplaySurface *new_surface,
958 if (dcl->ops->dpy_gfx_switch) {
959 dcl->ops->dpy_gfx_switch(dcl, new_surface);
962 if (update && dcl->ops->dpy_gfx_update) {
963 dcl->ops->dpy_gfx_update(dcl, 0, 0,
964 surface_width(new_surface),
965 surface_height(new_surface));
969 static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface)
971 if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) {
972 con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface);
976 static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface)
978 if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) {
979 con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface);
983 static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface,
984 int x, int y, int w, int h)
986 if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) {
987 con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h);
991 static void displaychangelistener_display_console(DisplayChangeListener *dcl,
995 static const char nodev[] =
996 "This VM has no graphic display device.";
997 static DisplaySurface *dummy;
999 if (!con || !console_compatible_with(con, dcl, errp)) {
1001 dummy = qemu_create_placeholder_surface(640, 480, nodev);
1004 dpy_gfx_create_texture(con, dummy);
1006 displaychangelistener_gfx_switch(dcl, dummy, TRUE);
1010 dpy_gfx_create_texture(con, con->surface);
1011 displaychangelistener_gfx_switch(dcl, con->surface,
1012 con->scanout.kind == SCANOUT_SURFACE);
1014 if (con->scanout.kind == SCANOUT_DMABUF &&
1015 displaychangelistener_has_dmabuf(dcl)) {
1016 dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
1017 } else if (con->scanout.kind == SCANOUT_TEXTURE &&
1018 dcl->ops->dpy_gl_scanout_texture) {
1019 dcl->ops->dpy_gl_scanout_texture(dcl,
1020 con->scanout.texture.backing_id,
1021 con->scanout.texture.backing_y_0_top,
1022 con->scanout.texture.backing_width,
1023 con->scanout.texture.backing_height,
1024 con->scanout.texture.x,
1025 con->scanout.texture.y,
1026 con->scanout.texture.width,
1027 con->scanout.texture.height,
1028 con->scanout.texture.d3d_tex2d);
1032 void console_select(unsigned int index)
1034 DisplayChangeListener *dcl;
1037 trace_console_select(index);
1038 s = qemu_console_lookup_by_index(index);
1040 DisplayState *ds = s->ds;
1043 QLIST_FOREACH (dcl, &ds->listeners, next) {
1044 if (dcl->con != NULL) {
1047 displaychangelistener_display_console(dcl, s, NULL);
1049 dpy_text_resize(s, s->width, s->height);
1050 text_console_update_cursor(NULL);
1056 QemuConsole *console;
1058 typedef struct VCChardev VCChardev;
1060 #define TYPE_CHARDEV_VC "chardev-vc"
1061 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
1064 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1066 VCChardev *drv = VC_CHARDEV(chr);
1067 QemuConsole *s = drv->console;
1074 s->update_x0 = s->width * FONT_WIDTH;
1075 s->update_y0 = s->height * FONT_HEIGHT;
1078 console_show_cursor(s, 0);
1079 for(i = 0; i < len; i++) {
1080 console_putchar(s, buf[i]);
1082 console_show_cursor(s, 1);
1083 if (s->update_x0 < s->update_x1) {
1084 dpy_gfx_update(s, s->update_x0, s->update_y0,
1085 s->update_x1 - s->update_x0,
1086 s->update_y1 - s->update_y0);
1091 static void kbd_send_chars(QemuConsole *s)
1093 uint32_t len, avail;
1095 len = qemu_chr_be_can_write(s->chr);
1096 avail = fifo8_num_used(&s->out_fifo);
1097 while (len > 0 && avail > 0) {
1101 buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size);
1102 qemu_chr_be_write(s->chr, buf, size);
1103 len = qemu_chr_be_can_write(s->chr);
1108 /* called when an ascii key is pressed */
1109 void kbd_put_keysym_console(QemuConsole *s, int keysym)
1111 uint8_t buf[16], *q;
1115 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1119 case QEMU_KEY_CTRL_UP:
1120 console_scroll(s, -1);
1122 case QEMU_KEY_CTRL_DOWN:
1123 console_scroll(s, 1);
1125 case QEMU_KEY_CTRL_PAGEUP:
1126 console_scroll(s, -10);
1128 case QEMU_KEY_CTRL_PAGEDOWN:
1129 console_scroll(s, 10);
1132 /* convert the QEMU keysym to VT100 key string */
1134 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1137 c = keysym - 0xe100;
1139 *q++ = '0' + (c / 10);
1140 *q++ = '0' + (c % 10);
1142 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1145 *q++ = keysym & 0xff;
1146 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1147 qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
1153 qemu_chr_write(s->chr, buf, q - buf, true);
1155 num_free = fifo8_num_free(&s->out_fifo);
1156 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
1162 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1163 [Q_KEY_CODE_UP] = QEMU_KEY_UP,
1164 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN,
1165 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT,
1166 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT,
1167 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME,
1168 [Q_KEY_CODE_END] = QEMU_KEY_END,
1169 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP,
1170 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN,
1171 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1172 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB,
1173 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1176 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1177 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP,
1178 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN,
1179 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT,
1180 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT,
1181 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME,
1182 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END,
1183 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP,
1184 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN,
1187 bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
1191 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1195 kbd_put_keysym_console(s, keysym);
1199 void kbd_put_string_console(QemuConsole *s, const char *str, int len)
1203 for (i = 0; i < len && str[i]; i++) {
1204 kbd_put_keysym_console(s, str[i]);
1208 void kbd_put_keysym(int keysym)
1210 kbd_put_keysym_console(active_console, keysym);
1213 static void text_console_invalidate(void *opaque)
1215 QemuConsole *s = (QemuConsole *) opaque;
1217 if (s->console_type == TEXT_CONSOLE) {
1218 text_console_resize(s);
1223 static void text_console_update(void *opaque, console_ch_t *chardata)
1225 QemuConsole *s = (QemuConsole *) opaque;
1228 if (s->text_x[0] <= s->text_x[1]) {
1229 src = (s->y_base + s->text_y[0]) * s->width;
1230 chardata += s->text_y[0] * s->width;
1231 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1232 for (j = 0; j < s->width; j++, src++) {
1233 console_write_ch(chardata ++,
1234 ATTR2CHTYPE(s->cells[src].ch,
1235 s->cells[src].t_attrib.fgcol,
1236 s->cells[src].t_attrib.bgcol,
1237 s->cells[src].t_attrib.bold));
1239 dpy_text_update(s, s->text_x[0], s->text_y[0],
1240 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1241 s->text_x[0] = s->width;
1242 s->text_y[0] = s->height;
1246 if (s->cursor_invalidate) {
1247 dpy_text_cursor(s, s->x, s->y);
1248 s->cursor_invalidate = 0;
1252 static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
1259 obj = object_new(TYPE_QEMU_CONSOLE);
1260 s = QEMU_CONSOLE(obj);
1261 qemu_co_queue_init(&s->dump_queue);
1263 object_property_add_link(obj, "device", TYPE_DEVICE,
1264 (Object **)&s->device,
1265 object_property_allow_set_link,
1266 OBJ_PROP_LINK_STRONG);
1267 object_property_add_uint32_ptr(obj, "head", &s->head,
1268 OBJ_PROP_FLAG_READ);
1270 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1271 (console_type == GRAPHIC_CONSOLE))) {
1275 s->console_type = console_type;
1278 if (QTAILQ_EMPTY(&consoles)) {
1280 QTAILQ_INSERT_TAIL(&consoles, s, next);
1281 } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) {
1282 QemuConsole *last = QTAILQ_LAST(&consoles);
1283 s->index = last->index + 1;
1284 QTAILQ_INSERT_TAIL(&consoles, s, next);
1287 * HACK: Put graphical consoles before text consoles.
1289 * Only do that for coldplugged devices. After initial device
1290 * initialization we will not renumber the consoles any more.
1292 QemuConsole *c = QTAILQ_FIRST(&consoles);
1294 while (QTAILQ_NEXT(c, next) != NULL &&
1295 c->console_type == GRAPHIC_CONSOLE) {
1296 c = QTAILQ_NEXT(c, next);
1298 if (c->console_type == GRAPHIC_CONSOLE) {
1299 /* have no text consoles */
1300 s->index = c->index + 1;
1301 QTAILQ_INSERT_AFTER(&consoles, c, s, next);
1303 s->index = c->index;
1304 QTAILQ_INSERT_BEFORE(c, s, next);
1305 /* renumber text consoles */
1306 for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) {
1315 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
1316 HANDLE h, uint32_t offset)
1318 assert(!surface->handle);
1320 surface->handle = h;
1321 surface->handle_offset = offset;
1325 win32_pixman_image_destroy(pixman_image_t *image, void *data)
1327 DisplaySurface *surface = data;
1329 if (!surface->handle) {
1333 assert(surface->handle_offset == 0);
1335 qemu_win32_map_free(
1336 pixman_image_get_data(surface->image),
1343 DisplaySurface *qemu_create_displaysurface(int width, int height)
1345 DisplaySurface *surface;
1348 HANDLE handle = NULL;
1351 trace_displaysurface_create(width, height);
1354 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
1357 surface = qemu_create_displaysurface_from(
1362 surface->flags = QEMU_ALLOCATED_FLAG;
1365 qemu_displaysurface_win32_set_handle(surface, handle, 0);
1370 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1371 pixman_format_code_t format,
1372 int linesize, uint8_t *data)
1374 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1376 trace_displaysurface_create_from(surface, width, height, format);
1377 surface->format = format;
1378 surface->image = pixman_image_create_bits(surface->format,
1380 (void *)data, linesize);
1381 assert(surface->image != NULL);
1383 pixman_image_set_destroy_function(surface->image,
1384 win32_pixman_image_destroy, surface);
1390 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1392 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1394 trace_displaysurface_create_pixman(surface);
1395 surface->format = pixman_image_get_format(image);
1396 surface->image = pixman_image_ref(image);
1401 DisplaySurface *qemu_create_placeholder_surface(int w, int h,
1404 DisplaySurface *surface = qemu_create_displaysurface(w, h);
1405 pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
1406 pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
1407 pixman_image_t *glyph;
1411 x = (w / FONT_WIDTH - len) / 2;
1412 y = (h / FONT_HEIGHT - 1) / 2;
1413 for (i = 0; i < len; i++) {
1414 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1415 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1416 x+i, y, FONT_WIDTH, FONT_HEIGHT);
1417 qemu_pixman_image_unref(glyph);
1419 surface->flags |= QEMU_PLACEHOLDER_FLAG;
1423 void qemu_free_displaysurface(DisplaySurface *surface)
1425 if (surface == NULL) {
1428 trace_displaysurface_free(surface);
1429 qemu_pixman_image_unref(surface->image);
1433 bool console_has_gl(QemuConsole *con)
1435 return con->gl != NULL;
1438 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
1440 if (dcl->ops->dpy_has_dmabuf) {
1441 return dcl->ops->dpy_has_dmabuf(dcl);
1444 if (dcl->ops->dpy_gl_scanout_dmabuf) {
1451 static bool console_compatible_with(QemuConsole *con,
1452 DisplayChangeListener *dcl, Error **errp)
1456 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
1458 if (console_has_gl(con) &&
1459 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
1460 error_setg(errp, "Display %s is incompatible with the GL context",
1461 dcl->ops->dpy_name);
1465 if (flags & GRAPHIC_FLAGS_GL &&
1466 !console_has_gl(con)) {
1467 error_setg(errp, "The console requires a GL context.");
1472 if (flags & GRAPHIC_FLAGS_DMABUF &&
1473 !displaychangelistener_has_dmabuf(dcl)) {
1474 error_setg(errp, "The console requires display DMABUF support.");
1481 void console_handle_touch_event(QemuConsole *con,
1482 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
1484 int width, int height,
1486 InputMultiTouchType type,
1489 struct touch_slot *slot;
1490 bool needs_sync = false;
1494 if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
1496 "Unexpected touch slot number: % " PRId64" >= %d",
1497 num_slot, INPUT_EVENT_SLOTS_MAX);
1501 slot = &touch_slots[num_slot];
1505 if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
1506 slot->tracking_id = num_slot;
1509 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
1510 if (i == num_slot) {
1513 update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
1516 slot = &touch_slots[i];
1518 if (slot->tracking_id == -1) {
1522 if (update == INPUT_MULTI_TOUCH_TYPE_END) {
1523 slot->tracking_id = -1;
1524 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1527 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1528 qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
1529 qemu_input_queue_mtt_abs(con,
1530 INPUT_AXIS_X, (int) slot->x,
1532 i, slot->tracking_id);
1533 qemu_input_queue_mtt_abs(con,
1534 INPUT_AXIS_Y, (int) slot->y,
1536 i, slot->tracking_id);
1542 qemu_input_event_sync();
1546 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
1548 /* display has opengl support */
1551 error_report("The console already has an OpenGL context.");
1557 void register_displaychangelistener(DisplayChangeListener *dcl)
1563 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1564 dcl->ds = get_alloc_displaystate();
1565 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1566 gui_setup_refresh(dcl->ds);
1571 con = active_console;
1573 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
1574 if (con && con->cursor && dcl->ops->dpy_cursor_define) {
1575 dcl->ops->dpy_cursor_define(dcl, con->cursor);
1577 if (con && dcl->ops->dpy_mouse_set) {
1578 dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
1580 text_console_update_cursor(NULL);
1583 void update_displaychangelistener(DisplayChangeListener *dcl,
1586 DisplayState *ds = dcl->ds;
1588 dcl->update_interval = interval;
1589 if (!ds->refreshing && ds->update_interval > interval) {
1590 timer_mod(ds->gui_timer, ds->last_update + interval);
1594 void unregister_displaychangelistener(DisplayChangeListener *dcl)
1596 DisplayState *ds = dcl->ds;
1597 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1601 QLIST_REMOVE(dcl, next);
1603 gui_setup_refresh(ds);
1606 static void dpy_set_ui_info_timer(void *opaque)
1608 QemuConsole *con = opaque;
1610 con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
1613 bool dpy_ui_info_supported(QemuConsole *con)
1616 con = active_console;
1619 return con->hw_ops->ui_info != NULL;
1622 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
1625 con = active_console;
1628 return &con->ui_info;
1631 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
1634 con = active_console;
1637 if (!dpy_ui_info_supported(con)) {
1640 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1641 /* nothing changed -- ignore */
1646 * Typically we get a flood of these as the user resizes the window.
1647 * Wait until the dust has settled (one second without updates), then
1648 * go notify the guest.
1650 con->ui_info = *info;
1651 timer_mod(con->ui_timer,
1652 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0));
1656 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1658 DisplayState *s = con->ds;
1659 DisplayChangeListener *dcl;
1660 int width = qemu_console_get_width(con, x + w);
1661 int height = qemu_console_get_height(con, y + h);
1667 w = MIN(w, width - x);
1668 h = MIN(h, height - y);
1670 if (!qemu_console_is_visible(con)) {
1673 dpy_gfx_update_texture(con, con->surface, x, y, w, h);
1674 QLIST_FOREACH(dcl, &s->listeners, next) {
1675 if (con != (dcl->con ? dcl->con : active_console)) {
1678 if (dcl->ops->dpy_gfx_update) {
1679 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1684 void dpy_gfx_update_full(QemuConsole *con)
1686 int w = qemu_console_get_width(con, 0);
1687 int h = qemu_console_get_height(con, 0);
1689 dpy_gfx_update(con, 0, 0, w, h);
1692 void dpy_gfx_replace_surface(QemuConsole *con,
1693 DisplaySurface *surface)
1695 static const char placeholder_msg[] = "Display output is not active.";
1696 DisplayState *s = con->ds;
1697 DisplaySurface *old_surface = con->surface;
1698 DisplaySurface *new_surface = surface;
1699 DisplayChangeListener *dcl;
1705 width = surface_width(old_surface);
1706 height = surface_height(old_surface);
1712 new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
1715 assert(old_surface != new_surface);
1717 con->scanout.kind = SCANOUT_SURFACE;
1718 con->surface = new_surface;
1719 dpy_gfx_create_texture(con, new_surface);
1720 QLIST_FOREACH(dcl, &s->listeners, next) {
1721 if (con != (dcl->con ? dcl->con : active_console)) {
1724 displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
1726 dpy_gfx_destroy_texture(con, old_surface);
1727 qemu_free_displaysurface(old_surface);
1730 bool dpy_gfx_check_format(QemuConsole *con,
1731 pixman_format_code_t format)
1733 DisplayChangeListener *dcl;
1734 DisplayState *s = con->ds;
1736 QLIST_FOREACH(dcl, &s->listeners, next) {
1737 if (dcl->con && dcl->con != con) {
1738 /* dcl bound to another console -> skip */
1741 if (dcl->ops->dpy_gfx_check_format) {
1742 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1746 /* default is to allow native 32 bpp only */
1747 if (format != qemu_default_pixman_format(32, true)) {
1755 static void dpy_refresh(DisplayState *s)
1757 DisplayChangeListener *dcl;
1759 QLIST_FOREACH(dcl, &s->listeners, next) {
1760 if (dcl->ops->dpy_refresh) {
1761 dcl->ops->dpy_refresh(dcl);
1766 void dpy_text_cursor(QemuConsole *con, int x, int y)
1768 DisplayState *s = con->ds;
1769 DisplayChangeListener *dcl;
1771 if (!qemu_console_is_visible(con)) {
1774 QLIST_FOREACH(dcl, &s->listeners, next) {
1775 if (con != (dcl->con ? dcl->con : active_console)) {
1778 if (dcl->ops->dpy_text_cursor) {
1779 dcl->ops->dpy_text_cursor(dcl, x, y);
1784 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1786 DisplayState *s = con->ds;
1787 DisplayChangeListener *dcl;
1789 if (!qemu_console_is_visible(con)) {
1792 QLIST_FOREACH(dcl, &s->listeners, next) {
1793 if (con != (dcl->con ? dcl->con : active_console)) {
1796 if (dcl->ops->dpy_text_update) {
1797 dcl->ops->dpy_text_update(dcl, x, y, w, h);
1802 void dpy_text_resize(QemuConsole *con, int w, int h)
1804 DisplayState *s = con->ds;
1805 DisplayChangeListener *dcl;
1807 if (!qemu_console_is_visible(con)) {
1810 QLIST_FOREACH(dcl, &s->listeners, next) {
1811 if (con != (dcl->con ? dcl->con : active_console)) {
1814 if (dcl->ops->dpy_text_resize) {
1815 dcl->ops->dpy_text_resize(dcl, w, h);
1820 void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1822 DisplayState *s = con->ds;
1823 DisplayChangeListener *dcl;
1827 con->cursor_on = on;
1828 if (!qemu_console_is_visible(con)) {
1831 QLIST_FOREACH(dcl, &s->listeners, next) {
1832 if (con != (dcl->con ? dcl->con : active_console)) {
1835 if (dcl->ops->dpy_mouse_set) {
1836 dcl->ops->dpy_mouse_set(dcl, x, y, on);
1841 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1843 DisplayState *s = con->ds;
1844 DisplayChangeListener *dcl;
1846 cursor_unref(con->cursor);
1847 con->cursor = cursor_ref(cursor);
1848 if (!qemu_console_is_visible(con)) {
1851 QLIST_FOREACH(dcl, &s->listeners, next) {
1852 if (con != (dcl->con ? dcl->con : active_console)) {
1855 if (dcl->ops->dpy_cursor_define) {
1856 dcl->ops->dpy_cursor_define(dcl, cursor);
1861 bool dpy_cursor_define_supported(QemuConsole *con)
1863 DisplayState *s = con->ds;
1864 DisplayChangeListener *dcl;
1866 QLIST_FOREACH(dcl, &s->listeners, next) {
1867 if (dcl->ops->dpy_cursor_define) {
1874 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
1875 struct QEMUGLParams *qparams)
1878 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
1881 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
1884 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
1887 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
1890 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
1893 void dpy_gl_scanout_disable(QemuConsole *con)
1895 DisplayState *s = con->ds;
1896 DisplayChangeListener *dcl;
1898 if (con->scanout.kind != SCANOUT_SURFACE) {
1899 con->scanout.kind = SCANOUT_NONE;
1901 QLIST_FOREACH(dcl, &s->listeners, next) {
1902 if (con != (dcl->con ? dcl->con : active_console)) {
1905 if (dcl->ops->dpy_gl_scanout_disable) {
1906 dcl->ops->dpy_gl_scanout_disable(dcl);
1911 void dpy_gl_scanout_texture(QemuConsole *con,
1912 uint32_t backing_id,
1913 bool backing_y_0_top,
1914 uint32_t backing_width,
1915 uint32_t backing_height,
1916 uint32_t x, uint32_t y,
1917 uint32_t width, uint32_t height,
1920 DisplayState *s = con->ds;
1921 DisplayChangeListener *dcl;
1923 con->scanout.kind = SCANOUT_TEXTURE;
1924 con->scanout.texture = (ScanoutTexture) {
1925 backing_id, backing_y_0_top, backing_width, backing_height,
1926 x, y, width, height, d3d_tex2d,
1928 QLIST_FOREACH(dcl, &s->listeners, next) {
1929 if (con != (dcl->con ? dcl->con : active_console)) {
1932 if (dcl->ops->dpy_gl_scanout_texture) {
1933 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
1935 backing_width, backing_height,
1936 x, y, width, height,
1942 void dpy_gl_scanout_dmabuf(QemuConsole *con,
1945 DisplayState *s = con->ds;
1946 DisplayChangeListener *dcl;
1948 con->scanout.kind = SCANOUT_DMABUF;
1949 con->scanout.dmabuf = dmabuf;
1950 QLIST_FOREACH(dcl, &s->listeners, next) {
1951 if (con != (dcl->con ? dcl->con : active_console)) {
1954 if (dcl->ops->dpy_gl_scanout_dmabuf) {
1955 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
1960 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
1961 bool have_hot, uint32_t hot_x, uint32_t hot_y)
1963 DisplayState *s = con->ds;
1964 DisplayChangeListener *dcl;
1966 QLIST_FOREACH(dcl, &s->listeners, next) {
1967 if (con != (dcl->con ? dcl->con : active_console)) {
1970 if (dcl->ops->dpy_gl_cursor_dmabuf) {
1971 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf,
1972 have_hot, hot_x, hot_y);
1977 void dpy_gl_cursor_position(QemuConsole *con,
1978 uint32_t pos_x, uint32_t pos_y)
1980 DisplayState *s = con->ds;
1981 DisplayChangeListener *dcl;
1983 QLIST_FOREACH(dcl, &s->listeners, next) {
1984 if (con != (dcl->con ? dcl->con : active_console)) {
1987 if (dcl->ops->dpy_gl_cursor_position) {
1988 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
1993 void dpy_gl_release_dmabuf(QemuConsole *con,
1996 DisplayState *s = con->ds;
1997 DisplayChangeListener *dcl;
1999 QLIST_FOREACH(dcl, &s->listeners, next) {
2000 if (con != (dcl->con ? dcl->con : active_console)) {
2003 if (dcl->ops->dpy_gl_release_dmabuf) {
2004 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
2009 void dpy_gl_update(QemuConsole *con,
2010 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
2012 DisplayState *s = con->ds;
2013 DisplayChangeListener *dcl;
2017 graphic_hw_gl_block(con, true);
2018 QLIST_FOREACH(dcl, &s->listeners, next) {
2019 if (con != (dcl->con ? dcl->con : active_console)) {
2022 if (dcl->ops->dpy_gl_update) {
2023 dcl->ops->dpy_gl_update(dcl, x, y, w, h);
2026 graphic_hw_gl_block(con, false);
2029 /***********************************************************/
2030 /* register display */
2032 /* console.c internal use only */
2033 static DisplayState *get_alloc_displaystate(void)
2035 if (!display_state) {
2036 display_state = g_new0(DisplayState, 1);
2037 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2038 text_console_update_cursor, NULL);
2040 return display_state;
2044 * Called by main(), after creating QemuConsoles
2045 * and before initializing ui (sdl/vnc/...).
2047 DisplayState *init_displaystate(void)
2052 get_alloc_displaystate();
2053 QTAILQ_FOREACH(con, &consoles, next) {
2054 if (con->console_type != GRAPHIC_CONSOLE &&
2056 text_console_do_init(con->chr, display_state);
2059 /* Hook up into the qom tree here (not in new_console()), once
2060 * all QemuConsoles are created and the order / numbering
2061 * doesn't change any more */
2062 name = g_strdup_printf("console[%d]", con->index);
2063 object_property_add_child(container_get(object_get_root(), "/backend"),
2068 return display_state;
2071 void graphic_console_set_hwops(QemuConsole *con,
2072 const GraphicHwOps *hw_ops,
2075 con->hw_ops = hw_ops;
2079 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
2080 const GraphicHwOps *hw_ops,
2083 static const char noinit[] =
2084 "Guest has not initialized the display (yet).";
2089 DisplaySurface *surface;
2091 ds = get_alloc_displaystate();
2092 s = qemu_console_lookup_unused();
2094 trace_console_gfx_reuse(s->index);
2095 width = qemu_console_get_width(s, 0);
2096 height = qemu_console_get_height(s, 0);
2098 trace_console_gfx_new();
2099 s = new_console(ds, GRAPHIC_CONSOLE, head);
2100 s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2101 dpy_set_ui_info_timer, s);
2103 graphic_console_set_hwops(s, hw_ops, opaque);
2105 object_property_set_link(OBJECT(s), "device", OBJECT(dev),
2109 surface = qemu_create_placeholder_surface(width, height, noinit);
2110 dpy_gfx_replace_surface(s, surface);
2111 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2112 graphic_hw_gl_unblock_timer, s);
2116 static const GraphicHwOps unused_ops = {
2120 void graphic_console_close(QemuConsole *con)
2122 static const char unplugged[] =
2123 "Guest display has been unplugged";
2124 DisplaySurface *surface;
2125 int width = qemu_console_get_width(con, 640);
2126 int height = qemu_console_get_height(con, 480);
2128 trace_console_gfx_close(con->index);
2129 object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
2130 graphic_console_set_hwops(con, &unused_ops, NULL);
2133 dpy_gl_scanout_disable(con);
2135 surface = qemu_create_placeholder_surface(width, height, unplugged);
2136 dpy_gfx_replace_surface(con, surface);
2139 QemuConsole *qemu_console_lookup_by_index(unsigned int index)
2143 QTAILQ_FOREACH(con, &consoles, next) {
2144 if (con->index == index) {
2151 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
2157 QTAILQ_FOREACH(con, &consoles, next) {
2158 obj = object_property_get_link(OBJECT(con),
2159 "device", &error_abort);
2160 if (DEVICE(obj) != dev) {
2163 h = object_property_get_uint(OBJECT(con),
2164 "head", &error_abort);
2173 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
2174 uint32_t head, Error **errp)
2179 dev = qdev_find_recursive(sysbus_get_default(), device_id);
2181 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
2182 "Device '%s' not found", device_id);
2186 con = qemu_console_lookup_by_device(dev, head);
2188 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
2196 QemuConsole *qemu_console_lookup_unused(void)
2201 QTAILQ_FOREACH(con, &consoles, next) {
2202 if (con->hw_ops != &unused_ops) {
2205 obj = object_property_get_link(OBJECT(con),
2206 "device", &error_abort);
2215 QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
2218 con = active_console;
2220 return con ? con->cursor : NULL;
2223 bool qemu_console_is_visible(QemuConsole *con)
2225 return (con == active_console) || (con->dcls > 0);
2228 bool qemu_console_is_graphic(QemuConsole *con)
2231 con = active_console;
2233 return con && (con->console_type == GRAPHIC_CONSOLE);
2236 bool qemu_console_is_fixedsize(QemuConsole *con)
2239 con = active_console;
2241 return con && (con->console_type != TEXT_CONSOLE);
2244 bool qemu_console_is_gl_blocked(QemuConsole *con)
2246 assert(con != NULL);
2247 return con->gl_block;
2250 bool qemu_console_is_multihead(DeviceState *dev)
2254 uint32_t f = 0xffffffff;
2257 QTAILQ_FOREACH(con, &consoles, next) {
2258 obj = object_property_get_link(OBJECT(con),
2259 "device", &error_abort);
2260 if (DEVICE(obj) != dev) {
2264 h = object_property_get_uint(OBJECT(con),
2265 "head", &error_abort);
2266 if (f == 0xffffffff) {
2268 } else if (h != f) {
2275 char *qemu_console_get_label(QemuConsole *con)
2277 if (con->console_type == GRAPHIC_CONSOLE) {
2282 dev = DEVICE(con->device);
2283 multihead = qemu_console_is_multihead(dev);
2285 return g_strdup_printf("%s.%d", dev->id ?
2287 object_get_typename(con->device),
2290 return g_strdup_printf("%s", dev->id ?
2292 object_get_typename(con->device));
2295 return g_strdup("VGA");
2297 if (con->chr && con->chr->label) {
2298 return g_strdup(con->chr->label);
2300 return g_strdup_printf("vc%d", con->index);
2304 int qemu_console_get_index(QemuConsole *con)
2307 con = active_console;
2309 return con ? con->index : -1;
2312 uint32_t qemu_console_get_head(QemuConsole *con)
2315 con = active_console;
2317 return con ? con->head : -1;
2320 int qemu_console_get_width(QemuConsole *con, int fallback)
2323 con = active_console;
2328 switch (con->scanout.kind) {
2329 case SCANOUT_DMABUF:
2330 return con->scanout.dmabuf->width;
2331 case SCANOUT_TEXTURE:
2332 return con->scanout.texture.width;
2333 case SCANOUT_SURFACE:
2334 return surface_width(con->surface);
2340 int qemu_console_get_height(QemuConsole *con, int fallback)
2343 con = active_console;
2348 switch (con->scanout.kind) {
2349 case SCANOUT_DMABUF:
2350 return con->scanout.dmabuf->height;
2351 case SCANOUT_TEXTURE:
2352 return con->scanout.texture.height;
2353 case SCANOUT_SURFACE:
2354 return surface_height(con->surface);
2360 static void vc_chr_accept_input(Chardev *chr)
2362 VCChardev *drv = VC_CHARDEV(chr);
2363 QemuConsole *s = drv->console;
2368 static void vc_chr_set_echo(Chardev *chr, bool echo)
2370 VCChardev *drv = VC_CHARDEV(chr);
2371 QemuConsole *s = drv->console;
2376 static void text_console_update_cursor_timer(void)
2378 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
2379 + CONSOLE_CURSOR_PERIOD / 2);
2382 static void text_console_update_cursor(void *opaque)
2387 cursor_visible_phase = !cursor_visible_phase;
2389 QTAILQ_FOREACH(s, &consoles, next) {
2390 if (qemu_console_is_graphic(s) ||
2391 !qemu_console_is_visible(s)) {
2395 graphic_hw_invalidate(s);
2399 text_console_update_cursor_timer();
2403 static const GraphicHwOps text_console_ops = {
2404 .invalidate = text_console_invalidate,
2405 .text_update = text_console_update,
2408 static void text_console_do_init(Chardev *chr, DisplayState *ds)
2410 VCChardev *drv = VC_CHARDEV(chr);
2411 QemuConsole *s = drv->console;
2412 int g_width = 80 * FONT_WIDTH;
2413 int g_height = 24 * FONT_HEIGHT;
2415 fifo8_create(&s->out_fifo, 16);
2420 s->total_height = DEFAULT_BACKSCROLL;
2423 if (s->scanout.kind != SCANOUT_SURFACE) {
2424 if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) {
2425 g_width = qemu_console_get_width(active_console, g_width);
2426 g_height = qemu_console_get_height(active_console, g_height);
2428 s->surface = qemu_create_displaysurface(g_width, g_height);
2429 s->scanout.kind = SCANOUT_SURFACE;
2432 s->hw_ops = &text_console_ops;
2435 /* Set text attribute defaults */
2436 s->t_attrib_default.bold = 0;
2437 s->t_attrib_default.uline = 0;
2438 s->t_attrib_default.blink = 0;
2439 s->t_attrib_default.invers = 0;
2440 s->t_attrib_default.unvisible = 0;
2441 s->t_attrib_default.fgcol = QEMU_COLOR_WHITE;
2442 s->t_attrib_default.bgcol = QEMU_COLOR_BLACK;
2443 /* set current text attributes to default */
2444 s->t_attrib = s->t_attrib_default;
2445 text_console_resize(s);
2450 s->t_attrib.bgcol = QEMU_COLOR_BLUE;
2451 msg = g_strdup_printf("%s console\r\n", chr->label);
2452 qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
2454 s->t_attrib = s->t_attrib_default;
2457 qemu_chr_be_event(chr, CHR_EVENT_OPENED);
2460 static void vc_chr_open(Chardev *chr,
2461 ChardevBackend *backend,
2465 ChardevVC *vc = backend->u.vc.data;
2466 VCChardev *drv = VC_CHARDEV(chr);
2469 unsigned height = 0;
2471 if (vc->has_width) {
2473 } else if (vc->has_cols) {
2474 width = vc->cols * FONT_WIDTH;
2477 if (vc->has_height) {
2478 height = vc->height;
2479 } else if (vc->has_rows) {
2480 height = vc->rows * FONT_HEIGHT;
2483 trace_console_txt_new(width, height);
2484 if (width == 0 || height == 0) {
2485 s = new_console(NULL, TEXT_CONSOLE, 0);
2487 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
2488 s->scanout.kind = SCANOUT_SURFACE;
2489 s->surface = qemu_create_displaysurface(width, height);
2493 error_setg(errp, "cannot create text console");
2500 if (display_state) {
2501 text_console_do_init(chr, display_state);
2504 /* console/chardev init sometimes completes elsewhere in a 2nd
2505 * stage, so defer OPENED events until they are fully initialized
2510 void qemu_console_resize(QemuConsole *s, int width, int height)
2512 DisplaySurface *surface = qemu_console_surface(s);
2514 assert(s->console_type == GRAPHIC_CONSOLE);
2516 if ((s->scanout.kind != SCANOUT_SURFACE ||
2517 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) &&
2518 qemu_console_get_width(s, -1) == width &&
2519 qemu_console_get_height(s, -1) == height) {
2523 surface = qemu_create_displaysurface(width, height);
2524 dpy_gfx_replace_surface(s, surface);
2527 DisplaySurface *qemu_console_surface(QemuConsole *console)
2529 switch (console->scanout.kind) {
2530 case SCANOUT_SURFACE:
2531 return console->surface;
2537 PixelFormat qemu_default_pixelformat(int bpp)
2539 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2540 PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2544 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2546 void qemu_display_register(QemuDisplay *ui)
2548 assert(ui->type < DISPLAY_TYPE__MAX);
2549 dpys[ui->type] = ui;
2552 bool qemu_display_find_default(DisplayOptions *opts)
2554 static DisplayType prio[] = {
2555 #if defined(CONFIG_GTK)
2558 #if defined(CONFIG_SDL)
2561 #if defined(CONFIG_COCOA)
2567 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
2568 if (dpys[prio[i]] == NULL) {
2569 Error *local_err = NULL;
2570 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
2572 error_report_err(local_err);
2575 if (dpys[prio[i]] == NULL) {
2578 opts->type = prio[i];
2584 void qemu_display_early_init(DisplayOptions *opts)
2586 assert(opts->type < DISPLAY_TYPE__MAX);
2587 if (opts->type == DISPLAY_TYPE_NONE) {
2590 if (dpys[opts->type] == NULL) {
2591 Error *local_err = NULL;
2592 int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
2594 error_report_err(local_err);
2597 if (dpys[opts->type] == NULL) {
2598 error_report("Display '%s' is not available.",
2599 DisplayType_str(opts->type));
2602 if (dpys[opts->type]->early_init) {
2603 dpys[opts->type]->early_init(opts);
2607 void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2609 assert(opts->type < DISPLAY_TYPE__MAX);
2610 if (opts->type == DISPLAY_TYPE_NONE) {
2613 assert(dpys[opts->type] != NULL);
2614 dpys[opts->type]->init(ds, opts);
2617 void qemu_display_help(void)
2621 printf("Available display backend types:\n");
2623 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
2625 Error *local_err = NULL;
2626 int rv = ui_module_load(DisplayType_str(idx), &local_err);
2628 error_report_err(local_err);
2632 printf("%s\n", DisplayType_str(dpys[idx]->type));
2637 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
2642 backend->type = CHARDEV_BACKEND_KIND_VC;
2643 vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2644 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
2646 val = qemu_opt_get_number(opts, "width", 0);
2648 vc->has_width = true;
2652 val = qemu_opt_get_number(opts, "height", 0);
2654 vc->has_height = true;
2658 val = qemu_opt_get_number(opts, "cols", 0);
2660 vc->has_cols = true;
2664 val = qemu_opt_get_number(opts, "rows", 0);
2666 vc->has_rows = true;
2671 static const TypeInfo qemu_console_info = {
2672 .name = TYPE_QEMU_CONSOLE,
2673 .parent = TYPE_OBJECT,
2674 .instance_size = sizeof(QemuConsole),
2675 .class_size = sizeof(QemuConsoleClass),
2678 static void char_vc_class_init(ObjectClass *oc, void *data)
2680 ChardevClass *cc = CHARDEV_CLASS(oc);
2682 cc->parse = qemu_chr_parse_vc;
2683 cc->open = vc_chr_open;
2684 cc->chr_write = vc_chr_write;
2685 cc->chr_accept_input = vc_chr_accept_input;
2686 cc->chr_set_echo = vc_chr_set_echo;
2689 static const TypeInfo char_vc_type_info = {
2690 .name = TYPE_CHARDEV_VC,
2691 .parent = TYPE_CHARDEV,
2692 .instance_size = sizeof(VCChardev),
2693 .class_init = char_vc_class_init,
2696 void qemu_console_early_init(void)
2698 /* set the default vc driver */
2699 if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2700 type_register(&char_vc_type_info);
2704 static void register_types(void)
2706 type_register_static(&qemu_console_info);
2709 type_init(register_types);