blob: b452bca10267c28b8a3ea9a7a129bce8a92a891c [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"
25#include "console.h"
26#include "qemu-timer.h"
bellarde7f0ad52004-07-14 17:28:59 +000027
pbrook6d6f7c22006-03-11 15:35:30 +000028//#define DEBUG_CONSOLE
bellarde7f0ad52004-07-14 17:28:59 +000029#define DEFAULT_BACKSCROLL 512
30#define MAX_CONSOLES 12
pbrookc60e08d2008-07-01 16:24:38 +000031#define DEFAULT_MONITOR_SIZE "800x600"
bellarde7f0ad52004-07-14 17:28:59 +000032
bellard26489842006-06-25 17:37:36 +000033#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
34#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
bellarde7f0ad52004-07-14 17:28:59 +000035
pbrook6d6f7c22006-03-11 15:35:30 +000036typedef struct TextAttributes {
37 uint8_t fgcol:4;
38 uint8_t bgcol:4;
39 uint8_t bold:1;
40 uint8_t uline:1;
41 uint8_t blink:1;
42 uint8_t invers:1;
43 uint8_t unvisible:1;
44} TextAttributes;
45
bellarde7f0ad52004-07-14 17:28:59 +000046typedef struct TextCell {
47 uint8_t ch;
pbrook6d6f7c22006-03-11 15:35:30 +000048 TextAttributes t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +000049} TextCell;
50
51#define MAX_ESC_PARAMS 3
52
53enum TTYState {
54 TTY_STATE_NORM,
55 TTY_STATE_ESC,
56 TTY_STATE_CSI,
57};
58
bellarde15d7372006-06-25 16:26:29 +000059typedef struct QEMUFIFO {
60 uint8_t *buf;
61 int buf_size;
62 int count, wptr, rptr;
63} QEMUFIFO;
64
pbrook9596ebb2007-11-18 01:44:38 +000065static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000066{
67 int l, len;
68
69 l = f->buf_size - f->count;
70 if (len1 > l)
71 len1 = l;
72 len = len1;
73 while (len > 0) {
74 l = f->buf_size - f->wptr;
75 if (l > len)
76 l = len;
77 memcpy(f->buf + f->wptr, buf, l);
78 f->wptr += l;
79 if (f->wptr >= f->buf_size)
80 f->wptr = 0;
81 buf += l;
82 len -= l;
83 }
84 f->count += len1;
85 return len1;
86}
87
pbrook9596ebb2007-11-18 01:44:38 +000088static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000089{
90 int l, len;
91
92 if (len1 > f->count)
93 len1 = f->count;
94 len = len1;
95 while (len > 0) {
96 l = f->buf_size - f->rptr;
97 if (l > len)
98 l = len;
99 memcpy(buf, f->buf + f->rptr, l);
100 f->rptr += l;
101 if (f->rptr >= f->buf_size)
102 f->rptr = 0;
103 buf += l;
104 len -= l;
105 }
106 f->count -= len1;
107 return len1;
108}
109
thsaf3a9032007-07-11 23:14:59 +0000110typedef enum {
111 GRAPHIC_CONSOLE,
balrogc21bbcf2008-09-24 03:32:33 +0000112 TEXT_CONSOLE,
113 TEXT_CONSOLE_FIXED_SIZE
thsaf3a9032007-07-11 23:14:59 +0000114} console_type_t;
115
pbrook95219892006-04-09 01:06:34 +0000116/* ??? This is mis-named.
117 It is used for both text and graphical consoles. */
bellarde7f0ad52004-07-14 17:28:59 +0000118struct TextConsole {
thsaf3a9032007-07-11 23:14:59 +0000119 console_type_t console_type;
bellarde7f0ad52004-07-14 17:28:59 +0000120 DisplayState *ds;
pbrook95219892006-04-09 01:06:34 +0000121 /* Graphic console state. */
122 vga_hw_update_ptr hw_update;
123 vga_hw_invalidate_ptr hw_invalidate;
124 vga_hw_screen_dump_ptr hw_screen_dump;
balrog4d3b6f62008-02-10 16:33:14 +0000125 vga_hw_text_update_ptr hw_text_update;
pbrook95219892006-04-09 01:06:34 +0000126 void *hw;
127
bellarde7f0ad52004-07-14 17:28:59 +0000128 int g_width, g_height;
129 int width;
130 int height;
131 int total_height;
132 int backscroll_height;
bellarde7f0ad52004-07-14 17:28:59 +0000133 int x, y;
thsadb47962007-01-16 23:02:36 +0000134 int x_saved, y_saved;
bellarde7f0ad52004-07-14 17:28:59 +0000135 int y_displayed;
136 int y_base;
pbrook6d6f7c22006-03-11 15:35:30 +0000137 TextAttributes t_attrib_default; /* default text attributes */
138 TextAttributes t_attrib; /* currently active text attributes */
bellarde7f0ad52004-07-14 17:28:59 +0000139 TextCell *cells;
balrog4d3b6f62008-02-10 16:33:14 +0000140 int text_x[2], text_y[2], cursor_invalidate;
bellarde7f0ad52004-07-14 17:28:59 +0000141
142 enum TTYState state;
143 int esc_params[MAX_ESC_PARAMS];
144 int nb_esc_params;
145
pbrooke5b0bc42007-01-27 23:46:43 +0000146 CharDriverState *chr;
bellarde15d7372006-06-25 16:26:29 +0000147 /* fifo for key pressed */
148 QEMUFIFO out_fifo;
149 uint8_t out_fifo_buf[16];
150 QEMUTimer *kbd_timer;
bellarde7f0ad52004-07-14 17:28:59 +0000151};
152
153static TextConsole *active_console;
154static TextConsole *consoles[MAX_CONSOLES];
155static int nb_consoles = 0;
156
pbrook95219892006-04-09 01:06:34 +0000157void vga_hw_update(void)
158{
thsadb47962007-01-16 23:02:36 +0000159 if (active_console && active_console->hw_update)
pbrook95219892006-04-09 01:06:34 +0000160 active_console->hw_update(active_console->hw);
161}
162
163void vga_hw_invalidate(void)
164{
165 if (active_console->hw_invalidate)
166 active_console->hw_invalidate(active_console->hw);
167}
168
169void vga_hw_screen_dump(const char *filename)
170{
balrog8571c052008-07-19 13:04:26 +0000171 TextConsole *previous_active_console;
172
173 previous_active_console = active_console;
174 active_console = consoles[0];
175 /* There is currently no way of specifying which screen we want to dump,
aurel327b455222008-09-02 00:09:16 +0000176 so always dump the first one. */
pbrook95219892006-04-09 01:06:34 +0000177 if (consoles[0]->hw_screen_dump)
178 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
balrog8571c052008-07-19 13:04:26 +0000179 active_console = previous_active_console;
pbrook95219892006-04-09 01:06:34 +0000180}
181
balrog4d3b6f62008-02-10 16:33:14 +0000182void vga_hw_text_update(console_ch_t *chardata)
183{
184 if (active_console && active_console->hw_text_update)
185 active_console->hw_text_update(active_console->hw, chardata);
186}
187
bellarde7f0ad52004-07-14 17:28:59 +0000188/* convert a RGBA color to a color index usable in graphic primitives */
189static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
190{
191 unsigned int r, g, b, color;
192
aliguori0e1f5a02008-11-24 19:29:13 +0000193 switch(ds_get_bits_per_pixel(ds)) {
bellarde7f0ad52004-07-14 17:28:59 +0000194#if 0
195 case 8:
196 r = (rgba >> 16) & 0xff;
197 g = (rgba >> 8) & 0xff;
198 b = (rgba) & 0xff;
ths5fafdf22007-09-16 21:08:06 +0000199 color = (rgb_to_index[r] * 6 * 6) +
200 (rgb_to_index[g] * 6) +
bellarde7f0ad52004-07-14 17:28:59 +0000201 (rgb_to_index[b]);
202 break;
203#endif
204 case 15:
205 r = (rgba >> 16) & 0xff;
206 g = (rgba >> 8) & 0xff;
207 b = (rgba) & 0xff;
208 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
209 break;
210 case 16:
211 r = (rgba >> 16) & 0xff;
212 g = (rgba >> 8) & 0xff;
213 b = (rgba) & 0xff;
214 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
215 break;
216 case 32:
217 default:
218 color = rgba;
219 break;
220 }
221 return color;
222}
223
ths5fafdf22007-09-16 21:08:06 +0000224static void vga_fill_rect (DisplayState *ds,
bellarde7f0ad52004-07-14 17:28:59 +0000225 int posx, int posy, int width, int height, uint32_t color)
226{
227 uint8_t *d, *d1;
228 int x, y, bpp;
ths3b46e622007-09-17 08:09:54 +0000229
aliguori0e1f5a02008-11-24 19:29:13 +0000230 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
231 d1 = ds_get_data(ds) +
232 ds_get_linesize(ds) * posy + bpp * posx;
bellarde7f0ad52004-07-14 17:28:59 +0000233 for (y = 0; y < height; y++) {
234 d = d1;
235 switch(bpp) {
236 case 1:
237 for (x = 0; x < width; x++) {
238 *((uint8_t *)d) = color;
239 d++;
240 }
241 break;
242 case 2:
243 for (x = 0; x < width; x++) {
244 *((uint16_t *)d) = color;
245 d += 2;
246 }
247 break;
248 case 4:
249 for (x = 0; x < width; x++) {
250 *((uint32_t *)d) = color;
251 d += 4;
252 }
253 break;
254 }
aliguori0e1f5a02008-11-24 19:29:13 +0000255 d1 += ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000256 }
257}
258
259/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
260static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
261{
262 const uint8_t *s;
263 uint8_t *d;
264 int wb, y, bpp;
265
aliguori0e1f5a02008-11-24 19:29:13 +0000266 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
bellarde7f0ad52004-07-14 17:28:59 +0000267 wb = w * bpp;
268 if (yd <= ys) {
aliguori0e1f5a02008-11-24 19:29:13 +0000269 s = ds_get_data(ds) +
270 ds_get_linesize(ds) * ys + bpp * xs;
271 d = ds_get_data(ds) +
272 ds_get_linesize(ds) * yd + bpp * xd;
bellarde7f0ad52004-07-14 17:28:59 +0000273 for (y = 0; y < h; y++) {
274 memmove(d, s, wb);
aliguori0e1f5a02008-11-24 19:29:13 +0000275 d += ds_get_linesize(ds);
276 s += ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000277 }
278 } else {
aliguori0e1f5a02008-11-24 19:29:13 +0000279 s = ds_get_data(ds) +
280 ds_get_linesize(ds) * (ys + h - 1) + bpp * xs;
281 d = ds_get_data(ds) +
282 ds_get_linesize(ds) * (yd + h - 1) + bpp * xd;
bellarde7f0ad52004-07-14 17:28:59 +0000283 for (y = 0; y < h; y++) {
284 memmove(d, s, wb);
aliguori0e1f5a02008-11-24 19:29:13 +0000285 d -= ds_get_linesize(ds);
286 s -= ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000287 }
288 }
289}
290
291/***********************************************************/
292/* basic char display */
293
294#define FONT_HEIGHT 16
295#define FONT_WIDTH 8
296
297#include "vgafont.h"
298
299#define cbswap_32(__x) \
300((uint32_t)( \
301 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
302 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
303 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
304 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
305
306#ifdef WORDS_BIGENDIAN
307#define PAT(x) x
308#else
309#define PAT(x) cbswap_32(x)
310#endif
311
312static const uint32_t dmask16[16] = {
313 PAT(0x00000000),
314 PAT(0x000000ff),
315 PAT(0x0000ff00),
316 PAT(0x0000ffff),
317 PAT(0x00ff0000),
318 PAT(0x00ff00ff),
319 PAT(0x00ffff00),
320 PAT(0x00ffffff),
321 PAT(0xff000000),
322 PAT(0xff0000ff),
323 PAT(0xff00ff00),
324 PAT(0xff00ffff),
325 PAT(0xffff0000),
326 PAT(0xffff00ff),
327 PAT(0xffffff00),
328 PAT(0xffffffff),
329};
330
331static const uint32_t dmask4[4] = {
332 PAT(0x00000000),
333 PAT(0x0000ffff),
334 PAT(0xffff0000),
335 PAT(0xffffffff),
336};
337
pbrook6d6f7c22006-03-11 15:35:30 +0000338static uint32_t color_table[2][8];
bellarde7f0ad52004-07-14 17:28:59 +0000339
pbrook6d6f7c22006-03-11 15:35:30 +0000340enum color_names {
341 COLOR_BLACK = 0,
342 COLOR_RED = 1,
343 COLOR_GREEN = 2,
344 COLOR_YELLOW = 3,
345 COLOR_BLUE = 4,
346 COLOR_MAGENTA = 5,
347 COLOR_CYAN = 6,
348 COLOR_WHITE = 7
349};
350
351static const uint32_t color_table_rgb[2][8] = {
352 { /* dark */
bellard26489842006-06-25 17:37:36 +0000353 QEMU_RGB(0x00, 0x00, 0x00), /* black */
354 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
355 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
356 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
357 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
358 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
359 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
360 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000361 },
362 { /* bright */
bellard26489842006-06-25 17:37:36 +0000363 QEMU_RGB(0x00, 0x00, 0x00), /* black */
364 QEMU_RGB(0xff, 0x00, 0x00), /* red */
365 QEMU_RGB(0x00, 0xff, 0x00), /* green */
366 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
367 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
368 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
369 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
370 QEMU_RGB(0xff, 0xff, 0xff), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000371 }
bellarde7f0ad52004-07-14 17:28:59 +0000372};
373
374static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
375{
aliguori0e1f5a02008-11-24 19:29:13 +0000376 switch(ds_get_bits_per_pixel(ds)) {
bellarde7f0ad52004-07-14 17:28:59 +0000377 case 8:
378 col |= col << 8;
379 col |= col << 16;
380 break;
381 case 15:
382 case 16:
383 col |= col << 16;
384 break;
385 default:
386 break;
387 }
388
389 return col;
390}
pbrook6d6f7c22006-03-11 15:35:30 +0000391#ifdef DEBUG_CONSOLE
392static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
393{
394 if (t_attrib->bold) {
395 printf("b");
396 } else {
397 printf(" ");
398 }
399 if (t_attrib->uline) {
400 printf("u");
401 } else {
402 printf(" ");
403 }
404 if (t_attrib->blink) {
405 printf("l");
406 } else {
407 printf(" ");
408 }
409 if (t_attrib->invers) {
410 printf("i");
411 } else {
412 printf(" ");
413 }
414 if (t_attrib->unvisible) {
415 printf("n");
416 } else {
417 printf(" ");
418 }
419
420 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
421}
422#endif
bellarde7f0ad52004-07-14 17:28:59 +0000423
ths5fafdf22007-09-16 21:08:06 +0000424static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000425 TextAttributes *t_attrib)
bellarde7f0ad52004-07-14 17:28:59 +0000426{
427 uint8_t *d;
428 const uint8_t *font_ptr;
429 unsigned int font_data, linesize, xorcol, bpp;
430 int i;
pbrook6d6f7c22006-03-11 15:35:30 +0000431 unsigned int fgcol, bgcol;
432
433#ifdef DEBUG_CONSOLE
434 printf("x: %2i y: %2i", x, y);
435 console_print_text_attributes(t_attrib, ch);
436#endif
437
438 if (t_attrib->invers) {
439 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
440 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
441 } else {
442 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
443 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
444 }
bellarde7f0ad52004-07-14 17:28:59 +0000445
aliguori0e1f5a02008-11-24 19:29:13 +0000446 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
447 d = ds_get_data(ds) +
448 ds_get_linesize(ds) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
449 linesize = ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000450 font_ptr = vgafont16 + FONT_HEIGHT * ch;
451 xorcol = bgcol ^ fgcol;
aliguori0e1f5a02008-11-24 19:29:13 +0000452 switch(ds_get_bits_per_pixel(ds)) {
bellarde7f0ad52004-07-14 17:28:59 +0000453 case 8:
454 for(i = 0; i < FONT_HEIGHT; i++) {
455 font_data = *font_ptr++;
pbrook6d6f7c22006-03-11 15:35:30 +0000456 if (t_attrib->uline
457 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
458 font_data = 0xFFFF;
459 }
bellarde7f0ad52004-07-14 17:28:59 +0000460 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
461 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
462 d += linesize;
463 }
464 break;
465 case 16:
466 case 15:
467 for(i = 0; i < FONT_HEIGHT; i++) {
468 font_data = *font_ptr++;
pbrook6d6f7c22006-03-11 15:35:30 +0000469 if (t_attrib->uline
470 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
471 font_data = 0xFFFF;
472 }
bellarde7f0ad52004-07-14 17:28:59 +0000473 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
474 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
475 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
476 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
477 d += linesize;
478 }
479 break;
480 case 32:
481 for(i = 0; i < FONT_HEIGHT; i++) {
482 font_data = *font_ptr++;
pbrook6d6f7c22006-03-11 15:35:30 +0000483 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
484 font_data = 0xFFFF;
485 }
bellarde7f0ad52004-07-14 17:28:59 +0000486 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
487 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
488 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
489 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
490 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
491 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
492 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
493 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
494 d += linesize;
495 }
496 break;
497 }
498}
499
500static void text_console_resize(TextConsole *s)
501{
502 TextCell *cells, *c, *c1;
503 int w1, x, y, last_width;
504
505 last_width = s->width;
506 s->width = s->g_width / FONT_WIDTH;
507 s->height = s->g_height / FONT_HEIGHT;
508
509 w1 = last_width;
510 if (s->width < w1)
511 w1 = s->width;
512
513 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
514 for(y = 0; y < s->total_height; y++) {
515 c = &cells[y * s->width];
516 if (w1 > 0) {
517 c1 = &s->cells[y * last_width];
518 for(x = 0; x < w1; x++) {
519 *c++ = *c1++;
520 }
521 }
522 for(x = w1; x < s->width; x++) {
523 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000524 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000525 c++;
526 }
527 }
balroga528b802007-10-30 22:38:53 +0000528 qemu_free(s->cells);
bellarde7f0ad52004-07-14 17:28:59 +0000529 s->cells = cells;
530}
531
balrog4d3b6f62008-02-10 16:33:14 +0000532static inline void text_update_xy(TextConsole *s, int x, int y)
533{
534 s->text_x[0] = MIN(s->text_x[0], x);
535 s->text_x[1] = MAX(s->text_x[1], x);
536 s->text_y[0] = MIN(s->text_y[0], y);
537 s->text_y[1] = MAX(s->text_y[1], y);
538}
539
bellarde7f0ad52004-07-14 17:28:59 +0000540static void update_xy(TextConsole *s, int x, int y)
541{
542 TextCell *c;
543 int y1, y2;
544
545 if (s == active_console) {
aliguori0e1f5a02008-11-24 19:29:13 +0000546 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000547 text_update_xy(s, x, y);
548 return;
549 }
550
bellarde7f0ad52004-07-14 17:28:59 +0000551 y1 = (s->y_base + y) % s->total_height;
552 y2 = y1 - s->y_displayed;
553 if (y2 < 0)
554 y2 += s->total_height;
555 if (y2 < s->height) {
556 c = &s->cells[y1 * s->width + x];
ths5fafdf22007-09-16 21:08:06 +0000557 vga_putcharxy(s->ds, x, y2, c->ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000558 &(c->t_attrib));
ths5fafdf22007-09-16 21:08:06 +0000559 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
bellarde7f0ad52004-07-14 17:28:59 +0000560 FONT_WIDTH, FONT_HEIGHT);
561 }
562 }
563}
564
565static void console_show_cursor(TextConsole *s, int show)
566{
567 TextCell *c;
568 int y, y1;
569
570 if (s == active_console) {
thsed8276a2007-02-10 22:37:56 +0000571 int x = s->x;
balrog4d3b6f62008-02-10 16:33:14 +0000572
aliguori0e1f5a02008-11-24 19:29:13 +0000573 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000574 s->cursor_invalidate = 1;
575 return;
576 }
577
thsed8276a2007-02-10 22:37:56 +0000578 if (x >= s->width) {
579 x = s->width - 1;
580 }
bellarde7f0ad52004-07-14 17:28:59 +0000581 y1 = (s->y_base + s->y) % s->total_height;
582 y = y1 - s->y_displayed;
583 if (y < 0)
584 y += s->total_height;
585 if (y < s->height) {
thsed8276a2007-02-10 22:37:56 +0000586 c = &s->cells[y1 * s->width + x];
bellarde7f0ad52004-07-14 17:28:59 +0000587 if (show) {
pbrook6d6f7c22006-03-11 15:35:30 +0000588 TextAttributes t_attrib = s->t_attrib_default;
589 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
thsed8276a2007-02-10 22:37:56 +0000590 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
bellarde7f0ad52004-07-14 17:28:59 +0000591 } else {
thsed8276a2007-02-10 22:37:56 +0000592 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
bellarde7f0ad52004-07-14 17:28:59 +0000593 }
ths5fafdf22007-09-16 21:08:06 +0000594 dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
bellarde7f0ad52004-07-14 17:28:59 +0000595 FONT_WIDTH, FONT_HEIGHT);
596 }
597 }
598}
599
600static void console_refresh(TextConsole *s)
601{
602 TextCell *c;
603 int x, y, y1;
604
ths5fafdf22007-09-16 21:08:06 +0000605 if (s != active_console)
bellarde7f0ad52004-07-14 17:28:59 +0000606 return;
aliguori0e1f5a02008-11-24 19:29:13 +0000607 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000608 s->text_x[0] = 0;
609 s->text_y[0] = 0;
610 s->text_x[1] = s->width - 1;
611 s->text_y[1] = s->height - 1;
612 s->cursor_invalidate = 1;
613 return;
614 }
bellarde7f0ad52004-07-14 17:28:59 +0000615
aliguori0e1f5a02008-11-24 19:29:13 +0000616 vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
pbrook6d6f7c22006-03-11 15:35:30 +0000617 color_table[0][COLOR_BLACK]);
bellarde7f0ad52004-07-14 17:28:59 +0000618 y1 = s->y_displayed;
619 for(y = 0; y < s->height; y++) {
620 c = s->cells + y1 * s->width;
621 for(x = 0; x < s->width; x++) {
ths5fafdf22007-09-16 21:08:06 +0000622 vga_putcharxy(s->ds, x, y, c->ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000623 &(c->t_attrib));
bellarde7f0ad52004-07-14 17:28:59 +0000624 c++;
625 }
626 if (++y1 == s->total_height)
627 y1 = 0;
628 }
aliguori0e1f5a02008-11-24 19:29:13 +0000629 dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
bellarde7f0ad52004-07-14 17:28:59 +0000630 console_show_cursor(s, 1);
631}
632
633static void console_scroll(int ydelta)
634{
635 TextConsole *s;
636 int i, y1;
ths3b46e622007-09-17 08:09:54 +0000637
bellarde7f0ad52004-07-14 17:28:59 +0000638 s = active_console;
thsaf3a9032007-07-11 23:14:59 +0000639 if (!s || (s->console_type == GRAPHIC_CONSOLE))
bellarde7f0ad52004-07-14 17:28:59 +0000640 return;
641
642 if (ydelta > 0) {
643 for(i = 0; i < ydelta; i++) {
644 if (s->y_displayed == s->y_base)
645 break;
646 if (++s->y_displayed == s->total_height)
647 s->y_displayed = 0;
648 }
649 } else {
650 ydelta = -ydelta;
651 i = s->backscroll_height;
652 if (i > s->total_height - s->height)
653 i = s->total_height - s->height;
654 y1 = s->y_base - i;
655 if (y1 < 0)
656 y1 += s->total_height;
657 for(i = 0; i < ydelta; i++) {
658 if (s->y_displayed == y1)
659 break;
660 if (--s->y_displayed < 0)
661 s->y_displayed = s->total_height - 1;
662 }
663 }
664 console_refresh(s);
665}
666
667static void console_put_lf(TextConsole *s)
668{
669 TextCell *c;
670 int x, y1;
671
bellarde7f0ad52004-07-14 17:28:59 +0000672 s->y++;
673 if (s->y >= s->height) {
674 s->y = s->height - 1;
pbrook6d6f7c22006-03-11 15:35:30 +0000675
bellarde7f0ad52004-07-14 17:28:59 +0000676 if (s->y_displayed == s->y_base) {
677 if (++s->y_displayed == s->total_height)
678 s->y_displayed = 0;
679 }
680 if (++s->y_base == s->total_height)
681 s->y_base = 0;
682 if (s->backscroll_height < s->total_height)
683 s->backscroll_height++;
684 y1 = (s->y_base + s->height - 1) % s->total_height;
685 c = &s->cells[y1 * s->width];
686 for(x = 0; x < s->width; x++) {
687 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000688 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000689 c++;
690 }
691 if (s == active_console && s->y_displayed == s->y_base) {
aliguori0e1f5a02008-11-24 19:29:13 +0000692 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000693 s->text_x[0] = 0;
694 s->text_y[0] = 0;
695 s->text_x[1] = s->width - 1;
696 s->text_y[1] = s->height - 1;
697 return;
698 }
699
ths5fafdf22007-09-16 21:08:06 +0000700 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
701 s->width * FONT_WIDTH,
bellarde7f0ad52004-07-14 17:28:59 +0000702 (s->height - 1) * FONT_HEIGHT);
703 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
ths5fafdf22007-09-16 21:08:06 +0000704 s->width * FONT_WIDTH, FONT_HEIGHT,
pbrook6d6f7c22006-03-11 15:35:30 +0000705 color_table[0][s->t_attrib_default.bgcol]);
ths5fafdf22007-09-16 21:08:06 +0000706 dpy_update(s->ds, 0, 0,
bellarde7f0ad52004-07-14 17:28:59 +0000707 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
708 }
709 }
710}
711
pbrook6d6f7c22006-03-11 15:35:30 +0000712/* Set console attributes depending on the current escape codes.
713 * NOTE: I know this code is not very efficient (checking every color for it
714 * self) but it is more readable and better maintainable.
715 */
716static void console_handle_escape(TextConsole *s)
717{
718 int i;
719
pbrook6d6f7c22006-03-11 15:35:30 +0000720 for (i=0; i<s->nb_esc_params; i++) {
721 switch (s->esc_params[i]) {
722 case 0: /* reset all console attributes to default */
723 s->t_attrib = s->t_attrib_default;
724 break;
725 case 1:
726 s->t_attrib.bold = 1;
727 break;
728 case 4:
729 s->t_attrib.uline = 1;
730 break;
731 case 5:
732 s->t_attrib.blink = 1;
733 break;
734 case 7:
735 s->t_attrib.invers = 1;
736 break;
737 case 8:
738 s->t_attrib.unvisible = 1;
739 break;
740 case 22:
741 s->t_attrib.bold = 0;
742 break;
743 case 24:
744 s->t_attrib.uline = 0;
745 break;
746 case 25:
747 s->t_attrib.blink = 0;
748 break;
749 case 27:
750 s->t_attrib.invers = 0;
751 break;
752 case 28:
753 s->t_attrib.unvisible = 0;
754 break;
755 /* set foreground color */
756 case 30:
757 s->t_attrib.fgcol=COLOR_BLACK;
758 break;
759 case 31:
760 s->t_attrib.fgcol=COLOR_RED;
761 break;
762 case 32:
763 s->t_attrib.fgcol=COLOR_GREEN;
764 break;
765 case 33:
766 s->t_attrib.fgcol=COLOR_YELLOW;
767 break;
768 case 34:
769 s->t_attrib.fgcol=COLOR_BLUE;
770 break;
771 case 35:
772 s->t_attrib.fgcol=COLOR_MAGENTA;
773 break;
774 case 36:
775 s->t_attrib.fgcol=COLOR_CYAN;
776 break;
777 case 37:
778 s->t_attrib.fgcol=COLOR_WHITE;
779 break;
780 /* set background color */
781 case 40:
782 s->t_attrib.bgcol=COLOR_BLACK;
783 break;
784 case 41:
785 s->t_attrib.bgcol=COLOR_RED;
786 break;
787 case 42:
788 s->t_attrib.bgcol=COLOR_GREEN;
789 break;
790 case 43:
791 s->t_attrib.bgcol=COLOR_YELLOW;
792 break;
793 case 44:
794 s->t_attrib.bgcol=COLOR_BLUE;
795 break;
796 case 45:
797 s->t_attrib.bgcol=COLOR_MAGENTA;
798 break;
799 case 46:
800 s->t_attrib.bgcol=COLOR_CYAN;
801 break;
802 case 47:
803 s->t_attrib.bgcol=COLOR_WHITE;
804 break;
805 }
806 }
807}
808
thsadb47962007-01-16 23:02:36 +0000809static void console_clear_xy(TextConsole *s, int x, int y)
810{
811 int y1 = (s->y_base + y) % s->total_height;
812 TextCell *c = &s->cells[y1 * s->width + x];
813 c->ch = ' ';
814 c->t_attrib = s->t_attrib_default;
815 c++;
816 update_xy(s, x, y);
817}
818
bellarde7f0ad52004-07-14 17:28:59 +0000819static void console_putchar(TextConsole *s, int ch)
820{
821 TextCell *c;
thsadb47962007-01-16 23:02:36 +0000822 int y1, i;
823 int x, y;
bellarde7f0ad52004-07-14 17:28:59 +0000824
825 switch(s->state) {
826 case TTY_STATE_NORM:
827 switch(ch) {
pbrook6d6f7c22006-03-11 15:35:30 +0000828 case '\r': /* carriage return */
bellarde7f0ad52004-07-14 17:28:59 +0000829 s->x = 0;
830 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000831 case '\n': /* newline */
bellarde7f0ad52004-07-14 17:28:59 +0000832 console_put_lf(s);
833 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000834 case '\b': /* backspace */
ths5fafdf22007-09-16 21:08:06 +0000835 if (s->x > 0)
bellarde15d7372006-06-25 16:26:29 +0000836 s->x--;
pbrook6d6f7c22006-03-11 15:35:30 +0000837 break;
838 case '\t': /* tabspace */
839 if (s->x + (8 - (s->x % 8)) > s->width) {
bellardbd468842006-07-14 20:24:31 +0000840 s->x = 0;
pbrook6d6f7c22006-03-11 15:35:30 +0000841 console_put_lf(s);
842 } else {
843 s->x = s->x + (8 - (s->x % 8));
844 }
845 break;
846 case '\a': /* alert aka. bell */
847 /* TODO: has to be implemented */
848 break;
thsadb47962007-01-16 23:02:36 +0000849 case 14:
850 /* SI (shift in), character set 0 (ignored) */
851 break;
852 case 15:
853 /* SO (shift out), character set 1 (ignored) */
854 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000855 case 27: /* esc (introducing an escape sequence) */
bellarde7f0ad52004-07-14 17:28:59 +0000856 s->state = TTY_STATE_ESC;
857 break;
858 default:
thsed8276a2007-02-10 22:37:56 +0000859 if (s->x >= s->width) {
860 /* line wrap */
861 s->x = 0;
862 console_put_lf(s);
thsadb47962007-01-16 23:02:36 +0000863 }
bellarde7f0ad52004-07-14 17:28:59 +0000864 y1 = (s->y_base + s->y) % s->total_height;
865 c = &s->cells[y1 * s->width + s->x];
866 c->ch = ch;
pbrook6d6f7c22006-03-11 15:35:30 +0000867 c->t_attrib = s->t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +0000868 update_xy(s, s->x, s->y);
869 s->x++;
bellarde7f0ad52004-07-14 17:28:59 +0000870 break;
871 }
872 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000873 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
bellarde7f0ad52004-07-14 17:28:59 +0000874 if (ch == '[') {
875 for(i=0;i<MAX_ESC_PARAMS;i++)
876 s->esc_params[i] = 0;
877 s->nb_esc_params = 0;
878 s->state = TTY_STATE_CSI;
879 } else {
880 s->state = TTY_STATE_NORM;
881 }
882 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000883 case TTY_STATE_CSI: /* handle escape sequence parameters */
bellarde7f0ad52004-07-14 17:28:59 +0000884 if (ch >= '0' && ch <= '9') {
885 if (s->nb_esc_params < MAX_ESC_PARAMS) {
ths5fafdf22007-09-16 21:08:06 +0000886 s->esc_params[s->nb_esc_params] =
bellarde7f0ad52004-07-14 17:28:59 +0000887 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
888 }
889 } else {
890 s->nb_esc_params++;
891 if (ch == ';')
892 break;
thsadb47962007-01-16 23:02:36 +0000893#ifdef DEBUG_CONSOLE
894 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
895 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
896#endif
bellarde7f0ad52004-07-14 17:28:59 +0000897 s->state = TTY_STATE_NORM;
898 switch(ch) {
thsadb47962007-01-16 23:02:36 +0000899 case 'A':
900 /* move cursor up */
901 if (s->esc_params[0] == 0) {
902 s->esc_params[0] = 1;
903 }
904 s->y -= s->esc_params[0];
905 if (s->y < 0) {
906 s->y = 0;
bellarde7f0ad52004-07-14 17:28:59 +0000907 }
908 break;
thsadb47962007-01-16 23:02:36 +0000909 case 'B':
910 /* move cursor down */
911 if (s->esc_params[0] == 0) {
912 s->esc_params[0] = 1;
913 }
914 s->y += s->esc_params[0];
915 if (s->y >= s->height) {
916 s->y = s->height - 1;
917 }
918 break;
919 case 'C':
920 /* move cursor right */
921 if (s->esc_params[0] == 0) {
922 s->esc_params[0] = 1;
923 }
924 s->x += s->esc_params[0];
925 if (s->x >= s->width) {
926 s->x = s->width - 1;
927 }
928 break;
929 case 'D':
930 /* move cursor left */
931 if (s->esc_params[0] == 0) {
932 s->esc_params[0] = 1;
933 }
934 s->x -= s->esc_params[0];
935 if (s->x < 0) {
936 s->x = 0;
937 }
938 break;
939 case 'G':
940 /* move cursor to column */
941 s->x = s->esc_params[0] - 1;
942 if (s->x < 0) {
943 s->x = 0;
944 }
945 break;
946 case 'f':
947 case 'H':
948 /* move cursor to row, column */
949 s->x = s->esc_params[1] - 1;
950 if (s->x < 0) {
951 s->x = 0;
952 }
953 s->y = s->esc_params[0] - 1;
954 if (s->y < 0) {
955 s->y = 0;
956 }
957 break;
958 case 'J':
959 switch (s->esc_params[0]) {
960 case 0:
961 /* clear to end of screen */
962 for (y = s->y; y < s->height; y++) {
963 for (x = 0; x < s->width; x++) {
964 if (y == s->y && x < s->x) {
965 continue;
966 }
967 console_clear_xy(s, x, y);
968 }
969 }
970 break;
971 case 1:
972 /* clear from beginning of screen */
973 for (y = 0; y <= s->y; y++) {
974 for (x = 0; x < s->width; x++) {
975 if (y == s->y && x > s->x) {
976 break;
977 }
978 console_clear_xy(s, x, y);
979 }
980 }
981 break;
982 case 2:
983 /* clear entire screen */
984 for (y = 0; y <= s->height; y++) {
985 for (x = 0; x < s->width; x++) {
986 console_clear_xy(s, x, y);
987 }
988 }
989 break;
990 }
991 case 'K':
992 switch (s->esc_params[0]) {
993 case 0:
994 /* clear to eol */
995 for(x = s->x; x < s->width; x++) {
996 console_clear_xy(s, x, s->y);
997 }
998 break;
999 case 1:
1000 /* clear from beginning of line */
1001 for (x = 0; x <= s->x; x++) {
1002 console_clear_xy(s, x, s->y);
1003 }
1004 break;
1005 case 2:
1006 /* clear entire line */
1007 for(x = 0; x < s->width; x++) {
1008 console_clear_xy(s, x, s->y);
1009 }
bellarde7f0ad52004-07-14 17:28:59 +00001010 break;
1011 }
thsadb47962007-01-16 23:02:36 +00001012 break;
1013 case 'm':
pbrook6d6f7c22006-03-11 15:35:30 +00001014 console_handle_escape(s);
bellarde7f0ad52004-07-14 17:28:59 +00001015 break;
thsadb47962007-01-16 23:02:36 +00001016 case 'n':
1017 /* report cursor position */
1018 /* TODO: send ESC[row;colR */
1019 break;
1020 case 's':
1021 /* save cursor position */
1022 s->x_saved = s->x;
1023 s->y_saved = s->y;
1024 break;
1025 case 'u':
1026 /* restore cursor position */
1027 s->x = s->x_saved;
1028 s->y = s->y_saved;
1029 break;
1030 default:
1031#ifdef DEBUG_CONSOLE
1032 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1033#endif
1034 break;
1035 }
1036 break;
bellarde7f0ad52004-07-14 17:28:59 +00001037 }
1038 }
1039}
1040
1041void console_select(unsigned int index)
1042{
1043 TextConsole *s;
pbrook6d6f7c22006-03-11 15:35:30 +00001044
bellarde7f0ad52004-07-14 17:28:59 +00001045 if (index >= MAX_CONSOLES)
1046 return;
aliguori7d957bd2009-01-15 22:14:11 +00001047 active_console->g_width = ds_get_width(active_console->ds);
1048 active_console->g_height = ds_get_height(active_console->ds);
bellarde7f0ad52004-07-14 17:28:59 +00001049 s = consoles[index];
1050 if (s) {
aliguori7d957bd2009-01-15 22:14:11 +00001051 DisplayState *ds = s->ds;
bellarde7f0ad52004-07-14 17:28:59 +00001052 active_console = s;
aliguori7d957bd2009-01-15 22:14:11 +00001053 ds->surface = qemu_resize_displaysurface(ds->surface, s->g_width,
1054 s->g_height, 32, 4 * s->g_width);
1055 dpy_resize(s->ds);
balrog4d3b6f62008-02-10 16:33:14 +00001056 vga_hw_invalidate();
bellarde7f0ad52004-07-14 17:28:59 +00001057 }
1058}
1059
1060static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1061{
1062 TextConsole *s = chr->opaque;
1063 int i;
1064
1065 console_show_cursor(s, 0);
1066 for(i = 0; i < len; i++) {
1067 console_putchar(s, buf[i]);
1068 }
1069 console_show_cursor(s, 1);
1070 return len;
1071}
1072
bellard6fcfafb2004-08-01 21:48:30 +00001073static void console_send_event(CharDriverState *chr, int event)
1074{
1075 TextConsole *s = chr->opaque;
1076 int i;
1077
1078 if (event == CHR_EVENT_FOCUS) {
1079 for(i = 0; i < nb_consoles; i++) {
1080 if (consoles[i] == s) {
1081 console_select(i);
1082 break;
1083 }
1084 }
1085 }
1086}
1087
bellarde15d7372006-06-25 16:26:29 +00001088static void kbd_send_chars(void *opaque)
1089{
1090 TextConsole *s = opaque;
1091 int len;
1092 uint8_t buf[16];
ths3b46e622007-09-17 08:09:54 +00001093
pbrooke5b0bc42007-01-27 23:46:43 +00001094 len = qemu_chr_can_read(s->chr);
bellarde15d7372006-06-25 16:26:29 +00001095 if (len > s->out_fifo.count)
1096 len = s->out_fifo.count;
1097 if (len > 0) {
1098 if (len > sizeof(buf))
1099 len = sizeof(buf);
1100 qemu_fifo_read(&s->out_fifo, buf, len);
pbrooke5b0bc42007-01-27 23:46:43 +00001101 qemu_chr_read(s->chr, buf, len);
bellarde15d7372006-06-25 16:26:29 +00001102 }
1103 /* characters are pending: we send them a bit later (XXX:
1104 horrible, should change char device API) */
1105 if (s->out_fifo.count > 0) {
1106 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1107 }
1108}
1109
bellarde7f0ad52004-07-14 17:28:59 +00001110/* called when an ascii key is pressed */
1111void kbd_put_keysym(int keysym)
1112{
1113 TextConsole *s;
1114 uint8_t buf[16], *q;
1115 int c;
1116
1117 s = active_console;
thsaf3a9032007-07-11 23:14:59 +00001118 if (!s || (s->console_type == GRAPHIC_CONSOLE))
bellarde7f0ad52004-07-14 17:28:59 +00001119 return;
1120
1121 switch(keysym) {
1122 case QEMU_KEY_CTRL_UP:
1123 console_scroll(-1);
1124 break;
1125 case QEMU_KEY_CTRL_DOWN:
1126 console_scroll(1);
1127 break;
1128 case QEMU_KEY_CTRL_PAGEUP:
1129 console_scroll(-10);
1130 break;
1131 case QEMU_KEY_CTRL_PAGEDOWN:
1132 console_scroll(10);
1133 break;
1134 default:
bellarde15d7372006-06-25 16:26:29 +00001135 /* convert the QEMU keysym to VT100 key string */
1136 q = buf;
1137 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1138 *q++ = '\033';
1139 *q++ = '[';
1140 c = keysym - 0xe100;
1141 if (c >= 10)
1142 *q++ = '0' + (c / 10);
1143 *q++ = '0' + (c % 10);
1144 *q++ = '~';
1145 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1146 *q++ = '\033';
1147 *q++ = '[';
1148 *q++ = keysym & 0xff;
1149 } else {
bellarde7f0ad52004-07-14 17:28:59 +00001150 *q++ = keysym;
bellarde15d7372006-06-25 16:26:29 +00001151 }
pbrooke5b0bc42007-01-27 23:46:43 +00001152 if (s->chr->chr_read) {
bellarde15d7372006-06-25 16:26:29 +00001153 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1154 kbd_send_chars(s);
bellarde7f0ad52004-07-14 17:28:59 +00001155 }
1156 break;
1157 }
1158}
1159
balrog4d3b6f62008-02-10 16:33:14 +00001160static void text_console_invalidate(void *opaque)
1161{
1162 TextConsole *s = (TextConsole *) opaque;
balrog4d3b6f62008-02-10 16:33:14 +00001163 console_refresh(s);
1164}
1165
1166static void text_console_update(void *opaque, console_ch_t *chardata)
1167{
1168 TextConsole *s = (TextConsole *) opaque;
1169 int i, j, src;
1170
1171 if (s->text_x[0] <= s->text_x[1]) {
1172 src = (s->y_base + s->text_y[0]) * s->width;
1173 chardata += s->text_y[0] * s->width;
1174 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1175 for (j = 0; j < s->width; j ++, src ++)
1176 console_write_ch(chardata ++, s->cells[src].ch |
1177 (s->cells[src].t_attrib.fgcol << 12) |
1178 (s->cells[src].t_attrib.bgcol << 8) |
1179 (s->cells[src].t_attrib.bold << 21));
1180 dpy_update(s->ds, s->text_x[0], s->text_y[0],
1181 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1182 s->text_x[0] = s->width;
1183 s->text_y[0] = s->height;
1184 s->text_x[1] = 0;
1185 s->text_y[1] = 0;
1186 }
1187 if (s->cursor_invalidate) {
1188 dpy_cursor(s->ds, s->x, s->y);
1189 s->cursor_invalidate = 0;
1190 }
1191}
1192
aliguori42aa98e2009-01-16 21:01:48 +00001193static TextConsole *get_graphic_console(DisplayState *ds)
blueswir1a147d622009-01-16 19:41:04 +00001194{
aliguori3023f3322009-01-16 19:04:14 +00001195 int i;
1196 TextConsole *s;
1197 for (i = 0; i < nb_consoles; i++) {
1198 s = consoles[i];
aliguori42aa98e2009-01-16 21:01:48 +00001199 if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
aliguori3023f3322009-01-16 19:04:14 +00001200 return s;
1201 }
1202 return NULL;
1203}
1204
thsaf3a9032007-07-11 23:14:59 +00001205static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
bellarde7f0ad52004-07-14 17:28:59 +00001206{
1207 TextConsole *s;
pbrook95219892006-04-09 01:06:34 +00001208 int i;
bellarde7f0ad52004-07-14 17:28:59 +00001209
1210 if (nb_consoles >= MAX_CONSOLES)
1211 return NULL;
1212 s = qemu_mallocz(sizeof(TextConsole));
1213 if (!s) {
1214 return NULL;
1215 }
thsaf3a9032007-07-11 23:14:59 +00001216 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1217 (console_type == GRAPHIC_CONSOLE))) {
bellarde7f0ad52004-07-14 17:28:59 +00001218 active_console = s;
thsaf3a9032007-07-11 23:14:59 +00001219 }
bellarde7f0ad52004-07-14 17:28:59 +00001220 s->ds = ds;
thsaf3a9032007-07-11 23:14:59 +00001221 s->console_type = console_type;
1222 if (console_type != GRAPHIC_CONSOLE) {
pbrook95219892006-04-09 01:06:34 +00001223 consoles[nb_consoles++] = s;
1224 } else {
1225 /* HACK: Put graphical consoles before text consoles. */
1226 for (i = nb_consoles; i > 0; i--) {
thsaf3a9032007-07-11 23:14:59 +00001227 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
pbrook95219892006-04-09 01:06:34 +00001228 break;
1229 consoles[i] = consoles[i - 1];
1230 }
1231 consoles[i] = s;
aliguori3023f3322009-01-16 19:04:14 +00001232 nb_consoles++;
pbrook95219892006-04-09 01:06:34 +00001233 }
bellarde7f0ad52004-07-14 17:28:59 +00001234 return s;
1235}
1236
aliguori3023f3322009-01-16 19:04:14 +00001237DisplayState *graphic_console_init(vga_hw_update_ptr update,
1238 vga_hw_invalidate_ptr invalidate,
1239 vga_hw_screen_dump_ptr screen_dump,
1240 vga_hw_text_update_ptr text_update,
1241 void *opaque)
bellarde7f0ad52004-07-14 17:28:59 +00001242{
pbrook95219892006-04-09 01:06:34 +00001243 TextConsole *s;
aliguori3023f3322009-01-16 19:04:14 +00001244 DisplayState *ds;
aurel32f0f2f972009-01-16 21:13:49 +00001245
aliguori3023f3322009-01-16 19:04:14 +00001246 ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
1247 if (ds == NULL)
1248 return NULL;
1249 ds->surface = qemu_create_displaysurface(640, 480, 32, 640 * 4);
pbrook95219892006-04-09 01:06:34 +00001250
thsaf3a9032007-07-11 23:14:59 +00001251 s = new_console(ds, GRAPHIC_CONSOLE);
aliguori3023f3322009-01-16 19:04:14 +00001252 if (s == NULL) {
1253 qemu_free_displaysurface(ds->surface);
1254 qemu_free(ds);
1255 return NULL;
1256 }
pbrook95219892006-04-09 01:06:34 +00001257 s->hw_update = update;
1258 s->hw_invalidate = invalidate;
1259 s->hw_screen_dump = screen_dump;
balrog4d3b6f62008-02-10 16:33:14 +00001260 s->hw_text_update = text_update;
pbrook95219892006-04-09 01:06:34 +00001261 s->hw = opaque;
aliguori3023f3322009-01-16 19:04:14 +00001262
aurel32f0f2f972009-01-16 21:13:49 +00001263 register_displaystate(ds);
aliguori3023f3322009-01-16 19:04:14 +00001264 return ds;
pbrook95219892006-04-09 01:06:34 +00001265}
1266
1267int is_graphic_console(void)
1268{
balrog4d3b6f62008-02-10 16:33:14 +00001269 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
bellarde7f0ad52004-07-14 17:28:59 +00001270}
1271
balrogc21bbcf2008-09-24 03:32:33 +00001272int is_fixedsize_console(void)
1273{
1274 return active_console && active_console->console_type != TEXT_CONSOLE;
1275}
1276
balroga528b802007-10-30 22:38:53 +00001277void console_color_init(DisplayState *ds)
1278{
1279 int i, j;
1280 for (j = 0; j < 2; j++) {
1281 for (i = 0; i < 8; i++) {
aurel32f0f2f972009-01-16 21:13:49 +00001282 color_table[j][i] = col_expand(ds,
balroga528b802007-10-30 22:38:53 +00001283 vga_get_color(ds, color_table_rgb[j][i]));
1284 }
1285 }
1286}
1287
aliguori2796dae2009-01-16 20:23:27 +00001288static int n_text_consoles;
1289static CharDriverState *text_consoles[128];
1290static char *text_console_strs[128];
1291
1292static void text_console_do_init(CharDriverState *chr, DisplayState *ds, const char *p)
bellarde7f0ad52004-07-14 17:28:59 +00001293{
bellarde7f0ad52004-07-14 17:28:59 +00001294 TextConsole *s;
thsaf3a9032007-07-11 23:14:59 +00001295 unsigned width;
1296 unsigned height;
bellarde7f0ad52004-07-14 17:28:59 +00001297 static int color_inited;
pbrook6d6f7c22006-03-11 15:35:30 +00001298
balrogc21bbcf2008-09-24 03:32:33 +00001299 s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
bellarde7f0ad52004-07-14 17:28:59 +00001300 if (!s) {
1301 free(chr);
aliguorifdb868e2009-01-16 21:06:20 +00001302 return;
bellarde7f0ad52004-07-14 17:28:59 +00001303 }
bellarde7f0ad52004-07-14 17:28:59 +00001304 chr->opaque = s;
1305 chr->chr_write = console_puts;
bellard6fcfafb2004-08-01 21:48:30 +00001306 chr->chr_send_event = console_send_event;
1307
pbrooke5b0bc42007-01-27 23:46:43 +00001308 s->chr = chr;
bellarde15d7372006-06-25 16:26:29 +00001309 s->out_fifo.buf = s->out_fifo_buf;
1310 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1311 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
aliguori3023f3322009-01-16 19:04:14 +00001312 s->ds = ds;
ths3b46e622007-09-17 08:09:54 +00001313
bellarde7f0ad52004-07-14 17:28:59 +00001314 if (!color_inited) {
1315 color_inited = 1;
balroga528b802007-10-30 22:38:53 +00001316 console_color_init(s->ds);
bellarde7f0ad52004-07-14 17:28:59 +00001317 }
1318 s->y_displayed = 0;
1319 s->y_base = 0;
1320 s->total_height = DEFAULT_BACKSCROLL;
1321 s->x = 0;
1322 s->y = 0;
aliguori0e1f5a02008-11-24 19:29:13 +00001323 width = ds_get_width(s->ds);
1324 height = ds_get_height(s->ds);
thsaf3a9032007-07-11 23:14:59 +00001325 if (p != 0) {
1326 width = strtoul(p, (char **)&p, 10);
1327 if (*p == 'C') {
1328 p++;
1329 width *= FONT_WIDTH;
1330 }
1331 if (*p == 'x') {
1332 p++;
1333 height = strtoul(p, (char **)&p, 10);
1334 if (*p == 'C') {
1335 p++;
1336 height *= FONT_HEIGHT;
1337 }
1338 }
1339 }
1340 s->g_width = width;
1341 s->g_height = height;
pbrook6d6f7c22006-03-11 15:35:30 +00001342
balrog4d3b6f62008-02-10 16:33:14 +00001343 s->hw_invalidate = text_console_invalidate;
1344 s->hw_text_update = text_console_update;
1345 s->hw = s;
1346
pbrook6d6f7c22006-03-11 15:35:30 +00001347 /* Set text attribute defaults */
1348 s->t_attrib_default.bold = 0;
1349 s->t_attrib_default.uline = 0;
1350 s->t_attrib_default.blink = 0;
1351 s->t_attrib_default.invers = 0;
1352 s->t_attrib_default.unvisible = 0;
1353 s->t_attrib_default.fgcol = COLOR_WHITE;
1354 s->t_attrib_default.bgcol = COLOR_BLACK;
pbrook6d6f7c22006-03-11 15:35:30 +00001355 /* set current text attributes to default */
1356 s->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +00001357 text_console_resize(s);
1358
ths86e94de2007-01-05 22:01:59 +00001359 qemu_chr_reset(chr);
bellarde7f0ad52004-07-14 17:28:59 +00001360}
pbrookc60e08d2008-07-01 16:24:38 +00001361
aliguori2796dae2009-01-16 20:23:27 +00001362CharDriverState *text_console_init(const char *p)
1363{
1364 CharDriverState *chr;
1365
1366 chr = qemu_mallocz(sizeof(CharDriverState));
1367 if (!chr)
1368 return NULL;
1369
1370 if (n_text_consoles == 128) {
1371 fprintf(stderr, "Too many text consoles\n");
1372 exit(1);
1373 }
1374 text_consoles[n_text_consoles] = chr;
1375 text_console_strs[n_text_consoles] = p ? qemu_strdup(p) : NULL;
1376 n_text_consoles++;
1377
1378 return chr;
1379}
1380
1381void text_consoles_set_display(DisplayState *ds)
1382{
1383 int i;
1384
1385 for (i = 0; i < n_text_consoles; i++) {
1386 text_console_do_init(text_consoles[i], ds, text_console_strs[i]);
1387 qemu_free(text_console_strs[i]);
1388 }
1389
1390 n_text_consoles = 0;
1391}
1392
aliguori3023f3322009-01-16 19:04:14 +00001393void qemu_console_resize(DisplayState *ds, int width, int height)
pbrookc60e08d2008-07-01 16:24:38 +00001394{
aliguori42aa98e2009-01-16 21:01:48 +00001395 TextConsole *s = get_graphic_console(ds);
aliguori3023f3322009-01-16 19:04:14 +00001396 s->g_width = width;
1397 s->g_height = height;
1398 if (is_graphic_console()) {
aliguori7d957bd2009-01-15 22:14:11 +00001399 ds->surface = qemu_resize_displaysurface(ds->surface, width, height, 32, 4 * width);
aliguori3023f3322009-01-16 19:04:14 +00001400 dpy_resize(ds);
pbrookc60e08d2008-07-01 16:24:38 +00001401 }
1402}
balrog38334f72008-09-24 02:21:24 +00001403
aliguori3023f3322009-01-16 19:04:14 +00001404void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
1405 int dst_x, int dst_y, int w, int h)
balrogc21bbcf2008-09-24 03:32:33 +00001406{
aliguori3023f3322009-01-16 19:04:14 +00001407 if (is_graphic_console()) {
1408 dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
balrog38334f72008-09-24 02:21:24 +00001409 }
1410}
aliguori7d957bd2009-01-15 22:14:11 +00001411
1412static PixelFormat qemu_default_pixelformat(int bpp)
1413{
1414 PixelFormat pf;
1415
1416 memset(&pf, 0x00, sizeof(PixelFormat));
1417
1418 pf.bits_per_pixel = bpp;
1419 pf.bytes_per_pixel = bpp / 8;
1420 pf.depth = bpp == 32 ? 24 : bpp;
1421
1422 switch (bpp) {
1423 case 8:
1424 pf.rmask = 0x000000E0;
1425 pf.gmask = 0x0000001C;
1426 pf.bmask = 0x00000003;
1427 pf.rmax = 7;
1428 pf.gmax = 7;
1429 pf.bmax = 3;
1430 pf.rshift = 5;
1431 pf.gshift = 2;
1432 pf.bshift = 0;
1433 break;
1434 case 16:
1435 pf.rmask = 0x0000F800;
1436 pf.gmask = 0x000007E0;
1437 pf.bmask = 0x0000001F;
1438 pf.rmax = 31;
1439 pf.gmax = 63;
1440 pf.bmax = 31;
1441 pf.rshift = 11;
1442 pf.gshift = 5;
1443 pf.bshift = 0;
1444 break;
1445 case 24:
1446 case 32:
1447 pf.rmask = 0x00FF0000;
1448 pf.gmask = 0x0000FF00;
1449 pf.bmask = 0x000000FF;
1450 pf.rmax = 255;
1451 pf.gmax = 255;
1452 pf.bmax = 255;
1453 pf.rshift = 16;
1454 pf.gshift = 8;
1455 pf.bshift = 0;
1456 break;
1457 default:
1458 break;
1459 }
1460 return pf;
1461}
1462
1463DisplaySurface* qemu_create_displaysurface(int width, int height, int bpp, int linesize)
1464{
1465 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1466 if (surface == NULL) {
1467 fprintf(stderr, "qemu_create_displaysurface: malloc failed\n");
1468 exit(1);
1469 }
1470
1471 surface->width = width;
1472 surface->height = height;
1473 surface->linesize = linesize;
1474 surface->pf = qemu_default_pixelformat(bpp);
1475#ifdef WORDS_BIGENDIAN
1476 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1477#else
1478 surface->flags = QEMU_ALLOCATED_FLAG;
1479#endif
1480 surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
1481 if (surface->data == NULL) {
1482 fprintf(stderr, "qemu_create_displaysurface: malloc failed\n");
1483 exit(1);
1484 }
1485
1486 return surface;
1487}
1488
1489DisplaySurface* qemu_resize_displaysurface(DisplaySurface *surface,
1490 int width, int height, int bpp, int linesize)
1491{
1492 surface->width = width;
1493 surface->height = height;
1494 surface->linesize = linesize;
1495 surface->pf = qemu_default_pixelformat(bpp);
1496 if (surface->flags & QEMU_ALLOCATED_FLAG)
1497 surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height);
1498 else
1499 surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
1500 if (surface->data == NULL) {
1501 fprintf(stderr, "qemu_resize_displaysurface: malloc failed\n");
1502 exit(1);
1503 }
1504#ifdef WORDS_BIGENDIAN
1505 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1506#else
1507 surface->flags = QEMU_ALLOCATED_FLAG;
1508#endif
1509
1510 return surface;
1511}
1512
1513DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
1514 int linesize, uint8_t *data)
1515{
1516 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1517 if (surface == NULL) {
1518 fprintf(stderr, "qemu_create_displaysurface_from: malloc failed\n");
1519 exit(1);
1520 }
1521
1522 surface->width = width;
1523 surface->height = height;
1524 surface->linesize = linesize;
1525 surface->pf = qemu_default_pixelformat(bpp);
1526#ifdef WORDS_BIGENDIAN
1527 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1528#endif
1529 surface->data = data;
1530
1531 return surface;
1532}
1533
1534void qemu_free_displaysurface(DisplaySurface *surface)
1535{
1536 if (surface == NULL)
1537 return;
1538 if (surface->flags & QEMU_ALLOCATED_FLAG)
1539 qemu_free(surface->data);
1540 qemu_free(surface);
1541}