Statistics
| Branch: | Revision:

root / ui / console.c @ 2c62f08d

History | View | Annotate | Download (48.5 kB)

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 "qemu-common.h"
25
#include "ui/console.h"
26
#include "qemu/timer.h"
27
#include "qmp-commands.h"
28
#include "sysemu/char.h"
29

    
30
//#define DEBUG_CONSOLE
31
#define DEFAULT_BACKSCROLL 512
32
#define MAX_CONSOLES 12
33
#define CONSOLE_CURSOR_PERIOD 500
34

    
35
typedef struct TextAttributes {
36
    uint8_t fgcol:4;
37
    uint8_t bgcol:4;
38
    uint8_t bold:1;
39
    uint8_t uline:1;
40
    uint8_t blink:1;
41
    uint8_t invers:1;
42
    uint8_t unvisible:1;
43
} TextAttributes;
44

    
45
typedef struct TextCell {
46
    uint8_t ch;
47
    TextAttributes t_attrib;
48
} TextCell;
49

    
50
#define MAX_ESC_PARAMS 3
51

    
52
enum TTYState {
53
    TTY_STATE_NORM,
54
    TTY_STATE_ESC,
55
    TTY_STATE_CSI,
56
};
57

    
58
typedef struct QEMUFIFO {
59
    uint8_t *buf;
60
    int buf_size;
61
    int count, wptr, rptr;
62
} QEMUFIFO;
63

    
64
static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
65
{
66
    int l, len;
67

    
68
    l = f->buf_size - f->count;
69
    if (len1 > l)
70
        len1 = l;
71
    len = len1;
72
    while (len > 0) {
73
        l = f->buf_size - f->wptr;
74
        if (l > len)
75
            l = len;
76
        memcpy(f->buf + f->wptr, buf, l);
77
        f->wptr += l;
78
        if (f->wptr >= f->buf_size)
79
            f->wptr = 0;
80
        buf += l;
81
        len -= l;
82
    }
83
    f->count += len1;
84
    return len1;
85
}
86

    
87
static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
88
{
89
    int l, len;
90

    
91
    if (len1 > f->count)
92
        len1 = f->count;
93
    len = len1;
94
    while (len > 0) {
95
        l = f->buf_size - f->rptr;
96
        if (l > len)
97
            l = len;
98
        memcpy(buf, f->buf + f->rptr, l);
99
        f->rptr += l;
100
        if (f->rptr >= f->buf_size)
101
            f->rptr = 0;
102
        buf += l;
103
        len -= l;
104
    }
105
    f->count -= len1;
106
    return len1;
107
}
108

    
109
typedef enum {
110
    GRAPHIC_CONSOLE,
111
    TEXT_CONSOLE,
112
    TEXT_CONSOLE_FIXED_SIZE
113
} console_type_t;
114

    
115
struct QemuConsole {
116
    int index;
117
    console_type_t console_type;
118
    DisplayState *ds;
119
    DisplaySurface *surface;
120

    
121
    /* Graphic console state.  */
122
    graphic_hw_update_ptr hw_update;
123
    graphic_hw_invalidate_ptr hw_invalidate;
124
    graphic_hw_text_update_ptr hw_text_update;
125
    void *hw;
126
    int g_width, g_height;
127

    
128
    /* Text console state */
129
    int width;
130
    int height;
131
    int total_height;
132
    int backscroll_height;
133
    int x, y;
134
    int x_saved, y_saved;
135
    int y_displayed;
136
    int y_base;
137
    TextAttributes t_attrib_default; /* default text attributes */
138
    TextAttributes t_attrib; /* currently active text attributes */
139
    TextCell *cells;
140
    int text_x[2], text_y[2], cursor_invalidate;
141
    int echo;
142
    bool cursor_visible_phase;
143
    QEMUTimer *cursor_timer;
144

    
145
    int update_x0;
146
    int update_y0;
147
    int update_x1;
148
    int update_y1;
149

    
150
    enum TTYState state;
151
    int esc_params[MAX_ESC_PARAMS];
152
    int nb_esc_params;
153

    
154
    CharDriverState *chr;
155
    /* fifo for key pressed */
156
    QEMUFIFO out_fifo;
157
    uint8_t out_fifo_buf[16];
158
    QEMUTimer *kbd_timer;
159
};
160

    
161
static DisplayState *display_state;
162
static QemuConsole *active_console;
163
static QemuConsole *consoles[MAX_CONSOLES];
164
static int nb_consoles = 0;
165

    
166
static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
167
static void dpy_gfx_switch_surface(DisplayState *ds,
168
                                   DisplaySurface *surface);
169

    
170
void graphic_hw_update(QemuConsole *con)
171
{
172
    if (!con) {
173
        con = active_console;
174
    }
175
    if (con && con->hw_update) {
176
        con->hw_update(con->hw);
177
    }
178
}
179

    
180
void graphic_hw_invalidate(QemuConsole *con)
181
{
182
    if (!con) {
183
        con = active_console;
184
    }
185
    if (con && con->hw_invalidate) {
186
        con->hw_invalidate(con->hw);
187
    }
188
}
189

    
190
static void ppm_save(const char *filename, struct DisplaySurface *ds,
191
                     Error **errp)
192
{
193
    int width = pixman_image_get_width(ds->image);
194
    int height = pixman_image_get_height(ds->image);
195
    FILE *f;
196
    int y;
197
    int ret;
198
    pixman_image_t *linebuf;
199

    
200
    trace_ppm_save(filename, ds);
201
    f = fopen(filename, "wb");
202
    if (!f) {
203
        error_setg(errp, "failed to open file '%s': %s", filename,
204
                   strerror(errno));
205
        return;
206
    }
207
    ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
208
    if (ret < 0) {
209
        linebuf = NULL;
210
        goto write_err;
211
    }
212
    linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
213
    for (y = 0; y < height; y++) {
214
        qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
215
        clearerr(f);
216
        ret = fwrite(pixman_image_get_data(linebuf), 1,
217
                     pixman_image_get_stride(linebuf), f);
218
        (void)ret;
219
        if (ferror(f)) {
220
            goto write_err;
221
        }
222
    }
223

    
224
out:
225
    qemu_pixman_image_unref(linebuf);
226
    fclose(f);
227
    return;
228

    
229
write_err:
230
    error_setg(errp, "failed to write to file '%s': %s", filename,
231
               strerror(errno));
232
    unlink(filename);
233
    goto out;
234
}
235

    
236
void qmp_screendump(const char *filename, Error **errp)
237
{
238
    QemuConsole *con = consoles[0];
239
    DisplaySurface *surface;
240

    
241
    if (con == NULL) {
242
        error_setg(errp, "There is no QemuConsole I can screendump from.");
243
        return;
244
    }
245

    
246
    graphic_hw_update(con);
247
    surface = qemu_console_surface(con);
248
    ppm_save(filename, surface, errp);
249
}
250

    
251
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
252
{
253
    if (!con) {
254
        con = active_console;
255
    }
256
    if (con && con->hw_text_update)
257
        con->hw_text_update(con->hw, chardata);
258
}
259

    
260
static void vga_fill_rect(QemuConsole *con,
261
                          int posx, int posy, int width, int height,
262
                          pixman_color_t color)
