blob: 584f069c58b2e422d59c8fcca9c5d9a9938289c1 [file] [log] [blame]
bellarde7f0ad52004-07-14 17:28:59 +00001/*
2 * QEMU graphical console
ths5fafdf22007-09-16 21:08:06 +00003 *
bellarde7f0ad52004-07-14 17:28:59 +00004 * Copyright (c) 2004 Fabrice Bellard
ths5fafdf22007-09-16 21:08:06 +00005 *
bellarde7f0ad52004-07-14 17:28:59 +00006 * 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 */
pbrook87ecb682007-11-17 17:14:51 +000024#include "qemu-common.h"
Paolo Bonzini28ecbae2012-11-28 12:06:30 +010025#include "ui/console.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010026#include "qemu/timer.h"
Luiz Capitulinoad39cf62012-05-24 13:48:23 -030027#include "qmp-commands.h"
Paolo Bonzinidccfcd02013-04-08 16:55:25 +020028#include "sysemu/char.h"
bellarde7f0ad52004-07-14 17:28:59 +000029
pbrook6d6f7c22006-03-11 15:35:30 +000030//#define DEBUG_CONSOLE
bellarde7f0ad52004-07-14 17:28:59 +000031#define DEFAULT_BACKSCROLL 512
32#define MAX_CONSOLES 12
Jan Kiszkabf1bed82012-07-10 22:00:55 +020033#define CONSOLE_CURSOR_PERIOD 500
bellarde7f0ad52004-07-14 17:28:59 +000034
bellard26489842006-06-25 17:37:36 +000035#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
36#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
bellarde7f0ad52004-07-14 17:28:59 +000037
pbrook6d6f7c22006-03-11 15:35:30 +000038typedef struct TextAttributes {
39 uint8_t fgcol:4;
40 uint8_t bgcol:4;
41 uint8_t bold:1;
42 uint8_t uline:1;
43 uint8_t blink:1;
44 uint8_t invers:1;
45 uint8_t unvisible:1;
46} TextAttributes;
47
bellarde7f0ad52004-07-14 17:28:59 +000048typedef struct TextCell {
49 uint8_t ch;
pbrook6d6f7c22006-03-11 15:35:30 +000050 TextAttributes t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +000051} TextCell;
52
53#define MAX_ESC_PARAMS 3
54
55enum TTYState {
56 TTY_STATE_NORM,
57 TTY_STATE_ESC,
58 TTY_STATE_CSI,
59};
60
bellarde15d7372006-06-25 16:26:29 +000061typedef struct QEMUFIFO {
62 uint8_t *buf;
63 int buf_size;
64 int count, wptr, rptr;
65} QEMUFIFO;
66
pbrook9596ebb2007-11-18 01:44:38 +000067static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000068{
69 int l, len;
70
71 l = f->buf_size - f->count;
72 if (len1 > l)
73 len1 = l;
74 len = len1;
75 while (len > 0) {
76 l = f->buf_size - f->wptr;
77 if (l > len)
78 l = len;
79 memcpy(f->buf + f->wptr, buf, l);
80 f->wptr += l;
81 if (f->wptr >= f->buf_size)
82 f->wptr = 0;
83 buf += l;
84 len -= l;
85 }
86 f->count += len1;
87 return len1;
88}
89
pbrook9596ebb2007-11-18 01:44:38 +000090static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000091{
92 int l, len;
93
94 if (len1 > f->count)
95 len1 = f->count;
96 len = len1;
97 while (len > 0) {
98 l = f->buf_size - f->rptr;
99 if (l > len)
100 l = len;
101 memcpy(buf, f->buf + f->rptr, l);
102 f->rptr += l;
103 if (f->rptr >= f->buf_size)
104 f->rptr = 0;
105 buf += l;
106 len -= l;
107 }
108 f->count -= len1;
109 return len1;
110}
111
thsaf3a9032007-07-11 23:14:59 +0000112typedef enum {
113 GRAPHIC_CONSOLE,
balrogc21bbcf2008-09-24 03:32:33 +0000114 TEXT_CONSOLE,
115 TEXT_CONSOLE_FIXED_SIZE
Anthony Liguoric227f092009-10-01 16:12:16 -0500116} console_type_t;
thsaf3a9032007-07-11 23:14:59 +0000117
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200118struct QemuConsole {
Jan Kiszkaf81bdef2011-09-16 00:48:07 +0200119 int index;
Anthony Liguoric227f092009-10-01 16:12:16 -0500120 console_type_t console_type;
bellarde7f0ad52004-07-14 17:28:59 +0000121 DisplayState *ds;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200122
pbrook95219892006-04-09 01:06:34 +0000123 /* Graphic console state. */
124 vga_hw_update_ptr hw_update;
125 vga_hw_invalidate_ptr hw_invalidate;
126 vga_hw_screen_dump_ptr hw_screen_dump;
balrog4d3b6f62008-02-10 16:33:14 +0000127 vga_hw_text_update_ptr hw_text_update;
pbrook95219892006-04-09 01:06:34 +0000128 void *hw;
bellarde7f0ad52004-07-14 17:28:59 +0000129 int g_width, g_height;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200130
131 /* Text console state */
bellarde7f0ad52004-07-14 17:28:59 +0000132 int width;
133 int height;
134 int total_height;
135 int backscroll_height;
bellarde7f0ad52004-07-14 17:28:59 +0000136 int x, y;
thsadb47962007-01-16 23:02:36 +0000137 int x_saved, y_saved;
bellarde7f0ad52004-07-14 17:28:59 +0000138 int y_displayed;
139 int y_base;
pbrook6d6f7c22006-03-11 15:35:30 +0000140 TextAttributes t_attrib_default; /* default text attributes */
141 TextAttributes t_attrib; /* currently active text attributes */
bellarde7f0ad52004-07-14 17:28:59 +0000142 TextCell *cells;
balrog4d3b6f62008-02-10 16:33:14 +0000143 int text_x[2], text_y[2], cursor_invalidate;
Paolo Bonzini41048332010-12-23 13:42:52 +0100144 int echo;
Jan Kiszkabf1bed82012-07-10 22:00:55 +0200145 bool cursor_visible_phase;
146 QEMUTimer *cursor_timer;
bellarde7f0ad52004-07-14 17:28:59 +0000147
pbrook14778c22009-01-21 03:02:52 +0000148 int update_x0;
149 int update_y0;
150 int update_x1;
151 int update_y1;
152
bellarde7f0ad52004-07-14 17:28:59 +0000153 enum TTYState state;
154 int esc_params[MAX_ESC_PARAMS];
155 int nb_esc_params;
156
pbrooke5b0bc42007-01-27 23:46:43 +0000157 CharDriverState *chr;
bellarde15d7372006-06-25 16:26:29 +0000158 /* fifo for key pressed */
159 QEMUFIFO out_fifo;
160 uint8_t out_fifo_buf[16];
161 QEMUTimer *kbd_timer;
bellarde7f0ad52004-07-14 17:28:59 +0000162};
163
Paolo Bonzini98b50082010-02-11 00:29:57 +0100164static DisplayState *display_state;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200165static QemuConsole *active_console;
166static QemuConsole *consoles[MAX_CONSOLES];
bellarde7f0ad52004-07-14 17:28:59 +0000167static int nb_consoles = 0;
168
pbrook95219892006-04-09 01:06:34 +0000169void vga_hw_update(void)
170{
thsadb47962007-01-16 23:02:36 +0000171 if (active_console && active_console->hw_update)
pbrook95219892006-04-09 01:06:34 +0000172 active_console->hw_update(active_console->hw);
173}
174
175void vga_hw_invalidate(void)
176{
Gerd Hoffmann26572b82010-05-20 15:23:06 +0200177 if (active_console && active_console->hw_invalidate)
pbrook95219892006-04-09 01:06:34 +0000178 active_console->hw_invalidate(active_console->hw);
179}
180
Luiz Capitulinoad39cf62012-05-24 13:48:23 -0300181void qmp_screendump(const char *filename, Error **errp)
pbrook95219892006-04-09 01:06:34 +0000182{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200183 QemuConsole *previous_active_console;
Gerd Hoffmann45efb162012-02-24 12:43:45 +0100184 bool cswitch;
balrog8571c052008-07-19 13:04:26 +0000185
186 previous_active_console = active_console;
Gerd Hoffmann45efb162012-02-24 12:43:45 +0100187 cswitch = previous_active_console && previous_active_console->index != 0;
Jan Kiszkaf81bdef2011-09-16 00:48:07 +0200188
balrog8571c052008-07-19 13:04:26 +0000189 /* There is currently no way of specifying which screen we want to dump,
aurel327b455222008-09-02 00:09:16 +0000190 so always dump the first one. */
Gerd Hoffmann45efb162012-02-24 12:43:45 +0100191 if (cswitch) {
192 console_select(0);
193 }
Jan Kiszkaf81bdef2011-09-16 00:48:07 +0200194 if (consoles[0] && consoles[0]->hw_screen_dump) {
Luiz Capitulinoad39cf62012-05-24 13:48:23 -0300195 consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
Gerd Hoffmann16735102012-02-24 12:43:44 +0100196 } else {
Markus Armbruster312fd5f2013-02-08 21:22:16 +0100197 error_setg(errp, "device doesn't support screendump");
Jan Kiszkaf81bdef2011-09-16 00:48:07 +0200198 }
199
Gerd Hoffmann45efb162012-02-24 12:43:45 +0100200 if (cswitch) {
Alexander Graf33bcd982011-11-18 16:41:59 +0100201 console_select(previous_active_console->index);
202 }
pbrook95219892006-04-09 01:06:34 +0000203}
204
Anthony Liguoric227f092009-10-01 16:12:16 -0500205void vga_hw_text_update(console_ch_t *chardata)
balrog4d3b6f62008-02-10 16:33:14 +0000206{
207 if (active_console && active_console->hw_text_update)
208 active_console->hw_text_update(active_console->hw, chardata);
209}
210
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100211static void vga_fill_rect(QemuConsole *con,
212 int posx, int posy, int width, int height,
213 uint32_t color)
bellarde7f0ad52004-07-14 17:28:59 +0000214{
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100215 DisplaySurface *surface = qemu_console_surface(con);
Gerd Hoffmann68db6dc2013-03-06 15:43:23 +0100216 pixman_rectangle16_t rect = {
217 .x = posx, .y = posy, .width = width, .height = height
218 };
219 pixman_color_t pcolor;
ths3b46e622007-09-17 08:09:54 +0000220
Gerd Hoffmann68db6dc2013-03-06 15:43:23 +0100221 pcolor = qemu_pixman_color(&surface->pf, color);
222 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
223 &pcolor, 1, &rect);
bellarde7f0ad52004-07-14 17:28:59 +0000224}
225
226/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100227static void vga_bitblt(QemuConsole *con,
228 int xs, int ys, int xd, int yd, int w, int h)
bellarde7f0ad52004-07-14 17:28:59 +0000229{
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100230 DisplaySurface *surface = qemu_console_surface(con);
bellarde7f0ad52004-07-14 17:28:59 +0000231
Gerd Hoffmann68db6dc2013-03-06 15:43:23 +0100232 pixman_image_composite(PIXMAN_OP_SRC,
233 surface->image, NULL, surface->image,
234 xs, ys, 0, 0, xd, yd, w, h);
bellarde7f0ad52004-07-14 17:28:59 +0000235}
236
237/***********************************************************/
238/* basic char display */
239
240#define FONT_HEIGHT 16
241#define FONT_WIDTH 8
242
243#include "vgafont.h"
244
Devin J. Pohlydf00bed2011-09-07 15:44:36 -0400245#ifndef CONFIG_CURSES
pbrook6d6f7c22006-03-11 15:35:30 +0000246enum color_names {
247 COLOR_BLACK = 0,
248 COLOR_RED = 1,
249 COLOR_GREEN = 2,
250 COLOR_YELLOW = 3,
251 COLOR_BLUE = 4,
252 COLOR_MAGENTA = 5,
253 COLOR_CYAN = 6,
254 COLOR_WHITE = 7
255};
Devin J. Pohlydf00bed2011-09-07 15:44:36 -0400256#endif
pbrook6d6f7c22006-03-11 15:35:30 +0000257
258static const uint32_t color_table_rgb[2][8] = {
259 { /* dark */
bellard26489842006-06-25 17:37:36 +0000260 QEMU_RGB(0x00, 0x00, 0x00), /* black */
261 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
262 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
263 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
264 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
265 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
266 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
267 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000268 },
269 { /* bright */
bellard26489842006-06-25 17:37:36 +0000270 QEMU_RGB(0x00, 0x00, 0x00), /* black */
271 QEMU_RGB(0xff, 0x00, 0x00), /* red */
272 QEMU_RGB(0x00, 0xff, 0x00), /* green */
273 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
274 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
275 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
276 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
277 QEMU_RGB(0xff, 0xff, 0xff), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000278 }
bellarde7f0ad52004-07-14 17:28:59 +0000279};
280
pbrook6d6f7c22006-03-11 15:35:30 +0000281#ifdef DEBUG_CONSOLE
282static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
283{
284 if (t_attrib->bold) {
285 printf("b");
286 } else {
287 printf(" ");
288 }
289 if (t_attrib->uline) {
290 printf("u");
291 } else {
292 printf(" ");
293 }
294 if (t_attrib->blink) {
295 printf("l");
296 } else {
297 printf(" ");
298 }
299 if (t_attrib->invers) {
300 printf("i");
301 } else {
302 printf(" ");
303 }
304 if (t_attrib->unvisible) {
305 printf("n");
306 } else {
307 printf(" ");
308 }
309
310 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
311}
312#endif
bellarde7f0ad52004-07-14 17:28:59 +0000313
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100314static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000315 TextAttributes *t_attrib)
bellarde7f0ad52004-07-14 17:28:59 +0000316{
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100317 static pixman_image_t *glyphs[256];
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100318 DisplaySurface *surface = qemu_console_surface(s);
pbrook6d6f7c22006-03-11 15:35:30 +0000319 unsigned int fgcol, bgcol;
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100320 pixman_image_t *ifg, *ibg;
321 pixman_color_t cfg, cbg;
pbrook6d6f7c22006-03-11 15:35:30 +0000322
323 if (t_attrib->invers) {
Gerd Hoffmanncf6f0542013-03-06 09:50:51 +0100324 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
325 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
pbrook6d6f7c22006-03-11 15:35:30 +0000326 } else {
Gerd Hoffmanncf6f0542013-03-06 09:50:51 +0100327 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
328 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
pbrook6d6f7c22006-03-11 15:35:30 +0000329 }
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100330 cfg = qemu_pixman_color(&surface->pf, fgcol);
331 cbg = qemu_pixman_color(&surface->pf, bgcol);
332 ifg = pixman_image_create_solid_fill(&cfg);
333 ibg = pixman_image_create_solid_fill(&cbg);
bellarde7f0ad52004-07-14 17:28:59 +0000334
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100335 if (!glyphs[ch]) {
336 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
bellarde7f0ad52004-07-14 17:28:59 +0000337 }
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100338 qemu_pixman_glyph_render(glyphs[ch], surface->image,
339 &cfg, &cbg, x, y, FONT_WIDTH, FONT_HEIGHT);
bellarde7f0ad52004-07-14 17:28:59 +0000340}
341
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200342static void text_console_resize(QemuConsole *s)
bellarde7f0ad52004-07-14 17:28:59 +0000343{
344 TextCell *cells, *c, *c1;
345 int w1, x, y, last_width;
346
347 last_width = s->width;
348 s->width = s->g_width / FONT_WIDTH;
349 s->height = s->g_height / FONT_HEIGHT;
350
351 w1 = last_width;
352 if (s->width < w1)
353 w1 = s->width;
354
Anthony Liguori7267c092011-08-20 22:09:37 -0500355 cells = g_malloc(s->width * s->total_height * sizeof(TextCell));
bellarde7f0ad52004-07-14 17:28:59 +0000356 for(y = 0; y < s->total_height; y++) {
357 c = &cells[y * s->width];
358 if (w1 > 0) {
359 c1 = &s->cells[y * last_width];
360 for(x = 0; x < w1; x++) {
361 *c++ = *c1++;
362 }
363 }
364 for(x = w1; x < s->width; x++) {
365 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000366 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000367 c++;
368 }
369 }
Anthony Liguori7267c092011-08-20 22:09:37 -0500370 g_free(s->cells);
bellarde7f0ad52004-07-14 17:28:59 +0000371 s->cells = cells;
372}
373
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200374static inline void text_update_xy(QemuConsole *s, int x, int y)
balrog4d3b6f62008-02-10 16:33:14 +0000375{
376 s->text_x[0] = MIN(s->text_x[0], x);
377 s->text_x[1] = MAX(s->text_x[1], x);
378 s->text_y[0] = MIN(s->text_y[0], y);
379 s->text_y[1] = MAX(s->text_y[1], y);
380}
381
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200382static void invalidate_xy(QemuConsole *s, int x, int y)
pbrook14778c22009-01-21 03:02:52 +0000383{
384 if (s->update_x0 > x * FONT_WIDTH)
385 s->update_x0 = x * FONT_WIDTH;
386 if (s->update_y0 > y * FONT_HEIGHT)
387 s->update_y0 = y * FONT_HEIGHT;
388 if (s->update_x1 < (x + 1) * FONT_WIDTH)
389 s->update_x1 = (x + 1) * FONT_WIDTH;
390 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
391 s->update_y1 = (y + 1) * FONT_HEIGHT;
392}
393
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200394static void update_xy(QemuConsole *s, int x, int y)
bellarde7f0ad52004-07-14 17:28:59 +0000395{
396 TextCell *c;
397 int y1, y2;
398
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100399 if (s != active_console) {
400 return;
401 }
balrog4d3b6f62008-02-10 16:33:14 +0000402
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100403 if (s->ds->have_text) {
404 text_update_xy(s, x, y);
405 }
406
407 if (s->ds->have_gfx) {
bellarde7f0ad52004-07-14 17:28:59 +0000408 y1 = (s->y_base + y) % s->total_height;
409 y2 = y1 - s->y_displayed;
410 if (y2 < 0)
411 y2 += s->total_height;
412 if (y2 < s->height) {
413 c = &s->cells[y1 * s->width + x];
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100414 vga_putcharxy(s, x, y2, c->ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000415 &(c->t_attrib));
pbrook14778c22009-01-21 03:02:52 +0000416 invalidate_xy(s, x, y2);
bellarde7f0ad52004-07-14 17:28:59 +0000417 }
418 }
419}
420
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200421static void console_show_cursor(QemuConsole *s, int show)
bellarde7f0ad52004-07-14 17:28:59 +0000422{
423 TextCell *c;
424 int y, y1;
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100425 int x = s->x;
bellarde7f0ad52004-07-14 17:28:59 +0000426
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100427 if (s != active_console) {
428 return;
429 }
balrog4d3b6f62008-02-10 16:33:14 +0000430
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100431 if (s->ds->have_text) {
432 s->cursor_invalidate = 1;
433 }
balrog4d3b6f62008-02-10 16:33:14 +0000434
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100435 if (s->ds->have_gfx) {
thsed8276a2007-02-10 22:37:56 +0000436 if (x >= s->width) {
437 x = s->width - 1;
438 }
bellarde7f0ad52004-07-14 17:28:59 +0000439 y1 = (s->y_base + s->y) % s->total_height;
440 y = y1 - s->y_displayed;
441 if (y < 0)
442 y += s->total_height;
443 if (y < s->height) {
thsed8276a2007-02-10 22:37:56 +0000444 c = &s->cells[y1 * s->width + x];
Jan Kiszkabf1bed82012-07-10 22:00:55 +0200445 if (show && s->cursor_visible_phase) {
pbrook6d6f7c22006-03-11 15:35:30 +0000446 TextAttributes t_attrib = s->t_attrib_default;
447 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100448 vga_putcharxy(s, x, y, c->ch, &t_attrib);
bellarde7f0ad52004-07-14 17:28:59 +0000449 } else {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100450 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
bellarde7f0ad52004-07-14 17:28:59 +0000451 }
pbrook14778c22009-01-21 03:02:52 +0000452 invalidate_xy(s, x, y);
bellarde7f0ad52004-07-14 17:28:59 +0000453 }
454 }
455}
456
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200457static void console_refresh(QemuConsole *s)
bellarde7f0ad52004-07-14 17:28:59 +0000458{
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100459 DisplaySurface *surface = qemu_console_surface(s);
bellarde7f0ad52004-07-14 17:28:59 +0000460 TextCell *c;
461 int x, y, y1;
462
ths5fafdf22007-09-16 21:08:06 +0000463 if (s != active_console)
bellarde7f0ad52004-07-14 17:28:59 +0000464 return;
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200465
466 if (s->ds->have_text) {
balrog4d3b6f62008-02-10 16:33:14 +0000467 s->text_x[0] = 0;
468 s->text_y[0] = 0;
469 s->text_x[1] = s->width - 1;
470 s->text_y[1] = s->height - 1;
471 s->cursor_invalidate = 1;
balrog4d3b6f62008-02-10 16:33:14 +0000472 }
bellarde7f0ad52004-07-14 17:28:59 +0000473
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200474 if (s->ds->have_gfx) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100475 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
Gerd Hoffmanncf6f0542013-03-06 09:50:51 +0100476 color_table_rgb[0][COLOR_BLACK]);
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200477 y1 = s->y_displayed;
478 for (y = 0; y < s->height; y++) {
479 c = s->cells + y1 * s->width;
480 for (x = 0; x < s->width; x++) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100481 vga_putcharxy(s, x, y, c->ch,
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200482 &(c->t_attrib));
483 c++;
484 }
485 if (++y1 == s->total_height) {
486 y1 = 0;
487 }
bellarde7f0ad52004-07-14 17:28:59 +0000488 }
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200489 console_show_cursor(s, 1);
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100490 dpy_gfx_update(s, 0, 0,
491 surface_width(surface), surface_height(surface));
bellarde7f0ad52004-07-14 17:28:59 +0000492 }
bellarde7f0ad52004-07-14 17:28:59 +0000493}
494
495static void console_scroll(int ydelta)
496{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200497 QemuConsole *s;
bellarde7f0ad52004-07-14 17:28:59 +0000498 int i, y1;
ths3b46e622007-09-17 08:09:54 +0000499
bellarde7f0ad52004-07-14 17:28:59 +0000500 s = active_console;
thsaf3a9032007-07-11 23:14:59 +0000501 if (!s || (s->console_type == GRAPHIC_CONSOLE))
bellarde7f0ad52004-07-14 17:28:59 +0000502 return;
503
504 if (ydelta > 0) {
505 for(i = 0; i < ydelta; i++) {
506 if (s->y_displayed == s->y_base)
507 break;
508 if (++s->y_displayed == s->total_height)
509 s->y_displayed = 0;
510 }
511 } else {
512 ydelta = -ydelta;
513 i = s->backscroll_height;
514 if (i > s->total_height - s->height)
515 i = s->total_height - s->height;
516 y1 = s->y_base - i;
517 if (y1 < 0)
518 y1 += s->total_height;
519 for(i = 0; i < ydelta; i++) {
520 if (s->y_displayed == y1)
521 break;
522 if (--s->y_displayed < 0)
523 s->y_displayed = s->total_height - 1;
524 }
525 }
526 console_refresh(s);
527}
528
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200529static void console_put_lf(QemuConsole *s)
bellarde7f0ad52004-07-14 17:28:59 +0000530{
531 TextCell *c;
532 int x, y1;
533
bellarde7f0ad52004-07-14 17:28:59 +0000534 s->y++;
535 if (s->y >= s->height) {
536 s->y = s->height - 1;
pbrook6d6f7c22006-03-11 15:35:30 +0000537
bellarde7f0ad52004-07-14 17:28:59 +0000538 if (s->y_displayed == s->y_base) {
539 if (++s->y_displayed == s->total_height)
540 s->y_displayed = 0;
541 }
542 if (++s->y_base == s->total_height)
543 s->y_base = 0;
544 if (s->backscroll_height < s->total_height)
545 s->backscroll_height++;
546 y1 = (s->y_base + s->height - 1) % s->total_height;
547 c = &s->cells[y1 * s->width];
548 for(x = 0; x < s->width; x++) {
549 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000550 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000551 c++;
552 }
553 if (s == active_console && s->y_displayed == s->y_base) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100554 if (s->ds->have_text) {
balrog4d3b6f62008-02-10 16:33:14 +0000555 s->text_x[0] = 0;
556 s->text_y[0] = 0;
557 s->text_x[1] = s->width - 1;
558 s->text_y[1] = s->height - 1;
balrog4d3b6f62008-02-10 16:33:14 +0000559 }
560
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100561 if (s->ds->have_gfx) {
562 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
563 s->width * FONT_WIDTH,
564 (s->height - 1) * FONT_HEIGHT);
565 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
566 s->width * FONT_WIDTH, FONT_HEIGHT,
567 color_table_rgb[0][s->t_attrib_default.bgcol]);
568 s->update_x0 = 0;
569 s->update_y0 = 0;
570 s->update_x1 = s->width * FONT_WIDTH;
571 s->update_y1 = s->height * FONT_HEIGHT;
572 }
bellarde7f0ad52004-07-14 17:28:59 +0000573 }
574 }
575}
576
pbrook6d6f7c22006-03-11 15:35:30 +0000577/* Set console attributes depending on the current escape codes.
578 * NOTE: I know this code is not very efficient (checking every color for it
579 * self) but it is more readable and better maintainable.
580 */
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200581static void console_handle_escape(QemuConsole *s)
pbrook6d6f7c22006-03-11 15:35:30 +0000582{
583 int i;
584
pbrook6d6f7c22006-03-11 15:35:30 +0000585 for (i=0; i<s->nb_esc_params; i++) {
586 switch (s->esc_params[i]) {
587 case 0: /* reset all console attributes to default */
588 s->t_attrib = s->t_attrib_default;
589 break;
590 case 1:
591 s->t_attrib.bold = 1;
592 break;
593 case 4:
594 s->t_attrib.uline = 1;
595 break;
596 case 5:
597 s->t_attrib.blink = 1;
598 break;
599 case 7:
600 s->t_attrib.invers = 1;
601 break;
602 case 8:
603 s->t_attrib.unvisible = 1;
604 break;
605 case 22:
606 s->t_attrib.bold = 0;
607 break;
608 case 24:
609 s->t_attrib.uline = 0;
610 break;
611 case 25:
612 s->t_attrib.blink = 0;
613 break;
614 case 27:
615 s->t_attrib.invers = 0;
616 break;
617 case 28:
618 s->t_attrib.unvisible = 0;
619 break;
620 /* set foreground color */
621 case 30:
622 s->t_attrib.fgcol=COLOR_BLACK;
623 break;
624 case 31:
625 s->t_attrib.fgcol=COLOR_RED;
626 break;
627 case 32:
628 s->t_attrib.fgcol=COLOR_GREEN;
629 break;
630 case 33:
631 s->t_attrib.fgcol=COLOR_YELLOW;
632 break;
633 case 34:
634 s->t_attrib.fgcol=COLOR_BLUE;
635 break;
636 case 35:
637 s->t_attrib.fgcol=COLOR_MAGENTA;
638 break;
639 case 36:
640 s->t_attrib.fgcol=COLOR_CYAN;
641 break;
642 case 37:
643 s->t_attrib.fgcol=COLOR_WHITE;
644 break;
645 /* set background color */
646 case 40:
647 s->t_attrib.bgcol=COLOR_BLACK;
648 break;
649 case 41:
650 s->t_attrib.bgcol=COLOR_RED;
651 break;
652 case 42:
653 s->t_attrib.bgcol=COLOR_GREEN;
654 break;
655 case 43:
656 s->t_attrib.bgcol=COLOR_YELLOW;
657 break;
658 case 44:
659 s->t_attrib.bgcol=COLOR_BLUE;
660 break;
661 case 45:
662 s->t_attrib.bgcol=COLOR_MAGENTA;
663 break;
664 case 46:
665 s->t_attrib.bgcol=COLOR_CYAN;
666 break;
667 case 47:
668 s->t_attrib.bgcol=COLOR_WHITE;
669 break;
670 }
671 }
672}
673
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200674static void console_clear_xy(QemuConsole *s, int x, int y)
thsadb47962007-01-16 23:02:36 +0000675{
676 int y1 = (s->y_base + y) % s->total_height;
677 TextCell *c = &s->cells[y1 * s->width + x];
678 c->ch = ' ';
679 c->t_attrib = s->t_attrib_default;
thsadb47962007-01-16 23:02:36 +0000680 update_xy(s, x, y);
681}
682
Ian Campbell3eea5492012-09-04 10:26:09 -0500683/* set cursor, checking bounds */
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200684static void set_cursor(QemuConsole *s, int x, int y)
Ian Campbell3eea5492012-09-04 10:26:09 -0500685{
686 if (x < 0) {
687 x = 0;
688 }
689 if (y < 0) {
690 y = 0;
691 }
692 if (y >= s->height) {
693 y = s->height - 1;
694 }
695 if (x >= s->width) {
696 x = s->width - 1;
697 }
698
699 s->x = x;
700 s->y = y;
701}
702
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200703static void console_putchar(QemuConsole *s, int ch)
bellarde7f0ad52004-07-14 17:28:59 +0000704{
705 TextCell *c;
thsadb47962007-01-16 23:02:36 +0000706 int y1, i;
707 int x, y;
bellarde7f0ad52004-07-14 17:28:59 +0000708
709 switch(s->state) {
710 case TTY_STATE_NORM:
711 switch(ch) {
pbrook6d6f7c22006-03-11 15:35:30 +0000712 case '\r': /* carriage return */
bellarde7f0ad52004-07-14 17:28:59 +0000713 s->x = 0;
714 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000715 case '\n': /* newline */
bellarde7f0ad52004-07-14 17:28:59 +0000716 console_put_lf(s);
717 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000718 case '\b': /* backspace */
ths5fafdf22007-09-16 21:08:06 +0000719 if (s->x > 0)
bellarde15d7372006-06-25 16:26:29 +0000720 s->x--;
pbrook6d6f7c22006-03-11 15:35:30 +0000721 break;
722 case '\t': /* tabspace */
723 if (s->x + (8 - (s->x % 8)) > s->width) {
bellardbd468842006-07-14 20:24:31 +0000724 s->x = 0;
pbrook6d6f7c22006-03-11 15:35:30 +0000725 console_put_lf(s);
726 } else {
727 s->x = s->x + (8 - (s->x % 8));
728 }
729 break;
730 case '\a': /* alert aka. bell */
731 /* TODO: has to be implemented */
732 break;
thsadb47962007-01-16 23:02:36 +0000733 case 14:
734 /* SI (shift in), character set 0 (ignored) */
735 break;
736 case 15:
737 /* SO (shift out), character set 1 (ignored) */
738 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000739 case 27: /* esc (introducing an escape sequence) */
bellarde7f0ad52004-07-14 17:28:59 +0000740 s->state = TTY_STATE_ESC;
741 break;
742 default:
thsed8276a2007-02-10 22:37:56 +0000743 if (s->x >= s->width) {
744 /* line wrap */
745 s->x = 0;
746 console_put_lf(s);
thsadb47962007-01-16 23:02:36 +0000747 }
bellarde7f0ad52004-07-14 17:28:59 +0000748 y1 = (s->y_base + s->y) % s->total_height;
749 c = &s->cells[y1 * s->width + s->x];
750 c->ch = ch;
pbrook6d6f7c22006-03-11 15:35:30 +0000751 c->t_attrib = s->t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +0000752 update_xy(s, s->x, s->y);
753 s->x++;
bellarde7f0ad52004-07-14 17:28:59 +0000754 break;
755 }
756 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000757 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
bellarde7f0ad52004-07-14 17:28:59 +0000758 if (ch == '[') {
759 for(i=0;i<MAX_ESC_PARAMS;i++)
760 s->esc_params[i] = 0;
761 s->nb_esc_params = 0;
762 s->state = TTY_STATE_CSI;
763 } else {
764 s->state = TTY_STATE_NORM;
765 }
766 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000767 case TTY_STATE_CSI: /* handle escape sequence parameters */
bellarde7f0ad52004-07-14 17:28:59 +0000768 if (ch >= '0' && ch <= '9') {
769 if (s->nb_esc_params < MAX_ESC_PARAMS) {
Laszlo Ersekc10600a2012-09-17 11:10:03 +0200770 int *param = &s->esc_params[s->nb_esc_params];
771 int digit = (ch - '0');
772
773 *param = (*param <= (INT_MAX - digit) / 10) ?
774 *param * 10 + digit : INT_MAX;
bellarde7f0ad52004-07-14 17:28:59 +0000775 }
776 } else {
Ian Campbell3eea5492012-09-04 10:26:09 -0500777 if (s->nb_esc_params < MAX_ESC_PARAMS)
778 s->nb_esc_params++;
bellarde7f0ad52004-07-14 17:28:59 +0000779 if (ch == ';')
780 break;
thsadb47962007-01-16 23:02:36 +0000781#ifdef DEBUG_CONSOLE
782 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
783 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
784#endif
bellarde7f0ad52004-07-14 17:28:59 +0000785 s->state = TTY_STATE_NORM;
786 switch(ch) {
thsadb47962007-01-16 23:02:36 +0000787 case 'A':
788 /* move cursor up */
789 if (s->esc_params[0] == 0) {
790 s->esc_params[0] = 1;
791 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500792 set_cursor(s, s->x, s->y - s->esc_params[0]);
bellarde7f0ad52004-07-14 17:28:59 +0000793 break;
thsadb47962007-01-16 23:02:36 +0000794 case 'B':
795 /* move cursor down */
796 if (s->esc_params[0] == 0) {
797 s->esc_params[0] = 1;
798 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500799 set_cursor(s, s->x, s->y + s->esc_params[0]);
thsadb47962007-01-16 23:02:36 +0000800 break;
801 case 'C':
802 /* move cursor right */
803 if (s->esc_params[0] == 0) {
804 s->esc_params[0] = 1;
805 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500806 set_cursor(s, s->x + s->esc_params[0], s->y);
thsadb47962007-01-16 23:02:36 +0000807 break;
808 case 'D':
809 /* move cursor left */
810 if (s->esc_params[0] == 0) {
811 s->esc_params[0] = 1;
812 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500813 set_cursor(s, s->x - s->esc_params[0], s->y);
thsadb47962007-01-16 23:02:36 +0000814 break;
815 case 'G':
816 /* move cursor to column */
Ian Campbell3eea5492012-09-04 10:26:09 -0500817 set_cursor(s, s->esc_params[0] - 1, s->y);
thsadb47962007-01-16 23:02:36 +0000818 break;
819 case 'f':
820 case 'H':
821 /* move cursor to row, column */
Ian Campbell3eea5492012-09-04 10:26:09 -0500822 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
thsadb47962007-01-16 23:02:36 +0000823 break;
824 case 'J':
825 switch (s->esc_params[0]) {
826 case 0:
827 /* clear to end of screen */
828 for (y = s->y; y < s->height; y++) {
829 for (x = 0; x < s->width; x++) {
830 if (y == s->y && x < s->x) {
831 continue;
832 }
833 console_clear_xy(s, x, y);
834 }
835 }
836 break;
837 case 1:
838 /* clear from beginning of screen */
839 for (y = 0; y <= s->y; y++) {
840 for (x = 0; x < s->width; x++) {
841 if (y == s->y && x > s->x) {
842 break;
843 }
844 console_clear_xy(s, x, y);
845 }
846 }
847 break;
848 case 2:
849 /* clear entire screen */
850 for (y = 0; y <= s->height; y++) {
851 for (x = 0; x < s->width; x++) {
852 console_clear_xy(s, x, y);
853 }
854 }
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100855 break;
thsadb47962007-01-16 23:02:36 +0000856 }
Markus Armbruster95d8f9f2011-11-22 11:59:07 +0100857 break;
thsadb47962007-01-16 23:02:36 +0000858 case 'K':
859 switch (s->esc_params[0]) {
860 case 0:
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100861 /* clear to eol */
862 for(x = s->x; x < s->width; x++) {
thsadb47962007-01-16 23:02:36 +0000863 console_clear_xy(s, x, s->y);
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100864 }
865 break;
thsadb47962007-01-16 23:02:36 +0000866 case 1:
867 /* clear from beginning of line */
868 for (x = 0; x <= s->x; x++) {
869 console_clear_xy(s, x, s->y);
870 }
871 break;
872 case 2:
873 /* clear entire line */
874 for(x = 0; x < s->width; x++) {
875 console_clear_xy(s, x, s->y);
876 }
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100877 break;
878 }
thsadb47962007-01-16 23:02:36 +0000879 break;
880 case 'm':
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100881 console_handle_escape(s);
882 break;
thsadb47962007-01-16 23:02:36 +0000883 case 'n':
884 /* report cursor position */
885 /* TODO: send ESC[row;colR */
886 break;
887 case 's':
888 /* save cursor position */
889 s->x_saved = s->x;
890 s->y_saved = s->y;
891 break;
892 case 'u':
893 /* restore cursor position */
894 s->x = s->x_saved;
895 s->y = s->y_saved;
896 break;
897 default:
898#ifdef DEBUG_CONSOLE
899 fprintf(stderr, "unhandled escape character '%c'\n", ch);
900#endif
901 break;
902 }
903 break;
bellarde7f0ad52004-07-14 17:28:59 +0000904 }
905 }
906}
907
908void console_select(unsigned int index)
909{
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100910 DisplaySurface *surface;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200911 QemuConsole *s;
pbrook6d6f7c22006-03-11 15:35:30 +0000912
bellarde7f0ad52004-07-14 17:28:59 +0000913 if (index >= MAX_CONSOLES)
914 return;
Stefan Hajnoczi358664c2010-09-20 14:11:19 +0100915 if (active_console) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100916 surface = qemu_console_surface(active_console);
917 active_console->g_width = surface_width(surface);
918 active_console->g_height = surface_height(surface);
Stefan Hajnoczi358664c2010-09-20 14:11:19 +0100919 }
bellarde7f0ad52004-07-14 17:28:59 +0000920 s = consoles[index];
921 if (s) {
aliguori7d957bd2009-01-15 22:14:11 +0000922 DisplayState *ds = s->ds;
Jan Kiszkabf1bed82012-07-10 22:00:55 +0200923
Stefan Weil8bd6b062012-08-17 15:50:44 +0200924 if (active_console && active_console->cursor_timer) {
Jan Kiszkabf1bed82012-07-10 22:00:55 +0200925 qemu_del_timer(active_console->cursor_timer);
926 }
bellarde7f0ad52004-07-14 17:28:59 +0000927 active_console = s;
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200928 if (ds->have_gfx) {
Gerd Hoffmannda229ef2013-02-28 10:48:02 +0100929 surface = qemu_create_displaysurface(s->g_width, s->g_height);
Gerd Hoffmannc78f7132013-03-05 15:24:14 +0100930 dpy_gfx_replace_surface(s, surface);
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200931 }
932 if (ds->have_text) {
Gerd Hoffmannc78f7132013-03-05 15:24:14 +0100933 dpy_text_resize(s, s->width, s->height);
aliguori68f00992009-01-21 18:59:12 +0000934 }
Jan Kiszkabf1bed82012-07-10 22:00:55 +0200935 if (s->cursor_timer) {
936 qemu_mod_timer(s->cursor_timer,
937 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
938 }
balrog4d3b6f62008-02-10 16:33:14 +0000939 vga_hw_invalidate();
bellarde7f0ad52004-07-14 17:28:59 +0000940 }
941}
942
943static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
944{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200945 QemuConsole *s = chr->opaque;
bellarde7f0ad52004-07-14 17:28:59 +0000946 int i;
947
pbrook14778c22009-01-21 03:02:52 +0000948 s->update_x0 = s->width * FONT_WIDTH;
949 s->update_y0 = s->height * FONT_HEIGHT;
950 s->update_x1 = 0;
951 s->update_y1 = 0;
bellarde7f0ad52004-07-14 17:28:59 +0000952 console_show_cursor(s, 0);
953 for(i = 0; i < len; i++) {
954 console_putchar(s, buf[i]);
955 }
956 console_show_cursor(s, 1);
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200957 if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
Gerd Hoffmannc78f7132013-03-05 15:24:14 +0100958 dpy_gfx_update(s, s->update_x0, s->update_y0,
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200959 s->update_x1 - s->update_x0,
960 s->update_y1 - s->update_y0);
pbrook14778c22009-01-21 03:02:52 +0000961 }
bellarde7f0ad52004-07-14 17:28:59 +0000962 return len;
963}
964
bellarde15d7372006-06-25 16:26:29 +0000965static void kbd_send_chars(void *opaque)
966{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200967 QemuConsole *s = opaque;
bellarde15d7372006-06-25 16:26:29 +0000968 int len;
969 uint8_t buf[16];
ths3b46e622007-09-17 08:09:54 +0000970
Anthony Liguori909cda12011-08-15 11:17:31 -0500971 len = qemu_chr_be_can_write(s->chr);
bellarde15d7372006-06-25 16:26:29 +0000972 if (len > s->out_fifo.count)
973 len = s->out_fifo.count;
974 if (len > 0) {
975 if (len > sizeof(buf))
976 len = sizeof(buf);
977 qemu_fifo_read(&s->out_fifo, buf, len);
Anthony Liguorifa5efcc2011-08-15 11:17:30 -0500978 qemu_chr_be_write(s->chr, buf, len);
bellarde15d7372006-06-25 16:26:29 +0000979 }
980 /* characters are pending: we send them a bit later (XXX:
981 horrible, should change char device API) */
982 if (s->out_fifo.count > 0) {
Paolo Bonzini7bd427d2011-03-11 16:47:48 +0100983 qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
bellarde15d7372006-06-25 16:26:29 +0000984 }
985}
986
bellarde7f0ad52004-07-14 17:28:59 +0000987/* called when an ascii key is pressed */
988void kbd_put_keysym(int keysym)
989{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200990 QemuConsole *s;
bellarde7f0ad52004-07-14 17:28:59 +0000991 uint8_t buf[16], *q;
992 int c;
993
994 s = active_console;
thsaf3a9032007-07-11 23:14:59 +0000995 if (!s || (s->console_type == GRAPHIC_CONSOLE))
bellarde7f0ad52004-07-14 17:28:59 +0000996 return;
997
998 switch(keysym) {
999 case QEMU_KEY_CTRL_UP:
1000 console_scroll(-1);
1001 break;
1002 case QEMU_KEY_CTRL_DOWN:
1003 console_scroll(1);
1004 break;
1005 case QEMU_KEY_CTRL_PAGEUP:
1006 console_scroll(-10);
1007 break;
1008 case QEMU_KEY_CTRL_PAGEDOWN:
1009 console_scroll(10);
1010 break;
1011 default:
bellarde15d7372006-06-25 16:26:29 +00001012 /* convert the QEMU keysym to VT100 key string */
1013 q = buf;
1014 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1015 *q++ = '\033';
1016 *q++ = '[';
1017 c = keysym - 0xe100;
1018 if (c >= 10)
1019 *q++ = '0' + (c / 10);
1020 *q++ = '0' + (c % 10);
1021 *q++ = '~';
1022 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1023 *q++ = '\033';
1024 *q++ = '[';
1025 *q++ = keysym & 0xff;
Paolo Bonzini41048332010-12-23 13:42:52 +01001026 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1027 console_puts(s->chr, (const uint8_t *) "\r", 1);
1028 *q++ = '\n';
bellarde15d7372006-06-25 16:26:29 +00001029 } else {
Paolo Bonzini41048332010-12-23 13:42:52 +01001030 *q++ = keysym;
1031 }
1032 if (s->echo) {
1033 console_puts(s->chr, buf, q - buf);
bellarde15d7372006-06-25 16:26:29 +00001034 }
pbrooke5b0bc42007-01-27 23:46:43 +00001035 if (s->chr->chr_read) {
bellarde15d7372006-06-25 16:26:29 +00001036 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1037 kbd_send_chars(s);
bellarde7f0ad52004-07-14 17:28:59 +00001038 }
1039 break;
1040 }
1041}
1042
balrog4d3b6f62008-02-10 16:33:14 +00001043static void text_console_invalidate(void *opaque)
1044{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001045 QemuConsole *s = (QemuConsole *) opaque;
Gerd Hoffmann1562e532013-03-06 13:40:47 +01001046 DisplaySurface *surface = qemu_console_surface(s);
1047
1048 if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1049 s->g_width = surface_width(surface);
1050 s->g_height = surface_height(surface);
aliguori68f00992009-01-21 18:59:12 +00001051 text_console_resize(s);
1052 }
balrog4d3b6f62008-02-10 16:33:14 +00001053 console_refresh(s);
1054}
1055
Anthony Liguoric227f092009-10-01 16:12:16 -05001056static void text_console_update(void *opaque, console_ch_t *chardata)
balrog4d3b6f62008-02-10 16:33:14 +00001057{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001058 QemuConsole *s = (QemuConsole *) opaque;
balrog4d3b6f62008-02-10 16:33:14 +00001059 int i, j, src;
1060
1061 if (s->text_x[0] <= s->text_x[1]) {
1062 src = (s->y_base + s->text_y[0]) * s->width;
1063 chardata += s->text_y[0] * s->width;
1064 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1065 for (j = 0; j < s->width; j ++, src ++)
1066 console_write_ch(chardata ++, s->cells[src].ch |
1067 (s->cells[src].t_attrib.fgcol << 12) |
1068 (s->cells[src].t_attrib.bgcol << 8) |
1069 (s->cells[src].t_attrib.bold << 21));
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001070 dpy_text_update(s, s->text_x[0], s->text_y[0],
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +02001071 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
balrog4d3b6f62008-02-10 16:33:14 +00001072 s->text_x[0] = s->width;
1073 s->text_y[0] = s->height;
1074 s->text_x[1] = 0;
1075 s->text_y[1] = 0;
1076 }
1077 if (s->cursor_invalidate) {
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001078 dpy_text_cursor(s, s->x, s->y);
balrog4d3b6f62008-02-10 16:33:14 +00001079 s->cursor_invalidate = 0;
1080 }
1081}
1082
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001083static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
bellarde7f0ad52004-07-14 17:28:59 +00001084{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001085 QemuConsole *s;
pbrook95219892006-04-09 01:06:34 +00001086 int i;
bellarde7f0ad52004-07-14 17:28:59 +00001087
1088 if (nb_consoles >= MAX_CONSOLES)
1089 return NULL;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001090 s = g_malloc0(sizeof(QemuConsole));
thsaf3a9032007-07-11 23:14:59 +00001091 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1092 (console_type == GRAPHIC_CONSOLE))) {
bellarde7f0ad52004-07-14 17:28:59 +00001093 active_console = s;
thsaf3a9032007-07-11 23:14:59 +00001094 }
bellarde7f0ad52004-07-14 17:28:59 +00001095 s->ds = ds;
thsaf3a9032007-07-11 23:14:59 +00001096 s->console_type = console_type;
1097 if (console_type != GRAPHIC_CONSOLE) {
Jan Kiszkaf81bdef2011-09-16 00:48:07 +02001098 s->index = nb_consoles;
pbrook95219892006-04-09 01:06:34 +00001099 consoles[nb_consoles++] = s;
1100 } else {
1101 /* HACK: Put graphical consoles before text consoles. */
1102 for (i = nb_consoles; i > 0; i--) {
thsaf3a9032007-07-11 23:14:59 +00001103 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
pbrook95219892006-04-09 01:06:34 +00001104 break;
1105 consoles[i] = consoles[i - 1];
Jan Kiszkaf81bdef2011-09-16 00:48:07 +02001106 consoles[i]->index = i;
pbrook95219892006-04-09 01:06:34 +00001107 }
Jan Kiszkaf81bdef2011-09-16 00:48:07 +02001108 s->index = i;
pbrook95219892006-04-09 01:06:34 +00001109 consoles[i] = s;
aliguori3023f332009-01-16 19:04:14 +00001110 nb_consoles++;
pbrook95219892006-04-09 01:06:34 +00001111 }
bellarde7f0ad52004-07-14 17:28:59 +00001112 return s;
1113}
1114
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001115static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1116 int linesize, PixelFormat pf, int newflags)
Jes Sorensenffe8b822011-03-16 13:33:30 +01001117{
Jes Sorensenffe8b822011-03-16 13:33:30 +01001118 surface->pf = pf;
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001119
1120 qemu_pixman_image_unref(surface->image);
1121 surface->image = NULL;
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001122
1123 surface->format = qemu_pixman_get_format(&pf);
1124 assert(surface->format != 0);
1125 surface->image = pixman_image_create_bits(surface->format,
1126 width, height,
1127 NULL, linesize);
1128 assert(surface->image != NULL);
1129
Jes Sorensenffe8b822011-03-16 13:33:30 +01001130 surface->flags = newflags | QEMU_ALLOCATED_FLAG;
Paolo Bonzini98b50082010-02-11 00:29:57 +01001131#ifdef HOST_WORDS_BIGENDIAN
Jes Sorensenffe8b822011-03-16 13:33:30 +01001132 surface->flags |= QEMU_BIG_ENDIAN_FLAG;
Paolo Bonzini98b50082010-02-11 00:29:57 +01001133#endif
Paolo Bonzini98b50082010-02-11 00:29:57 +01001134}
1135
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001136DisplaySurface *qemu_create_displaysurface(int width, int height)
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001137{
1138 DisplaySurface *surface = g_new0(DisplaySurface, 1);
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001139 int linesize = width * 4;
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001140
1141 trace_displaysurface_create(surface, width, height);
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001142 qemu_alloc_display(surface, width, height, linesize,
1143 qemu_default_pixelformat(32), 0);
1144 return surface;
1145}
1146
Gerd Hoffmann187cd1d2012-09-26 07:46:20 +02001147DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
Gerd Hoffmannb1424e02013-02-20 09:37:12 +01001148 int linesize, uint8_t *data,
1149 bool byteswap)
Paolo Bonzini98b50082010-02-11 00:29:57 +01001150{
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001151 DisplaySurface *surface = g_new0(DisplaySurface, 1);
Paolo Bonzini98b50082010-02-11 00:29:57 +01001152
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001153 trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
Gerd Hoffmannb1424e02013-02-20 09:37:12 +01001154 if (byteswap) {
1155 surface->pf = qemu_different_endianness_pixelformat(bpp);
1156 } else {
1157 surface->pf = qemu_default_pixelformat(bpp);
1158 }
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001159
1160 surface->format = qemu_pixman_get_format(&surface->pf);
1161 assert(surface->format != 0);
1162 surface->image = pixman_image_create_bits(surface->format,
1163 width, height,
1164 (void *)data, linesize);
1165 assert(surface->image != NULL);
1166
Paolo Bonzini98b50082010-02-11 00:29:57 +01001167#ifdef HOST_WORDS_BIGENDIAN
1168 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1169#endif
Paolo Bonzini98b50082010-02-11 00:29:57 +01001170
1171 return surface;
1172}
1173
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001174void qemu_free_displaysurface(DisplaySurface *surface)
Paolo Bonzini98b50082010-02-11 00:29:57 +01001175{
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001176 if (surface == NULL) {
Paolo Bonzini98b50082010-02-11 00:29:57 +01001177 return;
Gerd Hoffmann187cd1d2012-09-26 07:46:20 +02001178 }
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001179 trace_displaysurface_free(surface);
1180 qemu_pixman_image_unref(surface->image);
1181 g_free(surface);
Paolo Bonzini98b50082010-02-11 00:29:57 +01001182}
1183
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001184void register_displaychangelistener(DisplayState *ds,
1185 DisplayChangeListener *dcl)
1186{
1187 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1188 dcl->ds = ds;
1189 QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
1190 gui_setup_refresh(ds);
Gerd Hoffmannc12aeb82013-02-28 15:03:04 +01001191 if (dcl->ops->dpy_gfx_switch) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001192 dcl->ops->dpy_gfx_switch(dcl, ds->surface);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001193 }
1194}
1195
1196void unregister_displaychangelistener(DisplayChangeListener *dcl)
1197{
1198 DisplayState *ds = dcl->ds;
1199 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1200 QLIST_REMOVE(dcl, next);
1201 gui_setup_refresh(ds);
1202}
1203
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001204void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001205{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001206 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001207 struct DisplayChangeListener *dcl;
1208 int width = pixman_image_get_width(s->surface->image);
1209 int height = pixman_image_get_height(s->surface->image);
1210
1211 x = MAX(x, 0);
1212 y = MAX(y, 0);
1213 x = MIN(x, width);
1214 y = MIN(y, height);
1215 w = MIN(w, width - x);
1216 h = MIN(h, height - y);
1217
1218 QLIST_FOREACH(dcl, &s->listeners, next) {
1219 if (dcl->ops->dpy_gfx_update) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001220 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001221 }
1222 }
1223}
1224
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001225void dpy_gfx_replace_surface(QemuConsole *con,
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001226 DisplaySurface *surface)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001227{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001228 DisplayState *s = con->ds;
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001229 DisplaySurface *old_surface = s->surface;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001230 struct DisplayChangeListener *dcl;
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001231
1232 s->surface = surface;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001233 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmannc12aeb82013-02-28 15:03:04 +01001234 if (dcl->ops->dpy_gfx_switch) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001235 dcl->ops->dpy_gfx_switch(dcl, surface);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001236 }
1237 }
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001238 qemu_free_displaysurface(old_surface);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001239}
1240
1241void dpy_refresh(DisplayState *s)
1242{
1243 struct DisplayChangeListener *dcl;
1244 QLIST_FOREACH(dcl, &s->listeners, next) {
1245 if (dcl->ops->dpy_refresh) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001246 dcl->ops->dpy_refresh(dcl);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001247 }
1248 }
1249}
1250
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001251void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1252 int dst_x, int dst_y, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001253{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001254 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001255 struct DisplayChangeListener *dcl;
1256 QLIST_FOREACH(dcl, &s->listeners, next) {
1257 if (dcl->ops->dpy_gfx_copy) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001258 dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001259 } else { /* TODO */
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001260 dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001261 }
1262 }
1263}
1264
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001265void dpy_text_cursor(QemuConsole *con, int x, int y)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001266{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001267 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001268 struct DisplayChangeListener *dcl;
1269 QLIST_FOREACH(dcl, &s->listeners, next) {
1270 if (dcl->ops->dpy_text_cursor) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001271 dcl->ops->dpy_text_cursor(dcl, x, y);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001272 }
1273 }
1274}
1275
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001276void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001277{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001278 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001279 struct DisplayChangeListener *dcl;
1280 QLIST_FOREACH(dcl, &s->listeners, next) {
1281 if (dcl->ops->dpy_text_update) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001282 dcl->ops->dpy_text_update(dcl, x, y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001283 }
1284 }
1285}
1286
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001287void dpy_text_resize(QemuConsole *con, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001288{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001289 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001290 struct DisplayChangeListener *dcl;
1291 QLIST_FOREACH(dcl, &s->listeners, next) {
1292 if (dcl->ops->dpy_text_resize) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001293 dcl->ops->dpy_text_resize(dcl, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001294 }
1295 }
1296}
1297
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001298void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001299{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001300 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001301 struct DisplayChangeListener *dcl;
1302 QLIST_FOREACH(dcl, &s->listeners, next) {
1303 if (dcl->ops->dpy_mouse_set) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001304 dcl->ops->dpy_mouse_set(dcl, x, y, on);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001305 }
1306 }
1307}
1308
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001309void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001310{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001311 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001312 struct DisplayChangeListener *dcl;
1313 QLIST_FOREACH(dcl, &s->listeners, next) {
1314 if (dcl->ops->dpy_cursor_define) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001315 dcl->ops->dpy_cursor_define(dcl, cursor);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001316 }
1317 }
1318}
1319
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001320bool dpy_cursor_define_supported(QemuConsole *con)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001321{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001322 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001323 struct DisplayChangeListener *dcl;
1324 QLIST_FOREACH(dcl, &s->listeners, next) {
1325 if (dcl->ops->dpy_cursor_define) {
1326 return true;
1327 }
1328 }
1329 return false;
1330}
1331
Paolo Bonzini98b50082010-02-11 00:29:57 +01001332static void dumb_display_init(void)
1333{
Anthony Liguori7267c092011-08-20 22:09:37 -05001334 DisplayState *ds = g_malloc0(sizeof(DisplayState));
Jan Kiszka18026512011-06-19 11:53:02 +02001335 int width = 640;
1336 int height = 480;
1337
Jan Kiszka18026512011-06-19 11:53:02 +02001338 if (is_fixedsize_console()) {
1339 width = active_console->g_width;
1340 height = active_console->g_height;
1341 }
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001342 ds->surface = qemu_create_displaysurface(width, height);
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001343
Paolo Bonzini98b50082010-02-11 00:29:57 +01001344 register_displaystate(ds);
1345}
1346
1347/***********************************************************/
1348/* register display */
1349
1350void register_displaystate(DisplayState *ds)
1351{
1352 DisplayState **s;
1353 s = &display_state;
1354 while (*s != NULL)
1355 s = &(*s)->next;
1356 ds->next = NULL;
1357 *s = ds;
1358}
1359
1360DisplayState *get_displaystate(void)
1361{
1362 if (!display_state) {
1363 dumb_display_init ();
1364 }
1365 return display_state;
1366}
1367
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001368QemuConsole *graphic_console_init(vga_hw_update_ptr update,
1369 vga_hw_invalidate_ptr invalidate,
1370 vga_hw_screen_dump_ptr screen_dump,
1371 vga_hw_text_update_ptr text_update,
1372 void *opaque)
bellarde7f0ad52004-07-14 17:28:59 +00001373{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001374 QemuConsole *s;
aliguori3023f332009-01-16 19:04:14 +00001375 DisplayState *ds;
aurel32f0f2f972009-01-16 21:13:49 +00001376
Anthony Liguori7267c092011-08-20 22:09:37 -05001377 ds = (DisplayState *) g_malloc0(sizeof(DisplayState));
thsaf3a9032007-07-11 23:14:59 +00001378 s = new_console(ds, GRAPHIC_CONSOLE);
pbrook95219892006-04-09 01:06:34 +00001379 s->hw_update = update;
1380 s->hw_invalidate = invalidate;
1381 s->hw_screen_dump = screen_dump;
balrog4d3b6f62008-02-10 16:33:14 +00001382 s->hw_text_update = text_update;
pbrook95219892006-04-09 01:06:34 +00001383 s->hw = opaque;
aliguori3023f332009-01-16 19:04:14 +00001384
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001385 ds->surface = qemu_create_displaysurface(640, 480);
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001386
aurel32f0f2f972009-01-16 21:13:49 +00001387 register_displaystate(ds);
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001388 return s;
pbrook95219892006-04-09 01:06:34 +00001389}
1390
1391int is_graphic_console(void)
1392{
balrog4d3b6f62008-02-10 16:33:14 +00001393 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
bellarde7f0ad52004-07-14 17:28:59 +00001394}
1395
balrogc21bbcf2008-09-24 03:32:33 +00001396int is_fixedsize_console(void)
1397{
1398 return active_console && active_console->console_type != TEXT_CONSOLE;
1399}
1400
Paolo Bonzini41048332010-12-23 13:42:52 +01001401static void text_console_set_echo(CharDriverState *chr, bool echo)
1402{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001403 QemuConsole *s = chr->opaque;
Paolo Bonzini41048332010-12-23 13:42:52 +01001404
1405 s->echo = echo;
1406}
1407
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001408static void text_console_update_cursor(void *opaque)
1409{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001410 QemuConsole *s = opaque;
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001411
1412 s->cursor_visible_phase = !s->cursor_visible_phase;
1413 vga_hw_invalidate();
1414 qemu_mod_timer(s->cursor_timer,
1415 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1416}
1417
Paolo Bonzini44b37b92010-12-23 13:42:53 +01001418static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
bellarde7f0ad52004-07-14 17:28:59 +00001419{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001420 QemuConsole *s;
pbrook6d6f7c22006-03-11 15:35:30 +00001421
Paolo Bonzini491e1142010-12-23 13:42:51 +01001422 s = chr->opaque;
Gerd Hoffmann6ea314d2009-09-10 10:58:49 +02001423
bellarde7f0ad52004-07-14 17:28:59 +00001424 chr->chr_write = console_puts;
bellard6fcfafb2004-08-01 21:48:30 +00001425
bellarde15d7372006-06-25 16:26:29 +00001426 s->out_fifo.buf = s->out_fifo_buf;
1427 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
Paolo Bonzini7bd427d2011-03-11 16:47:48 +01001428 s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
aliguori3023f332009-01-16 19:04:14 +00001429 s->ds = ds;
ths3b46e622007-09-17 08:09:54 +00001430
bellarde7f0ad52004-07-14 17:28:59 +00001431 s->y_displayed = 0;
1432 s->y_base = 0;
1433 s->total_height = DEFAULT_BACKSCROLL;
1434 s->x = 0;
1435 s->y = 0;
Paolo Bonzini491e1142010-12-23 13:42:51 +01001436 if (s->console_type == TEXT_CONSOLE) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +01001437 s->g_width = surface_width(s->ds->surface);
1438 s->g_height = surface_height(s->ds->surface);
Paolo Bonzini491e1142010-12-23 13:42:51 +01001439 }
pbrook6d6f7c22006-03-11 15:35:30 +00001440
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001441 s->cursor_timer =
1442 qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
1443
balrog4d3b6f62008-02-10 16:33:14 +00001444 s->hw_invalidate = text_console_invalidate;
1445 s->hw_text_update = text_console_update;
1446 s->hw = s;
1447
pbrook6d6f7c22006-03-11 15:35:30 +00001448 /* Set text attribute defaults */
1449 s->t_attrib_default.bold = 0;
1450 s->t_attrib_default.uline = 0;
1451 s->t_attrib_default.blink = 0;
1452 s->t_attrib_default.invers = 0;
1453 s->t_attrib_default.unvisible = 0;
1454 s->t_attrib_default.fgcol = COLOR_WHITE;
1455 s->t_attrib_default.bgcol = COLOR_BLACK;
pbrook6d6f7c22006-03-11 15:35:30 +00001456 /* set current text attributes to default */
1457 s->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +00001458 text_console_resize(s);
1459
Gerd Hoffmann51bfa4d2009-12-08 13:11:39 +01001460 if (chr->label) {
1461 char msg[128];
1462 int len;
1463
Gerd Hoffmann735ba582009-12-08 13:11:40 +01001464 s->t_attrib.bgcol = COLOR_BLUE;
Gerd Hoffmann51bfa4d2009-12-08 13:11:39 +01001465 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1466 console_puts(chr, (uint8_t*)msg, len);
Gerd Hoffmann735ba582009-12-08 13:11:40 +01001467 s->t_attrib = s->t_attrib_default;
Gerd Hoffmann51bfa4d2009-12-08 13:11:39 +01001468 }
1469
Hans de Goedefee204f2013-03-26 11:07:54 +01001470 qemu_chr_be_generic_open(chr);
aurel32ceecf1d2009-01-18 14:08:04 +00001471 if (chr->init)
1472 chr->init(chr);
bellarde7f0ad52004-07-14 17:28:59 +00001473}
pbrookc60e08d2008-07-01 16:24:38 +00001474
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001475static CharDriverState *text_console_init(ChardevVC *vc)
aliguori2796dae2009-01-16 20:23:27 +00001476{
1477 CharDriverState *chr;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001478 QemuConsole *s;
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001479 unsigned width = 0;
1480 unsigned height = 0;
aliguori2796dae2009-01-16 20:23:27 +00001481
Anthony Liguori7267c092011-08-20 22:09:37 -05001482 chr = g_malloc0(sizeof(CharDriverState));
aliguori2796dae2009-01-16 20:23:27 +00001483
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001484 if (vc->has_width) {
1485 width = vc->width;
1486 } else if (vc->has_cols) {
1487 width = vc->cols * FONT_WIDTH;
1488 }
Paolo Bonzini491e1142010-12-23 13:42:51 +01001489
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001490 if (vc->has_height) {
1491 height = vc->height;
1492 } else if (vc->has_rows) {
1493 height = vc->rows * FONT_HEIGHT;
1494 }
Paolo Bonzini491e1142010-12-23 13:42:51 +01001495
1496 if (width == 0 || height == 0) {
1497 s = new_console(NULL, TEXT_CONSOLE);
1498 } else {
1499 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1500 }
1501
1502 if (!s) {
Stefan Weil5354d082011-10-02 18:53:09 +02001503 g_free(chr);
Markus Armbruster1f514702012-02-07 15:09:08 +01001504 return NULL;
Paolo Bonzini491e1142010-12-23 13:42:51 +01001505 }
1506
1507 s->chr = chr;
1508 s->g_width = width;
1509 s->g_height = height;
1510 chr->opaque = s;
Paolo Bonzini41048332010-12-23 13:42:52 +01001511 chr->chr_set_echo = text_console_set_echo;
Markus Armbruster1f514702012-02-07 15:09:08 +01001512 return chr;
aliguori2796dae2009-01-16 20:23:27 +00001513}
1514
Anthony Liguorid82831d2013-02-20 07:43:19 -06001515static VcHandler *vc_handler = text_console_init;
1516
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001517CharDriverState *vc_init(ChardevVC *vc)
Anthony Liguorid82831d2013-02-20 07:43:19 -06001518{
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001519 return vc_handler(vc);
Anthony Liguorid82831d2013-02-20 07:43:19 -06001520}
1521
1522void register_vc_handler(VcHandler *handler)
1523{
1524 vc_handler = handler;
1525}
1526
aliguori2796dae2009-01-16 20:23:27 +00001527void text_consoles_set_display(DisplayState *ds)
1528{
1529 int i;
1530
Markus Armbruster8811e1e2012-02-07 15:09:21 +01001531 for (i = 0; i < nb_consoles; i++) {
1532 if (consoles[i]->console_type != GRAPHIC_CONSOLE) {
1533 text_console_do_init(consoles[i]->chr, ds);
1534 }
aliguori2796dae2009-01-16 20:23:27 +00001535 }
aliguori2796dae2009-01-16 20:23:27 +00001536}
1537
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001538void qemu_console_resize(QemuConsole *s, int width, int height)
pbrookc60e08d2008-07-01 16:24:38 +00001539{
aliguori3023f332009-01-16 19:04:14 +00001540 s->g_width = width;
1541 s->g_height = height;
1542 if (is_graphic_console()) {
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001543 DisplaySurface *surface;
1544 surface = qemu_create_displaysurface(width, height);
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001545 dpy_gfx_replace_surface(s, surface);
pbrookc60e08d2008-07-01 16:24:38 +00001546 }
1547}
balrog38334f72008-09-24 02:21:24 +00001548
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001549void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
aliguori3023f332009-01-16 19:04:14 +00001550 int dst_x, int dst_y, int w, int h)
balrogc21bbcf2008-09-24 03:32:33 +00001551{
aliguori3023f332009-01-16 19:04:14 +00001552 if (is_graphic_console()) {
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001553 dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
balrog38334f72008-09-24 02:21:24 +00001554 }
1555}
aliguori7d957bd2009-01-15 22:14:11 +00001556
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001557DisplaySurface *qemu_console_surface(QemuConsole *console)
1558{
1559 return console->ds->surface;
1560}
1561
1562DisplayState *qemu_console_displaystate(QemuConsole *console)
1563{
1564 return console->ds;
1565}
1566
malc0da2ea12009-01-23 19:56:19 +00001567PixelFormat qemu_different_endianness_pixelformat(int bpp)
aliguori7d957bd2009-01-15 22:14:11 +00001568{
1569 PixelFormat pf;
1570
1571 memset(&pf, 0x00, sizeof(PixelFormat));
1572
1573 pf.bits_per_pixel = bpp;
BALATON Zoltanfeadf1a2012-08-22 17:19:42 +02001574 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
aliguori7d957bd2009-01-15 22:14:11 +00001575 pf.depth = bpp == 32 ? 24 : bpp;
1576
1577 switch (bpp) {
malc0da2ea12009-01-23 19:56:19 +00001578 case 24:
1579 pf.rmask = 0x000000FF;
1580 pf.gmask = 0x0000FF00;
1581 pf.bmask = 0x00FF0000;
1582 pf.rmax = 255;
1583 pf.gmax = 255;
1584 pf.bmax = 255;
1585 pf.rshift = 0;
1586 pf.gshift = 8;
1587 pf.bshift = 16;
aliguori90a1e3c2009-01-26 15:37:30 +00001588 pf.rbits = 8;
1589 pf.gbits = 8;
1590 pf.bbits = 8;
aliguori7d957bd2009-01-15 22:14:11 +00001591 break;
malc0da2ea12009-01-23 19:56:19 +00001592 case 32:
1593 pf.rmask = 0x0000FF00;
1594 pf.gmask = 0x00FF0000;
1595 pf.bmask = 0xFF000000;
1596 pf.amask = 0x00000000;
1597 pf.amax = 255;
1598 pf.rmax = 255;
1599 pf.gmax = 255;
1600 pf.bmax = 255;
1601 pf.ashift = 0;
1602 pf.rshift = 8;
1603 pf.gshift = 16;
1604 pf.bshift = 24;
aliguori90a1e3c2009-01-26 15:37:30 +00001605 pf.rbits = 8;
1606 pf.gbits = 8;
1607 pf.bbits = 8;
1608 pf.abits = 8;
malc0da2ea12009-01-23 19:56:19 +00001609 break;
1610 default:
1611 break;
1612 }
1613 return pf;
1614}
1615
1616PixelFormat qemu_default_pixelformat(int bpp)
1617{
1618 PixelFormat pf;
1619
1620 memset(&pf, 0x00, sizeof(PixelFormat));
1621
1622 pf.bits_per_pixel = bpp;
BALATON Zoltanfeadf1a2012-08-22 17:19:42 +02001623 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
malc0da2ea12009-01-23 19:56:19 +00001624 pf.depth = bpp == 32 ? 24 : bpp;
1625
1626 switch (bpp) {
Gerd Hoffmannb6278082010-05-21 11:59:14 +02001627 case 15:
1628 pf.bits_per_pixel = 16;
Gerd Hoffmannb6278082010-05-21 11:59:14 +02001629 pf.rmask = 0x00007c00;
1630 pf.gmask = 0x000003E0;
1631 pf.bmask = 0x0000001F;
1632 pf.rmax = 31;
1633 pf.gmax = 31;
1634 pf.bmax = 31;
1635 pf.rshift = 10;
1636 pf.gshift = 5;
1637 pf.bshift = 0;
1638 pf.rbits = 5;
1639 pf.gbits = 5;
1640 pf.bbits = 5;
1641 break;
aliguori7d957bd2009-01-15 22:14:11 +00001642 case 16:
1643 pf.rmask = 0x0000F800;
1644 pf.gmask = 0x000007E0;
1645 pf.bmask = 0x0000001F;
1646 pf.rmax = 31;
1647 pf.gmax = 63;
1648 pf.bmax = 31;
1649 pf.rshift = 11;
1650 pf.gshift = 5;
1651 pf.bshift = 0;
aliguori90a1e3c2009-01-26 15:37:30 +00001652 pf.rbits = 5;
1653 pf.gbits = 6;
1654 pf.bbits = 5;
aliguori7d957bd2009-01-15 22:14:11 +00001655 break;
1656 case 24:
aliguori7d957bd2009-01-15 22:14:11 +00001657 pf.rmask = 0x00FF0000;
1658 pf.gmask = 0x0000FF00;
1659 pf.bmask = 0x000000FF;
1660 pf.rmax = 255;
1661 pf.gmax = 255;
1662 pf.bmax = 255;
1663 pf.rshift = 16;
1664 pf.gshift = 8;
1665 pf.bshift = 0;
aliguori90a1e3c2009-01-26 15:37:30 +00001666 pf.rbits = 8;
1667 pf.gbits = 8;
1668 pf.bbits = 8;
Markus Armbruster0eba62e2011-11-22 12:56:10 +01001669 break;
malc0da2ea12009-01-23 19:56:19 +00001670 case 32:
1671 pf.rmask = 0x00FF0000;
1672 pf.gmask = 0x0000FF00;
1673 pf.bmask = 0x000000FF;
malc0da2ea12009-01-23 19:56:19 +00001674 pf.rmax = 255;
1675 pf.gmax = 255;
1676 pf.bmax = 255;
malc0da2ea12009-01-23 19:56:19 +00001677 pf.rshift = 16;
1678 pf.gshift = 8;
1679 pf.bshift = 0;
aliguori90a1e3c2009-01-26 15:37:30 +00001680 pf.rbits = 8;
1681 pf.gbits = 8;
1682 pf.bbits = 8;
aliguori7d957bd2009-01-15 22:14:11 +00001683 break;
1684 default:
1685 break;
1686 }
1687 return pf;
1688}
Anthony Liguori01f45d92013-03-05 23:21:32 +05301689
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001690static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1691 Error **errp)
1692{
1693 int val;
1694
1695 backend->vc = g_new0(ChardevVC, 1);
1696
1697 val = qemu_opt_get_number(opts, "width", 0);
1698 if (val != 0) {
1699 backend->vc->has_width = true;
1700 backend->vc->width = val;
1701 }
1702
1703 val = qemu_opt_get_number(opts, "height", 0);
1704 if (val != 0) {
1705 backend->vc->has_height = true;
1706 backend->vc->height = val;
1707 }
1708
1709 val = qemu_opt_get_number(opts, "cols", 0);
1710 if (val != 0) {
1711 backend->vc->has_cols = true;
1712 backend->vc->cols = val;
1713 }
1714
1715 val = qemu_opt_get_number(opts, "rows", 0);
1716 if (val != 0) {
1717 backend->vc->has_rows = true;
1718 backend->vc->rows = val;
1719 }
1720}
1721
Anthony Liguori01f45d92013-03-05 23:21:32 +05301722static void register_types(void)
1723{
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001724 register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1725 qemu_chr_parse_vc);
Anthony Liguori01f45d92013-03-05 23:21:32 +05301726}
1727
1728type_init(register_types);