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 50 enum maybe_keycode { 51 CURSES_KEYCODE, 52 CURSES_CHAR, 53 CURSES_CHAR_OR_KEYCODE, 54 }; 55 56 static DisplayChangeListener *dcl; 57 static console_ch_t screen[160 * 100]; 58 static WINDOW *screenpad = NULL; 59 static int width, height, gwidth, gheight, invalidate; 60 static int px, py, sminx, sminy, smaxx, smaxy; 61 62 static const char *font_charset = "CP437"; 63 static cchar_t vga_to_curses[256]; 64 65 static 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 96 static 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 135 static 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) 149 static volatile sig_atomic_t got_sigwinch; 150 static 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 172 static void curses_winch_handler(int signum) 173 { 174 got_sigwinch = true; 175 } 176 177 static 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 185 static void curses_winch_check(void) {} 186 static void curses_winch_init(void) {} 187 #endif 188 189 static 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 215 static kbd_layout_t *kbd_layout = NULL; 216 217 static 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 236 static 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 263 static 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 405 static 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 */ 419 static 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 */ 452 static 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 */ 485 static 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 */ 521 static 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 720 static 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 752 static 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 765 static 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 773 static 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 799 static QemuDisplay qemu_display_curses = { 800 .type = DISPLAY_TYPE_CURSES, 801 .init = curses_display_init, 802 }; 803 804 static void register_curses(void) 805 { 806 qemu_display_register(&qemu_display_curses); 807 } 808 809 type_init(register_curses); 810