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