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] & 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}
 409
 410/*
 411 * In the following:
 412 * - fch is the font glyph number
 413 * - uch is the unicode value
 414 * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
 415 * - mbch is the native local-dependent multibyte representation
 416 */
 417
 418/* Setup wchar glyph for one UCS-2 char */
 419static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
 420{
 421    char mbch[MB_LEN_MAX];
 422    wchar_t wch[2];
 423    char *puch, *pmbch;
 424    size_t such, smbch;
 425    mbstate_t ps;
 426
 427    puch = (char *) &uch;
 428    pmbch = (char *) mbch;
 429    such = sizeof(uch);
 430    smbch = sizeof(mbch);
 431
 432    if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
 433        fprintf(stderr, "Could not convert 0x%04x "
 434                        "from UCS-2 to a multibyte character: %s\n",
 435                        uch, strerror(errno));
 436        return;
 437    }
 438
 439    memset(&ps, 0, sizeof(ps));
 440    if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
 441        fprintf(stderr, "Could not convert 0x%04x "
 442                        "from a multibyte character to wchar_t: %s\n",
 443                        uch, strerror(errno));
 444        return;
 445    }
 446
 447    wch[1] = 0;
 448    setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
 449}
 450
 451/* Setup wchar glyph for one font character */
 452static void convert_font(unsigned char fch, iconv_t conv)
 453{
 454    char mbch[MB_LEN_MAX];
 455    wchar_t wch[2];
 456    char *pfch, *pmbch;
 457    size_t sfch, smbch;
 458    mbstate_t ps;
 459
 460    pfch = (char *) &fch;
 461    pmbch = (char *) &mbch;
 462    sfch = sizeof(fch);
 463    smbch = sizeof(mbch);
 464
 465    if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
 466        fprintf(stderr, "Could not convert font glyph 0x%02x "
 467                        "from %s to a multibyte character: %s\n",
 468                        fch, font_charset, strerror(errno));
 469        return;
 470    }
 471
 472    memset(&ps, 0, sizeof(ps));
 473    if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
 474        fprintf(stderr, "Could not convert font glyph 0x%02x "
 475                        "from a multibyte character to wchar_t: %s\n",
 476                        fch, strerror(errno));
 477        return;
 478    }
 479
 480    wch[1] = 0;
 481    setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
 482}
 483
 484/* Convert one wchar to UCS-2 */
 485static uint16_t get_ucs(wchar_t wch, iconv_t conv)
 486{
 487    char mbch[MB_LEN_MAX];
 488    uint16_t uch;
 489    char *pmbch, *puch;
 490    size_t smbch, such;
 491    mbstate_t ps;
 492    int ret;
 493
 494    memset(&ps, 0, sizeof(ps));
 495    ret = wcrtomb(mbch, wch, &ps);
 496    if (ret == -1) {
 497        fprintf(stderr, "Could not convert 0x%04lx "
 498                        "from wchar_t to a multibyte character: %s\n",
 499                        (unsigned long)wch, strerror(errno));
 500        return 0xFFFD;
 501    }
 502
 503    pmbch = (char *) mbch;
 504    puch = (char *) &uch;
 505    smbch = ret;
 506    such = sizeof(uch);
 507
 508    if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
 509        fprintf(stderr, "Could not convert 0x%04lx "
 510                        "from a multibyte character to UCS-2 : %s\n",
 511                        (unsigned long)wch, strerror(errno));
 512        return 0xFFFD;
 513    }
 514
 515    return uch;
 516}
 517
 518/*
 519 * Setup mapping for vga to curses line graphics.
 520 */
 521static void font_setup(void)
 522{
 523    iconv_t ucs2_to_nativecharset;
 524    iconv_t nativecharset_to_ucs2;
 525    iconv_t font_conv;
 526    int i;
 527
 528    /*
 529     * Control characters are normally non-printable, but VGA does have
 530     * well-known glyphs for them.
 531     */
 532    static uint16_t control_characters[0x20] = {
 533      0x0020,
 534      0x263a,
 535      0x263b,
 536      0x2665,
 537      0x2666,
 538      0x2663,
 539      0x2660,
 540      0x2022,
 541      0x25d8,
 542      0x25cb,
 543      0x25d9,
 544      0x2642,
 545      0x2640,
 546      0x266a,
 547      0x266b,
 548      0x263c,
 549      0x25ba,
 550      0x25c4,
 551      0x2195,
 552      0x203c,
 553      0x00b6,
 554      0x00a7,
 555      0x25ac,
 556      0x21a8,
 557      0x2191,
 558      0x2193,
 559      0x2192,
 560      0x2190,
 561      0x221f,
 562      0x2194,
 563      0x25b2,
 564      0x25bc
 565    };
 566
 567    ucs2_to_nativecharset = iconv_open(nl_langinfo(CODESET), "UCS-2");
 568    if (ucs2_to_nativecharset == (iconv_t) -1) {
 569        fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
 570                        strerror(errno));
 571        exit(1);
 572    }
 573
 574    nativecharset_to_ucs2 = iconv_open("UCS-2", nl_langinfo(CODESET));
 575    if (nativecharset_to_ucs2 == (iconv_t) -1) {
 576        iconv_close(ucs2_to_nativecharset);
 577        fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
 578                        strerror(errno));
 579        exit(1);
 580    }
 581
 582    font_conv = iconv_open(nl_langinfo(CODESET), font_charset);
 583    if (font_conv == (iconv_t) -1) {
 584        iconv_close(ucs2_to_nativecharset);
 585        iconv_close(nativecharset_to_ucs2);
 586        fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
 587                        font_charset, strerror(errno));
 588        exit(1);
 589    }
 590
 591    /* Control characters */
 592    for (i = 0; i <= 0x1F; i++) {
 593        convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
 594    }
 595
 596    for (i = 0x20; i <= 0xFF; i++) {
 597        convert_font(i, font_conv);
 598    }
 599
 600    /* DEL */
 601    convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
 602
 603    if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
 604        /* Non-Unicode capable, use termcap equivalents for those available */
 605        for (i = 0; i <= 0xFF; i++) {
 606            wchar_t wch[CCHARW_MAX];
 607            attr_t attr;
 608            short color;
 609            int ret;
 610
 611            ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
 612            if (ret == ERR)
 613                continue;
 614
 615            switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
 616            case 0x00a3:
 617                vga_to_curses[i] = *WACS_STERLING;
 618                break;
 619            case 0x2591:
 620                vga_to_curses[i] = *WACS_BOARD;
 621                break;
 622            case 0x2592:
 623                vga_to_curses[i] = *WACS_CKBOARD;
 624                break;
 625            case 0x2502:
 626                vga_to_curses[i] = *WACS_VLINE;
 627                break;
 628            case 0x2524:
 629                vga_to_curses[i] = *WACS_RTEE;
 630                break;
 631            case 0x2510:
 632                vga_to_curses[i] = *WACS_URCORNER;
 633                break;
 634            case 0x2514:
 635                vga_to_curses[i] = *WACS_LLCORNER;
 636                break;
 637            case 0x2534:
 638                vga_to_curses[i] = *WACS_BTEE;
 639                break;
 640            case 0x252c:
 641                vga_to_curses[i] = *WACS_TTEE;
 642                break;
 643            case 0x251c:
 644                vga_to_curses[i] = *WACS_LTEE;
 645                break;
 646            case 0x2500:
 647                vga_to_curses[i] = *WACS_HLINE;
 648                break;
 649            case 0x253c:
 650                vga_to_curses[i] = *WACS_PLUS;
 651                break;
 652            case 0x256c:
 653                vga_to_curses[i] = *WACS_LANTERN;
 654                break;
 655            case 0x256a:
 656                vga_to_curses[i] = *WACS_NEQUAL;
 657                break;
 658            case 0x2518:
 659                vga_to_curses[i] = *WACS_LRCORNER;
 660                break;
 661            case 0x250c:
 662                vga_to_curses[i] = *WACS_ULCORNER;
 663                break;
 664            case 0x2588:
 665                vga_to_curses[i] = *WACS_BLOCK;
 666                break;
 667            case 0x03c0:
 668                vga_to_curses[i] = *WACS_PI;
 669                break;
 670            case 0x00b1:
 671                vga_to_curses[i] = *WACS_PLMINUS;
 672                break;
 673            case 0x2265:
 674                vga_to_curses[i] = *WACS_GEQUAL;
 675                break;
 676            case 0x2264:
 677                vga_to_curses[i] = *WACS_LEQUAL;
 678                break;
 679            case 0x00b0:
 680                vga_to_curses[i] = *WACS_DEGREE;
 681                break;
 682            case 0x25a0:
 683                vga_to_curses[i] = *WACS_BULLET;
 684                break;
 685            case 0x2666:
 686                vga_to_curses[i] = *WACS_DIAMOND;
 687                break;
 688            case 0x2192:
 689                vga_to_curses[i] = *WACS_RARROW;
 690                break;
 691            case 0x2190:
 692                vga_to_curses[i] = *WACS_LARROW;
 693                break;
 694            case 0x2191:
 695                vga_to_curses[i] = *WACS_UARROW;
 696                break;
 697            case 0x2193:
 698                vga_to_curses[i] = *WACS_DARROW;
 699                break;
 700            case 0x23ba:
 701                vga_to_curses[i] = *WACS_S1;
 702                break;
 703            case 0x23bb:
 704                vga_to_curses[i] = *WACS_S3;
 705                break;
 706            case 0x23bc:
 707                vga_to_curses[i] = *WACS_S7;
 708                break;
 709            case 0x23bd:
 710                vga_to_curses[i] = *WACS_S9;
 711                break;
 712            }
 713        }
 714    }
 715    iconv_close(ucs2_to_nativecharset);
 716    iconv_close(nativecharset_to_ucs2);
 717    iconv_close(font_conv);
 718}
 719
 720static void curses_setup(void)
 721{
 722    int i, colour_default[8] = {
 723        [QEMU_COLOR_BLACK]   = COLOR_BLACK,
 724        [QEMU_COLOR_BLUE]    = COLOR_BLUE,
 725        [QEMU_COLOR_GREEN]   = COLOR_GREEN,
 726        [QEMU_COLOR_CYAN]    = COLOR_CYAN,
 727        [QEMU_COLOR_RED]     = COLOR_RED,
 728        [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
 729        [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
 730        [QEMU_COLOR_WHITE]   = COLOR_WHITE,
 731    };
 732
 733    /* input as raw as possible, let everything be interpreted
 734     * by the guest system */
 735    initscr(); noecho(); intrflush(stdscr, FALSE);
 736    nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
 737    start_color(); raw(); scrollok(stdscr, FALSE);
 738    set_escdelay(25);
 739
 740    /* Make color pair to match color format (3bits bg:3bits fg) */
 741    for (i = 0; i < 64; i++) {
 742        init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
 743    }
 744    /* Set default color for more than 64 for safety. */
 745    for (i = 64; i < COLOR_PAIRS; i++) {
 746        init_pair(i, COLOR_WHITE, COLOR_BLACK);
 747    }
 748
 749    font_setup();
 750}
 751
 752static void curses_keyboard_setup(void)
 753{
 754#if defined(__APPLE__)
 755    /* always use generic keymaps */
 756    if (!keyboard_layout)
 757        keyboard_layout = "en-us";
 758#endif
 759    if(keyboard_layout) {
 760        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
 761                                          &error_fatal);
 762    }
 763}
 764
 765static const DisplayChangeListenerOps dcl_ops = {
 766    .dpy_name        = "curses",
 767    .dpy_text_update = curses_update,
 768    .dpy_text_resize = curses_resize,
 769    .dpy_refresh     = curses_refresh,
 770    .dpy_text_cursor = curses_cursor_position,
 771};
 772
 773static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
 774{
 775#ifndef _WIN32
 776    if (!isatty(1)) {
 777        fprintf(stderr, "We need a terminal output\n");
 778        exit(1);
 779    }
 780#endif
 781
 782    setlocale(LC_CTYPE, "");
 783    if (opts->u.curses.charset) {
 784        font_charset = opts->u.curses.charset;
 785    }
 786    curses_setup();
 787    curses_keyboard_setup();
 788    atexit(curses_atexit);
 789
 790    curses_winch_init();
 791
 792    dcl = g_new0(DisplayChangeListener, 1);
 793    dcl->ops = &dcl_ops;
 794    register_displaychangelistener(dcl);
 795
 796    invalidate = 1;
 797}
 798
 799static QemuDisplay qemu_display_curses = {
 800    .type       = DISPLAY_TYPE_CURSES,
 801    .init       = curses_display_init,
 802};
 803
 804static void register_curses(void)
 805{
 806    qemu_display_register(&qemu_display_curses);
 807}
 808
 809type_init(register_curses);
 810