]> git.proxmox.com Git - mirror_qemu.git/blame - ui/console.c
ui/vc: move text fields to QemuTextConsole
[mirror_qemu.git] / ui / console.c
CommitLineData
e7f0ad58
FB
1/*
2 * QEMU graphical console
5fafdf24 3 *
e7f0ad58 4 * Copyright (c) 2004 Fabrice Bellard
5fafdf24 5 *
e7f0ad58
FB
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
e688df6b 24
e16f4c87 25#include "qemu/osdep.h"
28ecbaee 26#include "ui/console.h"
aa2beaa1 27#include "hw/qdev-core.h"
e688df6b 28#include "qapi/error.h"
9af23989 29#include "qapi/qapi-commands-ui.h"
098d57e7 30#include "qapi/visitor.h"
68ba85ce 31#include "qemu/coroutine.h"
0c9d0641 32#include "qemu/fifo8.h"
4f2c765b 33#include "qemu/error-report.h"
014b00cc 34#include "qemu/main-loop.h"
0b8fa32f 35#include "qemu/module.h"
922a01a0 36#include "qemu/option.h"
1de7afc9 37#include "qemu/timer.h"
014b00cc 38#include "chardev/char.h"
ac86048b 39#include "trace.h"
a77549b3 40#include "exec/memory.h"
db1015e9 41#include "qom/object.h"
e7f0ad58
FB
42
43#define DEFAULT_BACKSCROLL 512
bf1bed81 44#define CONSOLE_CURSOR_PERIOD 500
e7f0ad58 45
6d6f7c28
PB
46typedef 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;
55
3be82c6a
MAL
56#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
57 .fgcol = QEMU_COLOR_WHITE, \
58 .bgcol = QEMU_COLOR_BLACK \
59})
60
e7f0ad58
FB
61typedef struct TextCell {
62 uint8_t ch;
6d6f7c28 63 TextAttributes t_attrib;
e7f0ad58
FB
64} TextCell;
65
66#define MAX_ESC_PARAMS 3
67
68enum TTYState {
69 TTY_STATE_NORM,
70 TTY_STATE_ESC,
71 TTY_STATE_CSI,
72};
73
76ffb0b4 74struct QemuConsole {
95be0669
GH
75 Object parent;
76
f81bdefb 77 int index;
e7f0ad58 78 DisplayState *ds;
321f048d 79 DisplaySurface *surface;
ebced091 80 DisplayScanout scanout;
284d1c6b 81 int dcls;
5e79d516 82 DisplayGLCtx *gl;
a4ddc314 83 int gl_block;
a9b1e471 84 QEMUTimer *gl_unblock_timer;
b3cb21b9 85 int window_id;
76ffb0b4 86
95219897 87 /* Graphic console state. */
aa2beaa1 88 Object *device;
5643706a 89 uint32_t head;
6f90f3d7 90 QemuUIInfo ui_info;
cf1ecc82 91 QEMUTimer *ui_timer;
385ac97f 92 QEMUCursor *cursor;
6effaa16 93 int cursor_x, cursor_y, cursor_on;
380cd056 94 const GraphicHwOps *hw_ops;
95219897 95 void *hw;
76ffb0b4 96
0d9b90ce 97 CoQueue dump_queue;
cd6cd8fa
GH
98
99 QTAILQ_ENTRY(QemuConsole) next;
e7f0ad58
FB
100};
101
c105d60f 102OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT)
e265917c 103
b208f745
MAL
104typedef struct QemuGraphicConsole {
105 QemuConsole parent;
106} QemuGraphicConsole;
107
108typedef QemuConsoleClass QemuGraphicConsoleClass;
109
110#define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console"
111OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE)
112OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE)
113
114#define QEMU_IS_GRAPHIC_CONSOLE(c) \
115 object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE)
116
117typedef struct QemuTextConsole {
118 QemuConsole parent;
b2bb9cc4
MAL
119
120 int width;
121 int height;
122 int total_height;
123 int backscroll_height;
124 int x, y;
125 int y_displayed;
126 int y_base;
127 TextCell *cells;
128 int text_x[2], text_y[2], cursor_invalidate;
129 int echo;
130
131 int update_x0;
132 int update_y0;
133 int update_x1;
134 int update_y1;
135
136 Chardev *chr;
137 /* fifo for key pressed */
138 Fifo8 out_fifo;
b208f745
MAL
139} QemuTextConsole;
140
141typedef QemuConsoleClass QemuTextConsoleClass;
142
143#define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console"
144OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE)
145OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE)
146
147#define QEMU_IS_TEXT_CONSOLE(c) \
148 object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE)
149
150typedef struct QemuFixedTextConsole {
151 QemuTextConsole parent;
152} QemuFixedTextConsole;
153
154typedef QemuTextConsoleClass QemuFixedTextConsoleClass;
155
156#define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console"
157OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE)
158OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE)
159
160#define QEMU_IS_FIXED_TEXT_CONSOLE(c) \
161 object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE)
162
8c63667b
MAL
163struct VCChardev {
164 Chardev parent;
b2bb9cc4 165 QemuTextConsole *console;
6505fd8d
MAL
166
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;
8c63667b
MAL
172};
173typedef struct VCChardev VCChardev;
174
27be5587 175struct DisplayState {
1246b259 176 QEMUTimer *gui_timer;
0f7b2864
GH
177 uint64_t last_update;
178 uint64_t update_interval;
179 bool refreshing;
27be5587
GH
180
181 QLIST_HEAD(, DisplayChangeListener) listeners;
182};
183
98b50080 184static DisplayState *display_state;
76ffb0b4 185static QemuConsole *active_console;
eae3eb3e 186static QTAILQ_HEAD(, QemuConsole) consoles =
cd6cd8fa 187 QTAILQ_HEAD_INITIALIZER(consoles);
aea7947c
GH
188static bool cursor_visible_phase;
189static QEMUTimer *cursor_timer;
e7f0ad58 190
2fd319cf 191static void text_console_do_init(Chardev *chr);
0f7b2864 192static void dpy_refresh(DisplayState *s);
5209089f 193static DisplayState *get_alloc_displaystate(void);
aea7947c
GH
194static void text_console_update_cursor_timer(void);
195static void text_console_update_cursor(void *opaque);
ebced091 196static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
4b7b661d
MAL
197static bool console_compatible_with(QemuConsole *con,
198 DisplayChangeListener *dcl, Error **errp);
f9411aae 199static QemuConsole *qemu_graphic_console_lookup_unused(void);
cfde05d1 200static void dpy_set_ui_info_timer(void *opaque);
64840c66 201
98a9ad90
GH
202static void gui_update(void *opaque)
203{
0f7b2864
GH
204 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
205 uint64_t dcl_interval;
98a9ad90
GH
206 DisplayState *ds = opaque;
207 DisplayChangeListener *dcl;
208
0f7b2864 209 ds->refreshing = true;
98a9ad90 210 dpy_refresh(ds);
0f7b2864 211 ds->refreshing = false;
98a9ad90
GH
212
213 QLIST_FOREACH(dcl, &ds->listeners, next) {
0f7b2864
GH
214 dcl_interval = dcl->update_interval ?
215 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
216 if (interval > dcl_interval) {
217 interval = dcl_interval;
98a9ad90
GH
218 }
219 }
0f7b2864
GH
220 if (ds->update_interval != interval) {
221 ds->update_interval = interval;
222 trace_console_refresh(interval);
223 }
bc72ad67
AB
224 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
225 timer_mod(ds->gui_timer, ds->last_update + interval);
98a9ad90
GH
226}
227
228static void gui_setup_refresh(DisplayState *ds)
229{
230 DisplayChangeListener *dcl;
231 bool need_timer = false;
98a9ad90
GH
232
233 QLIST_FOREACH(dcl, &ds->listeners, next) {
234 if (dcl->ops->dpy_refresh != NULL) {
235 need_timer = true;
236 }
98a9ad90
GH
237 }
238
239 if (need_timer && ds->gui_timer == NULL) {
bc72ad67
AB
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));
98a9ad90
GH
242 }
243 if (!need_timer && ds->gui_timer != NULL) {
bc72ad67 244 timer_free(ds->gui_timer);
98a9ad90
GH
245 ds->gui_timer = NULL;
246 }
98a9ad90
GH
247}
248
4d631621
MAL
249void graphic_hw_update_done(QemuConsole *con)
250{
6fc5183a 251 if (con) {
d6ee15ad 252 qemu_co_enter_all(&con->dump_queue, NULL);
6fc5183a 253 }
4d631621
MAL
254}
255
1dbfa005 256void graphic_hw_update(QemuConsole *con)
95219897 257{
4d631621 258 bool async = false;
1cd8b948 259 con = con ? con : active_console;
1dbfa005 260 if (!con) {
1cd8b948 261 return;
1dbfa005 262 }
1cd8b948 263 if (con->hw_ops->gfx_update) {
380cd056 264 con->hw_ops->gfx_update(con->hw);
4d631621
MAL
265 async = con->hw_ops->gfx_update_async;
266 }
267 if (!async) {
268 graphic_hw_update_done(con);
1dbfa005 269 }
95219897
PB
270}
271
4f2c765b
MAL
272static void graphic_hw_update_bh(void *con)
273{
274 graphic_hw_update(con);
275}
276
277void qemu_console_co_wait_update(QemuConsole *con)
278{
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);
283 }
284 qemu_co_queue_wait(&con->dump_queue, NULL);
285
286}
287
a9b1e471
MAL
288static void graphic_hw_gl_unblock_timer(void *opaque)
289{
290 warn_report("console: no gl-unblock within one second");
291}
292
bba19b88
GH
293void graphic_hw_gl_block(QemuConsole *con, bool block)
294{
a9b1e471 295 uint64_t timeout;
f607867c
GH
296 assert(con != NULL);
297
a4ddc314
MAL
298 if (block) {
299 con->gl_block++;
300 } else {
301 con->gl_block--;
bba19b88 302 }
a4ddc314
MAL
303 assert(con->gl_block >= 0);
304 if (!con->hw_ops->gl_block) {
305 return;
306 }
307 if ((block && con->gl_block != 1) || (!block && con->gl_block != 0)) {
308 return;
309 }
310 con->hw_ops->gl_block(con->hw, block);
a9b1e471
MAL
311
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);
318 }
bba19b88
GH
319}
320
b3cb21b9
ST
321int qemu_console_get_window_id(QemuConsole *con)
322{
323 return con->window_id;
324}
325
326void qemu_console_set_window_id(QemuConsole *con, int window_id)
327{
328 con->window_id = window_id;
329}
330
1dbfa005 331void graphic_hw_invalidate(QemuConsole *con)
95219897 332{
1dbfa005
GH
333 if (!con) {
334 con = active_console;
335 }
380cd056
GH
336 if (con && con->hw_ops->invalidate) {
337 con->hw_ops->invalidate(con->hw);
1dbfa005 338 }
95219897
PB
339}
340
1dbfa005 341void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
4d3b6f6e 342{
1dbfa005
GH
343 if (!con) {
344 con = active_console;
345 }
380cd056
GH
346 if (con && con->hw_ops->text_update) {
347 con->hw_ops->text_update(con->hw, chardata);
348 }
4d3b6f6e
AZ
349}
350
1562e531
GH
351static void vga_fill_rect(QemuConsole *con,
352 int posx, int posy, int width, int height,
e27bd65a 353 pixman_color_t color)
e7f0ad58 354{
1562e531 355 DisplaySurface *surface = qemu_console_surface(con);
68db6dc5
GH
356 pixman_rectangle16_t rect = {
357 .x = posx, .y = posy, .width = width, .height = height
358 };
68db6dc5 359
68db6dc5 360 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
e27bd65a 361 &color, 1, &rect);
e7f0ad58
FB
362}
363
364/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
1562e531
GH
365static void vga_bitblt(QemuConsole *con,
366 int xs, int ys, int xd, int yd, int w, int h)
e7f0ad58 367{
1562e531 368 DisplaySurface *surface = qemu_console_surface(con);
68db6dc5
GH
369
370 pixman_image_composite(PIXMAN_OP_SRC,
371 surface->image, NULL, surface->image,
372 xs, ys, 0, 0, xd, yd, w, h);
e7f0ad58
FB
373}
374
375/***********************************************************/
376/* basic char display */
377
378#define FONT_HEIGHT 16
379#define FONT_WIDTH 8
380
381#include "vgafont.h"
382
e27bd65a
GH
383#define QEMU_RGB(r, g, b) \
384 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
385
386static const pixman_color_t color_table_rgb[2][8] = {
6d6f7c28 387 { /* dark */
4083733d
OH
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 */
6d6f7c28
PB
396 },
397 { /* bright */
4083733d
OH
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 */
6d6f7c28 406 }
e7f0ad58
FB
407};
408
1562e531 409static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
6d6f7c28 410 TextAttributes *t_attrib)
e7f0ad58 411{
7d6ba01c 412 static pixman_image_t *glyphs[256];
1562e531 413 DisplaySurface *surface = qemu_console_surface(s);
e27bd65a 414 pixman_color_t fgcol, bgcol;
6d6f7c28
PB
415
416 if (t_attrib->invers) {
cf6f0548
GH
417 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
418 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
6d6f7c28 419 } else {
cf6f0548
GH
420 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
421 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
6d6f7c28 422 }
e7f0ad58 423
7d6ba01c
GH
424 if (!glyphs[ch]) {
425 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
e7f0ad58 426 }
7d6ba01c 427 qemu_pixman_glyph_render(glyphs[ch], surface->image,
e27bd65a 428 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
e7f0ad58
FB
429}
430
b2bb9cc4 431static void text_console_resize(QemuTextConsole *t)
e7f0ad58 432{
b2bb9cc4 433 QemuConsole *s = QEMU_CONSOLE(t);
e7f0ad58
FB
434 TextCell *cells, *c, *c1;
435 int w1, x, y, last_width;
436
ebced091
MAL
437 assert(s->scanout.kind == SCANOUT_SURFACE);
438
b2bb9cc4
MAL
439 last_width = t->width;
440 t->width = surface_width(s->surface) / FONT_WIDTH;
441 t->height = surface_height(s->surface) / FONT_HEIGHT;
e7f0ad58
FB
442
443 w1 = last_width;
b2bb9cc4
MAL
444 if (t->width < w1)
445 w1 = t->width;
e7f0ad58 446
b2bb9cc4
MAL
447 cells = g_new(TextCell, t->width * t->total_height + 1);
448 for(y = 0; y < t->total_height; y++) {
449 c = &cells[y * t->width];
e7f0ad58 450 if (w1 > 0) {
b2bb9cc4 451 c1 = &t->cells[y * last_width];
e7f0ad58
FB
452 for(x = 0; x < w1; x++) {
453 *c++ = *c1++;
454 }
455 }
b2bb9cc4 456 for(x = w1; x < t->width; x++) {
e7f0ad58 457 c->ch = ' ';
3be82c6a 458 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
e7f0ad58
FB
459 c++;
460 }
461 }
b2bb9cc4
MAL
462 g_free(t->cells);
463 t->cells = cells;
e7f0ad58
FB
464}
465
b2bb9cc4 466static void invalidate_xy(QemuTextConsole *s, int x, int y)
14778c20 467{
b2bb9cc4 468 if (!qemu_console_is_visible(QEMU_CONSOLE(s))) {
b35e3ba0
GH
469 return;
470 }
14778c20
PB
471 if (s->update_x0 > x * FONT_WIDTH)
472 s->update_x0 = x * FONT_WIDTH;
473 if (s->update_y0 > y * FONT_HEIGHT)
474 s->update_y0 = y * FONT_HEIGHT;
475 if (s->update_x1 < (x + 1) * FONT_WIDTH)
476 s->update_x1 = (x + 1) * FONT_WIDTH;
477 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
478 s->update_y1 = (y + 1) * FONT_HEIGHT;
479}
480
4c946b7f 481static void vc_update_xy(VCChardev *vc, int x, int y)
e7f0ad58 482{
b2bb9cc4 483 QemuTextConsole *s = vc->console;
e7f0ad58
FB
484 TextCell *c;
485 int y1, y2;
486
d7c634aa
MAL
487 s->text_x[0] = MIN(s->text_x[0], x);
488 s->text_x[1] = MAX(s->text_x[1], x);
489 s->text_y[0] = MIN(s->text_y[0], y);
490 s->text_y[1] = MAX(s->text_y[1], y);
4d3b6f6e 491
b35e3ba0
GH
492 y1 = (s->y_base + y) % s->total_height;
493 y2 = y1 - s->y_displayed;
494 if (y2 < 0) {
495 y2 += s->total_height;
496 }
497 if (y2 < s->height) {
5b8541c6
GH
498 if (x >= s->width) {
499 x = s->width - 1;
500 }
b35e3ba0 501 c = &s->cells[y1 * s->width + x];
b2bb9cc4 502 vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch,
b35e3ba0
GH
503 &(c->t_attrib));
504 invalidate_xy(s, x, y2);
e7f0ad58
FB
505 }
506}
507
b2bb9cc4 508static void console_show_cursor(QemuTextConsole *s, int show)
e7f0ad58
FB
509{
510 TextCell *c;
511 int y, y1;
1562e531 512 int x = s->x;
e7f0ad58 513
17742278 514 s->cursor_invalidate = 1;
4d3b6f6e 515
b35e3ba0
GH
516 if (x >= s->width) {
517 x = s->width - 1;
518 }
519 y1 = (s->y_base + s->y) % s->total_height;
520 y = y1 - s->y_displayed;
521 if (y < 0) {
522 y += s->total_height;
523 }
524 if (y < s->height) {
525 c = &s->cells[y1 * s->width + x];
aea7947c 526 if (show && cursor_visible_phase) {
3be82c6a 527 TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
b35e3ba0 528 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
b2bb9cc4 529 vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &t_attrib);
b35e3ba0 530 } else {
b2bb9cc4 531 vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib));
e7f0ad58 532 }
b35e3ba0 533 invalidate_xy(s, x, y);
e7f0ad58
FB
534 }
535}
536
b2bb9cc4 537static void console_refresh(QemuTextConsole *s)
e7f0ad58 538{
b2bb9cc4 539 DisplaySurface *surface = qemu_console_surface(QEMU_CONSOLE(s));
e7f0ad58
FB
540 TextCell *c;
541 int x, y, y1;
542
17742278
MAL
543 s->text_x[0] = 0;
544 s->text_y[0] = 0;
545 s->text_x[1] = s->width - 1;
546 s->text_y[1] = s->height - 1;
547 s->cursor_invalidate = 1;
e7f0ad58 548
b2bb9cc4 549 vga_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface),
4083733d 550 color_table_rgb[0][QEMU_COLOR_BLACK]);
b35e3ba0
GH
551 y1 = s->y_displayed;
552 for (y = 0; y < s->height; y++) {
553 c = s->cells + y1 * s->width;
554 for (x = 0; x < s->width; x++) {
b2bb9cc4 555 vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch,
b35e3ba0
GH
556 &(c->t_attrib));
557 c++;
558 }
559 if (++y1 == s->total_height) {
560 y1 = 0;
e7f0ad58 561 }
e7f0ad58 562 }
b35e3ba0 563 console_show_cursor(s, 1);
b2bb9cc4 564 dpy_gfx_update(QEMU_CONSOLE(s), 0, 0,
b35e3ba0 565 surface_width(surface), surface_height(surface));
e7f0ad58
FB
566}
567
b2bb9cc4 568static void console_scroll(QemuTextConsole *s, int ydelta)
e7f0ad58 569{
e7f0ad58 570 int i, y1;
3b46e624 571
e7f0ad58
FB
572 if (ydelta > 0) {
573 for(i = 0; i < ydelta; i++) {
574 if (s->y_displayed == s->y_base)
575 break;
576 if (++s->y_displayed == s->total_height)
577 s->y_displayed = 0;
578 }
579 } else {
580 ydelta = -ydelta;
581 i = s->backscroll_height;
582 if (i > s->total_height - s->height)
583 i = s->total_height - s->height;
584 y1 = s->y_base - i;
585 if (y1 < 0)
586 y1 += s->total_height;
587 for(i = 0; i < ydelta; i++) {
588 if (s->y_displayed == y1)
589 break;
590 if (--s->y_displayed < 0)
591 s->y_displayed = s->total_height - 1;
592 }
593 }
594 console_refresh(s);
595}
596
4c946b7f 597static void vc_put_lf(VCChardev *vc)
e7f0ad58 598{
b2bb9cc4 599 QemuTextConsole *s = vc->console;
e7f0ad58
FB
600 TextCell *c;
601 int x, y1;
602
e7f0ad58
FB
603 s->y++;
604 if (s->y >= s->height) {
605 s->y = s->height - 1;
6d6f7c28 606
e7f0ad58
FB
607 if (s->y_displayed == s->y_base) {
608 if (++s->y_displayed == s->total_height)
609 s->y_displayed = 0;
610 }
611 if (++s->y_base == s->total_height)
612 s->y_base = 0;
613 if (s->backscroll_height < s->total_height)
614 s->backscroll_height++;
615 y1 = (s->y_base + s->height - 1) % s->total_height;
616 c = &s->cells[y1 * s->width];
617 for(x = 0; x < s->width; x++) {
618 c->ch = ' ';
3be82c6a 619 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
e7f0ad58
FB
620 c++;
621 }
b35e3ba0 622 if (s->y_displayed == s->y_base) {
17742278
MAL
623 s->text_x[0] = 0;
624 s->text_y[0] = 0;
625 s->text_x[1] = s->width - 1;
626 s->text_y[1] = s->height - 1;
4d3b6f6e 627
b2bb9cc4 628 vga_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0,
b35e3ba0
GH
629 s->width * FONT_WIDTH,
630 (s->height - 1) * FONT_HEIGHT);
b2bb9cc4 631 vga_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT,
b35e3ba0 632 s->width * FONT_WIDTH, FONT_HEIGHT,
3be82c6a 633 color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
b35e3ba0
GH
634 s->update_x0 = 0;
635 s->update_y0 = 0;
636 s->update_x1 = s->width * FONT_WIDTH;
637 s->update_y1 = s->height * FONT_HEIGHT;
e7f0ad58
FB
638 }
639 }
640}
641
6d6f7c28
PB
642/* Set console attributes depending on the current escape codes.
643 * NOTE: I know this code is not very efficient (checking every color for it
644 * self) but it is more readable and better maintainable.
645 */
4c946b7f 646static void vc_handle_escape(VCChardev *vc)
6d6f7c28
PB
647{
648 int i;
649
6505fd8d
MAL
650 for (i = 0; i < vc->nb_esc_params; i++) {
651 switch (vc->esc_params[i]) {
6d6f7c28 652 case 0: /* reset all console attributes to default */
6505fd8d 653 vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
6d6f7c28
PB
654 break;
655 case 1:
6505fd8d 656 vc->t_attrib.bold = 1;
6d6f7c28
PB
657 break;
658 case 4:
6505fd8d 659 vc->t_attrib.uline = 1;
6d6f7c28
PB
660 break;
661 case 5:
6505fd8d 662 vc->t_attrib.blink = 1;
6d6f7c28
PB
663 break;
664 case 7:
6505fd8d 665 vc->t_attrib.invers = 1;
6d6f7c28
PB
666 break;
667 case 8:
6505fd8d 668 vc->t_attrib.unvisible = 1;
6d6f7c28
PB
669 break;
670 case 22:
6505fd8d 671 vc->t_attrib.bold = 0;
6d6f7c28
PB
672 break;
673 case 24:
6505fd8d 674 vc->t_attrib.uline = 0;
6d6f7c28
PB
675 break;
676 case 25:
6505fd8d 677 vc->t_attrib.blink = 0;
6d6f7c28
PB
678 break;
679 case 27:
6505fd8d 680 vc->t_attrib.invers = 0;
6d6f7c28
PB
681 break;
682 case 28:
6505fd8d 683 vc->t_attrib.unvisible = 0;
6d6f7c28
PB
684 break;
685 /* set foreground color */
686 case 30:
6505fd8d 687 vc->t_attrib.fgcol = QEMU_COLOR_BLACK;
6d6f7c28
PB
688 break;
689 case 31:
6505fd8d 690 vc->t_attrib.fgcol = QEMU_COLOR_RED;
6d6f7c28
PB
691 break;
692 case 32:
6505fd8d 693 vc->t_attrib.fgcol = QEMU_COLOR_GREEN;
6d6f7c28
PB
694 break;
695 case 33:
6505fd8d 696 vc->t_attrib.fgcol = QEMU_COLOR_YELLOW;
6d6f7c28
PB
697 break;
698 case 34:
6505fd8d 699 vc->t_attrib.fgcol = QEMU_COLOR_BLUE;
6d6f7c28
PB
700 break;
701 case 35:
6505fd8d 702 vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
6d6f7c28
PB
703 break;
704 case 36:
6505fd8d 705 vc->t_attrib.fgcol = QEMU_COLOR_CYAN;
6d6f7c28
PB
706 break;
707 case 37:
6505fd8d 708 vc->t_attrib.fgcol = QEMU_COLOR_WHITE;
6d6f7c28
PB
709 break;
710 /* set background color */
711 case 40:
6505fd8d 712 vc->t_attrib.bgcol = QEMU_COLOR_BLACK;
6d6f7c28
PB
713 break;
714 case 41:
6505fd8d 715 vc->t_attrib.bgcol = QEMU_COLOR_RED;
6d6f7c28
PB
716 break;
717 case 42:
6505fd8d 718 vc->t_attrib.bgcol = QEMU_COLOR_GREEN;
6d6f7c28
PB
719 break;
720 case 43:
6505fd8d 721 vc->t_attrib.bgcol = QEMU_COLOR_YELLOW;
6d6f7c28
PB
722 break;
723 case 44:
6505fd8d 724 vc->t_attrib.bgcol = QEMU_COLOR_BLUE;
6d6f7c28
PB
725 break;
726 case 45:
6505fd8d 727 vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
6d6f7c28
PB
728 break;
729 case 46:
6505fd8d 730 vc->t_attrib.bgcol = QEMU_COLOR_CYAN;
6d6f7c28
PB
731 break;
732 case 47:
6505fd8d 733 vc->t_attrib.bgcol = QEMU_COLOR_WHITE;
6d6f7c28
PB
734 break;
735 }
736 }
737}
738
4c946b7f 739static void vc_clear_xy(VCChardev *vc, int x, int y)
adb47967 740{
b2bb9cc4 741 QemuTextConsole *s = vc->console;
adb47967 742 int y1 = (s->y_base + y) % s->total_height;
5b8541c6
GH
743 if (x >= s->width) {
744 x = s->width - 1;
745 }
adb47967
TS
746 TextCell *c = &s->cells[y1 * s->width + x];
747 c->ch = ' ';
3be82c6a 748 c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
4c946b7f 749 vc_update_xy(vc, x, y);
adb47967
TS
750}
751
4c946b7f 752static void vc_put_one(VCChardev *vc, int ch)
58aa7d8e 753{
b2bb9cc4 754 QemuTextConsole *s = vc->console;
58aa7d8e
RK
755 TextCell *c;
756 int y1;
757 if (s->x >= s->width) {
758 /* line wrap */
759 s->x = 0;
4c946b7f 760 vc_put_lf(vc);
58aa7d8e
RK
761 }
762 y1 = (s->y_base + s->y) % s->total_height;
763 c = &s->cells[y1 * s->width + s->x];
764 c->ch = ch;
6505fd8d 765 c->t_attrib = vc->t_attrib;
4c946b7f 766 vc_update_xy(vc, s->x, s->y);
58aa7d8e
RK
767 s->x++;
768}
769
4c946b7f 770static void vc_respond_str(VCChardev *vc, const char *buf)
58aa7d8e
RK
771{
772 while (*buf) {
4c946b7f 773 vc_put_one(vc, *buf);
58aa7d8e
RK
774 buf++;
775 }
776}
777
3eea5498 778/* set cursor, checking bounds */
4c946b7f 779static void vc_set_cursor(VCChardev *vc, int x, int y)
3eea5498 780{
b2bb9cc4 781 QemuTextConsole *s = vc->console;
4c946b7f 782
3eea5498
IC
783 if (x < 0) {
784 x = 0;
785 }
786 if (y < 0) {
787 y = 0;
788 }
789 if (y >= s->height) {
790 y = s->height - 1;
791 }
792 if (x >= s->width) {
793 x = s->width - 1;
794 }
795
796 s->x = x;
797 s->y = y;
798}
799
4c946b7f 800static void vc_putchar(VCChardev *vc, int ch)
e7f0ad58 801{
b2bb9cc4 802 QemuTextConsole *s = vc->console;
58aa7d8e 803 int i;
adb47967 804 int x, y;
58aa7d8e 805 char response[40];
e7f0ad58 806
6505fd8d 807 switch(vc->state) {
e7f0ad58
FB
808 case TTY_STATE_NORM:
809 switch(ch) {
6d6f7c28 810 case '\r': /* carriage return */
e7f0ad58
FB
811 s->x = 0;
812 break;
6d6f7c28 813 case '\n': /* newline */
4c946b7f 814 vc_put_lf(vc);
e7f0ad58 815 break;
6d6f7c28 816 case '\b': /* backspace */
5fafdf24 817 if (s->x > 0)
e15d7371 818 s->x--;
6d6f7c28
PB
819 break;
820 case '\t': /* tabspace */
821 if (s->x + (8 - (s->x % 8)) > s->width) {
bd468840 822 s->x = 0;
4c946b7f 823 vc_put_lf(vc);
6d6f7c28
PB
824 } else {
825 s->x = s->x + (8 - (s->x % 8));
826 }
827 break;
828 case '\a': /* alert aka. bell */
829 /* TODO: has to be implemented */
830 break;
adb47967
TS
831 case 14:
832 /* SI (shift in), character set 0 (ignored) */
833 break;
834 case 15:
835 /* SO (shift out), character set 1 (ignored) */
836 break;
6d6f7c28 837 case 27: /* esc (introducing an escape sequence) */
6505fd8d 838 vc->state = TTY_STATE_ESC;
e7f0ad58
FB
839 break;
840 default:
4c946b7f 841 vc_put_one(vc, ch);
e7f0ad58
FB
842 break;
843 }
844 break;
6d6f7c28 845 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
e7f0ad58
FB
846 if (ch == '[') {
847 for(i=0;i<MAX_ESC_PARAMS;i++)
6505fd8d
MAL
848 vc->esc_params[i] = 0;
849 vc->nb_esc_params = 0;
850 vc->state = TTY_STATE_CSI;
e7f0ad58 851 } else {
6505fd8d 852 vc->state = TTY_STATE_NORM;
e7f0ad58
FB
853 }
854 break;
6d6f7c28 855 case TTY_STATE_CSI: /* handle escape sequence parameters */
e7f0ad58 856 if (ch >= '0' && ch <= '9') {
6505fd8d
MAL
857 if (vc->nb_esc_params < MAX_ESC_PARAMS) {
858 int *param = &vc->esc_params[vc->nb_esc_params];
c10600af
LE
859 int digit = (ch - '0');
860
861 *param = (*param <= (INT_MAX - digit) / 10) ?
862 *param * 10 + digit : INT_MAX;
e7f0ad58
FB
863 }
864 } else {
6505fd8d
MAL
865 if (vc->nb_esc_params < MAX_ESC_PARAMS)
866 vc->nb_esc_params++;
7c336f9f 867 if (ch == ';' || ch == '?') {
e7f0ad58 868 break;
7c336f9f 869 }
6505fd8d
MAL
870 trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1],
871 ch, vc->nb_esc_params);
872 vc->state = TTY_STATE_NORM;
e7f0ad58 873 switch(ch) {
adb47967
TS
874 case 'A':
875 /* move cursor up */
6505fd8d
MAL
876 if (vc->esc_params[0] == 0) {
877 vc->esc_params[0] = 1;
adb47967 878 }
6505fd8d 879 vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]);
adb47967
TS
880 break;
881 case 'B':
882 /* move cursor down */
6505fd8d
MAL
883 if (vc->esc_params[0] == 0) {
884 vc->esc_params[0] = 1;
adb47967 885 }
6505fd8d 886 vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]);
e7f0ad58
FB
887 break;
888 case 'C':
adb47967 889 /* move cursor right */
6505fd8d
MAL
890 if (vc->esc_params[0] == 0) {
891 vc->esc_params[0] = 1;
adb47967 892 }
6505fd8d 893 vc_set_cursor(vc, s->x + vc->esc_params[0], s->y);
e7f0ad58 894 break;
adb47967
TS
895 case 'D':
896 /* move cursor left */
6505fd8d
MAL
897 if (vc->esc_params[0] == 0) {
898 vc->esc_params[0] = 1;
adb47967 899 }
6505fd8d 900 vc_set_cursor(vc, s->x - vc->esc_params[0], s->y);
adb47967
TS
901 break;
902 case 'G':
903 /* move cursor to column */
6505fd8d 904 vc_set_cursor(vc, vc->esc_params[0] - 1, s->y);
adb47967
TS
905 break;
906 case 'f':
907 case 'H':
908 /* move cursor to row, column */
6505fd8d 909 vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1);
adb47967
TS
910 break;
911 case 'J':
6505fd8d 912 switch (vc->esc_params[0]) {
adb47967
TS
913 case 0:
914 /* clear to end of screen */
915 for (y = s->y; y < s->height; y++) {
916 for (x = 0; x < s->width; x++) {
917 if (y == s->y && x < s->x) {
918 continue;
919 }
4c946b7f 920 vc_clear_xy(vc, x, y);
adb47967
TS
921 }
922 }
923 break;
924 case 1:
925 /* clear from beginning of screen */
926 for (y = 0; y <= s->y; y++) {
927 for (x = 0; x < s->width; x++) {
928 if (y == s->y && x > s->x) {
929 break;
930 }
4c946b7f 931 vc_clear_xy(vc, x, y);
adb47967
TS
932 }
933 }
934 break;
935 case 2:
936 /* clear entire screen */
937 for (y = 0; y <= s->height; y++) {
938 for (x = 0; x < s->width; x++) {
4c946b7f 939 vc_clear_xy(vc, x, y);
adb47967
TS
940 }
941 }
f94a950f 942 break;
adb47967 943 }
95d8f9f4 944 break;
e7f0ad58 945 case 'K':
6505fd8d 946 switch (vc->esc_params[0]) {
adb47967 947 case 0:
f94a950f
MA
948 /* clear to eol */
949 for(x = s->x; x < s->width; x++) {
4c946b7f 950 vc_clear_xy(vc, x, s->y);
f94a950f
MA
951 }
952 break;
adb47967
TS
953 case 1:
954 /* clear from beginning of line */
5b8541c6 955 for (x = 0; x <= s->x && x < s->width; x++) {
4c946b7f 956 vc_clear_xy(vc, x, s->y);
adb47967
TS
957 }
958 break;
959 case 2:
960 /* clear entire line */
961 for(x = 0; x < s->width; x++) {
4c946b7f 962 vc_clear_xy(vc, x, s->y);
adb47967 963 }
f94a950f
MA
964 break;
965 }
adb47967
TS
966 break;
967 case 'm':
4c946b7f 968 vc_handle_escape(vc);
f94a950f 969 break;
adb47967 970 case 'n':
6505fd8d 971 switch (vc->esc_params[0]) {
58aa7d8e
RK
972 case 5:
973 /* report console status (always succeed)*/
4c946b7f 974 vc_respond_str(vc, "\033[0n");
58aa7d8e
RK
975 break;
976 case 6:
977 /* report cursor position */
978 sprintf(response, "\033[%d;%dR",
979 (s->y_base + s->y) % s->total_height + 1,
980 s->x + 1);
4c946b7f 981 vc_respond_str(vc, response);
58aa7d8e
RK
982 break;
983 }
adb47967
TS
984 break;
985 case 's':
986 /* save cursor position */
6505fd8d
MAL
987 vc->x_saved = s->x;
988 vc->y_saved = s->y;
adb47967
TS
989 break;
990 case 'u':
991 /* restore cursor position */
6505fd8d
MAL
992 s->x = vc->x_saved;
993 s->y = vc->y_saved;
adb47967
TS
994 break;
995 default:
5d28b0e9 996 trace_console_putchar_unhandled(ch);
adb47967
TS
997 break;
998 }
999 break;
e7f0ad58
FB
1000 }
1001 }
1002}
1003
26b032b9 1004static void displaychangelistener_gfx_switch(DisplayChangeListener *dcl,
c84ab0a5
MAL
1005 struct DisplaySurface *new_surface,
1006 bool update)
26b032b9
MAL
1007{
1008 if (dcl->ops->dpy_gfx_switch) {
1009 dcl->ops->dpy_gfx_switch(dcl, new_surface);
1010 }
c84ab0a5
MAL
1011
1012 if (update && dcl->ops->dpy_gfx_update) {
1013 dcl->ops->dpy_gfx_update(dcl, 0, 0,
1014 surface_width(new_surface),
1015 surface_height(new_surface));
1016 }
26b032b9
MAL
1017}
1018
589089fe
MAL
1019static void dpy_gfx_create_texture(QemuConsole *con, DisplaySurface *surface)
1020{
1021 if (con->gl && con->gl->ops->dpy_gl_ctx_create_texture) {
1022 con->gl->ops->dpy_gl_ctx_create_texture(con->gl, surface);
1023 }
1024}
1025
1026static void dpy_gfx_destroy_texture(QemuConsole *con, DisplaySurface *surface)
1027{
1028 if (con->gl && con->gl->ops->dpy_gl_ctx_destroy_texture) {
1029 con->gl->ops->dpy_gl_ctx_destroy_texture(con->gl, surface);
1030 }
1031}
1032
1033static void dpy_gfx_update_texture(QemuConsole *con, DisplaySurface *surface,
1034 int x, int y, int w, int h)
1035{
1036 if (con->gl && con->gl->ops->dpy_gl_ctx_update_texture) {
1037 con->gl->ops->dpy_gl_ctx_update_texture(con->gl, surface, x, y, w, h);
1038 }
1039}
26b032b9 1040
ebced091 1041static void displaychangelistener_display_console(DisplayChangeListener *dcl,
4b7b661d
MAL
1042 QemuConsole *con,
1043 Error **errp)
ebced091
MAL
1044{
1045 static const char nodev[] =
1046 "This VM has no graphic display device.";
1047 static DisplaySurface *dummy;
1048
4b7b661d 1049 if (!con || !console_compatible_with(con, dcl, errp)) {
ebced091
MAL
1050 if (!dummy) {
1051 dummy = qemu_create_placeholder_surface(640, 480, nodev);
1052 }
589089fe
MAL
1053 if (con) {
1054 dpy_gfx_create_texture(con, dummy);
1055 }
c84ab0a5 1056 displaychangelistener_gfx_switch(dcl, dummy, TRUE);
ebced091
MAL
1057 return;
1058 }
1059
e1c676a2
MAL
1060 dpy_gfx_create_texture(con, con->surface);
1061 displaychangelistener_gfx_switch(dcl, con->surface,
1062 con->scanout.kind == SCANOUT_SURFACE);
1063
ebced091
MAL
1064 if (con->scanout.kind == SCANOUT_DMABUF &&
1065 displaychangelistener_has_dmabuf(dcl)) {
1066 dcl->ops->dpy_gl_scanout_dmabuf(dcl, con->scanout.dmabuf);
1067 } else if (con->scanout.kind == SCANOUT_TEXTURE &&
1068 dcl->ops->dpy_gl_scanout_texture) {
1069 dcl->ops->dpy_gl_scanout_texture(dcl,
1070 con->scanout.texture.backing_id,
1071 con->scanout.texture.backing_y_0_top,
1072 con->scanout.texture.backing_width,
1073 con->scanout.texture.backing_height,
1074 con->scanout.texture.x,
1075 con->scanout.texture.y,
1076 con->scanout.texture.width,
bf41ab61
MAL
1077 con->scanout.texture.height,
1078 con->scanout.texture.d3d_tex2d);
ebced091 1079 }
ebced091
MAL
1080}
1081
e7f0ad58
FB
1082void console_select(unsigned int index)
1083{
284d1c6b 1084 DisplayChangeListener *dcl;
76ffb0b4 1085 QemuConsole *s;
6d6f7c28 1086
437fe106 1087 trace_console_select(index);
284d1c6b 1088 s = qemu_console_lookup_by_index(index);
e7f0ad58 1089 if (s) {
7d957bd8 1090 DisplayState *ds = s->ds;
bf1bed81 1091
e7f0ad58 1092 active_console = s;
074b2409
MAL
1093 QLIST_FOREACH (dcl, &ds->listeners, next) {
1094 if (dcl->con != NULL) {
1095 continue;
2e5567c9 1096 }
074b2409 1097 displaychangelistener_display_console(dcl, s, NULL);
a93a4a22 1098 }
b2bb9cc4
MAL
1099
1100 if (QEMU_IS_TEXT_CONSOLE(s)) {
1101 dpy_text_resize(s, QEMU_TEXT_CONSOLE(s)->width, QEMU_TEXT_CONSOLE(s)->height);
1102 text_console_update_cursor(NULL);
1103 }
e7f0ad58
FB
1104 }
1105}
1106
777357d7 1107#define TYPE_CHARDEV_VC "chardev-vc"
8110fa1d
EH
1108DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
1109 TYPE_CHARDEV_VC)
777357d7 1110
5bf5adae 1111static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
e7f0ad58 1112{
777357d7 1113 VCChardev *drv = VC_CHARDEV(chr);
b2bb9cc4 1114 QemuTextConsole *s = drv->console;
e7f0ad58
FB
1115 int i;
1116
14778c20
PB
1117 s->update_x0 = s->width * FONT_WIDTH;
1118 s->update_y0 = s->height * FONT_HEIGHT;
1119 s->update_x1 = 0;
1120 s->update_y1 = 0;
e7f0ad58
FB
1121 console_show_cursor(s, 0);
1122 for(i = 0; i < len; i++) {
4c946b7f 1123 vc_putchar(drv, buf[i]);
e7f0ad58
FB
1124 }
1125 console_show_cursor(s, 1);
bc9b8bc9 1126 if (s->update_x0 < s->update_x1) {
b2bb9cc4 1127 dpy_gfx_update(QEMU_CONSOLE(s), s->update_x0, s->update_y0,
a93a4a22
GH
1128 s->update_x1 - s->update_x0,
1129 s->update_y1 - s->update_y0);
14778c20 1130 }
e7f0ad58
FB
1131 return len;
1132}
1133
b2bb9cc4 1134static void kbd_send_chars(QemuTextConsole *s)
e15d7371 1135{
0c9d0641 1136 uint32_t len, avail;
3b46e624 1137
909cda12 1138 len = qemu_chr_be_can_write(s->chr);
0c9d0641 1139 avail = fifo8_num_used(&s->out_fifo);
ec222519 1140 while (len > 0 && avail > 0) {
0c9d0641
VR
1141 const uint8_t *buf;
1142 uint32_t size;
1143
ec222519 1144 buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &size);
cf6280b9 1145 qemu_chr_be_write(s->chr, buf, size);
ec222519 1146 len = qemu_chr_be_can_write(s->chr);
0c9d0641 1147 avail -= size;
e15d7371 1148 }
e15d7371
FB
1149}
1150
e7f0ad58 1151/* called when an ascii key is pressed */
b2bb9cc4 1152void kbd_put_keysym_console(QemuConsole *con, int keysym)
e7f0ad58 1153{
b2bb9cc4 1154 QemuTextConsole *s = (QemuTextConsole *)object_dynamic_cast(OBJECT(con), TYPE_QEMU_TEXT_CONSOLE);
e7f0ad58
FB
1155 uint8_t buf[16], *q;
1156 int c;
0c9d0641 1157 uint32_t num_free;
e7f0ad58 1158
b2bb9cc4 1159 if (!s)
e7f0ad58
FB
1160 return;
1161
1162 switch(keysym) {
1163 case QEMU_KEY_CTRL_UP:
81c0d5a6 1164 console_scroll(s, -1);
e7f0ad58
FB
1165 break;
1166 case QEMU_KEY_CTRL_DOWN:
81c0d5a6 1167 console_scroll(s, 1);
e7f0ad58
FB
1168 break;
1169 case QEMU_KEY_CTRL_PAGEUP:
81c0d5a6 1170 console_scroll(s, -10);
e7f0ad58
FB
1171 break;
1172 case QEMU_KEY_CTRL_PAGEDOWN:
81c0d5a6 1173 console_scroll(s, 10);
e7f0ad58
FB
1174 break;
1175 default:
e15d7371
FB
1176 /* convert the QEMU keysym to VT100 key string */
1177 q = buf;
1178 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1179 *q++ = '\033';
1180 *q++ = '[';
1181 c = keysym - 0xe100;
1182 if (c >= 10)
1183 *q++ = '0' + (c / 10);
1184 *q++ = '0' + (c % 10);
1185 *q++ = '~';
1186 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1187 *q++ = '\033';
1188 *q++ = '[';
1189 *q++ = keysym & 0xff;
4104833f 1190 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
f1f7a1e2 1191 qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
4104833f 1192 *q++ = '\n';
e15d7371 1193 } else {
4104833f
PB
1194 *q++ = keysym;
1195 }
1196 if (s->echo) {
f1f7a1e2 1197 qemu_chr_write(s->chr, buf, q - buf, true);
e15d7371 1198 }
014b00cc
VR
1199 num_free = fifo8_num_free(&s->out_fifo);
1200 fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
1201 kbd_send_chars(s);
e7f0ad58
FB
1202 break;
1203 }
1204}
1205
7fb1cf16 1206static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
50ef4679
GH
1207 [Q_KEY_CODE_UP] = QEMU_KEY_UP,
1208 [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN,
1209 [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT,
1210 [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT,
1211 [Q_KEY_CODE_HOME] = QEMU_KEY_HOME,
1212 [Q_KEY_CODE_END] = QEMU_KEY_END,
1213 [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP,
1214 [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN,
1215 [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
df6322a8 1216 [Q_KEY_CODE_TAB] = QEMU_KEY_TAB,
344aa283 1217 [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
50ef4679
GH
1218};
1219
da024b1e
GH
1220static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1221 [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP,
1222 [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN,
1223 [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT,
1224 [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT,
1225 [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME,
1226 [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END,
1227 [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP,
1228 [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN,
1229};
1230
1231bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
50ef4679
GH
1232{
1233 int keysym;
1234
da024b1e 1235 keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
50ef4679
GH
1236 if (keysym == 0) {
1237 return false;
1238 }
1239 kbd_put_keysym_console(s, keysym);
1240 return true;
1241}
1242
bdef9724
GH
1243void kbd_put_string_console(QemuConsole *s, const char *str, int len)
1244{
1245 int i;
1246
1247 for (i = 0; i < len && str[i]; i++) {
1248 kbd_put_keysym_console(s, str[i]);
1249 }
1250}
1251
3f9a6e85
GH
1252void kbd_put_keysym(int keysym)
1253{
1254 kbd_put_keysym_console(active_console, keysym);
1255}
1256
4d3b6f6e
AZ
1257static void text_console_invalidate(void *opaque)
1258{
b2bb9cc4 1259 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
1562e531 1260
b2bb9cc4
MAL
1261 if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
1262 text_console_resize(QEMU_TEXT_CONSOLE(s));
68f00996 1263 }
4d3b6f6e
AZ
1264 console_refresh(s);
1265}
1266
c227f099 1267static void text_console_update(void *opaque, console_ch_t *chardata)
4d3b6f6e 1268{
b2bb9cc4 1269 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
4d3b6f6e
AZ
1270 int i, j, src;
1271
1272 if (s->text_x[0] <= s->text_x[1]) {
1273 src = (s->y_base + s->text_y[0]) * s->width;
1274 chardata += s->text_y[0] * s->width;
1275 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
4083733d
OH
1276 for (j = 0; j < s->width; j++, src++) {
1277 console_write_ch(chardata ++,
1278 ATTR2CHTYPE(s->cells[src].ch,
1279 s->cells[src].t_attrib.fgcol,
1280 s->cells[src].t_attrib.bgcol,
1281 s->cells[src].t_attrib.bold));
1282 }
b2bb9cc4 1283 dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0],
a93a4a22 1284 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
4d3b6f6e
AZ
1285 s->text_x[0] = s->width;
1286 s->text_y[0] = s->height;
1287 s->text_x[1] = 0;
1288 s->text_y[1] = 0;
1289 }
1290 if (s->cursor_invalidate) {
b2bb9cc4 1291 dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y);
4d3b6f6e
AZ
1292 s->cursor_invalidate = 0;
1293 }
1294}
1295
098d57e7 1296static void
c105d60f 1297qemu_console_register(QemuConsole *c)
e7f0ad58 1298{
95219897 1299 int i;
e7f0ad58 1300
c105d60f
MAL
1301 if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) &&
1302 QEMU_IS_GRAPHIC_CONSOLE(c))) {
098d57e7 1303 active_console = c;
af3a9031 1304 }
a1d2db08 1305
cd6cd8fa 1306 if (QTAILQ_EMPTY(&consoles)) {
098d57e7
MAL
1307 c->index = 0;
1308 QTAILQ_INSERT_TAIL(&consoles, c, next);
c105d60f 1309 } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) {
eae3eb3e 1310 QemuConsole *last = QTAILQ_LAST(&consoles);
098d57e7
MAL
1311 c->index = last->index + 1;
1312 QTAILQ_INSERT_TAIL(&consoles, c, next);
95219897 1313 } else {
9588d67e
GH
1314 /*
1315 * HACK: Put graphical consoles before text consoles.
1316 *
1317 * Only do that for coldplugged devices. After initial device
1318 * initialization we will not renumber the consoles any more.
1319 */
098d57e7 1320 QemuConsole *it = QTAILQ_FIRST(&consoles);
cd6cd8fa 1321
c105d60f 1322 while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) {
098d57e7 1323 it = QTAILQ_NEXT(it, next);
cd6cd8fa 1324 }
c105d60f 1325 if (QEMU_IS_GRAPHIC_CONSOLE(it)) {
cd6cd8fa 1326 /* have no text consoles */
098d57e7
MAL
1327 c->index = it->index + 1;
1328 QTAILQ_INSERT_AFTER(&consoles, it, c, next);
cd6cd8fa 1329 } else {
098d57e7
MAL
1330 c->index = it->index;
1331 QTAILQ_INSERT_BEFORE(it, c, next);
cd6cd8fa 1332 /* renumber text consoles */
098d57e7
MAL
1333 for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) {
1334 it->index = i;
cd6cd8fa 1335 }
95219897 1336 }
95219897 1337 }
95219897
PB
1338}
1339
e265917c
MAL
1340static void
1341qemu_console_finalize(Object *obj)
1342{
cfde05d1
MAL
1343 QemuConsole *c = QEMU_CONSOLE(obj);
1344
463c6b19
MAL
1345 /* TODO: check this code path, and unregister from consoles */
1346 g_clear_pointer(&c->device, object_unref);
1347 g_clear_pointer(&c->surface, qemu_free_displaysurface);
1348 g_clear_pointer(&c->gl_unblock_timer, timer_free);
cfde05d1 1349 g_clear_pointer(&c->ui_timer, timer_free);
098d57e7
MAL
1350}
1351
1352static void
1353qemu_console_prop_get_head(Object *obj, Visitor *v, const char *name,
1354 void *opaque, Error **errp)
1355{
1356 QemuConsole *c = QEMU_CONSOLE(obj);
1357
1358 visit_type_uint32(v, name, &c->head, errp);
e265917c
MAL
1359}
1360
1361static void
1362qemu_console_class_init(ObjectClass *oc, void *data)
1363{
098d57e7
MAL
1364 object_class_property_add_link(oc, "device", TYPE_DEVICE,
1365 offsetof(QemuConsole, device),
1366 object_property_allow_set_link,
1367 OBJ_PROP_LINK_STRONG);
1368 object_class_property_add(oc, "head", "uint32",
1369 qemu_console_prop_get_head,
1370 NULL, NULL, NULL);
e265917c
MAL
1371}
1372
1373static void
1374qemu_console_init(Object *obj)
1375{
098d57e7
MAL
1376 QemuConsole *c = QEMU_CONSOLE(obj);
1377 DisplayState *ds = get_alloc_displaystate();
1378
1379 qemu_co_queue_init(&c->dump_queue);
1380 c->ds = ds;
1381 c->window_id = -1;
cfde05d1
MAL
1382 c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1383 dpy_set_ui_info_timer, c);
ba0ec5c2 1384 qemu_console_register(c);
098d57e7
MAL
1385}
1386
b208f745
MAL
1387static void
1388qemu_graphic_console_finalize(Object *obj)
1389{
1390}
1391
1392static void
1393qemu_graphic_console_class_init(ObjectClass *oc, void *data)
1394{
1395}
1396
1397static void
1398qemu_graphic_console_init(Object *obj)
1399{
1400}
1401
1402static void
1403qemu_text_console_finalize(Object *obj)
1404{
1405}
1406
1407static void
1408qemu_text_console_class_init(ObjectClass *oc, void *data)
1409{
b97a76d0
MAL
1410 if (!cursor_timer) {
1411 cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1412 text_console_update_cursor, NULL);
1413 }
b208f745
MAL
1414}
1415
1416static void
1417qemu_text_console_init(Object *obj)
1418{
1419}
1420
1421static void
1422qemu_fixed_text_console_finalize(Object *obj)
1423{
1424}
1425
1426static void
1427qemu_fixed_text_console_class_init(ObjectClass *oc, void *data)
1428{
1429}
1430
1431static void
1432qemu_fixed_text_console_init(Object *obj)
1433{
1434}
1435
09b4c198
MAL
1436#ifdef WIN32
1437void qemu_displaysurface_win32_set_handle(DisplaySurface *surface,
1438 HANDLE h, uint32_t offset)
1439{
1440 assert(!surface->handle);
1441
1442 surface->handle = h;
1443 surface->handle_offset = offset;
1444}
1445
1446static void
1447win32_pixman_image_destroy(pixman_image_t *image, void *data)
1448{
1449 DisplaySurface *surface = data;
1450
1451 if (!surface->handle) {
1452 return;
1453 }
1454
1455 assert(surface->handle_offset == 0);
1456
1457 qemu_win32_map_free(
1458 pixman_image_get_data(surface->image),
1459 surface->handle,
1460 &error_warn
1461 );
1462}
1463#endif
1464
eb69442a 1465DisplaySurface *qemu_create_displaysurface(int width, int height)
ffe8b821 1466{
09b4c198
MAL
1467 DisplaySurface *surface;
1468 void *bits = NULL;
1469#ifdef WIN32
1470 HANDLE handle = NULL;
1471#endif
69c77777 1472
09b4c198
MAL
1473 trace_displaysurface_create(width, height);
1474
1475#ifdef WIN32
1476 bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort);
1477#endif
1478
1479 surface = qemu_create_displaysurface_from(
1480 width, height,
1481 PIXMAN_x8r8g8b8,
1482 width * 4, bits
1483 );
30f1e661 1484 surface->flags = QEMU_ALLOCATED_FLAG;
98b50080 1485
09b4c198
MAL
1486#ifdef WIN32
1487 qemu_displaysurface_win32_set_handle(surface, handle, 0);
1488#endif
537a4391
GH
1489 return surface;
1490}
1491
30f1e661
GH
1492DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1493 pixman_format_code_t format,
1494 int linesize, uint8_t *data)
98b50080 1495{
69c77777 1496 DisplaySurface *surface = g_new0(DisplaySurface, 1);
98b50080 1497
30f1e661
GH
1498 trace_displaysurface_create_from(surface, width, height, format);
1499 surface->format = format;
69c77777
GH
1500 surface->image = pixman_image_create_bits(surface->format,
1501 width, height,
1502 (void *)data, linesize);
1503 assert(surface->image != NULL);
09b4c198
MAL
1504#ifdef WIN32
1505 pixman_image_set_destroy_function(surface->image,
1506 win32_pixman_image_destroy, surface);
1507#endif
69c77777 1508
98b50080
PB
1509 return surface;
1510}
1511
ca58b45f
GH
1512DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1513{
1514 DisplaySurface *surface = g_new0(DisplaySurface, 1);
1515
1516 trace_displaysurface_create_pixman(surface);
1517 surface->format = pixman_image_get_format(image);
1518 surface->image = pixman_image_ref(image);
1519
1520 return surface;
1521}
1522
b5a087b0
AO
1523DisplaySurface *qemu_create_placeholder_surface(int w, int h,
1524 const char *msg)
d3002b04 1525{
521a580d 1526 DisplaySurface *surface = qemu_create_displaysurface(w, h);
4083733d
OH
1527 pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
1528 pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
d3002b04
GH
1529 pixman_image_t *glyph;
1530 int len, x, y, i;
1531
1532 len = strlen(msg);
521a580d
GH
1533 x = (w / FONT_WIDTH - len) / 2;
1534 y = (h / FONT_HEIGHT - 1) / 2;
d3002b04
GH
1535 for (i = 0; i < len; i++) {
1536 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1537 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1538 x+i, y, FONT_WIDTH, FONT_HEIGHT);
1539 qemu_pixman_image_unref(glyph);
1540 }
b5a087b0 1541 surface->flags |= QEMU_PLACEHOLDER_FLAG;
d3002b04
GH
1542 return surface;
1543}
1544
da229ef3 1545void qemu_free_displaysurface(DisplaySurface *surface)
98b50080 1546{
da229ef3 1547 if (surface == NULL) {
98b50080 1548 return;
187cd1d9 1549 }
da229ef3
GH
1550 trace_displaysurface_free(surface);
1551 qemu_pixman_image_unref(surface->image);
1552 g_free(surface);
98b50080
PB
1553}
1554
06020b95
GH
1555bool console_has_gl(QemuConsole *con)
1556{
1557 return con->gl != NULL;
1558}
1559
d0e137bc
MAL
1560static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
1561{
1562 if (dcl->ops->dpy_has_dmabuf) {
1563 return dcl->ops->dpy_has_dmabuf(dcl);
1564 }
1565
1566 if (dcl->ops->dpy_gl_scanout_dmabuf) {
1567 return true;
1568 }
1569
1570 return false;
1571}
1572
4b7b661d
MAL
1573static bool console_compatible_with(QemuConsole *con,
1574 DisplayChangeListener *dcl, Error **errp)
5983fdf1 1575{
5983fdf1
MAL
1576 int flags;
1577
1578 flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
1579
a62c4a17
MAL
1580 if (console_has_gl(con) &&
1581 !con->gl->ops->dpy_gl_ctx_is_compatible_dcl(con->gl, dcl)) {
398d1c91
MAL
1582 error_setg(errp, "Display %s is incompatible with the GL context",
1583 dcl->ops->dpy_name);
1584 return false;
1585 }
1586
5983fdf1
MAL
1587 if (flags & GRAPHIC_FLAGS_GL &&
1588 !console_has_gl(con)) {
1589 error_setg(errp, "The console requires a GL context.");
1590 return false;
1591
1592 }
1593
1594 if (flags & GRAPHIC_FLAGS_DMABUF &&
1595 !displaychangelistener_has_dmabuf(dcl)) {
1596 error_setg(errp, "The console requires display DMABUF support.");
1597 return false;
1598 }
1599
1600 return true;
1601}
1602
b6596785
BE
1603void console_handle_touch_event(QemuConsole *con,
1604 struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX],
1605 uint64_t num_slot,
1606 int width, int height,
1607 double x, double y,
1608 InputMultiTouchType type,
1609 Error **errp)
1610{
1611 struct touch_slot *slot;
1612 bool needs_sync = false;
1613 int update;
1614 int i;
1615
1616 if (num_slot >= INPUT_EVENT_SLOTS_MAX) {
1617 error_setg(errp,
1618 "Unexpected touch slot number: % " PRId64" >= %d",
1619 num_slot, INPUT_EVENT_SLOTS_MAX);
1620 return;
1621 }
1622
1623 slot = &touch_slots[num_slot];
1624 slot->x = x;
1625 slot->y = y;
1626
1627 if (type == INPUT_MULTI_TOUCH_TYPE_BEGIN) {
1628 slot->tracking_id = num_slot;
1629 }
1630
1631 for (i = 0; i < INPUT_EVENT_SLOTS_MAX; ++i) {
1632 if (i == num_slot) {
1633 update = type;
1634 } else {
1635 update = INPUT_MULTI_TOUCH_TYPE_UPDATE;
1636 }
1637
1638 slot = &touch_slots[i];
1639
1640 if (slot->tracking_id == -1) {
1641 continue;
1642 }
1643
1644 if (update == INPUT_MULTI_TOUCH_TYPE_END) {
1645 slot->tracking_id = -1;
1646 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1647 needs_sync = true;
1648 } else {
1649 qemu_input_queue_mtt(con, update, i, slot->tracking_id);
1650 qemu_input_queue_btn(con, INPUT_BUTTON_TOUCH, true);
1651 qemu_input_queue_mtt_abs(con,
1652 INPUT_AXIS_X, (int) slot->x,
1653 0, width,
1654 i, slot->tracking_id);
1655 qemu_input_queue_mtt_abs(con,
1656 INPUT_AXIS_Y, (int) slot->y,
1657 0, height,
1658 i, slot->tracking_id);
1659 needs_sync = true;
1660 }
1661 }
1662
1663 if (needs_sync) {
1664 qemu_input_event_sync();
1665 }
1666}
1667
5e79d516 1668void qemu_console_set_display_gl_ctx(QemuConsole *con, DisplayGLCtx *gl)
4f418149
MAL
1669{
1670 /* display has opengl support */
5e79d516
MAL
1671 assert(con);
1672 if (con->gl) {
1673 error_report("The console already has an OpenGL context.");
4f418149
MAL
1674 exit(1);
1675 }
5e79d516
MAL
1676 con->gl = gl;
1677}
1678
5209089f 1679void register_displaychangelistener(DisplayChangeListener *dcl)
7c20b4a3 1680{
284d1c6b
GH
1681 QemuConsole *con;
1682
e0665c3b
MAL
1683 assert(!dcl->ds);
1684
7c20b4a3 1685 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
5209089f
GH
1686 dcl->ds = get_alloc_displaystate();
1687 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1688 gui_setup_refresh(dcl->ds);
284d1c6b
GH
1689 if (dcl->con) {
1690 dcl->con->dcls++;
1691 con = dcl->con;
1692 } else {
1693 con = active_console;
1694 }
4b7b661d 1695 displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
de00b60d
MAL
1696 if (con && con->cursor && dcl->ops->dpy_cursor_define) {
1697 dcl->ops->dpy_cursor_define(dcl, con->cursor);
1698 }
6effaa16
MAL
1699 if (con && dcl->ops->dpy_mouse_set) {
1700 dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
1701 }
aea7947c 1702 text_console_update_cursor(NULL);
7c20b4a3
GH
1703}
1704
0f7b2864
GH
1705void update_displaychangelistener(DisplayChangeListener *dcl,
1706 uint64_t interval)
1707{
1708 DisplayState *ds = dcl->ds;
1709
1710 dcl->update_interval = interval;
1711 if (!ds->refreshing && ds->update_interval > interval) {
bc72ad67 1712 timer_mod(ds->gui_timer, ds->last_update + interval);
0f7b2864
GH
1713 }
1714}
1715
7c20b4a3
GH
1716void unregister_displaychangelistener(DisplayChangeListener *dcl)
1717{
1718 DisplayState *ds = dcl->ds;
1719 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
284d1c6b
GH
1720 if (dcl->con) {
1721 dcl->con->dcls--;
1722 }
7c20b4a3 1723 QLIST_REMOVE(dcl, next);
777c5f1e 1724 dcl->ds = NULL;
7c20b4a3
GH
1725 gui_setup_refresh(ds);
1726}
1727
cf1ecc82
GH
1728static void dpy_set_ui_info_timer(void *opaque)
1729{
1730 QemuConsole *con = opaque;
1731
1732 con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
1733}
1734
b7fb49f0
GH
1735bool dpy_ui_info_supported(QemuConsole *con)
1736{
5c4b107f
GH
1737 if (con == NULL) {
1738 con = active_console;
1739 }
1740
b7fb49f0
GH
1741 return con->hw_ops->ui_info != NULL;
1742}
1743
5eaf1e48
MAL
1744const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
1745{
5c4b107f
GH
1746 if (con == NULL) {
1747 con = active_console;
1748 }
5eaf1e48
MAL
1749
1750 return &con->ui_info;
1751}
1752
ca19ef52 1753int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info, bool delay)
6f90f3d7 1754{
5c4b107f
GH
1755 if (con == NULL) {
1756 con = active_console;
1757 }
1185fde4 1758
b7fb49f0 1759 if (!dpy_ui_info_supported(con)) {
cf1ecc82 1760 return -1;
6f90f3d7 1761 }
1185fde4
GH
1762 if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1763 /* nothing changed -- ignore */
1764 return 0;
1765 }
cf1ecc82
GH
1766
1767 /*
1768 * Typically we get a flood of these as the user resizes the window.
1769 * Wait until the dust has settled (one second without updates), then
1770 * go notify the guest.
1771 */
1185fde4 1772 con->ui_info = *info;
ca19ef52
MAL
1773 timer_mod(con->ui_timer,
1774 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + (delay ? 1000 : 0));
cf1ecc82 1775 return 0;
6f90f3d7
GH
1776}
1777
c78f7137 1778void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
7c20b4a3 1779{
c78f7137 1780 DisplayState *s = con->ds;
284d1c6b 1781 DisplayChangeListener *dcl;
ebced091
MAL
1782 int width = qemu_console_get_width(con, x + w);
1783 int height = qemu_console_get_height(con, y + h);
7c20b4a3
GH
1784
1785 x = MAX(x, 0);
1786 y = MAX(y, 0);
1787 x = MIN(x, width);
1788 y = MIN(y, height);
1789 w = MIN(w, width - x);
1790 h = MIN(h, height - y);
1791
81c0d5a6 1792 if (!qemu_console_is_visible(con)) {
321f048d
GH
1793 return;
1794 }
589089fe 1795 dpy_gfx_update_texture(con, con->surface, x, y, w, h);
7c20b4a3 1796 QLIST_FOREACH(dcl, &s->listeners, next) {
284d1c6b
GH
1797 if (con != (dcl->con ? dcl->con : active_console)) {
1798 continue;
1799 }
7c20b4a3 1800 if (dcl->ops->dpy_gfx_update) {
bc2ed970 1801 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
7c20b4a3
GH
1802 }
1803 }
1804}
1805
7cd0afe6
TZ
1806void dpy_gfx_update_full(QemuConsole *con)
1807{
ebced091
MAL
1808 int w = qemu_console_get_width(con, 0);
1809 int h = qemu_console_get_height(con, 0);
1810
1811 dpy_gfx_update(con, 0, 0, w, h);
7cd0afe6
TZ
1812}
1813
321f048d
GH
1814void dpy_gfx_replace_surface(QemuConsole *con,
1815 DisplaySurface *surface)
1816{
c821a58e 1817 static const char placeholder_msg[] = "Display output is not active.";
321f048d
GH
1818 DisplayState *s = con->ds;
1819 DisplaySurface *old_surface = con->surface;
0d0be876 1820 DisplaySurface *new_surface = surface;
284d1c6b 1821 DisplayChangeListener *dcl;
c821a58e
AO
1822 int width;
1823 int height;
1824
1825 if (!surface) {
1826 if (old_surface) {
1827 width = surface_width(old_surface);
1828 height = surface_height(old_surface);
1829 } else {
1830 width = 640;
1831 height = 480;
1832 }
1833
0d0be876 1834 new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
c821a58e 1835 }
321f048d 1836
0d0be876 1837 assert(old_surface != new_surface);
6905b934 1838
ebced091 1839 con->scanout.kind = SCANOUT_SURFACE;
0d0be876
DK
1840 con->surface = new_surface;
1841 dpy_gfx_create_texture(con, new_surface);
284d1c6b
GH
1842 QLIST_FOREACH(dcl, &s->listeners, next) {
1843 if (con != (dcl->con ? dcl->con : active_console)) {
1844 continue;
1845 }
0d0be876 1846 displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE);
321f048d 1847 }
589089fe 1848 dpy_gfx_destroy_texture(con, old_surface);
da229ef3 1849 qemu_free_displaysurface(old_surface);
7c20b4a3
GH
1850}
1851
49743df3
BH
1852bool dpy_gfx_check_format(QemuConsole *con,
1853 pixman_format_code_t format)
1854{
1855 DisplayChangeListener *dcl;
1856 DisplayState *s = con->ds;
1857
1858 QLIST_FOREACH(dcl, &s->listeners, next) {
1859 if (dcl->con && dcl->con != con) {
1860 /* dcl bound to another console -> skip */
1861 continue;
1862 }
1863 if (dcl->ops->dpy_gfx_check_format) {
1864 if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1865 return false;
1866 }
1867 } else {
75ae7c46 1868 /* default is to allow native 32 bpp only */
49743df3
BH
1869 if (format != qemu_default_pixman_format(32, true)) {
1870 return false;
1871 }
1872 }
1873 }
1874 return true;
1875}
1876
6075137d 1877static void dpy_refresh(DisplayState *s)
7c20b4a3 1878{
284d1c6b
GH
1879 DisplayChangeListener *dcl;
1880
7c20b4a3
GH
1881 QLIST_FOREACH(dcl, &s->listeners, next) {
1882 if (dcl->ops->dpy_refresh) {
3f8f1313 1883 dcl->ops->dpy_refresh(dcl);
7c20b4a3
GH
1884 }
1885 }
1886}
1887
c78f7137 1888void dpy_text_cursor(QemuConsole *con, int x, int y)
7c20b4a3 1889{
c78f7137 1890 DisplayState *s = con->ds;
284d1c6b 1891 DisplayChangeListener *dcl;
321f048d 1892
81c0d5a6 1893 if (!qemu_console_is_visible(con)) {
321f048d
GH
1894 return;
1895 }
7c20b4a3 1896 QLIST_FOREACH(dcl, &s->listeners, next) {
284d1c6b
GH
1897 if (con != (dcl->con ? dcl->con : active_console)) {
1898 continue;
1899 }
7c20b4a3 1900 if (dcl->ops->dpy_text_cursor) {
bc2ed970 1901 dcl->ops->dpy_text_cursor(dcl, x, y);
7c20b4a3
GH
1902 }
1903 }
1904}
1905
c78f7137 1906void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
7c20b4a3 1907{
c78f7137 1908 DisplayState *s = con->ds;
284d1c6b 1909 DisplayChangeListener *dcl;
321f048d 1910
81c0d5a6 1911 if (!qemu_console_is_visible(con)) {
321f048d
GH
1912 return;
1913 }
7c20b4a3 1914 QLIST_FOREACH(dcl, &s->listeners, next) {
284d1c6b
GH
1915 if (con != (dcl->con ? dcl->con : active_console)) {
1916 continue;
1917 }
7c20b4a3 1918 if (dcl->ops->dpy_text_update) {
bc2ed970 1919 dcl->ops->dpy_text_update(dcl, x, y, w, h);
7c20b4a3
GH
1920 }
1921 }
1922}
1923
c78f7137 1924void dpy_text_resize(QemuConsole *con, int w, int h)
7c20b4a3 1925{
c78f7137 1926 DisplayState *s = con->ds;
9425c004 1927 DisplayChangeListener *dcl;
321f048d 1928
81c0d5a6 1929 if (!qemu_console_is_visible(con)) {
321f048d
GH
1930 return;
1931 }
7c20b4a3 1932 QLIST_FOREACH(dcl, &s->listeners, next) {
284d1c6b
GH
1933 if (con != (dcl->con ? dcl->con : active_console)) {
1934 continue;
1935 }
7c20b4a3 1936 if (dcl->ops->dpy_text_resize) {
bc2ed970 1937 dcl->ops->dpy_text_resize(dcl, w, h);
7c20b4a3
GH
1938 }
1939 }
1940}
1941
c78f7137 1942void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
7c20b4a3 1943{
c78f7137 1944 DisplayState *s = con->ds;
284d1c6b 1945 DisplayChangeListener *dcl;
321f048d 1946
6effaa16
MAL
1947 con->cursor_x = x;
1948 con->cursor_y = y;
1949 con->cursor_on = on;
81c0d5a6 1950 if (!qemu_console_is_visible(con)) {
321f048d
GH
1951 return;
1952 }
7c20b4a3 1953 QLIST_FOREACH(dcl, &s->listeners, next) {
284d1c6b
GH
1954 if (con != (dcl->con ? dcl->con : active_console)) {
1955 continue;
1956 }
7c20b4a3 1957 if (dcl->ops->dpy_mouse_set) {
bc2ed970 1958 dcl->ops->dpy_mouse_set(dcl, x, y, on);
7c20b4a3
GH
1959 }
1960 }
1961}
1962
c78f7137 1963void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
7c20b4a3 1964{
c78f7137 1965 DisplayState *s = con->ds;
284d1c6b 1966 DisplayChangeListener *dcl;
321f048d 1967
385ac97f
MAL
1968 cursor_unref(con->cursor);
1969 con->cursor = cursor_ref(cursor);
81c0d5a6 1970 if (!qemu_console_is_visible(con)) {
321f048d
GH
1971 return;
1972 }
7c20b4a3 1973 QLIST_FOREACH(dcl, &s->listeners, next) {
284d1c6b
GH
1974 if (con != (dcl->con ? dcl->con : active_console)) {
1975 continue;
1976 }
7c20b4a3 1977 if (dcl->ops->dpy_cursor_define) {
bc2ed970 1978 dcl->ops->dpy_cursor_define(dcl, cursor);
7c20b4a3
GH
1979 }
1980 }
1981}
1982
c78f7137 1983bool dpy_cursor_define_supported(QemuConsole *con)
7c20b4a3 1984{
c78f7137 1985 DisplayState *s = con->ds;
284d1c6b
GH
1986 DisplayChangeListener *dcl;
1987
7c20b4a3
GH
1988 QLIST_FOREACH(dcl, &s->listeners, next) {
1989 if (dcl->ops->dpy_cursor_define) {
1990 return true;
1991 }
1992 }
1993 return false;
1994}
1995
06020b95
GH
1996QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
1997 struct QEMUGLParams *qparams)
1998{
1999 assert(con->gl);
2000 return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
2001}
2002
2003void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
2004{
2005 assert(con->gl);
2006 con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
2007}
2008
2009int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
2010{
2011 assert(con->gl);
2012 return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
2013}
2014
eaa92c76
GH
2015void dpy_gl_scanout_disable(QemuConsole *con)
2016{
7cc712e9
MAL
2017 DisplayState *s = con->ds;
2018 DisplayChangeListener *dcl;
2019
ebced091
MAL
2020 if (con->scanout.kind != SCANOUT_SURFACE) {
2021 con->scanout.kind = SCANOUT_NONE;
2022 }
7cc712e9 2023 QLIST_FOREACH(dcl, &s->listeners, next) {
1699d00e
AO
2024 if (con != (dcl->con ? dcl->con : active_console)) {
2025 continue;
2026 }
a9fbce5e
MAL
2027 if (dcl->ops->dpy_gl_scanout_disable) {
2028 dcl->ops->dpy_gl_scanout_disable(dcl);
2029 }
7cc712e9 2030 }
eaa92c76
GH
2031}
2032
f4c36bda
GH
2033void dpy_gl_scanout_texture(QemuConsole *con,
2034 uint32_t backing_id,
2035 bool backing_y_0_top,
2036 uint32_t backing_width,
2037 uint32_t backing_height,
2038 uint32_t x, uint32_t y,
bf41ab61
MAL
2039 uint32_t width, uint32_t height,
2040 void *d3d_tex2d)
06020b95 2041{
7cc712e9
MAL
2042 DisplayState *s = con->ds;
2043 DisplayChangeListener *dcl;
2044
ebced091
MAL
2045 con->scanout.kind = SCANOUT_TEXTURE;
2046 con->scanout.texture = (ScanoutTexture) {
2047 backing_id, backing_y_0_top, backing_width, backing_height,
bf41ab61 2048 x, y, width, height, d3d_tex2d,
ebced091 2049 };
7cc712e9 2050 QLIST_FOREACH(dcl, &s->listeners, next) {
1699d00e
AO
2051 if (con != (dcl->con ? dcl->con : active_console)) {
2052 continue;
2053 }
a9fbce5e
MAL
2054 if (dcl->ops->dpy_gl_scanout_texture) {
2055 dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
2056 backing_y_0_top,
2057 backing_width, backing_height,
bf41ab61
MAL
2058 x, y, width, height,
2059 d3d_tex2d);
a9fbce5e 2060 }
7cc712e9 2061 }
06020b95
GH
2062}
2063
4133fa71
GH
2064void dpy_gl_scanout_dmabuf(QemuConsole *con,
2065 QemuDmaBuf *dmabuf)
2066{
7cc712e9
MAL
2067 DisplayState *s = con->ds;
2068 DisplayChangeListener *dcl;
2069
ebced091
MAL
2070 con->scanout.kind = SCANOUT_DMABUF;
2071 con->scanout.dmabuf = dmabuf;
7cc712e9 2072 QLIST_FOREACH(dcl, &s->listeners, next) {
1699d00e
AO
2073 if (con != (dcl->con ? dcl->con : active_console)) {
2074 continue;
2075 }
a9fbce5e
MAL
2076 if (dcl->ops->dpy_gl_scanout_dmabuf) {
2077 dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
2078 }
7cc712e9 2079 }
4133fa71
GH
2080}
2081
6e1f2cb5
GH
2082void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
2083 bool have_hot, uint32_t hot_x, uint32_t hot_y)
4133fa71 2084{
7cc712e9
MAL
2085 DisplayState *s = con->ds;
2086 DisplayChangeListener *dcl;
4133fa71 2087
7cc712e9 2088 QLIST_FOREACH(dcl, &s->listeners, next) {
1699d00e
AO
2089 if (con != (dcl->con ? dcl->con : active_console)) {
2090 continue;
2091 }
7cc712e9
MAL
2092 if (dcl->ops->dpy_gl_cursor_dmabuf) {
2093 dcl->ops->dpy_gl_cursor_dmabuf(dcl, dmabuf,
6e1f2cb5 2094 have_hot, hot_x, hot_y);
7cc712e9 2095 }
6e1f2cb5
GH
2096 }
2097}
2098
2099void dpy_gl_cursor_position(QemuConsole *con,
2100 uint32_t pos_x, uint32_t pos_y)
2101{
7cc712e9
MAL
2102 DisplayState *s = con->ds;
2103 DisplayChangeListener *dcl;
6e1f2cb5 2104
7cc712e9 2105 QLIST_FOREACH(dcl, &s->listeners, next) {
1699d00e
AO
2106 if (con != (dcl->con ? dcl->con : active_console)) {
2107 continue;
2108 }
7cc712e9
MAL
2109 if (dcl->ops->dpy_gl_cursor_position) {
2110 dcl->ops->dpy_gl_cursor_position(dcl, pos_x, pos_y);
2111 }
4133fa71
GH
2112 }
2113}
2114
2115void dpy_gl_release_dmabuf(QemuConsole *con,
2116 QemuDmaBuf *dmabuf)
2117{
7cc712e9
MAL
2118 DisplayState *s = con->ds;
2119 DisplayChangeListener *dcl;
4133fa71 2120
7cc712e9 2121 QLIST_FOREACH(dcl, &s->listeners, next) {
1699d00e
AO
2122 if (con != (dcl->con ? dcl->con : active_console)) {
2123 continue;
2124 }
7cc712e9
MAL
2125 if (dcl->ops->dpy_gl_release_dmabuf) {
2126 dcl->ops->dpy_gl_release_dmabuf(dcl, dmabuf);
2127 }
4133fa71
GH
2128 }
2129}
2130
06020b95
GH
2131void dpy_gl_update(QemuConsole *con,
2132 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
2133{
7cc712e9
MAL
2134 DisplayState *s = con->ds;
2135 DisplayChangeListener *dcl;
2136
06020b95 2137 assert(con->gl);
f6413cbf
MAL
2138
2139 graphic_hw_gl_block(con, true);
7cc712e9 2140 QLIST_FOREACH(dcl, &s->listeners, next) {
1699d00e
AO
2141 if (con != (dcl->con ? dcl->con : active_console)) {
2142 continue;
2143 }
a9fbce5e
MAL
2144 if (dcl->ops->dpy_gl_update) {
2145 dcl->ops->dpy_gl_update(dcl, x, y, w, h);
2146 }
7cc712e9 2147 }
f6413cbf 2148 graphic_hw_gl_block(con, false);
06020b95
GH
2149}
2150
98b50080
PB
2151/***********************************************************/
2152/* register display */
2153
64840c66
GH
2154/* console.c internal use only */
2155static DisplayState *get_alloc_displaystate(void)
98b50080 2156{
64840c66
GH
2157 if (!display_state) {
2158 display_state = g_new0(DisplayState, 1);
2159 }
2160 return display_state;
98b50080
PB
2161}
2162
64840c66
GH
2163/*
2164 * Called by main(), after creating QemuConsoles
2165 * and before initializing ui (sdl/vnc/...).
2166 */
2167DisplayState *init_displaystate(void)
98b50080 2168{
43f420f8 2169 gchar *name;
cd6cd8fa 2170 QemuConsole *con;
64840c66 2171
cd6cd8fa 2172 QTAILQ_FOREACH(con, &consoles, next) {
34b77515 2173 /* Hook up into the qom tree here (not in object_new()), once
43f420f8
GH
2174 * all QemuConsoles are created and the order / numbering
2175 * doesn't change any more */
cd6cd8fa 2176 name = g_strdup_printf("console[%d]", con->index);
43f420f8 2177 object_property_add_child(container_get(object_get_root(), "/backend"),
d2623129 2178 name, OBJECT(con));
43f420f8 2179 g_free(name);
64840c66
GH
2180 }
2181
98b50080
PB
2182 return display_state;
2183}
2184
1c1f9498
GH
2185void graphic_console_set_hwops(QemuConsole *con,
2186 const GraphicHwOps *hw_ops,
2187 void *opaque)
2188{
2189 con->hw_ops = hw_ops;
2190 con->hw = opaque;
2191}
2192
5643706a 2193QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
aa2beaa1 2194 const GraphicHwOps *hw_ops,
c78f7137 2195 void *opaque)
95219897 2196{
521a580d
GH
2197 static const char noinit[] =
2198 "Guest has not initialized the display (yet).";
64840c66
GH
2199 int width = 640;
2200 int height = 480;
76ffb0b4 2201 QemuConsole *s;
9588d67e 2202 DisplaySurface *surface;
f0f2f976 2203
f9411aae 2204 s = qemu_graphic_console_lookup_unused();
9588d67e
GH
2205 if (s) {
2206 trace_console_gfx_reuse(s->index);
ebced091
MAL
2207 width = qemu_console_get_width(s, 0);
2208 height = qemu_console_get_height(s, 0);
9588d67e
GH
2209 } else {
2210 trace_console_gfx_new();
34b77515 2211 s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE);
9588d67e 2212 }
7fa4b804 2213 s->head = head;
1c1f9498 2214 graphic_console_set_hwops(s, hw_ops, opaque);
aa2beaa1 2215 if (dev) {
5325cc34 2216 object_property_set_link(OBJECT(s), "device", OBJECT(dev),
afff2b15 2217 &error_abort);
aa2beaa1 2218 }
3023f332 2219
b5a087b0 2220 surface = qemu_create_placeholder_surface(width, height, noinit);
9588d67e 2221 dpy_gfx_replace_surface(s, surface);
a9b1e471
MAL
2222 s->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
2223 graphic_hw_gl_unblock_timer, s);
c78f7137 2224 return s;
e7f0ad58
FB
2225}
2226
9588d67e
GH
2227static const GraphicHwOps unused_ops = {
2228 /* no callbacks */
2229};
2230
2231void graphic_console_close(QemuConsole *con)
2232{
2233 static const char unplugged[] =
2234 "Guest display has been unplugged";
2235 DisplaySurface *surface;
ebced091
MAL
2236 int width = qemu_console_get_width(con, 640);
2237 int height = qemu_console_get_height(con, 480);
9588d67e
GH
2238
2239 trace_console_gfx_close(con->index);
5325cc34 2240 object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
9588d67e
GH
2241 graphic_console_set_hwops(con, &unused_ops, NULL);
2242
2243 if (con->gl) {
2244 dpy_gl_scanout_disable(con);
2245 }
b5a087b0 2246 surface = qemu_create_placeholder_surface(width, height, unplugged);
9588d67e
GH
2247 dpy_gfx_replace_surface(con, surface);
2248}
2249
284d1c6b
GH
2250QemuConsole *qemu_console_lookup_by_index(unsigned int index)
2251{
cd6cd8fa
GH
2252 QemuConsole *con;
2253
2254 QTAILQ_FOREACH(con, &consoles, next) {
2255 if (con->index == index) {
2256 return con;
2257 }
284d1c6b 2258 }
cd6cd8fa 2259 return NULL;
284d1c6b
GH
2260}
2261
5643706a 2262QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
14a93649 2263{
cd6cd8fa 2264 QemuConsole *con;
14a93649 2265 Object *obj;
5643706a 2266 uint32_t h;
14a93649 2267
cd6cd8fa
GH
2268 QTAILQ_FOREACH(con, &consoles, next) {
2269 obj = object_property_get_link(OBJECT(con),
afff2b15 2270 "device", &error_abort);
5643706a
GH
2271 if (DEVICE(obj) != dev) {
2272 continue;
2273 }
cd6cd8fa 2274 h = object_property_get_uint(OBJECT(con),
ad664c1d 2275 "head", &error_abort);
5643706a
GH
2276 if (h != head) {
2277 continue;
14a93649 2278 }
cd6cd8fa 2279 return con;
14a93649
GH
2280 }
2281 return NULL;
2282}
2283
f2c1d54c
GH
2284QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
2285 uint32_t head, Error **errp)
2286{
2287 DeviceState *dev;
2288 QemuConsole *con;
2289
2290 dev = qdev_find_recursive(sysbus_get_default(), device_id);
2291 if (dev == NULL) {
2292 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
2293 "Device '%s' not found", device_id);
2294 return NULL;
2295 }
2296
2297 con = qemu_console_lookup_by_device(dev, head);
2298 if (con == NULL) {
2299 error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
2300 device_id, head);
2301 return NULL;
2302 }
2303
2304 return con;
2305}
2306
f9411aae 2307static QemuConsole *qemu_graphic_console_lookup_unused(void)
9588d67e 2308{
cd6cd8fa 2309 QemuConsole *con;
9588d67e 2310 Object *obj;
9588d67e 2311
cd6cd8fa 2312 QTAILQ_FOREACH(con, &consoles, next) {
f9411aae 2313 if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) {
9588d67e
GH
2314 continue;
2315 }
cd6cd8fa 2316 obj = object_property_get_link(OBJECT(con),
9588d67e
GH
2317 "device", &error_abort);
2318 if (obj != NULL) {
2319 continue;
2320 }
cd6cd8fa 2321 return con;
9588d67e
GH
2322 }
2323 return NULL;
2324}
2325
385ac97f
MAL
2326QEMUCursor *qemu_console_get_cursor(QemuConsole *con)
2327{
3c293a46
MAL
2328 if (con == NULL) {
2329 con = active_console;
2330 }
333e7599 2331 return con ? con->cursor : NULL;
385ac97f
MAL
2332}
2333
81c0d5a6 2334bool qemu_console_is_visible(QemuConsole *con)
e7f0ad58 2335{
284d1c6b 2336 return (con == active_console) || (con->dcls > 0);
e7f0ad58
FB
2337}
2338
81c0d5a6 2339bool qemu_console_is_graphic(QemuConsole *con)
c21bbcfa 2340{
81c0d5a6
GH
2341 if (con == NULL) {
2342 con = active_console;
2343 }
c105d60f 2344 return con && QEMU_IS_GRAPHIC_CONSOLE(con);
81c0d5a6
GH
2345}
2346
2347bool qemu_console_is_fixedsize(QemuConsole *con)
2348{
2349 if (con == NULL) {
2350 con = active_console;
2351 }
c105d60f 2352 return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con));
c21bbcfa
AZ
2353}
2354
f607867c
GH
2355bool qemu_console_is_gl_blocked(QemuConsole *con)
2356{
2357 assert(con != NULL);
2358 return con->gl_block;
2359}
2360
839a4826
WJ
2361bool qemu_console_is_multihead(DeviceState *dev)
2362{
2363 QemuConsole *con;
2364 Object *obj;
2365 uint32_t f = 0xffffffff;
2366 uint32_t h;
2367
2368 QTAILQ_FOREACH(con, &consoles, next) {
2369 obj = object_property_get_link(OBJECT(con),
2370 "device", &error_abort);
2371 if (DEVICE(obj) != dev) {
2372 continue;
2373 }
2374
2375 h = object_property_get_uint(OBJECT(con),
2376 "head", &error_abort);
2377 if (f == 0xffffffff) {
2378 f = h;
2379 } else if (h != f) {
2380 return true;
2381 }
2382 }
2383 return false;
2384}
2385
779ce88f
GH
2386char *qemu_console_get_label(QemuConsole *con)
2387{
c105d60f 2388 if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
779ce88f 2389 if (con->device) {
839a4826
WJ
2390 DeviceState *dev;
2391 bool multihead;
2392
2393 dev = DEVICE(con->device);
2394 multihead = qemu_console_is_multihead(dev);
2395 if (multihead) {
2396 return g_strdup_printf("%s.%d", dev->id ?
2397 dev->id :
2398 object_get_typename(con->device),
2399 con->head);
2400 } else {
2401 return g_strdup_printf("%s", dev->id ?
2402 dev->id :
2403 object_get_typename(con->device));
2404 }
779ce88f
GH
2405 }
2406 return g_strdup("VGA");
b2bb9cc4
MAL
2407 } else if (QEMU_IS_TEXT_CONSOLE(con)) {
2408 QemuTextConsole *c = QEMU_TEXT_CONSOLE(con);
2409 if (c->chr && c->chr->label) {
2410 return g_strdup(c->chr->label);
779ce88f 2411 }
779ce88f 2412 }
b2bb9cc4
MAL
2413
2414 return g_strdup_printf("vc%d", con->index);
779ce88f
GH
2415}
2416
d4c85337
GH
2417int qemu_console_get_index(QemuConsole *con)
2418{
2419 if (con == NULL) {
2420 con = active_console;
2421 }
2422 return con ? con->index : -1;
2423}
2424
5643706a
GH
2425uint32_t qemu_console_get_head(QemuConsole *con)
2426{
2427 if (con == NULL) {
2428 con = active_console;
2429 }
2430 return con ? con->head : -1;
2431}
2432
d4c85337
GH
2433int qemu_console_get_width(QemuConsole *con, int fallback)
2434{
2435 if (con == NULL) {
2436 con = active_console;
2437 }
ebced091
MAL
2438 if (con == NULL) {
2439 return fallback;
2440 }
2441 switch (con->scanout.kind) {
2442 case SCANOUT_DMABUF:
2443 return con->scanout.dmabuf->width;
2444 case SCANOUT_TEXTURE:
2445 return con->scanout.texture.width;
2446 case SCANOUT_SURFACE:
2447 return surface_width(con->surface);
2448 default:
2449 return fallback;
2450 }
d4c85337
GH
2451}
2452
2453int qemu_console_get_height(QemuConsole *con, int fallback)
2454{
2455 if (con == NULL) {
2456 con = active_console;
2457 }
ebced091
MAL
2458 if (con == NULL) {
2459 return fallback;
2460 }
2461 switch (con->scanout.kind) {
2462 case SCANOUT_DMABUF:
2463 return con->scanout.dmabuf->height;
2464 case SCANOUT_TEXTURE:
2465 return con->scanout.texture.height;
2466 case SCANOUT_SURFACE:
2467 return surface_height(con->surface);
2468 default:
2469 return fallback;
2470 }
d4c85337
GH
2471}
2472
ec222519
VR
2473static void vc_chr_accept_input(Chardev *chr)
2474{
2475 VCChardev *drv = VC_CHARDEV(chr);
ec222519 2476
b2bb9cc4 2477 kbd_send_chars(drv->console);
ec222519
VR
2478}
2479
5bf5adae 2480static void vc_chr_set_echo(Chardev *chr, bool echo)
4104833f 2481{
777357d7 2482 VCChardev *drv = VC_CHARDEV(chr);
4104833f 2483
b2bb9cc4 2484 drv->console->echo = echo;
4104833f
PB
2485}
2486
aea7947c
GH
2487static void text_console_update_cursor_timer(void)
2488{
2489 timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
2490 + CONSOLE_CURSOR_PERIOD / 2);
2491}
2492
bf1bed81
JK
2493static void text_console_update_cursor(void *opaque)
2494{
aea7947c 2495 QemuConsole *s;
cd6cd8fa 2496 int count = 0;
aea7947c
GH
2497
2498 cursor_visible_phase = !cursor_visible_phase;
bf1bed81 2499
cd6cd8fa 2500 QTAILQ_FOREACH(s, &consoles, next) {
aea7947c
GH
2501 if (qemu_console_is_graphic(s) ||
2502 !qemu_console_is_visible(s)) {
2503 continue;
2504 }
2505 count++;
2506 graphic_hw_invalidate(s);
2507 }
2508
2509 if (count) {
2510 text_console_update_cursor_timer();
2511 }
bf1bed81
JK
2512}
2513
380cd056
GH
2514static const GraphicHwOps text_console_ops = {
2515 .invalidate = text_console_invalidate,
2516 .text_update = text_console_update,
2517};
2518
2fd319cf 2519static void text_console_do_init(Chardev *chr)
e7f0ad58 2520{
777357d7 2521 VCChardev *drv = VC_CHARDEV(chr);
b2bb9cc4 2522 QemuTextConsole *s = drv->console;
36671fbd
GH
2523 int g_width = 80 * FONT_WIDTH;
2524 int g_height = 24 * FONT_HEIGHT;
6d6f7c28 2525
0c9d0641 2526 fifo8_create(&s->out_fifo, 16);
3b46e624 2527
e7f0ad58
FB
2528 s->y_displayed = 0;
2529 s->y_base = 0;
2530 s->total_height = DEFAULT_BACKSCROLL;
2531 s->x = 0;
2532 s->y = 0;
b2bb9cc4 2533 if (QEMU_CONSOLE(s)->scanout.kind != SCANOUT_SURFACE) {
ebced091
MAL
2534 if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) {
2535 g_width = qemu_console_get_width(active_console, g_width);
2536 g_height = qemu_console_get_height(active_console, g_height);
321f048d 2537 }
b2bb9cc4
MAL
2538 QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(g_width, g_height);
2539 QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE;
491e114a 2540 }
6d6f7c28 2541
b2bb9cc4
MAL
2542 QEMU_CONSOLE(s)->hw_ops = &text_console_ops;
2543 QEMU_CONSOLE(s)->hw = s;
4d3b6f6e 2544
6d6f7c28 2545 /* set current text attributes to default */
6505fd8d 2546 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
e7f0ad58
FB
2547 text_console_resize(s);
2548
51bfa4d3 2549 if (chr->label) {
18595181 2550 char *msg;
51bfa4d3 2551
6505fd8d 2552 drv->t_attrib.bgcol = QEMU_COLOR_BLUE;
18595181 2553 msg = g_strdup_printf("%s console\r\n", chr->label);
f1f7a1e2 2554 qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
18595181 2555 g_free(msg);
6505fd8d 2556 drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
51bfa4d3
GH
2557 }
2558
63618135 2559 qemu_chr_be_event(chr, CHR_EVENT_OPENED);
e7f0ad58 2560}
c60e08d9 2561
777357d7
MAL
2562static void vc_chr_open(Chardev *chr,
2563 ChardevBackend *backend,
2564 bool *be_opened,
2565 Error **errp)
2796dae0 2566{
6f974c84 2567 ChardevVC *vc = backend->u.vc.data;
777357d7 2568 VCChardev *drv = VC_CHARDEV(chr);
b2bb9cc4 2569 QemuTextConsole *s;
702ec69c
GH
2570 unsigned width = 0;
2571 unsigned height = 0;
2796dae0 2572
702ec69c
GH
2573 if (vc->has_width) {
2574 width = vc->width;
2575 } else if (vc->has_cols) {
2576 width = vc->cols * FONT_WIDTH;
2577 }
491e114a 2578
702ec69c
GH
2579 if (vc->has_height) {
2580 height = vc->height;
2581 } else if (vc->has_rows) {
2582 height = vc->rows * FONT_HEIGHT;
2583 }
491e114a 2584
437fe106 2585 trace_console_txt_new(width, height);
491e114a 2586 if (width == 0 || height == 0) {
b2bb9cc4 2587 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE));
491e114a 2588 } else {
b2bb9cc4
MAL
2589 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
2590 QEMU_CONSOLE(s)->scanout.kind = SCANOUT_SURFACE;
2591 QEMU_CONSOLE(s)->surface = qemu_create_displaysurface(width, height);
491e114a
PB
2592 }
2593
491e114a 2594 s->chr = chr;
41ac54b2 2595 drv->console = s;
64840c66 2596
2fd319cf 2597 text_console_do_init(chr);
2796dae0 2598
82878dac
MAL
2599 /* console/chardev init sometimes completes elsewhere in a 2nd
2600 * stage, so defer OPENED events until they are fully initialized
2601 */
2602 *be_opened = false;
d82831db
AL
2603}
2604
c78f7137 2605void qemu_console_resize(QemuConsole *s, int width, int height)
c60e08d9 2606{
88738ea4 2607 DisplaySurface *surface = qemu_console_surface(s);
321f048d 2608
c105d60f 2609 assert(QEMU_IS_GRAPHIC_CONSOLE(s));
cd958edb 2610
88738ea4
MAL
2611 if ((s->scanout.kind != SCANOUT_SURFACE ||
2612 (surface && surface->flags & QEMU_ALLOCATED_FLAG)) &&
2613 qemu_console_get_width(s, -1) == width &&
cb8962c1 2614 qemu_console_get_height(s, -1) == height) {
cd958edb
MAL
2615 return;
2616 }
2617
321f048d
GH
2618 surface = qemu_create_displaysurface(width, height);
2619 dpy_gfx_replace_surface(s, surface);
c60e08d9 2620}
38334f76 2621
c78f7137
GH
2622DisplaySurface *qemu_console_surface(QemuConsole *console)
2623{
ebced091
MAL
2624 switch (console->scanout.kind) {
2625 case SCANOUT_SURFACE:
2626 return console->surface;
2627 default:
2628 return NULL;
2629 }
c78f7137
GH
2630}
2631
0da2ea1b 2632PixelFormat qemu_default_pixelformat(int bpp)
2633{
56bd9ea1
GH
2634 pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2635 PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
7d957bd8
AL
2636 return pf;
2637}
01f45d98 2638
db71589f
GH
2639static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2640
2641void qemu_display_register(QemuDisplay *ui)
2642{
2643 assert(ui->type < DISPLAY_TYPE__MAX);
2644 dpys[ui->type] = ui;
2645}
2646
898f9d41
GH
2647bool qemu_display_find_default(DisplayOptions *opts)
2648{
2649 static DisplayType prio[] = {
66c2207f 2650#if defined(CONFIG_GTK)
898f9d41 2651 DISPLAY_TYPE_GTK,
66c2207f
TH
2652#endif
2653#if defined(CONFIG_SDL)
898f9d41 2654 DISPLAY_TYPE_SDL,
66c2207f
TH
2655#endif
2656#if defined(CONFIG_COCOA)
898f9d41 2657 DISPLAY_TYPE_COCOA
66c2207f 2658#endif
898f9d41
GH
2659 };
2660 int i;
2661
66c2207f 2662 for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
61b4d9a2 2663 if (dpys[prio[i]] == NULL) {
c551fb0b
CF
2664 Error *local_err = NULL;
2665 int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
2666 if (rv < 0) {
2667 error_report_err(local_err);
2668 }
61b4d9a2 2669 }
898f9d41
GH
2670 if (dpys[prio[i]] == NULL) {
2671 continue;
2672 }
2673 opts->type = prio[i];
2674 return true;
2675 }
2676 return false;
2677}
2678
db71589f
GH
2679void qemu_display_early_init(DisplayOptions *opts)
2680{
2681 assert(opts->type < DISPLAY_TYPE__MAX);
2682 if (opts->type == DISPLAY_TYPE_NONE) {
2683 return;
2684 }
61b4d9a2 2685 if (dpys[opts->type] == NULL) {
c551fb0b
CF
2686 Error *local_err = NULL;
2687 int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
2688 if (rv < 0) {
2689 error_report_err(local_err);
2690 }
61b4d9a2 2691 }
db71589f
GH
2692 if (dpys[opts->type] == NULL) {
2693 error_report("Display '%s' is not available.",
c809d1d2 2694 DisplayType_str(opts->type));
db71589f
GH
2695 exit(1);
2696 }
2697 if (dpys[opts->type]->early_init) {
2698 dpys[opts->type]->early_init(opts);
2699 }
2700}
2701
2702void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2703{
2704 assert(opts->type < DISPLAY_TYPE__MAX);
2705 if (opts->type == DISPLAY_TYPE_NONE) {
2706 return;
2707 }
2708 assert(dpys[opts->type] != NULL);
2709 dpys[opts->type]->init(ds, opts);
2710}
2711
c388f408
TH
2712void qemu_display_help(void)
2713{
2714 int idx;
2715
2716 printf("Available display backend types:\n");
a1e8853e 2717 printf("none\n");
c388f408
TH
2718 for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
2719 if (!dpys[idx]) {
c551fb0b
CF
2720 Error *local_err = NULL;
2721 int rv = ui_module_load(DisplayType_str(idx), &local_err);
2722 if (rv < 0) {
2723 error_report_err(local_err);
2724 }
c388f408
TH
2725 }
2726 if (dpys[idx]) {
2727 printf("%s\n", DisplayType_str(dpys[idx]->type));
2728 }
2729 }
2730}
2731
6f974c84 2732void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
702ec69c
GH
2733{
2734 int val;
21a933ea 2735 ChardevVC *vc;
702ec69c 2736
0b663b7d 2737 backend->type = CHARDEV_BACKEND_KIND_VC;
32bafa8f 2738 vc = backend->u.vc.data = g_new0(ChardevVC, 1);
21a933ea 2739 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
702ec69c
GH
2740
2741 val = qemu_opt_get_number(opts, "width", 0);
2742 if (val != 0) {
21a933ea
EB
2743 vc->has_width = true;
2744 vc->width = val;
702ec69c
GH
2745 }
2746
2747 val = qemu_opt_get_number(opts, "height", 0);
2748 if (val != 0) {
21a933ea
EB
2749 vc->has_height = true;
2750 vc->height = val;
702ec69c
GH
2751 }
2752
2753 val = qemu_opt_get_number(opts, "cols", 0);
2754 if (val != 0) {
21a933ea
EB
2755 vc->has_cols = true;
2756 vc->cols = val;
702ec69c
GH
2757 }
2758
2759 val = qemu_opt_get_number(opts, "rows", 0);
2760 if (val != 0) {
21a933ea
EB
2761 vc->has_rows = true;
2762 vc->rows = val;
702ec69c
GH
2763 }
2764}
2765
777357d7
MAL
2766static void char_vc_class_init(ObjectClass *oc, void *data)
2767{
2768 ChardevClass *cc = CHARDEV_CLASS(oc);
2769
88cace9f 2770 cc->parse = qemu_chr_parse_vc;
777357d7
MAL
2771 cc->open = vc_chr_open;
2772 cc->chr_write = vc_chr_write;
ec222519 2773 cc->chr_accept_input = vc_chr_accept_input;
777357d7
MAL
2774 cc->chr_set_echo = vc_chr_set_echo;
2775}
2776
2777static const TypeInfo char_vc_type_info = {
2778 .name = TYPE_CHARDEV_VC,
2779 .parent = TYPE_CHARDEV,
0ec7b3e7 2780 .instance_size = sizeof(VCChardev),
777357d7
MAL
2781 .class_init = char_vc_class_init,
2782};
2783
2784void qemu_console_early_init(void)
2785{
2786 /* set the default vc driver */
2787 if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2788 type_register(&char_vc_type_info);
777357d7
MAL
2789 }
2790}