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