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