xref: /openbmc/qemu/ui/curses.c (revision 7f6c3d1a)
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;
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;
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     g_free(vga_to_curses);
409     g_free(screen);
410 }
411 
412 /*
413  * In the following:
414  * - fch is the font glyph number
415  * - uch is the unicode value
416  * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
417  * - mbch is the native local-dependent multibyte representation
418  */
419 
420 /* Setup wchar glyph for one UCS-2 char */
421 static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
422 {
423     char mbch[MB_LEN_MAX];
424     wchar_t wch[2];
425     char *puch, *pmbch;
426     size_t such, smbch;
427     mbstate_t ps;
428 
429     puch = (char *) &uch;
430     pmbch = (char *) mbch;
431     such = sizeof(uch);
432     smbch = sizeof(mbch);
433 
434     if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
435         fprintf(stderr, "Could not convert 0x%04x "
436                         "from UCS-2 to a multibyte character: %s\n",
437                         uch, strerror(errno));
438         return;
439     }
440 
441     memset(&ps, 0, sizeof(ps));
442     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
443         fprintf(stderr, "Could not convert 0x%04x "
444                         "from a multibyte character to wchar_t: %s\n",
445                         uch, strerror(errno));
446         return;
447     }
448 
449     wch[1] = 0;
450     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
451 }
452 
453 /* Setup wchar glyph for one font character */
454 static void convert_font(unsigned char fch, iconv_t conv)
455 {
456     char mbch[MB_LEN_MAX];
457     wchar_t wch[2];
458     char *pfch, *pmbch;
459     size_t sfch, smbch;
460     mbstate_t ps;
461 
462     pfch = (char *) &fch;
463     pmbch = (char *) &mbch;
464     sfch = sizeof(fch);
465     smbch = sizeof(mbch);
466 
467     if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
468         fprintf(stderr, "Could not convert font glyph 0x%02x "
469                         "from %s to a multibyte character: %s\n",
470                         fch, font_charset, strerror(errno));
471         return;
472     }
473 
474     memset(&ps, 0, sizeof(ps));
475     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
476         fprintf(stderr, "Could not convert font glyph 0x%02x "
477                         "from a multibyte character to wchar_t: %s\n",
478                         fch, strerror(errno));
479         return;
480     }
481 
482     wch[1] = 0;
483     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
484 }
485 
486 /* Convert one wchar to UCS-2 */
487 static uint16_t get_ucs(wchar_t wch, iconv_t conv)
488 {
489     char mbch[MB_LEN_MAX];
490     uint16_t uch;
491     char *pmbch, *puch;
492     size_t smbch, such;
493     mbstate_t ps;
494     int ret;
495 
496     memset(&ps, 0, sizeof(ps));
497     ret = wcrtomb(mbch, wch, &ps);
498     if (ret == -1) {
499         fprintf(stderr, "Could not convert 0x%04lx "
500                         "from wchar_t to a multibyte character: %s\n",
501                         (unsigned long)wch, strerror(errno));
502         return 0xFFFD;
503     }
504 
505     pmbch = (char *) mbch;
506     puch = (char *) &uch;
507     smbch = ret;
508     such = sizeof(uch);
509 
510     if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
511         fprintf(stderr, "Could not convert 0x%04lx "
512                         "from a multibyte character to UCS-2 : %s\n",
513                         (unsigned long)wch, strerror(errno));
514         return 0xFFFD;
515     }
516 
517     return uch;
518 }
519 
520 /*
521  * Setup mapping for vga to curses line graphics.
522  */
523 static void font_setup(void)
524 {
525     iconv_t ucs2_to_nativecharset;
526     iconv_t nativecharset_to_ucs2;
527     iconv_t font_conv;
528     int i;
529 
530     /*
531      * Control characters are normally non-printable, but VGA does have
532      * well-known glyphs for them.
533      */
534     static const uint16_t control_characters[0x20] = {
535       0x0020,
536       0x263a,
537       0x263b,
538       0x2665,
539       0x2666,
540       0x2663,
541       0x2660,
542       0x2022,
543       0x25d8,
544       0x25cb,
545       0x25d9,
546       0x2642,
547       0x2640,
548       0x266a,
549       0x266b,
550       0x263c,
551       0x25ba,
552       0x25c4,
553       0x2195,
554       0x203c,
555       0x00b6,
556       0x00a7,
557       0x25ac,
558       0x21a8,
559       0x2191,
560       0x2193,
561       0x2192,
562       0x2190,
563       0x221f,
564       0x2194,
565       0x25b2,
566       0x25bc
567     };
568 
569     ucs2_to_nativecharset = iconv_open(nl_langinfo(CODESET), "UCS-2");
570     if (ucs2_to_nativecharset == (iconv_t) -1) {
571         fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
572                         strerror(errno));
573         exit(1);
574     }
575 
576     nativecharset_to_ucs2 = iconv_open("UCS-2", nl_langinfo(CODESET));
577     if (nativecharset_to_ucs2 == (iconv_t) -1) {
578         iconv_close(ucs2_to_nativecharset);
579         fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
580                         strerror(errno));
581         exit(1);
582     }
583 
584     font_conv = iconv_open(nl_langinfo(CODESET), font_charset);
585     if (font_conv == (iconv_t) -1) {
586         iconv_close(ucs2_to_nativecharset);
587         iconv_close(nativecharset_to_ucs2);
588         fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
589                         font_charset, strerror(errno));
590         exit(1);
591     }
592 
593     /* Control characters */
594     for (i = 0; i <= 0x1F; i++) {
595         convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
596     }
597 
598     for (i = 0x20; i <= 0xFF; i++) {
599         convert_font(i, font_conv);
600     }
601 
602     /* DEL */
603     convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
604 
605     if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
606         /* Non-Unicode capable, use termcap equivalents for those available */
607         for (i = 0; i <= 0xFF; i++) {
608             wchar_t wch[CCHARW_MAX];
609             attr_t attr;
610             short color;
611             int ret;
612 
613             ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
614             if (ret == ERR)
615                 continue;
616 
617             switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
618             case 0x00a3:
619                 vga_to_curses[i] = *WACS_STERLING;
620                 break;
621             case 0x2591:
622                 vga_to_curses[i] = *WACS_BOARD;
623                 break;
624             case 0x2592:
625                 vga_to_curses[i] = *WACS_CKBOARD;
626                 break;
627             case 0x2502:
628                 vga_to_curses[i] = *WACS_VLINE;
629                 break;
630             case 0x2524:
631                 vga_to_curses[i] = *WACS_RTEE;
632                 break;
633             case 0x2510:
634                 vga_to_curses[i] = *WACS_URCORNER;
635                 break;
636             case 0x2514:
637                 vga_to_curses[i] = *WACS_LLCORNER;
638                 break;
639             case 0x2534:
640                 vga_to_curses[i] = *WACS_BTEE;
641                 break;
642             case 0x252c:
643                 vga_to_curses[i] = *WACS_TTEE;
644                 break;
645             case 0x251c:
646                 vga_to_curses[i] = *WACS_LTEE;
647                 break;
648             case 0x2500:
649                 vga_to_curses[i] = *WACS_HLINE;
650                 break;
651             case 0x253c:
652                 vga_to_curses[i] = *WACS_PLUS;
653                 break;
654             case 0x256c:
655                 vga_to_curses[i] = *WACS_LANTERN;
656                 break;
657             case 0x256a:
658                 vga_to_curses[i] = *WACS_NEQUAL;
659                 break;
660             case 0x2518:
661                 vga_to_curses[i] = *WACS_LRCORNER;
662                 break;
663             case 0x250c:
664                 vga_to_curses[i] = *WACS_ULCORNER;
665                 break;
666             case 0x2588:
667                 vga_to_curses[i] = *WACS_BLOCK;
668                 break;
669             case 0x03c0:
670                 vga_to_curses[i] = *WACS_PI;
671                 break;
672             case 0x00b1:
673                 vga_to_curses[i] = *WACS_PLMINUS;
674                 break;
675             case 0x2265:
676                 vga_to_curses[i] = *WACS_GEQUAL;
677                 break;
678             case 0x2264:
679                 vga_to_curses[i] = *WACS_LEQUAL;
680                 break;
681             case 0x00b0:
682                 vga_to_curses[i] = *WACS_DEGREE;
683                 break;
684             case 0x25a0:
685                 vga_to_curses[i] = *WACS_BULLET;
686                 break;
687             case 0x2666:
688                 vga_to_curses[i] = *WACS_DIAMOND;
689                 break;
690             case 0x2192:
691                 vga_to_curses[i] = *WACS_RARROW;
692                 break;
693             case 0x2190:
694                 vga_to_curses[i] = *WACS_LARROW;
695                 break;
696             case 0x2191:
697                 vga_to_curses[i] = *WACS_UARROW;
698                 break;
699             case 0x2193:
700                 vga_to_curses[i] = *WACS_DARROW;
701                 break;
702             case 0x23ba:
703                 vga_to_curses[i] = *WACS_S1;
704                 break;
705             case 0x23bb:
706                 vga_to_curses[i] = *WACS_S3;
707                 break;
708             case 0x23bc:
709                 vga_to_curses[i] = *WACS_S7;
710                 break;
711             case 0x23bd:
712                 vga_to_curses[i] = *WACS_S9;
713                 break;
714             }
715         }
716     }
717     iconv_close(ucs2_to_nativecharset);
718     iconv_close(nativecharset_to_ucs2);
719     iconv_close(font_conv);
720 }
721 
722 static void curses_setup(void)
723 {
724     int i, colour_default[8] = {
725         [QEMU_COLOR_BLACK]   = COLOR_BLACK,
726         [QEMU_COLOR_BLUE]    = COLOR_BLUE,
727         [QEMU_COLOR_GREEN]   = COLOR_GREEN,
728         [QEMU_COLOR_CYAN]    = COLOR_CYAN,
729         [QEMU_COLOR_RED]     = COLOR_RED,
730         [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
731         [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
732         [QEMU_COLOR_WHITE]   = COLOR_WHITE,
733     };
734 
735     /* input as raw as possible, let everything be interpreted
736      * by the guest system */
737     initscr(); noecho(); intrflush(stdscr, FALSE);
738     nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
739     start_color(); raw(); scrollok(stdscr, FALSE);
740     set_escdelay(25);
741 
742     /* Make color pair to match color format (3bits bg:3bits fg) */
743     for (i = 0; i < 64; i++) {
744         init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
745     }
746     /* Set default color for more than 64 for safety. */
747     for (i = 64; i < COLOR_PAIRS; i++) {
748         init_pair(i, COLOR_WHITE, COLOR_BLACK);
749     }
750 
751     font_setup();
752 }
753 
754 static void curses_keyboard_setup(void)
755 {
756 #if defined(__APPLE__)
757     /* always use generic keymaps */
758     if (!keyboard_layout)
759         keyboard_layout = "en-us";
760 #endif
761     if(keyboard_layout) {
762         kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
763                                           &error_fatal);
764     }
765 }
766 
767 static const DisplayChangeListenerOps dcl_ops = {
768     .dpy_name        = "curses",
769     .dpy_text_update = curses_update,
770     .dpy_text_resize = curses_resize,
771     .dpy_refresh     = curses_refresh,
772     .dpy_text_cursor = curses_cursor_position,
773 };
774 
775 static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
776 {
777 #ifndef _WIN32
778     if (!isatty(1)) {
779         fprintf(stderr, "We need a terminal output\n");
780         exit(1);
781     }
782 #endif
783 
784     setlocale(LC_CTYPE, "");
785     if (opts->u.curses.charset) {
786         font_charset = opts->u.curses.charset;
787     }
788     screen = g_new0(console_ch_t, 160 * 100);
789     vga_to_curses = g_new0(cchar_t, 256);
790     curses_setup();
791     curses_keyboard_setup();
792     atexit(curses_atexit);
793 
794     curses_winch_init();
795 
796     dcl = g_new0(DisplayChangeListener, 1);
797     dcl->ops = &dcl_ops;
798     register_displaychangelistener(dcl);
799 
800     invalidate = 1;
801 }
802 
803 static QemuDisplay qemu_display_curses = {
804     .type       = DISPLAY_TYPE_CURSES,
805     .init       = curses_display_init,
806 };
807 
808 static void register_curses(void)
809 {
810     qemu_display_register(&qemu_display_curses);
811 }
812 
813 type_init(register_curses);
814