~ubuntu-branches/ubuntu/wily/qemu-kvm-spice/wily

« back to all changes in this revision

Viewing changes to ui/curses.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-10-19 10:44:56 UTC
  • Revision ID: james.westby@ubuntu.com-20111019104456-xgvskumk3sxi97f4
Tags: upstream-0.15.0+noroms
ImportĀ upstreamĀ versionĀ 0.15.0+noroms

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * QEMU curses/ncurses display driver
 
3
 * 
 
4
 * Copyright (c) 2005 Andrzej Zaborowski  <balrog@zabor.org>
 
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 <curses.h>
 
25
 
 
26
#ifndef _WIN32
 
27
#include <sys/ioctl.h>
 
28
#include <termios.h>
 
29
#endif
 
30
 
 
31
#ifdef __OpenBSD__
 
32
#define resize_term resizeterm
 
33
#endif
 
34
 
 
35
#include "qemu-common.h"
 
36
#include "console.h"
 
37
#include "sysemu.h"
 
38
 
 
39
#define FONT_HEIGHT 16
 
40
#define FONT_WIDTH 8
 
41
 
 
42
static console_ch_t screen[160 * 100];
 
43
static WINDOW *screenpad = NULL;
 
44
static int width, height, gwidth, gheight, invalidate;
 
45
static int px, py, sminx, sminy, smaxx, smaxy;
 
46
 
 
47
static void curses_update(DisplayState *ds, int x, int y, int w, int h)
 
48
{
 
49
    chtype *line;
 
50
 
 
51
    line = ((chtype *) screen) + y * width;
 
52
    for (h += y; y < h; y ++, line += width)
 
53
        mvwaddchnstr(screenpad, y, 0, line, width);
 
54
 
 
55
    pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
 
56
    refresh();
 
57
}
 
58
 
 
59
static void curses_calc_pad(void)
 
60
{
 
61
    if (is_fixedsize_console()) {
 
62
        width = gwidth;
 
63
        height = gheight;
 
64
    } else {
 
65
        width = COLS;
 
66
        height = LINES;
 
67
    }
 
68
 
 
69
    if (screenpad)
 
70
        delwin(screenpad);
 
71
 
 
72
    clear();
 
73
    refresh();
 
74
 
 
75
    screenpad = newpad(height, width);
 
76
 
 
77
    if (width > COLS) {
 
78
        px = (width - COLS) / 2;
 
79
        sminx = 0;
 
80
        smaxx = COLS;
 
81
    } else {
 
82
        px = 0;
 
83
        sminx = (COLS - width) / 2;
 
84
        smaxx = sminx + width;
 
85
    }
 
86
 
 
87
    if (height > LINES) {
 
88
        py = (height - LINES) / 2;
 
89
        sminy = 0;
 
90
        smaxy = LINES;
 
91
    } else {
 
92
        py = 0;
 
93
        sminy = (LINES - height) / 2;
 
94
        smaxy = sminy + height;
 
95
    }
 
96
}
 
97
 
 
98
static void curses_resize(DisplayState *ds)
 
99
{
 
100
    if (ds_get_width(ds) == gwidth && ds_get_height(ds) == gheight)
 
101
        return;
 
102
 
 
103
    gwidth = ds_get_width(ds);
 
104
    gheight = ds_get_height(ds);
 
105
 
 
106
    curses_calc_pad();
 
107
    ds->surface->width = width * FONT_WIDTH;
 
108
    ds->surface->height = height * FONT_HEIGHT;
 
109
}
 
110
 
 
111
#ifndef _WIN32
 
112
#if defined(SIGWINCH) && defined(KEY_RESIZE)
 
113
static void curses_winch_handler(int signum)
 
114
{
 
115
    struct winsize {
 
116
        unsigned short ws_row;
 
117
        unsigned short ws_col;
 
118
        unsigned short ws_xpixel;   /* unused */
 
119
        unsigned short ws_ypixel;   /* unused */
 
120
    } ws;
 
121
 
 
122
    /* terminal size changed */
 
123
    if (ioctl(1, TIOCGWINSZ, &ws) == -1)
 
124
        return;
 
125
 
 
126
    resize_term(ws.ws_row, ws.ws_col);
 
127
    curses_calc_pad();
 
128
    invalidate = 1;
 
129
 
 
130
    /* some systems require this */
 
131
    signal(SIGWINCH, curses_winch_handler);
 
132
}
 
133
#endif
 
134
#endif
 
