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