xref: /openbmc/qemu/ui/curses.c (revision cd21ee5b27b22ae66c103d36516aa5077881aa3d)
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 "system/system.h"
40 
41 #ifdef __APPLE__
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     wint_t chr = 0;
269     int keysym, keycode, keycode_alt;
270     enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
271 
272     curses_winch_check();
273 
274     if (invalidate) {
275         clear();
276         refresh();
277         curses_calc_pad();
278         graphic_hw_invalidate(dcl->con);
279         invalidate = 0;
280     }
281 
282     graphic_hw_text_update(dcl->con, screen);
283 
284     while (1) {
285         /* while there are any pending key strokes to process */
286         chr = console_getch(&maybe_keycode);
287 
288         if (chr == WEOF) {
289             break;
290         }
291 
292 #ifdef KEY_RESIZE
293         /* this shouldn't occur when we use a custom SIGWINCH handler */
294         if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
295             clear();
296             refresh();
297             curses_calc_pad();
298             curses_update(dcl, 0, 0, width, height);
299             continue;
300         }
301 #endif
302 
303         keycode = curses2keycode(chr, maybe_keycode);
304         keycode_alt = 0;
305 
306         /* alt or esc key */
307         if (keycode == 1) {
308             enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
309             wint_t nextchr = console_getch(&next_maybe_keycode);
310 
311             if (nextchr != WEOF) {
312                 chr = nextchr;
313                 maybe_keycode = next_maybe_keycode;
314                 keycode_alt = ALT;
315                 keycode = curses2keycode(chr, maybe_keycode);
316 
317                 if (keycode != -1) {
318                     keycode |= ALT;
319 
320                     /* process keys reserved for qemu */
321                     if (keycode >= QEMU_KEY_CONSOLE0 &&
322                             keycode < QEMU_KEY_CONSOLE0 + 9) {
323                         QemuConsole *con = qemu_console_lookup_by_index(keycode - QEMU_KEY_CONSOLE0);
324                         if (con) {
325                             erase();
326                             wnoutrefresh(stdscr);
327                             unregister_displaychangelistener(dcl);
328                             dcl->con = con;
329                             register_displaychangelistener(dcl);
330 
331                             invalidate = 1;
332                         }
333                         continue;
334                     }
335                 }
336             }
337         }
338 
339         if (kbd_layout) {
340             keysym = curses2keysym(chr, maybe_keycode);
341 
342             if (keysym == -1) {
343                 if (chr < ' ') {
344                     keysym = chr + '@';
345                     if (keysym >= 'A' && keysym <= 'Z')
346                         keysym += 'a' - 'A';
347                     keysym |= KEYSYM_CNTRL;
348                 } else
349                     keysym = chr;
350             }
351 
352             keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
353                                       NULL, false);
354             if (keycode == 0)
355                 continue;
356 
357             keycode |= (keysym & ~KEYSYM_MASK) >> 16;
358             keycode |= keycode_alt;
359         }
360 
361         if (keycode == -1)
362             continue;
363 
364         if (qemu_console_is_graphic(dcl->con)) {
365             /* since terminals don't know about key press and release
366              * events, we need to emit both for each key received */
367             if (keycode & SHIFT) {
368                 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, true);
369                 qemu_input_event_send_key_delay(0);
370             }
371             if (keycode & CNTRL) {
372                 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, true);
373                 qemu_input_event_send_key_delay(0);
374             }
375             if (keycode & ALT) {
376                 qemu_input_event_send_key_number(dcl->con, ALT_CODE, true);
377                 qemu_input_event_send_key_delay(0);
378             }
379             if (keycode & ALTGR) {
380                 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, true);
381                 qemu_input_event_send_key_delay(0);
382             }
383 
384             qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, true);
385             qemu_input_event_send_key_delay(0);
386             qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, false);
387             qemu_input_event_send_key_delay(0);
388 
389             if (keycode & ALTGR) {
390                 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, false);
391                 qemu_input_event_send_key_delay(0);
392             }
393             if (keycode & ALT) {
394                 qemu_input_event_send_key_number(dcl->con, ALT_CODE, false);
395                 qemu_input_event_send_key_delay(0);
396             }
397             if (keycode & CNTRL) {
398                 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, false);
399                 qemu_input_event_send_key_delay(0);
400             }
401             if (keycode & SHIFT) {
402                 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, false);
403                 qemu_input_event_send_key_delay(0);
404             }
405         } else {
406             keysym = curses2qemu(chr, maybe_keycode);
407             if (keysym == -1)
408                 keysym = chr;
409 
410             qemu_text_console_put_keysym(QEMU_TEXT_CONSOLE(dcl->con), keysym);
411         }
412     }
413 }
414 
curses_atexit(void)415 static void curses_atexit(void)
416 {
417     endwin();
418     g_free(vga_to_curses);
419     g_free(screen);
420 }
421 
422 /*
423  * In the following:
424  * - fch is the font glyph number
425  * - uch is the unicode value
426  * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
427  * - mbch is the native local-dependent multibyte representation
428  */
429 
430 /* Setup wchar glyph for one UCS-2 char */
convert_ucs(unsigned char fch,uint16_t uch,iconv_t conv)431 static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
432 {
433     char mbch[MB_LEN_MAX];
434     wchar_t wch[2];
435     char *puch, *pmbch;
436     size_t such, smbch;
437     mbstate_t ps;
438 
439     puch = (char *) &uch;
440     pmbch = (char *) mbch;
441     such = sizeof(uch);
442     smbch = sizeof(mbch);
443 
444     if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
445         fprintf(stderr, "Could not convert 0x%04x "
446                         "from UCS-2 to a multibyte character: %s\n",
447                         uch, strerror(errno));
448         return;
449     }
450 
451     memset(&ps, 0, sizeof(ps));
452     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
453         fprintf(stderr, "Could not convert 0x%04x "
454                         "from a multibyte character to wchar_t: %s\n",
455                         uch, strerror(errno));
456         return;
457     }
458 
459     wch[1] = 0;
460     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
461 }
462 
463 /* Setup wchar glyph for one font character */
convert_font(unsigned char fch,iconv_t conv)464 static void convert_font(unsigned char fch, iconv_t conv)
465 {
466     char mbch[MB_LEN_MAX];
467     wchar_t wch[2];
468     char *pfch, *pmbch;
469     size_t sfch, smbch;
470     mbstate_t ps;
471 
472     pfch = (char *) &fch;
473     pmbch = (char *) &mbch;
474     sfch = sizeof(fch);
475     smbch = sizeof(mbch);
476 
477     if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
478         fprintf(stderr, "Could not convert font glyph 0x%02x "
479                         "from %s to a multibyte character: %s\n",
480                         fch, font_charset, strerror(errno));
481         return;
482     }
483 
484     memset(&ps, 0, sizeof(ps));
485     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
486         fprintf(stderr, "Could not convert font glyph 0x%02x "
487                         "from a multibyte character to wchar_t: %s\n",
488                         fch, strerror(errno));
489         return;
490     }
491 
492     wch[1] = 0;
493     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
494 }
495 
496 /* Convert one wchar to UCS-2 */
get_ucs(wchar_t wch,iconv_t conv)497 static uint16_t get_ucs(wchar_t wch, iconv_t conv)
498 {
499     char mbch[MB_LEN_MAX];
500     uint16_t uch;
501     char *pmbch, *puch;
502     size_t smbch, such;
503     mbstate_t ps;
504     int ret;
505 
506     memset(&ps, 0, sizeof(ps));
507     ret = wcrtomb(mbch, wch, &ps);
508     if (ret == -1) {
509         fprintf(stderr, "Could not convert 0x%04lx "
510                         "from wchar_t to a multibyte character: %s\n",
511                         (unsigned long)wch, strerror(errno));
512         return 0xFFFD;
513     }
514 
515     pmbch = (char *) mbch;
516     puch = (char *) &uch;
517     smbch = ret;
518     such = sizeof(uch);
519 
520     if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
521         fprintf(stderr, "Could not convert 0x%04lx "
522                         "from a multibyte character to UCS-2 : %s\n",
523                         (unsigned long)wch, strerror(errno));
524         return 0xFFFD;
525     }
526 
527     return uch;
528 }
529 
530 /*
531  * Setup mapping for vga to curses line graphics.
532  */
font_setup(void)533 static void font_setup(void)
534 {
535     iconv_t ucs2_to_nativecharset;
536     iconv_t nativecharset_to_ucs2;
537     iconv_t font_conv;
538     int i;
539     g_autofree gchar *local_codeset = g_get_codeset();
540 
541     /*
542      * Control characters are normally non-printable, but VGA does have
543      * well-known glyphs for them.
544      */
545     static const uint16_t control_characters[0x20] = {
546       0x0020,
547       0x263a,
548       0x263b,
549       0x2665,
550       0x2666,
551       0x2663,
552       0x2660,
553       0x2022,
554       0x25d8,
555       0x25cb,
556       0x25d9,
557       0x2642,
558       0x2640,
559       0x266a,
560       0x266b,
561       0x263c,
562       0x25ba,
563       0x25c4,
564       0x2195,
565       0x203c,
566       0x00b6,
567       0x00a7,
568       0x25ac,
569       0x21a8,
570       0x2191,
571       0x2193,
572       0x2192,
573       0x2190,
574       0x221f,
575       0x2194,
576       0x25b2,
577       0x25bc
578     };
579 
580     ucs2_to_nativecharset = iconv_open(local_codeset, "UCS-2");
581     if (ucs2_to_nativecharset == (iconv_t) -1) {
582         fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
583                         strerror(errno));
584         exit(1);
585     }
586 
587     nativecharset_to_ucs2 = iconv_open("UCS-2", local_codeset);
588     if (nativecharset_to_ucs2 == (iconv_t) -1) {
589         iconv_close(ucs2_to_nativecharset);
590         fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
591                         strerror(errno));
592         exit(1);
593     }
594 
595     font_conv = iconv_open(local_codeset, font_charset);
596     if (font_conv == (iconv_t) -1) {
597         iconv_close(ucs2_to_nativecharset);
598         iconv_close(nativecharset_to_ucs2);
599         fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
600                         font_charset, strerror(errno));
601         exit(1);
602     }
603 
604     /* Control characters */
605     for (i = 0; i <= 0x1F; i++) {
606         convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
607     }
608 
609     for (i = 0x20; i <= 0xFF; i++) {
610         convert_font(i, font_conv);
611     }
612 
613     /* DEL */
614     convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
615 
616     if (strcmp(local_codeset, "UTF-8")) {
617         /* Non-Unicode capable, use termcap equivalents for those available */
618         for (i = 0; i <= 0xFF; i++) {
619             wchar_t wch[CCHARW_MAX];
620             attr_t attr;
621             short color;
622             int ret;
623 
624             ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
625             if (ret == ERR)
626                 continue;
627 
628             switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
629             case 0x00a3:
630                 vga_to_curses[i] = *WACS_STERLING;
631                 break;
632             case 0x2591:
633                 vga_to_curses[i] = *WACS_BOARD;
634                 break;
635             case 0x2592:
636                 vga_to_curses[i] = *WACS_CKBOARD;
637                 break;
638             case 0x2502:
639                 vga_to_curses[i] = *WACS_VLINE;
640                 break;
641             case 0x2524:
642                 vga_to_curses[i] = *WACS_RTEE;
643                 break;
644             case 0x2510:
645                 vga_to_curses[i] = *WACS_URCORNER;
646                 break;
647             case 0x2514:
648                 vga_to_curses[i] = *WACS_LLCORNER;
649                 break;
650             case 0x2534:
651                 vga_to_curses[i] = *WACS_BTEE;
652                 break;
653             case 0x252c:
654                 vga_to_curses[i] = *WACS_TTEE;
655                 break;
656             case 0x251c:
657                 vga_to_curses[i] = *WACS_LTEE;
658                 break;
659             case 0x2500:
660                 vga_to_curses[i] = *WACS_HLINE;
661                 break;
662             case 0x253c:
663                 vga_to_curses[i] = *WACS_PLUS;
664                 break;
665             case 0x256c:
666                 vga_to_curses[i] = *WACS_LANTERN;
667                 break;
668             case 0x256a:
669                 vga_to_curses[i] = *WACS_NEQUAL;
670                 break;
671             case 0x2518:
672                 vga_to_curses[i] = *WACS_LRCORNER;
673                 break;
674             case 0x250c:
675                 vga_to_curses[i] = *WACS_ULCORNER;
676                 break;
677             case 0x2588:
678                 vga_to_curses[i] = *WACS_BLOCK;
679                 break;
680             case 0x03c0:
681                 vga_to_curses[i] = *WACS_PI;
682                 break;
683             case 0x00b1:
684                 vga_to_curses[i] = *WACS_PLMINUS;
685                 break;
686             case 0x2265:
687                 vga_to_curses[i] = *WACS_GEQUAL;
688                 break;
689             case 0x2264:
690                 vga_to_curses[i] = *WACS_LEQUAL;
691                 break;
692             case 0x00b0:
693                 vga_to_curses[i] = *WACS_DEGREE;
694                 break;
695             case 0x25a0:
696                 vga_to_curses[i] = *WACS_BULLET;
697                 break;
698             case 0x2666:
699                 vga_to_curses[i] = *WACS_DIAMOND;
700                 break;
701             case 0x2192:
702                 vga_to_curses[i] = *WACS_RARROW;
703                 break;
704             case 0x2190:
705                 vga_to_curses[i] = *WACS_LARROW;
706                 break;
707             case 0x2191:
708                 vga_to_curses[i] = *WACS_UARROW;
709                 break;
710             case 0x2193:
711                 vga_to_curses[i] = *WACS_DARROW;
712                 break;
713             case 0x23ba:
714                 vga_to_curses[i] = *WACS_S1;
715                 break;
716             case 0x23bb:
717                 vga_to_curses[i] = *WACS_S3;
718                 break;
719             case 0x23bc:
720                 vga_to_curses[i] = *WACS_S7;
721                 break;
722             case 0x23bd:
723                 vga_to_curses[i] = *WACS_S9;
724                 break;
725             }
726         }
727     }
728     iconv_close(ucs2_to_nativecharset);
729     iconv_close(nativecharset_to_ucs2);
730     iconv_close(font_conv);
731 }
732 
curses_setup(void)733 static void curses_setup(void)
734 {
735     int i, colour_default[8] = {
736         [QEMU_COLOR_BLACK]   = COLOR_BLACK,
737         [QEMU_COLOR_BLUE]    = COLOR_BLUE,
738         [QEMU_COLOR_GREEN]   = COLOR_GREEN,
739         [QEMU_COLOR_CYAN]    = COLOR_CYAN,
740         [QEMU_COLOR_RED]     = COLOR_RED,
741         [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
742         [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
743         [QEMU_COLOR_WHITE]   = COLOR_WHITE,
744     };
745 
746     /* input as raw as possible, let everything be interpreted
747      * by the guest system */
748     initscr(); noecho(); intrflush(stdscr, FALSE);
749     nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
750     start_color(); raw(); scrollok(stdscr, FALSE);
751     set_escdelay(25);
752 
753     /* Make color pair to match color format (3bits bg:3bits fg) */
754     for (i = 0; i < 64; i++) {
755         init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
756     }
757     /* Set default color for more than 64 for safety. */
758     for (i = 64; i < COLOR_PAIRS; i++) {
759         init_pair(i, COLOR_WHITE, COLOR_BLACK);
760     }
761 
762     font_setup();
763 }
764 
curses_keyboard_setup(void)765 static void curses_keyboard_setup(void)
766 {
767 #if defined(__APPLE__)
768     /* always use generic keymaps */
769     if (!keyboard_layout)
770         keyboard_layout = "en-us";
771 #endif
772     if(keyboard_layout) {
773         kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
774                                           &error_fatal);
775     }
776 }
777 
778 static const DisplayChangeListenerOps dcl_ops = {
779     .dpy_name        = "curses",
780     .dpy_text_update = curses_update,
781     .dpy_text_resize = curses_resize,
782     .dpy_refresh     = curses_refresh,
783     .dpy_text_cursor = curses_cursor_position,
784 };
785 
curses_display_init(DisplayState * ds,DisplayOptions * opts)786 static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
787 {
788 #ifndef _WIN32
789     if (!isatty(1)) {
790         fprintf(stderr, "We need a terminal output\n");
791         exit(1);
792     }
793 #endif
794 
795     setlocale(LC_CTYPE, "");
796     if (opts->u.curses.charset) {
797         font_charset = opts->u.curses.charset;
798     }
799     screen = g_new0(console_ch_t, 160 * 100);
800     vga_to_curses = g_new0(cchar_t, 256);
801     curses_setup();
802     curses_keyboard_setup();
803     atexit(curses_atexit);
804 
805     curses_winch_init();
806 
807     dcl = g_new0(DisplayChangeListener, 1);
808     dcl->con = qemu_console_lookup_default();
809     dcl->ops = &dcl_ops;
810     register_displaychangelistener(dcl);
811 
812     invalidate = 1;
813 }
814 
815 static QemuDisplay qemu_display_curses = {
816     .type       = DISPLAY_TYPE_CURSES,
817     .init       = curses_display_init,
818 };
819 
register_curses(void)820 static void register_curses(void)
821 {
822     qemu_display_register(&qemu_display_curses);
823 }
824 
825 type_init(register_curses);
826