135
 
 
136
static void curses_cursor_position(DisplayState *ds, int x, int y)
 
137
{
 
138
    if (x >= 0) {
 
139
        x = sminx + x - px;
 
140
        y = sminy + y - py;
 
141
 
 
142
        if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
 
143
            move(y, x);
 
144
            curs_set(1);
 
145
            /* it seems that curs_set(1) must always be called before
 
146
             * curs_set(2) for the latter to have effect */
 
147
            if (!is_graphic_console())
 
148
                curs_set(2);
 
149
            return;
 
150
        }
 
151
    }
 
152
 
 
153
    curs_set(0);
 
154
}
 
155
 
 
156
/* generic keyboard conversion */
 
157
 
 
158
#include "curses_keys.h"
 
159
 
 
160
static kbd_layout_t *kbd_layout = NULL;
 
161
 
 
162
static void curses_refresh(DisplayState *ds)
 
163
{
 
164
    int chr, nextchr, keysym, keycode, keycode_alt;
 
165
 
 
166
    if (invalidate) {
 
167
        clear();
 
168
        refresh();
 
169
        curses_calc_pad();
 
170
        ds->surface->width = FONT_WIDTH * width;
 
171
        ds->surface->height = FONT_HEIGHT * height;
 
172
        vga_hw_invalidate();
 
173
        invalidate = 0;
 
174
    }
 
175
 
 
176
    vga_hw_text_update(screen);
 
177
 
 
178
    nextchr = ERR;
 
179
    while (1) {
 
180
        /* while there are any pending key strokes to process */
 
181
        if (nextchr == ERR)
 
182
            chr = getch();
 
183
        else {
 
184
            chr = nextchr;
 
185
            nextchr = ERR;
 
186
        }
 
187
 
 
188
        if (chr == ERR)
 
189
            break;
 
190
 
 
191
#ifdef KEY_RESIZE
 
192
        /* this shouldn't occur when we use a custom SIGWINCH handler */
 
193
        if (chr == KEY_RESIZE) {
 
194
            clear();
 
195
            refresh();
 
196
            curses_calc_pad();
 
197
            curses_update(ds, 0, 0, width, height);
 
198
            ds->surface->width = FONT_WIDTH * width;
 
199
            ds->surface->height = FONT_HEIGHT * height;
 
200
            continue;
 
201
        }
 
202
#endif
 
203
 
 
204
        keycode = curses2keycode[chr];
 
205
        keycode_alt = 0;
 
206
 
 
207
        /* alt key */
 
208
        if (keycode == 1) {
 
209
            nextchr = getch();
 
210
 
 
211
            if (nextchr != ERR) {
 
212
                chr = nextchr;
 
213
                keycode_alt = ALT;
 
214
                keycode = curses2keycode[nextchr];
 
215
                nextchr = ERR;
 
216
 
 
217
                if (keycode != -1) {
 
218
                    keycode |= ALT;
 
219
 
 
220
                    /* process keys reserved for qemu */
 
221
                    if (keycode >= QEMU_KEY_CONSOLE0 &&
 
222
                            keycode < QEMU_KEY_CONSOLE0 + 9) {
 
223
                        erase();
 
224
                        wnoutrefresh(stdscr);
 
225
                        console_select(keycode - QEMU_KEY_CONSOLE0);
 
226
 
 
227
                        invalidate = 1;
 
228
                        continue;
 
229
                    }
 
230
                }
 
231
            }
 
232
        }
 
233
 
 
234
        if (kbd_layout) {
 
235
            keysym = -1;
 
236
            if (chr < CURSES_KEYS)
 
237
                keysym = curses2keysym[chr];
 
238
 
 
239
            if (keysym == -1) {
 
240
                if (chr < ' ') {
 
241
                    keysym = chr + '@';
 
242
                    if (keysym >= 'A' && keysym <= 'Z')
 
243
                        keysym += 'a' - 'A';
 
244
                    keysym |= KEYSYM_CNTRL;
 
245
                } else
 
246
                    keysym = chr;
 
247
            }
 
248
 
 
249
            keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK);
 
250
            if (keycode == 0)
 
251
                continue;
 
252
 
 
253
            keycode |= (keysym & ~KEYSYM_MASK) >> 16;
 
254
            keycode |= keycode_alt;
 
255
        }
 
256
 
 
257
        if (keycode == -1)
 
258
            continue;
 
259
 
 
260
        if (is_graphic_console()) {
 
261
            /* since terminals don't know about key press and release
 
262
             * events, we need to emit both for each key received */
 
263
            if (keycode & SHIFT)
 
264
                kbd_put_keycode(SHIFT_CODE);
 
265
            if (keycode & CNTRL)
 
266
                kbd_put_keycode(CNTRL_CODE);
 
267
            if (keycode & ALT)
 
268
                kbd_put_keycode(ALT_CODE);
 
269
            if (keycode & ALTGR) {
 
270
                kbd_put_keycode(SCANCODE_EMUL0);
 
271
                kbd_put_keycode(ALT_CODE);
 
272
            }
 
273
            if (keycode & GREY)
 
274
                kbd_put_keycode(GREY_CODE);
 
275
            kbd_put_keycode(keycode & KEY_MASK);
 
276
            if (keycode & GREY)
 
277
                kbd_put_keycode(GREY_CODE);
 
278
            kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE);
 
279
            if (keycode & ALTGR) {
 
280
                kbd_put_keycode(SCANCODE_EMUL0);
 
281
                kbd_put_keycode(ALT_CODE | KEY_RELEASE);
 
282
            }
 
283
            if (keycode & ALT)
 
284
                kbd_put_keycode(ALT_CODE | KEY_RELEASE);
 
285
            if (keycode & CNTRL)
 
286
                kbd_put_keycode(CNTRL_CODE | KEY_RELEASE);
 
287
            if (keycode & SHIFT)
 
288
                kbd_put_keycode(SHIFT_CODE | KEY_RELEASE);
 
289
        } else {
 
290
            keysym = curses2qemu[chr];
 
291
            if (keysym == -1)
 
292
                keysym = chr;
 
293
 
 
294
            kbd_put_keysym(keysym);
 
295
        }
 
