2 * SPDX-License-Identifier: MIT
5 #include "qemu/osdep.h"
7 #include "chardev/char.h"
8 #include "qapi/error.h"
9 #include "qemu/fifo8.h"
10 #include "qemu/option.h"
11 #include "ui/console.h"
14 #include "console-priv.h"
16 #define DEFAULT_BACKSCROLL 512
17 #define CONSOLE_CURSOR_PERIOD 500
19 typedef struct TextAttributes
{
29 #define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
30 .fgcol = QEMU_COLOR_WHITE, \
31 .bgcol = QEMU_COLOR_BLACK \
34 typedef struct TextCell
{
36 TextAttributes t_attrib
;
39 #define MAX_ESC_PARAMS 3
47 typedef struct QemuTextConsole
{
53 int backscroll_height
;
58 int text_x
[2], text_y
[2], cursor_invalidate
;
67 /* fifo for key pressed */
71 typedef QemuConsoleClass QemuTextConsoleClass
;
73 OBJECT_DEFINE_TYPE(QemuTextConsole
, qemu_text_console
, QEMU_TEXT_CONSOLE
, QEMU_CONSOLE
)
75 typedef struct QemuFixedTextConsole
{
76 QemuTextConsole parent
;
77 } QemuFixedTextConsole
;
79 typedef QemuTextConsoleClass QemuFixedTextConsoleClass
;
81 OBJECT_DEFINE_TYPE(QemuFixedTextConsole
, qemu_fixed_text_console
, QEMU_FIXED_TEXT_CONSOLE
, QEMU_TEXT_CONSOLE
)
85 QemuTextConsole
*console
;
88 int esc_params
[MAX_ESC_PARAMS
];
90 TextAttributes t_attrib
; /* currently active text attributes */
93 typedef struct VCChardev VCChardev
;
95 static const pixman_color_t color_table_rgb
[2][8] = {
97 [QEMU_COLOR_BLACK
] = QEMU_PIXMAN_COLOR_BLACK
,
98 [QEMU_COLOR_BLUE
] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */
99 [QEMU_COLOR_GREEN
] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */
100 [QEMU_COLOR_CYAN
] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */
101 [QEMU_COLOR_RED
] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */
102 [QEMU_COLOR_MAGENTA
] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */
103 [QEMU_COLOR_YELLOW
] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */
104 [QEMU_COLOR_WHITE
] = QEMU_PIXMAN_COLOR_GRAY
,
107 [QEMU_COLOR_BLACK
] = QEMU_PIXMAN_COLOR_BLACK
,
108 [QEMU_COLOR_BLUE
] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */
109 [QEMU_COLOR_GREEN
] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */
110 [QEMU_COLOR_CYAN
] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */
111 [QEMU_COLOR_RED
] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */
112 [QEMU_COLOR_MAGENTA
] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */
113 [QEMU_COLOR_YELLOW
] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */
114 [QEMU_COLOR_WHITE
] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */
118 static bool cursor_visible_phase
;
119 static QEMUTimer
*cursor_timer
;
122 qemu_text_console_get_label(QemuTextConsole
*c
)
124 return c
->chr
? c
->chr
->label
: NULL
;
127 static void qemu_console_fill_rect(QemuConsole
*con
, int posx
, int posy
,
128 int width
, int height
, pixman_color_t color
)
130 DisplaySurface
*surface
= qemu_console_surface(con
);
131 pixman_rectangle16_t rect
= {
132 .x
= posx
, .y
= posy
, .width
= width
, .height
= height
136 pixman_image_fill_rectangles(PIXMAN_OP_SRC
, surface
->image
,
140 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
141 static void qemu_console_bitblt(QemuConsole
*con
,
142 int xs
, int ys
, int xd
, int yd
, int w
, int h
)
144 DisplaySurface
*surface
= qemu_console_surface(con
);
147 pixman_image_composite(PIXMAN_OP_SRC
,
148 surface
->image
, NULL
, surface
->image
,
149 xs
, ys
, 0, 0, xd
, yd
, w
, h
);
152 static void vga_putcharxy(QemuConsole
*s
, int x
, int y
, int ch
,
153 TextAttributes
*t_attrib
)
155 static pixman_image_t
*glyphs
[256];
156 DisplaySurface
*surface
= qemu_console_surface(s
);
157 pixman_color_t fgcol
, bgcol
;
160 if (t_attrib
->invers
) {
161 bgcol
= color_table_rgb
[t_attrib
->bold
][t_attrib
->fgcol
];
162 fgcol
= color_table_rgb
[t_attrib
->bold
][t_attrib
->bgcol
];
164 fgcol
= color_table_rgb
[t_attrib
->bold
][t_attrib
->fgcol
];
165 bgcol
= color_table_rgb
[t_attrib
->bold
][t_attrib
->bgcol
];
169 glyphs
[ch
] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT
, vgafont16
, ch
);
171 qemu_pixman_glyph_render(glyphs
[ch
], surface
->image
,
172 &fgcol
, &bgcol
, x
, y
, FONT_WIDTH
, FONT_HEIGHT
);
175 static void invalidate_xy(QemuTextConsole
*s
, int x
, int y
)
177 if (!qemu_console_is_visible(QEMU_CONSOLE(s
))) {
180 if (s
->update_x0
> x
* FONT_WIDTH
)
181 s
->update_x0
= x
* FONT_WIDTH
;
182 if (s
->update_y0
> y
* FONT_HEIGHT
)
183 s
->update_y0
= y
* FONT_HEIGHT
;
184 if (s
->update_x1
< (x
+ 1) * FONT_WIDTH
)
185 s
->update_x1
= (x
+ 1) * FONT_WIDTH
;
186 if (s
->update_y1
< (y
+ 1) * FONT_HEIGHT
)
187 s
->update_y1
= (y
+ 1) * FONT_HEIGHT
;
190 static void console_show_cursor(QemuTextConsole
*s
, int show
)
196 s
->cursor_invalidate
= 1;
201 y1
= (s
->y_base
+ s
->y
) % s
->total_height
;
202 y
= y1
- s
->y_displayed
;
204 y
+= s
->total_height
;
207 c
= &s
->cells
[y1
* s
->width
+ x
];
208 if (show
&& cursor_visible_phase
) {
209 TextAttributes t_attrib
= TEXT_ATTRIBUTES_DEFAULT
;
210 t_attrib
.invers
= !(t_attrib
.invers
); /* invert fg and bg */
211 vga_putcharxy(QEMU_CONSOLE(s
), x
, y
, c
->ch
, &t_attrib
);
213 vga_putcharxy(QEMU_CONSOLE(s
), x
, y
, c
->ch
, &(c
->t_attrib
));
215 invalidate_xy(s
, x
, y
);
219 static void console_refresh(QemuTextConsole
*s
)
221 DisplaySurface
*surface
= qemu_console_surface(QEMU_CONSOLE(s
));
228 s
->text_x
[1] = s
->width
- 1;
229 s
->text_y
[1] = s
->height
- 1;
230 s
->cursor_invalidate
= 1;
232 qemu_console_fill_rect(QEMU_CONSOLE(s
), 0, 0, surface_width(surface
), surface_height(surface
),
233 color_table_rgb
[0][QEMU_COLOR_BLACK
]);
235 for (y
= 0; y
< s
->height
; y
++) {
236 c
= s
->cells
+ y1
* s
->width
;
237 for (x
= 0; x
< s
->width
; x
++) {
238 vga_putcharxy(QEMU_CONSOLE(s
), x
, y
, c
->ch
,
242 if (++y1
== s
->total_height
) {
246 console_show_cursor(s
, 1);
247 dpy_gfx_update(QEMU_CONSOLE(s
), 0, 0,
248 surface_width(surface
), surface_height(surface
));
251 static void console_scroll(QemuTextConsole
*s
, int ydelta
)
256 for(i
= 0; i
< ydelta
; i
++) {
257 if (s
->y_displayed
== s
->y_base
)
259 if (++s
->y_displayed
== s
->total_height
)
264 i
= s
->backscroll_height
;
265 if (i
> s
->total_height
- s
->height
)
266 i
= s
->total_height
- s
->height
;
269 y1
+= s
->total_height
;
270 for(i
= 0; i
< ydelta
; i
++) {
271 if (s
->y_displayed
== y1
)
273 if (--s
->y_displayed
< 0)
274 s
->y_displayed
= s
->total_height
- 1;
280 static void kbd_send_chars(QemuTextConsole
*s
)
284 len
= qemu_chr_be_can_write(s
->chr
);
285 avail
= fifo8_num_used(&s
->out_fifo
);
286 while (len
> 0 && avail
> 0) {
290 buf
= fifo8_pop_buf(&s
->out_fifo
, MIN(len
, avail
), &size
);
291 qemu_chr_be_write(s
->chr
, buf
, size
);
292 len
= qemu_chr_be_can_write(s
->chr
);
297 /* called when an ascii key is pressed */
298 void qemu_text_console_handle_keysym(QemuTextConsole
*s
, int keysym
)
305 case QEMU_KEY_CTRL_UP
:
306 console_scroll(s
, -1);
308 case QEMU_KEY_CTRL_DOWN
:
309 console_scroll(s
, 1);
311 case QEMU_KEY_CTRL_PAGEUP
:
312 console_scroll(s
, -10);
314 case QEMU_KEY_CTRL_PAGEDOWN
:
315 console_scroll(s
, 10);
318 /* convert the QEMU keysym to VT100 key string */
320 if (keysym
>= 0xe100 && keysym
<= 0xe11f) {
325 *q
++ = '0' + (c
/ 10);
326 *q
++ = '0' + (c
% 10);
328 } else if (keysym
>= 0xe120 && keysym
<= 0xe17f) {
331 *q
++ = keysym
& 0xff;
332 } else if (s
->echo
&& (keysym
== '\r' || keysym
== '\n')) {
333 qemu_chr_write(s
->chr
, (uint8_t *)"\r", 1, true);
339 qemu_chr_write(s
->chr
, buf
, q
- buf
, true);
341 num_free
= fifo8_num_free(&s
->out_fifo
);
342 fifo8_push_all(&s
->out_fifo
, buf
, MIN(num_free
, q
- buf
));
348 static void text_console_update(void *opaque
, console_ch_t
*chardata
)
350 QemuTextConsole
*s
= QEMU_TEXT_CONSOLE(opaque
);
353 if (s
->text_x
[0] <= s
->text_x
[1]) {
354 src
= (s
->y_base
+ s
->text_y
[0]) * s
->width
;
355 chardata
+= s
->text_y
[0] * s
->width
;
356 for (i
= s
->text_y
[0]; i
<= s
->text_y
[1]; i
++)
357 for (j
= 0; j
< s
->width
; j
++, src
++) {
358 console_write_ch(chardata
++,
359 ATTR2CHTYPE(s
->cells
[src
].ch
,
360 s
->cells
[src
].t_attrib
.fgcol
,
361 s
->cells
[src
].t_attrib
.bgcol
,
362 s
->cells
[src
].t_attrib
.bold
));
364 dpy_text_update(QEMU_CONSOLE(s
), s
->text_x
[0], s
->text_y
[0],
365 s
->text_x
[1] - s
->text_x
[0], i
- s
->text_y
[0]);
366 s
->text_x
[0] = s
->width
;
367 s
->text_y
[0] = s
->height
;
371 if (s
->cursor_invalidate
) {
372 dpy_text_cursor(QEMU_CONSOLE(s
), s
->x
, s
->y
);
373 s
->cursor_invalidate
= 0;
377 static void text_console_resize(QemuTextConsole
*t
)
379 QemuConsole
*s
= QEMU_CONSOLE(t
);
380 TextCell
*cells
, *c
, *c1
;
381 int w1
, x
, y
, last_width
, w
, h
;
383 assert(s
->scanout
.kind
== SCANOUT_SURFACE
);
385 w
= surface_width(s
->surface
) / FONT_WIDTH
;
386 h
= surface_height(s
->surface
) / FONT_HEIGHT
;
387 if (w
== t
->width
&& h
== t
->height
) {
391 last_width
= t
->width
;
395 w1
= MIN(t
->width
, last_width
);
397 cells
= g_new(TextCell
, t
->width
* t
->total_height
+ 1);
398 for (y
= 0; y
< t
->total_height
; y
++) {
399 c
= &cells
[y
* t
->width
];
401 c1
= &t
->cells
[y
* last_width
];
402 for (x
= 0; x
< w1
; x
++) {
406 for (x
= w1
; x
< t
->width
; x
++) {
408 c
->t_attrib
= TEXT_ATTRIBUTES_DEFAULT
;
416 static void vc_put_lf(VCChardev
*vc
)
418 QemuTextConsole
*s
= vc
->console
;
423 if (s
->y
>= s
->height
) {
424 s
->y
= s
->height
- 1;
426 if (s
->y_displayed
== s
->y_base
) {
427 if (++s
->y_displayed
== s
->total_height
)
430 if (++s
->y_base
== s
->total_height
)
432 if (s
->backscroll_height
< s
->total_height
)
433 s
->backscroll_height
++;
434 y1
= (s
->y_base
+ s
->height
- 1) % s
->total_height
;
435 c
= &s
->cells
[y1
* s
->width
];
436 for(x
= 0; x
< s
->width
; x
++) {
438 c
->t_attrib
= TEXT_ATTRIBUTES_DEFAULT
;
441 if (s
->y_displayed
== s
->y_base
) {
444 s
->text_x
[1] = s
->width
- 1;
445 s
->text_y
[1] = s
->height
- 1;
447 qemu_console_bitblt(QEMU_CONSOLE(s
), 0, FONT_HEIGHT
, 0, 0,
448 s
->width
* FONT_WIDTH
,
449 (s
->height
- 1) * FONT_HEIGHT
);
450 qemu_console_fill_rect(QEMU_CONSOLE(s
), 0, (s
->height
- 1) * FONT_HEIGHT
,
451 s
->width
* FONT_WIDTH
, FONT_HEIGHT
,
452 color_table_rgb
[0][TEXT_ATTRIBUTES_DEFAULT
.bgcol
]);
455 s
->update_x1
= s
->width
* FONT_WIDTH
;
456 s
->update_y1
= s
->height
* FONT_HEIGHT
;
461 /* Set console attributes depending on the current escape codes.
462 * NOTE: I know this code is not very efficient (checking every color for it
463 * self) but it is more readable and better maintainable.
465 static void vc_handle_escape(VCChardev
*vc
)
469 for (i
= 0; i
< vc
->nb_esc_params
; i
++) {
470 switch (vc
->esc_params
[i
]) {
471 case 0: /* reset all console attributes to default */
472 vc
->t_attrib
= TEXT_ATTRIBUTES_DEFAULT
;
475 vc
->t_attrib
.bold
= 1;
478 vc
->t_attrib
.uline
= 1;
481 vc
->t_attrib
.blink
= 1;
484 vc
->t_attrib
.invers
= 1;
487 vc
->t_attrib
.unvisible
= 1;
490 vc
->t_attrib
.bold
= 0;
493 vc
->t_attrib
.uline
= 0;
496 vc
->t_attrib
.blink
= 0;
499 vc
->t_attrib
.invers
= 0;
502 vc
->t_attrib
.unvisible
= 0;
504 /* set foreground color */
506 vc
->t_attrib
.fgcol
= QEMU_COLOR_BLACK
;
509 vc
->t_attrib
.fgcol
= QEMU_COLOR_RED
;
512 vc
->t_attrib
.fgcol
= QEMU_COLOR_GREEN
;
515 vc
->t_attrib
.fgcol
= QEMU_COLOR_YELLOW
;
518 vc
->t_attrib
.fgcol
= QEMU_COLOR_BLUE
;
521 vc
->t_attrib
.fgcol
= QEMU_COLOR_MAGENTA
;
524 vc
->t_attrib
.fgcol
= QEMU_COLOR_CYAN
;
527 vc
->t_attrib
.fgcol
= QEMU_COLOR_WHITE
;
529 /* set background color */
531 vc
->t_attrib
.bgcol
= QEMU_COLOR_BLACK
;
534 vc
->t_attrib
.bgcol
= QEMU_COLOR_RED
;
537 vc
->t_attrib
.bgcol
= QEMU_COLOR_GREEN
;
540 vc
->t_attrib
.bgcol
= QEMU_COLOR_YELLOW
;
543 vc
->t_attrib
.bgcol
= QEMU_COLOR_BLUE
;
546 vc
->t_attrib
.bgcol
= QEMU_COLOR_MAGENTA
;
549 vc
->t_attrib
.bgcol
= QEMU_COLOR_CYAN
;
552 vc
->t_attrib
.bgcol
= QEMU_COLOR_WHITE
;
558 static void vc_update_xy(VCChardev
*vc
, int x
, int y
)
560 QemuTextConsole
*s
= vc
->console
;
564 s
->text_x
[0] = MIN(s
->text_x
[0], x
);
565 s
->text_x
[1] = MAX(s
->text_x
[1], x
);
566 s
->text_y
[0] = MIN(s
->text_y
[0], y
);
567 s
->text_y
[1] = MAX(s
->text_y
[1], y
);
569 y1
= (s
->y_base
+ y
) % s
->total_height
;
570 y2
= y1
- s
->y_displayed
;
572 y2
+= s
->total_height
;
574 if (y2
< s
->height
) {
578 c
= &s
->cells
[y1
* s
->width
+ x
];
579 vga_putcharxy(QEMU_CONSOLE(s
), x
, y2
, c
->ch
,
581 invalidate_xy(s
, x
, y2
);
585 static void vc_clear_xy(VCChardev
*vc
, int x
, int y
)
587 QemuTextConsole
*s
= vc
->console
;
588 int y1
= (s
->y_base
+ y
) % s
->total_height
;
592 TextCell
*c
= &s
->cells
[y1
* s
->width
+ x
];
594 c
->t_attrib
= TEXT_ATTRIBUTES_DEFAULT
;
595 vc_update_xy(vc
, x
, y
);
598 static void vc_put_one(VCChardev
*vc
, int ch
)
600 QemuTextConsole
*s
= vc
->console
;
603 if (s
->x
>= s
->width
) {
608 y1
= (s
->y_base
+ s
->y
) % s
->total_height
;
609 c
= &s
->cells
[y1
* s
->width
+ s
->x
];
611 c
->t_attrib
= vc
->t_attrib
;
612 vc_update_xy(vc
, s
->x
, s
->y
);
616 static void vc_respond_str(VCChardev
*vc
, const char *buf
)
619 vc_put_one(vc
, *buf
);
624 /* set cursor, checking bounds */
625 static void vc_set_cursor(VCChardev
*vc
, int x
, int y
)
627 QemuTextConsole
*s
= vc
->console
;
635 if (y
>= s
->height
) {
646 static void vc_putchar(VCChardev
*vc
, int ch
)
648 QemuTextConsole
*s
= vc
->console
;
656 case '\r': /* carriage return */
659 case '\n': /* newline */
662 case '\b': /* backspace */
666 case '\t': /* tabspace */
667 if (s
->x
+ (8 - (s
->x
% 8)) > s
->width
) {
671 s
->x
= s
->x
+ (8 - (s
->x
% 8));
674 case '\a': /* alert aka. bell */
675 /* TODO: has to be implemented */
678 /* SI (shift in), character set 0 (ignored) */
681 /* SO (shift out), character set 1 (ignored) */
683 case 27: /* esc (introducing an escape sequence) */
684 vc
->state
= TTY_STATE_ESC
;
691 case TTY_STATE_ESC
: /* check if it is a terminal escape sequence */
693 for(i
=0;i
<MAX_ESC_PARAMS
;i
++)
694 vc
->esc_params
[i
] = 0;
695 vc
->nb_esc_params
= 0;
696 vc
->state
= TTY_STATE_CSI
;
698 vc
->state
= TTY_STATE_NORM
;
701 case TTY_STATE_CSI
: /* handle escape sequence parameters */
702 if (ch
>= '0' && ch
<= '9') {
703 if (vc
->nb_esc_params
< MAX_ESC_PARAMS
) {
704 int *param
= &vc
->esc_params
[vc
->nb_esc_params
];
705 int digit
= (ch
- '0');
707 *param
= (*param
<= (INT_MAX
- digit
) / 10) ?
708 *param
* 10 + digit
: INT_MAX
;
711 if (vc
->nb_esc_params
< MAX_ESC_PARAMS
)
713 if (ch
== ';' || ch
== '?') {
716 trace_console_putchar_csi(vc
->esc_params
[0], vc
->esc_params
[1],
717 ch
, vc
->nb_esc_params
);
718 vc
->state
= TTY_STATE_NORM
;
722 if (vc
->esc_params
[0] == 0) {
723 vc
->esc_params
[0] = 1;
725 vc_set_cursor(vc
, s
->x
, s
->y
- vc
->esc_params
[0]);
728 /* move cursor down */
729 if (vc
->esc_params
[0] == 0) {
730 vc
->esc_params
[0] = 1;
732 vc_set_cursor(vc
, s
->x
, s
->y
+ vc
->esc_params
[0]);
735 /* move cursor right */
736 if (vc
->esc_params
[0] == 0) {
737 vc
->esc_params
[0] = 1;
739 vc_set_cursor(vc
, s
->x
+ vc
->esc_params
[0], s
->y
);
742 /* move cursor left */
743 if (vc
->esc_params
[0] == 0) {
744 vc
->esc_params
[0] = 1;
746 vc_set_cursor(vc
, s
->x
- vc
->esc_params
[0], s
->y
);
749 /* move cursor to column */
750 vc_set_cursor(vc
, vc
->esc_params
[0] - 1, s
->y
);
754 /* move cursor to row, column */
755 vc_set_cursor(vc
, vc
->esc_params
[1] - 1, vc
->esc_params
[0] - 1);
758 switch (vc
->esc_params
[0]) {
760 /* clear to end of screen */
761 for (y
= s
->y
; y
< s
->height
; y
++) {
762 for (x
= 0; x
< s
->width
; x
++) {
763 if (y
== s
->y
&& x
< s
->x
) {
766 vc_clear_xy(vc
, x
, y
);
771 /* clear from beginning of screen */
772 for (y
= 0; y
<= s
->y
; y
++) {
773 for (x
= 0; x
< s
->width
; x
++) {
774 if (y
== s
->y
&& x
> s
->x
) {
777 vc_clear_xy(vc
, x
, y
);
782 /* clear entire screen */
783 for (y
= 0; y
<= s
->height
; y
++) {
784 for (x
= 0; x
< s
->width
; x
++) {
785 vc_clear_xy(vc
, x
, y
);
792 switch (vc
->esc_params
[0]) {
795 for(x
= s
->x
; x
< s
->width
; x
++) {
796 vc_clear_xy(vc
, x
, s
->y
);
800 /* clear from beginning of line */
801 for (x
= 0; x
<= s
->x
&& x
< s
->width
; x
++) {
802 vc_clear_xy(vc
, x
, s
->y
);
806 /* clear entire line */
807 for(x
= 0; x
< s
->width
; x
++) {
808 vc_clear_xy(vc
, x
, s
->y
);
814 vc_handle_escape(vc
);
817 switch (vc
->esc_params
[0]) {
819 /* report console status (always succeed)*/
820 vc_respond_str(vc
, "\033[0n");
823 /* report cursor position */
824 sprintf(response
, "\033[%d;%dR",
825 (s
->y_base
+ s
->y
) % s
->total_height
+ 1,
827 vc_respond_str(vc
, response
);
832 /* save cursor position */
837 /* restore cursor position */
842 trace_console_putchar_unhandled(ch
);
850 #define TYPE_CHARDEV_VC "chardev-vc"
851 DECLARE_INSTANCE_CHECKER(VCChardev
, VC_CHARDEV
,
854 static int vc_chr_write(Chardev
*chr
, const uint8_t *buf
, int len
)
856 VCChardev
*drv
= VC_CHARDEV(chr
);
857 QemuTextConsole
*s
= drv
->console
;
860 s
->update_x0
= s
->width
* FONT_WIDTH
;
861 s
->update_y0
= s
->height
* FONT_HEIGHT
;
864 console_show_cursor(s
, 0);
865 for(i
= 0; i
< len
; i
++) {
866 vc_putchar(drv
, buf
[i
]);
868 console_show_cursor(s
, 1);
869 if (s
->update_x0
< s
->update_x1
) {
870 dpy_gfx_update(QEMU_CONSOLE(s
), s
->update_x0
, s
->update_y0
,
871 s
->update_x1
- s
->update_x0
,
872 s
->update_y1
- s
->update_y0
);
877 void qemu_text_console_update_cursor(void)
879 cursor_visible_phase
= !cursor_visible_phase
;
881 if (qemu_invalidate_text_consoles()) {
882 timer_mod(cursor_timer
,
883 qemu_clock_get_ms(QEMU_CLOCK_REALTIME
) + CONSOLE_CURSOR_PERIOD
/ 2);
888 cursor_timer_cb(void *opaque
)
890 qemu_text_console_update_cursor();
893 static void text_console_invalidate(void *opaque
)
895 QemuTextConsole
*s
= QEMU_TEXT_CONSOLE(opaque
);
897 if (!QEMU_IS_FIXED_TEXT_CONSOLE(s
)) {
898 text_console_resize(QEMU_TEXT_CONSOLE(s
));
904 qemu_text_console_finalize(Object
*obj
)
909 qemu_text_console_class_init(ObjectClass
*oc
, void *data
)
912 cursor_timer
= timer_new_ms(QEMU_CLOCK_REALTIME
, cursor_timer_cb
, NULL
);
916 static const GraphicHwOps text_console_ops
= {
917 .invalidate
= text_console_invalidate
,
918 .text_update
= text_console_update
,
922 qemu_text_console_init(Object
*obj
)
924 QemuTextConsole
*c
= QEMU_TEXT_CONSOLE(obj
);
926 fifo8_create(&c
->out_fifo
, 16);
927 c
->total_height
= DEFAULT_BACKSCROLL
;
928 QEMU_CONSOLE(c
)->hw_ops
= &text_console_ops
;
929 QEMU_CONSOLE(c
)->hw
= c
;
933 qemu_fixed_text_console_finalize(Object
*obj
)
938 qemu_fixed_text_console_class_init(ObjectClass
*oc
, void *data
)
943 qemu_fixed_text_console_init(Object
*obj
)
947 static void vc_chr_accept_input(Chardev
*chr
)
949 VCChardev
*drv
= VC_CHARDEV(chr
);
951 kbd_send_chars(drv
->console
);
954 static void vc_chr_set_echo(Chardev
*chr
, bool echo
)
956 VCChardev
*drv
= VC_CHARDEV(chr
);
958 drv
->console
->echo
= echo
;
961 void qemu_text_console_select(QemuTextConsole
*c
)
963 dpy_text_resize(QEMU_CONSOLE(c
), c
->width
, c
->height
);
964 qemu_text_console_update_cursor();
967 static void vc_chr_open(Chardev
*chr
,
968 ChardevBackend
*backend
,
972 ChardevVC
*vc
= backend
->u
.vc
.data
;
973 VCChardev
*drv
= VC_CHARDEV(chr
);
980 } else if (vc
->has_cols
) {
981 width
= vc
->cols
* FONT_WIDTH
;
984 if (vc
->has_height
) {
986 } else if (vc
->has_rows
) {
987 height
= vc
->rows
* FONT_HEIGHT
;
990 trace_console_txt_new(width
, height
);
991 if (width
== 0 || height
== 0) {
992 s
= QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE
));
993 width
= qemu_console_get_width(NULL
, 80 * FONT_WIDTH
);
994 height
= qemu_console_get_height(NULL
, 24 * FONT_HEIGHT
);
996 s
= QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE
));
999 dpy_gfx_replace_surface(QEMU_CONSOLE(s
), qemu_create_displaysurface(width
, height
));
1004 /* set current text attributes to default */
1005 drv
->t_attrib
= TEXT_ATTRIBUTES_DEFAULT
;
1006 text_console_resize(s
);
1011 drv
->t_attrib
.bgcol
= QEMU_COLOR_BLUE
;
1012 msg
= g_strdup_printf("%s console\r\n", chr
->label
);
1013 qemu_chr_write(chr
, (uint8_t *)msg
, strlen(msg
), true);
1015 drv
->t_attrib
= TEXT_ATTRIBUTES_DEFAULT
;
1021 static void vc_chr_parse(QemuOpts
*opts
, ChardevBackend
*backend
, Error
**errp
)
1026 backend
->type
= CHARDEV_BACKEND_KIND_VC
;
1027 vc
= backend
->u
.vc
.data
= g_new0(ChardevVC
, 1);
1028 qemu_chr_parse_common(opts
, qapi_ChardevVC_base(vc
));
1030 val
= qemu_opt_get_number(opts
, "width", 0);
1032 vc
->has_width
= true;
1036 val
= qemu_opt_get_number(opts
, "height", 0);
1038 vc
->has_height
= true;
1042 val
= qemu_opt_get_number(opts
, "cols", 0);
1044 vc
->has_cols
= true;
1048 val
= qemu_opt_get_number(opts
, "rows", 0);
1050 vc
->has_rows
= true;
1055 static void char_vc_class_init(ObjectClass
*oc
, void *data
)
1057 ChardevClass
*cc
= CHARDEV_CLASS(oc
);
1059 cc
->parse
= vc_chr_parse
;
1060 cc
->open
= vc_chr_open
;
1061 cc
->chr_write
= vc_chr_write
;
1062 cc
->chr_accept_input
= vc_chr_accept_input
;
1063 cc
->chr_set_echo
= vc_chr_set_echo
;
1066 static const TypeInfo char_vc_type_info
= {
1067 .name
= TYPE_CHARDEV_VC
,
1068 .parent
= TYPE_CHARDEV
,
1069 .instance_size
= sizeof(VCChardev
),
1070 .class_init
= char_vc_class_init
,
1073 void qemu_console_early_init(void)
1075 /* set the default vc driver */
1076 if (!object_class_by_name(TYPE_CHARDEV_VC
)) {
1077 type_register(&char_vc_type_info
);