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