263
{
264
    DisplaySurface *surface = qemu_console_surface(con);
265
    pixman_rectangle16_t rect = {
266
        .x = posx, .y = posy, .width = width, .height = height
267
    };
268

    
269
    pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
270
                                 &color, 1, &rect);
271
}
272

    
273
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
274
static void vga_bitblt(QemuConsole *con,
275
                       int xs, int ys, int xd, int yd, int w, int h)
276
{
277
    DisplaySurface *surface = qemu_console_surface(con);
278

    
279
    pixman_image_composite(PIXMAN_OP_SRC,
280
                           surface->image, NULL, surface->image,
281
                           xs, ys, 0, 0, xd, yd, w, h);
282
}
283

    
284
/***********************************************************/
285
/* basic char display */
286

    
287
#define FONT_HEIGHT 16
288
#define FONT_WIDTH 8
289

    
290
#include "vgafont.h"
291

    
292
#ifndef CONFIG_CURSES
293
enum color_names {
294
    COLOR_BLACK   = 0,
295
    COLOR_RED     = 1,
296
    COLOR_GREEN   = 2,
297
    COLOR_YELLOW  = 3,
298
    COLOR_BLUE    = 4,
299
    COLOR_MAGENTA = 5,
300
    COLOR_CYAN    = 6,
301
    COLOR_WHITE   = 7
302
};
303
#endif
304

    
305
#define QEMU_RGB(r, g, b)                                               \
306
    { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
307

    
308
static const pixman_color_t color_table_rgb[2][8] = {
309
    {   /* dark */
310
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
311
        QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
312
        QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
313
        QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
314
        QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
315
        QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
316
        QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
317
        QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
318
    },
319
    {   /* bright */
320
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
321
        QEMU_RGB(0xff, 0x00, 0x00),  /* red */
322
        QEMU_RGB(0x00, 0xff, 0x00),  /* green */
323
        QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
324
        QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
325
        QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
326
        QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
327
        QEMU_RGB(0xff, 0xff, 0xff),  /* white */
328
    }
329
};
330

    
331
#ifdef DEBUG_CONSOLE
332
static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
333
{
334
    if (t_attrib->bold) {
335
        printf("b");
336
    } else {
337
        printf(" ");
338
    }
339
    if (t_attrib->uline) {
340
        printf("u");
341
    } else {
342
        printf(" ");
343
    }
344
    if (t_attrib->blink) {
345
        printf("l");
346
    } else {
347
        printf(" ");
348
    }
349
    if (t_attrib->invers) {
350
        printf("i");
351
    } else {
352
        printf(" ");
353
    }
354
    if (t_attrib->unvisible) {
355
        printf("n");
356
    } else {
357
        printf(" ");
358
    }
359

    
360
    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
361
}
362
#endif
363

    
364
static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
365
                          TextAttributes *t_attrib)
366
{
367
    static pixman_image_t *glyphs[256];
368
    DisplaySurface *surface = qemu_console_surface(s);
369
    pixman_color_t fgcol, bgcol;
370

    
371
    if (t_attrib->invers) {
372
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
373
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
374
    } else {
375
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
376
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
377
    }
378

    
379
    if (!glyphs[ch]) {
380
        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
381
    }
382
    qemu_pixman_glyph_render(glyphs[ch], surface->image,
383
                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
384
}
385

    
386
static void text_console_resize(QemuConsole *s)
387
{
388
    TextCell *cells, *c, *c1;
389
    int w1, x, y, last_width;
390

    
391
    last_width = s->width;
392
    s->width = s->g_width / FONT_WIDTH;
393
    s->height = s->g_height / FONT_HEIGHT;
394

    
395
    w1 = last_width;
396
    if (s->width < w1)
397
        w1 = s->width;
398

    
399
    cells = g_malloc(s->width * s->total_height * sizeof(TextCell));
400
    for(y = 0; y < s->total_height; y++) {
401
        c = &cells[y * s->width];
402
        if (w1 > 0) {
403
            c1 = &s->cells[y * last_width];
404
            for(x = 0; x < w1; x++) {
405
                *c++ = *c1++;
406
            }
407
        }
408
        for(x = w1; x < s->width; x++) {
409
            c->ch = ' ';
410
            c->t_attrib = s->t_attrib_default;
411
            c++;
412
        }
413
    }
414
    g_free(s->cells);
415
    s->cells = cells;
416
}
417

    
418
static inline void text_update_xy(QemuConsole *s, int x, int y)
419
{
420
    s->text_x[0] = MIN(s->text_x[0], x);
421
    s->text_x[1] = MAX(s->text_x[1], x);
422
    s->text_y[0] = MIN(s->text_y[0], y);
423
    s->text_y[1] = MAX(s->text_y[1], y);
424
}
425

    
426
static void invalidate_xy(QemuConsole *s, int x, int y)
427
{
428
    if (s->update_x0 > x * FONT_WIDTH)
429
        s->update_x0 = x * FONT_WIDTH;
430
    if (s->update_y0 > y * FONT_HEIGHT)
431
        s->update_y0 = y * FONT_HEIGHT;
432
    if (s->update_x1 < (x + 1) * FONT_WIDTH)
433
        s->update_x1 = (x + 1) * FONT_WIDTH;
434
    if (s->update_y1 < (y + 1) * FONT_HEIGHT)
435
        s->update_y1 = (y + 1) * FONT_HEIGHT;
436
}
437

    
438
static void update_xy(QemuConsole *s, int x, int y)
439
{
440
    TextCell *c;
441
    int y1, y2;
442

    
443
    if (s != active_console) {
444
        return;
445
    }
446

    
447
    if (s->ds->have_text) {
448
        text_update_xy(s, x, y);
449
    }
450

    
451
    if (s->ds->have_gfx) {
452
        y1 = (s->y_base + y) % s->total_height;
453
        y2 = y1 - s->y_displayed;
454
        if (y2 < 0)
455
            y2 += s->total_height;
456
        if (y2 < s->height) {
457
            c = &s->cells[y1 * s->width + x];
458
            vga_putcharxy(s, x, y2, c->ch,
459
                          &(c->t_attrib));
460
            invalidate_xy(s, x, y2);
461
        }
462
    }
463
}
464

    
465
static void console_show_cursor(QemuConsole *s, int show)
466
{
467
    TextCell *c;
468
    int y, y1;
469
    int x = s->x;
470

    
471
    if (s != active_console) {
472
        return;
473
    }
474

    
475
    if (s->ds->have_text) {
476
        s->cursor_invalidate = 1;
477
    }
478

    
479
    if (s->ds->have_gfx) {
480
        if (x >= s->width) {
481
            x = s->width - 1;
482
        }
483
        y1 = (s->y_base + s->y) % s->total_height;
484
        y = y1 - s->y_displayed;
485
        if (y < 0)
486
            y += s->total_height;
487
        if (y < s->height) {
488
            c = &s->cells[y1 * s->width + x];
489
            if (show && s->cursor_visible_phase) {
490
                TextAttributes t_attrib = s->t_attrib_default;
491
                t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
492
                vga_putcharxy(s, x, y, c->ch, &t_attrib);
493
            } else {
494
                vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
495
            }
496
            invalidate_xy(s, x, y);
497
        }
498
    }
499
}
500

    
501
static void console_refresh(QemuConsole *s)
502
{
503
    DisplaySurface *surface = qemu_console_surface(s);
504
    TextCell *c;
505
    int x, y, y1;
506

    
507
    if (s != active_console)
508
        return;
509

    
510
    if (s->ds->have_text) {
511
        s->text_x[0] = 0;
512
        s->text_y[0] = 0;
513
        s->text_x[1] = s->width - 1;
514
        s->text_y[1] = s->height - 1;
515
        s->cursor_invalidate = 1;
516
    }
517

    
518
    if (s->ds->have_gfx) {
519
        vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
520
                      color_table_rgb[0][COLOR_BLACK]);
521
        y1 = s->y_displayed;
522
        for (y = 0; y < s->height; y++) {
523
            c = s->cells + y1 * s->width;
524
            for (x = 0; x < s->width; x++) {
525
                vga_putcharxy(s, x, y, c->ch,
526
                              &(c->t_attrib));
527
                c++;
528
            }
529
            if (++y1 == s->total_height) {
530
                y1 = 0;
531
            }
532
        }
533
        console_show_cursor(s, 1);
534
        dpy_gfx_update(s, 0, 0,
535
                       surface_width(surface), surface_height(surface));
536
    }
537
}
538

    
539
static void console_scroll(int ydelta)
540
{
541
    QemuConsole *s;
542
    int i, y1;
543

    
544
    s = active_console;
545
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
546
        return;
547

    
548
    if (ydelta > 0) {
549
        for(i = 0; i < ydelta; i++) {
550
            if (s->y_displayed == s->y_base)
551
                break;
552
            if (++s->y_displayed == s->total_height)
553
                s->y_displayed = 0;
554
        }
555
    } else {
556
        ydelta = -ydelta;
557
        i = s->backscroll_height;
558
        if (i > s->total_height - s->height)
559
            i = s->total_height - s->height;
560
        y1 = s->y_base - i;
561
        if (y1 < 0)
562
            y1 += s->total_height;
563
        for(i = 0; i < ydelta; i++) {
564
            if (s->y_displayed == y1)
565
                break;
566
            if (--s->y_displayed < 0)
567
                s->y_displayed = s->total_height - 1;
568
        }
569
    }
570
    console_refresh(s);
571
}
572

    
573
static void console_put_lf(QemuConsole *s)
574
{
575
    TextCell *c;
576
    int x, y1;
577

    
578
    s->y++;
579
    if (s->y >= s->height) {
580
        s->y = s->height - 1;
581

    
582
        if (s->y_displayed == s->y_base) {
583
            if (++s->y_displayed == s->total_height)
584
                s->y_displayed = 0;
585
        }
586
        if (++s->y_base == s->total_height)
587
            s->y_base = 0;
588
        if (s->backscroll_height < s->total_height)
589
            s->backscroll_height++;
590
        y1 = (s->y_base + s->height - 1) % s->total_height;
591
        c = &s->cells[y1 * s->width];
592
        for(x = 0; x < s->width; x++) {
593
            c->ch = ' ';
594
            c->t_attrib = s->t_attrib_default;
595
            c++;
596
        }
597
        if (s == active_console && s->y_displayed == s->y_base) {
598
            if (s->ds->have_text) {
599
                s->text_x[0] = 0;
600
                s->text_y[0] = 0;
601
                s->text_x[1] = s->width - 1;
602
                s->text_y[1] = s->height - 1;
603
            }
604

    
605
            if (s->ds->have_gfx) {
606
                vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
607
                           s->width * FONT_WIDTH,
608
                           (s->height - 1) * FONT_HEIGHT);
609
                vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
610
                              s->width * FONT_WIDTH, FONT_HEIGHT,
611
                              color_table_rgb[0][s->t_attrib_default.bgcol]);
612
                s->update_x0 = 0;
613
                s->update_y0 = 0;
614
                s->update_x1 = s->width * FONT_WIDTH;
615
                s->update_y1 = s->height * FONT_HEIGHT;
616
            }
617
        }
