xref: /qemu/ui/curses.c (revision 138ca49a)
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 /* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
42 #undef KEY_EVENT
43 #include <curses.h>
44 #undef KEY_EVENT
45 
46 #define FONT_HEIGHT 16
47 #define FONT_WIDTH 8
48 
49 enum maybe_keycode {
50     CURSES_KEYCODE,
51     CURSES_CHAR,
52     CURSES_CHAR_OR_KEYCODE,
53 };
54 
55 static DisplayChangeListener *dcl;
56 static console_ch_t *screen;
57 static WINDOW *screenpad = NULL;
58 static int width, height, gwidth, gheight, invalidate;
59 static int px, py, sminx, sminy, smaxx, smaxy;
60 
61 static const char *font_charset = "CP437";
62 static cchar_t *vga_to_curses;
63 
64 static void curses_update(DisplayChangeListener *dcl,
65                           int x, int y, int w, int h)
66 {
67     console_ch_t *line;
68     cchar_t curses_line[width];
69     wchar_t wch[CCHARW_MAX];
70     attr_t attrs;
71     short colors;
72     int ret;
73 
74     line = screen + y * width;
75     for (h += y; y < h; y ++, line += width) {
76         for (x = 0; x < width; x++) {
77             chtype ch = line[x] & A_CHARTEXT;
78             chtype at = line[x] & A_ATTRIBUTES;
79             short color_pair = PAIR_NUMBER(line[x]);
80 
81             ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
82             if (ret == ERR || wch[0] == 0) {
83                 wch[0] = ch;
84                 wch[1] = 0;
85             }
86             setcchar(&curses_line[x], wch, at, color_pair, NULL);
87         }
88         mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
89     }
90 
91     pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
92     refresh();
93 }
94 
95 static void curses_calc_pad(void)
96 {
97     if (qemu_console_is_fixedsize(NULL)) {
98         width = gwidth;
99         height = gheight;
100     } else {
101         width = COLS;
102         height = LINES;
103     }
104 
105     if (screenpad)
106         delwin(screenpad);
107 
108     clear();
109     refresh();
110 
111     screenpad = newpad(height, width);
112 
113     if (width > COLS) {
114         px = (width - COLS) / 2;
115         sminx = 0;
116         smaxx = COLS;
117     } else {
118         px = 0;
119         sminx = (COLS - width) / 2;
120         smaxx = sminx + width;
121     }
122 
123     if (height > LINES) {
124         py = (height - LINES) / 2;
125         sminy = 0;
126         smaxy = LINES;
127     } else {
128         py = 0;
129         sminy = (LINES - height) / 2;
130         smaxy = sminy + height;
131     }
132 }
133 
134 static void curses_resize(DisplayChangeListener *dcl,
135                           int width, int height)
136 {
137     if (width == gwidth && height == gheight) {
138         return;
139     }
140 
141     gwidth = width;
142     gheight = height;
143 
144     curses_calc_pad();
145 }
146 
147 #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
148 static volatile sig_atomic_t got_sigwinch;
149 static void curses_winch_check(void)
150 {
151     struct winsize {
152         unsigned short ws_row;
153         unsigned short ws_col;
154         unsigned short ws_xpixel;   /* unused */
155         unsigned short ws_ypixel;   /* unused */
156     } ws;
157 
158     if (!got_sigwinch) {
159         return;
160     }
161     got_sigwinch = false;
162 
163     if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
164         return;
165     }
166 
167     resize_term(ws.ws_row, ws.ws_col);
168     invalidate = 1;
169 }
170 
171 static void curses_winch_handler(int signum)
172 {
173     got_sigwinch = true;
174 }
175 
176 static void curses_winch_init(void)
177 {
178     struct sigaction old, winch = {
179         .sa_handler  = curses_winch_handler,
180     };
181     sigaction(SIGWINCH, &winch, &old);
182 }
183 #else
184 static void curses_winch_check(void) {}
185 static void curses_winch_init(void) {}
186 #endif
187 
188 static void curses_cursor_position(DisplayChangeListener *dcl,
189                                    int x, int y)
190 {
191     if (x >= 0) {
192         x = sminx + x - px;
193         y = sminy + y - py;
194 
195         if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
196             move(y, x);
197             curs_set(1);
198             /* it seems that curs_set(1) must always be called before
199              * curs_set(2) for the latter to have effect */
200             if (!qemu_console_is_graphic(NULL)) {
201                 curs_set(2);
202             }
203             return;
204         }
205     }
206 
207     curs_set(0);
208 }
209 
210 /* generic keyboard conversion */
211 
212 #include "curses_keys.h"
213 
214 static kbd_layout_t *kbd_layout = NULL;
215 
216 static wint_t console_getch(enum maybe_keycode *maybe_keycode)
217 {
218     wint_t ret;
219     switch (get_wch(&ret)) {
220     case KEY_CODE_YES:
221         *maybe_keycode = CURSES_KEYCODE;
222         break;
223     case OK:
224         *maybe_keycode = CURSES_CHAR;
225         break;
226     case ERR:
227         ret = -1;
228         break;
229     default:
230         abort();
231     }
232     return ret;
233 }
234 
235 static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
236                       int chr, enum maybe_keycode maybe_keycode)
237 {
238     int ret = -1;
239     if (maybe_keycode == CURSES_CHAR) {
240         if (chr < CURSES_CHARS) {
241             ret = _curses2foo[chr];
242         }
243     } else {
244         if (chr < CURSES_KEYS) {
245             ret = _curseskey2foo[chr];
246         }
247         if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
248             chr < CURSES_CHARS) {
249             ret = _curses2foo[chr];
250         }
251     }
252     return ret;
253 }
254 
255 #define curses2keycode(chr, maybe_keycode) \
256     curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
257 #define curses2keysym(chr, maybe_keycode) \
258     curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
259 #define curses2qemu(chr, maybe_keycode) \
260     curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
261 
262 static void curses_refresh(DisplayChangeListener *dcl)
263 {
264     int chr, keysym, keycode, keycode_alt;
265     enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
266 
267     curses_winch_check();
268 
269     if (invalidate) {
270         clear();
271         refresh();
272         curses_calc_pad();
273         graphic_hw_invalidate(NULL);
274         invalidate = 0;
275     }
276 
277     graphic_hw_text_update(NULL, screen);
278 
279     while (1) {
280         /* while there are any pending key strokes to process */
281         chr = console_getch(&maybe_keycode);
282 
283         if (chr == -1)
284             break;
285 
286 #ifdef KEY_RESIZE
287         /* this shouldn't occur when we use a custom SIGWINCH handler */
288         if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
289             clear();
290             refresh();
291             curses_calc_pad();
292             curses_update(dcl, 0, 0, width, height);
293             continue;
294         }
295 #endif
296 
297         keycode = curses2keycode(chr, maybe_keycode);
298         keycode_alt = 0;
299 
300         /* alt or esc key */
301         if (keycode == 1) {
302             enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
303             int nextchr = console_getch(&next_maybe_keycode);
304 
305             if (nextchr != -1) {
306                 chr = nextchr;
307                 maybe_keycode = next_maybe_keycode;
308                 keycode_alt = ALT;
309                 keycode = curses2keycode(chr, maybe_keycode);
310 
311                 if (keycode != -1) {
312                     keycode |= ALT;
313 
314                     /* process keys reserved for qemu */
315                     if (keycode >= QEMU_KEY_CONSOLE0 &&
316                             keycode < QEMU_KEY_CONSOLE0 + 9) {
317                         erase();
318                         wnoutrefresh(stdscr);
319                         console_select(keycode - QEMU_KEY_CONSOLE0);
320 
321                         invalidate = 1;
322                         continue;
323                     }
324                 }
325             }
326         }
327 
328         if (kbd_layout) {
329             keysym = curses2keysym(chr, maybe_keycode);
330 
331             if (keysym == -1) {
332                 if (chr < ' ') {
333                     keysym = chr + '@';
334                     if (keysym >= 'A' && keysym <= 'Z')
335                         keysym += 'a' - 'A';
336                     keysym |= KEYSYM_CNTRL;
337                 } else
338                     keysym = chr;
339             }
340 
341             keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
342                                       NULL, false);
343             if (keycode == 0)
344                 continue;
345 
346             keycode |= (keysym & ~KEYSYM_MASK) >> 16;
347             keycode |= keycode_alt;
348         }
349 
350         if (keycode == -1)
351             continue;
352 
353         if (qemu_console_is_graphic(NULL)) {
354             /* since terminals don't know about key press and release
355              * events, we need to emit both for each key received */
356             if (keycode & SHIFT) {
357                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
358                 qemu_input_event_send_key_delay(0);
359             }
360             if (keycode & CNTRL) {
361                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
362                 qemu_input_event_send_key_delay(0);
363             }
364             if (keycode & ALT) {
365                 qemu_input_event_send_key_number(NULL, ALT_CODE, true);
366                 qemu_input_event_send_key_delay(0);
367             }
368             if (keycode & ALTGR) {
369                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
370                 qemu_input_event_send_key_delay(0);
371             }
372 
373             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
374             qemu_input_event_send_key_delay(0);
375             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
376             qemu_input_event_send_key_delay(0);
377 
378             if (keycode & ALTGR) {
379                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
380                 qemu_input_event_send_key_delay(0);
381             }
382             if (keycode & ALT) {
383                 qemu_input_event_send_key_number(NULL, ALT_CODE, false);
384                 qemu_input_event_send_key_delay(0);
385             }
386             if (keycode & CNTRL) {
387                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
388                 qemu_input_event_send_key_delay(0);
389             }
390             if (keycode & SHIFT) {
391                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
392                 qemu_input_event_send_key_delay(0);
393             }
394         } else {
395             keysym = curses2qemu(chr, maybe_keycode);
396             if (keysym == -1)
397                 keysym = chr;
398 
399             kbd_put_keysym(keysym);
400         }
401     }
402 }
403 
404 static void curses_atexit(void)
405 {
406     endwin();
407     g_free(vga_to_curses);
408     g_free(screen);
409 }
410 
411 /*
412  * In the following:
413  * - fch is the font glyph number
414  * - uch is the unicode value
415  * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
416  * - mbch is the native local-dependent multibyte representation
417  */
418 
419 /* Setup wchar glyph for one UCS-2 char */
420 static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
421 {
422     char mbch[MB_LEN_MAX];
423     wchar_t wch[2];
424     char *puch, *pmbch;
425     size_t such, smbch;
426     mbstate_t ps;
427 
428     puch = (char *) &uch;
429     pmbch = (char *) mbch;
430     such = sizeof(uch);
431     smbch = sizeof(mbch);
432 
433     if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
434         fprintf(stderr, "Could not convert 0x%04x "
435                         "from UCS-2 to a multibyte character: %s\n",
436                         uch, strerror(errno));
437         return;
438     }
439 
440     memset(&ps, 0, sizeof(ps));
441     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
442         fprintf(stderr, "Could not convert 0x%04x "
443                         "from a multibyte character to wchar_t: %s\n",
444                         uch, strerror(errno));
445         return;
446     }
447 
448     wch[1] = 0;
449     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
450 }
451 
452 /* Setup wchar glyph for one font character */
453 static void convert_font(unsigned char fch, iconv_t conv)
454 {
455     char mbch[MB_LEN_MAX];
456     wchar_t wch[2];
457     char *pfch, *pmbch;
458     size_t sfch, smbch;
459     mbstate_t ps;
460 
461     pfch = (char *) &fch;
462     pmbch = (char *) &mbch;
463     sfch = sizeof(fch);
464     smbch = sizeof(mbch);
465 
466     if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
467         fprintf(stderr, "Could not convert font glyph 0x%02x "
468                         "from %s to a multibyte character: %s\n",
469                         fch, font_charset, strerror(errno));
470         return;
471     }
472 
473     memset(&ps, 0, sizeof(ps));
474     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
475         fprintf(stderr, "Could not convert font glyph 0x%02x "
476                         "from a multibyte character to wchar_t: %s\n",
477                         fch, strerror(errno));
478         return;
479     }
480 
481     wch[1] = 0;
482     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
483 }
484 
485 /* Convert one wchar to UCS-2 */
486 static uint16_t get_ucs(wchar_t wch, iconv_t conv)
487 {
488     char mbch[MB_LEN_MAX];
489     uint16_t uch;
490     char *pmbch, *puch;
491     size_t smbch, such;
492     mbstate_t ps;
493     int ret;
494 
495     memset(&ps, 0, sizeof(ps));
496     ret = wcrtomb(mbch, wch, &ps);
497     if (ret == -1) {
498         fprintf(stderr, "Could not convert 0x%04lx "
499                         "from wchar_t to a multibyte character: %s\n",
500                         (unsigned long)wch, strerror(errno));
501         return 0xFFFD;
502     }
503 
504     pmbch = (char *) mbch;
505     puch = (char *) &uch;
506     smbch = ret;
507     such = sizeof(uch);
508 
509     if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
510         fprintf(stderr, "Could not convert 0x%04lx "
511                         "from a multibyte character to UCS-2 : %s\n",
512                         (unsigned long)wch, strerror(errno));
513         return 0xFFFD;
514     }
515 
516     return uch;
517 }
518 
519 /*
520  * Setup mapping for vga to curses line graphics.
521  */
522 static void font_setup(void)
523 {
524     iconv_t ucs2_to_nativecharset;
525     iconv_t nativecharset_to_ucs2;
526     iconv_t font_conv;
527     int i;
528     g_autofree gchar *local_codeset = g_get_codeset();
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(local_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", local_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(local_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(local_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