]> git.proxmox.com Git - qemu.git/blob - console.c
fixed C0 status codes (Ralf Baechle)
[qemu.git] / console.c
1 /*
2 * QEMU graphical console
3 *
4 * Copyright (c) 2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include "vl.h"
25
26 #define DEFAULT_BACKSCROLL 512
27 #define MAX_CONSOLES 12
28
29 #define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
30 #define RGB(r, g, b) RGBA(r, g, b, 0xff)
31
32 typedef struct TextCell {
33 uint8_t ch;
34 uint8_t bgcol:4;
35 uint8_t fgcol:4;
36 } TextCell;
37
38 #define MAX_ESC_PARAMS 3
39
40 enum TTYState {
41 TTY_STATE_NORM,
42 TTY_STATE_ESC,
43 TTY_STATE_CSI,
44 };
45
46 struct TextConsole {
47 int text_console; /* true if text console */
48 DisplayState *ds;
49 int g_width, g_height;
50 int width;
51 int height;
52 int total_height;
53 int backscroll_height;
54 int fgcol;
55 int bgcol;
56 int x, y;
57 int y_displayed;
58 int y_base;
59 TextCell *cells;
60
61 enum TTYState state;
62 int esc_params[MAX_ESC_PARAMS];
63 int nb_esc_params;
64
65 /* kbd read handler */
66 IOReadHandler *fd_read;
67 void *fd_opaque;
68 };
69
70 static TextConsole *active_console;
71 static TextConsole *consoles[MAX_CONSOLES];
72 static int nb_consoles = 0;
73
74 /* convert a RGBA color to a color index usable in graphic primitives */
75 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
76 {
77 unsigned int r, g, b, color;
78
79 switch(ds->depth) {
80 #if 0
81 case 8:
82 r = (rgba >> 16) & 0xff;
83 g = (rgba >> 8) & 0xff;
84 b = (rgba) & 0xff;
85 color = (rgb_to_index[r] * 6 * 6) +
86 (rgb_to_index[g] * 6) +
87 (rgb_to_index[b]);
88 break;
89 #endif
90 case 15:
91 r = (rgba >> 16) & 0xff;
92 g = (rgba >> 8) & 0xff;
93 b = (rgba) & 0xff;
94 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
95 break;
96 case 16:
97 r = (rgba >> 16) & 0xff;
98 g = (rgba >> 8) & 0xff;
99 b = (rgba) & 0xff;
100 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
101 break;
102 case 32:
103 default:
104 color = rgba;
105 break;
106 }
107 return color;
108 }
109
110 static void vga_fill_rect (DisplayState *ds,
111 int posx, int posy, int width, int height, uint32_t color)
112 {
113 uint8_t *d, *d1;
114 int x, y, bpp;
115
116 bpp = (ds->depth + 7) >> 3;
117 d1 = ds->data +
118 ds->linesize * posy + bpp * posx;
119 for (y = 0; y < height; y++) {
120 d = d1;
121 switch(bpp) {
122 case 1:
123 for (x = 0; x < width; x++) {
124 *((uint8_t *)d) = color;
125 d++;
126 }
127 break;
128 case 2:
129 for (x = 0; x < width; x++) {
130 *((uint16_t *)d) = color;
131 d += 2;
132 }
133 break;
134 case 4:
135 for (x = 0; x < width; x++) {
136 *((uint32_t *)d) = color;
137 d += 4;
138 }
139 break;
140 }
141 d1 += ds->linesize;
142 }
143 }
144
145 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
146 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
147 {
148 const uint8_t *s;
149 uint8_t *d;
150 int wb, y, bpp;
151
152 bpp = (ds->depth + 7) >> 3;
153 wb = w * bpp;
154 if (yd <= ys) {
155 s = ds->data +
156 ds->linesize * ys + bpp * xs;
157 d = ds->data +
158 ds->linesize * yd + bpp * xd;
159 for (y = 0; y < h; y++) {
160 memmove(d, s, wb);
161 d += ds->linesize;
162 s += ds->linesize;
163 }
164 } else {
165 s = ds->data +
166 ds->linesize * (ys + h - 1) + bpp * xs;
167 d = ds->data +
168 ds->linesize * (yd + h - 1) + bpp * xd;
169 for (y = 0; y < h; y++) {
170 memmove(d, s, wb);
171 d -= ds->linesize;
172 s -= ds->linesize;
173 }
174 }
175 }
176
177 /***********************************************************/
178 /* basic char display */
179
180 #define FONT_HEIGHT 16
181 #define FONT_WIDTH 8
182
183 #include "vgafont.h"
184
185 #define cbswap_32(__x) \
186 ((uint32_t)( \
187 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
188 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
189 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
190 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
191
192 #ifdef WORDS_BIGENDIAN
193 #define PAT(x) x
194 #else
195 #define PAT(x) cbswap_32(x)
196 #endif
197
198 static const uint32_t dmask16[16] = {
199 PAT(0x00000000),
200 PAT(0x000000ff),
201 PAT(0x0000ff00),
202 PAT(0x0000ffff),
203 PAT(0x00ff0000),
204 PAT(0x00ff00ff),
205 PAT(0x00ffff00),
206 PAT(0x00ffffff),
207 PAT(0xff000000),
208 PAT(0xff0000ff),
209 PAT(0xff00ff00),
210 PAT(0xff00ffff),
211 PAT(0xffff0000),
212 PAT(0xffff00ff),
213 PAT(0xffffff00),
214 PAT(0xffffffff),
215 };
216
217 static const uint32_t dmask4[4] = {
218 PAT(0x00000000),
219 PAT(0x0000ffff),
220 PAT(0xffff0000),
221 PAT(0xffffffff),
222 };
223
224 static uint32_t color_table[8];
225
226 static const uint32_t color_table_rgb[8] = {
227 RGB(0x00, 0x00, 0x00),
228 RGB(0xff, 0x00, 0x00),
229 RGB(0x00, 0xff, 0x00),
230 RGB(0xff, 0xff, 0x00),
231 RGB(0x00, 0x00, 0xff),
232 RGB(0xff, 0x00, 0xff),
233 RGB(0x00, 0xff, 0xff),
234 RGB(0xff, 0xff, 0xff),
235 };
236
237 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
238 {
239 switch(ds->depth) {
240 case 8:
241 col |= col << 8;
242 col |= col << 16;
243 break;
244 case 15:
245 case 16:
246 col |= col << 16;
247 break;
248 default:
249 break;
250 }
251
252 return col;
253 }
254
255 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
256 unsigned int fgcol, unsigned int bgcol)
257 {
258 uint8_t *d;
259 const uint8_t *font_ptr;
260 unsigned int font_data, linesize, xorcol, bpp;
261 int i;
262
263 bpp = (ds->depth + 7) >> 3;
264 d = ds->data +
265 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
266 linesize = ds->linesize;
267 font_ptr = vgafont16 + FONT_HEIGHT * ch;
268 xorcol = bgcol ^ fgcol;
269 switch(ds->depth) {
270 case 8:
271 for(i = 0; i < FONT_HEIGHT; i++) {
272 font_data = *font_ptr++;
273 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
274 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
275 d += linesize;
276 }
277 break;
278 case 16:
279 case 15:
280 for(i = 0; i < FONT_HEIGHT; i++) {
281 font_data = *font_ptr++;
282 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
283 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
284 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
285 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
286 d += linesize;
287 }
288 break;
289 case 32:
290 for(i = 0; i < FONT_HEIGHT; i++) {
291 font_data = *font_ptr++;
292 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
293 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
294 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
295 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
296 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
297 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
298 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
299 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
300 d += linesize;
301 }
302 break;
303 }
304 }
305
306 static void text_console_resize(TextConsole *s)
307 {
308 TextCell *cells, *c, *c1;
309 int w1, x, y, last_width;
310
311 last_width = s->width;
312 s->width = s->g_width / FONT_WIDTH;
313 s->height = s->g_height / FONT_HEIGHT;
314
315 w1 = last_width;
316 if (s->width < w1)
317 w1 = s->width;
318
319 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
320 for(y = 0; y < s->total_height; y++) {
321 c = &cells[y * s->width];
322 if (w1 > 0) {
323 c1 = &s->cells[y * last_width];
324 for(x = 0; x < w1; x++) {
325 *c++ = *c1++;
326 }
327 }
328 for(x = w1; x < s->width; x++) {
329 c->ch = ' ';
330 c->fgcol = 7;
331 c->bgcol = 0;
332 c++;
333 }
334 }
335 free(s->cells);
336 s->cells = cells;
337 }
338
339 static void update_xy(TextConsole *s, int x, int y)
340 {
341 TextCell *c;
342 int y1, y2;
343
344 if (s == active_console) {
345 y1 = (s->y_base + y) % s->total_height;
346 y2 = y1 - s->y_displayed;
347 if (y2 < 0)
348 y2 += s->total_height;
349 if (y2 < s->height) {
350 c = &s->cells[y1 * s->width + x];
351 vga_putcharxy(s->ds, x, y2, c->ch,
352 color_table[c->fgcol], color_table[c->bgcol]);
353 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
354 FONT_WIDTH, FONT_HEIGHT);
355 }
356 }
357 }
358
359 static void console_show_cursor(TextConsole *s, int show)
360 {
361 TextCell *c;
362 int y, y1;
363
364 if (s == active_console) {
365 y1 = (s->y_base + s->y) % s->total_height;
366 y = y1 - s->y_displayed;
367 if (y < 0)
368 y += s->total_height;
369 if (y < s->height) {
370 c = &s->cells[y1 * s->width + s->x];
371 if (show) {
372 vga_putcharxy(s->ds, s->x, y, c->ch,
373 color_table[0], color_table[7]);
374 } else {
375 vga_putcharxy(s->ds, s->x, y, c->ch,
376 color_table[c->fgcol], color_table[c->bgcol]);
377 }
378 dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
379 FONT_WIDTH, FONT_HEIGHT);
380 }
381 }
382 }
383
384 static void console_refresh(TextConsole *s)
385 {
386 TextCell *c;
387 int x, y, y1;
388
389 if (s != active_console)
390 return;
391
392 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
393 color_table[0]);
394 y1 = s->y_displayed;
395 for(y = 0; y < s->height; y++) {
396 c = s->cells + y1 * s->width;
397 for(x = 0; x < s->width; x++) {
398 vga_putcharxy(s->ds, x, y, c->ch,
399 color_table[c->fgcol], color_table[c->bgcol]);
400 c++;
401 }
402 if (++y1 == s->total_height)
403 y1 = 0;
404 }
405 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
406 console_show_cursor(s, 1);
407 }
408
409 static void console_scroll(int ydelta)
410 {
411 TextConsole *s;
412 int i, y1;
413
414 s = active_console;
415 if (!s || !s->text_console)
416 return;
417
418 if (ydelta > 0) {
419 for(i = 0; i < ydelta; i++) {
420 if (s->y_displayed == s->y_base)
421 break;
422 if (++s->y_displayed == s->total_height)
423 s->y_displayed = 0;
424 }
425 } else {
426 ydelta = -ydelta;
427 i = s->backscroll_height;
428 if (i > s->total_height - s->height)
429 i = s->total_height - s->height;
430 y1 = s->y_base - i;
431 if (y1 < 0)
432 y1 += s->total_height;
433 for(i = 0; i < ydelta; i++) {
434 if (s->y_displayed == y1)
435 break;
436 if (--s->y_displayed < 0)
437 s->y_displayed = s->total_height - 1;
438 }
439 }
440 console_refresh(s);
441 }
442
443 static void console_put_lf(TextConsole *s)
444 {
445 TextCell *c;
446 int x, y1;
447
448 s->x = 0;
449 s->y++;
450 if (s->y >= s->height) {
451 s->y = s->height - 1;
452
453 if (s->y_displayed == s->y_base) {
454 if (++s->y_displayed == s->total_height)
455 s->y_displayed = 0;
456 }
457 if (++s->y_base == s->total_height)
458 s->y_base = 0;
459 if (s->backscroll_height < s->total_height)
460 s->backscroll_height++;
461 y1 = (s->y_base + s->height - 1) % s->total_height;
462 c = &s->cells[y1 * s->width];
463 for(x = 0; x < s->width; x++) {
464 c->ch = ' ';
465 c->fgcol = s->fgcol;
466 c->bgcol = s->bgcol;
467 c++;
468 }
469 if (s == active_console && s->y_displayed == s->y_base) {
470 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
471 s->width * FONT_WIDTH,
472 (s->height - 1) * FONT_HEIGHT);
473 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
474 s->width * FONT_WIDTH, FONT_HEIGHT,
475 color_table[s->bgcol]);
476 dpy_update(s->ds, 0, 0,
477 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
478 }
479 }
480 }
481
482 static void console_putchar(TextConsole *s, int ch)
483 {
484 TextCell *c;
485 int y1, i, x;
486
487 switch(s->state) {
488 case TTY_STATE_NORM:
489 switch(ch) {
490 case '\r':
491 s->x = 0;
492 break;
493 case '\n':
494 console_put_lf(s);
495 break;
496 case 27:
497 s->state = TTY_STATE_ESC;
498 break;
499 default:
500 y1 = (s->y_base + s->y) % s->total_height;
501 c = &s->cells[y1 * s->width + s->x];
502 c->ch = ch;
503 c->fgcol = s->fgcol;
504 c->bgcol = s->bgcol;
505 update_xy(s, s->x, s->y);
506 s->x++;
507 if (s->x >= s->width)
508 console_put_lf(s);
509 break;
510 }
511 break;
512 case TTY_STATE_ESC:
513 if (ch == '[') {
514 for(i=0;i<MAX_ESC_PARAMS;i++)
515 s->esc_params[i] = 0;
516 s->nb_esc_params = 0;
517 s->state = TTY_STATE_CSI;
518 } else {
519 s->state = TTY_STATE_NORM;
520 }
521 break;
522 case TTY_STATE_CSI:
523 if (ch >= '0' && ch <= '9') {
524 if (s->nb_esc_params < MAX_ESC_PARAMS) {
525 s->esc_params[s->nb_esc_params] =
526 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
527 }
528 } else {
529 s->nb_esc_params++;
530 if (ch == ';')
531 break;
532 s->state = TTY_STATE_NORM;
533 switch(ch) {
534 case 'D':
535 if (s->x > 0)
536 s->x--;
537 break;
538 case 'C':
539 if (s->x < (s->width - 1))
540 s->x++;
541 break;
542 case 'K':
543 /* clear to eol */
544 y1 = (s->y_base + s->y) % s->total_height;
545 for(x = s->x; x < s->width; x++) {
546 c = &s->cells[y1 * s->width + x];
547 c->ch = ' ';
548 c->fgcol = s->fgcol;
549 c->bgcol = s->bgcol;
550 c++;
551 update_xy(s, x, s->y);
552 }
553 break;
554 default:
555 break;
556 }
557 break;
558 }
559 }
560 }
561
562 void console_select(unsigned int index)
563 {
564 TextConsole *s;
565
566 if (index >= MAX_CONSOLES)
567 return;
568 s = consoles[index];
569 if (s) {
570 active_console = s;
571 if (s->text_console) {
572 if (s->g_width != s->ds->width ||
573 s->g_height != s->ds->height) {
574 s->g_width = s->ds->width;
575 s->g_height = s->ds->height;
576 text_console_resize(s);
577 }
578 console_refresh(s);
579 }
580 }
581 }
582
583 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
584 {
585 TextConsole *s = chr->opaque;
586 int i;
587
588 console_show_cursor(s, 0);
589 for(i = 0; i < len; i++) {
590 console_putchar(s, buf[i]);
591 }
592 console_show_cursor(s, 1);
593 return len;
594 }
595
596 static void console_chr_add_read_handler(CharDriverState *chr,
597 IOCanRWHandler *fd_can_read,
598 IOReadHandler *fd_read, void *opaque)
599 {
600 TextConsole *s = chr->opaque;
601 s->fd_read = fd_read;
602 s->fd_opaque = opaque;
603 }
604
605 static void console_send_event(CharDriverState *chr, int event)
606 {
607 TextConsole *s = chr->opaque;
608 int i;
609
610 if (event == CHR_EVENT_FOCUS) {
611 for(i = 0; i < nb_consoles; i++) {
612 if (consoles[i] == s) {
613 console_select(i);
614 break;
615 }
616 }
617 }
618 }
619
620 /* called when an ascii key is pressed */
621 void kbd_put_keysym(int keysym)
622 {
623 TextConsole *s;
624 uint8_t buf[16], *q;
625 int c;
626
627 s = active_console;
628 if (!s || !s->text_console)
629 return;
630
631 switch(keysym) {
632 case QEMU_KEY_CTRL_UP:
633 console_scroll(-1);
634 break;
635 case QEMU_KEY_CTRL_DOWN:
636 console_scroll(1);
637 break;
638 case QEMU_KEY_CTRL_PAGEUP:
639 console_scroll(-10);
640 break;
641 case QEMU_KEY_CTRL_PAGEDOWN:
642 console_scroll(10);
643 break;
644 default:
645 if (s->fd_read) {
646 /* convert the QEMU keysym to VT100 key string */
647 q = buf;
648 if (keysym >= 0xe100 && keysym <= 0xe11f) {
649 *q++ = '\033';
650 *q++ = '[';
651 c = keysym - 0xe100;
652 if (c >= 10)
653 *q++ = '0' + (c / 10);
654 *q++ = '0' + (c % 10);
655 *q++ = '~';
656 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
657 *q++ = '\033';
658 *q++ = '[';
659 *q++ = keysym & 0xff;
660 } else {
661 *q++ = keysym;
662 }
663 s->fd_read(s->fd_opaque, buf, q - buf);
664 }
665 break;
666 }
667 }
668
669 TextConsole *graphic_console_init(DisplayState *ds)
670 {
671 TextConsole *s;
672
673 if (nb_consoles >= MAX_CONSOLES)
674 return NULL;
675 s = qemu_mallocz(sizeof(TextConsole));
676 if (!s) {
677 return NULL;
678 }
679 if (!active_console)
680 active_console = s;
681 s->ds = ds;
682 consoles[nb_consoles++] = s;
683 return s;
684 }
685
686 int is_active_console(TextConsole *s)
687 {
688 return s == active_console;
689 }
690
691 CharDriverState *text_console_init(DisplayState *ds)
692 {
693 CharDriverState *chr;
694 TextConsole *s;
695 int i;
696 static int color_inited;
697
698 chr = qemu_mallocz(sizeof(CharDriverState));
699 if (!chr)
700 return NULL;
701 s = graphic_console_init(ds);
702 if (!s) {
703 free(chr);
704 return NULL;
705 }
706 s->text_console = 1;
707 chr->opaque = s;
708 chr->chr_write = console_puts;
709 chr->chr_add_read_handler = console_chr_add_read_handler;
710 chr->chr_send_event = console_send_event;
711
712 if (!color_inited) {
713 color_inited = 1;
714 for(i = 0; i < 8; i++) {
715 color_table[i] = col_expand(s->ds,
716 vga_get_color(s->ds, color_table_rgb[i]));
717 }
718 }
719 s->y_displayed = 0;
720 s->y_base = 0;
721 s->total_height = DEFAULT_BACKSCROLL;
722 s->x = 0;
723 s->y = 0;
724 s->fgcol = 7;
725 s->bgcol = 0;
726 s->g_width = s->ds->width;
727 s->g_height = s->ds->height;
728 text_console_resize(s);
729
730 return chr;
731 }