618
    }
619
}
620

    
621
/* Set console attributes depending on the current escape codes.
622
 * NOTE: I know this code is not very efficient (checking every color for it
623
 * self) but it is more readable and better maintainable.
624
 */
625
static void console_handle_escape(QemuConsole *s)
626
{
627
    int i;
628

    
629
    for (i=0; i<s->nb_esc_params; i++) {
630
        switch (s->esc_params[i]) {
631
            case 0: /* reset all console attributes to default */
632
                s->t_attrib = s->t_attrib_default;
633
                break;
634
            case 1:
635
                s->t_attrib.bold = 1;
636
                break;
637
            case 4:
638
                s->t_attrib.uline = 1;
639
                break;
640
            case 5:
641
                s->t_attrib.blink = 1;
642
                break;
643
            case 7:
644
                s->t_attrib.invers = 1;
645
                break;
646
            case 8:
647
                s->t_attrib.unvisible = 1;
648
                break;
649
            case 22:
650
                s->t_attrib.bold = 0;
651
                break;
652
            case 24:
653
                s->t_attrib.uline = 0;
654
                break;
655
            case 25:
656
                s->t_attrib.blink = 0;
657
                break;
658
            case 27:
659
                s->t_attrib.invers = 0;
660
                break;
661
            case 28:
662
                s->t_attrib.unvisible = 0;
663
                break;
664
            /* set foreground color */
665
            case 30:
666
                s->t_attrib.fgcol=COLOR_BLACK;
667
                break;
668
            case 31:
669
                s->t_attrib.fgcol=COLOR_RED;
670
                break;
671
            case 32:
672
                s->t_attrib.fgcol=COLOR_GREEN;
673
                break;
674
            case 33:
675
                s->t_attrib.fgcol=COLOR_YELLOW;
676
                break;
677
            case 34:
678
                s->t_attrib.fgcol=COLOR_BLUE;
679
                break;
680
            case 35:
681
                s->t_attrib.fgcol=COLOR_MAGENTA;
682
                break;
683
            case 36:
684
                s->t_attrib.fgcol=COLOR_CYAN;
685
                break;
686
            case 37:
687
                s->t_attrib.fgcol=COLOR_WHITE;
688
                break;
689
            /* set background color */
690
            case 40:
691
                s->t_attrib.bgcol=COLOR_BLACK;
692
                break;
693
            case 41:
694
                s->t_attrib.bgcol=COLOR_RED;
695
                break;
696
            case 42:
697
                s->t_attrib.bgcol=COLOR_GREEN;
698
                break;
699
            case 43:
700
                s->t_attrib.bgcol=COLOR_YELLOW;
701
                break;
702
            case 44:
703
                s->t_attrib.bgcol=COLOR_BLUE;
704
                break;
705
            case 45:
706
                s->t_attrib.bgcol=COLOR_MAGENTA;
707
                break;
708
            case 46:
709
                s->t_attrib.bgcol=COLOR_CYAN;
710
                break;
711
            case 47:
712
                s->t_attrib.bgcol=COLOR_WHITE;
713
                break;
714
        }
715
    }
716
}
717

    
718
static void console_clear_xy(QemuConsole *s, int x, int y)
719
{
720
    int y1 = (s->y_base + y) % s->total_height;
721
    TextCell *c = &s->cells[y1 * s->width + x];
722
    c->ch = ' ';
723
    c->t_attrib = s->t_attrib_default;
724
    update_xy(s, x, y);
725
}
726

    
727
/* set cursor, checking bounds */
728
static void set_cursor(QemuConsole *s, int x, int y)
729
{
730
    if (x < 0) {
731
        x = 0;
732
    }
733
    if (y < 0) {
734
        y = 0;
735
    }
736
    if (y >= s->height) {
737
        y = s->height - 1;
738
    }
739
    if (x >= s->width) {
740
        x = s->width - 1;
741
    }
742

    
743
    s->x = x;
744
    s->y = y;
745
}
746

    
747
static void console_putchar(QemuConsole *s, int ch)
748
{
749
    TextCell *c;
750
    int y1, i;
751
    int x, y;
752

    
753
    switch(s->state) {
754
    case TTY_STATE_NORM:
755
        switch(ch) {
756
        case '\r':  /* carriage return */
757
            s->x = 0;
758
            break;
759
        case '\n':  /* newline */
760
            console_put_lf(s);
761
            break;
762
        case '\b':  /* backspace */
763
            if (s->x > 0)
764
                s->x--;
765
            break;
766
        case '\t':  /* tabspace */
767
            if (s->x + (8 - (s->x % 8)) > s->width) {
768
                s->x = 0;
769
                console_put_lf(s);
770
            } else {
771
                s->x = s->x + (8 - (s->x % 8));
772
            }
773
            break;
774
        case '\a':  /* alert aka. bell */
775
            /* TODO: has to be implemented */
776
            break;
777
        case 14:
778
            /* SI (shift in), character set 0 (ignored) */
779
            break;
780
        case 15:
781
            /* SO (shift out), character set 1 (ignored) */
782
            break;
783
        case 27:    /* esc (introducing an escape sequence) */
784
            s->state = TTY_STATE_ESC;
785
            break;
786
        default:
787
            if (s->x >= s->width) {
788
                /* line wrap */
789
                s->x = 0;
790
                console_put_lf(s);
791
            }
792
            y1 = (s->y_base + s->y) % s->total_height;
793
            c = &s->cells[y1 * s->width + s->x];
794
            c->ch = ch;
795
            c->t_attrib = s->t_attrib;
796
            update_xy(s, s->x, s->y);
797
            s->x++;
798
            break;
799
        }
800
        break;
801
    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
802
        if (ch == '[') {
803
            for(i=0;i<MAX_ESC_PARAMS;i++)
804
                s->esc_params[i] = 0;
805
            s->nb_esc_params = 0;
806
            s->state = TTY_STATE_CSI;
807
        } else {
808
            s->state = TTY_STATE_NORM;
809
        }
810
        break;
811
    case TTY_STATE_CSI: /* handle escape sequence parameters */
812
        if (ch >= '0' && ch <= '9') {
813
            if (s->nb_esc_params < MAX_ESC_PARAMS) {
814
                int *param = &s->esc_params[s->nb_esc_params];
815
                int digit = (ch - '0');
816

    
817
                *param = (*param <= (INT_MAX - digit) / 10) ?
818
                         *param * 10 + digit : INT_MAX;
819
            }
820
        } else {
821
            if (s->nb_esc_params < MAX_ESC_PARAMS)
822
                s->nb_esc_params++;
823
            if (ch == ';')
824
                break;
825
#ifdef DEBUG_CONSOLE
826
            fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
827
                    s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
828
#endif
829
            s->state = TTY_STATE_NORM;
830
            switch(ch) {
831
            case 'A':
832
                /* move cursor up */
833
                if (s->esc_params[0] == 0) {
834
                    s->esc_params[0] = 1;
835
                }
836
                set_cursor(s, s->x, s->y - s->esc_params[0]);
837
                break;
838
            case 'B':
839
                /* move cursor down */
840
                if (s->esc_params[0] == 0) {
841
                    s->esc_params[0] = 1;
842
                }
843
                set_cursor(s, s->x, s->y + s->esc_params[0]);
844
                break;
845
            case 'C':
846
                /* move cursor right */
847
                if (s->esc_params[0] == 0) {
848
                    s->esc_params[0] = 1;
849
                }
850
                set_cursor(s, s->x + s->esc_params[0], s->y);
851
                break;
852
            case 'D':
853
                /* move cursor left */
854
                if (s->esc_params[0] == 0) {
855
                    s->esc_params[0] = 1;
856
                }
857
                set_cursor(s, s->x - s->esc_params[0], s->y);
858
                break;
859
            case 'G':
860
                /* move cursor to column */
861
                set_cursor(s, s->esc_params[0] - 1, s->y);
862
                break;
863
            case 'f':
864
            case 'H':
865
                /* move cursor to row, column */
866
                set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
867
                break;
868
            case 'J':
869
                switch (s->esc_params[0]) {
870
                case 0:
871
                    /* clear to end of screen */
872
                    for (y = s->y; y < s->height; y++) {
873
                        for (x = 0; x < s->width; x++) {
874
                            if (y == s->y && x < s->x) {
875
                                continue;
876
                            }
877
                            console_clear_xy(s, x, y);
878
                        }
879
                    }
880
                    break;
881
                case 1:
882
                    /* clear from beginning of screen */
883
                    for (y = 0; y <= s->y; y++) {
884
                        for (x = 0; x < s->width; x++) {
885
                            if (y == s->y && x > s->x) {
886
                                break;
887
                            }
888
                            console_clear_xy(s, x, y);
889
                        }
890
                    }
891
                    break;
892
                case 2:
893
                    /* clear entire screen */
894
                    for (y = 0; y <= s->height; y++) {
895
                        for (x = 0; x < s->width; x++) {
896
                            console_clear_xy(s, x, y);
897
                        }
898
                    }
899
                    break;
900
                }
901
                break;
902
            case 'K':
903
                switch (s->esc_params[0]) {
904
                case 0:
905
                    /* clear to eol */
906
                    for(x = s->x; x < s->width; x++) {
907
                        console_clear_xy(s, x, s->y);
908
                    }
909
                    break;
910
                case 1:
911
                    /* clear from beginning of line */
912
                    for (x = 0; x <= s->x; x++) {
913
                        console_clear_xy(s, x, s->y);
914
                    }
915
                    break;
916
                case 2:
917
                    /* clear entire line */
918
                    for(x = 0; x < s->width; x++) {
919
                        console_clear_xy(s, x, s->y);
920
                    }
921
                    break;
922
                }
923
                break;
924
            case 'm':
925
                console_handle_escape(s);
926
                break;
927
            case 'n':
928
                /* report cursor position */
929
                /* TODO: send ESC[row;colR */
930
                break;
931
            case 's':
932
                /* save cursor position */
933
                s->x_saved = s->x;
934
                s->y_saved = s->y;
935
                break;
936
            case 'u':
937
                /* restore cursor position */
938
                s->x = s->x_saved;
939
                s->y = s->y_saved;
940
                break;
941
            default:
942
#ifdef DEBUG_CONSOLE
943
                fprintf(stderr, "unhandled escape character '%c'\n", ch);
944
#endif
945
                break;
946
            }
947
            break;
948
        }
949
    }
950
}
951

    
952
void console_select(unsigned int index)
953
{
954
    DisplaySurface *surface;
955
    QemuConsole *s;
956

    
957
    if (index >= MAX_CONSOLES)
958
        return;
959

    
960
    trace_console_select(index);
961
    if (active_console) {
962
        surface = qemu_console_surface(active_console);
963
        active_console->g_width = surface_width(surface);
964
        active_console->g_height = surface_height(surface);
965
    }
966
    s = consoles[index];
967
    if (s) {
968
        DisplayState *ds = s->ds;
969

    
970
        if (active_console && active_console->cursor_timer) {
971
            qemu_del_timer(active_console->cursor_timer);
972
        }
973
        active_console = s;
974
        if (ds->have_gfx) {
975
            dpy_gfx_switch_surface(ds, s->surface);
976
            dpy_gfx_update(s, 0, 0, surface_width(s->surface),
977
                           surface_height(s->surface));
978
        }
979
        if (ds->have_text) {
980
            dpy_text_resize(s, s->width, s->height);
981
        }
982
        if (s->cursor_timer) {
983
            qemu_mod_timer(s->cursor_timer,
984
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
985
        }
986
    }
987
}
988

    
989
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
990
{
991
    QemuConsole *s = chr->opaque;
992
    int i;
993

    
994
    s->update_x0 = s->width * FONT_WIDTH;
995
    s->update_y0 = s->height * FONT_HEIGHT;
996
    s->update_x1 = 0;
997
    s->update_y1 = 0;
998
    console_show_cursor(s, 0);
999
    for(i = 0; i < len; i++) {
1000
        console_putchar(s, buf[i]);
1001
    }
1002
    console_show_cursor(s, 1);
1003
    if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
1004
        dpy_gfx_update(s, s->update_x0, s->update_y0,
1005
                       s->update_x1 - s->update_x0,
1006
                       s->update_y1 - s->update_y0);
1007
    }
1008
    return len;
1009
}
1010

    
1011
static void kbd_send_chars(void *opaque)
1012
{
1013
    QemuConsole *s = opaque;
1014
    int len;
1015
    uint8_t buf[16];
1016

    
1017
    len = qemu_chr_be_can_write(s->chr);
1018
    if (len > s->out_fifo.count)
1019
        len = s->out_fifo.count;
1020
    if (len > 0) {
1021
        if (len > sizeof(buf))
1022
            len = sizeof(buf);
1023
        qemu_fifo_read(&s->out_fifo, buf, len);
1024
        qemu_chr_be_write(s->chr, buf, len);
1025
    }
1026
    /* characters are pending: we send them a bit later (XXX:
1027
       horrible, should change char device API) */
1028
    if (s->out_fifo.count > 0) {
1029
        qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
1030
    }
1031
}
1032

    
1033
/* called when an ascii key is pressed */
1034
void kbd_put_keysym(int keysym)
1035
{
1036
    QemuConsole *s;
1037
    uint8_t buf[16], *q;
1038
    int c;
1039

    
1040
    s = active_console;
1041
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
1042
        return;
1043

    
1044
    switch(keysym) {
1045
    case QEMU_KEY_CTRL_UP:
1046
        console_scroll(-1);
1047
        break;
1048
    case QEMU_KEY_CTRL_DOWN:
1049
        console_scroll(1);
1050
        break;
1051
    case QEMU_KEY_CTRL_PAGEUP:
1052
        console_scroll(-10);
1053
        break;
1054
    case QEMU_KEY_CTRL_PAGEDOWN:
1055
        console_scroll(10);
1056
        break;
1057
    default:
1058
        /* convert the QEMU keysym to VT100 key string */
1059
        q = buf;
1060
        if (keysym >= 0xe100 && keysym <= 0xe11f) {
1061
            *q++ = '\033';
1062
            *q++ = '[';
1063
            c = keysym - 0xe100;
1064
            if (c >= 10)
1065
                *q++ = '0' + (c / 10);
1066
            *q++ = '0' + (c % 10);
1067
            *q++ = '~';
1068
        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1069
            *q++ = '\033';
1070
            *q++ = '[';
1071
            *q++ = keysym & 0xff;
1072
        } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1073
            console_puts(s->chr, (const uint8_t *) "\r", 1);
1074
            *q++ = '\n';
1075
        } else {
1076
            *q++ = keysym;
1077
        }
1078
        if (s->echo) {
1079
            console_puts(s->chr, buf, q - buf);
1080
        }
1081
        if (s->chr->chr_read) {
1082
            qemu_fifo_write(&s->out_fifo, buf, q - buf);
1083
            kbd_send_chars(s);
1084
        }
1085
        break;
1086
    }
