xref: /openbmc/qemu/ui/curses.c (revision 65f5279761e20a1754dce450ca111382ff3b1e5c)
13e230dd2SCorentin Chary /*
23e230dd2SCorentin Chary  * QEMU curses/ncurses display driver
33e230dd2SCorentin Chary  *
43e230dd2SCorentin Chary  * Copyright (c) 2005 Andrzej Zaborowski  <balrog@zabor.org>
53e230dd2SCorentin Chary  *
63e230dd2SCorentin Chary  * Permission is hereby granted, free of charge, to any person obtaining a copy
73e230dd2SCorentin Chary  * of this software and associated documentation files (the "Software"), to deal
83e230dd2SCorentin Chary  * in the Software without restriction, including without limitation the rights
93e230dd2SCorentin Chary  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
103e230dd2SCorentin Chary  * copies of the Software, and to permit persons to whom the Software is
113e230dd2SCorentin Chary  * furnished to do so, subject to the following conditions:
123e230dd2SCorentin Chary  *
133e230dd2SCorentin Chary  * The above copyright notice and this permission notice shall be included in
143e230dd2SCorentin Chary  * all copies or substantial portions of the Software.
153e230dd2SCorentin Chary  *
163e230dd2SCorentin Chary  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
173e230dd2SCorentin Chary  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
183e230dd2SCorentin Chary  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
193e230dd2SCorentin Chary  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
203e230dd2SCorentin Chary  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
213e230dd2SCorentin Chary  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
223e230dd2SCorentin Chary  * THE SOFTWARE.
233e230dd2SCorentin Chary  */
240b8fa32fSMarkus Armbruster 
25e16f4c87SPeter Maydell #include "qemu/osdep.h"
263e230dd2SCorentin Chary 
273e230dd2SCorentin Chary #ifndef _WIN32
283e230dd2SCorentin Chary #include <sys/ioctl.h>
293e230dd2SCorentin Chary #include <termios.h>
303e230dd2SCorentin Chary #endif
312f8b7cd5SSamuel Thibault #include <locale.h>
322f8b7cd5SSamuel Thibault #include <wchar.h>
332f8b7cd5SSamuel Thibault #include <iconv.h>
343e230dd2SCorentin Chary 
35ab4f931eSFei Li #include "qapi/error.h"
360b8fa32fSMarkus Armbruster #include "qemu/module.h"
3728ecbaeeSPaolo Bonzini #include "ui/console.h"
38cd100328SGerd Hoffmann #include "ui/input.h"
399c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
403e230dd2SCorentin Chary 
41e2f82e92SGerd Hoffmann /* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
42e2f82e92SGerd Hoffmann #undef KEY_EVENT
43e2f82e92SGerd Hoffmann #include <curses.h>
44e2f82e92SGerd Hoffmann #undef KEY_EVENT
45e2f82e92SGerd Hoffmann 
463e230dd2SCorentin Chary #define FONT_HEIGHT 16
473e230dd2SCorentin Chary #define FONT_WIDTH 8
483e230dd2SCorentin Chary 
49459a707eSSamuel Thibault enum maybe_keycode {
50459a707eSSamuel Thibault     CURSES_KEYCODE,
51459a707eSSamuel Thibault     CURSES_CHAR,
52459a707eSSamuel Thibault     CURSES_CHAR_OR_KEYCODE,
53459a707eSSamuel Thibault };
54459a707eSSamuel Thibault 
557c20b4a3SGerd Hoffmann static DisplayChangeListener *dcl;
5676c51fc3SPhilippe Mathieu-Daudé static console_ch_t *screen;
573e230dd2SCorentin Chary static WINDOW *screenpad = NULL;
583e230dd2SCorentin Chary static int width, height, gwidth, gheight, invalidate;
593e230dd2SCorentin Chary static int px, py, sminx, sminy, smaxx, smaxy;
603e230dd2SCorentin Chary 
612f8b7cd5SSamuel Thibault static const char *font_charset = "CP437";
6276c51fc3SPhilippe Mathieu-Daudé static cchar_t *vga_to_curses;
63e2368dc9SOGAWA Hirofumi 
647c20b4a3SGerd Hoffmann static void curses_update(DisplayChangeListener *dcl,
657c20b4a3SGerd Hoffmann                           int x, int y, int w, int h)
663e230dd2SCorentin Chary {
67e2f82e92SGerd Hoffmann     console_ch_t *line;
682f8b7cd5SSamuel Thibault     cchar_t curses_line[width];
69962cf8fdSSamuel Thibault     wchar_t wch[CCHARW_MAX];
70962cf8fdSSamuel Thibault     attr_t attrs;
71962cf8fdSSamuel Thibault     short colors;
72962cf8fdSSamuel Thibault     int ret;
733e230dd2SCorentin Chary 
74e2f82e92SGerd Hoffmann     line = screen + y * width;
75e2f82e92SGerd Hoffmann     for (h += y; y < h; y ++, line += width) {
76e2f82e92SGerd Hoffmann         for (x = 0; x < width; x++) {
77cd54ea45SMatthew Kilgore             chtype ch = line[x] & A_CHARTEXT;
78cd54ea45SMatthew Kilgore             chtype at = line[x] & A_ATTRIBUTES;
7930f5a9ddSMatthew Kilgore             short color_pair = PAIR_NUMBER(line[x]);
8030f5a9ddSMatthew Kilgore 
81962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
82962cf8fdSSamuel Thibault             if (ret == ERR || wch[0] == 0) {
83962cf8fdSSamuel Thibault                 wch[0] = ch;
84962cf8fdSSamuel Thibault                 wch[1] = 0;
85e2f82e92SGerd Hoffmann             }
8630f5a9ddSMatthew Kilgore             setcchar(&curses_line[x], wch, at, color_pair, NULL);
87e2f82e92SGerd Hoffmann         }
882f8b7cd5SSamuel Thibault         mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
89e2f82e92SGerd Hoffmann     }
903e230dd2SCorentin Chary 
913e230dd2SCorentin Chary     pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
923e230dd2SCorentin Chary     refresh();
933e230dd2SCorentin Chary }
943e230dd2SCorentin Chary 
953e230dd2SCorentin Chary static void curses_calc_pad(void)
963e230dd2SCorentin Chary {
9781c0d5a6SGerd Hoffmann     if (qemu_console_is_fixedsize(NULL)) {
983e230dd2SCorentin Chary         width = gwidth;
993e230dd2SCorentin Chary         height = gheight;
1003e230dd2SCorentin Chary     } else {
1013e230dd2SCorentin Chary         width = COLS;
1023e230dd2SCorentin Chary         height = LINES;
1033e230dd2SCorentin Chary     }
1043e230dd2SCorentin Chary 
1053e230dd2SCorentin Chary     if (screenpad)
1063e230dd2SCorentin Chary         delwin(screenpad);
1073e230dd2SCorentin Chary 
1083e230dd2SCorentin Chary     clear();
1093e230dd2SCorentin Chary     refresh();
1103e230dd2SCorentin Chary 
1113e230dd2SCorentin Chary     screenpad = newpad(height, width);
1123e230dd2SCorentin Chary 
1133e230dd2SCorentin Chary     if (width > COLS) {
1143e230dd2SCorentin Chary         px = (width - COLS) / 2;
1153e230dd2SCorentin Chary         sminx = 0;
1163e230dd2SCorentin Chary         smaxx = COLS;
1173e230dd2SCorentin Chary     } else {
1183e230dd2SCorentin Chary         px = 0;
1193e230dd2SCorentin Chary         sminx = (COLS - width) / 2;
1203e230dd2SCorentin Chary         smaxx = sminx + width;
1213e230dd2SCorentin Chary     }
1223e230dd2SCorentin Chary 
1233e230dd2SCorentin Chary     if (height > LINES) {
1243e230dd2SCorentin Chary         py = (height - LINES) / 2;
1253e230dd2SCorentin Chary         sminy = 0;
1263e230dd2SCorentin Chary         smaxy = LINES;
1273e230dd2SCorentin Chary     } else {
1283e230dd2SCorentin Chary         py = 0;
1293e230dd2SCorentin Chary         sminy = (LINES - height) / 2;
1303e230dd2SCorentin Chary         smaxy = sminy + height;
1313e230dd2SCorentin Chary     }
1323e230dd2SCorentin Chary }
1333e230dd2SCorentin Chary 
1347c20b4a3SGerd Hoffmann static void curses_resize(DisplayChangeListener *dcl,
1357c20b4a3SGerd Hoffmann                           int width, int height)
1363e230dd2SCorentin Chary {
137a93a4a22SGerd Hoffmann     if (width == gwidth && height == gheight) {
1383e230dd2SCorentin Chary         return;
139a93a4a22SGerd Hoffmann     }
1403e230dd2SCorentin Chary 
141a93a4a22SGerd Hoffmann     gwidth = width;
142a93a4a22SGerd Hoffmann     gheight = height;
1433e230dd2SCorentin Chary 
1443e230dd2SCorentin Chary     curses_calc_pad();
1453e230dd2SCorentin Chary }
1463e230dd2SCorentin Chary 
147032ac6f8SGerd Hoffmann #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
148032ac6f8SGerd Hoffmann static volatile sig_atomic_t got_sigwinch;
149032ac6f8SGerd Hoffmann static void curses_winch_check(void)
1503e230dd2SCorentin Chary {
1513e230dd2SCorentin Chary     struct winsize {
1523e230dd2SCorentin Chary         unsigned short ws_row;
1533e230dd2SCorentin Chary         unsigned short ws_col;
1543e230dd2SCorentin Chary         unsigned short ws_xpixel;   /* unused */
1553e230dd2SCorentin Chary         unsigned short ws_ypixel;   /* unused */
1563e230dd2SCorentin Chary     } ws;
1573e230dd2SCorentin Chary 
158032ac6f8SGerd Hoffmann     if (!got_sigwinch) {
1593e230dd2SCorentin Chary         return;
160032ac6f8SGerd Hoffmann     }
161032ac6f8SGerd Hoffmann     got_sigwinch = false;
162032ac6f8SGerd Hoffmann 
163032ac6f8SGerd Hoffmann     if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
164032ac6f8SGerd Hoffmann         return;
165032ac6f8SGerd Hoffmann     }
1663e230dd2SCorentin Chary 
1673e230dd2SCorentin Chary     resize_term(ws.ws_row, ws.ws_col);
1683e230dd2SCorentin Chary     invalidate = 1;
1693e230dd2SCorentin Chary }
170032ac6f8SGerd Hoffmann 
171032ac6f8SGerd Hoffmann static void curses_winch_handler(int signum)
172032ac6f8SGerd Hoffmann {
173032ac6f8SGerd Hoffmann     got_sigwinch = true;
174032ac6f8SGerd Hoffmann }
175032ac6f8SGerd Hoffmann 
176032ac6f8SGerd Hoffmann static void curses_winch_init(void)
177032ac6f8SGerd Hoffmann {
178032ac6f8SGerd Hoffmann     struct sigaction old, winch = {
179032ac6f8SGerd Hoffmann         .sa_handler  = curses_winch_handler,
180032ac6f8SGerd Hoffmann     };
181032ac6f8SGerd Hoffmann     sigaction(SIGWINCH, &winch, &old);
182032ac6f8SGerd Hoffmann }
183032ac6f8SGerd Hoffmann #else
184032ac6f8SGerd Hoffmann static void curses_winch_check(void) {}
185032ac6f8SGerd Hoffmann static void curses_winch_init(void) {}
1863e230dd2SCorentin Chary #endif
1873e230dd2SCorentin Chary 
1887c20b4a3SGerd Hoffmann static void curses_cursor_position(DisplayChangeListener *dcl,
1897c20b4a3SGerd Hoffmann                                    int x, int y)
1903e230dd2SCorentin Chary {
1913e230dd2SCorentin Chary     if (x >= 0) {
1923e230dd2SCorentin Chary         x = sminx + x - px;
1933e230dd2SCorentin Chary         y = sminy + y - py;
1943e230dd2SCorentin Chary 
1953e230dd2SCorentin Chary         if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
1963e230dd2SCorentin Chary             move(y, x);
1973e230dd2SCorentin Chary             curs_set(1);
1983e230dd2SCorentin Chary             /* it seems that curs_set(1) must always be called before
1993e230dd2SCorentin Chary              * curs_set(2) for the latter to have effect */
20081c0d5a6SGerd Hoffmann             if (!qemu_console_is_graphic(NULL)) {
2013e230dd2SCorentin Chary                 curs_set(2);
20281c0d5a6SGerd Hoffmann             }
2033e230dd2SCorentin Chary             return;
2043e230dd2SCorentin Chary         }
2053e230dd2SCorentin Chary     }
2063e230dd2SCorentin Chary 
2073e230dd2SCorentin Chary     curs_set(0);
2083e230dd2SCorentin Chary }
2093e230dd2SCorentin Chary 
2103e230dd2SCorentin Chary /* generic keyboard conversion */
2113e230dd2SCorentin Chary 
2123e230dd2SCorentin Chary #include "curses_keys.h"
2133e230dd2SCorentin Chary 
2143e230dd2SCorentin Chary static kbd_layout_t *kbd_layout = NULL;
2153e230dd2SCorentin Chary 
216459a707eSSamuel Thibault static wint_t console_getch(enum maybe_keycode *maybe_keycode)
217459a707eSSamuel Thibault {
218459a707eSSamuel Thibault     wint_t ret;
219459a707eSSamuel Thibault     switch (get_wch(&ret)) {
220459a707eSSamuel Thibault     case KEY_CODE_YES:
221459a707eSSamuel Thibault         *maybe_keycode = CURSES_KEYCODE;
222459a707eSSamuel Thibault         break;
223459a707eSSamuel Thibault     case OK:
224459a707eSSamuel Thibault         *maybe_keycode = CURSES_CHAR;
225459a707eSSamuel Thibault         break;
226459a707eSSamuel Thibault     case ERR:
227459a707eSSamuel Thibault         ret = -1;
228459a707eSSamuel Thibault         break;
22968097ed5SPaolo Bonzini     default:
23068097ed5SPaolo Bonzini         abort();
231459a707eSSamuel Thibault     }
232459a707eSSamuel Thibault     return ret;
233459a707eSSamuel Thibault }
234459a707eSSamuel Thibault 
235459a707eSSamuel Thibault static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
236459a707eSSamuel Thibault                       int chr, enum maybe_keycode maybe_keycode)
237459a707eSSamuel Thibault {
238459a707eSSamuel Thibault     int ret = -1;
239459a707eSSamuel Thibault     if (maybe_keycode == CURSES_CHAR) {
240459a707eSSamuel Thibault         if (chr < CURSES_CHARS) {
241459a707eSSamuel Thibault             ret = _curses2foo[chr];
242459a707eSSamuel Thibault         }
243459a707eSSamuel Thibault     } else {
244459a707eSSamuel Thibault         if (chr < CURSES_KEYS) {
245459a707eSSamuel Thibault             ret = _curseskey2foo[chr];
246459a707eSSamuel Thibault         }
247459a707eSSamuel Thibault         if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
248459a707eSSamuel Thibault             chr < CURSES_CHARS) {
249459a707eSSamuel Thibault             ret = _curses2foo[chr];
250459a707eSSamuel Thibault         }
251459a707eSSamuel Thibault     }
252459a707eSSamuel Thibault     return ret;
253459a707eSSamuel Thibault }
254459a707eSSamuel Thibault 
255459a707eSSamuel Thibault #define curses2keycode(chr, maybe_keycode) \
256459a707eSSamuel Thibault     curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
257459a707eSSamuel Thibault #define curses2keysym(chr, maybe_keycode) \
258459a707eSSamuel Thibault     curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
259459a707eSSamuel Thibault #define curses2qemu(chr, maybe_keycode) \
260459a707eSSamuel Thibault     curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
261459a707eSSamuel Thibault 
262bc2ed970SGerd Hoffmann static void curses_refresh(DisplayChangeListener *dcl)
2633e230dd2SCorentin Chary {
26499a9ef44SPeter Maydell     int chr, keysym, keycode, keycode_alt;
265*65f52797SYonggang Luo     enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
2663e230dd2SCorentin Chary 
267032ac6f8SGerd Hoffmann     curses_winch_check();
268032ac6f8SGerd Hoffmann 
2693e230dd2SCorentin Chary     if (invalidate) {
2703e230dd2SCorentin Chary         clear();
2713e230dd2SCorentin Chary         refresh();
2723e230dd2SCorentin Chary         curses_calc_pad();
2731dbfa005SGerd Hoffmann         graphic_hw_invalidate(NULL);
2743e230dd2SCorentin Chary         invalidate = 0;
2753e230dd2SCorentin Chary     }
2763e230dd2SCorentin Chary 
2771dbfa005SGerd Hoffmann     graphic_hw_text_update(NULL, screen);
2783e230dd2SCorentin Chary 
2793e230dd2SCorentin Chary     while (1) {
2803e230dd2SCorentin Chary         /* while there are any pending key strokes to process */
281459a707eSSamuel Thibault         chr = console_getch(&maybe_keycode);
2823e230dd2SCorentin Chary 
283459a707eSSamuel Thibault         if (chr == -1)
2843e230dd2SCorentin Chary             break;
2853e230dd2SCorentin Chary 
2863e230dd2SCorentin Chary #ifdef KEY_RESIZE
2873e230dd2SCorentin Chary         /* this shouldn't occur when we use a custom SIGWINCH handler */
288459a707eSSamuel Thibault         if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
2893e230dd2SCorentin Chary             clear();
2903e230dd2SCorentin Chary             refresh();
2913e230dd2SCorentin Chary             curses_calc_pad();
292bc2ed970SGerd Hoffmann             curses_update(dcl, 0, 0, width, height);
2933e230dd2SCorentin Chary             continue;
2943e230dd2SCorentin Chary         }
2953e230dd2SCorentin Chary #endif
2963e230dd2SCorentin Chary 
297459a707eSSamuel Thibault         keycode = curses2keycode(chr, maybe_keycode);
2983e230dd2SCorentin Chary         keycode_alt = 0;
2993e230dd2SCorentin Chary 
300633786feSSamuel Thibault         /* alt or esc key */
3013e230dd2SCorentin Chary         if (keycode == 1) {
302*65f52797SYonggang Luo             enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
303459a707eSSamuel Thibault             int nextchr = console_getch(&next_maybe_keycode);
3043e230dd2SCorentin Chary 
305459a707eSSamuel Thibault             if (nextchr != -1) {
3063e230dd2SCorentin Chary                 chr = nextchr;
307459a707eSSamuel Thibault                 maybe_keycode = next_maybe_keycode;
3083e230dd2SCorentin Chary                 keycode_alt = ALT;
309459a707eSSamuel Thibault                 keycode = curses2keycode(chr, maybe_keycode);
3103e230dd2SCorentin Chary 
3113e230dd2SCorentin Chary                 if (keycode != -1) {
3123e230dd2SCorentin Chary                     keycode |= ALT;
3133e230dd2SCorentin Chary 
3143e230dd2SCorentin Chary                     /* process keys reserved for qemu */
3153e230dd2SCorentin Chary                     if (keycode >= QEMU_KEY_CONSOLE0 &&
3163e230dd2SCorentin Chary                             keycode < QEMU_KEY_CONSOLE0 + 9) {
3173e230dd2SCorentin Chary                         erase();
3183e230dd2SCorentin Chary                         wnoutrefresh(stdscr);
3193e230dd2SCorentin Chary                         console_select(keycode - QEMU_KEY_CONSOLE0);
3203e230dd2SCorentin Chary 
3213e230dd2SCorentin Chary                         invalidate = 1;
3223e230dd2SCorentin Chary                         continue;
3233e230dd2SCorentin Chary                     }
3243e230dd2SCorentin Chary                 }
3253e230dd2SCorentin Chary             }
3263e230dd2SCorentin Chary         }
3273e230dd2SCorentin Chary 
3283e230dd2SCorentin Chary         if (kbd_layout) {
329459a707eSSamuel Thibault             keysym = curses2keysym(chr, maybe_keycode);
3303e230dd2SCorentin Chary 
3313e230dd2SCorentin Chary             if (keysym == -1) {
332d03703c8SSamuel Thibault                 if (chr < ' ') {
333d03703c8SSamuel Thibault                     keysym = chr + '@';
334d03703c8SSamuel Thibault                     if (keysym >= 'A' && keysym <= 'Z')
335d03703c8SSamuel Thibault                         keysym += 'a' - 'A';
336d03703c8SSamuel Thibault                     keysym |= KEYSYM_CNTRL;
337d03703c8SSamuel Thibault                 } else
3383e230dd2SCorentin Chary                     keysym = chr;
3393e230dd2SCorentin Chary             }
3403e230dd2SCorentin Chary 
341abb4f2c9SGerd Hoffmann             keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
34219c1b9fdSGerd Hoffmann                                       NULL, false);
3433e230dd2SCorentin Chary             if (keycode == 0)
3443e230dd2SCorentin Chary                 continue;
3453e230dd2SCorentin Chary 
3463e230dd2SCorentin Chary             keycode |= (keysym & ~KEYSYM_MASK) >> 16;
3473e230dd2SCorentin Chary             keycode |= keycode_alt;
3483e230dd2SCorentin Chary         }
3493e230dd2SCorentin Chary 
3503e230dd2SCorentin Chary         if (keycode == -1)
3513e230dd2SCorentin Chary             continue;
3523e230dd2SCorentin Chary 
35381c0d5a6SGerd Hoffmann         if (qemu_console_is_graphic(NULL)) {
3543e230dd2SCorentin Chary             /* since terminals don't know about key press and release
3553e230dd2SCorentin Chary              * events, we need to emit both for each key received */
356cd100328SGerd Hoffmann             if (keycode & SHIFT) {
357cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
3585a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3593e230dd2SCorentin Chary             }
360cd100328SGerd Hoffmann             if (keycode & CNTRL) {
361cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
3625a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3633e230dd2SCorentin Chary             }
364cd100328SGerd Hoffmann             if (keycode & ALT) {
365cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, ALT_CODE, true);
3665a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
367cd100328SGerd Hoffmann             }
368cd100328SGerd Hoffmann             if (keycode & ALTGR) {
369cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
3705a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
371cd100328SGerd Hoffmann             }
372cd100328SGerd Hoffmann 
373f5c0ab13SAndrew Oates             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
3745a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
375f5c0ab13SAndrew Oates             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
3765a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
377cd100328SGerd Hoffmann 
378cd100328SGerd Hoffmann             if (keycode & ALTGR) {
379cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
3805a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
381cd100328SGerd Hoffmann             }
382cd100328SGerd Hoffmann             if (keycode & ALT) {
383cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, ALT_CODE, false);
3845a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
385cd100328SGerd Hoffmann             }
386cd100328SGerd Hoffmann             if (keycode & CNTRL) {
387cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
3885a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
389cd100328SGerd Hoffmann             }
390cd100328SGerd Hoffmann             if (keycode & SHIFT) {
391cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
3925a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
393cd100328SGerd Hoffmann             }
3943e230dd2SCorentin Chary         } else {
395459a707eSSamuel Thibault             keysym = curses2qemu(chr, maybe_keycode);
3963e230dd2SCorentin Chary             if (keysym == -1)
3973e230dd2SCorentin Chary                 keysym = chr;
3983e230dd2SCorentin Chary 
3993e230dd2SCorentin Chary             kbd_put_keysym(keysym);
4003e230dd2SCorentin Chary         }
4013e230dd2SCorentin Chary     }
4023e230dd2SCorentin Chary }
4033e230dd2SCorentin Chary 
4043e230dd2SCorentin Chary static void curses_atexit(void)
4053e230dd2SCorentin Chary {
4063e230dd2SCorentin Chary     endwin();
40776c51fc3SPhilippe Mathieu-Daudé     g_free(vga_to_curses);
40876c51fc3SPhilippe Mathieu-Daudé     g_free(screen);
4093e230dd2SCorentin Chary }
4103e230dd2SCorentin Chary 
411b7b664a4SSamuel Thibault /*
412b7b664a4SSamuel Thibault  * In the following:
413b7b664a4SSamuel Thibault  * - fch is the font glyph number
414b7b664a4SSamuel Thibault  * - uch is the unicode value
415b7b664a4SSamuel Thibault  * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
416b7b664a4SSamuel Thibault  * - mbch is the native local-dependent multibyte representation
417b7b664a4SSamuel Thibault  */
418b7b664a4SSamuel Thibault 
4192f8b7cd5SSamuel Thibault /* Setup wchar glyph for one UCS-2 char */
420b7b664a4SSamuel Thibault static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
4212f8b7cd5SSamuel Thibault {
422b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
423962cf8fdSSamuel Thibault     wchar_t wch[2];
424b7b664a4SSamuel Thibault     char *puch, *pmbch;
425b7b664a4SSamuel Thibault     size_t such, smbch;
426b7b664a4SSamuel Thibault     mbstate_t ps;
4272f8b7cd5SSamuel Thibault 
428b7b664a4SSamuel Thibault     puch = (char *) &uch;
429b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
430b7b664a4SSamuel Thibault     such = sizeof(uch);
431b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4322f8b7cd5SSamuel Thibault 
433b7b664a4SSamuel Thibault     if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
434b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
435b7b664a4SSamuel Thibault                         "from UCS-2 to a multibyte character: %s\n",
436b7b664a4SSamuel Thibault                         uch, strerror(errno));
437b7b664a4SSamuel Thibault         return;
4382f8b7cd5SSamuel Thibault     }
439b7b664a4SSamuel Thibault 
440b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
441962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
442b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
443b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
444b7b664a4SSamuel Thibault                         uch, strerror(errno));
445b7b664a4SSamuel Thibault         return;
446b7b664a4SSamuel Thibault     }
447962cf8fdSSamuel Thibault 
448962cf8fdSSamuel Thibault     wch[1] = 0;
449962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4502f8b7cd5SSamuel Thibault }
4512f8b7cd5SSamuel Thibault 
4522f8b7cd5SSamuel Thibault /* Setup wchar glyph for one font character */
453b7b664a4SSamuel Thibault static void convert_font(unsigned char fch, iconv_t conv)
4542f8b7cd5SSamuel Thibault {
455b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
456962cf8fdSSamuel Thibault     wchar_t wch[2];
457b7b664a4SSamuel Thibault     char *pfch, *pmbch;
458b7b664a4SSamuel Thibault     size_t sfch, smbch;
459b7b664a4SSamuel Thibault     mbstate_t ps;
4602f8b7cd5SSamuel Thibault 
461b7b664a4SSamuel Thibault     pfch = (char *) &fch;
462b7b664a4SSamuel Thibault     pmbch = (char *) &mbch;
463b7b664a4SSamuel Thibault     sfch = sizeof(fch);
464b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4652f8b7cd5SSamuel Thibault 
466b7b664a4SSamuel Thibault     if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
467b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
468b7b664a4SSamuel Thibault                         "from %s to a multibyte character: %s\n",
469b7b664a4SSamuel Thibault                         fch, font_charset, strerror(errno));
470b7b664a4SSamuel Thibault         return;
4712f8b7cd5SSamuel Thibault     }
472b7b664a4SSamuel Thibault 
473b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
474962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
475b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
476b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
477b7b664a4SSamuel Thibault                         fch, strerror(errno));
478b7b664a4SSamuel Thibault         return;
479b7b664a4SSamuel Thibault     }
480962cf8fdSSamuel Thibault 
481962cf8fdSSamuel Thibault     wch[1] = 0;
482962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4832f8b7cd5SSamuel Thibault }
4842f8b7cd5SSamuel Thibault 
4852f8b7cd5SSamuel Thibault /* Convert one wchar to UCS-2 */
4862f8b7cd5SSamuel Thibault static uint16_t get_ucs(wchar_t wch, iconv_t conv)
4872f8b7cd5SSamuel Thibault {
488b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
489b7b664a4SSamuel Thibault     uint16_t uch;
490b7b664a4SSamuel Thibault     char *pmbch, *puch;
491b7b664a4SSamuel Thibault     size_t smbch, such;
492b7b664a4SSamuel Thibault     mbstate_t ps;
493b7b664a4SSamuel Thibault     int ret;
4942f8b7cd5SSamuel Thibault 
495b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
496b7b664a4SSamuel Thibault     ret = wcrtomb(mbch, wch, &ps);
497b7b664a4SSamuel Thibault     if (ret == -1) {
498dc3c871aSMax Reitz         fprintf(stderr, "Could not convert 0x%04lx "
499b7b664a4SSamuel Thibault                         "from wchar_t to a multibyte character: %s\n",
500dc3c871aSMax Reitz                         (unsigned long)wch, strerror(errno));
5012f8b7cd5SSamuel Thibault         return 0xFFFD;
5022f8b7cd5SSamuel Thibault     }
5032f8b7cd5SSamuel Thibault 
504b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
505b7b664a4SSamuel Thibault     puch = (char *) &uch;
506b7b664a4SSamuel Thibault     smbch = ret;
507b7b664a4SSamuel Thibault     such = sizeof(uch);
508b7b664a4SSamuel Thibault 
509b7b664a4SSamuel Thibault     if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
510dc3c871aSMax Reitz         fprintf(stderr, "Could not convert 0x%04lx "
511b7b664a4SSamuel Thibault                         "from a multibyte character to UCS-2 : %s\n",
512dc3c871aSMax Reitz                         (unsigned long)wch, strerror(errno));
513b7b664a4SSamuel Thibault         return 0xFFFD;
514b7b664a4SSamuel Thibault     }
515b7b664a4SSamuel Thibault 
516b7b664a4SSamuel Thibault     return uch;
5172f8b7cd5SSamuel Thibault }
5182f8b7cd5SSamuel Thibault 
5192f8b7cd5SSamuel Thibault /*
5202f8b7cd5SSamuel Thibault  * Setup mapping for vga to curses line graphics.
5212f8b7cd5SSamuel Thibault  */
5222f8b7cd5SSamuel Thibault static void font_setup(void)
5232f8b7cd5SSamuel Thibault {
524b7b664a4SSamuel Thibault     iconv_t ucs2_to_nativecharset;
525b7b664a4SSamuel Thibault     iconv_t nativecharset_to_ucs2;
526b7b664a4SSamuel Thibault     iconv_t font_conv;
527b7b664a4SSamuel Thibault     int i;
52880d3ab61SYonggang Luo     g_autofree gchar *local_codeset = g_get_codeset();
529b7b664a4SSamuel Thibault 
5302f8b7cd5SSamuel Thibault     /*
5312f8b7cd5SSamuel Thibault      * Control characters are normally non-printable, but VGA does have
5322f8b7cd5SSamuel Thibault      * well-known glyphs for them.
5332f8b7cd5SSamuel Thibault      */
53480e8c2edSPhilippe Mathieu-Daudé     static const uint16_t control_characters[0x20] = {
5352f8b7cd5SSamuel Thibault       0x0020,
5362f8b7cd5SSamuel Thibault       0x263a,
5372f8b7cd5SSamuel Thibault       0x263b,
5382f8b7cd5SSamuel Thibault       0x2665,
5392f8b7cd5SSamuel Thibault       0x2666,
5402f8b7cd5SSamuel Thibault       0x2663,
5412f8b7cd5SSamuel Thibault       0x2660,
5422f8b7cd5SSamuel Thibault       0x2022,
5432f8b7cd5SSamuel Thibault       0x25d8,
5442f8b7cd5SSamuel Thibault       0x25cb,
5452f8b7cd5SSamuel Thibault       0x25d9,
5462f8b7cd5SSamuel Thibault       0x2642,
5472f8b7cd5SSamuel Thibault       0x2640,
5482f8b7cd5SSamuel Thibault       0x266a,
5492f8b7cd5SSamuel Thibault       0x266b,
5502f8b7cd5SSamuel Thibault       0x263c,
5512f8b7cd5SSamuel Thibault       0x25ba,
5522f8b7cd5SSamuel Thibault       0x25c4,
5532f8b7cd5SSamuel Thibault       0x2195,
5542f8b7cd5SSamuel Thibault       0x203c,
5552f8b7cd5SSamuel Thibault       0x00b6,
5562f8b7cd5SSamuel Thibault       0x00a7,
5572f8b7cd5SSamuel Thibault       0x25ac,
5582f8b7cd5SSamuel Thibault       0x21a8,
5592f8b7cd5SSamuel Thibault       0x2191,
5602f8b7cd5SSamuel Thibault       0x2193,
5612f8b7cd5SSamuel Thibault       0x2192,
5622f8b7cd5SSamuel Thibault       0x2190,
5632f8b7cd5SSamuel Thibault       0x221f,
5642f8b7cd5SSamuel Thibault       0x2194,
5652f8b7cd5SSamuel Thibault       0x25b2,
5662f8b7cd5SSamuel Thibault       0x25bc
5672f8b7cd5SSamuel Thibault     };
5682f8b7cd5SSamuel Thibault 
56980d3ab61SYonggang Luo     ucs2_to_nativecharset = iconv_open(local_codeset, "UCS-2");
570b7b664a4SSamuel Thibault     if (ucs2_to_nativecharset == (iconv_t) -1) {
5712f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
5722f8b7cd5SSamuel Thibault                         strerror(errno));
5732f8b7cd5SSamuel Thibault         exit(1);
5742f8b7cd5SSamuel Thibault     }
5752f8b7cd5SSamuel Thibault 
57680d3ab61SYonggang Luo     nativecharset_to_ucs2 = iconv_open("UCS-2", local_codeset);
577b7b664a4SSamuel Thibault     if (nativecharset_to_ucs2 == (iconv_t) -1) {
578b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
5792f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
5802f8b7cd5SSamuel Thibault                         strerror(errno));
5812f8b7cd5SSamuel Thibault         exit(1);
5822f8b7cd5SSamuel Thibault     }
5832f8b7cd5SSamuel Thibault 
58480d3ab61SYonggang Luo     font_conv = iconv_open(local_codeset, font_charset);
5852f8b7cd5SSamuel Thibault     if (font_conv == (iconv_t) -1) {
586b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
587b7b664a4SSamuel Thibault         iconv_close(nativecharset_to_ucs2);
5882f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
5892f8b7cd5SSamuel Thibault                         font_charset, strerror(errno));
5902f8b7cd5SSamuel Thibault         exit(1);
5912f8b7cd5SSamuel Thibault     }
5922f8b7cd5SSamuel Thibault 
5932f8b7cd5SSamuel Thibault     /* Control characters */
5942f8b7cd5SSamuel Thibault     for (i = 0; i <= 0x1F; i++) {
595b7b664a4SSamuel Thibault         convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
5962f8b7cd5SSamuel Thibault     }
5972f8b7cd5SSamuel Thibault 
5982f8b7cd5SSamuel Thibault     for (i = 0x20; i <= 0xFF; i++) {
5992f8b7cd5SSamuel Thibault         convert_font(i, font_conv);
6002f8b7cd5SSamuel Thibault     }
6012f8b7cd5SSamuel Thibault 
6022f8b7cd5SSamuel Thibault     /* DEL */
603b7b664a4SSamuel Thibault     convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
6042f8b7cd5SSamuel Thibault 
60580d3ab61SYonggang Luo     if (strcmp(local_codeset, "UTF-8")) {
6062f8b7cd5SSamuel Thibault         /* Non-Unicode capable, use termcap equivalents for those available */
6072f8b7cd5SSamuel Thibault         for (i = 0; i <= 0xFF; i++) {
608962cf8fdSSamuel Thibault             wchar_t wch[CCHARW_MAX];
609962cf8fdSSamuel Thibault             attr_t attr;
610962cf8fdSSamuel Thibault             short color;
611962cf8fdSSamuel Thibault             int ret;
612962cf8fdSSamuel Thibault 
613962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
614962cf8fdSSamuel Thibault             if (ret == ERR)
615962cf8fdSSamuel Thibault                 continue;
616962cf8fdSSamuel Thibault 
617962cf8fdSSamuel Thibault             switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
6182f8b7cd5SSamuel Thibault             case 0x00a3:
6192f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_STERLING;
6202f8b7cd5SSamuel Thibault                 break;
6212f8b7cd5SSamuel Thibault             case 0x2591:
6222f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BOARD;
6232f8b7cd5SSamuel Thibault                 break;
6242f8b7cd5SSamuel Thibault             case 0x2592:
6252f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_CKBOARD;
6262f8b7cd5SSamuel Thibault                 break;
6272f8b7cd5SSamuel Thibault             case 0x2502:
6282f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_VLINE;
6292f8b7cd5SSamuel Thibault                 break;
6302f8b7cd5SSamuel Thibault             case 0x2524:
6312f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RTEE;
6322f8b7cd5SSamuel Thibault                 break;
6332f8b7cd5SSamuel Thibault             case 0x2510:
6342f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_URCORNER;
6352f8b7cd5SSamuel Thibault                 break;
6362f8b7cd5SSamuel Thibault             case 0x2514:
6372f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LLCORNER;
6382f8b7cd5SSamuel Thibault                 break;
6392f8b7cd5SSamuel Thibault             case 0x2534:
6402f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BTEE;
6412f8b7cd5SSamuel Thibault                 break;
6422f8b7cd5SSamuel Thibault             case 0x252c:
6432f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_TTEE;
6442f8b7cd5SSamuel Thibault                 break;
6452f8b7cd5SSamuel Thibault             case 0x251c:
6462f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LTEE;
6472f8b7cd5SSamuel Thibault                 break;
6482f8b7cd5SSamuel Thibault             case 0x2500:
6492f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_HLINE;
6502f8b7cd5SSamuel Thibault                 break;
6512f8b7cd5SSamuel Thibault             case 0x253c:
6522f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLUS;
6532f8b7cd5SSamuel Thibault                 break;
6542f8b7cd5SSamuel Thibault             case 0x256c:
6552f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LANTERN;
6562f8b7cd5SSamuel Thibault                 break;
6572f8b7cd5SSamuel Thibault             case 0x256a:
6582f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_NEQUAL;
6592f8b7cd5SSamuel Thibault                 break;
6602f8b7cd5SSamuel Thibault             case 0x2518:
6612f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LRCORNER;
6622f8b7cd5SSamuel Thibault                 break;
6632f8b7cd5SSamuel Thibault             case 0x250c:
6642f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_ULCORNER;
6652f8b7cd5SSamuel Thibault                 break;
6662f8b7cd5SSamuel Thibault             case 0x2588:
6672f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BLOCK;
6682f8b7cd5SSamuel Thibault                 break;
6692f8b7cd5SSamuel Thibault             case 0x03c0:
6702f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PI;
6712f8b7cd5SSamuel Thibault                 break;
6722f8b7cd5SSamuel Thibault             case 0x00b1:
6732f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLMINUS;
6742f8b7cd5SSamuel Thibault                 break;
6752f8b7cd5SSamuel Thibault             case 0x2265:
6762f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_GEQUAL;
6772f8b7cd5SSamuel Thibault                 break;
6782f8b7cd5SSamuel Thibault             case 0x2264:
6792f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LEQUAL;
6802f8b7cd5SSamuel Thibault                 break;
6812f8b7cd5SSamuel Thibault             case 0x00b0:
6822f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DEGREE;
6832f8b7cd5SSamuel Thibault                 break;
6842f8b7cd5SSamuel Thibault             case 0x25a0:
6852f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BULLET;
6862f8b7cd5SSamuel Thibault                 break;
6872f8b7cd5SSamuel Thibault             case 0x2666:
6882f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DIAMOND;
6892f8b7cd5SSamuel Thibault                 break;
6902f8b7cd5SSamuel Thibault             case 0x2192:
6912f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RARROW;
6922f8b7cd5SSamuel Thibault                 break;
6932f8b7cd5SSamuel Thibault             case 0x2190:
6942f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LARROW;
6952f8b7cd5SSamuel Thibault                 break;
6962f8b7cd5SSamuel Thibault             case 0x2191:
6972f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_UARROW;
6982f8b7cd5SSamuel Thibault                 break;
6992f8b7cd5SSamuel Thibault             case 0x2193:
7002f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DARROW;
7012f8b7cd5SSamuel Thibault                 break;
7022f8b7cd5SSamuel Thibault             case 0x23ba:
7032f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S1;
7042f8b7cd5SSamuel Thibault                 break;
7052f8b7cd5SSamuel Thibault             case 0x23bb:
7062f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S3;
7072f8b7cd5SSamuel Thibault                 break;
7082f8b7cd5SSamuel Thibault             case 0x23bc:
7092f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S7;
7102f8b7cd5SSamuel Thibault                 break;
7112f8b7cd5SSamuel Thibault             case 0x23bd:
7122f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S9;
7132f8b7cd5SSamuel Thibault                 break;
7142f8b7cd5SSamuel Thibault             }
7152f8b7cd5SSamuel Thibault         }
7162f8b7cd5SSamuel Thibault     }
717b7b664a4SSamuel Thibault     iconv_close(ucs2_to_nativecharset);
718b7b664a4SSamuel Thibault     iconv_close(nativecharset_to_ucs2);
719a9fda247SSamuel Thibault     iconv_close(font_conv);
7202f8b7cd5SSamuel Thibault }
7212f8b7cd5SSamuel Thibault 
7223e230dd2SCorentin Chary static void curses_setup(void)
7233e230dd2SCorentin Chary {
7243e230dd2SCorentin Chary     int i, colour_default[8] = {
7254083733dSOGAWA Hirofumi         [QEMU_COLOR_BLACK]   = COLOR_BLACK,
7264083733dSOGAWA Hirofumi         [QEMU_COLOR_BLUE]    = COLOR_BLUE,
7274083733dSOGAWA Hirofumi         [QEMU_COLOR_GREEN]   = COLOR_GREEN,
7284083733dSOGAWA Hirofumi         [QEMU_COLOR_CYAN]    = COLOR_CYAN,
7294083733dSOGAWA Hirofumi         [QEMU_COLOR_RED]     = COLOR_RED,
7304083733dSOGAWA Hirofumi         [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
7314083733dSOGAWA Hirofumi         [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
7324083733dSOGAWA Hirofumi         [QEMU_COLOR_WHITE]   = COLOR_WHITE,
7333e230dd2SCorentin Chary     };
7343e230dd2SCorentin Chary 
7353e230dd2SCorentin Chary     /* input as raw as possible, let everything be interpreted
7363e230dd2SCorentin Chary      * by the guest system */
7373e230dd2SCorentin Chary     initscr(); noecho(); intrflush(stdscr, FALSE);
7383e230dd2SCorentin Chary     nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
7393e230dd2SCorentin Chary     start_color(); raw(); scrollok(stdscr, FALSE);
740633786feSSamuel Thibault     set_escdelay(25);
7413e230dd2SCorentin Chary 
7424083733dSOGAWA Hirofumi     /* Make color pair to match color format (3bits bg:3bits fg) */
743615220ddSOGAWA Hirofumi     for (i = 0; i < 64; i++) {
7443e230dd2SCorentin Chary         init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
7453e230dd2SCorentin Chary     }
7464083733dSOGAWA Hirofumi     /* Set default color for more than 64 for safety. */
747615220ddSOGAWA Hirofumi     for (i = 64; i < COLOR_PAIRS; i++) {
748615220ddSOGAWA Hirofumi         init_pair(i, COLOR_WHITE, COLOR_BLACK);
749615220ddSOGAWA Hirofumi     }
750e2368dc9SOGAWA Hirofumi 
7512f8b7cd5SSamuel Thibault     font_setup();
752615220ddSOGAWA Hirofumi }
7533e230dd2SCorentin Chary 
7543e230dd2SCorentin Chary static void curses_keyboard_setup(void)
7553e230dd2SCorentin Chary {
7563e230dd2SCorentin Chary #if defined(__APPLE__)
7573e230dd2SCorentin Chary     /* always use generic keymaps */
7583e230dd2SCorentin Chary     if (!keyboard_layout)
7593e230dd2SCorentin Chary         keyboard_layout = "en-us";
7603e230dd2SCorentin Chary #endif
7613e230dd2SCorentin Chary     if(keyboard_layout) {
762ab4f931eSFei Li         kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
763ab4f931eSFei Li                                           &error_fatal);
7643e230dd2SCorentin Chary     }
7653e230dd2SCorentin Chary }
7663e230dd2SCorentin Chary 
7677c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
7687c20b4a3SGerd Hoffmann     .dpy_name        = "curses",
7697c20b4a3SGerd Hoffmann     .dpy_text_update = curses_update,
7707c20b4a3SGerd Hoffmann     .dpy_text_resize = curses_resize,
7717c20b4a3SGerd Hoffmann     .dpy_refresh     = curses_refresh,
7727c20b4a3SGerd Hoffmann     .dpy_text_cursor = curses_cursor_position,
7737c20b4a3SGerd Hoffmann };
7747c20b4a3SGerd Hoffmann 
775b0766612SGerd Hoffmann static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
7763e230dd2SCorentin Chary {
7773e230dd2SCorentin Chary #ifndef _WIN32
7783e230dd2SCorentin Chary     if (!isatty(1)) {
7793e230dd2SCorentin Chary         fprintf(stderr, "We need a terminal output\n");
7803e230dd2SCorentin Chary         exit(1);
7813e230dd2SCorentin Chary     }
7823e230dd2SCorentin Chary #endif
7833e230dd2SCorentin Chary 
7842f8b7cd5SSamuel Thibault     setlocale(LC_CTYPE, "");
7852f8b7cd5SSamuel Thibault     if (opts->u.curses.charset) {
7862f8b7cd5SSamuel Thibault         font_charset = opts->u.curses.charset;
7872f8b7cd5SSamuel Thibault     }
78876c51fc3SPhilippe Mathieu-Daudé     screen = g_new0(console_ch_t, 160 * 100);
78976c51fc3SPhilippe Mathieu-Daudé     vga_to_curses = g_new0(cchar_t, 256);
7903e230dd2SCorentin Chary     curses_setup();
7913e230dd2SCorentin Chary     curses_keyboard_setup();
7923e230dd2SCorentin Chary     atexit(curses_atexit);
7933e230dd2SCorentin Chary 
794032ac6f8SGerd Hoffmann     curses_winch_init();
7953e230dd2SCorentin Chary 
796fedf0d35SMarkus Armbruster     dcl = g_new0(DisplayChangeListener, 1);
7977c20b4a3SGerd Hoffmann     dcl->ops = &dcl_ops;
7985209089fSGerd Hoffmann     register_displaychangelistener(dcl);
7993e230dd2SCorentin Chary 
8003e230dd2SCorentin Chary     invalidate = 1;
8013e230dd2SCorentin Chary }
802b0766612SGerd Hoffmann 
803b0766612SGerd Hoffmann static QemuDisplay qemu_display_curses = {
804b0766612SGerd Hoffmann     .type       = DISPLAY_TYPE_CURSES,
805b0766612SGerd Hoffmann     .init       = curses_display_init,
806b0766612SGerd Hoffmann };
807b0766612SGerd Hoffmann 
808b0766612SGerd Hoffmann static void register_curses(void)
809b0766612SGerd Hoffmann {
810b0766612SGerd Hoffmann     qemu_display_register(&qemu_display_curses);
811b0766612SGerd Hoffmann }
812b0766612SGerd Hoffmann 
813b0766612SGerd Hoffmann type_init(register_curses);
814