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 "system/system.h" 40 41 #ifdef __APPLE__ 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(dcl->con)) { 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(dcl->con)) { 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 wint_t chr = 0; 269 int keysym, keycode, keycode_alt; 270 enum maybe_keycode maybe_keycode = CURSES_KEYCODE; 271 272 curses_winch_check(); 273 274 if (invalidate) { 275 clear(); 276 refresh(); 277 curses_calc_pad(); 278 graphic_hw_invalidate(dcl->con); 279 invalidate = 0; 280 } 281 282 graphic_hw_text_update(dcl->con, screen); 283 284 while (1) { 285 /* while there are any pending key strokes to process */ 286 chr = console_getch(&maybe_keycode); 287 288 if (chr == WEOF) { 289 break; 290 } 291 292 #ifdef KEY_RESIZE 293 /* this shouldn't occur when we use a custom SIGWINCH handler */ 294 if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) { 295 clear(); 296 refresh(); 297 curses_calc_pad(); 298 curses_update(dcl, 0, 0, width, height); 299 continue; 300 } 301 #endif 302 303 keycode = curses2keycode(chr, maybe_keycode); 304 keycode_alt = 0; 305 306 /* alt or esc key */ 307 if (keycode == 1) { 308 enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE; 309 wint_t nextchr = console_getch(&next_maybe_keycode); 310 311 if (nextchr != WEOF) { 312 chr = nextchr; 313 maybe_keycode = next_maybe_keycode; 314 keycode_alt = ALT; 315 keycode = curses2keycode(chr, maybe_keycode); 316 317 if (keycode != -1) { 318 keycode |= ALT; 319 320 /* process keys reserved for qemu */ 321 if (keycode >= QEMU_KEY_CONSOLE0 && 322 keycode < QEMU_KEY_CONSOLE0 + 9) { 323 QemuConsole *con = qemu_console_lookup_by_index(keycode - QEMU_KEY_CONSOLE0); 324 if (con) { 325 erase(); 326 wnoutrefresh(stdscr); 327 unregister_displaychangelistener(dcl); 328 dcl->con = con; 329 register_displaychangelistener(dcl); 330 331 invalidate = 1; 332 } 333 continue; 334 } 335 } 336 } 337 } 338 339 if (kbd_layout) { 340 keysym = curses2keysym(chr, maybe_keycode); 341 342 if (keysym == -1) { 343 if (chr < ' ') { 344 keysym = chr + '@'; 345 if (keysym >= 'A' && keysym <= 'Z') 346 keysym += 'a' - 'A'; 347 keysym |= KEYSYM_CNTRL; 348 } else 349 keysym = chr; 350 } 351 352 keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK, 353 NULL, false); 354 if (keycode == 0) 355 continue; 356 357 keycode |= (keysym & ~KEYSYM_MASK) >> 16; 358 keycode |= keycode_alt; 359 } 360 361 if (keycode == -1) 362 continue; 363 364 if (qemu_console_is_graphic(dcl->con)) { 365 /* since terminals don't know about key press and release 366 * events, we need to emit both for each key received */ 367 if (keycode & SHIFT) { 368 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, true); 369 qemu_input_event_send_key_delay(0); 370 } 371 if (keycode & CNTRL) { 372 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, true); 373 qemu_input_event_send_key_delay(0); 374 } 375 if (keycode & ALT) { 376 qemu_input_event_send_key_number(dcl->con, ALT_CODE, true); 377 qemu_input_event_send_key_delay(0); 378 } 379 if (keycode & ALTGR) { 380 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, true); 381 qemu_input_event_send_key_delay(0); 382 } 383 384 qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, true); 385 qemu_input_event_send_key_delay(0); 386 qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, false); 387 qemu_input_event_send_key_delay(0); 388 389 if (keycode & ALTGR) { 390 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, false); 391 qemu_input_event_send_key_delay(0); 392 } 393 if (keycode & ALT) { 394 qemu_input_event_send_key_number(dcl->con, ALT_CODE, false); 395 qemu_input_event_send_key_delay(0); 396 } 397 if (keycode & CNTRL) { 398 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, false); 399 qemu_input_event_send_key_delay(0); 400 } 401 if (keycode & SHIFT) { 402 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, false); 403 qemu_input_event_send_key_delay(0); 404 } 405 } else { 406 keysym = curses2qemu(chr, maybe_keycode); 407 if (keysym == -1) 408 keysym = chr; 409 410 qemu_text_console_put_keysym(QEMU_TEXT_CONSOLE(dcl->con), keysym); 411 } 412 } 413 } 414 415 static void curses_atexit(void) 416 { 417 endwin(); 418 g_free(vga_to_curses); 419 g_free(screen); 420 } 421 422 /* 423 * In the following: 424 * - fch is the font glyph number 425 * - uch is the unicode value 426 * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris) 427 * - mbch is the native local-dependent multibyte representation 428 */ 429 430 /* Setup wchar glyph for one UCS-2 char */ 431 static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv) 432 { 433 char mbch[MB_LEN_MAX]; 434 wchar_t wch[2]; 435 char *puch, *pmbch; 436 size_t such, smbch; 437 mbstate_t ps; 438 439 puch = (char *) &uch; 440 pmbch = (char *) mbch; 441 such = sizeof(uch); 442 smbch = sizeof(mbch); 443 444 if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) { 445 fprintf(stderr, "Could not convert 0x%04x " 446 "from UCS-2 to a multibyte character: %s\n", 447 uch, strerror(errno)); 448 return; 449 } 450 451 memset(&ps, 0, sizeof(ps)); 452 if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) { 453 fprintf(stderr, "Could not convert 0x%04x " 454 "from a multibyte character to wchar_t: %s\n", 455 uch, strerror(errno)); 456 return; 457 } 458 459 wch[1] = 0; 460 setcchar(&vga_to_curses[fch], wch, 0, 0, NULL); 461 } 462 463 /* Setup wchar glyph for one font character */ 464 static void convert_font(unsigned char fch, iconv_t conv) 465 { 466 char mbch[MB_LEN_MAX]; 467 wchar_t wch[2]; 468 char *pfch, *pmbch; 469 size_t sfch, smbch; 470 mbstate_t ps; 471 472 pfch = (char *) &fch; 473 pmbch = (char *) &mbch; 474 sfch = sizeof(fch); 475 smbch = sizeof(mbch); 476 477 if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) { 478 fprintf(stderr, "Could not convert font glyph 0x%02x " 479 "from %s to a multibyte character: %s\n", 480 fch, font_charset, strerror(errno)); 481 return; 482 } 483 484 memset(&ps, 0, sizeof(ps)); 485 if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) { 486 fprintf(stderr, "Could not convert font glyph 0x%02x " 487 "from a multibyte character to wchar_t: %s\n", 488 fch, strerror(errno)); 489 return; 490 } 491 492 wch[1] = 0; 493 setcchar(&vga_to_curses[fch], wch, 0, 0, NULL); 494 } 495 496 /* Convert one wchar to UCS-2 */ 497 static uint16_t get_ucs(wchar_t wch, iconv_t conv) 498 { 499 char mbch[MB_LEN_MAX]; 500 uint16_t uch; 501 char *pmbch, *puch; 502 size_t smbch, such; 503 mbstate_t ps; 504 int ret; 505 506 memset(&ps, 0, sizeof(ps)); 507 ret = wcrtomb(mbch, wch, &ps); 508 if (ret == -1) { 509 fprintf(stderr, "Could not convert 0x%04lx " 510 "from wchar_t to a multibyte character: %s\n", 511 (unsigned long)wch, strerror(errno)); 512 return 0xFFFD; 513 } 514 515 pmbch = (char *) mbch; 516 puch = (char *) &uch; 517 smbch = ret; 518 such = sizeof(uch); 519 520 if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) { 521 fprintf(stderr, "Could not convert 0x%04lx " 522 "from a multibyte character to UCS-2 : %s\n", 523 (unsigned long)wch, strerror(errno)); 524 return 0xFFFD; 525 } 526 527 return uch; 528 } 529 530 /* 531 * Setup mapping for vga to curses line graphics. 532 */ 533 static void font_setup(void) 534 { 535 iconv_t ucs2_to_nativecharset; 536 iconv_t nativecharset_to_ucs2; 537 iconv_t font_conv; 538 int i; 539 g_autofree gchar *local_codeset = g_get_codeset(); 540 541 /* 542 * Control characters are normally non-printable, but VGA does have 543 * well-known glyphs for them. 544 */ 545 static const uint16_t control_characters[0x20] = { 546 0x0020, 547 0x263a, 548 0x263b, 549 0x2665, 550 0x2666, 551 0x2663, 552 0x2660, 553 0x2022, 554 0x25d8, 555 0x25cb, 556 0x25d9, 557 0x2642, 558 0x2640, 559 0x266a, 560 0x266b, 561 0x263c, 562 0x25ba, 563 0x25c4, 564 0x2195, 565 0x203c, 566 0x00b6, 567 0x00a7, 568 0x25ac, 569 0x21a8, 570 0x2191, 571 0x2193, 572 0x2192, 573 0x2190, 574 0x221f, 575 0x2194, 576 0x25b2, 577 0x25bc 578 }; 579 580 ucs2_to_nativecharset = iconv_open(local_codeset, "UCS-2"); 581 if (ucs2_to_nativecharset == (iconv_t) -1) { 582 fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n", 583 strerror(errno)); 584 exit(1); 585 } 586 587 nativecharset_to_ucs2 = iconv_open("UCS-2", local_codeset); 588 if (nativecharset_to_ucs2 == (iconv_t) -1) { 589 iconv_close(ucs2_to_nativecharset); 590 fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n", 591 strerror(errno)); 592 exit(1); 593 } 594 595 font_conv = iconv_open(local_codeset, font_charset); 596 if (font_conv == (iconv_t) -1) { 597 iconv_close(ucs2_to_nativecharset); 598 iconv_close(nativecharset_to_ucs2); 599 fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n", 600 font_charset, strerror(errno)); 601 exit(1); 602 } 603 604 /* Control characters */ 605 for (i = 0; i <= 0x1F; i++) { 606 convert_ucs(i, control_characters[i], ucs2_to_nativecharset); 607 } 608 609 for (i = 0x20; i <= 0xFF; i++) { 610 convert_font(i, font_conv); 611 } 612 613 /* DEL */ 614 convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset); 615 616 if (strcmp(local_codeset, "UTF-8")) { 617 /* Non-Unicode capable, use termcap equivalents for those available */ 618 for (i = 0; i <= 0xFF; i++) { 619 wchar_t wch[CCHARW_MAX]; 620 attr_t attr; 621 short color; 622 int ret; 623 624 ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL); 625 if (ret == ERR) 626 continue; 627 628 switch (get_ucs(wch[0], nativecharset_to_ucs2)) { 629 case 0x00a3: 630 vga_to_curses[i] = *WACS_STERLING; 631 break; 632 case 0x2591: 633 vga_to_curses[i] = *WACS_BOARD; 634 break; 635 case 0x2592: 636 vga_to_curses[i] = *WACS_CKBOARD; 637 break; 638 case 0x2502: 639 vga_to_curses[i] = *WACS_VLINE; 640 break; 641 case 0x2524: 642 vga_to_curses[i] = *WACS_RTEE; 643 break; 644 case 0x2510: 645 vga_to_curses[i] = *WACS_URCORNER; 646 break; 647 case 0x2514: 648 vga_to_curses[i] = *WACS_LLCORNER; 649 break; 650 case 0x2534: 651 vga_to_curses[i] = *WACS_BTEE; 652 break; 653 case 0x252c: 654 vga_to_curses[i] = *WACS_TTEE; 655 break; 656 case 0x251c: 657 vga_to_curses[i] = *WACS_LTEE; 658 break; 659 case 0x2500: 660 vga_to_curses[i] = *WACS_HLINE; 661 break; 662 case 0x253c: 663 vga_to_curses[i] = *WACS_PLUS; 664 break; 665 case 0x256c: 666 vga_to_curses[i] = *WACS_LANTERN; 667 break; 668 case 0x256a: 669 vga_to_curses[i] = *WACS_NEQUAL; 670 break; 671 case 0x2518: 672 vga_to_curses[i] = *WACS_LRCORNER; 673 break; 674 case 0x250c: 675 vga_to_curses[i] = *WACS_ULCORNER; 676 break; 677 case 0x2588: 678 vga_to_curses[i] = *WACS_BLOCK; 679 break; 680 case 0x03c0: 681 vga_to_curses[i] = *WACS_PI; 682 break; 683 case 0x00b1: 684 vga_to_curses[i] = *WACS_PLMINUS; 685 break; 686 case 0x2265: 687 vga_to_curses[i] = *WACS_GEQUAL; 688 break; 689 case 0x2264: 690 vga_to_curses[i] = *WACS_LEQUAL; 691 break; 692 case 0x00b0: 693 vga_to_curses[i] = *WACS_DEGREE; 694 break; 695 case 0x25a0: 696 vga_to_curses[i] = *WACS_BULLET; 697 break; 698 case 0x2666: 699 vga_to_curses[i] = *WACS_DIAMOND; 700 break; 701 case 0x2192: 702 vga_to_curses[i] = *WACS_RARROW; 703 break; 704 case 0x2190: 705 vga_to_curses[i] = *WACS_LARROW; 706 break; 707 case 0x2191: 708 vga_to_curses[i] = *WACS_UARROW; 709 break; 710 case 0x2193: 711 vga_to_curses[i] = *WACS_DARROW; 712 break; 713 case 0x23ba: 714 vga_to_curses[i] = *WACS_S1; 715 break; 716 case 0x23bb: 717 vga_to_curses[i] = *WACS_S3; 718 break; 719 case 0x23bc: 720 vga_to_curses[i] = *WACS_S7; 721 break; 722 case 0x23bd: 723 vga_to_curses[i] = *WACS_S9; 724 break; 725 } 726 } 727 } 728 iconv_close(ucs2_to_nativecharset); 729 iconv_close(nativecharset_to_ucs2); 730 iconv_close(font_conv); 731 } 732 733 static void curses_setup(void) 734 { 735 int i, colour_default[8] = { 736 [QEMU_COLOR_BLACK] = COLOR_BLACK, 737 [QEMU_COLOR_BLUE] = COLOR_BLUE, 738 [QEMU_COLOR_GREEN] = COLOR_GREEN, 739 [QEMU_COLOR_CYAN] = COLOR_CYAN, 740 [QEMU_COLOR_RED] = COLOR_RED, 741 [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA, 742 [QEMU_COLOR_YELLOW] = COLOR_YELLOW, 743 [QEMU_COLOR_WHITE] = COLOR_WHITE, 744 }; 745 746 /* input as raw as possible, let everything be interpreted 747 * by the guest system */ 748 initscr(); noecho(); intrflush(stdscr, FALSE); 749 nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); 750 start_color(); raw(); scrollok(stdscr, FALSE); 751 set_escdelay(25); 752 753 /* Make color pair to match color format (3bits bg:3bits fg) */ 754 for (i = 0; i < 64; i++) { 755 init_pair(i, colour_default[i & 7], colour_default[i >> 3]); 756 } 757 /* Set default color for more than 64 for safety. */ 758 for (i = 64; i < COLOR_PAIRS; i++) { 759 init_pair(i, COLOR_WHITE, COLOR_BLACK); 760 } 761 762 font_setup(); 763 } 764 765 static void curses_keyboard_setup(void) 766 { 767 #if defined(__APPLE__) 768 /* always use generic keymaps */ 769 if (!keyboard_layout) 770 keyboard_layout = "en-us"; 771 #endif 772 if(keyboard_layout) { 773 kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout, 774 &error_fatal); 775 } 776 } 777 778 static const DisplayChangeListenerOps dcl_ops = { 779 .dpy_name = "curses", 780 .dpy_text_update = curses_update, 781 .dpy_text_resize = curses_resize, 782 .dpy_refresh = curses_refresh, 783 .dpy_text_cursor = curses_cursor_position, 784 }; 785 786 static void curses_display_init(DisplayState *ds, DisplayOptions *opts) 787 { 788 #ifndef _WIN32 789 if (!isatty(1)) { 790 fprintf(stderr, "We need a terminal output\n"); 791 exit(1); 792 } 793 #endif 794 795 setlocale(LC_CTYPE, ""); 796 if (opts->u.curses.charset) { 797 font_charset = opts->u.curses.charset; 798 } 799 screen = g_new0(console_ch_t, 160 * 100); 800 vga_to_curses = g_new0(cchar_t, 256); 801 curses_setup(); 802 curses_keyboard_setup(); 803 atexit(curses_atexit); 804 805 curses_winch_init(); 806 807 dcl = g_new0(DisplayChangeListener, 1); 808 dcl->con = qemu_console_lookup_default(); 809 dcl->ops = &dcl_ops; 810 register_displaychangelistener(dcl); 811 812 invalidate = 1; 813 } 814 815 static QemuDisplay qemu_display_curses = { 816 .type = DISPLAY_TYPE_CURSES, 817 .init = curses_display_init, 818 }; 819 820 static void register_curses(void) 821 { 822 qemu_display_register(&qemu_display_curses); 823 } 824 825 type_init(register_curses); 826