1087
}
1088

    
1089
static void text_console_invalidate(void *opaque)
1090
{
1091
    QemuConsole *s = (QemuConsole *) opaque;
1092
    DisplaySurface *surface = qemu_console_surface(s);
1093

    
1094
    if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1095
        s->g_width = surface_width(surface);
1096
        s->g_height = surface_height(surface);
1097
        text_console_resize(s);
1098
    }
1099
    console_refresh(s);
1100
}
1101

    
1102
static void text_console_update(void *opaque, console_ch_t *chardata)
1103
{
1104
    QemuConsole *s = (QemuConsole *) opaque;
1105
    int i, j, src;
1106

    
1107
    if (s->text_x[0] <= s->text_x[1]) {
1108
        src = (s->y_base + s->text_y[0]) * s->width;
1109
        chardata += s->text_y[0] * s->width;
1110
        for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1111
            for (j = 0; j < s->width; j ++, src ++)
1112
                console_write_ch(chardata ++, s->cells[src].ch |
1113
                                (s->cells[src].t_attrib.fgcol << 12) |
1114
                                (s->cells[src].t_attrib.bgcol << 8) |
1115
                                (s->cells[src].t_attrib.bold << 21));
1116
        dpy_text_update(s, s->text_x[0], s->text_y[0],
1117
                        s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1118
        s->text_x[0] = s->width;
1119
        s->text_y[0] = s->height;
1120
        s->text_x[1] = 0;
1121
        s->text_y[1] = 0;
1122
    }
1123
    if (s->cursor_invalidate) {
1124
        dpy_text_cursor(s, s->x, s->y);
1125
        s->cursor_invalidate = 0;
1126
    }
1127
}
1128

    
1129
static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
1130
{
1131
    QemuConsole *s;
1132
    int i;
1133

    
1134
    if (nb_consoles >= MAX_CONSOLES)
1135
        return NULL;
1136
    s = g_malloc0(sizeof(QemuConsole));
1137
    if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1138
        (console_type == GRAPHIC_CONSOLE))) {
1139
        active_console = s;
1140
    }
