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