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