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/* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
  42#undef KEY_EVENT
  43#include <curses.h>
  44#undef KEY_EVENT
  45
  46#define FONT_HEIGHT 16
  47#define FONT_WIDTH 8
  48
  49enum maybe_keycode {
  50    CURSES_KEYCODE,
  51    CURSES_CHAR,
  52    CURSES_CHAR_OR_KEYCODE,
  53};
  54
  55static DisplayChangeListener *dcl;
  56static console_ch_t *screen;
  57static WINDOW *screenpad = NULL;
  58static int width, height, gwidth, gheight, invalidate;
  59static int px, py, sminx, sminy, smaxx, smaxy;
  60
  61static const char *font_charset = "CP437";
  62static cchar_t *vga_to_curses;
  63
  64static void curses_update(DisplayChangeListener *dcl,
  65                          int x, int y, int w, int h)
  66{
  67    console_ch_t *line;
  68    cchar_t curses_line[width];
  69    wchar_t wch[CCHARW_MAX];
  70    attr_t attrs;
  71    short colors;
  72    int ret;
  73
  74    line = screen + y * width;
  75    for (h += y; y < h; y ++, line += width) {
  76        for (x = 0; x < width; x++) {
  77            chtype ch = line[x] & A_CHARTEXT;
  78            chtype at = line[x] & A_ATTRIBUTES;
  79            short color_pair = PAIR_NUMBER(line[x]);
  80
  81            ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
  82            if (ret == ERR || wch[0] == 0) {
  83                wch[0] = ch;
  84                wch[1] = 0;
  85            }
  86            setcchar(&curses_line[x], wch, at, color_pair, NULL);
  87        }
  88        mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
  89    }
  90
  91    pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
  92    refresh();
  93}
  94
  95static void curses_calc_pad(void)
  96{
  97    if (qemu_console_is_fixedsize(NULL)) {
  98        width = gwidth;
  99        height = gheight;
 100    } else {
 101        width = COLS;
 102        height = LINES;
 103    }
 104
 105    if (screenpad)
 106        delwin(screenpad);
 107
 108    clear();
 109    refresh();
 110
 111    screenpad = newpad(height, width);
 112
 113    if (width > COLS) {
 114        px = (width - COLS) / 2;
 115        sminx = 0;
 116        smaxx = COLS;
 117    } else {
 118        px = 0;
 119        sminx = (COLS - width) / 2;
 120        smaxx = sminx + width;
 121    }
 122
 123    if (height > LINES) {
 124        py = (height - LINES) / 2;
 125        sminy = 0;
 126        smaxy = LINES;
 127    } else {
 128        py = 0;
 129        sminy = (LINES - height) / 2;
 130        smaxy = sminy + height;
 131    }
 132}
 133
 134static void curses_resize(DisplayChangeListener *dcl,
 135                          int width, int height)
 136{
 137    if (width == gwidth && height == gheight) {
 138        return;
 139    }
 140
 141    gwidth = width;
 142    gheight = height;
 143
 144    curses_calc_pad();
 145}
 146
 147#if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
 148static volatile sig_atomic_t got_sigwinch;
 149static void curses_winch_check(void)
 150{
 151    struct winsize {
 152        unsigned short ws_row;
 153        unsigned short ws_col;
 154        unsigned short ws_xpixel;   /* unused */
 155        unsigned short ws_ypixel;   /* unused */
 156    } ws;
 157
 158    if (!got_sigwinch) {
 159        return;
 160    }
 161    got_sigwinch = false;
 162
 163    if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
 164        return;
 165    }
 166
 167    resize_term(ws.ws_row, ws.ws_col);
 168    invalidate = 1;
 169}
 170
 171static void curses_winch_handler(int signum)
 172{
 173    got_sigwinch = true;
 174}
 175
 176static void curses_winch_init(void)
 177{
 178    struct sigaction old, winch = {
 179        .sa_handler  = curses_winch_handler,
 180    };
 181    sigaction(SIGWINCH, &winch, &old);
 182}
 183#else
 184static void curses_winch_check(void) {}
 185static void curses_winch_init(void) {}
 186#endif
 187
 188static void curses_cursor_position(DisplayChangeListener *dcl,
 189                                   int x, int y)
 190{
 191    if (x >= 0) {
 192        x = sminx + x - px;
 193        y = sminy + y - py;
 194
 195        if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
 196            move(y, x);
 197            curs_set(1);
 198            /* it seems that curs_set(1) must always be called before
 199             * curs_set(2) for the latter to have effect */
 200            if (!qemu_console_is_graphic(NULL)) {
 201                curs_set(2);
 202            }
 203            return;
 204        }
 205    }
 206
 207    curs_set(0);
 208}
 209
 210/* generic keyboard conversion */
 211
 212#include "curses_keys.h"
 213
 214static kbd_layout_t *kbd_layout = NULL;
 215
 216static wint_t console_getch(enum maybe_keycode *maybe_keycode)
 217{
 218    wint_t ret;
 219    switch (get_wch(&ret)) {
 220    case KEY_CODE_YES:
 221        *maybe_keycode = CURSES_KEYCODE;
 222        break;
 223    case OK:
 224        *maybe_keycode = CURSES_CHAR;
 225        break;
 226    case ERR:
 227        ret = -1;
 228        break;
 229    default:
 230        abort();
 231    }
 232    return ret;
 233}
 234
 235static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
 236                      int chr, enum maybe_keycode maybe_keycode)
 237{
 238    int ret = -1;
 239    if (maybe_keycode == CURSES_CHAR) {
 240        if (chr < CURSES_CHARS) {
 241            ret = _curses2foo[chr];
 242        }
 243    } else {
 244        if (chr < CURSES_KEYS) {
 245            ret = _curseskey2foo[chr];
 246        }
 247        if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
 248            chr < CURSES_CHARS) {
 249            ret = _curses2foo[chr];
 250        }
 251    }
 252    return ret;
 253}
 254
 255#define curses2keycode(chr, maybe_keycode) \
 256    curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
 257#define curses2keysym(chr, maybe_keycode) \
 258    curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
 259#define curses2qemu(chr, maybe_keycode) \
 260    curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
 261
 262static void curses_refresh(DisplayChangeListener *dcl)
 263{
 264    int chr, keysym, keycode, keycode_alt;
 265    enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
 266
 267    curses_winch_check();
 268
 269    if (invalidate) {
 270        clear();
 271        refresh();
 272        curses_calc_pad();
 273        graphic_hw_invalidate(NULL);
 274        invalidate = 0;
 275    }
 276
 277    graphic_hw_text_update(NULL, screen);
 278
 279    while (1) {
 280        /* while there are any pending key strokes to process */
 281        chr = console_getch(&maybe_keycode);
 282
 283        if (chr == -1)
 284            break;
 285
 286#ifdef KEY_RESIZE
 287        /* this shouldn't occur when we use a custom SIGWINCH handler */
 288        if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
 289            clear();
 290            refresh();
 291            curses_calc_pad();
 292            curses_update(dcl, 0, 0, width, height);
 293            continue;
 294        }
 295#endif
 296
 297        keycode = curses2keycode(chr, maybe_keycode);
 298        keycode_alt = 0;
 299
 300        /* alt or esc key */
 301        if (keycode == 1) {
 302            enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
 303            int nextchr = console_getch(&next_maybe_keycode);
 304
 305            if (nextchr != -1) {
 306                chr = nextchr;
 307                maybe_keycode = next_maybe_keycode;
 308                keycode_alt = ALT;
 309                keycode = curses2keycode(chr, maybe_keycode);
 310
 311                if (keycode != -1) {
 312                    keycode |= ALT;
 313
 314                    /* process keys reserved for qemu */
 315                    if (keycode >= QEMU_KEY_CONSOLE0 &&
 316                            keycode < QEMU_KEY_CONSOLE0 + 9) {
 317                        erase();
 318                        wnoutrefresh(stdscr);
 319                        console_select(keycode - QEMU_KEY_CONSOLE0);
 320
 321                        invalidate = 1;
 322                        continue;
 323                    }
 324                }
 325            }
 326        }
 327
 328        if (kbd_layout) {
 329            keysym = curses2keysym(chr, maybe_keycode);
 330
 331            if (keysym == -1) {
 332                if (chr < ' ') {
 333                    keysym = chr + '@';
 334                    if (keysym >= 'A' && keysym <= 'Z')
 335                        keysym += 'a' - 'A';
 336                    keysym |= KEYSYM_CNTRL;
 337                } else
 338                    keysym = chr;
 339            }
 340
 341            keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
 342                                      NULL, false);
 343            if (keycode == 0)
 344                continue;
 345
 346            keycode |= (keysym & ~KEYSYM_MASK) >> 16;
 347            keycode |= keycode_alt;
 348        }
 349
 350        if (keycode == -1)
 351            continue;
 352
 353        if (qemu_console_is_graphic(NULL)) {
 354            /* since terminals don't know about key press and release
 355             * events, we need to emit both for each key received */
 356            if (keycode & SHIFT) {
 357                qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
 358                qemu_input_event_send_key_delay(0);
 359            }
 360            if (keycode & CNTRL) {
 361                qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
 362                qemu_input_event_send_key_delay(0);
 363            }
 364            if (keycode & ALT) {
 365                qemu_input_event_send_key_number(NULL, ALT_CODE, true);
 366                qemu_input_event_send_key_delay(0);
 367            }
 368            if (keycode & ALTGR) {
 369                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
 370                qemu_input_event_send_key_delay(0);
 371            }
 372
 373            qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
 374            qemu_input_event_send_key_delay(0);
 375            qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
 376            qemu_input_event_send_key_delay(0);
 377
 378            if (keycode & ALTGR) {
 379                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
 380                qemu_input_event_send_key_delay(0);
 381            }
 382            if (keycode & ALT) {
 383                qemu_input_event_send_key_number(NULL, ALT_CODE, false);
 384                qemu_input_event_send_key_delay(0);
 385            }
 386            if (keycode & CNTRL) {
 387                qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
 388                qemu_input_event_send_key_delay(0);
 389            }
 390            if (keycode & SHIFT) {
 391                qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
 392                qemu_input_event_send_key_delay(0);
 393            }
 394        } else {
 395            keysym = curses2qemu(chr, maybe_keycode);
 396            if (keysym == -1)
 397                keysym = chr;
 398
 399            kbd_put_keysym(keysym);
 400        }
 401    }
 402}
 403
 404static void curses_atexit(void)
 405{
 406    endwin();
 407    g_free(vga_to_curses);
 408    g_free(screen);
 409}
 410
 411/*
 412 * In the following:
 413 * - fch is the font glyph number
 414 * - uch is the unicode value
 415 * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
 416 * - mbch is the native local-dependent multibyte representation
 417 */
 418
 419/* Setup wchar glyph for one UCS-2 char */
 420static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
 421{
 422    char mbch[MB_LEN_MAX];
 423    wchar_t wch[2];
 424    char *puch, *pmbch;
 425    size_t such, smbch;
 426    mbstate_t ps;
 427
 428    puch = (char *) &uch;
 429    pmbch = (char *) mbch;
 430    such = sizeof(uch);
 431    smbch = sizeof(mbch);
 432
 433    if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
 434        fprintf(stderr, "Could not convert 0x%04x "
 435                        "from UCS-2 to a multibyte character: %s\n",
 436                        uch, strerror(errno));
 437        return;
 438    }
 439
 440    memset(&ps, 0, sizeof(ps));
 441    if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
 442        fprintf(stderr, "Could not convert 0x%04x "
 443                        "from a multibyte character to wchar_t: %s\n",
 444                        uch, strerror(errno));
 445        return;
 446    }
 447
 448    wch[1] = 0;
 449    setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
 450}
 451
 452/* Setup wchar glyph for one font character */
 453static void convert_font(unsigned char fch, iconv_t conv)
 454{
 455    char mbch[MB_LEN_MAX];
 456    wchar_t wch[2];
 457    char *pfch, *pmbch;
 458    size_t sfch, smbch;
 459    mbstate_t ps;
 460
 461    pfch = (char *) &fch;
 462    pmbch = (char *) &mbch;
 463    sfch = sizeof(fch);
 464    smbch = sizeof(mbch);
 465
 466    if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
 467        fprintf(stderr, "Could not convert font glyph 0x%02x "
 468                        "from %s to a multibyte character: %s\n",
 469                        fch, font_charset, strerror(errno));
 470        return;
 471    }
 472
 473    memset(&ps, 0, sizeof(ps));
 474    if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
 475        fprintf(stderr, "Could not convert font glyph 0x%02x "
 476                        "from a multibyte character to wchar_t: %s\n",
 477                        fch, strerror(errno));
 478        return;
 479    }
 480
 481    wch[1] = 0;
 482    setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
 483}
 484
 485/* Convert one wchar to UCS-2 */
 486static uint16_t get_ucs(wchar_t wch, iconv_t conv)
 487{
 488    char mbch[MB_LEN_MAX];
 489    uint16_t uch;
 490    char *pmbch, *puch;
 491    size_t smbch, such;
 492    mbstate_t ps;
 493    int ret;
 494
 495    memset(&ps, 0, sizeof(ps));
 496    ret = wcrtomb(mbch, wch, &ps);
 497    if (ret == -1) {
 498        fprintf(stderr, "Could not convert 0x%04lx "
 499                        "from wchar_t to a multibyte character: %s\n",
 500                        (unsigned long)wch, strerror(errno));
 501        return 0xFFFD;
 502    }
 503
 504    pmbch = (char *) mbch;
 505    puch = (char *) &uch;
 506    smbch = ret;
 507    such = sizeof(uch);
 508
 509    if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
 510        fprintf(stderr, "Could not convert 0x%04lx "
 511                        "from a multibyte character to UCS-2 : %s\n",
 512                        (unsigned long)wch, strerror(errno));
 513        return 0xFFFD;
 514    }
 515
 516    return uch;
 517}
 518
 519/*
 520 * Setup mapping for vga to curses line graphics.
 521 */
 522static void font_setup(void)
 523{
 524    iconv_t ucs2_to_nativecharset;
 525    iconv_t nativecharset_to_ucs2;
 526    iconv_t font_conv;
 527    int i;
 528    g_autofree gchar *local_codeset = g_get_codeset();
 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(local_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", local_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(local_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(local_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