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