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