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