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