1141
    s->ds = ds;
1142
    s->console_type = console_type;
1143
    if (console_type != GRAPHIC_CONSOLE) {
1144
        s->index = nb_consoles;
1145
        consoles[nb_consoles++] = s;
1146
    } else {
1147
        /* HACK: Put graphical consoles before text consoles.  */
1148
        for (i = nb_consoles; i > 0; i--) {
1149
            if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1150
                break;
1151
            consoles[i] = consoles[i - 1];
1152
            consoles[i]->index = i;
1153
        }
1154
        s->index = i;
1155
        consoles[i] = s;
1156
        nb_consoles++;
1157
    }
1158
    return s;
1159
}
1160

    
1161
static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1162
                               int linesize, PixelFormat pf, int newflags)
1163
{
1164
    surface->pf = pf;
1165

    
1166
    qemu_pixman_image_unref(surface->image);
1167
    surface->image = NULL;
1168

    
1169
    surface->format = qemu_pixman_get_format(&pf);
1170
    assert(surface->format != 0);
1171
    surface->image = pixman_image_create_bits(surface->format,
1172
                                              width, height,
1173
                                              NULL, linesize);
1174
    assert(surface->image != NULL);
1175

    
1176
    surface->flags = newflags | QEMU_ALLOCATED_FLAG;
1177
#ifdef HOST_WORDS_BIGENDIAN
1178
    surface->flags |= QEMU_BIG_ENDIAN_FLAG;
1179
#endif
1180
}
1181

    
1182
DisplaySurface *qemu_create_displaysurface(int width, int height)
1183
{
1184
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1185
    int linesize = width * 4;
1186

    
1187
    trace_displaysurface_create(surface, width, height);
1188
    qemu_alloc_display(surface, width, height, linesize,
1189
                       qemu_default_pixelformat(32), 0);
1190
    return surface;
1191
}
1192

    
1193
DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
1194
                                                int linesize, uint8_t *data,
