xref: /openbmc/qemu/ui/curses.c (revision 68097ed5e5bd5bc5bfead8076dbef700a8537b15)
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 <langinfo.h>
342f8b7cd5SSamuel Thibault #include <iconv.h>
353e230dd2SCorentin Chary 
36ab4f931eSFei Li #include "qapi/error.h"
370b8fa32fSMarkus Armbruster #include "qemu/module.h"
3828ecbaeeSPaolo Bonzini #include "ui/console.h"
39cd100328SGerd Hoffmann #include "ui/input.h"
409c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
413e230dd2SCorentin Chary 
42e2f82e92SGerd Hoffmann /* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
43e2f82e92SGerd Hoffmann #undef KEY_EVENT
44e2f82e92SGerd Hoffmann #include <curses.h>
45e2f82e92SGerd Hoffmann #undef KEY_EVENT
46e2f82e92SGerd Hoffmann 
473e230dd2SCorentin Chary #define FONT_HEIGHT 16
483e230dd2SCorentin Chary #define FONT_WIDTH 8
493e230dd2SCorentin Chary 
50459a707eSSamuel Thibault enum maybe_keycode {
51459a707eSSamuel Thibault     CURSES_KEYCODE,
52459a707eSSamuel Thibault     CURSES_CHAR,
53459a707eSSamuel Thibault     CURSES_CHAR_OR_KEYCODE,
54459a707eSSamuel Thibault };
55459a707eSSamuel Thibault 
567c20b4a3SGerd Hoffmann static DisplayChangeListener *dcl;
573e230dd2SCorentin Chary static console_ch_t screen[160 * 100];
583e230dd2SCorentin Chary static WINDOW *screenpad = NULL;
593e230dd2SCorentin Chary static int width, height, gwidth, gheight, invalidate;
603e230dd2SCorentin Chary static int px, py, sminx, sminy, smaxx, smaxy;
613e230dd2SCorentin Chary 
622f8b7cd5SSamuel Thibault static const char *font_charset = "CP437";
632f8b7cd5SSamuel Thibault static cchar_t vga_to_curses[256];
64e2368dc9SOGAWA Hirofumi 
657c20b4a3SGerd Hoffmann static void curses_update(DisplayChangeListener *dcl,
667c20b4a3SGerd Hoffmann                           int x, int y, int w, int h)
673e230dd2SCorentin Chary {
68e2f82e92SGerd Hoffmann     console_ch_t *line;
692f8b7cd5SSamuel Thibault     cchar_t curses_line[width];
70962cf8fdSSamuel Thibault     wchar_t wch[CCHARW_MAX];
71962cf8fdSSamuel Thibault     attr_t attrs;
72962cf8fdSSamuel Thibault     short colors;
73962cf8fdSSamuel Thibault     int ret;
743e230dd2SCorentin Chary 
75e2f82e92SGerd Hoffmann     line = screen + y * width;
76e2f82e92SGerd Hoffmann     for (h += y; y < h; y ++, line += width) {
77e2f82e92SGerd Hoffmann         for (x = 0; x < width; x++) {
78e2f82e92SGerd Hoffmann             chtype ch = line[x] & 0xff;
79e2f82e92SGerd Hoffmann             chtype at = line[x] & ~0xff;
80962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
81962cf8fdSSamuel Thibault             if (ret == ERR || wch[0] == 0) {
82962cf8fdSSamuel Thibault                 wch[0] = ch;
83962cf8fdSSamuel Thibault                 wch[1] = 0;
84e2f82e92SGerd Hoffmann             }
85962cf8fdSSamuel Thibault             setcchar(&curses_line[x], wch, at, 0, NULL);
86e2f82e92SGerd Hoffmann         }
872f8b7cd5SSamuel Thibault         mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
88e2f82e92SGerd Hoffmann     }
893e230dd2SCorentin Chary 
903e230dd2SCorentin Chary     pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
913e230dd2SCorentin Chary     refresh();
923e230dd2SCorentin Chary }
933e230dd2SCorentin Chary 
943e230dd2SCorentin Chary static void curses_calc_pad(void)
953e230dd2SCorentin Chary {
9681c0d5a6SGerd Hoffmann     if (qemu_console_is_fixedsize(NULL)) {
973e230dd2SCorentin Chary         width = gwidth;
983e230dd2SCorentin Chary         height = gheight;
993e230dd2SCorentin Chary     } else {
1003e230dd2SCorentin Chary         width = COLS;
1013e230dd2SCorentin Chary         height = LINES;
1023e230dd2SCorentin Chary     }
1033e230dd2SCorentin Chary 
1043e230dd2SCorentin Chary     if (screenpad)
1053e230dd2SCorentin Chary         delwin(screenpad);
1063e230dd2SCorentin Chary 
1073e230dd2SCorentin Chary     clear();
1083e230dd2SCorentin Chary     refresh();
1093e230dd2SCorentin Chary 
1103e230dd2SCorentin Chary     screenpad = newpad(height, width);
1113e230dd2SCorentin Chary 
1123e230dd2SCorentin Chary     if (width > COLS) {
1133e230dd2SCorentin Chary         px = (width - COLS) / 2;
1143e230dd2SCorentin Chary         sminx = 0;
1153e230dd2SCorentin Chary         smaxx = COLS;
1163e230dd2SCorentin Chary     } else {
1173e230dd2SCorentin Chary         px = 0;
1183e230dd2SCorentin Chary         sminx = (COLS - width) / 2;
1193e230dd2SCorentin Chary         smaxx = sminx + width;
1203e230dd2SCorentin Chary     }
1213e230dd2SCorentin Chary 
1223e230dd2SCorentin Chary     if (height > LINES) {
1233e230dd2SCorentin Chary         py = (height - LINES) / 2;
1243e230dd2SCorentin Chary         sminy = 0;
1253e230dd2SCorentin Chary         smaxy = LINES;
1263e230dd2SCorentin Chary     } else {
1273e230dd2SCorentin Chary         py = 0;
1283e230dd2SCorentin Chary         sminy = (LINES - height) / 2;
1293e230dd2SCorentin Chary         smaxy = sminy + height;
1303e230dd2SCorentin Chary     }
1313e230dd2SCorentin Chary }
1323e230dd2SCorentin Chary 
1337c20b4a3SGerd Hoffmann static void curses_resize(DisplayChangeListener *dcl,
1347c20b4a3SGerd Hoffmann                           int width, int height)
1353e230dd2SCorentin Chary {
136a93a4a22SGerd Hoffmann     if (width == gwidth && height == gheight) {
1373e230dd2SCorentin Chary         return;
138a93a4a22SGerd Hoffmann     }
1393e230dd2SCorentin Chary 
140a93a4a22SGerd Hoffmann     gwidth = width;
141a93a4a22SGerd Hoffmann     gheight = height;
1423e230dd2SCorentin Chary 
1433e230dd2SCorentin Chary     curses_calc_pad();
1443e230dd2SCorentin Chary }
1453e230dd2SCorentin Chary 
146032ac6f8SGerd Hoffmann #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
147032ac6f8SGerd Hoffmann static volatile sig_atomic_t got_sigwinch;
148032ac6f8SGerd Hoffmann static void curses_winch_check(void)
1493e230dd2SCorentin Chary {
1503e230dd2SCorentin Chary     struct winsize {
1513e230dd2SCorentin Chary         unsigned short ws_row;
1523e230dd2SCorentin Chary         unsigned short ws_col;
1533e230dd2SCorentin Chary         unsigned short ws_xpixel;   /* unused */
1543e230dd2SCorentin Chary         unsigned short ws_ypixel;   /* unused */
1553e230dd2SCorentin Chary     } ws;
1563e230dd2SCorentin Chary 
157032ac6f8SGerd Hoffmann     if (!got_sigwinch) {
1583e230dd2SCorentin Chary         return;
159032ac6f8SGerd Hoffmann     }
160032ac6f8SGerd Hoffmann     got_sigwinch = false;
161032ac6f8SGerd Hoffmann 
162032ac6f8SGerd Hoffmann     if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
163032ac6f8SGerd Hoffmann         return;
164032ac6f8SGerd Hoffmann     }
1653e230dd2SCorentin Chary 
1663e230dd2SCorentin Chary     resize_term(ws.ws_row, ws.ws_col);
1673e230dd2SCorentin Chary     invalidate = 1;
1683e230dd2SCorentin Chary }
169032ac6f8SGerd Hoffmann 
170032ac6f8SGerd Hoffmann static void curses_winch_handler(int signum)
171032ac6f8SGerd Hoffmann {
172032ac6f8SGerd Hoffmann     got_sigwinch = true;
173032ac6f8SGerd Hoffmann }
174032ac6f8SGerd Hoffmann 
175032ac6f8SGerd Hoffmann static void curses_winch_init(void)
176032ac6f8SGerd Hoffmann {
177032ac6f8SGerd Hoffmann     struct sigaction old, winch = {
178032ac6f8SGerd Hoffmann         .sa_handler  = curses_winch_handler,
179032ac6f8SGerd Hoffmann     };
180032ac6f8SGerd Hoffmann     sigaction(SIGWINCH, &winch, &old);
181032ac6f8SGerd Hoffmann }
182032ac6f8SGerd Hoffmann #else
183032ac6f8SGerd Hoffmann static void curses_winch_check(void) {}
184032ac6f8SGerd Hoffmann static void curses_winch_init(void) {}
1853e230dd2SCorentin Chary #endif
1863e230dd2SCorentin Chary 
1877c20b4a3SGerd Hoffmann static void curses_cursor_position(DisplayChangeListener *dcl,
1887c20b4a3SGerd Hoffmann                                    int x, int y)
1893e230dd2SCorentin Chary {
1903e230dd2SCorentin Chary     if (x >= 0) {
1913e230dd2SCorentin Chary         x = sminx + x - px;
1923e230dd2SCorentin Chary         y = sminy + y - py;
1933e230dd2SCorentin Chary 
1943e230dd2SCorentin Chary         if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
1953e230dd2SCorentin Chary             move(y, x);
1963e230dd2SCorentin Chary             curs_set(1);
1973e230dd2SCorentin Chary             /* it seems that curs_set(1) must always be called before
1983e230dd2SCorentin Chary              * curs_set(2) for the latter to have effect */
19981c0d5a6SGerd Hoffmann             if (!qemu_console_is_graphic(NULL)) {
2003e230dd2SCorentin Chary                 curs_set(2);
20181c0d5a6SGerd Hoffmann             }
2023e230dd2SCorentin Chary             return;
2033e230dd2SCorentin Chary         }
2043e230dd2SCorentin Chary     }
2053e230dd2SCorentin Chary 
2063e230dd2SCorentin Chary     curs_set(0);
2073e230dd2SCorentin Chary }
2083e230dd2SCorentin Chary 
2093e230dd2SCorentin Chary /* generic keyboard conversion */
2103e230dd2SCorentin Chary 
2113e230dd2SCorentin Chary #include "curses_keys.h"
2123e230dd2SCorentin Chary 
2133e230dd2SCorentin Chary static kbd_layout_t *kbd_layout = NULL;
2143e230dd2SCorentin Chary 
215459a707eSSamuel Thibault static wint_t console_getch(enum maybe_keycode *maybe_keycode)
216459a707eSSamuel Thibault {
217459a707eSSamuel Thibault     wint_t ret;
218459a707eSSamuel Thibault     switch (get_wch(&ret)) {
219459a707eSSamuel Thibault     case KEY_CODE_YES:
220459a707eSSamuel Thibault         *maybe_keycode = CURSES_KEYCODE;
221459a707eSSamuel Thibault         break;
222459a707eSSamuel Thibault     case OK:
223459a707eSSamuel Thibault         *maybe_keycode = CURSES_CHAR;
224459a707eSSamuel Thibault         break;
225459a707eSSamuel Thibault     case ERR:
226459a707eSSamuel Thibault         ret = -1;
227459a707eSSamuel Thibault         break;
228*68097ed5SPaolo Bonzini     default:
229*68097ed5SPaolo Bonzini         abort();
230459a707eSSamuel Thibault     }
231459a707eSSamuel Thibault     return ret;
232459a707eSSamuel Thibault }
233459a707eSSamuel Thibault 
234459a707eSSamuel Thibault static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
235459a707eSSamuel Thibault                       int chr, enum maybe_keycode maybe_keycode)
236459a707eSSamuel Thibault {
237459a707eSSamuel Thibault     int ret = -1;
238459a707eSSamuel Thibault     if (maybe_keycode == CURSES_CHAR) {
239459a707eSSamuel Thibault         if (chr < CURSES_CHARS) {
240459a707eSSamuel Thibault             ret = _curses2foo[chr];
241459a707eSSamuel Thibault         }
242459a707eSSamuel Thibault     } else {
243459a707eSSamuel Thibault         if (chr < CURSES_KEYS) {
244459a707eSSamuel Thibault             ret = _curseskey2foo[chr];
245459a707eSSamuel Thibault         }
246459a707eSSamuel Thibault         if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
247459a707eSSamuel Thibault             chr < CURSES_CHARS) {
248459a707eSSamuel Thibault             ret = _curses2foo[chr];
249459a707eSSamuel Thibault         }
250459a707eSSamuel Thibault     }
251459a707eSSamuel Thibault     return ret;
252459a707eSSamuel Thibault }
253459a707eSSamuel Thibault 
254459a707eSSamuel Thibault #define curses2keycode(chr, maybe_keycode) \
255459a707eSSamuel Thibault     curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
256459a707eSSamuel Thibault #define curses2keysym(chr, maybe_keycode) \
257459a707eSSamuel Thibault     curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
258459a707eSSamuel Thibault #define curses2qemu(chr, maybe_keycode) \
259459a707eSSamuel Thibault     curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
260459a707eSSamuel Thibault 
261bc2ed970SGerd Hoffmann static void curses_refresh(DisplayChangeListener *dcl)
2623e230dd2SCorentin Chary {
26399a9ef44SPeter Maydell     int chr, keysym, keycode, keycode_alt;
264459a707eSSamuel Thibault     enum maybe_keycode maybe_keycode;
2653e230dd2SCorentin Chary 
266032ac6f8SGerd Hoffmann     curses_winch_check();
267032ac6f8SGerd Hoffmann 
2683e230dd2SCorentin Chary     if (invalidate) {
2693e230dd2SCorentin Chary         clear();
2703e230dd2SCorentin Chary         refresh();
2713e230dd2SCorentin Chary         curses_calc_pad();
2721dbfa005SGerd Hoffmann         graphic_hw_invalidate(NULL);
2733e230dd2SCorentin Chary         invalidate = 0;
2743e230dd2SCorentin Chary     }
2753e230dd2SCorentin Chary 
2761dbfa005SGerd Hoffmann     graphic_hw_text_update(NULL, screen);
2773e230dd2SCorentin Chary 
2783e230dd2SCorentin Chary     while (1) {
2793e230dd2SCorentin Chary         /* while there are any pending key strokes to process */
280459a707eSSamuel Thibault         chr = console_getch(&maybe_keycode);
2813e230dd2SCorentin Chary 
282459a707eSSamuel Thibault         if (chr == -1)
2833e230dd2SCorentin Chary             break;
2843e230dd2SCorentin Chary 
2853e230dd2SCorentin Chary #ifdef KEY_RESIZE
2863e230dd2SCorentin Chary         /* this shouldn't occur when we use a custom SIGWINCH handler */
287459a707eSSamuel Thibault         if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
2883e230dd2SCorentin Chary             clear();
2893e230dd2SCorentin Chary             refresh();
2903e230dd2SCorentin Chary             curses_calc_pad();
291bc2ed970SGerd Hoffmann             curses_update(dcl, 0, 0, width, height);
2923e230dd2SCorentin Chary             continue;
2933e230dd2SCorentin Chary         }
2943e230dd2SCorentin Chary #endif
2953e230dd2SCorentin Chary 
296459a707eSSamuel Thibault         keycode = curses2keycode(chr, maybe_keycode);
2973e230dd2SCorentin Chary         keycode_alt = 0;
2983e230dd2SCorentin Chary 
299633786feSSamuel Thibault         /* alt or esc key */
3003e230dd2SCorentin Chary         if (keycode == 1) {
301459a707eSSamuel Thibault             enum maybe_keycode next_maybe_keycode;
302459a707eSSamuel Thibault             int nextchr = console_getch(&next_maybe_keycode);
3033e230dd2SCorentin Chary 
304459a707eSSamuel Thibault             if (nextchr != -1) {
3053e230dd2SCorentin Chary                 chr = nextchr;
306459a707eSSamuel Thibault                 maybe_keycode = next_maybe_keycode;
3073e230dd2SCorentin Chary                 keycode_alt = ALT;
308459a707eSSamuel Thibault                 keycode = curses2keycode(chr, maybe_keycode);
3093e230dd2SCorentin Chary 
3103e230dd2SCorentin Chary                 if (keycode != -1) {
3113e230dd2SCorentin Chary                     keycode |= ALT;
3123e230dd2SCorentin Chary 
3133e230dd2SCorentin Chary                     /* process keys reserved for qemu */
3143e230dd2SCorentin Chary                     if (keycode >= QEMU_KEY_CONSOLE0 &&
3153e230dd2SCorentin Chary                             keycode < QEMU_KEY_CONSOLE0 + 9) {
3163e230dd2SCorentin Chary                         erase();
3173e230dd2SCorentin Chary                         wnoutrefresh(stdscr);
3183e230dd2SCorentin Chary                         console_select(keycode - QEMU_KEY_CONSOLE0);
3193e230dd2SCorentin Chary 
3203e230dd2SCorentin Chary                         invalidate = 1;
3213e230dd2SCorentin Chary                         continue;
3223e230dd2SCorentin Chary                     }
3233e230dd2SCorentin Chary                 }
3243e230dd2SCorentin Chary             }
3253e230dd2SCorentin Chary         }
3263e230dd2SCorentin Chary 
3273e230dd2SCorentin Chary         if (kbd_layout) {
328459a707eSSamuel Thibault             keysym = curses2keysym(chr, maybe_keycode);
3293e230dd2SCorentin Chary 
3303e230dd2SCorentin Chary             if (keysym == -1) {
331d03703c8SSamuel Thibault                 if (chr < ' ') {
332d03703c8SSamuel Thibault                     keysym = chr + '@';
333d03703c8SSamuel Thibault                     if (keysym >= 'A' && keysym <= 'Z')
334d03703c8SSamuel Thibault                         keysym += 'a' - 'A';
335d03703c8SSamuel Thibault                     keysym |= KEYSYM_CNTRL;
336d03703c8SSamuel Thibault                 } else
3373e230dd2SCorentin Chary                     keysym = chr;
3383e230dd2SCorentin Chary             }
3393e230dd2SCorentin Chary 
340abb4f2c9SGerd Hoffmann             keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
34119c1b9fdSGerd Hoffmann                                       NULL, false);
3423e230dd2SCorentin Chary             if (keycode == 0)
3433e230dd2SCorentin Chary                 continue;
3443e230dd2SCorentin Chary 
3453e230dd2SCorentin Chary             keycode |= (keysym & ~KEYSYM_MASK) >> 16;
3463e230dd2SCorentin Chary             keycode |= keycode_alt;
3473e230dd2SCorentin Chary         }
3483e230dd2SCorentin Chary 
3493e230dd2SCorentin Chary         if (keycode == -1)
3503e230dd2SCorentin Chary             continue;
3513e230dd2SCorentin Chary 
35281c0d5a6SGerd Hoffmann         if (qemu_console_is_graphic(NULL)) {
3533e230dd2SCorentin Chary             /* since terminals don't know about key press and release
3543e230dd2SCorentin Chary              * events, we need to emit both for each key received */
355cd100328SGerd Hoffmann             if (keycode & SHIFT) {
356cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
3575a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3583e230dd2SCorentin Chary             }
359cd100328SGerd Hoffmann             if (keycode & CNTRL) {
360cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
3615a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3623e230dd2SCorentin Chary             }
363cd100328SGerd Hoffmann             if (keycode & ALT) {
364cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, ALT_CODE, true);
3655a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
366cd100328SGerd Hoffmann             }
367cd100328SGerd Hoffmann             if (keycode & ALTGR) {
368cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
3695a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
370cd100328SGerd Hoffmann             }
371cd100328SGerd Hoffmann 
372f5c0ab13SAndrew Oates             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
3735a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
374f5c0ab13SAndrew Oates             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
3755a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
376cd100328SGerd Hoffmann 
377cd100328SGerd Hoffmann             if (keycode & ALTGR) {
378cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
3795a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
380cd100328SGerd Hoffmann             }
381cd100328SGerd Hoffmann             if (keycode & ALT) {
382cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, ALT_CODE, false);
3835a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
384cd100328SGerd Hoffmann             }
385cd100328SGerd Hoffmann             if (keycode & CNTRL) {
386cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
3875a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
388cd100328SGerd Hoffmann             }
389cd100328SGerd Hoffmann             if (keycode & SHIFT) {
390cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
3915a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
392cd100328SGerd Hoffmann             }
3933e230dd2SCorentin Chary         } else {
394459a707eSSamuel Thibault             keysym = curses2qemu(chr, maybe_keycode);
3953e230dd2SCorentin Chary             if (keysym == -1)
3963e230dd2SCorentin Chary                 keysym = chr;
3973e230dd2SCorentin Chary 
3983e230dd2SCorentin Chary             kbd_put_keysym(keysym);
3993e230dd2SCorentin Chary         }
4003e230dd2SCorentin Chary     }
4013e230dd2SCorentin Chary }
4023e230dd2SCorentin Chary 
4033e230dd2SCorentin Chary static void curses_atexit(void)
4043e230dd2SCorentin Chary {
4053e230dd2SCorentin Chary     endwin();
4063e230dd2SCorentin Chary }
4073e230dd2SCorentin Chary 
408b7b664a4SSamuel Thibault /*
409b7b664a4SSamuel Thibault  * In the following:
410b7b664a4SSamuel Thibault  * - fch is the font glyph number
411b7b664a4SSamuel Thibault  * - uch is the unicode value
412b7b664a4SSamuel Thibault  * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
413b7b664a4SSamuel Thibault  * - mbch is the native local-dependent multibyte representation
414b7b664a4SSamuel Thibault  */
415b7b664a4SSamuel Thibault 
4162f8b7cd5SSamuel Thibault /* Setup wchar glyph for one UCS-2 char */
417b7b664a4SSamuel Thibault static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
4182f8b7cd5SSamuel Thibault {
419b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
420962cf8fdSSamuel Thibault     wchar_t wch[2];
421b7b664a4SSamuel Thibault     char *puch, *pmbch;
422b7b664a4SSamuel Thibault     size_t such, smbch;
423b7b664a4SSamuel Thibault     mbstate_t ps;
4242f8b7cd5SSamuel Thibault 
425b7b664a4SSamuel Thibault     puch = (char *) &uch;
426b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
427b7b664a4SSamuel Thibault     such = sizeof(uch);
428b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4292f8b7cd5SSamuel Thibault 
430b7b664a4SSamuel Thibault     if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
431b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
432b7b664a4SSamuel Thibault                         "from UCS-2 to a multibyte character: %s\n",
433b7b664a4SSamuel Thibault                         uch, strerror(errno));
434b7b664a4SSamuel Thibault         return;
4352f8b7cd5SSamuel Thibault     }
436b7b664a4SSamuel Thibault 
437b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
438962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
439b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
440b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
441b7b664a4SSamuel Thibault                         uch, strerror(errno));
442b7b664a4SSamuel Thibault         return;
443b7b664a4SSamuel Thibault     }
444962cf8fdSSamuel Thibault 
445962cf8fdSSamuel Thibault     wch[1] = 0;
446962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4472f8b7cd5SSamuel Thibault }
4482f8b7cd5SSamuel Thibault 
4492f8b7cd5SSamuel Thibault /* Setup wchar glyph for one font character */
450b7b664a4SSamuel Thibault static void convert_font(unsigned char fch, iconv_t conv)
4512f8b7cd5SSamuel Thibault {
452b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
453962cf8fdSSamuel Thibault     wchar_t wch[2];
454b7b664a4SSamuel Thibault     char *pfch, *pmbch;
455b7b664a4SSamuel Thibault     size_t sfch, smbch;
456b7b664a4SSamuel Thibault     mbstate_t ps;
4572f8b7cd5SSamuel Thibault 
458b7b664a4SSamuel Thibault     pfch = (char *) &fch;
459b7b664a4SSamuel Thibault     pmbch = (char *) &mbch;
460b7b664a4SSamuel Thibault     sfch = sizeof(fch);
461b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4622f8b7cd5SSamuel Thibault 
463b7b664a4SSamuel Thibault     if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
464b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
465b7b664a4SSamuel Thibault                         "from %s to a multibyte character: %s\n",
466b7b664a4SSamuel Thibault                         fch, font_charset, strerror(errno));
467b7b664a4SSamuel Thibault         return;
4682f8b7cd5SSamuel Thibault     }
469b7b664a4SSamuel Thibault 
470b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
471962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
472b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
473b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
474b7b664a4SSamuel Thibault                         fch, strerror(errno));
475b7b664a4SSamuel Thibault         return;
476b7b664a4SSamuel Thibault     }
477962cf8fdSSamuel Thibault 
478962cf8fdSSamuel Thibault     wch[1] = 0;
479962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4802f8b7cd5SSamuel Thibault }
4812f8b7cd5SSamuel Thibault 
4822f8b7cd5SSamuel Thibault /* Convert one wchar to UCS-2 */
4832f8b7cd5SSamuel Thibault static uint16_t get_ucs(wchar_t wch, iconv_t conv)
4842f8b7cd5SSamuel Thibault {
485b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
486b7b664a4SSamuel Thibault     uint16_t uch;
487b7b664a4SSamuel Thibault     char *pmbch, *puch;
488b7b664a4SSamuel Thibault     size_t smbch, such;
489b7b664a4SSamuel Thibault     mbstate_t ps;
490b7b664a4SSamuel Thibault     int ret;
4912f8b7cd5SSamuel Thibault 
492b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
493b7b664a4SSamuel Thibault     ret = wcrtomb(mbch, wch, &ps);
494b7b664a4SSamuel Thibault     if (ret == -1) {
495dc3c871aSMax Reitz         fprintf(stderr, "Could not convert 0x%04lx "
496b7b664a4SSamuel Thibault                         "from wchar_t to a multibyte character: %s\n",
497dc3c871aSMax Reitz                         (unsigned long)wch, strerror(errno));
4982f8b7cd5SSamuel Thibault         return 0xFFFD;
4992f8b7cd5SSamuel Thibault     }
5002f8b7cd5SSamuel Thibault 
501b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
502b7b664a4SSamuel Thibault     puch = (char *) &uch;
503b7b664a4SSamuel Thibault     smbch = ret;
504b7b664a4SSamuel Thibault     such = sizeof(uch);
505b7b664a4SSamuel Thibault 
506b7b664a4SSamuel Thibault     if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
507dc3c871aSMax Reitz         fprintf(stderr, "Could not convert 0x%04lx "
508b7b664a4SSamuel Thibault                         "from a multibyte character to UCS-2 : %s\n",
509dc3c871aSMax Reitz                         (unsigned long)wch, strerror(errno));
510b7b664a4SSamuel Thibault         return 0xFFFD;
511b7b664a4SSamuel Thibault     }
512b7b664a4SSamuel Thibault 
513b7b664a4SSamuel Thibault     return uch;
5142f8b7cd5SSamuel Thibault }
5152f8b7cd5SSamuel Thibault 
5162f8b7cd5SSamuel Thibault /*
5172f8b7cd5SSamuel Thibault  * Setup mapping for vga to curses line graphics.
5182f8b7cd5SSamuel Thibault  */
5192f8b7cd5SSamuel Thibault static void font_setup(void)
5202f8b7cd5SSamuel Thibault {
521b7b664a4SSamuel Thibault     iconv_t ucs2_to_nativecharset;
522b7b664a4SSamuel Thibault     iconv_t nativecharset_to_ucs2;
523b7b664a4SSamuel Thibault     iconv_t font_conv;
524b7b664a4SSamuel Thibault     int i;
525b7b664a4SSamuel Thibault 
5262f8b7cd5SSamuel Thibault     /*
5272f8b7cd5SSamuel Thibault      * Control characters are normally non-printable, but VGA does have
5282f8b7cd5SSamuel Thibault      * well-known glyphs for them.
5292f8b7cd5SSamuel Thibault      */
5302f8b7cd5SSamuel Thibault     static uint16_t control_characters[0x20] = {
5312f8b7cd5SSamuel Thibault       0x0020,
5322f8b7cd5SSamuel Thibault       0x263a,
5332f8b7cd5SSamuel Thibault       0x263b,
5342f8b7cd5SSamuel Thibault       0x2665,
5352f8b7cd5SSamuel Thibault       0x2666,
5362f8b7cd5SSamuel Thibault       0x2663,
5372f8b7cd5SSamuel Thibault       0x2660,
5382f8b7cd5SSamuel Thibault       0x2022,
5392f8b7cd5SSamuel Thibault       0x25d8,
5402f8b7cd5SSamuel Thibault       0x25cb,
5412f8b7cd5SSamuel Thibault       0x25d9,
5422f8b7cd5SSamuel Thibault       0x2642,
5432f8b7cd5SSamuel Thibault       0x2640,
5442f8b7cd5SSamuel Thibault       0x266a,
5452f8b7cd5SSamuel Thibault       0x266b,
5462f8b7cd5SSamuel Thibault       0x263c,
5472f8b7cd5SSamuel Thibault       0x25ba,
5482f8b7cd5SSamuel Thibault       0x25c4,
5492f8b7cd5SSamuel Thibault       0x2195,
5502f8b7cd5SSamuel Thibault       0x203c,
5512f8b7cd5SSamuel Thibault       0x00b6,
5522f8b7cd5SSamuel Thibault       0x00a7,
5532f8b7cd5SSamuel Thibault       0x25ac,
5542f8b7cd5SSamuel Thibault       0x21a8,
5552f8b7cd5SSamuel Thibault       0x2191,
5562f8b7cd5SSamuel Thibault       0x2193,
5572f8b7cd5SSamuel Thibault       0x2192,
5582f8b7cd5SSamuel Thibault       0x2190,
5592f8b7cd5SSamuel Thibault       0x221f,
5602f8b7cd5SSamuel Thibault       0x2194,
5612f8b7cd5SSamuel Thibault       0x25b2,
5622f8b7cd5SSamuel Thibault       0x25bc
5632f8b7cd5SSamuel Thibault     };
5642f8b7cd5SSamuel Thibault 
565b7b664a4SSamuel Thibault     ucs2_to_nativecharset = iconv_open(nl_langinfo(CODESET), "UCS-2");
566b7b664a4SSamuel Thibault     if (ucs2_to_nativecharset == (iconv_t) -1) {
5672f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
5682f8b7cd5SSamuel Thibault                         strerror(errno));
5692f8b7cd5SSamuel Thibault         exit(1);
5702f8b7cd5SSamuel Thibault     }
5712f8b7cd5SSamuel Thibault 
572b7b664a4SSamuel Thibault     nativecharset_to_ucs2 = iconv_open("UCS-2", nl_langinfo(CODESET));
573b7b664a4SSamuel Thibault     if (nativecharset_to_ucs2 == (iconv_t) -1) {
574b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
5752f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
5762f8b7cd5SSamuel Thibault                         strerror(errno));
5772f8b7cd5SSamuel Thibault         exit(1);
5782f8b7cd5SSamuel Thibault     }
5792f8b7cd5SSamuel Thibault 
580b7b664a4SSamuel Thibault     font_conv = iconv_open(nl_langinfo(CODESET), font_charset);
5812f8b7cd5SSamuel Thibault     if (font_conv == (iconv_t) -1) {
582b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
583b7b664a4SSamuel Thibault         iconv_close(nativecharset_to_ucs2);
5842f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
5852f8b7cd5SSamuel Thibault                         font_charset, strerror(errno));
5862f8b7cd5SSamuel Thibault         exit(1);
5872f8b7cd5SSamuel Thibault     }
5882f8b7cd5SSamuel Thibault 
5892f8b7cd5SSamuel Thibault     /* Control characters */
5902f8b7cd5SSamuel Thibault     for (i = 0; i <= 0x1F; i++) {
591b7b664a4SSamuel Thibault         convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
5922f8b7cd5SSamuel Thibault     }
5932f8b7cd5SSamuel Thibault 
5942f8b7cd5SSamuel Thibault     for (i = 0x20; i <= 0xFF; i++) {
5952f8b7cd5SSamuel Thibault         convert_font(i, font_conv);
5962f8b7cd5SSamuel Thibault     }
5972f8b7cd5SSamuel Thibault 
5982f8b7cd5SSamuel Thibault     /* DEL */
599b7b664a4SSamuel Thibault     convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
6002f8b7cd5SSamuel Thibault 
6012f8b7cd5SSamuel Thibault     if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
6022f8b7cd5SSamuel Thibault         /* Non-Unicode capable, use termcap equivalents for those available */
6032f8b7cd5SSamuel Thibault         for (i = 0; i <= 0xFF; i++) {
604962cf8fdSSamuel Thibault             wchar_t wch[CCHARW_MAX];
605962cf8fdSSamuel Thibault             attr_t attr;
606962cf8fdSSamuel Thibault             short color;
607962cf8fdSSamuel Thibault             int ret;
608962cf8fdSSamuel Thibault 
609962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
610962cf8fdSSamuel Thibault             if (ret == ERR)
611962cf8fdSSamuel Thibault                 continue;
612962cf8fdSSamuel Thibault 
613962cf8fdSSamuel Thibault             switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
6142f8b7cd5SSamuel Thibault             case 0x00a3:
6152f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_STERLING;
6162f8b7cd5SSamuel Thibault                 break;
6172f8b7cd5SSamuel Thibault             case 0x2591:
6182f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BOARD;
6192f8b7cd5SSamuel Thibault                 break;
6202f8b7cd5SSamuel Thibault             case 0x2592:
6212f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_CKBOARD;
6222f8b7cd5SSamuel Thibault                 break;
6232f8b7cd5SSamuel Thibault             case 0x2502:
6242f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_VLINE;
6252f8b7cd5SSamuel Thibault                 break;
6262f8b7cd5SSamuel Thibault             case 0x2524:
6272f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RTEE;
6282f8b7cd5SSamuel Thibault                 break;
6292f8b7cd5SSamuel Thibault             case 0x2510:
6302f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_URCORNER;
6312f8b7cd5SSamuel Thibault                 break;
6322f8b7cd5SSamuel Thibault             case 0x2514:
6332f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LLCORNER;
6342f8b7cd5SSamuel Thibault                 break;
6352f8b7cd5SSamuel Thibault             case 0x2534:
6362f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BTEE;
6372f8b7cd5SSamuel Thibault                 break;
6382f8b7cd5SSamuel Thibault             case 0x252c:
6392f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_TTEE;
6402f8b7cd5SSamuel Thibault                 break;
6412f8b7cd5SSamuel Thibault             case 0x251c:
6422f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LTEE;
6432f8b7cd5SSamuel Thibault                 break;
6442f8b7cd5SSamuel Thibault             case 0x2500:
6452f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_HLINE;
6462f8b7cd5SSamuel Thibault                 break;
6472f8b7cd5SSamuel Thibault             case 0x253c:
6482f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLUS;
6492f8b7cd5SSamuel Thibault                 break;
6502f8b7cd5SSamuel Thibault             case 0x256c:
6512f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LANTERN;
6522f8b7cd5SSamuel Thibault                 break;
6532f8b7cd5SSamuel Thibault             case 0x256a:
6542f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_NEQUAL;
6552f8b7cd5SSamuel Thibault                 break;
6562f8b7cd5SSamuel Thibault             case 0x2518:
6572f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LRCORNER;
6582f8b7cd5SSamuel Thibault                 break;
6592f8b7cd5SSamuel Thibault             case 0x250c:
6602f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_ULCORNER;
6612f8b7cd5SSamuel Thibault                 break;
6622f8b7cd5SSamuel Thibault             case 0x2588:
6632f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BLOCK;
6642f8b7cd5SSamuel Thibault                 break;
6652f8b7cd5SSamuel Thibault             case 0x03c0:
6662f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PI;
6672f8b7cd5SSamuel Thibault                 break;
6682f8b7cd5SSamuel Thibault             case 0x00b1:
6692f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLMINUS;
6702f8b7cd5SSamuel Thibault                 break;
6712f8b7cd5SSamuel Thibault             case 0x2265:
6722f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_GEQUAL;
6732f8b7cd5SSamuel Thibault                 break;
6742f8b7cd5SSamuel Thibault             case 0x2264:
6752f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LEQUAL;
6762f8b7cd5SSamuel Thibault                 break;
6772f8b7cd5SSamuel Thibault             case 0x00b0:
6782f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DEGREE;
6792f8b7cd5SSamuel Thibault                 break;
6802f8b7cd5SSamuel Thibault             case 0x25a0:
6812f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BULLET;
6822f8b7cd5SSamuel Thibault                 break;
6832f8b7cd5SSamuel Thibault             case 0x2666:
6842f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DIAMOND;
6852f8b7cd5SSamuel Thibault                 break;
6862f8b7cd5SSamuel Thibault             case 0x2192:
6872f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RARROW;
6882f8b7cd5SSamuel Thibault                 break;
6892f8b7cd5SSamuel Thibault             case 0x2190:
6902f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LARROW;
6912f8b7cd5SSamuel Thibault                 break;
6922f8b7cd5SSamuel Thibault             case 0x2191:
6932f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_UARROW;
6942f8b7cd5SSamuel Thibault                 break;
6952f8b7cd5SSamuel Thibault             case 0x2193:
6962f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DARROW;
6972f8b7cd5SSamuel Thibault                 break;
6982f8b7cd5SSamuel Thibault             case 0x23ba:
6992f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S1;
7002f8b7cd5SSamuel Thibault                 break;
7012f8b7cd5SSamuel Thibault             case 0x23bb:
7022f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S3;
7032f8b7cd5SSamuel Thibault                 break;
7042f8b7cd5SSamuel Thibault             case 0x23bc:
7052f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S7;
7062f8b7cd5SSamuel Thibault                 break;
7072f8b7cd5SSamuel Thibault             case 0x23bd:
7082f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S9;
7092f8b7cd5SSamuel Thibault                 break;
7102f8b7cd5SSamuel Thibault             }
7112f8b7cd5SSamuel Thibault         }
7122f8b7cd5SSamuel Thibault     }
713b7b664a4SSamuel Thibault     iconv_close(ucs2_to_nativecharset);
714b7b664a4SSamuel Thibault     iconv_close(nativecharset_to_ucs2);
715a9fda247SSamuel Thibault     iconv_close(font_conv);
7162f8b7cd5SSamuel Thibault }
7172f8b7cd5SSamuel Thibault 
7183e230dd2SCorentin Chary static void curses_setup(void)
7193e230dd2SCorentin Chary {
7203e230dd2SCorentin Chary     int i, colour_default[8] = {
7214083733dSOGAWA Hirofumi         [QEMU_COLOR_BLACK]   = COLOR_BLACK,
7224083733dSOGAWA Hirofumi         [QEMU_COLOR_BLUE]    = COLOR_BLUE,
7234083733dSOGAWA Hirofumi         [QEMU_COLOR_GREEN]   = COLOR_GREEN,
7244083733dSOGAWA Hirofumi         [QEMU_COLOR_CYAN]    = COLOR_CYAN,
7254083733dSOGAWA Hirofumi         [QEMU_COLOR_RED]     = COLOR_RED,
7264083733dSOGAWA Hirofumi         [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
7274083733dSOGAWA Hirofumi         [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
7284083733dSOGAWA Hirofumi         [QEMU_COLOR_WHITE]   = COLOR_WHITE,
7293e230dd2SCorentin Chary     };
7303e230dd2SCorentin Chary 
7313e230dd2SCorentin Chary     /* input as raw as possible, let everything be interpreted
7323e230dd2SCorentin Chary      * by the guest system */
7333e230dd2SCorentin Chary     initscr(); noecho(); intrflush(stdscr, FALSE);
7343e230dd2SCorentin Chary     nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
7353e230dd2SCorentin Chary     start_color(); raw(); scrollok(stdscr, FALSE);
736633786feSSamuel Thibault     set_escdelay(25);
7373e230dd2SCorentin Chary 
7384083733dSOGAWA Hirofumi     /* Make color pair to match color format (3bits bg:3bits fg) */
739615220ddSOGAWA Hirofumi     for (i = 0; i < 64; i++) {
7403e230dd2SCorentin Chary         init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
7413e230dd2SCorentin Chary     }
7424083733dSOGAWA Hirofumi     /* Set default color for more than 64 for safety. */
743615220ddSOGAWA Hirofumi     for (i = 64; i < COLOR_PAIRS; i++) {
744615220ddSOGAWA Hirofumi         init_pair(i, COLOR_WHITE, COLOR_BLACK);
745615220ddSOGAWA Hirofumi     }
746e2368dc9SOGAWA Hirofumi 
7472f8b7cd5SSamuel Thibault     font_setup();
748615220ddSOGAWA Hirofumi }
7493e230dd2SCorentin Chary 
7503e230dd2SCorentin Chary static void curses_keyboard_setup(void)
7513e230dd2SCorentin Chary {
7523e230dd2SCorentin Chary #if defined(__APPLE__)
7533e230dd2SCorentin Chary     /* always use generic keymaps */
7543e230dd2SCorentin Chary     if (!keyboard_layout)
7553e230dd2SCorentin Chary         keyboard_layout = "en-us";
7563e230dd2SCorentin Chary #endif
7573e230dd2SCorentin Chary     if(keyboard_layout) {
758ab4f931eSFei Li         kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
759ab4f931eSFei Li                                           &error_fatal);
7603e230dd2SCorentin Chary     }
7613e230dd2SCorentin Chary }
7623e230dd2SCorentin Chary 
7637c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
7647c20b4a3SGerd Hoffmann     .dpy_name        = "curses",
7657c20b4a3SGerd Hoffmann     .dpy_text_update = curses_update,
7667c20b4a3SGerd Hoffmann     .dpy_text_resize = curses_resize,
7677c20b4a3SGerd Hoffmann     .dpy_refresh     = curses_refresh,
7687c20b4a3SGerd Hoffmann     .dpy_text_cursor = curses_cursor_position,
7697c20b4a3SGerd Hoffmann };
7707c20b4a3SGerd Hoffmann 
771b0766612SGerd Hoffmann static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
7723e230dd2SCorentin Chary {
7733e230dd2SCorentin Chary #ifndef _WIN32
7743e230dd2SCorentin Chary     if (!isatty(1)) {
7753e230dd2SCorentin Chary         fprintf(stderr, "We need a terminal output\n");
7763e230dd2SCorentin Chary         exit(1);
7773e230dd2SCorentin Chary     }
7783e230dd2SCorentin Chary #endif
7793e230dd2SCorentin Chary 
7802f8b7cd5SSamuel Thibault     setlocale(LC_CTYPE, "");
7812f8b7cd5SSamuel Thibault     if (opts->u.curses.charset) {
7822f8b7cd5SSamuel Thibault         font_charset = opts->u.curses.charset;
7832f8b7cd5SSamuel Thibault     }
7843e230dd2SCorentin Chary     curses_setup();
7853e230dd2SCorentin Chary     curses_keyboard_setup();
7863e230dd2SCorentin Chary     atexit(curses_atexit);
7873e230dd2SCorentin Chary 
788032ac6f8SGerd Hoffmann     curses_winch_init();
7893e230dd2SCorentin Chary 
790fedf0d35SMarkus Armbruster     dcl = g_new0(DisplayChangeListener, 1);
7917c20b4a3SGerd Hoffmann     dcl->ops = &dcl_ops;
7925209089fSGerd Hoffmann     register_displaychangelistener(dcl);
7933e230dd2SCorentin Chary 
7943e230dd2SCorentin Chary     invalidate = 1;
7953e230dd2SCorentin Chary }
796b0766612SGerd Hoffmann 
797b0766612SGerd Hoffmann static QemuDisplay qemu_display_curses = {
798b0766612SGerd Hoffmann     .type       = DISPLAY_TYPE_CURSES,
799b0766612SGerd Hoffmann     .init       = curses_display_init,
800b0766612SGerd Hoffmann };
801b0766612SGerd Hoffmann 
802b0766612SGerd Hoffmann static void register_curses(void)
803b0766612SGerd Hoffmann {
804b0766612SGerd Hoffmann     qemu_display_register(&qemu_display_curses);
805b0766612SGerd Hoffmann }
806b0766612SGerd Hoffmann 
807b0766612SGerd Hoffmann type_init(register_curses);
808