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