1195
                                                bool byteswap)
1196
{
1197
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1198

    
1199
    trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
1200
    if (byteswap) {
1201
        surface->pf = qemu_different_endianness_pixelformat(bpp);
1202
    } else {
1203
        surface->pf = qemu_default_pixelformat(bpp);
1204
    }
1205

    
1206
    surface->format = qemu_pixman_get_format(&surface->pf);
1207
    assert(surface->format != 0);
1208
    surface->image = pixman_image_create_bits(surface->format,
1209
                                              width, height,
1210
                                              (void *)data, linesize);
1211
    assert(surface->image != NULL);
1212

    
1213
#ifdef HOST_WORDS_BIGENDIAN
1214
    surface->flags = QEMU_BIG_ENDIAN_FLAG;
1215
#endif
1216

    
1217
    return surface;
1218
}
1219

    
1220
void qemu_free_displaysurface(DisplaySurface *surface)
1221
{
1222
    if (surface == NULL) {
1223
        return;
1224
    }
1225
    trace_displaysurface_free(surface);
1226
    qemu_pixman_image_unref(surface->image);
1227
    g_free(surface);
1228
}
1229

    
1230
void register_displaychangelistener(DisplayState *ds,
1231
                                    DisplayChangeListener *dcl)
1232
{
1233
    trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1234
    dcl->ds = ds;
1235
    QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
1236
    gui_setup_refresh(ds);
1237
    if (dcl->ops->dpy_gfx_switch && active_console) {
1238
        dcl->ops->dpy_gfx_switch(dcl, active_console->surface);
1239
    }
1240
}
1241

    
1242
void unregister_displaychangelistener(DisplayChangeListener *dcl)
1243
{
1244
    DisplayState *ds = dcl->ds;
1245
    trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1246
    QLIST_REMOVE(dcl, next);
1247
    gui_setup_refresh(ds);
1248
}
1249

    
1250
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1251
{
1252
    DisplayState *s = con->ds;
1253
    struct DisplayChangeListener *dcl;
1254
    int width = surface_width(con->surface);
1255
    int height = surface_height(con->surface);
1256

    
1257
    x = MAX(x, 0);
1258
    y = MAX(y, 0);
1259
    x = MIN(x, width);
1260
    y = MIN(y, height);
1261
    w = MIN(w, width - x);
1262
    h = MIN(h, height - y);
1263

    
1264
    if (con != active_console) {
1265
        return;
1266
    }
1267
    QLIST_FOREACH(dcl, &s->listeners, next) {
1268
        if (dcl->ops->dpy_gfx_update) {
1269
            dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1270
        }
1271
    }
1272
}
1273

    
1274
static void dpy_gfx_switch_surface(DisplayState *ds,
1275
                                   DisplaySurface *surface)
1276
{
1277
    struct DisplayChangeListener *dcl;
1278

    
1279
    QLIST_FOREACH(dcl, &ds->listeners, next) {
1280
        if (dcl->ops->dpy_gfx_switch) {
1281
            dcl->ops->dpy_gfx_switch(dcl, surface);
1282
        }
1283
    }
1284
}
1285

    
1286
void dpy_gfx_replace_surface(QemuConsole *con,
1287
                             DisplaySurface *surface)
1288
{
1289
    DisplayState *s = con->ds;
1290
    DisplaySurface *old_surface = con->surface;
1291

    
1292
    con->surface = surface;
1293
    if (con == active_console) {
1294
        dpy_gfx_switch_surface(s, surface);
1295
    }
1296
    qemu_free_displaysurface(old_surface);
1297
}
1298

    
1299
void dpy_refresh(DisplayState *s)
1300
{
1301
    struct DisplayChangeListener *dcl;
1302
    QLIST_FOREACH(dcl, &s->listeners, next) {
1303
        if (dcl->ops->dpy_refresh) {
1304
            dcl->ops->dpy_refresh(dcl);
1305
        }
1306
    }
1307
}
1308

    
1309
void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1310
                  int dst_x, int dst_y, int w, int h)
