ui/console: allocate ui_timer in QemuConsole
[qemu/ar7.git] / ui / console.c
blob8c4a2c83fa89406beea908ad98d606aaabdc89ef
1 /*
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
22 * THE SOFTWARE.
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 "qapi/visitor.h"
31 #include "qemu/coroutine.h"
32 #include "qemu/fifo8.h"
33 #include "qemu/error-report.h"
34 #include "qemu/main-loop.h"
35 #include "qemu/module.h"
36 #include "qemu/option.h"
37 #include "qemu/timer.h"
38 #include "chardev/char.h"
39 #include "trace.h"
40 #include "exec/memory.h"
41 #include "qom/object.h"
43 #define DEFAULT_BACKSCROLL 512
44 #define CONSOLE_CURSOR_PERIOD 500
46 typedef struct TextAttributes {
47 uint8_t fgcol:4;
48 uint8_t bgcol:4;
49 uint8_t bold:1;
50 uint8_t uline:1;
51 uint8_t blink:1;
52 uint8_t invers:1;
53 uint8_t unvisible:1;
54 } TextAttributes;
56 #define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
57 .fgcol = QEMU_COLOR_WHITE, \
58 .bgcol = QEMU_COLOR_BLACK \
61 typedef struct TextCell {
62 uint8_t ch;
63 TextAttributes t_attrib;
64 } TextCell;
66 #define MAX_ESC_PARAMS 3
68 enum TTYState {
69 TTY_STATE_NORM,
70 TTY_STATE_ESC,
71 TTY_STATE_CSI,
74 struct QemuConsole {
75 Object parent;
77 int index;
78 DisplayState *ds;
79 DisplaySurface *surface;
80 DisplayScanout scanout;
81 int dcls;
82 DisplayGLCtx *gl;
83 int gl_block;
84 QEMUTimer *gl_unblock_timer;
85 int window_id;
87 /* Graphic console state. */
88 Object *device;
89 uint32_t head;
90 QemuUIInfo ui_info;
91 QEMUTimer *ui_timer;
92 QEMUCursor *cursor;
93 int cursor_x, cursor_y, cursor_on;
94 const GraphicHwOps *hw_ops;
95 void *hw;
97 /* Text console state */
98 int width;
99 int height;
100 int total_height;
101 int backscroll_height;
102 int x, y;
103 int y_displayed;
104 int y_base;
105 TextCell *cells;
106 int text_x[2], text_y[2], cursor_invalidate;
107 int echo;
109 int update_x0;
110 int update_y0;
111 int update_x1;
112 int update_y1;
114 Chardev *chr;
115 /* fifo for key pressed */
116 Fifo8 out_fifo;
117 CoQueue dump_queue;
119 QTAILQ_ENTRY(QemuConsole) next;
122 OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT)
124 typedef struct QemuGraphicConsole {
125 QemuConsole parent;
126 } QemuGraphicConsole;
128 typedef QemuConsoleClass QemuGraphicConsoleClass;
130 #define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console"
131 OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE)
132 OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE)
134 #define QEMU_IS_GRAPHIC_CONSOLE(c) \
135 object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE)
137 typedef struct QemuTextConsole {
138 QemuConsole parent;
139 } QemuTextConsole;
141 typedef QemuConsoleClass QemuTextConsoleClass;
143 #define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console"
144 OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE)
145 OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE)
147 #define QEMU_IS_TEXT_CONSOLE(c) \
148 object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE)
150 typedef struct QemuFixedTextConsole {
151 QemuTextConsole parent;
152 } QemuFixedTextConsole;
154 typedef QemuTextConsoleClass QemuFixedTextConsoleClass;
156 #define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console"
157 OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE)
158 OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE)
160 #define QEMU_IS_FIXED_TEXT_CONSOLE(c) \
161 object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE)
163 struct VCChardev {
164 Chardev parent;
165 QemuConsole *console;
167 enum TTYState state;
168 int esc_params[MAX_ESC_PARAMS];
169 int nb_esc_params;
170 TextAttributes t_attrib; /* currently active text attributes */
171 int x_saved, y_saved;
173 typedef struct VCChardev VCChardev;
175 struct DisplayState {
176 QEMUTimer *gui_timer;
177 uint64_t last_update;
178 uint64_t update_interval;
179 bool refreshing;
181 QLIST_HEAD(, DisplayChangeListener) listeners;
184 static DisplayState *display_state;
185 static QemuConsole *active_console;
186 static QTAILQ_HEAD(, QemuConsole) consoles =
187 QTAILQ_HEAD_INITIALIZER(consoles);
188 static bool cursor_visible_phase;
189 static QEMUTimer *cursor_timer;
191 static void text_console_do_init(Chardev *chr);
192 static void dpy_refresh(DisplayState *s);
193 static DisplayState *get_alloc_displaystate(void);
194 static void text_console_update_cursor_timer(void);
195 static void text_console_update_cursor(void *opaque);
196 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
197 static bool console_compatible_with(QemuConsole *con,
198 DisplayChangeListener *dcl, Error **errp);
199 static QemuConsole *qemu_graphic_console_lookup_unused(void);
200 static void dpy_set_ui_info_timer(void *opaque);
202 static void gui_update(void *opaque)
204 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
205 uint64_t dcl_interval;
206 DisplayState *ds = opaque;
207 DisplayChangeListener *dcl;
209 ds->refreshing = true;
210 dpy_refresh(ds);
211 ds->refreshing = false;
213 QLIST_FOREACH(dcl, &ds->listeners, next) {
214 dcl_interval = dcl->update_interval ?
215 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
216 if (interval > dcl_interval) {
217 interval = dcl_interval;
220 if (ds->update_interval != interval) {
221 ds->update_interval = interval;
222 trace_console_refresh(interval);
224 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
225 timer_mod(ds->gui_timer, ds->last_update + interval);
228 static void gui_setup_refresh(DisplayState *ds)
230 DisplayChangeListener *dcl;
231 bool need_timer = false;
233 QLIST_FOREACH(dcl, &ds->listeners, next) {
234 if (dcl->ops->dpy_refresh != NULL) {
235 need_timer = true;
239 if (need_timer && ds->gui_timer == NULL) {
240 ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
241 timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
243 if (!need_timer && ds->gui_timer != NULL) {
244 timer_free(ds->gui_timer);
245 ds->gui_timer = NULL;
249 void graphic_hw_update_done(QemuConsole *con)
251 if (con) {
252 qemu_co_enter_all(&con->dump_queue, NULL);
256 void graphic_hw_update(QemuConsole *con)
258 bool async = false;
259 con = con ? con : active_console;
260 if (!con) {
261 return;
263 if (con->hw_ops->gfx_update) {
264 con->hw_ops->gfx_update(con->hw);
265 async = con->hw_ops->gfx_update_async;
267 if (!async) {
268 graphic_hw_update_done(con);
272 static void graphic_hw_update_bh(void *con)
274 graphic_hw_update(con);
277 void qemu_console_co_wait_update(QemuConsole *con)
279 if (qemu_co_queue_empty(&con->dump_queue)) {
280 /* Defer the update, it will restart the pending coroutines */
281 aio_bh_schedule_oneshot(qemu_get_aio_context(),
282 graphic_hw_update_bh, con);
284 qemu_co_queue_wait(&con->dump_queue, NULL);
288 static void graphic_hw_gl_unblock_timer(void *opaque)
290 warn_report("console: no gl-unblock within one second");
293 void graphic_hw_gl_block(QemuConsole *con, bool block)
295 uint64_t timeout;
296 assert(con != NULL);
298 if (block) {
299 con->gl_block++;
300 } else {
301 con->gl_block--;
303 assert(con->gl_block >= 0);
304 if (!con->hw_ops->gl_block) {
305 return;
307 if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {
308 return;
310 con->hw_ops->gl_block(con->hw, block);
312 if (block) {
313 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
314 timeout += 1000; /* one sec */
315 timer_mod(con->gl_unblock_timer, timeout);
316 } else {
317 timer_del(con->gl_unblock_timer);
321 int qemu_console_get_window_id(QemuConsole *con)
323 return con->window_id;
326 void qemu_console_set_window_id(QemuConsole *con, int window_id)
328 con->window_id = window_id;
331 void graphic_hw_invalidate(QemuConsole *con)
333 if (!con) {
334 con = active_console;
336 if (con && con->hw_ops->invalidate) {
337 con->hw_ops->invalidate(con->hw);
341 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
343 if (!con) {
344 con = active_console;
346 if (con && con->hw_ops->text_update) {
347 con->hw_ops->text_update(con->hw, chardata);
351 static void vga_fill_rect(QemuConsole *con,
352 int posx, int posy, int width, int height,
353 pixman_color_t color)
355 DisplaySurface *surface = qemu_console_surface(con);
356 pixman_rectangle16_t rect = {
357 .x = posx, .y = posy, .width = width, .height = height
360 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
361 &color, 1, &rect);
364 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
365 static void vga_bitblt(QemuConsole *con,
366 int xs, int ys, int xd, int yd, int w, int h)
368 DisplaySurface *surface = qemu_console_surface(con);
370 pixman_image_composite(PIXMAN_OP_SRC,
371 surface->image, NULL, surface->image,
372 xs, ys, 0, 0, xd, yd, w, h);
375 /***********************************************************/
376 /* basic char display */
378 #define FONT_HEIGHT 16
379 #define FONT_WIDTH 8
381 #include "vgafont.h"
383 #define QEMU_RGB(r, g, b) \
384 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
386 static const pixman_color_t color_table_rgb[2][8] = {
387 { /* dark */
388 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
389 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
390 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */
391 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
392 [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */
393 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
394 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
395 [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
397 { /* bright */
398 [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
399 [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */
400 [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */
401 [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
402 [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */
403 [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
404 [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
405 [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */
409 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
410 TextAttributes *t_attrib)
412 static pixman_image_t *glyphs[256];
413 DisplaySurface *surface = qemu_console_surface(s);
414 pixman_color_t fgcol, bgcol;
416 if (t_attrib->invers) {
417 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
418 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
419 } else {
420 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
421 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
424 if (!glyphs[ch]) {
425 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
427 qemu_pixman_glyph_render(glyphs[ch], surface->image,
428 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
431 static void text_console_resize(QemuConsole *s)
433 TextCell *cells, *c, *c1;
434 int w1, x, y, last_width;
436 assert(s->scanout.kind == SCANOUT_SURFACE);
438 last_width = s->width;
439 s->width = surface_width(s->surface) / FONT_WIDTH;
440 s->height = surface_height(s->surface) / FONT_HEIGHT;
442 w1 = last_width;
443 if (s->width < w1)
444 w1 = s->width;
446 cells = g_new(TextCell, s->width * s->total_height + 1);
447 for(y = 0; y < s->total_height; y++) {
448 c = &cells[y * s->width];
449 if (w1 > 0) {
450 c1 = &s->cells[y * last_width];
451 for(x = 0; x < w1; x++) {
452 *c++ = *c1++;
455 for(x = w1; x < s->width; x++) {
456 c->ch = ' ';
457 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
458 c++;
461 g_free(s->cells);
462 s->cells = cells;
465 static void invalidate_xy(QemuConsole *s, int x, int y)
467 if (!qemu_console_is_visible(s)) {
468 return;
470 if (s->update_x0 > x * FONT_WIDTH)
471 s->update_x0 = x * FONT_WIDTH;
472 if (s->update_y0 > y * FONT_HEIGHT)
473 s->update_y0 = y * FONT_HEIGHT;
474 if (s->update_x1 < (x + 1) * FONT_WIDTH)
475 s->update_x1 = (x + 1) * FONT_WIDTH;
476 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
477 s->update_y1 = (y + 1) * FONT_HEIGHT;
480 static void vc_update_xy(VCChardev *vc, int x, int y)
482 QemuConsole *s = vc->console;
483 TextCell *c;
484 int y1, y2;
486 s->text_x[0] = MIN(s->text_x[0], x);
487 s->text_x[1] = MAX(s->text_x[1], x);
488 s->text_y[0] = MIN(s->text_y[0], y);
489 s->text_y[1] = MAX(s->text_y[1], y);
491 y1 = (s->y_base + y) % s->total_height;
492 y2 = y1 - s->y_displayed;
493 if (y2 < 0) {
494 y2 += s->total_height;
496 if (y2 < s->height) {
497 if (x >= s->width) {
498 x = s->width - 1;
500 c = &s->cells[y1 * s->width + x];
501 vga_putcharxy(s, x, y2, c->ch,
502 &(c->t_attrib));
503 invalidate_xy(s, x, y2);
507 static void console_show_cursor(QemuConsole *s, int show)
509 TextCell *c;
510 int y, y1;
511 int x = s->x;
513 s->cursor_invalidate = 1;
515 if (x >= s->width) {
516 x = s->width - 1;
518 y1 = (s->y_base + s->y) % s->total_height;
519 y = y1 - s->y_displayed;
520 if (y < 0) {
521 y += s->total_height;
523 if (y < s->height) {
524 c = &s->cells[y1 * s->width + x];
525 if (show && cursor_visible_phase) {
526 TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
527 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
528 vga_putcharxy(s, x, y, c->ch, &t_attrib);
529 } else {
530 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
532 invalidate_xy(s, x, y);
536 static void console_refresh(QemuConsole *s)
538 DisplaySurface *surface = qemu_console_surface(s);
539 TextCell *c;
540 int x, y, y1;
542 s->text_x[0] = 0;
543 s->text_y[0] = 0;
544 s->text_x[1] = s->width - 1;
545 s->text_y[1] = s->height - 1;
546 s->cursor_invalidate = 1;
548 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
549 color_table_rgb[0][QEMU_COLOR_BLACK]);
550 y1 = s->y_displayed;
551 for (y = 0; y < s->height; y++) {
552 c = s->cells + y1 * s->width;
553 for (x = 0; x < s->width; x++) {
554 vga_putcharxy(s, x, y, c->ch,
555 &(c->t_attrib));
556 c++;
558 if (++y1 == s->total_height) {
559 y1 = 0;
562 console_show_cursor(s, 1);
563 dpy_gfx_update(s, 0, 0,
564 surface_width(surface), surface_height(surface));
567 static void console_scroll(QemuConsole *s, int ydelta)
569 int i, y1;
571 if (ydelta > 0) {
572 for(i = 0; i < ydelta; i++) {
573 if (s->y_displayed == s->y_base)
574 break;
575 if (++s->y_displayed == s->total_height)
576 s->y_displayed = 0;
578 } else {
579 ydelta = -ydelta;
580 i = s->backscroll_height;
581 if (i > s->total_height - s->height)
582 i = s->total_height - s->height;
583 y1 = s->y_base - i;
584 if (y1 < 0)
585 y1 += s->total_height;
586 for(i = 0; i < ydelta; i++) {
587 if (s->y_displayed == y1)
588 break;
589 if (--s->y_displayed < 0)
590 s->y_displayed = s->total_height - 1;
593 console_refresh(s);
596 static void vc_put_lf(VCChardev *vc)
598 QemuConsole *s = vc->console;
599 TextCell *c;
600 int x, y1;
602 s->y++;
603 if (s->y >= s->height) {
604 s->y = s->height - 1;
606 if (s->y_displayed == s->y_base) {
607 if (++s->y_displayed == s->total_height)
608 s->y_displayed = 0;
610 if (++s->y_base == s->total_height)
611 s->y_base = 0;
612 if (s->backscroll_height < s->total_height)
613 s->backscroll_height++;
614 y1 = (s->y_base + s->height - 1) % s->total_height;
615 c = &s->cells[y1 * s->width];
616 for(x = 0; x < s->width; x++) {
617 c->ch = ' ';
618 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
619 c++;
621 if (s->y_displayed == s->y_base) {
622 s->text_x[0] = 0;
623 s->text_y[0] = 0;
624 s->text_x[1] = s->width - 1;
625 s->text_y[1] = s->height - 1;
627 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
628 s->width * FONT_WIDTH,
629 (s->height - 1) * FONT_HEIGHT);
630 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
631 s->width * FONT_WIDTH, FONT_HEIGHT,
632 color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
633 s->update_x0 = 0;
634 s->update_y0 = 0;
635 s->update_x1 = s->width * FONT_WIDTH;
636 s->update_y1 = s->height * FONT_HEIGHT;
641 /* Set console attributes depending on the current escape codes.
642 * NOTE: I know this code is not very efficient (checking every color for it
643 * self) but it is more readable and better maintainable.
645 static void vc_handle_escape(VCChardev *vc)
647 int i;
649 for (i = 0; i < vc->nb_esc_params; i++) {
650 switch (vc->esc_params[i]) {
651 case 0: /* reset all console attributes to default */
652 vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
653 break;
654 case 1:
655 vc->t_attrib.bold = 1;
656 break;
657 case 4:
658 vc->t_attrib.uline = 1;
659 break;
660 case 5:
661 vc->t_attrib.blink = 1;
662 break;
663 case 7:
664 vc->t_attrib.invers = 1;
665 break;
666 case 8:
667 vc->t_attrib.unvisible = 1;
668 break;
669 case 22:
670 vc->t_attrib.bold = 0;
671 break;
672 case 24:
673 vc->t_attrib.uline = 0;
674 break;
675 case 25:
676 vc->t_attrib.blink = 0;
677 break;
678 case 27:
679 vc->t_attrib.invers = 0;
680 break;
681 case 28:
682 vc->t_attrib.unvisible = 0;
683 break;
684 /* set foreground color */
685 case 30:
686 vc->t_attrib.fgcol = QEMU_COLOR_BLACK;
687 break;
688 case 31:
689 vc->t_attrib.fgcol = QEMU_COLOR_RED;
690 break;
691 case 32:
692 vc->t_attrib.fgcol = QEMU_COLOR_GREEN;
693 break;
694 case 33:
695 vc->t_attrib.fgcol = QEMU_COLOR_YELLOW;
696 break;
697 case 34:
698 vc->t_attrib.fgcol = QEMU_COLOR_BLUE;
699 break;
700 case 35:
701 vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
702 break;
703 case 36:
704 vc->t_attrib.fgcol = QEMU_COLOR_CYAN;
705 break;
706 case 37:
707 vc->t_attrib.fgcol = QEMU_COLOR_WHITE;
708 break;
709 /* set background color */
710 case 40:
711 vc->t_attrib.bgcol = QEMU_COLOR_BLACK;
712 break;
713 case 41:
714 vc->t_attrib.bgcol = QEMU_COLOR_RED;
715 break;
716 case 42:
717 vc->t_attrib.bgcol = QEMU_COLOR_GREEN;
718 break;
719 case 43:
720 vc->t_attrib.bgcol = QEMU_COLOR_YELLOW;
721 break;
722 case 44:
723 vc->t_attrib.bgcol = QEMU_COLOR_BLUE;
724 break;
725 case 45:
726 vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
727 break;
728 case 46:
729 vc->t_attrib.bgcol = QEMU_COLOR_CYAN;
730 break;
731 case 47:
732 vc->t_attrib.bgcol = QEMU_COLOR_WHITE;
733 break;
738 static void vc_clear_xy(VCChardev *vc, int x, int y)
740 QemuConsole *s = vc->console;
741 int y1 = (s->y_base + y) % s->total_height;
742 if (x >= s->width) {
743 x = s->width - 1;
745 TextCell *c = &s->cells[y1 * s->width + x];
746 c->ch = ' ';
747 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
748 vc_update_xy(vc, x, y);
751 static void vc_put_one(VCChardev *vc, int ch)
753 QemuConsole *s = vc->console;
754 TextCell *c;
755 int y1;
756 if (s->x >= s->width) {
757 /* line wrap */
758 s->x = 0;
759 vc_put_lf(vc);
761 y1 = (s->y_base + s->y) % s->total_height;
762 c = &s->cells[y1 * s->width + s->x];
763 c->ch = ch;
764 c->t_attrib = vc->t_attrib;
765 vc_update_xy(vc, s->x, s->y);
766 s->x++;
769 static void vc_respond_str(VCChardev *vc, const char *buf)
771 while (*buf) {
772 vc_put_one(vc, *buf);
773 buf++;
777 /* set cursor, checking bounds */
778 static void vc_set_cursor(VCChardev *vc, int x, int y)
780 QemuConsole *s = vc->console;
782 if (x < 0) {
783 x = 0;
785 if (y < 0) {
786 y = 0;
788 if (y >= s->height) {
789 y = s->height - 1;
791 if (x >= s->width) {
792 x = s->width - 1;
795 s->x = x;
796 s->y = y;
799 static void vc_putchar(VCChardev *vc, int ch)
801 QemuConsole *s = vc->console;
802 int i;
803 int x, y;
804 char response[40];
806 switch(vc->state) {
807 case TTY_STATE_NORM:
808 switch(ch) {
809 case '\r': /* carriage return */
810 s->x = 0;
811 break;
812 case '\n': /* newline */
813 vc_put_lf(vc);
814 break;
815 case '\b': /* backspace */
816 if (s->x > 0)
817 s->x--;
818 break;
819 case '\t': /* tabspace */
820 if (s->x + (8 - (s->x % 8)) > s->width) {
821 s->x = 0;
822 vc_put_lf(vc);
823 } else {
824 s->x = s->x + (8 - (s->x % 8));
826 break;
827 case '\a': /* alert aka. bell */
828 /* TODO: has to be implemented */
829 break;
830 case 14:
831 /* SI (shift in), character set 0 (ignored) */
832 break;
833 case 15:
834 /* SO (shift out), character set 1 (ignored) */
835 break;
836 case 27: /* esc (introducing an escape sequence) */
837 vc->state = TTY_STATE_ESC;
838 break;
839 default:
840 vc_put_one(vc, ch);
841 break;
843 break;
844 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
845 if (ch == '[') {
846 for(i=0;i<MAX_ESC_PARAMS;i++)
847 vc->esc_params[i] = 0;
848 vc->nb_esc_params = 0;
849 vc->state = TTY_STATE_CSI;
850 } else {
851 vc->state = TTY_STATE_NORM;
853 break;
854 case TTY_STATE_CSI: /* handle escape sequence parameters */
855 if (ch >= '0' && ch <= '9') {
856 if (vc->nb_esc_params < MAX_ESC_PARAMS) {
857 int *param = &vc->esc_params[vc->nb_esc_params];
858 int digit = (ch - '0');
860 *param = (*param <= (INT_MAX - digit) / 10) ?
861 *param * 10 + digit : INT_MAX;
863 } else {
864 if (vc->nb_esc_params < MAX_ESC_PARAMS)
865 vc->nb_esc_params++;
866 if (ch == ';' || ch == '?') {
867 break;
869 trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1],
870 ch, vc->nb_esc_params);
871 vc->state = TTY_STATE_NORM;
872 switch(ch) {
873 case 'A':
874 /* move cursor up */
875 if (vc->esc_params[0] == 0) {
876 vc->esc_params[0] = 1;
878 vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]);
879 break;
880 case 'B':
881 /* move cursor down */
882 if (vc->esc_params[0] == 0) {
883 vc->esc_params[0] = 1;
885 vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]);
886 break;
887 case 'C':
888 /* move cursor right */
889 if (vc->esc_params[0] == 0) {
890 vc->esc_params[0] = 1;
892 vc_set_cursor(vc, s->x + vc->esc_params[0], s->y);
893 break;
894 case 'D':
895 /* move cursor left */
896 if (vc->esc_params[0] == 0) {
897 vc->esc_params[0] = 1;
899 vc_set_cursor(vc, s->x - vc->esc_params[0], s->y);
900 break;
901 case 'G':
902 /* move cursor to column */
903 vc_set_cursor(vc, vc->esc_params[0] - 1, s->y);
904 break;
905 case 'f':
906 case 'H':
907 /* move cursor to row, column */
908 vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1);
909 break;
910 case 'J':
911 switch (vc->esc_params[0]) {
912 case 0:
913 /* clear to end of screen */
914 for (y = s->y; y < s->height; y++) {
915 for (x = 0; x < s->width; x++) {
916 if (y == s->y && x < s->x) {
917 continue;
919 vc_clear_xy(vc, x, y);
922 break;
923 case 1:
924 /* clear from beginning of screen */
925 for (y = 0; y <= s->y; y++) {
926 for (x = 0; x < s->width; x++) {
927 if (y == s->y && x > s->x) {
928 break;
930 vc_clear_xy(vc, x, y);
933 break;
934 case 2:
935 /* clear entire screen */
936 for (y = 0; y <= s->height; y++) {
937 for (x = 0; x < s->width; x++) {
938 vc_clear_xy(vc, x, y);
941 break;
943 break;
944 case 'K':
945 switch (vc->esc_params[0]) {
946 case 0:
947 /* clear to eol */
948 for(x = s->x; x < s->width; x++) {
949 vc_clear_xy(vc, x, s->y);
951 break;
952 case 1:
953 /* clear from beginning of line */
954 for (x = 0; x <= s->x && x < s->width; x++) {
955 vc_clear_xy(vc, x, s->y);
957 break;
958 case 2:
959 /* clear entire line */
960 for(x = 0; x < s->width; x++) {
961 vc_clear_xy(vc, x, s->y);
963 break;
965 break;
966 case 'm':
967 vc_handle_escape(vc);
968 break;
969 case 'n':
970 switch (vc->esc_params[0]) {
971 case 5:
972 /* report console status (always succeed)*/
973 vc_respond_str(vc, "\033[0n");
974 break;
975 case 6:
976 /* report cursor position */
977 sprintf(response, "\033[%d;%dR",
978 (s->y_base + s->y) % s->total_height + 1,
979 s->x + 1);
980 vc_respond_str(vc, response);
981 break;
983 break;
984 case 's':
985 /* save cursor position */
986 vc->x_saved = s->x;
987 vc->y_saved = s->y;
988 break;
989 case 'u':
990 /* restore cursor position */
991 s->x = vc->x_saved;
992 s->y = vc->y_saved;
993 break;
994 default:
995 trace_console_putchar_unhandled(ch);
996 break;
998 break;
1003 static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl,
1004 struct DisplaySurface *new_surface,
1005 bool update)
1007 if (dcl->ops->dpy_gfx_switch) {
1008 dcl->ops->dpy_gfx_switch(dcl, new_surface);
1011 if (update && dcl->ops->dpy_gfx_update) {
1012 dcl->ops->dpy_gfx_update(dcl, 0, 0,
1013 surface_width(new_surface),
1014 surface_height(new_surface));
1018 static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface)
1020 if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) {
1021 con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface);
1025 static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface)
1027 if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) {
1028 con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface);
1032 static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface,
1033 int x, int y, int w, int h)
1035 if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) {
1036 con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h);
1040 static void displaychangelistener_display_console(DisplayChangeListener *dcl,
1041 QemuConsole *con,
1042 Error **errp)
1044 static const char nodev[] =
1045 "This VM has no graphic display device.";
1046 static DisplaySurface *dummy;
1048 if (!con || !console_compatible_with(con, dcl, errp)) {
1049 if (!dummy) {
1050 dummy = qemu_create_placeholder_surface(640, 480, nodev);
1052 if (con) {
1053 dpy_gfx_create_texture(con, dummy);
1055 displaychangelistener_gfx_switch(dcl, dummy, TRUE);
1056 return;
1059 dpy_gfx_create_texture(con, con->surface);
1060 displaychangelistener_gfx_switch(dcl, con->surface,
1061 con->scanout.kind == SCANOUT_SURFACE);
1063 if (con->scanout.kind == SCANOUT_DMABUF &&
1064 displaychangelistener_has_dmabuf(dcl)) {
1065 dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
1066 } else if (con->scanout.kind == SCANOUT_TEXTURE &&
1067 dcl->ops->dpy_gl_scanout_texture) {
1068 dcl->ops->dpy_gl_scanout_texture(dcl,
1069 con->scanout.texture.backing_id,
1070 con->scanout.texture.backing_y_0_top,
1071 con->scanout.texture.backing_width,
1072 con->scanout.texture.backing_height,
1073 con->scanout.texture.x,
1074 con->scanout.texture.y,
1075 con->scanout.texture.width,
1076 con->scanout.texture.height,
1077 con->scanout.texture.d3d_tex2d);
1081 void console_select(unsigned int index)
1083 DisplayChangeListener *dcl;
1084 QemuConsole *s;
1086 trace_console_select(index);
1087 s = qemu_console_lookup_by_index(index);
1088 if (s) {
1089 DisplayState *ds = s->ds;
1091 active_console = s;
1092 QLIST_FOREACH (dcl, &ds->listeners, next) {
1093 if (dcl->con != NULL) {
1094 continue;
1096 displaychangelistener_display_console(dcl, s, NULL);
1098 dpy_text_resize(s, s->width, s->height);
1099 text_console_update_cursor(NULL);
1103 #define TYPE_CHARDEV_VC "chardev-vc"
1104 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
1105 TYPE_CHARDEV_VC)
1107 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1109 VCChardev *drv = VC_CHARDEV(chr);
1110 QemuConsole *s = drv->console;
1111 int i;
1113 s->update_x0 = s->width * FONT_WIDTH;
1114 s->update_y0 = s->height * FONT_HEIGHT;
1115 s->update_x1 = 0;
1116 s->update_y1 = 0;
1117 console_show_cursor(s, 0);
1118 for(i = 0; i < len; i++) {
1119 vc_putchar(drv, buf[i]);
1121 console_show_cursor(s, 1);
1122 if (s->update_x0 < s->update_x1) {
1123 dpy_gfx_update(s, s->update_x0, s->update_y0,
1124 s->update_x1 - s->update_x0,
1125 s->update_y1 - s->update_y0);
1127 return len;
1130 static void kbd_send_chars(QemuConsole *s)
1132 uint32_t len, avail;
1134 len = qemu_chr_be_can_write(s->chr);
1135 avail = fifo8_num_used(&s->out_fifo);
1136 while (len > 0 && avail > 0) {
1137 const uint8_t *buf;
1138 uint32_t size;
1140 buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size);
1141 qemu_chr_be_write(s->chr, buf, size);
1142 len = qemu_chr_be_can_write(s->chr);
1143 avail -= size;
1147 /* called when an ascii key is pressed */
1148 void kbd_put_keysym_console(QemuConsole *s, int keysym)
1150 uint8_t buf[16], *q;
1151 int c;
1152 uint32_t num_free;
1154 if (!s || QEMU_IS_GRAPHIC_CONSOLE(s))
1155 return;
1157 switch(keysym) {
1158 case QEMU_KEY_CTRL_UP:
1159 console_scroll(s, -1);
1160 break;
1161 case QEMU_KEY_CTRL_DOWN:
1162 console_scroll(s, 1);
1163 break;
1164 case QEMU_KEY_CTRL_PAGEUP:
1165 console_scroll(s, -10);
1166 break;
1167 case QEMU_KEY_CTRL_PAGEDOWN:
1168 console_scroll(s, 10);
1169 break;
1170 default:
1171 /* convert the QEMU keysym to VT100 key string */
1172 q = buf;
1173 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1174 *q++ = '\033';
1175 *q++ = '[';
1176 c = keysym - 0xe100;
1177 if (c >= 10)
1178 *q++ = '0' + (c / 10);
1179 *q++ = '0' + (c % 10);
1180 *q++ = '~';
1181 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1182 *q++ = '\033';
1183 *q++ = '[';
1184 *q++ = keysym & 0xff;
1185 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1186 qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
1187 *q++ = '\n';
1188 } else {
1189 *q++ = keysym;
1191 if (s->echo) {
1192 qemu_chr_write(s->chr, buf, q - buf, true);
1194 num_free = fifo8_num_free(&s->out_fifo);
1195 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
1196 kbd_send_chars(s);
1197 break;
1201 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1202 [Q_KEY_CODE_UP] = QEMU_KEY_UP,
1203 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN,
1204 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT,
1205 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT,
1206 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME,
1207 [Q_KEY_CODE_END] = QEMU_KEY_END,
1208 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP,
1209 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN,
1210 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1211 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB,
1212 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1215 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1216 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP,
1217 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN,
1218 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT,
1219 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT,
1220 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME,
1221 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END,
1222 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP,
1223 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN,
1226 bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
1228 int keysym;
1230 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1231 if (keysym == 0) {
1232 return false;
1234 kbd_put_keysym_console(s, keysym);
1235 return true;
1238 void kbd_put_string_console(QemuConsole *s, const char *str, int len)
1240 int i;
1242 for (i = 0; i < len && str[i]; i++) {
1243 kbd_put_keysym_console(s, str[i]);
1247 void kbd_put_keysym(int keysym)
1249 kbd_put_keysym_console(active_console, keysym);
1252 static void text_console_invalidate(void *opaque)
1254 QemuConsole *s = (QemuConsole *) opaque;
1256 if (QEMU_IS_TEXT_CONSOLE(s) && !QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
1257 text_console_resize(s);
1259 console_refresh(s);
1262 static void text_console_update(void *opaque, console_ch_t *chardata)
1264 QemuConsole *s = (QemuConsole *) opaque;
1265 int i, j, src;
1267 if (s->text_x[0] <= s->text_x[1]) {
1268 src = (s->y_base + s->text_y[0]) * s->width;
1269 chardata += s->text_y[0] * s->width;
1270 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1271 for (j = 0; j < s->width; j++, src++) {
1272 console_write_ch(chardata ++,
1273 ATTR2CHTYPE(s->cells[src].ch,
1274 s->cells[src].t_attrib.fgcol,
1275 s->cells[src].t_attrib.bgcol,
1276 s->cells[src].t_attrib.bold));
1278 dpy_text_update(s, s->text_x[0], s->text_y[0],
1279 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1280 s->text_x[0] = s->width;
1281 s->text_y[0] = s->height;
1282 s->text_x[1] = 0;
1283 s->text_y[1] = 0;
1285 if (s->cursor_invalidate) {
1286 dpy_text_cursor(s, s->x, s->y);
1287 s->cursor_invalidate = 0;
1291 static void
1292 qemu_console_register(QemuConsole *c)
1294 int i;
1296 if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) &&
1297 QEMU_IS_GRAPHIC_CONSOLE(c))) {
1298 active_console = c;
1301 if (QTAILQ_EMPTY(&consoles)) {
1302 c->index = 0;
1303 QTAILQ_INSERT_TAIL(&consoles, c, next);
1304 } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) {
1305 QemuConsole *last = QTAILQ_LAST(&consoles);
1306 c->index = last->index + 1;
1307 QTAILQ_INSERT_TAIL(&consoles, c, next);
1308 } else {
1310 * HACK: Put graphical consoles before text consoles.
1312 * Only do that for coldplugged devices. After initial device
1313 * initialization we will not renumber the consoles any more.
1315 QemuConsole *it = QTAILQ_FIRST(&consoles);
1317 while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) {
1318 it = QTAILQ_NEXT(it, next);
1320 if (QEMU_IS_GRAPHIC_CONSOLE(it)) {
1321 /* have no text consoles */
1322 c->index = it->index + 1;
1323 QTAILQ_INSERT_AFTER(&consoles, it, c, next);
1324 } else {
1325 c->index = it->index;
1326 QTAILQ_INSERT_BEFORE(it, c, next);
1327 /* renumber text consoles */
1328 for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) {
1329 it->index = i;
1335 static void
1336 qemu_console_finalize(Object *obj)
1338 QemuConsole *c = QEMU_CONSOLE(obj);
1340 g_clear_pointer(&c->ui_timer, timer_free);
1341 /* TODO: should unregister from consoles and free itself */
1344 static void
1345 qemu_console_prop_get_head(Object *obj, Visitor *v, const char *name,
1346 void *opaque, Error **errp)
1348 QemuConsole *c = QEMU_CONSOLE(obj);
1350 visit_type_uint32(v, name, &c->head, errp);
1353 static void
1354 qemu_console_class_init(ObjectClass *oc, void *data)
1356 object_class_property_add_link(oc, "device", TYPE_DEVICE,
1357 offsetof(QemuConsole, device),
1358 object_property_allow_set_link,
1359 OBJ_PROP_LINK_STRONG);
1360 object_class_property_add(oc, "head", "uint32",
1361 qemu_console_prop_get_head,
1362 NULL, NULL, NULL);
1365 static void
1366 qemu_console_init(Object *obj)
1368 QemuConsole *c = QEMU_CONSOLE(obj);
1369 DisplayState *ds = get_alloc_displaystate();
1371 qemu_co_queue_init(&c->dump_queue);
1372 c->ds = ds;
1373 c->window_id = -1;
1374 c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1375 dpy_set_ui_info_timer, c);
1376 qemu_console_register(c);
1379 static void
1380 qemu_graphic_console_finalize(Object *obj)
1384 static void
1385 qemu_graphic_console_class_init(ObjectClass *oc, void *data)
1389 static void
1390 qemu_graphic_console_init(Object *obj)
1394 static void
1395 qemu_text_console_finalize(Object *obj)
1399 static void
1400 qemu_text_console_class_init(ObjectClass *oc, void *data)
1404 static void
1405 qemu_text_console_init(Object *obj)
1409 static void
1410 qemu_fixed_text_console_finalize(Object *obj)
1414 static void
1415 qemu_fixed_text_console_class_init(ObjectClass *oc, void *data)
1419 static void
1420 qemu_fixed_text_console_init(Object *obj)
1424 #ifdef WIN32
1425 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
1426 HANDLE h, uint32_t offset)
1428 assert(!surface->handle);
1430 surface->handle = h;
1431 surface->handle_offset = offset;
1434 static void
1435 win32_pixman_image_destroy(pixman_image_t *image, void *data)
1437 DisplaySurface *surface = data;
1439 if (!surface->handle) {
1440 return;
1443 assert(surface->handle_offset == 0);
1445 qemu_win32_map_free(
1446 pixman_image_get_data(surface->image),
1447 surface->handle,
1448 &error_warn
1451 #endif
1453 DisplaySurface *qemu_create_displaysurface(int width, int height)
1455 DisplaySurface *surface;
1456 void *bits = NULL;
1457 #ifdef WIN32
1458 HANDLE handle = NULL;
1459 #endif
1461 trace_displaysurface_create(width, height);
1463 #ifdef WIN32
1464 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
1465 #endif
1467 surface = qemu_create_displaysurface_from(
1468 width, height,
1469 PIXMAN_x8r8g8b8,
1470 width * 4, bits
1472 surface->flags = QEMU_ALLOCATED_FLAG;
1474 #ifdef WIN32
1475 qemu_displaysurface_win32_set_handle(surface, handle, 0);
1476 #endif
1477 return surface;
1480 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1481 pixman_format_code_t format,
1482 int linesize, uint8_t *data)
1484 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1486 trace_displaysurface_create_from(surface, width, height, format);
1487 surface->format = format;
1488 surface->image = pixman_image_create_bits(surface->format,
1489 width, height,
1490 (void *)data, linesize);
1491 assert(surface->image != NULL);
1492 #ifdef WIN32
1493 pixman_image_set_destroy_function(surface->image,
1494 win32_pixman_image_destroy, surface);
1495 #endif
1497 return surface;
1500 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1502 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1504 trace_displaysurface_create_pixman(surface);
1505 surface->format = pixman_image_get_format(image);
1506 surface->image = pixman_image_ref(image);
1508 return surface;
1511 DisplaySurface *qemu_create_placeholder_surface(int w, int h,
1512 const char *msg)
1514 DisplaySurface *surface = qemu_create_displaysurface(w, h);
1515 pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
1516 pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
1517 pixman_image_t *glyph;
1518 int len, x, y, i;
1520 len = strlen(msg);
1521 x = (w / FONT_WIDTH - len) / 2;
1522 y = (h / FONT_HEIGHT - 1) / 2;
1523 for (i = 0; i < len; i++) {
1524 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1525 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1526 x+i, y, FONT_WIDTH, FONT_HEIGHT);
1527 qemu_pixman_image_unref(glyph);
1529 surface->flags |= QEMU_PLACEHOLDER_FLAG;
1530 return surface;
1533 void qemu_free_displaysurface(DisplaySurface *surface)
1535 if (surface == NULL) {
1536 return;
1538 trace_displaysurface_free(surface);
1539 qemu_pixman_image_unref(surface->image);
1540 g_free(surface);
1543 bool console_has_gl(QemuConsole *con)
1545 return con->gl != NULL;
1548 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
1550 if (dcl->ops->dpy_has_dmabuf) {
1551 return dcl->ops->dpy_has_dmabuf(dcl);
1554 if (dcl->ops->dpy_gl_scanout_dmabuf) {
1555 return true;
1558 return false;
1561 static bool console_compatible_with(QemuConsole *con,
1562 DisplayChangeListener *dcl, Error **errp)
1564 int flags;
1566 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
1568 if (console_has_gl(con) &&
1569 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
1570 error_setg(errp, "Display %s is incompatible with the GL context",
1571 dcl->ops->dpy_name);
1572 return false;
1575 if (flags & GRAPHIC_FLAGS_GL &&
1576 !console_has_gl(con)) {
1577 error_setg(errp, "The console requires a GL context.");
1578 return false;
1582 if (flags & GRAPHIC_FLAGS_DMABUF &&
1583 !displaychangelistener_has_dmabuf(dcl)) {
1584 error_setg(errp, "The console requires display DMABUF support.");
1585 return false;
1588 return true;
1591 void console_handle_touch_event(QemuConsole *con,
1592 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
1593 uint64_t num_slot,
1594 int width, int height,
1595 double x, double y,
1596 InputMultiTouchType type,
1597 Error **errp)
1599 struct touch_slot *slot;
1600 bool needs_sync = false;
1601 int update;
1602 int i;
1604 if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
1605 error_setg(errp,
1606 "Unexpected touch slot number: % " PRId64" >= %d",
1607 num_slot, INPUT_EVENT_SLOTS_MAX);
1608 return;
1611 slot = &touch_slots[num_slot];
1612 slot->x = x;
1613 slot->y = y;
1615 if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
1616 slot->tracking_id = num_slot;
1619 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
1620 if (i == num_slot) {
1621 update = type;
1622 } else {
1623 update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
1626 slot = &touch_slots[i];
1628 if (slot->tracking_id == -1) {
1629 continue;
1632 if (update == INPUT_MULTI_TOUCH_TYPE_END) {
1633 slot->tracking_id = -1;
1634 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1635 needs_sync = true;
1636 } else {
1637 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1638 qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
1639 qemu_input_queue_mtt_abs(con,
1640 INPUT_AXIS_X, (int) slot->x,
1641 0, width,
1642 i, slot->tracking_id);
1643 qemu_input_queue_mtt_abs(con,
1644 INPUT_AXIS_Y, (int) slot->y,
1645 0, height,
1646 i, slot->tracking_id);
1647 needs_sync = true;
1651 if (needs_sync) {
1652 qemu_input_event_sync();
1656 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
1658 /* display has opengl support */
1659 assert(con);
1660 if (con->gl) {
1661 error_report("The console already has an OpenGL context.");
1662 exit(1);
1664 con->gl = gl;
1667 void register_displaychangelistener(DisplayChangeListener *dcl)
1669 QemuConsole *con;
1671 assert(!dcl->ds);
1673 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1674 dcl->ds = get_alloc_displaystate();
1675 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1676 gui_setup_refresh(dcl->ds);
1677 if (dcl->con) {
1678 dcl->con->dcls++;
1679 con = dcl->con;
1680 } else {
1681 con = active_console;
1683 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
1684 if (con && con->cursor && dcl->ops->dpy_cursor_define) {
1685 dcl->ops->dpy_cursor_define(dcl, con->cursor);
1687 if (con && dcl->ops->dpy_mouse_set) {
1688 dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
1690 text_console_update_cursor(NULL);
1693 void update_displaychangelistener(DisplayChangeListener *dcl,
1694 uint64_t interval)
1696 DisplayState *ds = dcl->ds;
1698 dcl->update_interval = interval;
1699 if (!ds->refreshing && ds->update_interval > interval) {
1700 timer_mod(ds->gui_timer, ds->last_update + interval);
1704 void unregister_displaychangelistener(DisplayChangeListener *dcl)
1706 DisplayState *ds = dcl->ds;
1707 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1708 if (dcl->con) {
1709 dcl->con->dcls--;
1711 QLIST_REMOVE(dcl, next);
1712 dcl->ds = NULL;
1713 gui_setup_refresh(ds);
1716 static void dpy_set_ui_info_timer(void *opaque)
1718 QemuConsole *con = opaque;
1720 con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
1723 bool dpy_ui_info_supported(QemuConsole *con)
1725 if (con == NULL) {
1726 con = active_console;
1729 return con->hw_ops->ui_info != NULL;
1732 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
1734 if (con == NULL) {
1735 con = active_console;
1738 return &con->ui_info;
1741 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
1743 if (con == NULL) {
1744 con = active_console;
1747 if (!dpy_ui_info_supported(con)) {
1748 return -1;
1750 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1751 /* nothing changed -- ignore */
1752 return 0;
1756 * Typically we get a flood of these as the user resizes the window.
1757 * Wait until the dust has settled (one second without updates), then
1758 * go notify the guest.
1760 con->ui_info = *info;
1761 timer_mod(con->ui_timer,
1762 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0));
1763 return 0;
1766 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1768 DisplayState *s = con->ds;
1769 DisplayChangeListener *dcl;
1770 int width = qemu_console_get_width(con, x + w);
1771 int height = qemu_console_get_height(con, y + h);
1773 x = MAX(x, 0);
1774 y = MAX(y, 0);
1775 x = MIN(x, width);
1776 y = MIN(y, height);
1777 w = MIN(w, width - x);
1778 h = MIN(h, height - y);
1780 if (!qemu_console_is_visible(con)) {
1781 return;
1783 dpy_gfx_update_texture(con, con->surface, x, y, w, h);
1784 QLIST_FOREACH(dcl, &s->listeners, next) {
1785 if (con != (dcl->con ? dcl->con : active_console)) {
1786 continue;
1788 if (dcl->ops->dpy_gfx_update) {
1789 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1794 void dpy_gfx_update_full(QemuConsole *con)
1796 int w = qemu_console_get_width(con, 0);
1797 int h = qemu_console_get_height(con, 0);
1799 dpy_gfx_update(con, 0, 0, w, h);
1802 void dpy_gfx_replace_surface(QemuConsole *con,
1803 DisplaySurface *surface)
1805 static const char placeholder_msg[] = "Display output is not active.";
1806 DisplayState *s = con->ds;
1807 DisplaySurface *old_surface = con->surface;
1808 DisplaySurface *new_surface = surface;
1809 DisplayChangeListener *dcl;
1810 int width;
1811 int height;
1813 if (!surface) {
1814 if (old_surface) {
1815 width = surface_width(old_surface);
1816 height = surface_height(old_surface);
1817 } else {
1818 width = 640;
1819 height = 480;
1822 new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
1825 assert(old_surface != new_surface);
1827 con->scanout.kind = SCANOUT_SURFACE;
1828 con->surface = new_surface;
1829 dpy_gfx_create_texture(con, new_surface);
1830 QLIST_FOREACH(dcl, &s->listeners, next) {
1831 if (con != (dcl->con ? dcl->con : active_console)) {
1832 continue;
1834 displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
1836 dpy_gfx_destroy_texture(con, old_surface);
1837 qemu_free_displaysurface(old_surface);
1840 bool dpy_gfx_check_format(QemuConsole *con,
1841 pixman_format_code_t format)
1843 DisplayChangeListener *dcl;
1844 DisplayState *s = con->ds;
1846 QLIST_FOREACH(dcl, &s->listeners, next) {
1847 if (dcl->con && dcl->con != con) {
1848 /* dcl bound to another console -> skip */
1849 continue;
1851 if (dcl->ops->dpy_gfx_check_format) {
1852 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1853 return false;
1855 } else {
1856 /* default is to allow native 32 bpp only */
1857 if (format != qemu_default_pixman_format(32, true)) {
1858 return false;
1862 return true;
1865 static void dpy_refresh(DisplayState *s)
1867 DisplayChangeListener *dcl;
1869 QLIST_FOREACH(dcl, &s->listeners, next) {
1870 if (dcl->ops->dpy_refresh) {
1871 dcl->ops->dpy_refresh(dcl);
1876 void dpy_text_cursor(QemuConsole *con, int x, int y)
1878 DisplayState *s = con->ds;
1879 DisplayChangeListener *dcl;
1881 if (!qemu_console_is_visible(con)) {
1882 return;
1884 QLIST_FOREACH(dcl, &s->listeners, next) {
1885 if (con != (dcl->con ? dcl->con : active_console)) {
1886 continue;
1888 if (dcl->ops->dpy_text_cursor) {
1889 dcl->ops->dpy_text_cursor(dcl, x, y);
1894 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1896 DisplayState *s = con->ds;
1897 DisplayChangeListener *dcl;
1899 if (!qemu_console_is_visible(con)) {
1900 return;
1902 QLIST_FOREACH(dcl, &s->listeners, next) {
1903 if (con != (dcl->con ? dcl->con : active_console)) {
1904 continue;
1906 if (dcl->ops->dpy_text_update) {
1907 dcl->ops->dpy_text_update(dcl, x, y, w, h);
1912 void dpy_text_resize(QemuConsole *con, int w, int h)
1914 DisplayState *s = con->ds;
1915 DisplayChangeListener *dcl;
1917 if (!qemu_console_is_visible(con)) {
1918 return;
1920 QLIST_FOREACH(dcl, &s->listeners, next) {
1921 if (con != (dcl->con ? dcl->con : active_console)) {
1922 continue;
1924 if (dcl->ops->dpy_text_resize) {
1925 dcl->ops->dpy_text_resize(dcl, w, h);
1930 void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1932 DisplayState *s = con->ds;
1933 DisplayChangeListener *dcl;
1935 con->cursor_x = x;
1936 con->cursor_y = y;
1937 con->cursor_on = on;
1938 if (!qemu_console_is_visible(con)) {
1939 return;
1941 QLIST_FOREACH(dcl, &s->listeners, next) {
1942 if (con != (dcl->con ? dcl->con : active_console)) {
1943 continue;
1945 if (dcl->ops->dpy_mouse_set) {
1946 dcl->ops->dpy_mouse_set(dcl, x, y, on);
1951 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1953 DisplayState *s = con->ds;
1954 DisplayChangeListener *dcl;
1956 cursor_unref(con->cursor);
1957 con->cursor = cursor_ref(cursor);
1958 if (!qemu_console_is_visible(con)) {
1959 return;
1961 QLIST_FOREACH(dcl, &s->listeners, next) {
1962 if (con != (dcl->con ? dcl->con : active_console)) {
1963 continue;
1965 if (dcl->ops->dpy_cursor_define) {
1966 dcl->ops->dpy_cursor_define(dcl, cursor);
1971 bool dpy_cursor_define_supported(QemuConsole *con)
1973 DisplayState *s = con->ds;
1974 DisplayChangeListener *dcl;
1976 QLIST_FOREACH(dcl, &s->listeners, next) {
1977 if (dcl->ops->dpy_cursor_define) {
1978 return true;
1981 return false;
1984 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
1985 struct QEMUGLParams *qparams)
1987 assert(con->gl);
1988 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
1991 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
1993 assert(con->gl);
1994 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
1997 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
1999 assert(con->gl);
2000 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
2003 void dpy_gl_scanout_disable(QemuConsole *con)
2005 DisplayState *s = con->ds;
2006 DisplayChangeListener *dcl;
2008 if (con->scanout.kind != SCANOUT_SURFACE) {
2009 con->scanout.kind = SCANOUT_NONE;
2011 QLIST_FOREACH(dcl, &s->listeners, next) {
2012 if (con != (dcl->con ? dcl->con : active_console)) {
2013 continue;
2015 if (dcl->ops->dpy_gl_scanout_disable) {
2016 dcl->ops->dpy_gl_scanout_disable(dcl);
2021 void dpy_gl_scanout_texture(QemuConsole *con,
2022 uint32_t backing_id,
2023 bool backing_y_0_top,
2024 uint32_t backing_width,
2025 uint32_t backing_height,
2026 uint32_t x, uint32_t y,
2027 uint32_t width, uint32_t height,
2028 void *d3d_tex2d)
2030 DisplayState *s = con->ds;
2031 DisplayChangeListener *dcl;
2033 con->scanout.kind = SCANOUT_TEXTURE;
2034 con->scanout.texture = (ScanoutTexture) {
2035 backing_id, backing_y_0_top, backing_width, backing_height,
2036 x, y, width, height, d3d_tex2d,
2038 QLIST_FOREACH(dcl, &s->listeners, next) {
2039 if (con != (dcl->con ? dcl->con : active_console)) {
2040 continue;
2042 if (dcl->ops->dpy_gl_scanout_texture) {
2043 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
2044 backing_y_0_top,
2045 backing_width, backing_height,
2046 x, y, width, height,
2047 d3d_tex2d);
2052 void dpy_gl_scanout_dmabuf(QemuConsole *con,
2053 QemuDmaBuf *dmabuf)
2055 DisplayState *s = con->ds;
2056 DisplayChangeListener *dcl;
2058 con->scanout.kind = SCANOUT_DMABUF;
2059 con->scanout.dmabuf = dmabuf;
2060 QLIST_FOREACH(dcl, &s->listeners, next) {
2061 if (con != (dcl->con ? dcl->con : active_console)) {
2062 continue;
2064 if (dcl->ops->dpy_gl_scanout_dmabuf) {
2065 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
2070 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
2071 bool have_hot, uint32_t hot_x, uint32_t hot_y)
2073 DisplayState *s = con->ds;
2074 DisplayChangeListener *dcl;
2076 QLIST_FOREACH(dcl, &s->listeners, next) {
2077 if (con != (dcl->con ? dcl->con : active_console)) {
2078 continue;
2080 if (dcl->ops->dpy_gl_cursor_dmabuf) {
2081 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf,
2082 have_hot, hot_x, hot_y);
2087 void dpy_gl_cursor_position(QemuConsole *con,
2088 uint32_t pos_x, uint32_t pos_y)
2090 DisplayState *s = con->ds;
2091 DisplayChangeListener *dcl;
2093 QLIST_FOREACH(dcl, &s->listeners, next) {
2094 if (con != (dcl->con ? dcl->con : active_console)) {
2095 continue;
2097 if (dcl->ops->dpy_gl_cursor_position) {
2098 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
2103 void dpy_gl_release_dmabuf(QemuConsole *con,
2104 QemuDmaBuf *dmabuf)
2106 DisplayState *s = con->ds;
2107 DisplayChangeListener *dcl;
2109 QLIST_FOREACH(dcl, &s->listeners, next) {
2110 if (con != (dcl->con ? dcl->con : active_console)) {
2111 continue;
2113 if (dcl->ops->dpy_gl_release_dmabuf) {
2114 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
2119 void dpy_gl_update(QemuConsole *con,
2120 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
2122 DisplayState *s = con->ds;
2123 DisplayChangeListener *dcl;
2125 assert(con->gl);
2127 graphic_hw_gl_block(con, true);
2128 QLIST_FOREACH(dcl, &s->listeners, next) {
2129 if (con != (dcl->con ? dcl->con : active_console)) {
2130 continue;
2132 if (dcl->ops->dpy_gl_update) {
2133 dcl->ops->dpy_gl_update(dcl, x, y, w, h);
2136 graphic_hw_gl_block(con, false);
2139 /***********************************************************/
2140 /* register display */
2142 /* console.c internal use only */
2143 static DisplayState *get_alloc_displaystate(void)
2145 if (!display_state) {
2146 display_state = g_new0(DisplayState, 1);
2147 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2148 text_console_update_cursor, NULL);
2150 return display_state;
2154 * Called by main(), after creating QemuConsoles
2155 * and before initializing ui (sdl/vnc/...).
2157 DisplayState *init_displaystate(void)
2159 gchar *name;
2160 QemuConsole *con;
2162 QTAILQ_FOREACH(con, &consoles, next) {
2163 /* Hook up into the qom tree here (not in object_new()), once
2164 * all QemuConsoles are created and the order / numbering
2165 * doesn't change any more */
2166 name = g_strdup_printf("console[%d]", con->index);
2167 object_property_add_child(container_get(object_get_root(), "/backend"),
2168 name, OBJECT(con));
2169 g_free(name);
2172 return display_state;
2175 void graphic_console_set_hwops(QemuConsole *con,
2176 const GraphicHwOps *hw_ops,
2177 void *opaque)
2179 con->hw_ops = hw_ops;
2180 con->hw = opaque;
2183 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
2184 const GraphicHwOps *hw_ops,
2185 void *opaque)
2187 static const char noinit[] =
2188 "Guest has not initialized the display (yet).";
2189 int width = 640;
2190 int height = 480;
2191 QemuConsole *s;
2192 DisplaySurface *surface;
2194 s = qemu_graphic_console_lookup_unused();
2195 if (s) {
2196 trace_console_gfx_reuse(s->index);
2197 width = qemu_console_get_width(s, 0);
2198 height = qemu_console_get_height(s, 0);
2199 } else {
2200 trace_console_gfx_new();
2201 s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE);
2203 s->head = head;
2204 graphic_console_set_hwops(s, hw_ops, opaque);
2205 if (dev) {
2206 object_property_set_link(OBJECT(s), "device", OBJECT(dev),
2207 &error_abort);
2210 surface = qemu_create_placeholder_surface(width, height, noinit);
2211 dpy_gfx_replace_surface(s, surface);
2212 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2213 graphic_hw_gl_unblock_timer, s);
2214 return s;
2217 static const GraphicHwOps unused_ops = {
2218 /* no callbacks */
2221 void graphic_console_close(QemuConsole *con)
2223 static const char unplugged[] =
2224 "Guest display has been unplugged";
2225 DisplaySurface *surface;
2226 int width = qemu_console_get_width(con, 640);
2227 int height = qemu_console_get_height(con, 480);
2229 trace_console_gfx_close(con->index);
2230 object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
2231 graphic_console_set_hwops(con, &unused_ops, NULL);
2233 if (con->gl) {
2234 dpy_gl_scanout_disable(con);
2236 surface = qemu_create_placeholder_surface(width, height, unplugged);
2237 dpy_gfx_replace_surface(con, surface);
2240 QemuConsole *qemu_console_lookup_by_index(unsigned int index)
2242 QemuConsole *con;
2244 QTAILQ_FOREACH(con, &consoles, next) {
2245 if (con->index == index) {
2246 return con;
2249 return NULL;
2252 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
2254 QemuConsole *con;
2255 Object *obj;
2256 uint32_t h;
2258 QTAILQ_FOREACH(con, &consoles, next) {
2259 obj = object_property_get_link(OBJECT(con),
2260 "device", &error_abort);
2261 if (DEVICE(obj) != dev) {
2262 continue;
2264 h = object_property_get_uint(OBJECT(con),
2265 "head", &error_abort);
2266 if (h != head) {
2267 continue;
2269 return con;
2271 return NULL;
2274 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
2275 uint32_t head, Error **errp)
2277 DeviceState *dev;
2278 QemuConsole *con;
2280 dev = qdev_find_recursive(sysbus_get_default(), device_id);
2281 if (dev == NULL) {
2282 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
2283 "Device '%s' not found", device_id);
2284 return NULL;
2287 con = qemu_console_lookup_by_device(dev, head);
2288 if (con == NULL) {
2289 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
2290 device_id, head);
2291 return NULL;
2294 return con;
2297 static QemuConsole *qemu_graphic_console_lookup_unused(void)
2299 QemuConsole *con;
2300 Object *obj;
2302 QTAILQ_FOREACH(con, &consoles, next) {
2303 if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) {
2304 continue;
2306 obj = object_property_get_link(OBJECT(con),
2307 "device", &error_abort);
2308 if (obj != NULL) {
2309 continue;
2311 return con;
2313 return NULL;
2316 QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
2318 if (con == NULL) {
2319 con = active_console;
2321 return con ? con->cursor : NULL;
2324 bool qemu_console_is_visible(QemuConsole *con)
2326 return (con == active_console) || (con->dcls > 0);
2329 bool qemu_console_is_graphic(QemuConsole *con)
2331 if (con == NULL) {
2332 con = active_console;
2334 return con && QEMU_IS_GRAPHIC_CONSOLE(con);
2337 bool qemu_console_is_fixedsize(QemuConsole *con)
2339 if (con == NULL) {
2340 con = active_console;
2342 return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con));
2345 bool qemu_console_is_gl_blocked(QemuConsole *con)
2347 assert(con != NULL);
2348 return con->gl_block;
2351 bool qemu_console_is_multihead(DeviceState *dev)
2353 QemuConsole *con;
2354 Object *obj;
2355 uint32_t f = 0xffffffff;
2356 uint32_t h;
2358 QTAILQ_FOREACH(con, &consoles, next) {
2359 obj = object_property_get_link(OBJECT(con),
2360 "device", &error_abort);
2361 if (DEVICE(obj) != dev) {
2362 continue;
2365 h = object_property_get_uint(OBJECT(con),
2366 "head", &error_abort);
2367 if (f == 0xffffffff) {
2368 f = h;
2369 } else if (h != f) {
2370 return true;
2373 return false;
2376 char *qemu_console_get_label(QemuConsole *con)
2378 if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
2379 if (con->device) {
2380 DeviceState *dev;
2381 bool multihead;
2383 dev = DEVICE(con->device);
2384 multihead = qemu_console_is_multihead(dev);
2385 if (multihead) {
2386 return g_strdup_printf("%s.%d", dev->id ?
2387 dev->id :
2388 object_get_typename(con->device),
2389 con->head);
2390 } else {
2391 return g_strdup_printf("%s", dev->id ?
2392 dev->id :
2393 object_get_typename(con->device));
2396 return g_strdup("VGA");
2397 } else {
2398 if (con->chr && con->chr->label) {
2399 return g_strdup(con->chr->label);
2401 return g_strdup_printf("vc%d", con->index);
2405 int qemu_console_get_index(QemuConsole *con)
2407 if (con == NULL) {
2408 con = active_console;
2410 return con ? con->index : -1;
2413 uint32_t qemu_console_get_head(QemuConsole *con)
2415 if (con == NULL) {
2416 con = active_console;
2418 return con ? con->head : -1;
2421 int qemu_console_get_width(QemuConsole *con, int fallback)
2423 if (con == NULL) {
2424 con = active_console;
2426 if (con == NULL) {
2427 return fallback;
2429 switch (con->scanout.kind) {
2430 case SCANOUT_DMABUF:
2431 return con->scanout.dmabuf->width;
2432 case SCANOUT_TEXTURE:
2433 return con->scanout.texture.width;
2434 case SCANOUT_SURFACE:
2435 return surface_width(con->surface);
2436 default:
2437 return fallback;
2441 int qemu_console_get_height(QemuConsole *con, int fallback)
2443 if (con == NULL) {
2444 con = active_console;
2446 if (con == NULL) {
2447 return fallback;
2449 switch (con->scanout.kind) {
2450 case SCANOUT_DMABUF:
2451 return con->scanout.dmabuf->height;
2452 case SCANOUT_TEXTURE:
2453 return con->scanout.texture.height;
2454 case SCANOUT_SURFACE:
2455 return surface_height(con->surface);
2456 default:
2457 return fallback;
2461 static void vc_chr_accept_input(Chardev *chr)
2463 VCChardev *drv = VC_CHARDEV(chr);
2464 QemuConsole *s = drv->console;
2466 kbd_send_chars(s);
2469 static void vc_chr_set_echo(Chardev *chr, bool echo)
2471 VCChardev *drv = VC_CHARDEV(chr);
2472 QemuConsole *s = drv->console;
2474 s->echo = echo;
2477 static void text_console_update_cursor_timer(void)
2479 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
2480 + CONSOLE_CURSOR_PERIOD / 2);
2483 static void text_console_update_cursor(void *opaque)
2485 QemuConsole *s;
2486 int count = 0;
2488 cursor_visible_phase = !cursor_visible_phase;
2490 QTAILQ_FOREACH(s, &consoles, next) {
2491 if (qemu_console_is_graphic(s) ||
2492 !qemu_console_is_visible(s)) {
2493 continue;
2495 count++;
2496 graphic_hw_invalidate(s);
2499 if (count) {
2500 text_console_update_cursor_timer();
2504 static const GraphicHwOps text_console_ops = {
2505 .invalidate = text_console_invalidate,
2506 .text_update = text_console_update,
2509 static void text_console_do_init(Chardev *chr)
2511 VCChardev *drv = VC_CHARDEV(chr);
2512 QemuConsole *s = drv->console;
2513 int g_width = 80 * FONT_WIDTH;
2514 int g_height = 24 * FONT_HEIGHT;
2516 fifo8_create(&s->out_fifo, 16);
2518 s->y_displayed = 0;
2519 s->y_base = 0;
2520 s->total_height = DEFAULT_BACKSCROLL;
2521 s->x = 0;
2522 s->y = 0;
2523 if (s->scanout.kind != SCANOUT_SURFACE) {
2524 if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) {
2525 g_width = qemu_console_get_width(active_console, g_width);
2526 g_height = qemu_console_get_height(active_console, g_height);
2528 s->surface = qemu_create_displaysurface(g_width, g_height);
2529 s->scanout.kind = SCANOUT_SURFACE;
2532 s->hw_ops = &text_console_ops;
2533 s->hw = s;
2535 /* set current text attributes to default */
2536 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
2537 text_console_resize(s);
2539 if (chr->label) {
2540 char *msg;
2542 drv->t_attrib.bgcol = QEMU_COLOR_BLUE;
2543 msg = g_strdup_printf("%s console\r\n", chr->label);
2544 qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
2545 g_free(msg);
2546 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
2549 qemu_chr_be_event(chr, CHR_EVENT_OPENED);
2552 static void vc_chr_open(Chardev *chr,
2553 ChardevBackend *backend,
2554 bool *be_opened,
2555 Error **errp)
2557 ChardevVC *vc = backend->u.vc.data;
2558 VCChardev *drv = VC_CHARDEV(chr);
2559 QemuConsole *s;
2560 unsigned width = 0;
2561 unsigned height = 0;
2563 if (vc->has_width) {
2564 width = vc->width;
2565 } else if (vc->has_cols) {
2566 width = vc->cols * FONT_WIDTH;
2569 if (vc->has_height) {
2570 height = vc->height;
2571 } else if (vc->has_rows) {
2572 height = vc->rows * FONT_HEIGHT;
2575 trace_console_txt_new(width, height);
2576 if (width == 0 || height == 0) {
2577 s = (QemuConsole *)object_new(TYPE_QEMU_TEXT_CONSOLE);
2578 } else {
2579 s = (QemuConsole *)object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE);
2580 s->scanout.kind = SCANOUT_SURFACE;
2581 s->surface = qemu_create_displaysurface(width, height);
2584 s->chr = chr;
2585 drv->console = s;
2587 text_console_do_init(chr);
2589 /* console/chardev init sometimes completes elsewhere in a 2nd
2590 * stage, so defer OPENED events until they are fully initialized
2592 *be_opened = false;
2595 void qemu_console_resize(QemuConsole *s, int width, int height)
2597 DisplaySurface *surface = qemu_console_surface(s);
2599 assert(QEMU_IS_GRAPHIC_CONSOLE(s));
2601 if ((s->scanout.kind != SCANOUT_SURFACE ||
2602 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) &&
2603 qemu_console_get_width(s, -1) == width &&
2604 qemu_console_get_height(s, -1) == height) {
2605 return;
2608 surface = qemu_create_displaysurface(width, height);
2609 dpy_gfx_replace_surface(s, surface);
2612 DisplaySurface *qemu_console_surface(QemuConsole *console)
2614 switch (console->scanout.kind) {
2615 case SCANOUT_SURFACE:
2616 return console->surface;
2617 default:
2618 return NULL;
2622 PixelFormat qemu_default_pixelformat(int bpp)
2624 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2625 PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2626 return pf;
2629 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2631 void qemu_display_register(QemuDisplay *ui)
2633 assert(ui->type < DISPLAY_TYPE__MAX);
2634 dpys[ui->type] = ui;
2637 bool qemu_display_find_default(DisplayOptions *opts)
2639 static DisplayType prio[] = {
2640 #if defined(CONFIG_GTK)
2641 DISPLAY_TYPE_GTK,
2642 #endif
2643 #if defined(CONFIG_SDL)
2644 DISPLAY_TYPE_SDL,
2645 #endif
2646 #if defined(CONFIG_COCOA)
2647 DISPLAY_TYPE_COCOA
2648 #endif
2650 int i;
2652 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
2653 if (dpys[prio[i]] == NULL) {
2654 Error *local_err = NULL;
2655 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
2656 if (rv < 0) {
2657 error_report_err(local_err);
2660 if (dpys[prio[i]] == NULL) {
2661 continue;
2663 opts->type = prio[i];
2664 return true;
2666 return false;
2669 void qemu_display_early_init(DisplayOptions *opts)
2671 assert(opts->type < DISPLAY_TYPE__MAX);
2672 if (opts->type == DISPLAY_TYPE_NONE) {
2673 return;
2675 if (dpys[opts->type] == NULL) {
2676 Error *local_err = NULL;
2677 int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
2678 if (rv < 0) {
2679 error_report_err(local_err);
2682 if (dpys[opts->type] == NULL) {
2683 error_report("Display '%s' is not available.",
2684 DisplayType_str(opts->type));
2685 exit(1);
2687 if (dpys[opts->type]->early_init) {
2688 dpys[opts->type]->early_init(opts);
2692 void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2694 assert(opts->type < DISPLAY_TYPE__MAX);
2695 if (opts->type == DISPLAY_TYPE_NONE) {
2696 return;
2698 assert(dpys[opts->type] != NULL);
2699 dpys[opts->type]->init(ds, opts);
2702 void qemu_display_help(void)
2704 int idx;
2706 printf("Available display backend types:\n");
2707 printf("none\n");
2708 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
2709 if (!dpys[idx]) {
2710 Error *local_err = NULL;
2711 int rv = ui_module_load(DisplayType_str(idx), &local_err);
2712 if (rv < 0) {
2713 error_report_err(local_err);
2716 if (dpys[idx]) {
2717 printf("%s\n", DisplayType_str(dpys[idx]->type));
2722 void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
2724 int val;
2725 ChardevVC *vc;
2727 backend->type = CHARDEV_BACKEND_KIND_VC;
2728 vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2729 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
2731 val = qemu_opt_get_number(opts, "width", 0);
2732 if (val != 0) {
2733 vc->has_width = true;
2734 vc->width = val;
2737 val = qemu_opt_get_number(opts, "height", 0);
2738 if (val != 0) {
2739 vc->has_height = true;
2740 vc->height = val;
2743 val = qemu_opt_get_number(opts, "cols", 0);
2744 if (val != 0) {
2745 vc->has_cols = true;
2746 vc->cols = val;
2749 val = qemu_opt_get_number(opts, "rows", 0);
2750 if (val != 0) {
2751 vc->has_rows = true;
2752 vc->rows = val;
2756 static void char_vc_class_init(ObjectClass *oc, void *data)
2758 ChardevClass *cc = CHARDEV_CLASS(oc);
2760 cc->parse = qemu_chr_parse_vc;
2761 cc->open = vc_chr_open;
2762 cc->chr_write = vc_chr_write;
2763 cc->chr_accept_input = vc_chr_accept_input;
2764 cc->chr_set_echo = vc_chr_set_echo;
2767 static const TypeInfo char_vc_type_info = {
2768 .name = TYPE_CHARDEV_VC,
2769 .parent = TYPE_CHARDEV,
2770 .instance_size = sizeof(VCChardev),
2771 .class_init = char_vc_class_init,
2774 void qemu_console_early_init(void)
2776 /* set the default vc driver */
2777 if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2778 type_register(&char_vc_type_info);