296
    }
 
297
}
 
298
 
 
299
static void curses_atexit(void)
 
300
{
 
301
    endwin();
 
302
}
 
303
 
 
304
static void curses_setup(void)
 
305
{
 
306
    int i, colour_default[8] = {
 
307
        COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
 
308
        COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE,
 
309
    };
 
310
 
 
311
    /* input as raw as possible, let everything be interpreted
 
312
     * by the guest system */
 
313
    initscr(); noecho(); intrflush(stdscr, FALSE);
 
314
    nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
 
315
    start_color(); raw(); scrollok(stdscr, FALSE);
 
316
 
 
317
    for (i = 0; i < 64; i ++)
 
318
        init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
 
319
}
 
320
 
 
321
static void curses_keyboard_setup(void)
 
322
{
 
323
#if defined(__APPLE__)
 
324
    /* always use generic keymaps */
 
325
    if (!keyboard_layout)
 
326
        keyboard_layout = "en-us";
 
327
#endif
 
328
    if(keyboard_layout) {
 
329
        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
 
330
        if (!kbd_layout)
 
331
            exit(1);
 
332
    }
 
333
}
 
334
 
 
335
void curses_display_init(DisplayState *ds, int full_screen)
 
336
{
 
337
    DisplayChangeListener *dcl;
 
338
#ifndef _WIN32
 
339
    if (!isatty(1)) {
 
340
        fprintf(stderr, "We need a terminal output\n");
 
341
        exit(1);
 
342
    }
 
343
#endif
 
344
 
 
345
    curses_setup();
 
346
    curses_keyboard_setup();
 
347
    atexit(curses_atexit);
 
348
 
 
349
#ifndef _WIN32
 
350
#if defined(SIGWINCH) && defined(KEY_RESIZE)
 
351
    /* some curses implementations provide a handler, but we
 
352
     * want to be sure this is handled regardless of the library */
 
353
    signal(SIGWINCH, curses_winch_handler);
 
354
#endif
 
355
#endif
 
356
 
 
357
    dcl = (DisplayChangeListener *) qemu_mallocz(sizeof(DisplayChangeListener));
 
358
    dcl->dpy_update = curses_update;
 
359
    dcl->dpy_resize = curses_resize;
 
360
    dcl->dpy_refresh = curses_refresh;
 
361
    dcl->dpy_text_cursor = curses_cursor_position;
 
362
    register_displaychangelistener(ds, dcl);
 
363
    qemu_free_displaysurface(ds);
 
364
    ds->surface = qemu_create_displaysurface_from(640, 400, 0, 0, (uint8_t*) screen);
 
365
 
 
366
    invalidate = 1;
 
367
}