xref: /openbmc/qemu/ui/curses.c (revision 962cf8fd4faefb2d5f425abc5c988ea7ba2bbce9)
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  */
24e16f4c87SPeter Maydell #include "qemu/osdep.h"
253e230dd2SCorentin Chary 
263e230dd2SCorentin Chary #ifndef _WIN32
273e230dd2SCorentin Chary #include <sys/ioctl.h>
283e230dd2SCorentin Chary #include <termios.h>
293e230dd2SCorentin Chary #endif
302f8b7cd5SSamuel Thibault #include <locale.h>
312f8b7cd5SSamuel Thibault #include <wchar.h>
322f8b7cd5SSamuel Thibault #include <langinfo.h>
332f8b7cd5SSamuel Thibault #include <iconv.h>
343e230dd2SCorentin Chary 
35ab4f931eSFei Li #include "qapi/error.h"
363e230dd2SCorentin Chary #include "qemu-common.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;
563e230dd2SCorentin Chary static console_ch_t screen[160 * 100];
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";
622f8b7cd5SSamuel Thibault static cchar_t vga_to_curses[256];
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];
69*962cf8fdSSamuel Thibault     wchar_t wch[CCHARW_MAX];
70*962cf8fdSSamuel Thibault     attr_t attrs;
71*962cf8fdSSamuel Thibault     short colors;
72*962cf8fdSSamuel 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++) {
77e2f82e92SGerd Hoffmann             chtype ch = line[x] & 0xff;
78e2f82e92SGerd Hoffmann             chtype at = line[x] & ~0xff;
79*962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
80*962cf8fdSSamuel Thibault             if (ret == ERR || wch[0] == 0) {
81*962cf8fdSSamuel Thibault                 wch[0] = ch;
82*962cf8fdSSamuel Thibault                 wch[1] = 0;
83e2f82e92SGerd Hoffmann             }
84*962cf8fdSSamuel Thibault             setcchar(&curses_line[x], wch, at, 0, NULL);
85e2f82e92SGerd Hoffmann         }
862f8b7cd5SSamuel Thibault         mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
87e2f82e92SGerd Hoffmann     }
883e230dd2SCorentin Chary 
893e230dd2SCorentin Chary     pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
903e230dd2SCorentin Chary     refresh();
913e230dd2SCorentin Chary }
923e230dd2SCorentin Chary 
933e230dd2SCorentin Chary static void curses_calc_pad(void)
943e230dd2SCorentin Chary {
9581c0d5a6SGerd Hoffmann     if (qemu_console_is_fixedsize(NULL)) {
963e230dd2SCorentin Chary         width = gwidth;
973e230dd2SCorentin Chary         height = gheight;
983e230dd2SCorentin Chary     } else {
993e230dd2SCorentin Chary         width = COLS;
1003e230dd2SCorentin Chary         height = LINES;
1013e230dd2SCorentin Chary     }
1023e230dd2SCorentin Chary 
1033e230dd2SCorentin Chary     if (screenpad)
1043e230dd2SCorentin Chary         delwin(screenpad);
1053e230dd2SCorentin Chary 
1063e230dd2SCorentin Chary     clear();
1073e230dd2SCorentin Chary     refresh();
1083e230dd2SCorentin Chary 
1093e230dd2SCorentin Chary     screenpad = newpad(height, width);
1103e230dd2SCorentin Chary 
1113e230dd2SCorentin Chary     if (width > COLS) {
1123e230dd2SCorentin Chary         px = (width - COLS) / 2;
1133e230dd2SCorentin Chary         sminx = 0;
1143e230dd2SCorentin Chary         smaxx = COLS;
1153e230dd2SCorentin Chary     } else {
1163e230dd2SCorentin Chary         px = 0;
1173e230dd2SCorentin Chary         sminx = (COLS - width) / 2;
1183e230dd2SCorentin Chary         smaxx = sminx + width;
1193e230dd2SCorentin Chary     }
1203e230dd2SCorentin Chary 
1213e230dd2SCorentin Chary     if (height > LINES) {
1223e230dd2SCorentin Chary         py = (height - LINES) / 2;
1233e230dd2SCorentin Chary         sminy = 0;
1243e230dd2SCorentin Chary         smaxy = LINES;
1253e230dd2SCorentin Chary     } else {
1263e230dd2SCorentin Chary         py = 0;
1273e230dd2SCorentin Chary         sminy = (LINES - height) / 2;
1283e230dd2SCorentin Chary         smaxy = sminy + height;
1293e230dd2SCorentin Chary     }
1303e230dd2SCorentin Chary }
1313e230dd2SCorentin Chary 
1327c20b4a3SGerd Hoffmann static void curses_resize(DisplayChangeListener *dcl,
1337c20b4a3SGerd Hoffmann                           int width, int height)
1343e230dd2SCorentin Chary {
135a93a4a22SGerd Hoffmann     if (width == gwidth && height == gheight) {
1363e230dd2SCorentin Chary         return;
137a93a4a22SGerd Hoffmann     }
1383e230dd2SCorentin Chary 
139a93a4a22SGerd Hoffmann     gwidth = width;
140a93a4a22SGerd Hoffmann     gheight = height;
1413e230dd2SCorentin Chary 
1423e230dd2SCorentin Chary     curses_calc_pad();
1433e230dd2SCorentin Chary }
1443e230dd2SCorentin Chary 
145032ac6f8SGerd Hoffmann #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
146032ac6f8SGerd Hoffmann static volatile sig_atomic_t got_sigwinch;
147032ac6f8SGerd Hoffmann static void curses_winch_check(void)
1483e230dd2SCorentin Chary {
1493e230dd2SCorentin Chary     struct winsize {
1503e230dd2SCorentin Chary         unsigned short ws_row;
1513e230dd2SCorentin Chary         unsigned short ws_col;
1523e230dd2SCorentin Chary         unsigned short ws_xpixel;   /* unused */
1533e230dd2SCorentin Chary         unsigned short ws_ypixel;   /* unused */
1543e230dd2SCorentin Chary     } ws;
1553e230dd2SCorentin Chary 
156032ac6f8SGerd Hoffmann     if (!got_sigwinch) {
1573e230dd2SCorentin Chary         return;
158032ac6f8SGerd Hoffmann     }
159032ac6f8SGerd Hoffmann     got_sigwinch = false;
160032ac6f8SGerd Hoffmann 
161032ac6f8SGerd Hoffmann     if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
162032ac6f8SGerd Hoffmann         return;
163032ac6f8SGerd Hoffmann     }
1643e230dd2SCorentin Chary 
1653e230dd2SCorentin Chary     resize_term(ws.ws_row, ws.ws_col);
1663e230dd2SCorentin Chary     invalidate = 1;
1673e230dd2SCorentin Chary }
168032ac6f8SGerd Hoffmann 
169032ac6f8SGerd Hoffmann static void curses_winch_handler(int signum)
170032ac6f8SGerd Hoffmann {
171032ac6f8SGerd Hoffmann     got_sigwinch = true;
172032ac6f8SGerd Hoffmann }
173032ac6f8SGerd Hoffmann 
174032ac6f8SGerd Hoffmann static void curses_winch_init(void)
175032ac6f8SGerd Hoffmann {
176032ac6f8SGerd Hoffmann     struct sigaction old, winch = {
177032ac6f8SGerd Hoffmann         .sa_handler  = curses_winch_handler,
178032ac6f8SGerd Hoffmann     };
179032ac6f8SGerd Hoffmann     sigaction(SIGWINCH, &winch, &old);
180032ac6f8SGerd Hoffmann }
181032ac6f8SGerd Hoffmann #else
182032ac6f8SGerd Hoffmann static void curses_winch_check(void) {}
183032ac6f8SGerd Hoffmann static void curses_winch_init(void) {}
1843e230dd2SCorentin Chary #endif
1853e230dd2SCorentin Chary 
1867c20b4a3SGerd Hoffmann static void curses_cursor_position(DisplayChangeListener *dcl,
1877c20b4a3SGerd Hoffmann                                    int x, int y)
1883e230dd2SCorentin Chary {
1893e230dd2SCorentin Chary     if (x >= 0) {
1903e230dd2SCorentin Chary         x = sminx + x - px;
1913e230dd2SCorentin Chary         y = sminy + y - py;
1923e230dd2SCorentin Chary 
1933e230dd2SCorentin Chary         if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
1943e230dd2SCorentin Chary             move(y, x);
1953e230dd2SCorentin Chary             curs_set(1);
1963e230dd2SCorentin Chary             /* it seems that curs_set(1) must always be called before
1973e230dd2SCorentin Chary              * curs_set(2) for the latter to have effect */
19881c0d5a6SGerd Hoffmann             if (!qemu_console_is_graphic(NULL)) {
1993e230dd2SCorentin Chary                 curs_set(2);
20081c0d5a6SGerd Hoffmann             }
2013e230dd2SCorentin Chary             return;
2023e230dd2SCorentin Chary         }
2033e230dd2SCorentin Chary     }
2043e230dd2SCorentin Chary 
2053e230dd2SCorentin Chary     curs_set(0);
2063e230dd2SCorentin Chary }
2073e230dd2SCorentin Chary 
2083e230dd2SCorentin Chary /* generic keyboard conversion */
2093e230dd2SCorentin Chary 
2103e230dd2SCorentin Chary #include "curses_keys.h"
2113e230dd2SCorentin Chary 
2123e230dd2SCorentin Chary static kbd_layout_t *kbd_layout = NULL;
2133e230dd2SCorentin Chary 
214459a707eSSamuel Thibault static wint_t console_getch(enum maybe_keycode *maybe_keycode)
215459a707eSSamuel Thibault {
216459a707eSSamuel Thibault     wint_t ret;
217459a707eSSamuel Thibault     switch (get_wch(&ret)) {
218459a707eSSamuel Thibault     case KEY_CODE_YES:
219459a707eSSamuel Thibault         *maybe_keycode = CURSES_KEYCODE;
220459a707eSSamuel Thibault         break;
221459a707eSSamuel Thibault     case OK:
222459a707eSSamuel Thibault         *maybe_keycode = CURSES_CHAR;
223459a707eSSamuel Thibault         break;
224459a707eSSamuel Thibault     case ERR:
225459a707eSSamuel Thibault         ret = -1;
226459a707eSSamuel Thibault         break;
227459a707eSSamuel Thibault     }
228459a707eSSamuel Thibault     return ret;
229459a707eSSamuel Thibault }
230459a707eSSamuel Thibault 
231459a707eSSamuel Thibault static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
232459a707eSSamuel Thibault                       int chr, enum maybe_keycode maybe_keycode)
233459a707eSSamuel Thibault {
234459a707eSSamuel Thibault     int ret = -1;
235459a707eSSamuel Thibault     if (maybe_keycode == CURSES_CHAR) {
236459a707eSSamuel Thibault         if (chr < CURSES_CHARS) {
237459a707eSSamuel Thibault             ret = _curses2foo[chr];
238459a707eSSamuel Thibault         }
239459a707eSSamuel Thibault     } else {
240459a707eSSamuel Thibault         if (chr < CURSES_KEYS) {
241459a707eSSamuel Thibault             ret = _curseskey2foo[chr];
242459a707eSSamuel Thibault         }
243459a707eSSamuel Thibault         if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
244459a707eSSamuel Thibault             chr < CURSES_CHARS) {
245459a707eSSamuel Thibault             ret = _curses2foo[chr];
246459a707eSSamuel Thibault         }
247459a707eSSamuel Thibault     }
248459a707eSSamuel Thibault     return ret;
249459a707eSSamuel Thibault }
250459a707eSSamuel Thibault 
251459a707eSSamuel Thibault #define curses2keycode(chr, maybe_keycode) \
252459a707eSSamuel Thibault     curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
253459a707eSSamuel Thibault #define curses2keysym(chr, maybe_keycode) \
254459a707eSSamuel Thibault     curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
255459a707eSSamuel Thibault #define curses2qemu(chr, maybe_keycode) \
256459a707eSSamuel Thibault     curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
257459a707eSSamuel Thibault 
258bc2ed970SGerd Hoffmann static void curses_refresh(DisplayChangeListener *dcl)
2593e230dd2SCorentin Chary {
26099a9ef44SPeter Maydell     int chr, keysym, keycode, keycode_alt;
261459a707eSSamuel Thibault     enum maybe_keycode maybe_keycode;
2623e230dd2SCorentin Chary 
263032ac6f8SGerd Hoffmann     curses_winch_check();
264032ac6f8SGerd Hoffmann 
2653e230dd2SCorentin Chary     if (invalidate) {
2663e230dd2SCorentin Chary         clear();
2673e230dd2SCorentin Chary         refresh();
2683e230dd2SCorentin Chary         curses_calc_pad();
2691dbfa005SGerd Hoffmann         graphic_hw_invalidate(NULL);
2703e230dd2SCorentin Chary         invalidate = 0;
2713e230dd2SCorentin Chary     }
2723e230dd2SCorentin Chary 
2731dbfa005SGerd Hoffmann     graphic_hw_text_update(NULL, screen);
2743e230dd2SCorentin Chary 
2753e230dd2SCorentin Chary     while (1) {
2763e230dd2SCorentin Chary         /* while there are any pending key strokes to process */
277459a707eSSamuel Thibault         chr = console_getch(&maybe_keycode);
2783e230dd2SCorentin Chary 
279459a707eSSamuel Thibault         if (chr == -1)
2803e230dd2SCorentin Chary             break;
2813e230dd2SCorentin Chary 
2823e230dd2SCorentin Chary #ifdef KEY_RESIZE
2833e230dd2SCorentin Chary         /* this shouldn't occur when we use a custom SIGWINCH handler */
284459a707eSSamuel Thibault         if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
2853e230dd2SCorentin Chary             clear();
2863e230dd2SCorentin Chary             refresh();
2873e230dd2SCorentin Chary             curses_calc_pad();
288bc2ed970SGerd Hoffmann             curses_update(dcl, 0, 0, width, height);
2893e230dd2SCorentin Chary             continue;
2903e230dd2SCorentin Chary         }
2913e230dd2SCorentin Chary #endif
2923e230dd2SCorentin Chary 
293459a707eSSamuel Thibault         keycode = curses2keycode(chr, maybe_keycode);
2943e230dd2SCorentin Chary         keycode_alt = 0;
2953e230dd2SCorentin Chary 
296633786feSSamuel Thibault         /* alt or esc key */
2973e230dd2SCorentin Chary         if (keycode == 1) {
298459a707eSSamuel Thibault             enum maybe_keycode next_maybe_keycode;
299459a707eSSamuel Thibault             int nextchr = console_getch(&next_maybe_keycode);
3003e230dd2SCorentin Chary 
301459a707eSSamuel Thibault             if (nextchr != -1) {
3023e230dd2SCorentin Chary                 chr = nextchr;
303459a707eSSamuel Thibault                 maybe_keycode = next_maybe_keycode;
3043e230dd2SCorentin Chary                 keycode_alt = ALT;
305459a707eSSamuel Thibault                 keycode = curses2keycode(chr, maybe_keycode);
3063e230dd2SCorentin Chary 
3073e230dd2SCorentin Chary                 if (keycode != -1) {
3083e230dd2SCorentin Chary                     keycode |= ALT;
3093e230dd2SCorentin Chary 
3103e230dd2SCorentin Chary                     /* process keys reserved for qemu */
3113e230dd2SCorentin Chary                     if (keycode >= QEMU_KEY_CONSOLE0 &&
3123e230dd2SCorentin Chary                             keycode < QEMU_KEY_CONSOLE0 + 9) {
3133e230dd2SCorentin Chary                         erase();
3143e230dd2SCorentin Chary                         wnoutrefresh(stdscr);
3153e230dd2SCorentin Chary                         console_select(keycode - QEMU_KEY_CONSOLE0);
3163e230dd2SCorentin Chary 
3173e230dd2SCorentin Chary                         invalidate = 1;
3183e230dd2SCorentin Chary                         continue;
3193e230dd2SCorentin Chary                     }
3203e230dd2SCorentin Chary                 }
3213e230dd2SCorentin Chary             }
3223e230dd2SCorentin Chary         }
3233e230dd2SCorentin Chary 
3243e230dd2SCorentin Chary         if (kbd_layout) {
325459a707eSSamuel Thibault             keysym = curses2keysym(chr, maybe_keycode);
3263e230dd2SCorentin Chary 
3273e230dd2SCorentin Chary             if (keysym == -1) {
328d03703c8SSamuel Thibault                 if (chr < ' ') {
329d03703c8SSamuel Thibault                     keysym = chr + '@';
330d03703c8SSamuel Thibault                     if (keysym >= 'A' && keysym <= 'Z')
331d03703c8SSamuel Thibault                         keysym += 'a' - 'A';
332d03703c8SSamuel Thibault                     keysym |= KEYSYM_CNTRL;
333d03703c8SSamuel Thibault                 } else
3343e230dd2SCorentin Chary                     keysym = chr;
3353e230dd2SCorentin Chary             }
3363e230dd2SCorentin Chary 
337abb4f2c9SGerd Hoffmann             keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
33819c1b9fdSGerd Hoffmann                                       NULL, false);
3393e230dd2SCorentin Chary             if (keycode == 0)
3403e230dd2SCorentin Chary                 continue;
3413e230dd2SCorentin Chary 
3423e230dd2SCorentin Chary             keycode |= (keysym & ~KEYSYM_MASK) >> 16;
3433e230dd2SCorentin Chary             keycode |= keycode_alt;
3443e230dd2SCorentin Chary         }
3453e230dd2SCorentin Chary 
3463e230dd2SCorentin Chary         if (keycode == -1)
3473e230dd2SCorentin Chary             continue;
3483e230dd2SCorentin Chary 
34981c0d5a6SGerd Hoffmann         if (qemu_console_is_graphic(NULL)) {
3503e230dd2SCorentin Chary             /* since terminals don't know about key press and release
3513e230dd2SCorentin Chary              * events, we need to emit both for each key received */
352cd100328SGerd Hoffmann             if (keycode & SHIFT) {
353cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
3545a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3553e230dd2SCorentin Chary             }
356cd100328SGerd Hoffmann             if (keycode & CNTRL) {
357cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
3585a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
3593e230dd2SCorentin Chary             }
360cd100328SGerd Hoffmann             if (keycode & ALT) {
361cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, ALT_CODE, true);
3625a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
363cd100328SGerd Hoffmann             }
364cd100328SGerd Hoffmann             if (keycode & ALTGR) {
365cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
3665a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
367cd100328SGerd Hoffmann             }
368cd100328SGerd Hoffmann 
369f5c0ab13SAndrew Oates             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
3705a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
371f5c0ab13SAndrew Oates             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
3725a165668SGerd Hoffmann             qemu_input_event_send_key_delay(0);
373cd100328SGerd Hoffmann 
374cd100328SGerd Hoffmann             if (keycode & ALTGR) {
375cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
3765a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
377cd100328SGerd Hoffmann             }
378cd100328SGerd Hoffmann             if (keycode & ALT) {
379cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, ALT_CODE, false);
3805a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
381cd100328SGerd Hoffmann             }
382cd100328SGerd Hoffmann             if (keycode & CNTRL) {
383cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
3845a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
385cd100328SGerd Hoffmann             }
386cd100328SGerd Hoffmann             if (keycode & SHIFT) {
387cd100328SGerd Hoffmann                 qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
3885a165668SGerd Hoffmann                 qemu_input_event_send_key_delay(0);
389cd100328SGerd Hoffmann             }
3903e230dd2SCorentin Chary         } else {
391459a707eSSamuel Thibault             keysym = curses2qemu(chr, maybe_keycode);
3923e230dd2SCorentin Chary             if (keysym == -1)
3933e230dd2SCorentin Chary                 keysym = chr;
3943e230dd2SCorentin Chary 
3953e230dd2SCorentin Chary             kbd_put_keysym(keysym);
3963e230dd2SCorentin Chary         }
3973e230dd2SCorentin Chary     }
3983e230dd2SCorentin Chary }
3993e230dd2SCorentin Chary 
4003e230dd2SCorentin Chary static void curses_atexit(void)
4013e230dd2SCorentin Chary {
4023e230dd2SCorentin Chary     endwin();
4033e230dd2SCorentin Chary }
4043e230dd2SCorentin Chary 
405b7b664a4SSamuel Thibault /*
406b7b664a4SSamuel Thibault  * In the following:
407b7b664a4SSamuel Thibault  * - fch is the font glyph number
408b7b664a4SSamuel Thibault  * - uch is the unicode value
409b7b664a4SSamuel Thibault  * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
410b7b664a4SSamuel Thibault  * - mbch is the native local-dependent multibyte representation
411b7b664a4SSamuel Thibault  */
412b7b664a4SSamuel Thibault 
4132f8b7cd5SSamuel Thibault /* Setup wchar glyph for one UCS-2 char */
414b7b664a4SSamuel Thibault static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
4152f8b7cd5SSamuel Thibault {
416b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
417*962cf8fdSSamuel Thibault     wchar_t wch[2];
418b7b664a4SSamuel Thibault     char *puch, *pmbch;
419b7b664a4SSamuel Thibault     size_t such, smbch;
420b7b664a4SSamuel Thibault     mbstate_t ps;
4212f8b7cd5SSamuel Thibault 
422b7b664a4SSamuel Thibault     puch = (char *) &uch;
423b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
424b7b664a4SSamuel Thibault     such = sizeof(uch);
425b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4262f8b7cd5SSamuel Thibault 
427b7b664a4SSamuel Thibault     if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
428b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
429b7b664a4SSamuel Thibault                         "from UCS-2 to a multibyte character: %s\n",
430b7b664a4SSamuel Thibault                         uch, strerror(errno));
431b7b664a4SSamuel Thibault         return;
4322f8b7cd5SSamuel Thibault     }
433b7b664a4SSamuel Thibault 
434b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
435*962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
436b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
437b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
438b7b664a4SSamuel Thibault                         uch, strerror(errno));
439b7b664a4SSamuel Thibault         return;
440b7b664a4SSamuel Thibault     }
441*962cf8fdSSamuel Thibault 
442*962cf8fdSSamuel Thibault     wch[1] = 0;
443*962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4442f8b7cd5SSamuel Thibault }
4452f8b7cd5SSamuel Thibault 
4462f8b7cd5SSamuel Thibault /* Setup wchar glyph for one font character */
447b7b664a4SSamuel Thibault static void convert_font(unsigned char fch, iconv_t conv)
4482f8b7cd5SSamuel Thibault {
449b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
450*962cf8fdSSamuel Thibault     wchar_t wch[2];
451b7b664a4SSamuel Thibault     char *pfch, *pmbch;
452b7b664a4SSamuel Thibault     size_t sfch, smbch;
453b7b664a4SSamuel Thibault     mbstate_t ps;
4542f8b7cd5SSamuel Thibault 
455b7b664a4SSamuel Thibault     pfch = (char *) &fch;
456b7b664a4SSamuel Thibault     pmbch = (char *) &mbch;
457b7b664a4SSamuel Thibault     sfch = sizeof(fch);
458b7b664a4SSamuel Thibault     smbch = sizeof(mbch);
4592f8b7cd5SSamuel Thibault 
460b7b664a4SSamuel Thibault     if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
461b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
462b7b664a4SSamuel Thibault                         "from %s to a multibyte character: %s\n",
463b7b664a4SSamuel Thibault                         fch, font_charset, strerror(errno));
464b7b664a4SSamuel Thibault         return;
4652f8b7cd5SSamuel Thibault     }
466b7b664a4SSamuel Thibault 
467b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
468*962cf8fdSSamuel Thibault     if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
469b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert font glyph 0x%02x "
470b7b664a4SSamuel Thibault                         "from a multibyte character to wchar_t: %s\n",
471b7b664a4SSamuel Thibault                         fch, strerror(errno));
472b7b664a4SSamuel Thibault         return;
473b7b664a4SSamuel Thibault     }
474*962cf8fdSSamuel Thibault 
475*962cf8fdSSamuel Thibault     wch[1] = 0;
476*962cf8fdSSamuel Thibault     setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
4772f8b7cd5SSamuel Thibault }
4782f8b7cd5SSamuel Thibault 
4792f8b7cd5SSamuel Thibault /* Convert one wchar to UCS-2 */
4802f8b7cd5SSamuel Thibault static uint16_t get_ucs(wchar_t wch, iconv_t conv)
4812f8b7cd5SSamuel Thibault {
482b7b664a4SSamuel Thibault     char mbch[MB_LEN_MAX];
483b7b664a4SSamuel Thibault     uint16_t uch;
484b7b664a4SSamuel Thibault     char *pmbch, *puch;
485b7b664a4SSamuel Thibault     size_t smbch, such;
486b7b664a4SSamuel Thibault     mbstate_t ps;
487b7b664a4SSamuel Thibault     int ret;
4882f8b7cd5SSamuel Thibault 
489b7b664a4SSamuel Thibault     memset(&ps, 0, sizeof(ps));
490b7b664a4SSamuel Thibault     ret = wcrtomb(mbch, wch, &ps);
491b7b664a4SSamuel Thibault     if (ret == -1) {
492b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
493b7b664a4SSamuel Thibault                         "from wchar_t to a multibyte character: %s\n",
494b7b664a4SSamuel Thibault                         wch, strerror(errno));
4952f8b7cd5SSamuel Thibault         return 0xFFFD;
4962f8b7cd5SSamuel Thibault     }
4972f8b7cd5SSamuel Thibault 
498b7b664a4SSamuel Thibault     pmbch = (char *) mbch;
499b7b664a4SSamuel Thibault     puch = (char *) &uch;
500b7b664a4SSamuel Thibault     smbch = ret;
501b7b664a4SSamuel Thibault     such = sizeof(uch);
502b7b664a4SSamuel Thibault 
503b7b664a4SSamuel Thibault     if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
504b7b664a4SSamuel Thibault         fprintf(stderr, "Could not convert 0x%04x "
505b7b664a4SSamuel Thibault                         "from a multibyte character to UCS-2 : %s\n",
506b7b664a4SSamuel Thibault                         wch, strerror(errno));
507b7b664a4SSamuel Thibault         return 0xFFFD;
508b7b664a4SSamuel Thibault     }
509b7b664a4SSamuel Thibault 
510b7b664a4SSamuel Thibault     return uch;
5112f8b7cd5SSamuel Thibault }
5122f8b7cd5SSamuel Thibault 
5132f8b7cd5SSamuel Thibault /*
5142f8b7cd5SSamuel Thibault  * Setup mapping for vga to curses line graphics.
5152f8b7cd5SSamuel Thibault  */
5162f8b7cd5SSamuel Thibault static void font_setup(void)
5172f8b7cd5SSamuel Thibault {
518b7b664a4SSamuel Thibault     iconv_t ucs2_to_nativecharset;
519b7b664a4SSamuel Thibault     iconv_t nativecharset_to_ucs2;
520b7b664a4SSamuel Thibault     iconv_t font_conv;
521b7b664a4SSamuel Thibault     int i;
522b7b664a4SSamuel Thibault 
5232f8b7cd5SSamuel Thibault     /*
5242f8b7cd5SSamuel Thibault      * Control characters are normally non-printable, but VGA does have
5252f8b7cd5SSamuel Thibault      * well-known glyphs for them.
5262f8b7cd5SSamuel Thibault      */
5272f8b7cd5SSamuel Thibault     static uint16_t control_characters[0x20] = {
5282f8b7cd5SSamuel Thibault       0x0020,
5292f8b7cd5SSamuel Thibault       0x263a,
5302f8b7cd5SSamuel Thibault       0x263b,
5312f8b7cd5SSamuel Thibault       0x2665,
5322f8b7cd5SSamuel Thibault       0x2666,
5332f8b7cd5SSamuel Thibault       0x2663,
5342f8b7cd5SSamuel Thibault       0x2660,
5352f8b7cd5SSamuel Thibault       0x2022,
5362f8b7cd5SSamuel Thibault       0x25d8,
5372f8b7cd5SSamuel Thibault       0x25cb,
5382f8b7cd5SSamuel Thibault       0x25d9,
5392f8b7cd5SSamuel Thibault       0x2642,
5402f8b7cd5SSamuel Thibault       0x2640,
5412f8b7cd5SSamuel Thibault       0x266a,
5422f8b7cd5SSamuel Thibault       0x266b,
5432f8b7cd5SSamuel Thibault       0x263c,
5442f8b7cd5SSamuel Thibault       0x25ba,
5452f8b7cd5SSamuel Thibault       0x25c4,
5462f8b7cd5SSamuel Thibault       0x2195,
5472f8b7cd5SSamuel Thibault       0x203c,
5482f8b7cd5SSamuel Thibault       0x00b6,
5492f8b7cd5SSamuel Thibault       0x00a7,
5502f8b7cd5SSamuel Thibault       0x25ac,
5512f8b7cd5SSamuel Thibault       0x21a8,
5522f8b7cd5SSamuel Thibault       0x2191,
5532f8b7cd5SSamuel Thibault       0x2193,
5542f8b7cd5SSamuel Thibault       0x2192,
5552f8b7cd5SSamuel Thibault       0x2190,
5562f8b7cd5SSamuel Thibault       0x221f,
5572f8b7cd5SSamuel Thibault       0x2194,
5582f8b7cd5SSamuel Thibault       0x25b2,
5592f8b7cd5SSamuel Thibault       0x25bc
5602f8b7cd5SSamuel Thibault     };
5612f8b7cd5SSamuel Thibault 
562b7b664a4SSamuel Thibault     ucs2_to_nativecharset = iconv_open(nl_langinfo(CODESET), "UCS-2");
563b7b664a4SSamuel Thibault     if (ucs2_to_nativecharset == (iconv_t) -1) {
5642f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
5652f8b7cd5SSamuel Thibault                         strerror(errno));
5662f8b7cd5SSamuel Thibault         exit(1);
5672f8b7cd5SSamuel Thibault     }
5682f8b7cd5SSamuel Thibault 
569b7b664a4SSamuel Thibault     nativecharset_to_ucs2 = iconv_open("UCS-2", nl_langinfo(CODESET));
570b7b664a4SSamuel Thibault     if (nativecharset_to_ucs2 == (iconv_t) -1) {
571b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
5722f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
5732f8b7cd5SSamuel Thibault                         strerror(errno));
5742f8b7cd5SSamuel Thibault         exit(1);
5752f8b7cd5SSamuel Thibault     }
5762f8b7cd5SSamuel Thibault 
577b7b664a4SSamuel Thibault     font_conv = iconv_open(nl_langinfo(CODESET), font_charset);
5782f8b7cd5SSamuel Thibault     if (font_conv == (iconv_t) -1) {
579b7b664a4SSamuel Thibault         iconv_close(ucs2_to_nativecharset);
580b7b664a4SSamuel Thibault         iconv_close(nativecharset_to_ucs2);
5812f8b7cd5SSamuel Thibault         fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
5822f8b7cd5SSamuel Thibault                         font_charset, strerror(errno));
5832f8b7cd5SSamuel Thibault         exit(1);
5842f8b7cd5SSamuel Thibault     }
5852f8b7cd5SSamuel Thibault 
5862f8b7cd5SSamuel Thibault     /* Control characters */
5872f8b7cd5SSamuel Thibault     for (i = 0; i <= 0x1F; i++) {
588b7b664a4SSamuel Thibault         convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
5892f8b7cd5SSamuel Thibault     }
5902f8b7cd5SSamuel Thibault 
5912f8b7cd5SSamuel Thibault     for (i = 0x20; i <= 0xFF; i++) {
5922f8b7cd5SSamuel Thibault         convert_font(i, font_conv);
5932f8b7cd5SSamuel Thibault     }
5942f8b7cd5SSamuel Thibault 
5952f8b7cd5SSamuel Thibault     /* DEL */
596b7b664a4SSamuel Thibault     convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
5972f8b7cd5SSamuel Thibault 
5982f8b7cd5SSamuel Thibault     if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
5992f8b7cd5SSamuel Thibault         /* Non-Unicode capable, use termcap equivalents for those available */
6002f8b7cd5SSamuel Thibault         for (i = 0; i <= 0xFF; i++) {
601*962cf8fdSSamuel Thibault             wchar_t wch[CCHARW_MAX];
602*962cf8fdSSamuel Thibault             attr_t attr;
603*962cf8fdSSamuel Thibault             short color;
604*962cf8fdSSamuel Thibault             int ret;
605*962cf8fdSSamuel Thibault 
606*962cf8fdSSamuel Thibault             ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
607*962cf8fdSSamuel Thibault             if (ret == ERR)
608*962cf8fdSSamuel Thibault                 continue;
609*962cf8fdSSamuel Thibault 
610*962cf8fdSSamuel Thibault             switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
6112f8b7cd5SSamuel Thibault             case 0x00a3:
6122f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_STERLING;
6132f8b7cd5SSamuel Thibault                 break;
6142f8b7cd5SSamuel Thibault             case 0x2591:
6152f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BOARD;
6162f8b7cd5SSamuel Thibault                 break;
6172f8b7cd5SSamuel Thibault             case 0x2592:
6182f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_CKBOARD;
6192f8b7cd5SSamuel Thibault                 break;
6202f8b7cd5SSamuel Thibault             case 0x2502:
6212f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_VLINE;
6222f8b7cd5SSamuel Thibault                 break;
6232f8b7cd5SSamuel Thibault             case 0x2524:
6242f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RTEE;
6252f8b7cd5SSamuel Thibault                 break;
6262f8b7cd5SSamuel Thibault             case 0x2510:
6272f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_URCORNER;
6282f8b7cd5SSamuel Thibault                 break;
6292f8b7cd5SSamuel Thibault             case 0x2514:
6302f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LLCORNER;
6312f8b7cd5SSamuel Thibault                 break;
6322f8b7cd5SSamuel Thibault             case 0x2534:
6332f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BTEE;
6342f8b7cd5SSamuel Thibault                 break;
6352f8b7cd5SSamuel Thibault             case 0x252c:
6362f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_TTEE;
6372f8b7cd5SSamuel Thibault                 break;
6382f8b7cd5SSamuel Thibault             case 0x251c:
6392f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LTEE;
6402f8b7cd5SSamuel Thibault                 break;
6412f8b7cd5SSamuel Thibault             case 0x2500:
6422f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_HLINE;
6432f8b7cd5SSamuel Thibault                 break;
6442f8b7cd5SSamuel Thibault             case 0x253c:
6452f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLUS;
6462f8b7cd5SSamuel Thibault                 break;
6472f8b7cd5SSamuel Thibault             case 0x256c:
6482f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LANTERN;
6492f8b7cd5SSamuel Thibault                 break;
6502f8b7cd5SSamuel Thibault             case 0x256a:
6512f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_NEQUAL;
6522f8b7cd5SSamuel Thibault                 break;
6532f8b7cd5SSamuel Thibault             case 0x2518:
6542f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LRCORNER;
6552f8b7cd5SSamuel Thibault                 break;
6562f8b7cd5SSamuel Thibault             case 0x250c:
6572f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_ULCORNER;
6582f8b7cd5SSamuel Thibault                 break;
6592f8b7cd5SSamuel Thibault             case 0x2588:
6602f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BLOCK;
6612f8b7cd5SSamuel Thibault                 break;
6622f8b7cd5SSamuel Thibault             case 0x03c0:
6632f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PI;
6642f8b7cd5SSamuel Thibault                 break;
6652f8b7cd5SSamuel Thibault             case 0x00b1:
6662f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_PLMINUS;
6672f8b7cd5SSamuel Thibault                 break;
6682f8b7cd5SSamuel Thibault             case 0x2265:
6692f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_GEQUAL;
6702f8b7cd5SSamuel Thibault                 break;
6712f8b7cd5SSamuel Thibault             case 0x2264:
6722f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LEQUAL;
6732f8b7cd5SSamuel Thibault                 break;
6742f8b7cd5SSamuel Thibault             case 0x00b0:
6752f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DEGREE;
6762f8b7cd5SSamuel Thibault                 break;
6772f8b7cd5SSamuel Thibault             case 0x25a0:
6782f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_BULLET;
6792f8b7cd5SSamuel Thibault                 break;
6802f8b7cd5SSamuel Thibault             case 0x2666:
6812f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DIAMOND;
6822f8b7cd5SSamuel Thibault                 break;
6832f8b7cd5SSamuel Thibault             case 0x2192:
6842f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_RARROW;
6852f8b7cd5SSamuel Thibault                 break;
6862f8b7cd5SSamuel Thibault             case 0x2190:
6872f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_LARROW;
6882f8b7cd5SSamuel Thibault                 break;
6892f8b7cd5SSamuel Thibault             case 0x2191:
6902f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_UARROW;
6912f8b7cd5SSamuel Thibault                 break;
6922f8b7cd5SSamuel Thibault             case 0x2193:
6932f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_DARROW;
6942f8b7cd5SSamuel Thibault                 break;
6952f8b7cd5SSamuel Thibault             case 0x23ba:
6962f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S1;
6972f8b7cd5SSamuel Thibault                 break;
6982f8b7cd5SSamuel Thibault             case 0x23bb:
6992f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S3;
7002f8b7cd5SSamuel Thibault                 break;
7012f8b7cd5SSamuel Thibault             case 0x23bc:
7022f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S7;
7032f8b7cd5SSamuel Thibault                 break;
7042f8b7cd5SSamuel Thibault             case 0x23bd:
7052f8b7cd5SSamuel Thibault                 vga_to_curses[i] = *WACS_S9;
7062f8b7cd5SSamuel Thibault                 break;
7072f8b7cd5SSamuel Thibault             }
7082f8b7cd5SSamuel Thibault         }
7092f8b7cd5SSamuel Thibault     }
710b7b664a4SSamuel Thibault     iconv_close(ucs2_to_nativecharset);
711b7b664a4SSamuel Thibault     iconv_close(nativecharset_to_ucs2);
712a9fda247SSamuel Thibault     iconv_close(font_conv);
7132f8b7cd5SSamuel Thibault }
7142f8b7cd5SSamuel Thibault 
7153e230dd2SCorentin Chary static void curses_setup(void)
7163e230dd2SCorentin Chary {
7173e230dd2SCorentin Chary     int i, colour_default[8] = {
7184083733dSOGAWA Hirofumi         [QEMU_COLOR_BLACK]   = COLOR_BLACK,
7194083733dSOGAWA Hirofumi         [QEMU_COLOR_BLUE]    = COLOR_BLUE,
7204083733dSOGAWA Hirofumi         [QEMU_COLOR_GREEN]   = COLOR_GREEN,
7214083733dSOGAWA Hirofumi         [QEMU_COLOR_CYAN]    = COLOR_CYAN,
7224083733dSOGAWA Hirofumi         [QEMU_COLOR_RED]     = COLOR_RED,
7234083733dSOGAWA Hirofumi         [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
7244083733dSOGAWA Hirofumi         [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
7254083733dSOGAWA Hirofumi         [QEMU_COLOR_WHITE]   = COLOR_WHITE,
7263e230dd2SCorentin Chary     };
7273e230dd2SCorentin Chary 
7283e230dd2SCorentin Chary     /* input as raw as possible, let everything be interpreted
7293e230dd2SCorentin Chary      * by the guest system */
7303e230dd2SCorentin Chary     initscr(); noecho(); intrflush(stdscr, FALSE);
7313e230dd2SCorentin Chary     nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
7323e230dd2SCorentin Chary     start_color(); raw(); scrollok(stdscr, FALSE);
733633786feSSamuel Thibault     set_escdelay(25);
7343e230dd2SCorentin Chary 
7354083733dSOGAWA Hirofumi     /* Make color pair to match color format (3bits bg:3bits fg) */
736615220ddSOGAWA Hirofumi     for (i = 0; i < 64; i++) {
7373e230dd2SCorentin Chary         init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
7383e230dd2SCorentin Chary     }
7394083733dSOGAWA Hirofumi     /* Set default color for more than 64 for safety. */
740615220ddSOGAWA Hirofumi     for (i = 64; i < COLOR_PAIRS; i++) {
741615220ddSOGAWA Hirofumi         init_pair(i, COLOR_WHITE, COLOR_BLACK);
742615220ddSOGAWA Hirofumi     }
743e2368dc9SOGAWA Hirofumi 
7442f8b7cd5SSamuel Thibault     font_setup();
745615220ddSOGAWA Hirofumi }
7463e230dd2SCorentin Chary 
7473e230dd2SCorentin Chary static void curses_keyboard_setup(void)
7483e230dd2SCorentin Chary {
7493e230dd2SCorentin Chary #if defined(__APPLE__)
7503e230dd2SCorentin Chary     /* always use generic keymaps */
7513e230dd2SCorentin Chary     if (!keyboard_layout)
7523e230dd2SCorentin Chary         keyboard_layout = "en-us";
7533e230dd2SCorentin Chary #endif
7543e230dd2SCorentin Chary     if(keyboard_layout) {
755ab4f931eSFei Li         kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
756ab4f931eSFei Li                                           &error_fatal);
7573e230dd2SCorentin Chary     }
7583e230dd2SCorentin Chary }
7593e230dd2SCorentin Chary 
7607c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
7617c20b4a3SGerd Hoffmann     .dpy_name        = "curses",
7627c20b4a3SGerd Hoffmann     .dpy_text_update = curses_update,
7637c20b4a3SGerd Hoffmann     .dpy_text_resize = curses_resize,
7647c20b4a3SGerd Hoffmann     .dpy_refresh     = curses_refresh,
7657c20b4a3SGerd Hoffmann     .dpy_text_cursor = curses_cursor_position,
7667c20b4a3SGerd Hoffmann };
7677c20b4a3SGerd Hoffmann 
768b0766612SGerd Hoffmann static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
7693e230dd2SCorentin Chary {
7703e230dd2SCorentin Chary #ifndef _WIN32
7713e230dd2SCorentin Chary     if (!isatty(1)) {
7723e230dd2SCorentin Chary         fprintf(stderr, "We need a terminal output\n");
7733e230dd2SCorentin Chary         exit(1);
7743e230dd2SCorentin Chary     }
7753e230dd2SCorentin Chary #endif
7763e230dd2SCorentin Chary 
7772f8b7cd5SSamuel Thibault     setlocale(LC_CTYPE, "");
7782f8b7cd5SSamuel Thibault     if (opts->u.curses.charset) {
7792f8b7cd5SSamuel Thibault         font_charset = opts->u.curses.charset;
7802f8b7cd5SSamuel Thibault     }
7813e230dd2SCorentin Chary     curses_setup();
7823e230dd2SCorentin Chary     curses_keyboard_setup();
7833e230dd2SCorentin Chary     atexit(curses_atexit);
7843e230dd2SCorentin Chary 
785032ac6f8SGerd Hoffmann     curses_winch_init();
7863e230dd2SCorentin Chary 
787fedf0d35SMarkus Armbruster     dcl = g_new0(DisplayChangeListener, 1);
7887c20b4a3SGerd Hoffmann     dcl->ops = &dcl_ops;
7895209089fSGerd Hoffmann     register_displaychangelistener(dcl);
7903e230dd2SCorentin Chary 
7913e230dd2SCorentin Chary     invalidate = 1;
7923e230dd2SCorentin Chary }
793b0766612SGerd Hoffmann 
794b0766612SGerd Hoffmann static QemuDisplay qemu_display_curses = {
795b0766612SGerd Hoffmann     .type       = DISPLAY_TYPE_CURSES,
796b0766612SGerd Hoffmann     .init       = curses_display_init,
797b0766612SGerd Hoffmann };
798b0766612SGerd Hoffmann 
799b0766612SGerd Hoffmann static void register_curses(void)
800b0766612SGerd Hoffmann {
801b0766612SGerd Hoffmann     qemu_display_register(&qemu_display_curses);
802b0766612SGerd Hoffmann }
803b0766612SGerd Hoffmann 
804b0766612SGerd Hoffmann type_init(register_curses);
805