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