xref: /openbmc/qemu/ui/curses.c (revision 55522f72149fbf95ee3b057f1419da0cad46d0dd)
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 
41*f8d3116fSBrad Smith #ifdef __APPLE__
42fbab8cc2SStefan Weil #define _XOPEN_SOURCE_EXTENDED 1
43fbab8cc2SStefan Weil #endif
44fbab8cc2SStefan Weil 
45e2f82e92SGerd Hoffmann /* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
46e2f82e92SGerd Hoffmann #undef KEY_EVENT
47e2f82e92SGerd Hoffmann #include <curses.h>
48e2f82e92SGerd Hoffmann #undef KEY_EVENT
49e2f82e92SGerd Hoffmann 
503e230dd2SCorentin Chary #define FONT_HEIGHT 16
513e230dd2SCorentin Chary #define FONT_WIDTH 8
523e230dd2SCorentin Chary 
53459a707eSSamuel Thibault enum maybe_keycode {
54459a707eSSamuel Thibault     CURSES_KEYCODE,
55459a707eSSamuel Thibault     CURSES_CHAR,
56459a707eSSamuel Thibault     CURSES_CHAR_OR_KEYCODE,
57459a707eSSamuel Thibault };
58459a707eSSamuel Thibault 
597c20b4a3SGerd Hoffmann static DisplayChangeListener *dcl;
6076c51fc3SPhilippe Mathieu-Daudé static console_ch_t *screen;
613e230dd2SCorentin Chary static WINDOW *screenpad = NULL;
623e230dd2SCorentin Chary static int width, height, gwidth, gheight, invalidate;
633e230dd2SCorentin Chary static int px, py, sminx, sminy, smaxx, smaxy;
643e230dd2SCorentin Chary 
652f8b7cd5SSamuel Thibault static const char *font_charset = "CP437";
6676c51fc3SPhilippe Mathieu-Daudé static cchar_t *vga_to_curses;
67e2368dc9SOGAWA Hirofumi 
curses_update(DisplayChangeListener * dcl,int x,int y,int w,int h)687c20b4a3SGerd Hoffmann static void curses_update(DisplayChangeListener *dcl,
697c20b4a3SGerd Hoffmann                           int x, int y, int w, int h)
703e230dd2SCorentin Chary {
71e2f82e92SGerd Hoffmann     console_ch_t *line;
722d5f4a71SPhilippe Mathieu-Daudé     g_autofree cchar_t *curses_line = g_new(cchar_t, width);
73962cf8fdSSamuel Thibault     wchar_t wch[CCHARW_MAX];
74962cf8fdSSamuel Thibault     attr_t attrs;
75962cf8fdSSamuel Thibault     short colors;
76962cf8fdSSamuel Thibault     int ret;
773e230dd2SCorentin Chary 
78e2f82e92SGerd Hoffmann     line = screen + y * width;
79e2f82e92SGerd Hoffmann     for (h += y; y < h; y ++, line += width) {
80e2f82e92SGerd Hoffmann         for (x = 0; x < width; x++) {
81cd54ea45SMatthew Kilgore             chtype ch = line[x] & A_CHARTEXT;
82cd54ea45SMatthew Kilgore             chtype at = line[x] & A_ATTRIBUTES;
8330f5a9ddSMatthew Kilgore             short color_pair = PAIR_NUMBER(line[x]);
8430f5a9ddSMatthew Kilgore 
85962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
86962cf8fdSSamuel Thibault             if (ret == ERR || wch[0] == 0) {
87962cf8fdSSamuel Thibault                 wch[0] = ch;
88962cf8fdSSamuel Thibault                 wch[1] = 0;
89e2f82e92SGerd Hoffmann             }
9030f5a9ddSMatthew Kilgore             setcchar(&curses_line[x], wch, at, color_pair, NULL);
91e2f82e92SGerd Hoffmann         }
922f8b7cd5SSamuel Thibault         mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
93e2f82e92SGerd Hoffmann     }
943e230dd2SCorentin Chary 
953e230dd2SCorentin Chary     pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
963e230dd2SCorentin Chary     refresh();
973e230dd2SCorentin Chary }
983e230dd2SCorentin Chary 
curses_calc_pad(void)993e230dd2SCorentin Chary static void curses_calc_pad(void)
1003e230dd2SCorentin Chary {
101e99441a3SAkihiko Odaki     if (qemu_console_is_fixedsize(dcl->con)) {
1023e230dd2SCorentin Chary         width = gwidth;
1033e230dd2SCorentin Chary         height = gheight;
1043e230dd2SCorentin Chary     } else {
1053e230dd2SCorentin Chary         width = COLS;
1063e230dd2SCorentin Chary         height = LINES;
1073e230dd2SCorentin Chary     }
1083e230dd2SCorentin Chary 
1093e230dd2SCorentin Chary     if (screenpad)
1103e230dd2SCorentin Chary         delwin(screenpad);
1113e230dd2SCorentin Chary 
1123e230dd2SCorentin Chary     clear();
1133e230dd2SCorentin Chary     refresh();
1143e230dd2SCorentin Chary 
1153e230dd2SCorentin Chary     screenpad = newpad(height, width);
1163e230dd2SCorentin Chary 
1173e230dd2SCorentin Chary     if (width > COLS) {
1183e230dd2SCorentin Chary         px = (width - COLS) / 2;
1193e230dd2SCorentin Chary         sminx = 0;
1203e230dd2SCorentin Chary         smaxx = COLS;
1213e230dd2SCorentin Chary     } else {
1223e230dd2SCorentin Chary         px = 0;
1233e230dd2SCorentin Chary         sminx = (COLS - width) / 2;
1243e230dd2SCorentin Chary         smaxx = sminx + width;
1253e230dd2SCorentin Chary     }
1263e230dd2SCorentin Chary 
1273e230dd2SCorentin Chary     if (height > LINES) {
1283e230dd2SCorentin Chary         py = (height - LINES) / 2;
1293e230dd2SCorentin Chary         sminy = 0;
1303e230dd2SCorentin Chary         smaxy = LINES;
1313e230dd2SCorentin Chary     } else {
1323e230dd2SCorentin Chary         py = 0;
1333e230dd2SCorentin Chary         sminy = (LINES - height) / 2;
1343e230dd2SCorentin Chary         smaxy = sminy + height;
1353e230dd2SCorentin Chary     }
1363e230dd2SCorentin Chary }
1373e230dd2SCorentin Chary 
curses_resize(DisplayChangeListener * dcl,int width,int height)1387c20b4a3SGerd Hoffmann static void curses_resize(DisplayChangeListener *dcl,
1397c20b4a3SGerd Hoffmann                           int width, int height)
1403e230dd2SCorentin Chary {
141a93a4a22SGerd Hoffmann     if (width == gwidth && height == gheight) {
1423e230dd2SCorentin Chary         return;
143a93a4a22SGerd Hoffmann     }
1443e230dd2SCorentin Chary 
145a93a4a22SGerd Hoffmann     gwidth = width;
146a93a4a22SGerd Hoffmann     gheight = height;
1473e230dd2SCorentin Chary 
1483e230dd2SCorentin Chary     curses_calc_pad();
1493e230dd2SCorentin Chary }
1503e230dd2SCorentin Chary 
151032ac6f8SGerd Hoffmann #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
152032ac6f8SGerd Hoffmann static volatile sig_atomic_t got_sigwinch;
curses_winch_check(void)153032ac6f8SGerd Hoffmann static void curses_winch_check(void)
1543e230dd2SCorentin Chary {
1553e230dd2SCorentin Chary     struct winsize {
1563e230dd2SCorentin Chary         unsigned short ws_row;
1573e230dd2SCorentin Chary         unsigned short ws_col;
1583e230dd2SCorentin Chary         unsigned short ws_xpixel;   /* unused */
1593e230dd2SCorentin Chary         unsigned short ws_ypixel;   /* unused */
1603e230dd2SCorentin Chary     } ws;
1613e230dd2SCorentin Chary 
162032ac6f8SGerd Hoffmann     if (!got_sigwinch) {
1633e230dd2SCorentin Chary         return;
164032ac6f8SGerd Hoffmann     }
165032ac6f8SGerd Hoffmann     got_sigwinch = false;
166032ac6f8SGerd Hoffmann 
167032ac6f8SGerd Hoffmann     if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
168032ac6f8SGerd Hoffmann         return;
169032ac6f8SGerd Hoffmann     }
1703e230dd2SCorentin Chary 
1713e230dd2SCorentin Chary     resize_term(ws.ws_row, ws.ws_col);
1723e230dd2SCorentin Chary     invalidate = 1;
1733e230dd2SCorentin Chary }
174032ac6f8SGerd Hoffmann 
curses_winch_handler(int signum)175032ac6f8SGerd Hoffmann static void curses_winch_handler(int signum)
176032ac6f8SGerd Hoffmann {
177032ac6f8SGerd Hoffmann     got_sigwinch = true;
178032ac6f8SGerd Hoffmann }
179032ac6f8SGerd Hoffmann 
curses_winch_init(void)180032ac6f8SGerd Hoffmann static void curses_winch_init(void)
181032ac6f8SGerd Hoffmann {
182032ac6f8SGerd Hoffmann     struct sigaction old, winch = {
183032ac6f8SGerd Hoffmann         .sa_handler  = curses_winch_handler,
184032ac6f8SGerd Hoffmann     };
185032ac6f8SGerd Hoffmann     sigaction(SIGWINCH, &winch, &old);
186032ac6f8SGerd Hoffmann }
187032ac6f8SGerd Hoffmann #else
curses_winch_check(void)188032ac6f8SGerd Hoffmann static void curses_winch_check(void) {}
curses_winch_init(void)189032ac6f8SGerd Hoffmann static void curses_winch_init(void) {}
1903e230dd2SCorentin Chary #endif
1913e230dd2SCorentin Chary 
curses_cursor_position(DisplayChangeListener * dcl,int x,int y)1927c20b4a3SGerd Hoffmann static void curses_cursor_position(DisplayChangeListener *dcl,
1937c20b4a3SGerd Hoffmann                                    int x, int y)
1943e230dd2SCorentin Chary {
1953e230dd2SCorentin Chary     if (x >= 0) {
1963e230dd2SCorentin Chary         x = sminx + x - px;
1973e230dd2SCorentin Chary         y = sminy + y - py;
1983e230dd2SCorentin Chary 
1993e230dd2SCorentin Chary         if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
2003e230dd2SCorentin Chary             move(y, x);
2013e230dd2SCorentin Chary             curs_set(1);
2023e230dd2SCorentin Chary             /* it seems that curs_set(1) must always be called before
2033e230dd2SCorentin Chary              * curs_set(2) for the latter to have effect */
204e99441a3SAkihiko Odaki             if (!qemu_console_is_graphic(dcl->con)) {
2053e230dd2SCorentin Chary                 curs_set(2);
20681c0d5a6SGerd Hoffmann             }
2073e230dd2SCorentin Chary             return;
2083e230dd2SCorentin Chary         }
2093e230dd2SCorentin Chary     }
2103e230dd2SCorentin Chary 
2113e230dd2SCorentin Chary     curs_set(0);
2123e230dd2SCorentin Chary }
2133e230dd2SCorentin Chary 
2143e230dd2SCorentin Chary /* generic keyboard conversion */
2153e230dd2SCorentin Chary 
2163e230dd2SCorentin Chary #include "curses_keys.h"
2173e230dd2SCorentin Chary 
2183e230dd2SCorentin Chary static kbd_layout_t *kbd_layout = NULL;
2193e230dd2SCorentin Chary 
console_getch(enum maybe_keycode * maybe_keycode)220459a707eSSamuel Thibault static wint_t console_getch(enum maybe_keycode *maybe_keycode)
221459a707eSSamuel Thibault {
222459a707eSSamuel Thibault     wint_t ret;
223459a707eSSamuel Thibault     switch (get_wch(&ret)) {
224459a707eSSamuel Thibault     case KEY_CODE_YES:
225459a707eSSamuel Thibault         *maybe_keycode = CURSES_KEYCODE;
226459a707eSSamuel Thibault         break;
227459a707eSSamuel Thibault     case OK:
228459a707eSSamuel Thibault         *maybe_keycode = CURSES_CHAR;
229459a707eSSamuel Thibault         break;
230459a707eSSamuel Thibault     case ERR:
231459a707eSSamuel Thibault         ret = -1;
232459a707eSSamuel Thibault         break;
23368097ed5SPaolo Bonzini     default:
23468097ed5SPaolo Bonzini         abort();
235459a707eSSamuel Thibault     }
236459a707eSSamuel Thibault     return ret;
237459a707eSSamuel Thibault }
238459a707eSSamuel Thibault 
curses2foo(const int _curses2foo[],const int _curseskey2foo[],int chr,enum maybe_keycode maybe_keycode)239459a707eSSamuel Thibault static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
240459a707eSSamuel Thibault                       int chr, enum maybe_keycode maybe_keycode)
241459a707eSSamuel Thibault {
242459a707eSSamuel Thibault     int ret = -1;
243459a707eSSamuel Thibault     if (maybe_keycode == CURSES_CHAR) {
244459a707eSSamuel Thibault         if (chr < CURSES_CHARS) {
245459a707eSSamuel Thibault             ret = _curses2foo[chr];
246459a707eSSamuel Thibault         }
247459a707eSSamuel Thibault     } else {
248459a707eSSamuel Thibault         if (chr < CURSES_KEYS) {
249459a707eSSamuel Thibault             ret = _curseskey2foo[chr];
250459a707eSSamuel Thibault         }
251459a707eSSamuel Thibault         if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
252459a707eSSamuel Thibault             chr < CURSES_CHARS) {
253459a707eSSamuel Thibault             ret = _curses2foo[chr];
254459a707eSSamuel Thibault         }
255459a707eSSamuel Thibault     }
256459a707eSSamuel Thibault     return ret;
257459a707eSSamuel Thibault }
258459a707eSSamuel Thibault 
259459a707eSSamuel Thibault #define curses2keycode(chr, maybe_keycode) \
260459a707eSSamuel Thibault     curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
261459a707eSSamuel Thibault #define curses2keysym(chr, maybe_keycode) \
262459a707eSSamuel Thibault     curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
263459a707eSSamuel Thibault #define curses2qemu(chr, maybe_keycode) \
264459a707eSSamuel Thibault     curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
265459a707eSSamuel Thibault 
curses_refresh(DisplayChangeListener * dcl)266bc2ed970SGerd Hoffmann static void curses_refresh(DisplayChangeListener *dcl)
2673e230dd2SCorentin Chary {
26899a9ef44SPeter Maydell     int chr, keysym, keycode, keycode_alt;
26965f52797SYonggang Luo     enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
2703e230dd2SCorentin Chary 
271032ac6f8SGerd Hoffmann     curses_winch_check();
272032ac6f8SGerd Hoffmann 
2733e230dd2SCorentin Chary     if (invalidate) {
2743e230dd2SCorentin Chary         clear();
2753e230dd2SCorentin Chary         refresh();
2763e230dd2SCorentin Chary         curses_calc_pad();
277e99441a3SAkihiko Odaki         graphic_hw_invalidate(dcl->con);
2783e230dd2SCorentin Chary         invalidate = 0;
2793e230dd2SCorentin Chary     }
2803e230dd2SCorentin Chary 
281e99441a3SAkihiko Odaki     graphic_hw_text_update(dcl->con, screen);
2823e230dd2SCorentin Chary 
2833e230dd2SCorentin Chary     while (1) {
2843e230dd2SCorentin Chary         /* while there are any pending key strokes to process */
285459a707eSSamuel Thibault         chr = console_getch(&maybe_keycode);
2863e230dd2SCorentin Chary 
287459a707eSSamuel Thibault         if (chr == -1)
2883e230dd2SCorentin Chary             break;
2893e230dd2SCorentin Chary 
2903e230dd2SCorentin Chary #ifdef KEY_RESIZE
2913e230dd2SCorentin Chary         /* this shouldn't occur when we use a custom SIGWINCH handler */
292459a707eSSamuel Thibault         if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
2933e230dd2SCorentin Chary             clear();
2943e230dd2SCorentin Chary             refresh();
2953e230dd2SCorentin Chary             curses_calc_pad();
296bc2ed970SGerd Hoffmann             curses_update(dcl, 0, 0, width, height);
2973e230dd2SCorentin Chary             continue;
2983e230dd2SCorentin Chary         }
2993e230dd2SCorentin Chary #endif
3003e230dd2SCorentin Chary 
301459a707eSSamuel Thibault         keycode = curses2keycode(chr, maybe_keycode);
3023e230dd2SCorentin Chary         keycode_alt = 0;
3033e230dd2SCorentin Chary 
304633786feSSamuel Thibault         /* alt or esc key */
3053e230dd2SCorentin Chary         if (keycode == 1) {
30665f52797SYonggang Luo             enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
307459a707eSSamuel Thibault             int nextchr = console_getch(&next_maybe_keycode);
3083e230dd2SCorentin Chary 
309459a707eSSamuel Thibault             if (nextchr != -1) {
3103e230dd2SCorentin Chary                 chr = nextchr;
311459a707eSSamuel Thibault                 maybe_keycode = next_maybe_keycode;
3123e230dd2SCorentin Chary                 keycode_alt = ALT;
313459a707eSSamuel Thibault                 keycode = curses2keycode(chr, maybe_keycode);
3143e230dd2SCorentin Chary 
3153e230dd2SCorentin Chary                 if (keycode != -1) {
3163e230dd2SCorentin Chary                     keycode |= ALT;
3173e230dd2SCorentin Chary 
3183e230dd2SCorentin Chary                     /* process keys reserved for qemu */
3193e230dd2SCorentin Chary                     if (keycode >= QEMU_KEY_CONSOLE0 &&
3203e230dd2SCorentin Chary                             keycode < QEMU_KEY_CONSOLE0 + 9) {
321e99441a3SAkihiko Odaki                         QemuConsole *con = qemu_console_lookup_by_index(keycode - QEMU_KEY_CONSOLE0);
322e99441a3SAkihiko Odaki                         if (con) {
3233e230dd2SCorentin Chary                             erase();
3243e230dd2SCorentin Chary                             wnoutrefresh(stdscr);
325e99441a3SAkihiko Odaki                             unregister_displaychangelistener(dcl);
326e99441a3SAkihiko Odaki                             dcl->con = con;
327e99441a3SAkihiko Odaki                             register_displaychangelistener(dcl);
3283e230dd2SCorentin Chary 
3293e230dd2SCorentin Chary                             invalidate = 1;
330e99441a3SAkihiko Odaki                         }
3313e230dd2SCorentin Chary                         continue;
3323e230dd2SCorentin Chary                     }
3333e230dd2SCorentin Chary                 }
3343e230dd2SCorentin Chary             }
3353e230dd2SCorentin Chary         }
3363e230dd2SCorentin Chary 
3373e230dd2SCorentin Chary         if (kbd_layout) {
338459a707eSSamuel Thibault             keysym = curses2keysym(chr, maybe_keycode);
3393e230dd2SCorentin Chary 
3403e230dd2SCorentin Chary             if (keysym == -1) {
341d03703c8SSamuel Thibault                 if (chr < ' ') {
342d03703c8SSamuel Thibault                     keysym = chr + '@';
343d03703c8SSamuel Thibault                     if (keysym >= 'A' && keysym <= 'Z')
344d03703c8SSamuel Thibault                         keysym += 'a' - 'A';
345d03703c8SSamuel Thibault                     keysym |= KEYSYM_CNTRL;
346d03703c8SSamuel Thibault                 } else
3473e230dd2SCorentin Chary                     keysym = chr;
3483e230dd2SCorentin Chary             }
3493e230dd2SCorentin Chary 
350abb4f2c9SGerd Hoffmann             keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
35119c1b9fdSGerd Hoffmann                                       NULL, false);
3523e230dd2SCorentin Chary             if (keycode == 0)
3533e230dd2SCorentin Chary                 continue;
3543e230dd2SCorentin Chary 
3553e230dd2SCorentin Chary             keycode |= (keysym & ~KEYSYM_MASK) >> 16;
3563e230dd2SCorentin Chary             keycode |= keycode_alt;
3573e230dd2SCorentin Chary         }
3583e230dd2SCorentin Chary 
3593e230dd2SCorentin Chary         if (keycode == -1)
3603e230dd2SCorentin Chary             continue;
3613e230dd2SCorentin Chary 
362e99441a3SAkihiko Odaki         if (qemu_console_is_graphic(dcl->con)) {
3633e230dd2SCorentin Chary             /* since terminals don't know about key press and release
3643e230dd2SCorentin Chary              * events, we need to emit both for each key received */
365cd100328SGerd Hoffmann             if (keycode & SHIFT) {
366e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, true);
3675a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3683e230dd2SCorentin Chary             }
369cd100328SGerd Hoffmann             if (keycode & CNTRL) {
370e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, true);
3715a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3723e230dd2SCorentin Chary             }
373cd100328SGerd Hoffmann             if (keycode & ALT) {
374e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, ALT_CODE, true);
3755a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
376cd100328SGerd Hoffmann             }
377cd100328SGerd Hoffmann             if (keycode & ALTGR) {
378e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, true);
3795a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
380cd100328SGerd Hoffmann             }
381cd100328SGerd Hoffmann 
382e99441a3SAkihiko Odaki             qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, true);
3835a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
384e99441a3SAkihiko Odaki             qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, false);
3855a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
386cd100328SGerd Hoffmann 
387cd100328SGerd Hoffmann             if (keycode & ALTGR) {
388e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, false);
3895a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
390cd100328SGerd Hoffmann             }
391cd100328SGerd Hoffmann             if (keycode & ALT) {
392e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, ALT_CODE, false);
3935a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
394cd100328SGerd Hoffmann             }
395cd100328SGerd Hoffmann             if (keycode & CNTRL) {
396e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, false);
3975a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
398cd100328SGerd Hoffmann             }
399cd100328SGerd Hoffmann             if (keycode & SHIFT) {
400e99441a3SAkihiko Odaki                 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, false);
4015a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
402cd100328SGerd Hoffmann             }
4033e230dd2SCorentin Chary         } else {
404459a707eSSamuel Thibault             keysym = curses2qemu(chr, maybe_keycode);
4053e230dd2SCorentin Chary             if (keysym == -1)
4063e230dd2SCorentin Chary                 keysym = chr;
4073e230dd2SCorentin Chary 
408e99441a3SAkihiko Odaki             qemu_text_console_put_keysym(QEMU_TEXT_CONSOLE(dcl->con), keysym);
4093e230dd2SCorentin Chary         }
4103e230dd2SCorentin Chary     }
4113e230dd2SCorentin Chary }
4123e230dd2SCorentin Chary 
curses_atexit(void)4133e230dd2SCorentin Chary static void curses_atexit(void)
4143e230dd2SCorentin Chary {
4153e230dd2SCorentin Chary     endwin();
41676c51fc3SPhilippe Mathieu-Daudé     g_free(vga_to_curses);
41776c51fc3SPhilippe Mathieu-Daudé     g_free(screen);
4183e230dd2SCorentin Chary }
4193e230dd2SCorentin Chary 
420b7b664a4SSamuel Thibault /*
421b7b664a4SSamuel Thibault  * In the following:
422b7b664a4SSamuel Thibault  * - fch is the font glyph number
423b7b664a4SSamuel Thibault  * - uch is the unicode value
424b7b664a4SSamuel Thibault  * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
425b7b664a4SSamuel Thibault  * - mbch is the native local-dependent multibyte representation
426b7b664a4SSamuel Thibault  */
427b7b664a4SSamuel Thibault 
4282f8b7cd5SSamuel Thibault /* Setup wchar glyph for one UCS-2 char */
convert_ucs(unsigned char fch,uint16_t uch,iconv_t conv)429b7b664a4SSamuel Thibault static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
4302f8b7cd5SSamuel Thibault {
431b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
432962cf8fdSSamuel Thibault     wchar_t wch[2];
433b7b664a4SSamuel Thibault     char *puch, *pmbch;
434b7b664a4SSamuel Thibault     size_t such, smbch;
435b7b664a4SSamuel Thibault     mbstate_t ps;
4362f8b7cd5SSamuel Thibault 
437b7b664a4SSamuel Thibault     puch = (char *) &uch;
438b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
439b7b664a4SSamuel Thibault     such = sizeof(uch);
440b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4412f8b7cd5SSamuel Thibault 
442b7b664a4SSamuel Thibault     if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
443b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
444b7b664a4SSamuel Thibault                         "from UCS-2 to a multibyte character: %s\n",
445b7b664a4SSamuel Thibault                         uch, strerror(errno));
446b7b664a4SSamuel Thibault         return;
4472f8b7cd5SSamuel Thibault     }
448b7b664a4SSamuel Thibault 
449b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
450962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
451b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
452b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
453b7b664a4SSamuel Thibault                         uch, strerror(errno));
454b7b664a4SSamuel Thibault         return;
455b7b664a4SSamuel Thibault     }
456962cf8fdSSamuel Thibault 
457962cf8fdSSamuel Thibault     wch[1] = 0;
458962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4592f8b7cd5SSamuel Thibault }
4602f8b7cd5SSamuel Thibault 
4612f8b7cd5SSamuel Thibault /* Setup wchar glyph for one font character */
convert_font(unsigned char fch,iconv_t conv)462b7b664a4SSamuel Thibault static void convert_font(unsigned char fch, iconv_t conv)
4632f8b7cd5SSamuel Thibault {
464b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
465962cf8fdSSamuel Thibault     wchar_t wch[2];
466b7b664a4SSamuel Thibault     char *pfch, *pmbch;
467b7b664a4SSamuel Thibault     size_t sfch, smbch;
468b7b664a4SSamuel Thibault     mbstate_t ps;
4692f8b7cd5SSamuel Thibault 
470b7b664a4SSamuel Thibault     pfch = (char *) &fch;
471b7b664a4SSamuel Thibault     pmbch = (char *) &mbch;
472b7b664a4SSamuel Thibault     sfch = sizeof(fch);
473b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4742f8b7cd5SSamuel Thibault 
475b7b664a4SSamuel Thibault     if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
476b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
477b7b664a4SSamuel Thibault                         "from %s to a multibyte character: %s\n",
478b7b664a4SSamuel Thibault                         fch, font_charset, strerror(errno));
479b7b664a4SSamuel Thibault         return;
4802f8b7cd5SSamuel Thibault     }
481b7b664a4SSamuel Thibault 
482b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
483962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
484b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
485b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
486b7b664a4SSamuel Thibault                         fch, strerror(errno));
487b7b664a4SSamuel Thibault         return;
488b7b664a4SSamuel Thibault     }
489962cf8fdSSamuel Thibault 
490962cf8fdSSamuel Thibault     wch[1] = 0;
491962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4922f8b7cd5SSamuel Thibault }
4932f8b7cd5SSamuel Thibault 
4942f8b7cd5SSamuel Thibault /* Convert one wchar to UCS-2 */
get_ucs(wchar_t wch,iconv_t conv)4952f8b7cd5SSamuel Thibault static uint16_t get_ucs(wchar_t wch, iconv_t conv)
4962f8b7cd5SSamuel Thibault {
497b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
498b7b664a4SSamuel Thibault     uint16_t uch;
499b7b664a4SSamuel Thibault     char *pmbch, *puch;
500b7b664a4SSamuel Thibault     size_t smbch, such;
501b7b664a4SSamuel Thibault     mbstate_t ps;
502b7b664a4SSamuel Thibault     int ret;
5032f8b7cd5SSamuel Thibault 
504b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
505b7b664a4SSamuel Thibault     ret = wcrtomb(mbch, wch, &ps);
506b7b664a4SSamuel Thibault     if (ret == -1) {
507dc3c871aSMax Reitz         fprintf(stderr, "Could not convert 0x%04lx "
508b7b664a4SSamuel Thibault                         "from wchar_t to a multibyte character: %s\n",
509dc3c871aSMax Reitz                         (unsigned long)wch, strerror(errno));
5102f8b7cd5SSamuel Thibault         return 0xFFFD;
5112f8b7cd5SSamuel Thibault     }
5122f8b7cd5SSamuel Thibault 
513b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
514b7b664a4SSamuel Thibault     puch = (char *) &uch;
515b7b664a4SSamuel Thibault     smbch = ret;
516b7b664a4SSamuel Thibault     such = sizeof(uch);
517b7b664a4SSamuel Thibault 
518b7b664a4SSamuel Thibault     if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
519dc3c871aSMax Reitz         fprintf(stderr, "Could not convert 0x%04lx "
520b7b664a4SSamuel Thibault                         "from a multibyte character to UCS-2 : %s\n",
521dc3c871aSMax Reitz                         (unsigned long)wch, strerror(errno));
522b7b664a4SSamuel Thibault         return 0xFFFD;
523b7b664a4SSamuel Thibault     }
524b7b664a4SSamuel Thibault 
525b7b664a4SSamuel Thibault     return uch;
5262f8b7cd5SSamuel Thibault }
5272f8b7cd5SSamuel Thibault 
5282f8b7cd5SSamuel Thibault /*
5292f8b7cd5SSamuel Thibault  * Setup mapping for vga to curses line graphics.
5302f8b7cd5SSamuel Thibault  */
font_setup(void)5312f8b7cd5SSamuel Thibault static void font_setup(void)
5322f8b7cd5SSamuel Thibault {
533b7b664a4SSamuel Thibault     iconv_t ucs2_to_nativecharset;
534b7b664a4SSamuel Thibault     iconv_t nativecharset_to_ucs2;
535b7b664a4SSamuel Thibault     iconv_t font_conv;
536b7b664a4SSamuel Thibault     int i;
53780d3ab61SYonggang Luo     g_autofree gchar *local_codeset = g_get_codeset();
538b7b664a4SSamuel Thibault 
5392f8b7cd5SSamuel Thibault     /*
5402f8b7cd5SSamuel Thibault      * Control characters are normally non-printable, but VGA does have
5412f8b7cd5SSamuel Thibault      * well-known glyphs for them.
5422f8b7cd5SSamuel Thibault      */
54380e8c2edSPhilippe Mathieu-Daudé     static const uint16_t control_characters[0x20] = {
5442f8b7cd5SSamuel Thibault       0x0020,
5452f8b7cd5SSamuel Thibault       0x263a,
5462f8b7cd5SSamuel Thibault       0x263b,
5472f8b7cd5SSamuel Thibault       0x2665,
5482f8b7cd5SSamuel Thibault       0x2666,
5492f8b7cd5SSamuel Thibault       0x2663,
5502f8b7cd5SSamuel Thibault       0x2660,
5512f8b7cd5SSamuel Thibault       0x2022,
5522f8b7cd5SSamuel Thibault       0x25d8,
5532f8b7cd5SSamuel Thibault       0x25cb,
5542f8b7cd5SSamuel Thibault       0x25d9,
5552f8b7cd5SSamuel Thibault       0x2642,
5562f8b7cd5SSamuel Thibault       0x2640,
5572f8b7cd5SSamuel Thibault       0x266a,
5582f8b7cd5SSamuel Thibault       0x266b,
5592f8b7cd5SSamuel Thibault       0x263c,
5602f8b7cd5SSamuel Thibault       0x25ba,
5612f8b7cd5SSamuel Thibault       0x25c4,
5622f8b7cd5SSamuel Thibault       0x2195,
5632f8b7cd5SSamuel Thibault       0x203c,
5642f8b7cd5SSamuel Thibault       0x00b6,
5652f8b7cd5SSamuel Thibault       0x00a7,
5662f8b7cd5SSamuel Thibault       0x25ac,
5672f8b7cd5SSamuel Thibault       0x21a8,
5682f8b7cd5SSamuel Thibault       0x2191,
5692f8b7cd5SSamuel Thibault       0x2193,
5702f8b7cd5SSamuel Thibault       0x2192,
5712f8b7cd5SSamuel Thibault       0x2190,
5722f8b7cd5SSamuel Thibault       0x221f,
5732f8b7cd5SSamuel Thibault       0x2194,
5742f8b7cd5SSamuel Thibault       0x25b2,
5752f8b7cd5SSamuel Thibault       0x25bc
5762f8b7cd5SSamuel Thibault     };
5772f8b7cd5SSamuel Thibault 
57880d3ab61SYonggang Luo     ucs2_to_nativecharset = iconv_open(local_codeset, "UCS-2");
579b7b664a4SSamuel Thibault     if (ucs2_to_nativecharset == (iconv_t) -1) {
5802f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
5812f8b7cd5SSamuel Thibault                         strerror(errno));
5822f8b7cd5SSamuel Thibault         exit(1);
5832f8b7cd5SSamuel Thibault     }
5842f8b7cd5SSamuel Thibault 
58580d3ab61SYonggang Luo     nativecharset_to_ucs2 = iconv_open("UCS-2", local_codeset);
586b7b664a4SSamuel Thibault     if (nativecharset_to_ucs2 == (iconv_t) -1) {
587b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
5882f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
5892f8b7cd5SSamuel Thibault                         strerror(errno));
5902f8b7cd5SSamuel Thibault         exit(1);
5912f8b7cd5SSamuel Thibault     }
5922f8b7cd5SSamuel Thibault 
59380d3ab61SYonggang Luo     font_conv = iconv_open(local_codeset, font_charset);
5942f8b7cd5SSamuel Thibault     if (font_conv == (iconv_t) -1) {
595b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
596b7b664a4SSamuel Thibault         iconv_close(nativecharset_to_ucs2);
5972f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
5982f8b7cd5SSamuel Thibault                         font_charset, strerror(errno));
5992f8b7cd5SSamuel Thibault         exit(1);
6002f8b7cd5SSamuel Thibault     }
6012f8b7cd5SSamuel Thibault 
6022f8b7cd5SSamuel Thibault     /* Control characters */
6032f8b7cd5SSamuel Thibault     for (i = 0; i <= 0x1F; i++) {
604b7b664a4SSamuel Thibault         convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
6052f8b7cd5SSamuel Thibault     }
6062f8b7cd5SSamuel Thibault 
6072f8b7cd5SSamuel Thibault     for (i = 0x20; i <= 0xFF; i++) {
6082f8b7cd5SSamuel Thibault         convert_font(i, font_conv);
6092f8b7cd5SSamuel Thibault     }
6102f8b7cd5SSamuel Thibault 
6112f8b7cd5SSamuel Thibault     /* DEL */
612b7b664a4SSamuel Thibault     convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
6132f8b7cd5SSamuel Thibault 
61480d3ab61SYonggang Luo     if (strcmp(local_codeset, "UTF-8")) {
6152f8b7cd5SSamuel Thibault         /* Non-Unicode capable, use termcap equivalents for those available */
6162f8b7cd5SSamuel Thibault         for (i = 0; i <= 0xFF; i++) {
617962cf8fdSSamuel Thibault             wchar_t wch[CCHARW_MAX];
618962cf8fdSSamuel Thibault             attr_t attr;
619962cf8fdSSamuel Thibault             short color;
620962cf8fdSSamuel Thibault             int ret;
621962cf8fdSSamuel Thibault 
622962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
623962cf8fdSSamuel Thibault             if (ret == ERR)
624962cf8fdSSamuel Thibault                 continue;
625962cf8fdSSamuel Thibault 
626962cf8fdSSamuel Thibault             switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
6272f8b7cd5SSamuel Thibault             case 0x00a3:
6282f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_STERLING;
6292f8b7cd5SSamuel Thibault                 break;
6302f8b7cd5SSamuel Thibault             case 0x2591:
6312f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BOARD;
6322f8b7cd5SSamuel Thibault                 break;
6332f8b7cd5SSamuel Thibault             case 0x2592:
6342f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_CKBOARD;
6352f8b7cd5SSamuel Thibault                 break;
6362f8b7cd5SSamuel Thibault             case 0x2502:
6372f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_VLINE;
6382f8b7cd5SSamuel Thibault                 break;
6392f8b7cd5SSamuel Thibault             case 0x2524:
6402f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RTEE;
6412f8b7cd5SSamuel Thibault                 break;
6422f8b7cd5SSamuel Thibault             case 0x2510:
6432f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_URCORNER;
6442f8b7cd5SSamuel Thibault                 break;
6452f8b7cd5SSamuel Thibault             case 0x2514:
6462f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LLCORNER;
6472f8b7cd5SSamuel Thibault                 break;
6482f8b7cd5SSamuel Thibault             case 0x2534:
6492f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BTEE;
6502f8b7cd5SSamuel Thibault                 break;
6512f8b7cd5SSamuel Thibault             case 0x252c:
6522f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_TTEE;
6532f8b7cd5SSamuel Thibault                 break;
6542f8b7cd5SSamuel Thibault             case 0x251c:
6552f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LTEE;
6562f8b7cd5SSamuel Thibault                 break;
6572f8b7cd5SSamuel Thibault             case 0x2500:
6582f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_HLINE;
6592f8b7cd5SSamuel Thibault                 break;
6602f8b7cd5SSamuel Thibault             case 0x253c:
6612f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLUS;
6622f8b7cd5SSamuel Thibault                 break;
6632f8b7cd5SSamuel Thibault             case 0x256c:
6642f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LANTERN;
6652f8b7cd5SSamuel Thibault                 break;
6662f8b7cd5SSamuel Thibault             case 0x256a:
6672f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_NEQUAL;
6682f8b7cd5SSamuel Thibault                 break;
6692f8b7cd5SSamuel Thibault             case 0x2518:
6702f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LRCORNER;
6712f8b7cd5SSamuel Thibault                 break;
6722f8b7cd5SSamuel Thibault             case 0x250c:
6732f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_ULCORNER;
6742f8b7cd5SSamuel Thibault                 break;
6752f8b7cd5SSamuel Thibault             case 0x2588:
6762f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BLOCK;
6772f8b7cd5SSamuel Thibault                 break;
6782f8b7cd5SSamuel Thibault             case 0x03c0:
6792f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PI;
6802f8b7cd5SSamuel Thibault                 break;
6812f8b7cd5SSamuel Thibault             case 0x00b1:
6822f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLMINUS;
6832f8b7cd5SSamuel Thibault                 break;
6842f8b7cd5SSamuel Thibault             case 0x2265:
6852f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_GEQUAL;
6862f8b7cd5SSamuel Thibault                 break;
6872f8b7cd5SSamuel Thibault             case 0x2264:
6882f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LEQUAL;
6892f8b7cd5SSamuel Thibault                 break;
6902f8b7cd5SSamuel Thibault             case 0x00b0:
6912f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DEGREE;
6922f8b7cd5SSamuel Thibault                 break;
6932f8b7cd5SSamuel Thibault             case 0x25a0:
6942f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BULLET;
6952f8b7cd5SSamuel Thibault                 break;
6962f8b7cd5SSamuel Thibault             case 0x2666:
6972f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DIAMOND;
6982f8b7cd5SSamuel Thibault                 break;
6992f8b7cd5SSamuel Thibault             case 0x2192:
7002f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RARROW;
7012f8b7cd5SSamuel Thibault                 break;
7022f8b7cd5SSamuel Thibault             case 0x2190:
7032f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LARROW;
7042f8b7cd5SSamuel Thibault                 break;
7052f8b7cd5SSamuel Thibault             case 0x2191:
7062f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_UARROW;
7072f8b7cd5SSamuel Thibault                 break;
7082f8b7cd5SSamuel Thibault             case 0x2193:
7092f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DARROW;
7102f8b7cd5SSamuel Thibault                 break;
7112f8b7cd5SSamuel Thibault             case 0x23ba:
7122f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S1;
7132f8b7cd5SSamuel Thibault                 break;
7142f8b7cd5SSamuel Thibault             case 0x23bb:
7152f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S3;
7162f8b7cd5SSamuel Thibault                 break;
7172f8b7cd5SSamuel Thibault             case 0x23bc:
7182f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S7;
7192f8b7cd5SSamuel Thibault                 break;
7202f8b7cd5SSamuel Thibault             case 0x23bd:
7212f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S9;
7222f8b7cd5SSamuel Thibault                 break;
7232f8b7cd5SSamuel Thibault             }
7242f8b7cd5SSamuel Thibault         }
7252f8b7cd5SSamuel Thibault     }
726b7b664a4SSamuel Thibault     iconv_close(ucs2_to_nativecharset);
727b7b664a4SSamuel Thibault     iconv_close(nativecharset_to_ucs2);
728a9fda247SSamuel Thibault     iconv_close(font_conv);
7292f8b7cd5SSamuel Thibault }
7302f8b7cd5SSamuel Thibault 
curses_setup(void)7313e230dd2SCorentin Chary static void curses_setup(void)
7323e230dd2SCorentin Chary {
7333e230dd2SCorentin Chary     int i, colour_default[8] = {
7344083733dSOGAWA Hirofumi         [QEMU_COLOR_BLACK]   = COLOR_BLACK,
7354083733dSOGAWA Hirofumi         [QEMU_COLOR_BLUE]    = COLOR_BLUE,
7364083733dSOGAWA Hirofumi         [QEMU_COLOR_GREEN]   = COLOR_GREEN,
7374083733dSOGAWA Hirofumi         [QEMU_COLOR_CYAN]    = COLOR_CYAN,
7384083733dSOGAWA Hirofumi         [QEMU_COLOR_RED]     = COLOR_RED,
7394083733dSOGAWA Hirofumi         [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
7404083733dSOGAWA Hirofumi         [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
7414083733dSOGAWA Hirofumi         [QEMU_COLOR_WHITE]   = COLOR_WHITE,
7423e230dd2SCorentin Chary     };
7433e230dd2SCorentin Chary 
7443e230dd2SCorentin Chary     /* input as raw as possible, let everything be interpreted
7453e230dd2SCorentin Chary      * by the guest system */
7463e230dd2SCorentin Chary     initscr(); noecho(); intrflush(stdscr, FALSE);
7473e230dd2SCorentin Chary     nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
7483e230dd2SCorentin Chary     start_color(); raw(); scrollok(stdscr, FALSE);
749633786feSSamuel Thibault     set_escdelay(25);
7503e230dd2SCorentin Chary 
7514083733dSOGAWA Hirofumi     /* Make color pair to match color format (3bits bg:3bits fg) */
752615220ddSOGAWA Hirofumi     for (i = 0; i < 64; i++) {
7533e230dd2SCorentin Chary         init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
7543e230dd2SCorentin Chary     }
7554083733dSOGAWA Hirofumi     /* Set default color for more than 64 for safety. */
756615220ddSOGAWA Hirofumi     for (i = 64; i < COLOR_PAIRS; i++) {
757615220ddSOGAWA Hirofumi         init_pair(i, COLOR_WHITE, COLOR_BLACK);
758615220ddSOGAWA Hirofumi     }
759e2368dc9SOGAWA Hirofumi 
7602f8b7cd5SSamuel Thibault     font_setup();
761615220ddSOGAWA Hirofumi }
7623e230dd2SCorentin Chary 
curses_keyboard_setup(void)7633e230dd2SCorentin Chary static void curses_keyboard_setup(void)
7643e230dd2SCorentin Chary {
7653e230dd2SCorentin Chary #if defined(__APPLE__)
7663e230dd2SCorentin Chary     /* always use generic keymaps */
7673e230dd2SCorentin Chary     if (!keyboard_layout)
7683e230dd2SCorentin Chary         keyboard_layout = "en-us";
7693e230dd2SCorentin Chary #endif
7703e230dd2SCorentin Chary     if(keyboard_layout) {
771ab4f931eSFei Li         kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
772ab4f931eSFei Li                                           &error_fatal);
7733e230dd2SCorentin Chary     }
7743e230dd2SCorentin Chary }
7753e230dd2SCorentin Chary 
7767c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
7777c20b4a3SGerd Hoffmann     .dpy_name        = "curses",
7787c20b4a3SGerd Hoffmann     .dpy_text_update = curses_update,
7797c20b4a3SGerd Hoffmann     .dpy_text_resize = curses_resize,
7807c20b4a3SGerd Hoffmann     .dpy_refresh     = curses_refresh,
7817c20b4a3SGerd Hoffmann     .dpy_text_cursor = curses_cursor_position,
7827c20b4a3SGerd Hoffmann };
7837c20b4a3SGerd Hoffmann 
curses_display_init(DisplayState * ds,DisplayOptions * opts)784b0766612SGerd Hoffmann static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
7853e230dd2SCorentin Chary {
7863e230dd2SCorentin Chary #ifndef _WIN32
7873e230dd2SCorentin Chary     if (!isatty(1)) {
7883e230dd2SCorentin Chary         fprintf(stderr, "We need a terminal output\n");
7893e230dd2SCorentin Chary         exit(1);
7903e230dd2SCorentin Chary     }
7913e230dd2SCorentin Chary #endif
7923e230dd2SCorentin Chary 
7932f8b7cd5SSamuel Thibault     setlocale(LC_CTYPE, "");
7942f8b7cd5SSamuel Thibault     if (opts->u.curses.charset) {
7952f8b7cd5SSamuel Thibault         font_charset = opts->u.curses.charset;
7962f8b7cd5SSamuel Thibault     }
79776c51fc3SPhilippe Mathieu-Daudé     screen = g_new0(console_ch_t, 160 * 100);
79876c51fc3SPhilippe Mathieu-Daudé     vga_to_curses = g_new0(cchar_t, 256);
7993e230dd2SCorentin Chary     curses_setup();
8003e230dd2SCorentin Chary     curses_keyboard_setup();
8013e230dd2SCorentin Chary     atexit(curses_atexit);
8023e230dd2SCorentin Chary 
803032ac6f8SGerd Hoffmann     curses_winch_init();
8043e230dd2SCorentin Chary 
805fedf0d35SMarkus Armbruster     dcl = g_new0(DisplayChangeListener, 1);
806e99441a3SAkihiko Odaki     dcl->con = qemu_console_lookup_default();
8077c20b4a3SGerd Hoffmann     dcl->ops = &dcl_ops;
8085209089fSGerd Hoffmann     register_displaychangelistener(dcl);
8093e230dd2SCorentin Chary 
8103e230dd2SCorentin Chary     invalidate = 1;
8113e230dd2SCorentin Chary }
812b0766612SGerd Hoffmann 
813b0766612SGerd Hoffmann static QemuDisplay qemu_display_curses = {
814b0766612SGerd Hoffmann     .type       = DISPLAY_TYPE_CURSES,
815b0766612SGerd Hoffmann     .init       = curses_display_init,
816b0766612SGerd Hoffmann };
817b0766612SGerd Hoffmann 
register_curses(void)818b0766612SGerd Hoffmann static void register_curses(void)
819b0766612SGerd Hoffmann {
820b0766612SGerd Hoffmann     qemu_display_register(&qemu_display_curses);
821b0766612SGerd Hoffmann }
822b0766612SGerd Hoffmann 
823b0766612SGerd Hoffmann type_init(register_curses);
824