xref: /openbmc/qemu/ui/curses.c (revision ddda3748)
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