qemu/ui/curses.c
<<
>>
Prefs
   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
  25#include "qemu/osdep.h"
  26
  27#ifndef _WIN32
  28#include <sys/ioctl.h>
  29#include <termios.h>
  30#endif
  31#include <locale.h>
  32#include <wchar.h>
  33#include <iconv.h>
  34
  35#include "qapi/error.h"
  36#include "qemu/module.h"
  37#include "ui/console.h"
  38#include "ui/input.h"
  39#include "sysemu/sysemu.h"
  40
  41#if defined(__APPLE__) || defined(__OpenBSD__)
  42#define _XOPEN_SOURCE_EXTENDED 1
  43#endif
  44
  45/* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
  46#undef KEY_EVENT
  47#include <curses.h>
  48#undef KEY_EVENT
  49
  50#define FONT_HEIGHT 16
  51#define FONT_WIDTH 8
  52
  53enum maybe_keycode {
  54    CURSES_KEYCODE,
  55    CURSES_CHAR,
  56    CURSES_CHAR_OR_KEYCODE,
  57};
  58
  59static DisplayChangeListener *dcl;
  60static console_ch_t *screen;
  61static WINDOW *screenpad = NULL;
  62static int width, height, gwidth, gheight, invalidate;
  63static int px, py, sminx, sminy, smaxx, smaxy;
  64
  65static const char *font_charset = "CP437";
  66static cchar_t *vga_to_curses;
  67
  68static void curses_update(DisplayChangeListener *dcl,
  69                          int x, int y, int w, int h)
  70{
  71    console_ch_t *line;
  72    cchar_t curses_line[width];
  73    wchar_t wch[CCHARW_MAX];
  74    attr_t attrs;
  75    short colors;
  76    int ret;
  77
  78    line = screen + y * width;
  79    for (h += y; y < h; y ++, line += width) {
  80        for (x = 0; x < width; x++) {
  81            chtype ch = line[x] & A_CHARTEXT;
  82            chtype at = line[x] & A_ATTRIBUTES;
  83            short color_pair = PAIR_NUMBER(line[x]);
  84
  85            ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
  86            if (ret == ERR || wch[0] == 0) {
  87                wch[0] = ch;
  88                wch[1] = 0;
  89            }
  90            setcchar(&curses_line[x], wch, at, color_pair, NULL);
  91        }
  92        mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
  93    }
  94
  95    pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
  96    refresh();
  97}
  98
  99static void curses_calc_pad(void)
 100{
 101    if (qemu_console_is_fixedsize(NULL)) {
 102        width = gwidth;
 103        height = gheight;
 104    } else {
 105        width = COLS;
 106        height = LINES;
 107    }
 108
 109    if (screenpad)
 110        delwin(screenpad);
 111
 112    clear();
 113    refresh();
 114
 115    screenpad = newpad(height, width);
 116
 117    if (width > COLS) {
 118        px = (width - COLS) / 2;
 119        sminx = 0;
 120        smaxx = COLS;
 121    } else {
 122        px = 0;
 123        sminx = (COLS - width) / 2;
 124        smaxx = sminx + width;
 125    }
 126
 127    if (height > LINES) {
 128        py = (height - LINES) / 2;
 129        sminy = 0;
 130        smaxy = LINES;
 131    } else {
 132        py = 0;
 133        sminy = (LINES - height) / 2;
 134        smaxy = sminy + height;
 135    }
 136}
 137
 138static void curses_resize(DisplayChangeListener *dcl,
 139                          int width, int height)
 140{
 141    if (width == gwidth && height == gheight) {
 142        return;
 143    }
 144
 145    gwidth = width;
 146    gheight = height;
 147
 148    curses_calc_pad();
 149}
 150
 151#if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
 152static volatile sig_atomic_t got_sigwinch;
 153static void curses_winch_check(void)
 154{
 155    struct winsize {
 156        unsigned short ws_row;
 157        unsigned short ws_col;
 158        unsigned short ws_xpixel;   /* unused */
 159        unsigned short ws_ypixel;   /* unused */
 160    } ws;
 161
 162    if (!got_sigwinch) {
 163        return;
 164    }
 165    got_sigwinch = false;
 166
 167    if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
 168        return;
 169    }
 170
 171    resize_term(ws.ws_row, ws.ws_col);
 172    invalidate = 1;
 173}
 174
 175static void curses_winch_handler(int signum)
 176{
 177    got_sigwinch = true;
 178}
 179
 180static void curses_winch_init(void)
 181{
 182    struct sigaction old, winch = {
 183        .sa_handler  = curses_winch_handler,
 184    };
 185    sigaction(SIGWINCH, &winch, &old);
 186}
 187#else
 188static void curses_winch_check(void) {}
 189static void curses_winch_init(void) {}
 190#endif
 191
 192static void curses_cursor_position(DisplayChangeListener *dcl,
 193                                   int x, int y)
 194{
 195    if (x >= 0) {
 196        x = sminx + x - px;
 197        y = sminy + y - py;
 198
 199        if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
 200            move(y, x);
 201            curs_set(1);
 202            /* it seems that curs_set(1) must always be called before
 203             * curs_set(2) for the latter to have effect */
 204            if (!qemu_console_is_graphic(NULL)) {
 205                curs_set(2);
 206            }
 207            return;
 208        }
 209    }
 210
 211    curs_set(0);
 212}
 213
 214/* generic keyboard conversion */
 215
 216#include "curses_keys.h"
 217
 218static kbd_layout_t *kbd_layout = NULL;
 219
 220static wint_t console_getch(enum maybe_keycode *maybe_keycode)
 221{
 222    wint_t ret;
 223    switch (get_wch(&ret)) {
 224    case KEY_CODE_YES:
 225        *maybe_keycode = CURSES_KEYCODE;
 226        break;
 227    case OK:
 228        *maybe_keycode = CURSES_CHAR;
 229        break;
 230    case ERR:
 231        ret = -1;
 232        break;
 233    default:
 234        abort();
 235    }
 236    return ret;
 237}
 238
 239static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
 240                      int chr, enum maybe_keycode maybe_keycode)
 241{
 242    int ret = -1;
 243    if (maybe_keycode == CURSES_CHAR) {
 244        if (chr < CURSES_CHARS) {
 245            ret = _curses2foo[chr];
 246        }
 247    } else {
 248        if (chr < CURSES_KEYS) {
 249            ret = _curseskey2foo[chr];
 250        }
 251        if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
 252            chr < CURSES_CHARS) {
 253            ret = _curses2foo[chr];
 254        }
 255    }
 256    return ret;
 257}
 258
 259#define curses2keycode(chr, maybe_keycode) \
 260    curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
 261#define curses2keysym(chr, maybe_keycode) \
 262    curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
 263#define curses2qemu(chr, maybe_keycode) \
 264    curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
 265
 266static void curses_refresh(DisplayChangeListener *dcl)
 267{
 268    int chr, keysym, keycode, keycode_alt;
 269    enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
 270
 271    curses_winch_check();
 272
 273    if (invalidate) {
 274        clear();
 275        refresh();
 276        curses_calc_pad();
 277        graphic_hw_invalidate(NULL);
 278        invalidate = 0;
 279    }
 280
 281    graphic_hw_text_update(NULL, screen);
 282
 283    while (1) {
 284        /* while there are any pending key strokes to process */
 285        chr = console_getch(&maybe_keycode);
 286
 287        if (chr == -1)
 288            break;
 289
 290#ifdef KEY_RESIZE
 291        /* this shouldn't occur when we use a custom SIGWINCH handler */
 292        if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
 293            clear();
 294            refresh();
 295            curses_calc_pad();
 296            curses_update(dcl, 0, 0, width, height);
 297            continue;
 298        }
 299#endif
 300
 301        keycode = curses2keycode(chr, maybe_keycode);
 302        keycode_alt = 0;
 303
 304        /* alt or esc key */
 305        if (keycode == 1) {
 306            enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
 307            int nextchr = console_getch(&next_maybe_keycode);
 308
 309            if (nextchr != -1) {
 310                chr = nextchr;
 311                maybe_keycode = next_maybe_keycode;
 312                keycode_alt = ALT;
 313                keycode = curses2keycode(chr, maybe_keycode);
 314
 315                if (keycode != -1) {
 316                    keycode |= ALT;
 317
 318                    /* process keys reserved for qemu */
 319                    if (keycode >= QEMU_KEY_CONSOLE0 &&
 320                            keycode < QEMU_KEY_CONSOLE0 + 9) {
 321                        erase();
 322                        wnoutrefresh(stdscr);
 323                        console_select(keycode - QEMU_KEY_CONSOLE0);
 324
 325                        invalidate = 1;
 326                        continue;
 327                    }
 328                }
 329            }
 330        }
 331
 332        if (kbd_layout) {
 333            keysym = curses2keysym(chr, maybe_keycode);
 334
 335            if (keysym == -1) {
 336                if (chr < ' ') {
 337                    keysym = chr + '@';
 338                    if (keysym >= 'A' && keysym <= 'Z')
 339                        keysym += 'a' - 'A';
 340                    keysym |= KEYSYM_CNTRL;
 341                } else
 342                    keysym = chr;
 343            }
 344
 345            keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
 346                                      NULL, false);
 347            if (keycode == 0)
 348                continue;
 349
 350            keycode |= (keysym & ~KEYSYM_MASK) >> 16;
 351            keycode |= keycode_alt;
 352        }
 353
 354        if (keycode == -1)
 355            continue;
 356
 357        if (qemu_console_is_graphic(NULL)) {
 358            /* since terminals don't know about key press and release
 359             * events, we need to emit both for each key received */
 360            if (keycode & SHIFT) {
 361                qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
 362                qemu_input_event_send_key_delay(0);
 363            }
 364            if (keycode & CNTRL) {
 365                qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
 366                qemu_input_event_send_key_delay(0);
 367            }
 368            if (keycode & ALT) {
 369                qemu_input_event_send_key_number(NULL, ALT_CODE, true);
 370                qemu_input_event_send_key_delay(0);
 371            }
 372            if (keycode & ALTGR) {
 373                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
 374                qemu_input_event_send_key_delay(0);
 375            }
 376
 377            qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
 378            qemu_input_event_send_key_delay(0);
 379            qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
 380            qemu_input_event_send_key_delay(0);
 381
 382            if (keycode & ALTGR) {
 383                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
 384                qemu_input_event_send_key_delay(0);
 385            }
 386            if (keycode & ALT) {
 387                qemu_input_event_send_key_number(NULL, ALT_CODE, false);
 388                qemu_input_event_send_key_delay(0);
 389            }
 390            if (keycode & CNTRL) {
 391                qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
 392                qemu_input_event_send_key_delay(0);
 393            }
 394            if (keycode & SHIFT) {
 395                qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
 396                qemu_input_event_send_key_delay(0);
 397            }
 398        } else {
 399            keysym = curses2qemu(chr, maybe_keycode);
 400            if (keysym == -1)
 401                keysym = chr;
 402
 403            kbd_put_keysym(keysym);
 404        }
 405    }
 406}
 407
 408static void curses_atexit(void)
 409{
 410    endwin();
 411    g_free(vga_to_curses);
 412    g_free(screen);
 413}
 414
 415/*
 416 * In the following:
 417 * - fch is the font glyph number
 418 * - uch is the unicode value
 419 * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
 420 * - mbch is the native local-dependent multibyte representation
 421 */
 422
 423/* Setup wchar glyph for one UCS-2 char */
 424static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
 425{
 426    char mbch[MB_LEN_MAX];
 427    wchar_t wch[2];
 428    char *puch, *pmbch;
 429    size_t such, smbch;
 430    mbstate_t ps;
 431
 432    puch = (char *) &uch;
 433    pmbch = (char *) mbch;
 434    such = sizeof(uch);
 435    smbch = sizeof(mbch);
 436
 437    if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
 438        fprintf(stderr, "Could not convert 0x%04x "
 439                        "from UCS-2 to a multibyte character: %s\n",
 440                        uch, strerror(errno));
 441        return;
 442    }
 443
 444    memset(&ps, 0, sizeof(ps));
 445    if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
 446        fprintf(stderr, "Could not convert 0x%04x "
 447                        "from a multibyte character to wchar_t: %s\n",
 448                        uch, strerror(errno));
 449        return;
 450    }
 451
 452    wch[1] = 0;
 453    setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
 454}
 455
 456/* Setup wchar glyph for one font character */
 457static void convert_font(unsigned char fch, iconv_t conv)
 458{
 459    char mbch[MB_LEN_MAX];
 460    wchar_t wch[2];
 461    char *pfch, *pmbch;
 462    size_t sfch, smbch;
 463    mbstate_t ps;
 464
 465    pfch = (char *) &fch;
 466    pmbch = (char *) &mbch;
 467    sfch = sizeof(fch);
 468    smbch = sizeof(mbch);
 469
 470    if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
 471        fprintf(stderr, "Could not convert font glyph 0x%02x "
 472                        "from %s to a multibyte character: %s\n",
 473                        fch, font_charset, strerror(errno));
 474        return;
 475    }
 476
 477    memset(&ps, 0, sizeof(ps));
 478    if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
 479        fprintf(stderr, "Could not convert font glyph 0x%02x "
 480                        "from a multibyte character to wchar_t: %s\n",
 481                        fch, strerror(errno));
 482        return;
 483    }
 484
 485    wch[1] = 0;
 486    setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
 487}
 488
 489/* Convert one wchar to UCS-2 */
 490static uint16_t get_ucs(wchar_t wch, iconv_t conv)
 491{
 492    char mbch[MB_LEN_MAX];
 493    uint16_t uch;
 494    char *pmbch, *puch;
 495    size_t smbch, such;
 496    mbstate_t ps;
 497    int ret;
 498
 499    memset(&ps, 0, sizeof(ps));
 500    ret = wcrtomb(mbch, wch, &ps);
 501    if (ret == -1) {
 502        fprintf(stderr, "Could not convert 0x%04lx "
 503                        "from wchar_t to a multibyte character: %s\n",
 504                        (unsigned long)wch, strerror(errno));
 505        return 0xFFFD;
 506    }
 507
 508    pmbch = (char *) mbch;
 509    puch = (char *) &uch;
 510    smbch = ret;
 511    such = sizeof(uch);
 512
 513    if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
 514        fprintf(stderr, "Could not convert 0x%04lx "
 515                        "from a multibyte character to UCS-2 : %s\n",
 516                        (unsigned long)wch, strerror(errno));
 517        return 0xFFFD;
 518    }
 519
 520    return uch;
 521}
 522
 523/*
 524 * Setup mapping for vga to curses line graphics.
 525 */
 526static void font_setup(void)
 527{
 528    iconv_t ucs2_to_nativecharset;
 529    iconv_t nativecharset_to_ucs2;
 530    iconv_t font_conv;
 531    int i;
 532    g_autofree gchar *local_codeset = g_get_codeset();
 533
 534    /*
 535     * Control characters are normally non-printable, but VGA does have
 536     * well-known glyphs for them.
 537     */
 538    static const uint16_t control_characters[0x20] = {
 539      0x0020,
 540      0x263a,
 541      0x263b,
 542      0x2665,
 543      0x2666,
 544      0x2663,
 545      0x2660,
 546      0x2022,
 547      0x25d8,
 548      0x25cb,
 549      0x25d9,
 550      0x2642,
 551      0x2640,
 552      0x266a,
 553      0x266b,
 554      0x263c,
 555      0x25ba,
 556      0x25c4,
 557      0x2195,
 558      0x203c,
 559      0x00b6,
 560      0x00a7,
 561      0x25ac,
 562      0x21a8,
 563      0x2191,
 564      0x2193,
 565      0x2192,
 566      0x2190,
 567      0x221f,
 568      0x2194,
 569      0x25b2,
 570      0x25bc
 571    };
 572
 573    ucs2_to_nativecharset = iconv_open(local_codeset, "UCS-2");
 574    if (ucs2_to_nativecharset == (iconv_t) -1) {
 575        fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
 576                        strerror(errno));
 577        exit(1);
 578    }
 579
 580    nativecharset_to_ucs2 = iconv_open("UCS-2", local_codeset);
 581    if (nativecharset_to_ucs2 == (iconv_t) -1) {
 582        iconv_close(ucs2_to_nativecharset);
 583        fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
 584                        strerror(errno));
 585        exit(1);
 586    }
 587
 588    font_conv = iconv_open(local_codeset, font_charset);
 589    if (font_conv == (iconv_t) -1) {
 590        iconv_close(ucs2_to_nativecharset);
 591        iconv_close(nativecharset_to_ucs2);
 592        fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
 593                        font_charset, strerror(errno));
 594        exit(1);
 595    }
 596
 597    /* Control characters */
 598    for (i = 0; i <= 0x1F; i++) {
 599        convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
 600    }
 601
 602    for (i = 0x20; i <= 0xFF; i++) {
 603        convert_font(i, font_conv);
 604    }
 605
 606    /* DEL */
 607    convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
 608
 609    if (strcmp(local_codeset, "UTF-8")) {
 610        /* Non-Unicode capable, use termcap equivalents for those available */
 611        for (i = 0; i <= 0xFF; i++) {
 612            wchar_t wch[CCHARW_MAX];
 613            attr_t attr;
 614            short color;
 615            int ret;
 616
 617            ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
 618            if (ret == ERR)
 619                continue;
 620
 621            switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
 622            case 0x00a3:
 623                vga_to_curses[i] = *WACS_STERLING;
 624                break;
 625            case 0x2591:
 626                vga_to_curses[i] = *WACS_BOARD;
 627                break;
 628            case 0x2592:
 629                vga_to_curses[i] = *WACS_CKBOARD;
 630                break;
 631            case 0x2502:
 632                vga_to_curses[i] = *WACS_VLINE;
 633                break;
 634            case 0x2524:
 635                vga_to_curses[i] = *WACS_RTEE;
 636                break;
 637            case 0x2510:
 638                vga_to_curses[i] = *WACS_URCORNER;
 639                break;
 640            case 0x2514:
 641                vga_to_curses[i] = *WACS_LLCORNER;
 642                break;
 643            case 0x2534:
 644                vga_to_curses[i] = *WACS_BTEE;
 645                break;
 646            case 0x252c:
 647                vga_to_curses[i] = *WACS_TTEE;
 648                break;
 649            case 0x251c:
 650                vga_to_curses[i] = *WACS_LTEE;
 651                break;
 652            case 0x2500:
 653                vga_to_curses[i] = *WACS_HLINE;
 654                break;
 655            case 0x253c:
 656                vga_to_curses[i] = *WACS_PLUS;
 657                break;
 658            case 0x256c:
 659                vga_to_curses[i] = *WACS_LANTERN;
 660                break;
 661            case 0x256a:
 662                vga_to_curses[i] = *WACS_NEQUAL;
 663                break;
 664            case 0x2518:
 665                vga_to_curses[i] = *WACS_LRCORNER;
 666                break;
 667            case 0x250c:
 668                vga_to_curses[i] = *WACS_ULCORNER;
 669                break;
 670            case 0x2588:
 671                vga_to_curses[i] = *WACS_BLOCK;
 672                break;
 673            case 0x03c0:
 674                vga_to_curses[i] = *WACS_PI;
 675                break;
 676            case 0x00b1:
 677                vga_to_curses[i] = *WACS_PLMINUS;
 678                break;
 679            case 0x2265:
 680                vga_to_curses[i] = *WACS_GEQUAL;
 681                break;
 682            case 0x2264:
 683                vga_to_curses[i] = *WACS_LEQUAL;
 684                break;
 685            case 0x00b0:
 686                vga_to_curses[i] = *WACS_DEGREE;
 687                break;
 688            case 0x25a0:
 689                vga_to_curses[i] = *WACS_BULLET;
 690                break;
 691            case 0x2666:
 692                vga_to_curses[i] = *WACS_DIAMOND;
 693                break;
 694            case 0x2192:
 695                vga_to_curses[i] = *WACS_RARROW;
 696                break;
 697            case 0x2190:
 698                vga_to_curses[i] = *WACS_LARROW;
 699                break;
 700            case 0x2191:
 701                vga_to_curses[i] = *WACS_UARROW;
 702                break;
 703            case 0x2193:
 704                vga_to_curses[i] = *WACS_DARROW;
 705                break;
 706            case 0x23ba:
 707                vga_to_curses[i] = *WACS_S1;
 708                break;
 709            case 0x23bb:
 710                vga_to_curses[i] = *WACS_S3;
 711                break;
 712            case 0x23bc:
 713                vga_to_curses[i] = *WACS_S7;
 714                break;
 715            case 0x23bd:
 716                vga_to_curses[i] = *WACS_S9;
 717                break;
 718            }
 719        }
 720    }
 721    iconv_close(ucs2_to_nativecharset);
 722    iconv_close(nativecharset_to_ucs2);
 723    iconv_close(font_conv);
 724}
 725
 726static void curses_setup(void)
 727{
 728    int i, colour_default[8] = {
 729        [QEMU_COLOR_BLACK]   = COLOR_BLACK,
 730        [QEMU_COLOR_BLUE]    = COLOR_BLUE,
 731        [QEMU_COLOR_GREEN]   = COLOR_GREEN,
 732        [QEMU_COLOR_CYAN]    = COLOR_CYAN,
 733        [QEMU_COLOR_RED]     = COLOR_RED,
 734        [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
 735        [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
 736        [QEMU_COLOR_WHITE]   = COLOR_WHITE,
 737    };
 738
 739    /* input as raw as possible, let everything be interpreted
 740     * by the guest system */
 741    initscr(); noecho(); intrflush(stdscr, FALSE);
 742    nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
 743    start_color(); raw(); scrollok(stdscr, FALSE);
 744    set_escdelay(25);
 745
 746    /* Make color pair to match color format (3bits bg:3bits fg) */
 747    for (i = 0; i < 64; i++) {
 748        init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
 749    }
 750    /* Set default color for more than 64 for safety. */
 751    for (i = 64; i < COLOR_PAIRS; i++) {
 752        init_pair(i, COLOR_WHITE, COLOR_BLACK);
 753    }
 754
 755    font_setup();
 756}
 757
 758static void curses_keyboard_setup(void)
 759{
 760#if defined(__APPLE__)
 761    /* always use generic keymaps */
 762    if (!keyboard_layout)
 763        keyboard_layout = "en-us";
 764#endif
 765    if(keyboard_layout) {
 766        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
 767                                          &error_fatal);
 768    }
 769}
 770
 771static const DisplayChangeListenerOps dcl_ops = {
 772    .dpy_name        = "curses",
 773    .dpy_text_update = curses_update,
 774    .dpy_text_resize = curses_resize,
 775    .dpy_refresh     = curses_refresh,
 776    .dpy_text_cursor = curses_cursor_position,
 777};
 778
 779static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
 780{
 781#ifndef _WIN32
 782    if (!isatty(1)) {
 783        fprintf(stderr, "We need a terminal output\n");
 784        exit(1);
 785    }
 786#endif
 787
 788    setlocale(LC_CTYPE, "");
 789    if (opts->u.curses.charset) {
 790        font_charset = opts->u.curses.charset;
 791    }
 792    screen = g_new0(console_ch_t, 160 * 100);
 793    vga_to_curses = g_new0(cchar_t, 256);
 794    curses_setup();
 795    curses_keyboard_setup();
 796    atexit(curses_atexit);
 797
 798    curses_winch_init();
 799
 800    dcl = g_new0(DisplayChangeListener, 1);
 801    dcl->ops = &dcl_ops;
 802    register_displaychangelistener(dcl);
 803
 804    invalidate = 1;
 805}
 806
 807static QemuDisplay qemu_display_curses = {
 808    .type       = DISPLAY_TYPE_CURSES,
 809    .init       = curses_display_init,
 810};
 811
 812static void register_curses(void)
 813{
 814    qemu_display_register(&qemu_display_curses);
 815}
 816
 817type_init(register_curses);
 818