1311
{
1312
    DisplayState *s = con->ds;
1313
    struct DisplayChangeListener *dcl;
1314

    
1315
    if (con != active_console) {
1316
        return;
1317
    }
1318
    QLIST_FOREACH(dcl, &s->listeners, next) {
1319
        if (dcl->ops->dpy_gfx_copy) {
1320
            dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
1321
        } else { /* TODO */
1322
            dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
1323
        }
1324
    }
1325
}
1326

    
1327
void dpy_text_cursor(QemuConsole *con, int x, int y)
1328
{
1329
    DisplayState *s = con->ds;
1330
    struct DisplayChangeListener *dcl;
1331

    
1332
    if (con != active_console) {
1333
        return;
1334
    }
1335
    QLIST_FOREACH(dcl, &s->listeners, next) {
1336
        if (dcl->ops->dpy_text_cursor) {
1337
            dcl->ops->dpy_text_cursor(dcl, x, y);
1338
        }
1339
    }
1340
}
1341

    
1342
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1343
{
1344
    DisplayState *s = con->ds;
1345
    struct DisplayChangeListener *dcl;
1346

    
1347
    if (con != active_console) {
1348
        return;
1349
    }
1350
    QLIST_FOREACH(dcl, &s->listeners, next) {
1351
        if (dcl->ops->dpy_text_update) {
1352
            dcl->ops->dpy_text_update(dcl, x, y, w, h);
1353
        }
1354
    }
1355
}
1356

    
1357
void dpy_text_resize(QemuConsole *con, int w, int h)
1358
{
1359
    DisplayState *s = con->ds;
1360
    struct DisplayChangeListener *dcl;
1361

    
1362
    if (con != active_console) {
1363
        return;
1364
    }
1365
    QLIST_FOREACH(dcl, &s->listeners, next) {
1366
        if (dcl->ops->dpy_text_resize) {
1367
            dcl->ops->dpy_text_resize(dcl, w, h);
1368
        }
1369
    }
1370
}
1371

    
1372
void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1373
{
1374
    DisplayState *s = con->ds;
1375
    struct DisplayChangeListener *dcl;
1376

    
1377
    if (con != active_console) {
1378
        return;
1379
    }
1380
    QLIST_FOREACH(dcl, &s->listeners, next) {
1381
        if (dcl->ops->dpy_mouse_set) {
1382
            dcl->ops->dpy_mouse_set(dcl, x, y, on);
1383
        }
1384
    }
1385
}
1386

    
1387
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1388
{
1389
    DisplayState *s = con->ds;
1390
    struct DisplayChangeListener *dcl;
1391

    
1392
    if (con != active_console) {
1393
        return;
1394
    }
1395
    QLIST_FOREACH(dcl, &s->listeners, next) {
1396
        if (dcl->ops->dpy_cursor_define) {
1397
            dcl->ops->dpy_cursor_define(dcl, cursor);
1398
        }
1399
    }
1400
}
1401

    
1402
bool dpy_cursor_define_supported(QemuConsole *con)
1403
{
1404
    DisplayState *s = con->ds;
1405
    struct DisplayChangeListener *dcl;
1406
    QLIST_FOREACH(dcl, &s->listeners, next) {
1407
        if (dcl->ops->dpy_cursor_define) {
1408
            return true;
1409
        }
1410
    }
1411
    return false;
1412
}
1413

    
1414
/***********************************************************/
1415
/* register display */
1416

    
1417
/* console.c internal use only */
1418
static DisplayState *get_alloc_displaystate(void)
1419
{
1420
    if (!display_state) {
1421
        display_state = g_new0(DisplayState, 1);
1422
    }
1423
    return display_state;
1424
}
1425

    
1426
/*
1427
 * Called by main(), after creating QemuConsoles
1428
 * and before initializing ui (sdl/vnc/...).
1429
 */
1430
DisplayState *init_displaystate(void)
1431
{
1432
    int i;
1433

    
1434
    if (!display_state) {
1435
        display_state = g_new0(DisplayState, 1);
1436
    }
1437

    
1438
    for (i = 0; i < nb_consoles; i++) {
1439
        if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
1440
            consoles[i]->ds == NULL) {
1441
            text_console_do_init(consoles[i]->chr, display_state);
1442
        }
1443
    }
1444

    
1445
    return display_state;
1446
}
1447

    
1448
QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
1449
                                  graphic_hw_invalidate_ptr invalidate,
1450
                                  graphic_hw_text_update_ptr text_update,
1451
                                  void *opaque)
1452
{
1453
    int width = 640;
1454
    int height = 480;
1455
    QemuConsole *s;
1456
    DisplayState *ds;
1457

    
1458
    ds = get_alloc_displaystate();
1459
    trace_console_gfx_new();
1460
    s = new_console(ds, GRAPHIC_CONSOLE);
1461
    s->hw_update = update;
1462
    s->hw_invalidate = invalidate;
1463
    s->hw_text_update = text_update;
1464
    s->hw = opaque;
1465

    
1466
    s->surface = qemu_create_displaysurface(width, height);
1467
    return s;
1468
}
1469

    
1470
int is_graphic_console(void)
1471
{
1472
    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1473
}
1474

    
1475
int is_fixedsize_console(void)
1476
{
1477
    return active_console && active_console->console_type != TEXT_CONSOLE;
1478
}
1479

    
1480
static void text_console_set_echo(CharDriverState *chr, bool echo)
1481
{
1482
    QemuConsole *s = chr->opaque;
1483

    
1484
    s->echo = echo;
1485
}
1486

    
1487
static void text_console_update_cursor(void *opaque)
1488
{
1489
    QemuConsole *s = opaque;
1490

    
1491
    s->cursor_visible_phase = !s->cursor_visible_phase;
1492
    graphic_hw_invalidate(s);
1493
    qemu_mod_timer(s->cursor_timer,
1494
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1495
}
1496

    
1497
static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
1498
{
1499
    QemuConsole *s;
1500

    
1501
    s = chr->opaque;
1502

    
1503
    chr->chr_write = console_puts;
1504

    
1505
    s->out_fifo.buf = s->out_fifo_buf;
1506
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1507
    s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
1508
    s->ds = ds;
1509

    
1510
    s->y_displayed = 0;
1511
    s->y_base = 0;
1512
    s->total_height = DEFAULT_BACKSCROLL;
1513
    s->x = 0;
1514
    s->y = 0;
1515
    if (s->console_type == TEXT_CONSOLE) {
1516
        if (active_console && active_console->surface) {
1517
            s->g_width = surface_width(active_console->surface);
1518
            s->g_height = surface_height(active_console->surface);
1519
        } else {
1520
            s->g_width = 80 * FONT_WIDTH;
1521
            s->g_height = 24 * FONT_HEIGHT;
1522
        }
1523
    }
1524
    s->surface = qemu_create_displaysurface(s->g_width, s->g_height);
1525

    
1526
    s->cursor_timer =
1527
        qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
1528

    
1529
    s->hw_invalidate = text_console_invalidate;
1530
    s->hw_text_update = text_console_update;
1531
    s->hw = s;
1532

    
1533
    /* Set text attribute defaults */
1534
    s->t_attrib_default.bold = 0;
1535
    s->t_attrib_default.uline = 0;
1536
    s->t_attrib_default.blink = 0;
1537
    s->t_attrib_default.invers = 0;
1538
    s->t_attrib_default.unvisible = 0;
1539
    s->t_attrib_default.fgcol = COLOR_WHITE;
1540
    s->t_attrib_default.bgcol = COLOR_BLACK;
1541
    /* set current text attributes to default */
1542
    s->t_attrib = s->t_attrib_default;
1543
    text_console_resize(s);
1544

    
1545
    if (chr->label) {
1546
        char msg[128];
1547
        int len;
1548

    
1549
        s->t_attrib.bgcol = COLOR_BLUE;
1550
        len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1551
        console_puts(chr, (uint8_t*)msg, len);
1552
        s->t_attrib = s->t_attrib_default;
1553
    }
1554

    
1555
    qemu_chr_be_generic_open(chr);
1556
    if (chr->init)
1557
        chr->init(chr);
1558
}
1559

    
1560
static CharDriverState *text_console_init(ChardevVC *vc)
1561
{
1562
    CharDriverState *chr;
1563
    QemuConsole *s;
1564
    unsigned width = 0;
1565
    unsigned height = 0;
1566

    
1567
    chr = g_malloc0(sizeof(CharDriverState));
1568

    
1569
    if (vc->has_width) {
1570
        width = vc->width;
1571
    } else if (vc->has_cols) {
1572
        width = vc->cols * FONT_WIDTH;
1573
    }
1574

    
1575
    if (vc->has_height) {
1576
        height = vc->height;
1577
    } else if (vc->has_rows) {
1578
        height = vc->rows * FONT_HEIGHT;
1579
    }
1580

    
1581
    trace_console_txt_new(width, height);
1582
    if (width == 0 || height == 0) {
1583
        s = new_console(NULL, TEXT_CONSOLE);
1584
    } else {
1585
        s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1586
    }
1587

    
1588
    if (!s) {
1589
        g_free(chr);
1590
        return NULL;
1591
    }
1592

    
1593
    s->chr = chr;
1594
    s->g_width = width;
1595
    s->g_height = height;
1596
    chr->opaque = s;
1597
    chr->chr_set_echo = text_console_set_echo;
1598

    
1599
    if (display_state) {
1600
        text_console_do_init(chr, display_state);
1601
    }
1602
    return chr;
1603
}
1604

    
1605
static VcHandler *vc_handler = text_console_init;
1606

    
1607
CharDriverState *vc_init(ChardevVC *vc)
1608
{
1609
    return vc_handler(vc);
1610
}
1611

    
1612
void register_vc_handler(VcHandler *handler)
1613
{
1614
    vc_handler = handler;
1615
}
1616

    
1617
void qemu_console_resize(QemuConsole *s, int width, int height)
1618
{
1619
    DisplaySurface *surface;
1620

    
1621
    assert(s->console_type == GRAPHIC_CONSOLE);
1622
    s->g_width = width;
1623
    s->g_height = height;
1624
    surface = qemu_create_displaysurface(width, height);
1625
    dpy_gfx_replace_surface(s, surface);
1626
}
1627

    
1628
void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
1629
                       int dst_x, int dst_y, int w, int h)
