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