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 49 enum maybe_keycode { 50 CURSES_KEYCODE, 51 CURSES_CHAR, 52 CURSES_CHAR_OR_KEYCODE, 53 }; 54 55 static DisplayChangeListener *dcl; 56 static console_ch_t *screen; 57 static WINDOW *screenpad = NULL; 58 static int width, height, gwidth, gheight, invalidate; 59 static int px, py, sminx, sminy, smaxx, smaxy; 60 61 static const char *font_charset = "CP437"; 62 static cchar_t *vga_to_curses; 63 64 static 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 95 static 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 134 static 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) 148 static volatile sig_atomic_t got_sigwinch; 149 static 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 171 static void curses_winch_handler(int signum) 172 { 173 got_sigwinch = true; 174 } 175 176 static 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 184 static void curses_winch_check(void) {} 185 static void curses_winch_init(void) {} 186 #endif 187 188 static 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 214 static kbd_layout_t *kbd_layout = NULL; 215 216 static 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 235 static 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 262 static 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 404 static 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 */ 420 static 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 */ 453 static 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 */ 486 static 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 */ 522 static 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 722 static 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 754 static 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 767 static 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 775 static 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 803 static QemuDisplay qemu_display_curses = { 804 .type = DISPLAY_TYPE_CURSES, 805 .init = curses_display_init, 806 }; 807 808 static void register_curses(void) 809 { 810 qemu_display_register(&qemu_display_curses); 811 } 812 813 type_init(register_curses); 814