1630
{
1631
    assert(con->console_type == GRAPHIC_CONSOLE);
1632
    dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
1633
}
1634

    
1635
DisplaySurface *qemu_console_surface(QemuConsole *console)
1636
{
1637
    return console->surface;
1638
}
1639

    
1640
DisplayState *qemu_console_displaystate(QemuConsole *console)
1641
{
1642
    return console->ds;
1643
}
1644

    
1645
PixelFormat qemu_different_endianness_pixelformat(int bpp)
1646
{
1647
    PixelFormat pf;
1648

    
1649
    memset(&pf, 0x00, sizeof(PixelFormat));
1650

    
1651
    pf.bits_per_pixel = bpp;
1652
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1653
    pf.depth = bpp == 32 ? 24 : bpp;
1654

    
1655
    switch (bpp) {
1656
        case 24:
1657
            pf.rmask = 0x000000FF;
1658
            pf.gmask = 0x0000FF00;
1659
            pf.bmask = 0x00FF0000;
1660
            pf.rmax = 255;
1661
            pf.gmax = 255;
1662
            pf.bmax = 255;
1663
            pf.rshift = 0;
1664
            pf.gshift = 8;
1665
            pf.bshift = 16;
1666
            pf.rbits = 8;
1667
            pf.gbits = 8;
1668
            pf.bbits = 8;
1669
            break;
1670
        case 32:
1671
            pf.rmask = 0x0000FF00;
1672
            pf.gmask = 0x00FF0000;
1673
            pf.bmask = 0xFF000000;
1674
            pf.amask = 0x00000000;
1675
            pf.amax = 255;
1676
            pf.rmax = 255;
1677
            pf.gmax = 255;
1678
            pf.bmax = 255;
1679
            pf.ashift = 0;
1680
            pf.rshift = 8;
1681
            pf.gshift = 16;
1682
            pf.bshift = 24;
1683
            pf.rbits = 8;
1684
            pf.gbits = 8;
1685
            pf.bbits = 8;
1686
            pf.abits = 8;
1687
            break;
1688
        default:
1689
            break;
1690
    }
1691
    return pf;
1692
}
1693

    
1694
PixelFormat qemu_default_pixelformat(int bpp)
1695
{
1696
    PixelFormat pf;
1697

    
1698
    memset(&pf, 0x00, sizeof(PixelFormat));
1699

    
1700
    pf.bits_per_pixel = bpp;
1701
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1702
    pf.depth = bpp == 32 ? 24 : bpp;
1703

    
1704
    switch (bpp) {
1705
        case 15:
1706
            pf.bits_per_pixel = 16;
1707
            pf.rmask = 0x00007c00;
1708
            pf.gmask = 0x000003E0;
1709
            pf.bmask = 0x0000001F;
1710
            pf.rmax = 31;
1711
            pf.gmax = 31;
1712
            pf.bmax = 31;
1713
            pf.rshift = 10;
1714
            pf.gshift = 5;
1715
            pf.bshift = 0;
1716
            pf.rbits = 5;
1717
            pf.gbits = 5;
1718
            pf.bbits = 5;
1719
            break;
1720
        case 16:
1721
            pf.rmask = 0x0000F800;
1722
            pf.gmask = 0x000007E0;
1723
            pf.bmask = 0x0000001F;
1724
            pf.rmax = 31;
1725
            pf.gmax = 63;
1726
            pf.bmax = 31;
1727
            pf.rshift = 11;
1728
            pf.gshift = 5;
1729
            pf.bshift = 0;
1730
            pf.rbits = 5;
1731
            pf.gbits = 6;
1732
            pf.bbits = 5;
1733
            break;
1734
        case 24:
1735
            pf.rmask = 0x00FF0000;
1736
            pf.gmask = 0x0000FF00;
1737
            pf.bmask = 0x000000FF;
1738
            pf.rmax = 255;
1739
            pf.gmax = 255;
1740
            pf.bmax = 255;
1741
            pf.rshift = 16;
1742
            pf.gshift = 8;
1743
            pf.bshift = 0;
1744
            pf.rbits = 8;
1745
            pf.gbits = 8;
1746
            pf.bbits = 8;
1747
            break;
1748
        case 32:
1749
            pf.rmask = 0x00FF0000;
1750
            pf.gmask = 0x0000FF00;
1751
            pf.bmask = 0x000000FF;
1752
            pf.rmax = 255;
1753
            pf.gmax = 255;
1754
            pf.bmax = 255;
1755
            pf.rshift = 16;
1756
            pf.gshift = 8;
1757
            pf.bshift = 0;
1758
            pf.rbits = 8;
1759
            pf.gbits = 8;
1760
            pf.bbits = 8;
1761
            break;
1762
        default:
1763
            break;
1764
    }
1765
    return pf;
1766
}
1767

    
1768
static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1769
                              Error **errp)
1770
{
1771
    int val;
1772

    
1773
    backend->vc = g_new0(ChardevVC, 1);
1774

    
1775
    val = qemu_opt_get_number(opts, "width", 0);
1776
    if (val != 0) {
1777
        backend->vc->has_width = true;
1778
        backend->vc->width = val;
1779
    }
1780

    
1781
    val = qemu_opt_get_number(opts, "height", 0);
1782
    if (val != 0) {
1783
        backend->vc->has_height = true;
1784
        backend->vc->height = val;
1785
    }
1786

    
1787
    val = qemu_opt_get_number(opts, "cols", 0);
1788
    if (val != 0) {
1789
        backend->vc->has_cols = true;
1790
        backend->vc->cols = val;
1791
    }
1792

    
1793
    val = qemu_opt_get_number(opts, "rows", 0);
1794
    if (val != 0) {
1795
        backend->vc->has_rows = true;
1796
        backend->vc->rows = val;
1797
    }
1798
}
1799

    
1800
static void register_types(void)
1801
{
1802
    register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1803
                              qemu_chr_parse_vc);
1804
}
1805

    
1806
type_init(register_types);