hexagon: spelling fixes
[qemu/armbru.git] / ui / console.c
blobe4d61794bb2c687f349df88cd0968e2e31ddd14f
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;
86 QemuUIInfo ui_info;
87 QEMUTimer *ui_timer;
88 const GraphicHwOps *hw_ops;
89 void *hw;
90 CoQueue dump_queue;
92 QTAILQ_ENTRY(QemuConsole) next;
95 OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT)
97 typedef struct QemuGraphicConsole {
98 QemuConsole parent;
100 Object *device;
101 uint32_t head;
103 QEMUCursor *cursor;
104 int cursor_x, cursor_y, cursor_on;
105 } QemuGraphicConsole;
107 typedef QemuConsoleClass QemuGraphicConsoleClass;
109 OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE)
111 typedef struct QemuTextConsole {
112 QemuConsole parent;
114 int width;
115 int height;
116 int total_height;
117 int backscroll_height;
118 int x, y;
119 int y_displayed;
120 int y_base;
121 TextCell *cells;
122 int text_x[2], text_y[2], cursor_invalidate;
123 int echo;
125 int update_x0;
126 int update_y0;
127 int update_x1;
128 int update_y1;
130 Chardev *chr;
131 /* fifo for key pressed */
132 Fifo8 out_fifo;
133 } QemuTextConsole;
135 typedef QemuConsoleClass QemuTextConsoleClass;
137 OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE)
139 typedef struct QemuFixedTextConsole {
140 QemuTextConsole parent;
141 } QemuFixedTextConsole;
143 typedef QemuTextConsoleClass QemuFixedTextConsoleClass;
145 OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE)
147 struct VCChardev {
148 Chardev parent;
149 QemuTextConsole *console;
151 enum TTYState state;
152 int esc_params[MAX_ESC_PARAMS];
153 int nb_esc_params;
154 TextAttributes t_attrib; /* currently active text attributes */
155 int x_saved, y_saved;
157 typedef struct VCChardev VCChardev;
159 struct DisplayState {
160 QEMUTimer *gui_timer;
161 uint64_t last_update;
162 uint64_t update_interval;
163 bool refreshing;
165 QLIST_HEAD(, DisplayChangeListener) listeners;
168 static DisplayState *display_state;
169 static QemuConsole *active_console;
170 static QTAILQ_HEAD(, QemuConsole) consoles =
171 QTAILQ_HEAD_INITIALIZER(consoles);
172 static bool cursor_visible_phase;
173 static QEMUTimer *cursor_timer;
175 static void dpy_refresh(DisplayState *s);
176 static DisplayState *get_alloc_displaystate(void);
177 static void text_console_update_cursor(void *opaque);
178 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
179 static bool console_compatible_with(QemuConsole *con,
180 DisplayChangeListener *dcl, Error **errp);
181 static QemuConsole *qemu_graphic_console_lookup_unused(void);
182 static void dpy_set_ui_info_timer(void *opaque);
184 static void gui_update(void *opaque)
186 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
187 uint64_t dcl_interval;
188 DisplayState *ds = opaque;
189 DisplayChangeListener *dcl;
191 ds->refreshing = true;
192 dpy_refresh(ds);
193 ds->refreshing = false;
195 QLIST_FOREACH(dcl, &ds->listeners, next) {
196 dcl_interval = dcl->update_interval ?
197 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
198 if (interval > dcl_interval) {
199 interval = dcl_interval;
202 if (ds->update_interval != interval) {
203 ds->update_interval = interval;
204 trace_console_refresh(interval);
206 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
207 timer_mod(ds->gui_timer, ds->last_update + interval);
210 static void gui_setup_refresh(DisplayState *ds)
212 DisplayChangeListener *dcl;
213 bool need_timer = false;
215 QLIST_FOREACH(dcl, &ds->listeners, next) {
216 if (dcl->ops->dpy_refresh != NULL) {
217 need_timer = true;
221 if (need_timer && ds->gui_timer == NULL) {
222 ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
223 timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
225 if (!need_timer && ds->gui_timer != NULL) {
226 timer_free(ds->gui_timer);
227 ds->gui_timer = NULL;
231 void graphic_hw_update_done(QemuConsole *con)
233 if (con) {
234 qemu_co_enter_all(&con->dump_queue, NULL);
238 void graphic_hw_update(QemuConsole *con)
240 bool async = false;
241 con = con ? con : active_console;
242 if (!con) {
243 return;
245 if (con->hw_ops->gfx_update) {
246 con->hw_ops->gfx_update(con->hw);
247 async = con->hw_ops->gfx_update_async;
249 if (!async) {
250 graphic_hw_update_done(con);
254 static void graphic_hw_update_bh(void *con)
256 graphic_hw_update(con);
259 void qemu_console_co_wait_update(QemuConsole *con)
261 if (qemu_co_queue_empty(&con->dump_queue)) {
262 /* Defer the update, it will restart the pending coroutines */
263 aio_bh_schedule_oneshot(qemu_get_aio_context(),
264 graphic_hw_update_bh, con);
266 qemu_co_queue_wait(&con->dump_queue, NULL);
270 static void graphic_hw_gl_unblock_timer(void *opaque)
272 warn_report("console: no gl-unblock within one second");
275 void graphic_hw_gl_block(QemuConsole *con, bool block)
277 uint64_t timeout;
278 assert(con != NULL);
280 if (block) {
281 con->gl_block++;
282 } else {
283 con->gl_block--;
285 assert(con->gl_block >= 0);
286 if (!con->hw_ops->gl_block) {
287 return;
289 if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {
290 return;
292 con->hw_ops->gl_block(con->hw, block);
294 if (block) {
295 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
296 timeout += 1000; /* one sec */
297 timer_mod(con->gl_unblock_timer, timeout);
298 } else {
299 timer_del(con->gl_unblock_timer);
303 int qemu_console_get_window_id(QemuConsole *con)
305 return con->window_id;
308 void qemu_console_set_window_id(QemuConsole *con, int window_id)
310 con->window_id = window_id;
313 void graphic_hw_invalidate(QemuConsole *con)
315 if (!con) {
316 con = active_console;
318 if (con && con->hw_ops->invalidate) {
319 con->hw_ops->invalidate(con->hw);
323 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
325 if (!con) {
326 con = active_console;
328 if (con && con->hw_ops->text_update) {
329 con->hw_ops->text_update(con->hw, chardata);
333 static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy,
334 int width, int height, pixman_color_t color)
336 DisplaySurface *surface = qemu_console_surface(con);
337 pixman_rectangle16_t rect = {
338 .x = posx, .y = posy, .width = width, .height = height
341 assert(surface);
342 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
343 &color, 1, &rect);
346 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
347 static void qemu_console_bitblt(QemuConsole *con,
348 int xs, int ys, int xd, int yd, int w, int h)
350 DisplaySurface *surface = qemu_console_surface(con);
352 assert(surface);
353 pixman_image_composite(PIXMAN_OP_SRC,
354 surface->image, NULL, surface->image,
355 xs, ys, 0, 0, xd, yd, w, h);
358 /***********************************************************/
359 /* basic char display */
361 #define FONT_HEIGHT 16
362 #define FONT_WIDTH 8
364 #include "vgafont.h"
366 static const pixman_color_t color_table_rgb[2][8] = {
367 { /* dark */
368 [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
369 [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */
370 [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */
371 [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */
372 [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */
373 [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */
374 [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */
375 [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR_GRAY,
377 { /* bright */
378 [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
379 [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */
380 [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */
381 [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */
382 [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */
383 [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */
384 [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */
385 [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */
389 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
390 TextAttributes *t_attrib)
392 static pixman_image_t *glyphs[256];
393 DisplaySurface *surface = qemu_console_surface(s);
394 pixman_color_t fgcol, bgcol;
396 assert(surface);
397 if (t_attrib->invers) {
398 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
399 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
400 } else {
401 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
402 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
405 if (!glyphs[ch]) {
406 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
408 qemu_pixman_glyph_render(glyphs[ch], surface->image,
409 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
412 static void text_console_resize(QemuTextConsole *t)
414 QemuConsole *s = QEMU_CONSOLE(t);
415 TextCell *cells, *c, *c1;
416 int w1, x, y, last_width, w, h;
418 assert(s->scanout.kind == SCANOUT_SURFACE);
420 w = surface_width(s->surface) / FONT_WIDTH;
421 h = surface_height(s->surface) / FONT_HEIGHT;
422 if (w == t->width && h == t->height) {
423 return;
426 last_width = t->width;
427 t->width = w;
428 t->height = h;
430 w1 = MIN(t->width, last_width);
432 cells = g_new(TextCell, t->width * t->total_height + 1);
433 for (y = 0; y < t->total_height; y++) {
434 c = &cells[y * t->width];
435 if (w1 > 0) {
436 c1 = &t->cells[y * last_width];
437 for (x = 0; x < w1; x++) {
438 *c++ = *c1++;
441 for (x = w1; x < t->width; x++) {
442 c->ch = ' ';
443 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
444 c++;
447 g_free(t->cells);
448 t->cells = cells;
451 static void invalidate_xy(QemuTextConsole *s, int x, int y)
453 if (!qemu_console_is_visible(QEMU_CONSOLE(s))) {
454 return;
456 if (s->update_x0 > x * FONT_WIDTH)
457 s->update_x0 = x * FONT_WIDTH;
458 if (s->update_y0 > y * FONT_HEIGHT)
459 s->update_y0 = y * FONT_HEIGHT;
460 if (s->update_x1 < (x + 1) * FONT_WIDTH)
461 s->update_x1 = (x + 1) * FONT_WIDTH;
462 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
463 s->update_y1 = (y + 1) * FONT_HEIGHT;
466 static void vc_update_xy(VCChardev *vc, int x, int y)
468 QemuTextConsole *s = vc->console;
469 TextCell *c;
470 int y1, y2;
472 s->text_x[0] = MIN(s->text_x[0], x);
473 s->text_x[1] = MAX(s->text_x[1], x);
474 s->text_y[0] = MIN(s->text_y[0], y);
475 s->text_y[1] = MAX(s->text_y[1], y);
477 y1 = (s->y_base + y) % s->total_height;
478 y2 = y1 - s->y_displayed;
479 if (y2 < 0) {
480 y2 += s->total_height;
482 if (y2 < s->height) {
483 if (x >= s->width) {
484 x = s->width - 1;
486 c = &s->cells[y1 * s->width + x];
487 vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch,
488 &(c->t_attrib));
489 invalidate_xy(s, x, y2);
493 static void console_show_cursor(QemuTextConsole *s, int show)
495 TextCell *c;
496 int y, y1;
497 int x = s->x;
499 s->cursor_invalidate = 1;
501 if (x >= s->width) {
502 x = s->width - 1;
504 y1 = (s->y_base + s->y) % s->total_height;
505 y = y1 - s->y_displayed;
506 if (y < 0) {
507 y += s->total_height;
509 if (y < s->height) {
510 c = &s->cells[y1 * s->width + x];
511 if (show && cursor_visible_phase) {
512 TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
513 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
514 vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &t_attrib);
515 } else {
516 vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib));
518 invalidate_xy(s, x, y);
522 static void console_refresh(QemuTextConsole *s)
524 DisplaySurface *surface = qemu_console_surface(QEMU_CONSOLE(s));
525 TextCell *c;
526 int x, y, y1;
528 assert(surface);
529 s->text_x[0] = 0;
530 s->text_y[0] = 0;
531 s->text_x[1] = s->width - 1;
532 s->text_y[1] = s->height - 1;
533 s->cursor_invalidate = 1;
535 qemu_console_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface),
536 color_table_rgb[0][QEMU_COLOR_BLACK]);
537 y1 = s->y_displayed;
538 for (y = 0; y < s->height; y++) {
539 c = s->cells + y1 * s->width;
540 for (x = 0; x < s->width; x++) {
541 vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch,
542 &(c->t_attrib));
543 c++;
545 if (++y1 == s->total_height) {
546 y1 = 0;
549 console_show_cursor(s, 1);
550 dpy_gfx_update(QEMU_CONSOLE(s), 0, 0,
551 surface_width(surface), surface_height(surface));
554 static void console_scroll(QemuTextConsole *s, int ydelta)
556 int i, y1;
558 if (ydelta > 0) {
559 for(i = 0; i < ydelta; i++) {
560 if (s->y_displayed == s->y_base)
561 break;
562 if (++s->y_displayed == s->total_height)
563 s->y_displayed = 0;
565 } else {
566 ydelta = -ydelta;
567 i = s->backscroll_height;
568 if (i > s->total_height - s->height)
569 i = s->total_height - s->height;
570 y1 = s->y_base - i;
571 if (y1 < 0)
572 y1 += s->total_height;
573 for(i = 0; i < ydelta; i++) {
574 if (s->y_displayed == y1)
575 break;
576 if (--s->y_displayed < 0)
577 s->y_displayed = s->total_height - 1;
580 console_refresh(s);
583 static void vc_put_lf(VCChardev *vc)
585 QemuTextConsole *s = vc->console;
586 TextCell *c;
587 int x, y1;
589 s->y++;
590 if (s->y >= s->height) {
591 s->y = s->height - 1;
593 if (s->y_displayed == s->y_base) {
594 if (++s->y_displayed == s->total_height)
595 s->y_displayed = 0;
597 if (++s->y_base == s->total_height)
598 s->y_base = 0;
599 if (s->backscroll_height < s->total_height)
600 s->backscroll_height++;
601 y1 = (s->y_base + s->height - 1) % s->total_height;
602 c = &s->cells[y1 * s->width];
603 for(x = 0; x < s->width; x++) {
604 c->ch = ' ';
605 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
606 c++;
608 if (s->y_displayed == s->y_base) {
609 s->text_x[0] = 0;
610 s->text_y[0] = 0;
611 s->text_x[1] = s->width - 1;
612 s->text_y[1] = s->height - 1;
614 qemu_console_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0,
615 s->width * FONT_WIDTH,
616 (s->height - 1) * FONT_HEIGHT);
617 qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT,
618 s->width * FONT_WIDTH, FONT_HEIGHT,
619 color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
620 s->update_x0 = 0;
621 s->update_y0 = 0;
622 s->update_x1 = s->width * FONT_WIDTH;
623 s->update_y1 = s->height * FONT_HEIGHT;
628 /* Set console attributes depending on the current escape codes.
629 * NOTE: I know this code is not very efficient (checking every color for it
630 * self) but it is more readable and better maintainable.
632 static void vc_handle_escape(VCChardev *vc)
634 int i;
636 for (i = 0; i < vc->nb_esc_params; i++) {
637 switch (vc->esc_params[i]) {
638 case 0: /* reset all console attributes to default */
639 vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
640 break;
641 case 1:
642 vc->t_attrib.bold = 1;
643 break;
644 case 4:
645 vc->t_attrib.uline = 1;
646 break;
647 case 5:
648 vc->t_attrib.blink = 1;
649 break;
650 case 7:
651 vc->t_attrib.invers = 1;
652 break;
653 case 8:
654 vc->t_attrib.unvisible = 1;
655 break;
656 case 22:
657 vc->t_attrib.bold = 0;
658 break;
659 case 24:
660 vc->t_attrib.uline = 0;
661 break;
662 case 25:
663 vc->t_attrib.blink = 0;
664 break;
665 case 27:
666 vc->t_attrib.invers = 0;
667 break;
668 case 28:
669 vc->t_attrib.unvisible = 0;
670 break;
671 /* set foreground color */
672 case 30:
673 vc->t_attrib.fgcol = QEMU_COLOR_BLACK;
674 break;
675 case 31:
676 vc->t_attrib.fgcol = QEMU_COLOR_RED;
677 break;
678 case 32:
679 vc->t_attrib.fgcol = QEMU_COLOR_GREEN;
680 break;
681 case 33:
682 vc->t_attrib.fgcol = QEMU_COLOR_YELLOW;
683 break;
684 case 34:
685 vc->t_attrib.fgcol = QEMU_COLOR_BLUE;
686 break;
687 case 35:
688 vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
689 break;
690 case 36:
691 vc->t_attrib.fgcol = QEMU_COLOR_CYAN;
692 break;
693 case 37:
694 vc->t_attrib.fgcol = QEMU_COLOR_WHITE;
695 break;
696 /* set background color */
697 case 40:
698 vc->t_attrib.bgcol = QEMU_COLOR_BLACK;
699 break;
700 case 41:
701 vc->t_attrib.bgcol = QEMU_COLOR_RED;
702 break;
703 case 42:
704 vc->t_attrib.bgcol = QEMU_COLOR_GREEN;
705 break;
706 case 43:
707 vc->t_attrib.bgcol = QEMU_COLOR_YELLOW;
708 break;
709 case 44:
710 vc->t_attrib.bgcol = QEMU_COLOR_BLUE;
711 break;
712 case 45:
713 vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
714 break;
715 case 46:
716 vc->t_attrib.bgcol = QEMU_COLOR_CYAN;
717 break;
718 case 47:
719 vc->t_attrib.bgcol = QEMU_COLOR_WHITE;
720 break;
725 static void vc_clear_xy(VCChardev *vc, int x, int y)
727 QemuTextConsole *s = vc->console;
728 int y1 = (s->y_base + y) % s->total_height;
729 if (x >= s->width) {
730 x = s->width - 1;
732 TextCell *c = &s->cells[y1 * s->width + x];
733 c->ch = ' ';
734 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
735 vc_update_xy(vc, x, y);
738 static void vc_put_one(VCChardev *vc, int ch)
740 QemuTextConsole *s = vc->console;
741 TextCell *c;
742 int y1;
743 if (s->x >= s->width) {
744 /* line wrap */
745 s->x = 0;
746 vc_put_lf(vc);
748 y1 = (s->y_base + s->y) % s->total_height;
749 c = &s->cells[y1 * s->width + s->x];
750 c->ch = ch;
751 c->t_attrib = vc->t_attrib;
752 vc_update_xy(vc, s->x, s->y);
753 s->x++;
756 static void vc_respond_str(VCChardev *vc, const char *buf)
758 while (*buf) {
759 vc_put_one(vc, *buf);
760 buf++;
764 /* set cursor, checking bounds */
765 static void vc_set_cursor(VCChardev *vc, int x, int y)
767 QemuTextConsole *s = vc->console;
769 if (x < 0) {
770 x = 0;
772 if (y < 0) {
773 y = 0;
775 if (y >= s->height) {
776 y = s->height - 1;
778 if (x >= s->width) {
779 x = s->width - 1;
782 s->x = x;
783 s->y = y;
786 static void vc_putchar(VCChardev *vc, int ch)
788 QemuTextConsole *s = vc->console;
789 int i;
790 int x, y;
791 char response[40];
793 switch(vc->state) {
794 case TTY_STATE_NORM:
795 switch(ch) {
796 case '\r': /* carriage return */
797 s->x = 0;
798 break;
799 case '\n': /* newline */
800 vc_put_lf(vc);
801 break;
802 case '\b': /* backspace */
803 if (s->x > 0)
804 s->x--;
805 break;
806 case '\t': /* tabspace */
807 if (s->x + (8 - (s->x % 8)) > s->width) {
808 s->x = 0;
809 vc_put_lf(vc);
810 } else {
811 s->x = s->x + (8 - (s->x % 8));
813 break;
814 case '\a': /* alert aka. bell */
815 /* TODO: has to be implemented */
816 break;
817 case 14:
818 /* SI (shift in), character set 0 (ignored) */
819 break;
820 case 15:
821 /* SO (shift out), character set 1 (ignored) */
822 break;
823 case 27: /* esc (introducing an escape sequence) */
824 vc->state = TTY_STATE_ESC;
825 break;
826 default:
827 vc_put_one(vc, ch);
828 break;
830 break;
831 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
832 if (ch == '[') {
833 for(i=0;i<MAX_ESC_PARAMS;i++)
834 vc->esc_params[i] = 0;
835 vc->nb_esc_params = 0;
836 vc->state = TTY_STATE_CSI;
837 } else {
838 vc->state = TTY_STATE_NORM;
840 break;
841 case TTY_STATE_CSI: /* handle escape sequence parameters */
842 if (ch >= '0' && ch <= '9') {
843 if (vc->nb_esc_params < MAX_ESC_PARAMS) {
844 int *param = &vc->esc_params[vc->nb_esc_params];
845 int digit = (ch - '0');
847 *param = (*param <= (INT_MAX - digit) / 10) ?
848 *param * 10 + digit : INT_MAX;
850 } else {
851 if (vc->nb_esc_params < MAX_ESC_PARAMS)
852 vc->nb_esc_params++;
853 if (ch == ';' || ch == '?') {
854 break;
856 trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1],
857 ch, vc->nb_esc_params);
858 vc->state = TTY_STATE_NORM;
859 switch(ch) {
860 case 'A':
861 /* move cursor up */
862 if (vc->esc_params[0] == 0) {
863 vc->esc_params[0] = 1;
865 vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]);
866 break;
867 case 'B':
868 /* move cursor down */
869 if (vc->esc_params[0] == 0) {
870 vc->esc_params[0] = 1;
872 vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]);
873 break;
874 case 'C':
875 /* move cursor right */
876 if (vc->esc_params[0] == 0) {
877 vc->esc_params[0] = 1;
879 vc_set_cursor(vc, s->x + vc->esc_params[0], s->y);
880 break;
881 case 'D':
882 /* move cursor left */
883 if (vc->esc_params[0] == 0) {
884 vc->esc_params[0] = 1;
886 vc_set_cursor(vc, s->x - vc->esc_params[0], s->y);
887 break;
888 case 'G':
889 /* move cursor to column */
890 vc_set_cursor(vc, vc->esc_params[0] - 1, s->y);
891 break;
892 case 'f':
893 case 'H':
894 /* move cursor to row, column */
895 vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1);
896 break;
897 case 'J':
898 switch (vc->esc_params[0]) {
899 case 0:
900 /* clear to end of screen */
901 for (y = s->y; y < s->height; y++) {
902 for (x = 0; x < s->width; x++) {
903 if (y == s->y && x < s->x) {
904 continue;
906 vc_clear_xy(vc, x, y);
909 break;
910 case 1:
911 /* clear from beginning of screen */
912 for (y = 0; y <= s->y; y++) {
913 for (x = 0; x < s->width; x++) {
914 if (y == s->y && x > s->x) {
915 break;
917 vc_clear_xy(vc, x, y);
920 break;
921 case 2:
922 /* clear entire screen */
923 for (y = 0; y <= s->height; y++) {
924 for (x = 0; x < s->width; x++) {
925 vc_clear_xy(vc, x, y);
928 break;
930 break;
931 case 'K':
932 switch (vc->esc_params[0]) {
933 case 0:
934 /* clear to eol */
935 for(x = s->x; x < s->width; x++) {
936 vc_clear_xy(vc, x, s->y);
938 break;
939 case 1:
940 /* clear from beginning of line */
941 for (x = 0; x <= s->x && x < s->width; x++) {
942 vc_clear_xy(vc, x, s->y);
944 break;
945 case 2:
946 /* clear entire line */
947 for(x = 0; x < s->width; x++) {
948 vc_clear_xy(vc, x, s->y);
950 break;
952 break;
953 case 'm':
954 vc_handle_escape(vc);
955 break;
956 case 'n':
957 switch (vc->esc_params[0]) {
958 case 5:
959 /* report console status (always succeed)*/
960 vc_respond_str(vc, "\033[0n");
961 break;
962 case 6:
963 /* report cursor position */
964 sprintf(response, "\033[%d;%dR",
965 (s->y_base + s->y) % s->total_height + 1,
966 s->x + 1);
967 vc_respond_str(vc, response);
968 break;
970 break;
971 case 's':
972 /* save cursor position */
973 vc->x_saved = s->x;
974 vc->y_saved = s->y;
975 break;
976 case 'u':
977 /* restore cursor position */
978 s->x = vc->x_saved;
979 s->y = vc->y_saved;
980 break;
981 default:
982 trace_console_putchar_unhandled(ch);
983 break;
985 break;
990 static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl,
991 struct DisplaySurface *new_surface,
992 bool update)
994 if (dcl->ops->dpy_gfx_switch) {
995 dcl->ops->dpy_gfx_switch(dcl, new_surface);
998 if (update && dcl->ops->dpy_gfx_update) {
999 dcl->ops->dpy_gfx_update(dcl, 0, 0,
1000 surface_width(new_surface),
1001 surface_height(new_surface));
1005 static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface)
1007 if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) {
1008 con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface);
1012 static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface)
1014 if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) {
1015 con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface);
1019 static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface,
1020 int x, int y, int w, int h)
1022 if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) {
1023 con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h);
1027 static void displaychangelistener_display_console(DisplayChangeListener *dcl,
1028 QemuConsole *con,
1029 Error **errp)
1031 static const char nodev[] =
1032 "This VM has no graphic display device.";
1033 static DisplaySurface *dummy;
1035 if (!con || !console_compatible_with(con, dcl, errp)) {
1036 if (!dummy) {
1037 dummy = qemu_create_placeholder_surface(640, 480, nodev);
1039 if (con) {
1040 dpy_gfx_create_texture(con, dummy);
1042 displaychangelistener_gfx_switch(dcl, dummy, TRUE);
1043 return;
1046 dpy_gfx_create_texture(con, con->surface);
1047 displaychangelistener_gfx_switch(dcl, con->surface,
1048 con->scanout.kind == SCANOUT_SURFACE);
1050 if (con->scanout.kind == SCANOUT_DMABUF &&
1051 displaychangelistener_has_dmabuf(dcl)) {
1052 dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
1053 } else if (con->scanout.kind == SCANOUT_TEXTURE &&
1054 dcl->ops->dpy_gl_scanout_texture) {
1055 dcl->ops->dpy_gl_scanout_texture(dcl,
1056 con->scanout.texture.backing_id,
1057 con->scanout.texture.backing_y_0_top,
1058 con->scanout.texture.backing_width,
1059 con->scanout.texture.backing_height,
1060 con->scanout.texture.x,
1061 con->scanout.texture.y,
1062 con->scanout.texture.width,
1063 con->scanout.texture.height,
1064 con->scanout.texture.d3d_tex2d);
1068 void console_select(unsigned int index)
1070 DisplayChangeListener *dcl;
1071 QemuConsole *s;
1073 trace_console_select(index);
1074 s = qemu_console_lookup_by_index(index);
1075 if (s) {
1076 DisplayState *ds = s->ds;
1078 active_console = s;
1079 QLIST_FOREACH (dcl, &ds->listeners, next) {
1080 if (dcl->con != NULL) {
1081 continue;
1083 displaychangelistener_display_console(dcl, s, NULL);
1086 if (QEMU_IS_TEXT_CONSOLE(s)) {
1087 dpy_text_resize(s, QEMU_TEXT_CONSOLE(s)->width, QEMU_TEXT_CONSOLE(s)->height);
1088 text_console_update_cursor(NULL);
1093 #define TYPE_CHARDEV_VC "chardev-vc"
1094 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
1095 TYPE_CHARDEV_VC)
1097 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1099 VCChardev *drv = VC_CHARDEV(chr);
1100 QemuTextConsole *s = drv->console;
1101 int i;
1103 s->update_x0 = s->width * FONT_WIDTH;
1104 s->update_y0 = s->height * FONT_HEIGHT;
1105 s->update_x1 = 0;
1106 s->update_y1 = 0;
1107 console_show_cursor(s, 0);
1108 for(i = 0; i < len; i++) {
1109 vc_putchar(drv, buf[i]);
1111 console_show_cursor(s, 1);
1112 if (s->update_x0 < s->update_x1) {
1113 dpy_gfx_update(QEMU_CONSOLE(s), s->update_x0, s->update_y0,
1114 s->update_x1 - s->update_x0,
1115 s->update_y1 - s->update_y0);
1117 return len;
1120 static void kbd_send_chars(QemuTextConsole *s)
1122 uint32_t len, avail;
1124 len = qemu_chr_be_can_write(s->chr);
1125 avail = fifo8_num_used(&s->out_fifo);
1126 while (len > 0 && avail > 0) {
1127 const uint8_t *buf;
1128 uint32_t size;
1130 buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size);
1131 qemu_chr_be_write(s->chr, buf, size);
1132 len = qemu_chr_be_can_write(s->chr);
1133 avail -= size;
1137 /* called when an ascii key is pressed */
1138 void kbd_put_keysym_console(QemuTextConsole *s, int keysym)
1140 uint8_t buf[16], *q;
1141 int c;
1142 uint32_t num_free;
1144 switch(keysym) {
1145 case QEMU_KEY_CTRL_UP:
1146 console_scroll(s, -1);
1147 break;
1148 case QEMU_KEY_CTRL_DOWN:
1149 console_scroll(s, 1);
1150 break;
1151 case QEMU_KEY_CTRL_PAGEUP:
1152 console_scroll(s, -10);
1153 break;
1154 case QEMU_KEY_CTRL_PAGEDOWN:
1155 console_scroll(s, 10);
1156 break;
1157 default:
1158 /* convert the QEMU keysym to VT100 key string */
1159 q = buf;
1160 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1161 *q++ = '\033';
1162 *q++ = '[';
1163 c = keysym - 0xe100;
1164 if (c >= 10)
1165 *q++ = '0' + (c / 10);
1166 *q++ = '0' + (c % 10);
1167 *q++ = '~';
1168 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1169 *q++ = '\033';
1170 *q++ = '[';
1171 *q++ = keysym & 0xff;
1172 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1173 qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
1174 *q++ = '\n';
1175 } else {
1176 *q++ = keysym;
1178 if (s->echo) {
1179 qemu_chr_write(s->chr, buf, q - buf, true);
1181 num_free = fifo8_num_free(&s->out_fifo);
1182 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
1183 kbd_send_chars(s);
1184 break;
1188 static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1189 [Q_KEY_CODE_UP] = QEMU_KEY_UP,
1190 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN,
1191 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT,
1192 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT,
1193 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME,
1194 [Q_KEY_CODE_END] = QEMU_KEY_END,
1195 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP,
1196 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN,
1197 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1198 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB,
1199 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1202 static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1203 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP,
1204 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN,
1205 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT,
1206 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT,
1207 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME,
1208 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END,
1209 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP,
1210 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN,
1213 bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl)
1215 int keysym;
1217 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1218 if (keysym == 0) {
1219 return false;
1221 kbd_put_keysym_console(s, keysym);
1222 return true;
1225 void kbd_put_string_console(QemuTextConsole *s, const char *str, int len)
1227 int i;
1229 for (i = 0; i < len && str[i]; i++) {
1230 kbd_put_keysym_console(s, str[i]);
1234 void kbd_put_keysym(int keysym)
1236 if (QEMU_IS_TEXT_CONSOLE(active_console)) {
1237 kbd_put_keysym_console(QEMU_TEXT_CONSOLE(active_console), keysym);
1241 static void text_console_invalidate(void *opaque)
1243 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
1245 if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
1246 text_console_resize(QEMU_TEXT_CONSOLE(s));
1248 console_refresh(s);
1251 static void text_console_update(void *opaque, console_ch_t *chardata)
1253 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
1254 int i, j, src;
1256 if (s->text_x[0] <= s->text_x[1]) {
1257 src = (s->y_base + s->text_y[0]) * s->width;
1258 chardata += s->text_y[0] * s->width;
1259 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1260 for (j = 0; j < s->width; j++, src++) {
1261 console_write_ch(chardata ++,
1262 ATTR2CHTYPE(s->cells[src].ch,
1263 s->cells[src].t_attrib.fgcol,
1264 s->cells[src].t_attrib.bgcol,
1265 s->cells[src].t_attrib.bold));
1267 dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0],
1268 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1269 s->text_x[0] = s->width;
1270 s->text_y[0] = s->height;
1271 s->text_x[1] = 0;
1272 s->text_y[1] = 0;
1274 if (s->cursor_invalidate) {
1275 dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y);
1276 s->cursor_invalidate = 0;
1280 static void
1281 qemu_console_register(QemuConsole *c)
1283 int i;
1285 if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) &&
1286 QEMU_IS_GRAPHIC_CONSOLE(c))) {
1287 active_console = c;
1290 if (QTAILQ_EMPTY(&consoles)) {
1291 c->index = 0;
1292 QTAILQ_INSERT_TAIL(&consoles, c, next);
1293 } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) {
1294 QemuConsole *last = QTAILQ_LAST(&consoles);
1295 c->index = last->index + 1;
1296 QTAILQ_INSERT_TAIL(&consoles, c, next);
1297 } else {
1299 * HACK: Put graphical consoles before text consoles.
1301 * Only do that for coldplugged devices. After initial device
1302 * initialization we will not renumber the consoles any more.
1304 QemuConsole *it = QTAILQ_FIRST(&consoles);
1306 while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) {
1307 it = QTAILQ_NEXT(it, next);
1309 if (QEMU_IS_GRAPHIC_CONSOLE(it)) {
1310 /* have no text consoles */
1311 c->index = it->index + 1;
1312 QTAILQ_INSERT_AFTER(&consoles, it, c, next);
1313 } else {
1314 c->index = it->index;
1315 QTAILQ_INSERT_BEFORE(it, c, next);
1316 /* renumber text consoles */
1317 for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) {
1318 it->index = i;
1324 static void
1325 qemu_console_finalize(Object *obj)
1327 QemuConsole *c = QEMU_CONSOLE(obj);
1329 /* TODO: check this code path, and unregister from consoles */
1330 g_clear_pointer(&c->surface, qemu_free_displaysurface);
1331 g_clear_pointer(&c->gl_unblock_timer, timer_free);
1332 g_clear_pointer(&c->ui_timer, timer_free);
1335 static void
1336 qemu_console_class_init(ObjectClass *oc, void *data)
1340 static void
1341 qemu_console_init(Object *obj)
1343 QemuConsole *c = QEMU_CONSOLE(obj);
1344 DisplayState *ds = get_alloc_displaystate();
1346 qemu_co_queue_init(&c->dump_queue);
1347 c->ds = ds;
1348 c->window_id = -1;
1349 c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1350 dpy_set_ui_info_timer, c);
1351 qemu_console_register(c);
1354 static void
1355 qemu_graphic_console_finalize(Object *obj)
1357 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
1359 g_clear_pointer(&c->device, object_unref);
1362 static void
1363 qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name,
1364 void *opaque, Error **errp)
1366 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
1368 visit_type_uint32(v, name, &c->head, errp);
1371 static void
1372 qemu_graphic_console_class_init(ObjectClass *oc, void *data)
1374 object_class_property_add_link(oc, "device", TYPE_DEVICE,
1375 offsetof(QemuGraphicConsole, device),
1376 object_property_allow_set_link,
1377 OBJ_PROP_LINK_STRONG);
1378 object_class_property_add(oc, "head", "uint32",
1379 qemu_graphic_console_prop_get_head,
1380 NULL, NULL, NULL);
1383 static void
1384 qemu_graphic_console_init(Object *obj)
1388 static void
1389 qemu_text_console_finalize(Object *obj)
1393 static void
1394 qemu_text_console_class_init(ObjectClass *oc, void *data)
1396 if (!cursor_timer) {
1397 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1398 text_console_update_cursor, NULL);
1402 static const GraphicHwOps text_console_ops = {
1403 .invalidate = text_console_invalidate,
1404 .text_update = text_console_update,
1407 static void
1408 qemu_text_console_init(Object *obj)
1410 QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
1412 fifo8_create(&c->out_fifo, 16);
1413 c->total_height = DEFAULT_BACKSCROLL;
1414 QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
1415 QEMU_CONSOLE(c)->hw = c;
1418 static void
1419 qemu_fixed_text_console_finalize(Object *obj)
1423 static void
1424 qemu_fixed_text_console_class_init(ObjectClass *oc, void *data)
1428 static void
1429 qemu_fixed_text_console_init(Object *obj)
1433 #ifdef WIN32
1434 void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
1435 HANDLE h, uint32_t offset)
1437 assert(!surface->handle);
1439 surface->handle = h;
1440 surface->handle_offset = offset;
1443 static void
1444 win32_pixman_image_destroy(pixman_image_t *image, void *data)
1446 DisplaySurface *surface = data;
1448 if (!surface->handle) {
1449 return;
1452 assert(surface->handle_offset == 0);
1454 qemu_win32_map_free(
1455 pixman_image_get_data(surface->image),
1456 surface->handle,
1457 &error_warn
1460 #endif
1462 DisplaySurface *qemu_create_displaysurface(int width, int height)
1464 DisplaySurface *surface;
1465 void *bits = NULL;
1466 #ifdef WIN32
1467 HANDLE handle = NULL;
1468 #endif
1470 trace_displaysurface_create(width, height);
1472 #ifdef WIN32
1473 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
1474 #endif
1476 surface = qemu_create_displaysurface_from(
1477 width, height,
1478 PIXMAN_x8r8g8b8,
1479 width * 4, bits
1481 surface->flags = QEMU_ALLOCATED_FLAG;
1483 #ifdef WIN32
1484 qemu_displaysurface_win32_set_handle(surface, handle, 0);
1485 #endif
1486 return surface;
1489 DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1490 pixman_format_code_t format,
1491 int linesize, uint8_t *data)
1493 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1495 trace_displaysurface_create_from(surface, width, height, format);
1496 surface->format = format;
1497 surface->image = pixman_image_create_bits(surface->format,
1498 width, height,
1499 (void *)data, linesize);
1500 assert(surface->image != NULL);
1501 #ifdef WIN32
1502 pixman_image_set_destroy_function(surface->image,
1503 win32_pixman_image_destroy, surface);
1504 #endif
1506 return surface;
1509 DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1511 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1513 trace_displaysurface_create_pixman(surface);
1514 surface->format = pixman_image_get_format(image);
1515 surface->image = pixman_image_ref(image);
1517 return surface;
1520 DisplaySurface *qemu_create_placeholder_surface(int w, int h,
1521 const char *msg)
1523 DisplaySurface *surface = qemu_create_displaysurface(w, h);
1524 pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK;
1525 pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY;
1526 pixman_image_t *glyph;
1527 int len, x, y, i;
1529 len = strlen(msg);
1530 x = (w / FONT_WIDTH - len) / 2;
1531 y = (h / FONT_HEIGHT - 1) / 2;
1532 for (i = 0; i < len; i++) {
1533 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1534 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1535 x+i, y, FONT_WIDTH, FONT_HEIGHT);
1536 qemu_pixman_image_unref(glyph);
1538 surface->flags |= QEMU_PLACEHOLDER_FLAG;
1539 return surface;
1542 void qemu_free_displaysurface(DisplaySurface *surface)
1544 if (surface == NULL) {
1545 return;
1547 trace_displaysurface_free(surface);
1548 qemu_pixman_image_unref(surface->image);
1549 g_free(surface);
1552 bool console_has_gl(QemuConsole *con)
1554 return con->gl != NULL;
1557 static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
1559 if (dcl->ops->dpy_has_dmabuf) {
1560 return dcl->ops->dpy_has_dmabuf(dcl);
1563 if (dcl->ops->dpy_gl_scanout_dmabuf) {
1564 return true;
1567 return false;
1570 static bool console_compatible_with(QemuConsole *con,
1571 DisplayChangeListener *dcl, Error **errp)
1573 int flags;
1575 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
1577 if (console_has_gl(con) &&
1578 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
1579 error_setg(errp, "Display %s is incompatible with the GL context",
1580 dcl->ops->dpy_name);
1581 return false;
1584 if (flags & GRAPHIC_FLAGS_GL &&
1585 !console_has_gl(con)) {
1586 error_setg(errp, "The console requires a GL context.");
1587 return false;
1591 if (flags & GRAPHIC_FLAGS_DMABUF &&
1592 !displaychangelistener_has_dmabuf(dcl)) {
1593 error_setg(errp, "The console requires display DMABUF support.");
1594 return false;
1597 return true;
1600 void console_handle_touch_event(QemuConsole *con,
1601 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
1602 uint64_t num_slot,
1603 int width, int height,
1604 double x, double y,
1605 InputMultiTouchType type,
1606 Error **errp)
1608 struct touch_slot *slot;
1609 bool needs_sync = false;
1610 int update;
1611 int i;
1613 if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
1614 error_setg(errp,
1615 "Unexpected touch slot number: % " PRId64" >= %d",
1616 num_slot, INPUT_EVENT_SLOTS_MAX);
1617 return;
1620 slot = &touch_slots[num_slot];
1621 slot->x = x;
1622 slot->y = y;
1624 if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
1625 slot->tracking_id = num_slot;
1628 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
1629 if (i == num_slot) {
1630 update = type;
1631 } else {
1632 update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
1635 slot = &touch_slots[i];
1637 if (slot->tracking_id == -1) {
1638 continue;
1641 if (update == INPUT_MULTI_TOUCH_TYPE_END) {
1642 slot->tracking_id = -1;
1643 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1644 needs_sync = true;
1645 } else {
1646 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1647 qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
1648 qemu_input_queue_mtt_abs(con,
1649 INPUT_AXIS_X, (int) slot->x,
1650 0, width,
1651 i, slot->tracking_id);
1652 qemu_input_queue_mtt_abs(con,
1653 INPUT_AXIS_Y, (int) slot->y,
1654 0, height,
1655 i, slot->tracking_id);
1656 needs_sync = true;
1660 if (needs_sync) {
1661 qemu_input_event_sync();
1665 void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
1667 /* display has opengl support */
1668 assert(con);
1669 if (con->gl) {
1670 error_report("The console already has an OpenGL context.");
1671 exit(1);
1673 con->gl = gl;
1676 static void
1677 dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con)
1679 if (con && con->cursor && dcl->ops->dpy_cursor_define) {
1680 dcl->ops->dpy_cursor_define(dcl, con->cursor);
1682 if (con && dcl->ops->dpy_mouse_set) {
1683 dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
1686 void register_displaychangelistener(DisplayChangeListener *dcl)
1688 QemuConsole *con;
1690 assert(!dcl->ds);
1692 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1693 dcl->ds = get_alloc_displaystate();
1694 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1695 gui_setup_refresh(dcl->ds);
1696 if (dcl->con) {
1697 dcl->con->dcls++;
1698 con = dcl->con;
1699 } else {
1700 con = active_console;
1702 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
1703 if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
1704 dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(con));
1706 text_console_update_cursor(NULL);
1709 void update_displaychangelistener(DisplayChangeListener *dcl,
1710 uint64_t interval)
1712 DisplayState *ds = dcl->ds;
1714 dcl->update_interval = interval;
1715 if (!ds->refreshing && ds->update_interval > interval) {
1716 timer_mod(ds->gui_timer, ds->last_update + interval);
1720 void unregister_displaychangelistener(DisplayChangeListener *dcl)
1722 DisplayState *ds = dcl->ds;
1723 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1724 if (dcl->con) {
1725 dcl->con->dcls--;
1727 QLIST_REMOVE(dcl, next);
1728 dcl->ds = NULL;
1729 gui_setup_refresh(ds);
1732 static void dpy_set_ui_info_timer(void *opaque)
1734 QemuConsole *con = opaque;
1735 uint32_t head = qemu_console_get_head(con);
1737 con->hw_ops->ui_info(con->hw, head, &con->ui_info);
1740 bool dpy_ui_info_supported(QemuConsole *con)
1742 if (con == NULL) {
1743 con = active_console;
1746 return con->hw_ops->ui_info != NULL;
1749 const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
1751 if (con == NULL) {
1752 con = active_console;
1755 return &con->ui_info;
1758 int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
1760 if (con == NULL) {
1761 con = active_console;
1764 if (!dpy_ui_info_supported(con)) {
1765 return -1;
1767 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1768 /* nothing changed -- ignore */
1769 return 0;
1773 * Typically we get a flood of these as the user resizes the window.
1774 * Wait until the dust has settled (one second without updates), then
1775 * go notify the guest.
1777 con->ui_info = *info;
1778 timer_mod(con->ui_timer,
1779 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0));
1780 return 0;
1783 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1785 DisplayState *s = con->ds;
1786 DisplayChangeListener *dcl;
1787 int width = qemu_console_get_width(con, x + w);
1788 int height = qemu_console_get_height(con, y + h);
1790 x = MAX(x, 0);
1791 y = MAX(y, 0);
1792 x = MIN(x, width);
1793 y = MIN(y, height);
1794 w = MIN(w, width - x);
1795 h = MIN(h, height - y);
1797 if (!qemu_console_is_visible(con)) {
1798 return;
1800 dpy_gfx_update_texture(con, con->surface, x, y, w, h);
1801 QLIST_FOREACH(dcl, &s->listeners, next) {
1802 if (con != (dcl->con ? dcl->con : active_console)) {
1803 continue;
1805 if (dcl->ops->dpy_gfx_update) {
1806 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1811 void dpy_gfx_update_full(QemuConsole *con)
1813 int w = qemu_console_get_width(con, 0);
1814 int h = qemu_console_get_height(con, 0);
1816 dpy_gfx_update(con, 0, 0, w, h);
1819 void dpy_gfx_replace_surface(QemuConsole *con,
1820 DisplaySurface *surface)
1822 static const char placeholder_msg[] = "Display output is not active.";
1823 DisplayState *s = con->ds;
1824 DisplaySurface *old_surface = con->surface;
1825 DisplaySurface *new_surface = surface;
1826 DisplayChangeListener *dcl;
1827 int width;
1828 int height;
1830 if (!surface) {
1831 if (old_surface) {
1832 width = surface_width(old_surface);
1833 height = surface_height(old_surface);
1834 } else {
1835 width = 640;
1836 height = 480;
1839 new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
1842 assert(old_surface != new_surface);
1844 con->scanout.kind = SCANOUT_SURFACE;
1845 con->surface = new_surface;
1846 dpy_gfx_create_texture(con, new_surface);
1847 QLIST_FOREACH(dcl, &s->listeners, next) {
1848 if (con != (dcl->con ? dcl->con : active_console)) {
1849 continue;
1851 displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
1853 dpy_gfx_destroy_texture(con, old_surface);
1854 qemu_free_displaysurface(old_surface);
1857 bool dpy_gfx_check_format(QemuConsole *con,
1858 pixman_format_code_t format)
1860 DisplayChangeListener *dcl;
1861 DisplayState *s = con->ds;
1863 QLIST_FOREACH(dcl, &s->listeners, next) {
1864 if (dcl->con && dcl->con != con) {
1865 /* dcl bound to another console -> skip */
1866 continue;
1868 if (dcl->ops->dpy_gfx_check_format) {
1869 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1870 return false;
1872 } else {
1873 /* default is to allow native 32 bpp only */
1874 if (format != qemu_default_pixman_format(32, true)) {
1875 return false;
1879 return true;
1882 static void dpy_refresh(DisplayState *s)
1884 DisplayChangeListener *dcl;
1886 QLIST_FOREACH(dcl, &s->listeners, next) {
1887 if (dcl->ops->dpy_refresh) {
1888 dcl->ops->dpy_refresh(dcl);
1893 void dpy_text_cursor(QemuConsole *con, int x, int y)
1895 DisplayState *s = con->ds;
1896 DisplayChangeListener *dcl;
1898 if (!qemu_console_is_visible(con)) {
1899 return;
1901 QLIST_FOREACH(dcl, &s->listeners, next) {
1902 if (con != (dcl->con ? dcl->con : active_console)) {
1903 continue;
1905 if (dcl->ops->dpy_text_cursor) {
1906 dcl->ops->dpy_text_cursor(dcl, x, y);
1911 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1913 DisplayState *s = con->ds;
1914 DisplayChangeListener *dcl;
1916 if (!qemu_console_is_visible(con)) {
1917 return;
1919 QLIST_FOREACH(dcl, &s->listeners, next) {
1920 if (con != (dcl->con ? dcl->con : active_console)) {
1921 continue;
1923 if (dcl->ops->dpy_text_update) {
1924 dcl->ops->dpy_text_update(dcl, x, y, w, h);
1929 void dpy_text_resize(QemuConsole *con, int w, int h)
1931 DisplayState *s = con->ds;
1932 DisplayChangeListener *dcl;
1934 if (!qemu_console_is_visible(con)) {
1935 return;
1937 QLIST_FOREACH(dcl, &s->listeners, next) {
1938 if (con != (dcl->con ? dcl->con : active_console)) {
1939 continue;
1941 if (dcl->ops->dpy_text_resize) {
1942 dcl->ops->dpy_text_resize(dcl, w, h);
1947 void dpy_mouse_set(QemuConsole *c, int x, int y, int on)
1949 QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
1950 DisplayState *s = c->ds;
1951 DisplayChangeListener *dcl;
1953 con->cursor_x = x;
1954 con->cursor_y = y;
1955 con->cursor_on = on;
1956 if (!qemu_console_is_visible(c)) {
1957 return;
1959 QLIST_FOREACH(dcl, &s->listeners, next) {
1960 if (c != (dcl->con ? dcl->con : active_console)) {
1961 continue;
1963 if (dcl->ops->dpy_mouse_set) {
1964 dcl->ops->dpy_mouse_set(dcl, x, y, on);
1969 void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor)
1971 QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
1972 DisplayState *s = c->ds;
1973 DisplayChangeListener *dcl;
1975 cursor_unref(con->cursor);
1976 con->cursor = cursor_ref(cursor);
1977 if (!qemu_console_is_visible(c)) {
1978 return;
1980 QLIST_FOREACH(dcl, &s->listeners, next) {
1981 if (c != (dcl->con ? dcl->con : active_console)) {
1982 continue;
1984 if (dcl->ops->dpy_cursor_define) {
1985 dcl->ops->dpy_cursor_define(dcl, cursor);
1990 bool dpy_cursor_define_supported(QemuConsole *con)
1992 DisplayState *s = con->ds;
1993 DisplayChangeListener *dcl;
1995 QLIST_FOREACH(dcl, &s->listeners, next) {
1996 if (dcl->ops->dpy_cursor_define) {
1997 return true;
2000 return false;
2003 QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
2004 struct QEMUGLParams *qparams)
2006 assert(con->gl);
2007 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
2010 void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
2012 assert(con->gl);
2013 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
2016 int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
2018 assert(con->gl);
2019 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
2022 void dpy_gl_scanout_disable(QemuConsole *con)
2024 DisplayState *s = con->ds;
2025 DisplayChangeListener *dcl;
2027 if (con->scanout.kind != SCANOUT_SURFACE) {
2028 con->scanout.kind = SCANOUT_NONE;
2030 QLIST_FOREACH(dcl, &s->listeners, next) {
2031 if (con != (dcl->con ? dcl->con : active_console)) {
2032 continue;
2034 if (dcl->ops->dpy_gl_scanout_disable) {
2035 dcl->ops->dpy_gl_scanout_disable(dcl);
2040 void dpy_gl_scanout_texture(QemuConsole *con,
2041 uint32_t backing_id,
2042 bool backing_y_0_top,
2043 uint32_t backing_width,
2044 uint32_t backing_height,
2045 uint32_t x, uint32_t y,
2046 uint32_t width, uint32_t height,
2047 void *d3d_tex2d)
2049 DisplayState *s = con->ds;
2050 DisplayChangeListener *dcl;
2052 con->scanout.kind = SCANOUT_TEXTURE;
2053 con->scanout.texture = (ScanoutTexture) {
2054 backing_id, backing_y_0_top, backing_width, backing_height,
2055 x, y, width, height, d3d_tex2d,
2057 QLIST_FOREACH(dcl, &s->listeners, next) {
2058 if (con != (dcl->con ? dcl->con : active_console)) {
2059 continue;
2061 if (dcl->ops->dpy_gl_scanout_texture) {
2062 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
2063 backing_y_0_top,
2064 backing_width, backing_height,
2065 x, y, width, height,
2066 d3d_tex2d);
2071 void dpy_gl_scanout_dmabuf(QemuConsole *con,
2072 QemuDmaBuf *dmabuf)
2074 DisplayState *s = con->ds;
2075 DisplayChangeListener *dcl;
2077 con->scanout.kind = SCANOUT_DMABUF;
2078 con->scanout.dmabuf = dmabuf;
2079 QLIST_FOREACH(dcl, &s->listeners, next) {
2080 if (con != (dcl->con ? dcl->con : active_console)) {
2081 continue;
2083 if (dcl->ops->dpy_gl_scanout_dmabuf) {
2084 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
2089 void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
2090 bool have_hot, uint32_t hot_x, uint32_t hot_y)
2092 DisplayState *s = con->ds;
2093 DisplayChangeListener *dcl;
2095 QLIST_FOREACH(dcl, &s->listeners, next) {
2096 if (con != (dcl->con ? dcl->con : active_console)) {
2097 continue;
2099 if (dcl->ops->dpy_gl_cursor_dmabuf) {
2100 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf,
2101 have_hot, hot_x, hot_y);
2106 void dpy_gl_cursor_position(QemuConsole *con,
2107 uint32_t pos_x, uint32_t pos_y)
2109 DisplayState *s = con->ds;
2110 DisplayChangeListener *dcl;
2112 QLIST_FOREACH(dcl, &s->listeners, next) {
2113 if (con != (dcl->con ? dcl->con : active_console)) {
2114 continue;
2116 if (dcl->ops->dpy_gl_cursor_position) {
2117 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
2122 void dpy_gl_release_dmabuf(QemuConsole *con,
2123 QemuDmaBuf *dmabuf)
2125 DisplayState *s = con->ds;
2126 DisplayChangeListener *dcl;
2128 QLIST_FOREACH(dcl, &s->listeners, next) {
2129 if (con != (dcl->con ? dcl->con : active_console)) {
2130 continue;
2132 if (dcl->ops->dpy_gl_release_dmabuf) {
2133 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
2138 void dpy_gl_update(QemuConsole *con,
2139 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
2141 DisplayState *s = con->ds;
2142 DisplayChangeListener *dcl;
2144 assert(con->gl);
2146 graphic_hw_gl_block(con, true);
2147 QLIST_FOREACH(dcl, &s->listeners, next) {
2148 if (con != (dcl->con ? dcl->con : active_console)) {
2149 continue;
2151 if (dcl->ops->dpy_gl_update) {
2152 dcl->ops->dpy_gl_update(dcl, x, y, w, h);
2155 graphic_hw_gl_block(con, false);
2158 /***********************************************************/
2159 /* register display */
2161 /* console.c internal use only */
2162 static DisplayState *get_alloc_displaystate(void)
2164 if (!display_state) {
2165 display_state = g_new0(DisplayState, 1);
2167 return display_state;
2171 * Called by main(), after creating QemuConsoles
2172 * and before initializing ui (sdl/vnc/...).
2174 DisplayState *init_displaystate(void)
2176 gchar *name;
2177 QemuConsole *con;
2179 QTAILQ_FOREACH(con, &consoles, next) {
2180 /* Hook up into the qom tree here (not in object_new()), once
2181 * all QemuConsoles are created and the order / numbering
2182 * doesn't change any more */
2183 name = g_strdup_printf("console[%d]", con->index);
2184 object_property_add_child(container_get(object_get_root(), "/backend"),
2185 name, OBJECT(con));
2186 g_free(name);
2189 return display_state;
2192 void graphic_console_set_hwops(QemuConsole *con,
2193 const GraphicHwOps *hw_ops,
2194 void *opaque)
2196 con->hw_ops = hw_ops;
2197 con->hw = opaque;
2200 QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
2201 const GraphicHwOps *hw_ops,
2202 void *opaque)
2204 static const char noinit[] =
2205 "Guest has not initialized the display (yet).";
2206 int width = 640;
2207 int height = 480;
2208 QemuConsole *s;
2209 DisplaySurface *surface;
2211 s = qemu_graphic_console_lookup_unused();
2212 if (s) {
2213 trace_console_gfx_reuse(s->index);
2214 width = qemu_console_get_width(s, 0);
2215 height = qemu_console_get_height(s, 0);
2216 } else {
2217 trace_console_gfx_new();
2218 s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE);
2220 QEMU_GRAPHIC_CONSOLE(s)->head = head;
2221 graphic_console_set_hwops(s, hw_ops, opaque);
2222 if (dev) {
2223 object_property_set_link(OBJECT(s), "device", OBJECT(dev),
2224 &error_abort);
2227 surface = qemu_create_placeholder_surface(width, height, noinit);
2228 dpy_gfx_replace_surface(s, surface);
2229 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2230 graphic_hw_gl_unblock_timer, s);
2231 return s;
2234 static const GraphicHwOps unused_ops = {
2235 /* no callbacks */
2238 void graphic_console_close(QemuConsole *con)
2240 static const char unplugged[] =
2241 "Guest display has been unplugged";
2242 DisplaySurface *surface;
2243 int width = qemu_console_get_width(con, 640);
2244 int height = qemu_console_get_height(con, 480);
2246 trace_console_gfx_close(con->index);
2247 object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
2248 graphic_console_set_hwops(con, &unused_ops, NULL);
2250 if (con->gl) {
2251 dpy_gl_scanout_disable(con);
2253 surface = qemu_create_placeholder_surface(width, height, unplugged);
2254 dpy_gfx_replace_surface(con, surface);
2257 QemuConsole *qemu_console_lookup_by_index(unsigned int index)
2259 QemuConsole *con;
2261 QTAILQ_FOREACH(con, &consoles, next) {
2262 if (con->index == index) {
2263 return con;
2266 return NULL;
2269 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
2271 QemuConsole *con;
2272 Object *obj;
2273 uint32_t h;
2275 QTAILQ_FOREACH(con, &consoles, next) {
2276 obj = object_property_get_link(OBJECT(con),
2277 "device", &error_abort);
2278 if (DEVICE(obj) != dev) {
2279 continue;
2281 h = object_property_get_uint(OBJECT(con),
2282 "head", &error_abort);
2283 if (h != head) {
2284 continue;
2286 return con;
2288 return NULL;
2291 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
2292 uint32_t head, Error **errp)
2294 DeviceState *dev;
2295 QemuConsole *con;
2297 dev = qdev_find_recursive(sysbus_get_default(), device_id);
2298 if (dev == NULL) {
2299 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
2300 "Device '%s' not found", device_id);
2301 return NULL;
2304 con = qemu_console_lookup_by_device(dev, head);
2305 if (con == NULL) {
2306 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
2307 device_id, head);
2308 return NULL;
2311 return con;
2314 static QemuConsole *qemu_graphic_console_lookup_unused(void)
2316 QemuConsole *con;
2317 Object *obj;
2319 QTAILQ_FOREACH(con, &consoles, next) {
2320 if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) {
2321 continue;
2323 obj = object_property_get_link(OBJECT(con),
2324 "device", &error_abort);
2325 if (obj != NULL) {
2326 continue;
2328 return con;
2330 return NULL;
2333 QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
2335 if (con == NULL) {
2336 con = active_console;
2338 return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL;
2341 bool qemu_console_is_visible(QemuConsole *con)
2343 return (con == active_console) || (con->dcls > 0);
2346 bool qemu_console_is_graphic(QemuConsole *con)
2348 if (con == NULL) {
2349 con = active_console;
2351 return con && QEMU_IS_GRAPHIC_CONSOLE(con);
2354 bool qemu_console_is_fixedsize(QemuConsole *con)
2356 if (con == NULL) {
2357 con = active_console;
2359 return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con));
2362 bool qemu_console_is_gl_blocked(QemuConsole *con)
2364 assert(con != NULL);
2365 return con->gl_block;
2368 bool qemu_console_is_multihead(DeviceState *dev)
2370 QemuConsole *con;
2371 Object *obj;
2372 uint32_t f = 0xffffffff;
2373 uint32_t h;
2375 QTAILQ_FOREACH(con, &consoles, next) {
2376 obj = object_property_get_link(OBJECT(con),
2377 "device", &error_abort);
2378 if (DEVICE(obj) != dev) {
2379 continue;
2382 h = object_property_get_uint(OBJECT(con),
2383 "head", &error_abort);
2384 if (f == 0xffffffff) {
2385 f = h;
2386 } else if (h != f) {
2387 return true;
2390 return false;
2393 char *qemu_console_get_label(QemuConsole *con)
2395 if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
2396 QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con);
2397 if (c->device) {
2398 DeviceState *dev;
2399 bool multihead;
2401 dev = DEVICE(c->device);
2402 multihead = qemu_console_is_multihead(dev);
2403 if (multihead) {
2404 return g_strdup_printf("%s.%d", dev->id ?
2405 dev->id :
2406 object_get_typename(c->device),
2407 c->head);
2408 } else {
2409 return g_strdup_printf("%s", dev->id ?
2410 dev->id :
2411 object_get_typename(c->device));
2414 return g_strdup("VGA");
2415 } else if (QEMU_IS_TEXT_CONSOLE(con)) {
2416 QemuTextConsole *c = QEMU_TEXT_CONSOLE(con);
2417 if (c->chr && c->chr->label) {
2418 return g_strdup(c->chr->label);
2422 return g_strdup_printf("vc%d", con->index);
2425 int qemu_console_get_index(QemuConsole *con)
2427 if (con == NULL) {
2428 con = active_console;
2430 return con ? con->index : -1;
2433 uint32_t qemu_console_get_head(QemuConsole *con)
2435 if (con == NULL) {
2436 con = active_console;
2438 if (con == NULL) {
2439 return -1;
2441 if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
2442 return QEMU_GRAPHIC_CONSOLE(con)->head;
2444 return 0;
2447 int qemu_console_get_width(QemuConsole *con, int fallback)
2449 if (con == NULL) {
2450 con = active_console;
2452 if (con == NULL) {
2453 return fallback;
2455 switch (con->scanout.kind) {
2456 case SCANOUT_DMABUF:
2457 return con->scanout.dmabuf->width;
2458 case SCANOUT_TEXTURE:
2459 return con->scanout.texture.width;
2460 case SCANOUT_SURFACE:
2461 return surface_width(con->surface);
2462 default:
2463 return fallback;
2467 int qemu_console_get_height(QemuConsole *con, int fallback)
2469 if (con == NULL) {
2470 con = active_console;
2472 if (con == NULL) {
2473 return fallback;
2475 switch (con->scanout.kind) {
2476 case SCANOUT_DMABUF:
2477 return con->scanout.dmabuf->height;
2478 case SCANOUT_TEXTURE:
2479 return con->scanout.texture.height;
2480 case SCANOUT_SURFACE:
2481 return surface_height(con->surface);
2482 default:
2483 return fallback;
2487 static void vc_chr_accept_input(Chardev *chr)
2489 VCChardev *drv = VC_CHARDEV(chr);
2491 kbd_send_chars(drv->console);
2494 static void vc_chr_set_echo(Chardev *chr, bool echo)
2496 VCChardev *drv = VC_CHARDEV(chr);
2498 drv->console->echo = echo;
2501 int qemu_invalidate_text_consoles(void)
2503 QemuConsole *s;
2504 int count = 0;
2506 QTAILQ_FOREACH(s, &consoles, next) {
2507 if (qemu_console_is_graphic(s) ||
2508 !qemu_console_is_visible(s)) {
2509 continue;
2511 count++;
2512 graphic_hw_invalidate(s);
2515 return count;
2518 static void text_console_update_cursor(void *opaque)
2520 cursor_visible_phase = !cursor_visible_phase;
2522 if (qemu_invalidate_text_consoles()) {
2523 timer_mod(cursor_timer,
2524 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
2528 static void vc_chr_open(Chardev *chr,
2529 ChardevBackend *backend,
2530 bool *be_opened,
2531 Error **errp)
2533 ChardevVC *vc = backend->u.vc.data;
2534 VCChardev *drv = VC_CHARDEV(chr);
2535 QemuTextConsole *s;
2536 unsigned width = 0;
2537 unsigned height = 0;
2539 if (vc->has_width) {
2540 width = vc->width;
2541 } else if (vc->has_cols) {
2542 width = vc->cols * FONT_WIDTH;
2545 if (vc->has_height) {
2546 height = vc->height;
2547 } else if (vc->has_rows) {
2548 height = vc->rows * FONT_HEIGHT;
2551 trace_console_txt_new(width, height);
2552 if (width == 0 || height == 0) {
2553 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE));
2554 width = qemu_console_get_width(NULL, 80 * FONT_WIDTH);
2555 height = qemu_console_get_height(NULL, 24 * FONT_HEIGHT);
2556 } else {
2557 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
2560 dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
2562 s->chr = chr;
2563 drv->console = s;
2565 /* set current text attributes to default */
2566 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
2567 text_console_resize(s);
2569 if (chr->label) {
2570 char *msg;
2572 drv->t_attrib.bgcol = QEMU_COLOR_BLUE;
2573 msg = g_strdup_printf("%s console\r\n", chr->label);
2574 qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
2575 g_free(msg);
2576 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
2579 *be_opened = true;
2582 void qemu_console_resize(QemuConsole *s, int width, int height)
2584 DisplaySurface *surface = qemu_console_surface(s);
2586 assert(QEMU_IS_GRAPHIC_CONSOLE(s));
2588 if ((s->scanout.kind != SCANOUT_SURFACE ||
2589 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) &&
2590 qemu_console_get_width(s, -1) == width &&
2591 qemu_console_get_height(s, -1) == height) {
2592 return;
2595 surface = qemu_create_displaysurface(width, height);
2596 dpy_gfx_replace_surface(s, surface);
2599 DisplaySurface *qemu_console_surface(QemuConsole *console)
2601 switch (console->scanout.kind) {
2602 case SCANOUT_SURFACE:
2603 return console->surface;
2604 default:
2605 return NULL;
2609 PixelFormat qemu_default_pixelformat(int bpp)
2611 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2612 PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2613 return pf;
2616 static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2618 void qemu_display_register(QemuDisplay *ui)
2620 assert(ui->type < DISPLAY_TYPE__MAX);
2621 dpys[ui->type] = ui;
2624 bool qemu_display_find_default(DisplayOptions *opts)
2626 static DisplayType prio[] = {
2627 #if defined(CONFIG_GTK)
2628 DISPLAY_TYPE_GTK,
2629 #endif
2630 #if defined(CONFIG_SDL)
2631 DISPLAY_TYPE_SDL,
2632 #endif
2633 #if defined(CONFIG_COCOA)
2634 DISPLAY_TYPE_COCOA
2635 #endif
2637 int i;
2639 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
2640 if (dpys[prio[i]] == NULL) {
2641 Error *local_err = NULL;
2642 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
2643 if (rv < 0) {
2644 error_report_err(local_err);
2647 if (dpys[prio[i]] == NULL) {
2648 continue;
2650 opts->type = prio[i];
2651 return true;
2653 return false;
2656 void qemu_display_early_init(DisplayOptions *opts)
2658 assert(opts->type < DISPLAY_TYPE__MAX);
2659 if (opts->type == DISPLAY_TYPE_NONE) {
2660 return;
2662 if (dpys[opts->type] == NULL) {
2663 Error *local_err = NULL;
2664 int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
2665 if (rv < 0) {
2666 error_report_err(local_err);
2669 if (dpys[opts->type] == NULL) {
2670 error_report("Display '%s' is not available.",
2671 DisplayType_str(opts->type));
2672 exit(1);
2674 if (dpys[opts->type]->early_init) {
2675 dpys[opts->type]->early_init(opts);
2679 void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2681 assert(opts->type < DISPLAY_TYPE__MAX);
2682 if (opts->type == DISPLAY_TYPE_NONE) {
2683 return;
2685 assert(dpys[opts->type] != NULL);
2686 dpys[opts->type]->init(ds, opts);
2689 void qemu_display_help(void)
2691 int idx;
2693 printf("Available display backend types:\n");
2694 printf("none\n");
2695 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
2696 if (!dpys[idx]) {
2697 Error *local_err = NULL;
2698 int rv = ui_module_load(DisplayType_str(idx), &local_err);
2699 if (rv < 0) {
2700 error_report_err(local_err);
2703 if (dpys[idx]) {
2704 printf("%s\n", DisplayType_str(dpys[idx]->type));
2709 static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
2711 int val;
2712 ChardevVC *vc;
2714 backend->type = CHARDEV_BACKEND_KIND_VC;
2715 vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2716 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
2718 val = qemu_opt_get_number(opts, "width", 0);
2719 if (val != 0) {
2720 vc->has_width = true;
2721 vc->width = val;
2724 val = qemu_opt_get_number(opts, "height", 0);
2725 if (val != 0) {
2726 vc->has_height = true;
2727 vc->height = val;
2730 val = qemu_opt_get_number(opts, "cols", 0);
2731 if (val != 0) {
2732 vc->has_cols = true;
2733 vc->cols = val;
2736 val = qemu_opt_get_number(opts, "rows", 0);
2737 if (val != 0) {
2738 vc->has_rows = true;
2739 vc->rows = val;
2743 static void char_vc_class_init(ObjectClass *oc, void *data)
2745 ChardevClass *cc = CHARDEV_CLASS(oc);
2747 cc->parse = vc_chr_parse;
2748 cc->open = vc_chr_open;
2749 cc->chr_write = vc_chr_write;
2750 cc->chr_accept_input = vc_chr_accept_input;
2751 cc->chr_set_echo = vc_chr_set_echo;
2754 static const TypeInfo char_vc_type_info = {
2755 .name = TYPE_CHARDEV_VC,
2756 .parent = TYPE_CHARDEV,
2757 .instance_size = sizeof(VCChardev),
2758 .class_init = char_vc_class_init,
2761 void qemu_console_early_init(void)
2763 /* set the default vc driver */
2764 if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2765 type_register(&char_vc_type_info);