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