console focus support
[qemu] / 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                 text_console_resize(s);
575             console_refresh(s);
576         }
577     }
578 }
579
580 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
581 {
582     TextConsole *s = chr->opaque;
583     int i;
584
585     console_show_cursor(s, 0);
586     for(i = 0; i < len; i++) {
587         console_putchar(s, buf[i]);
588     }
589     console_show_cursor(s, 1);
590     return len;
591 }
592
593 static void console_chr_add_read_handler(CharDriverState *chr, 
594                                          IOCanRWHandler *fd_can_read, 
595                                          IOReadHandler *fd_read, void *opaque)
596 {
597     TextConsole *s = chr->opaque;
598     s->fd_read = fd_read;
599     s->fd_opaque = opaque;
600 }
601
602 static void console_send_event(CharDriverState *chr, int event)
603 {
604     TextConsole *s = chr->opaque;
605     int i;
606
607     if (event == CHR_EVENT_FOCUS) {
608         for(i = 0; i < nb_consoles; i++) {
609             if (consoles[i] == s) {
610                 console_select(i);
611                 break;
612             }
613         }
614     }
615 }
616
617 /* called when an ascii key is pressed */
618 void kbd_put_keysym(int keysym)
619 {
620     TextConsole *s;
621     uint8_t buf[16], *q;
622     int c;
623
624     s = active_console;
625     if (!s || !s->text_console)
626         return;
627
628     switch(keysym) {
629     case QEMU_KEY_CTRL_UP:
630         console_scroll(-1);
631         break;
632     case QEMU_KEY_CTRL_DOWN:
633         console_scroll(1);
634         break;
635     case QEMU_KEY_CTRL_PAGEUP:
636         console_scroll(-10);
637         break;
638     case QEMU_KEY_CTRL_PAGEDOWN:
639         console_scroll(10);
640         break;
641     default:
642         if (s->fd_read) {
643             /* convert the QEMU keysym to VT100 key string */
644             q = buf;
645             if (keysym >= 0xe100 && keysym <= 0xe11f) {
646                 *q++ = '\033';
647                 *q++ = '[';
648                 c = keysym - 0xe100;
649                 if (c >= 10)
650                     *q++ = '0' + (c / 10);
651                 *q++ = '0' + (c % 10);
652                 *q++ = '~';
653             } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
654                 *q++ = '\033';
655                 *q++ = '[';
656                 *q++ = keysym & 0xff;
657             } else {
658                 *q++ = keysym;
659             }
660             s->fd_read(s->fd_opaque, buf, q - buf);
661         }
662         break;
663     }
664 }
665
666 TextConsole *graphic_console_init(DisplayState *ds)
667 {
668     TextConsole *s;
669
670     if (nb_consoles >= MAX_CONSOLES)
671         return NULL;
672     s = qemu_mallocz(sizeof(TextConsole));
673     if (!s) {
674         return NULL;
675     }
676     if (!active_console)
677         active_console = s;
678     s->ds = ds;
679     consoles[nb_consoles++] = s;
680     return s;
681 }
682
683 int is_active_console(TextConsole *s)
684 {
685     return s == active_console;
686 }
687
688 CharDriverState *text_console_init(DisplayState *ds)
689 {
690     CharDriverState *chr;
691     TextConsole *s;
692     int i;
693     static int color_inited;
694     
695     chr = qemu_mallocz(sizeof(CharDriverState));
696     if (!chr)
697         return NULL;
698     s = graphic_console_init(ds);
699     if (!s) {
700         free(chr);
701         return NULL;
702     }
703     s->text_console = 1;
704     chr->opaque = s;
705     chr->chr_write = console_puts;
706     chr->chr_add_read_handler = console_chr_add_read_handler;
707     chr->chr_send_event = console_send_event;
708
709     if (!color_inited) {
710         color_inited = 1;
711         for(i = 0; i < 8; i++) {
712             color_table[i] = col_expand(s->ds, 
713                                         vga_get_color(s->ds, color_table_rgb[i]));
714         }
715     }
716     s->y_displayed = 0;
717     s->y_base = 0;
718     s->total_height = DEFAULT_BACKSCROLL;
719     s->x = 0;
720     s->y = 0;
721     s->fgcol = 7;
722     s->bgcol = 0;
723     s->g_width = s->ds->width;
724     s->g_height = s->ds->height;
725     text_console_resize(s);
726
727     return chr;
728 }