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 30*2f8b7cd5SSamuel Thibault #include <locale.h> 31*2f8b7cd5SSamuel Thibault #include <wchar.h> 32*2f8b7cd5SSamuel Thibault #include <langinfo.h> 33*2f8b7cd5SSamuel 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 61*2f8b7cd5SSamuel Thibault static const char *font_charset = "CP437"; 62*2f8b7cd5SSamuel 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; 68*2f8b7cd5SSamuel Thibault cchar_t curses_line[width]; 693e230dd2SCorentin Chary 70e2f82e92SGerd Hoffmann line = screen + y * width; 71e2f82e92SGerd Hoffmann for (h += y; y < h; y ++, line += width) { 72e2f82e92SGerd Hoffmann for (x = 0; x < width; x++) { 73e2f82e92SGerd Hoffmann chtype ch = line[x] & 0xff; 74e2f82e92SGerd Hoffmann chtype at = line[x] & ~0xff; 75*2f8b7cd5SSamuel Thibault if (vga_to_curses[ch].chars[0]) { 76*2f8b7cd5SSamuel Thibault curses_line[x] = vga_to_curses[ch]; 77*2f8b7cd5SSamuel Thibault } else { 78*2f8b7cd5SSamuel Thibault curses_line[x].chars[0] = ch; 79*2f8b7cd5SSamuel Thibault curses_line[x].chars[1] = 0; 80*2f8b7cd5SSamuel Thibault curses_line[x].attr = 0; 81e2f82e92SGerd Hoffmann } 82*2f8b7cd5SSamuel Thibault curses_line[x].attr |= at; 83e2f82e92SGerd Hoffmann } 84*2f8b7cd5SSamuel Thibault mvwadd_wchnstr(screenpad, y, 0, curses_line, width); 85e2f82e92SGerd Hoffmann } 863e230dd2SCorentin Chary 873e230dd2SCorentin Chary pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1); 883e230dd2SCorentin Chary refresh(); 893e230dd2SCorentin Chary } 903e230dd2SCorentin Chary 913e230dd2SCorentin Chary static void curses_calc_pad(void) 923e230dd2SCorentin Chary { 9381c0d5a6SGerd Hoffmann if (qemu_console_is_fixedsize(NULL)) { 943e230dd2SCorentin Chary width = gwidth; 953e230dd2SCorentin Chary height = gheight; 963e230dd2SCorentin Chary } else { 973e230dd2SCorentin Chary width = COLS; 983e230dd2SCorentin Chary height = LINES; 993e230dd2SCorentin Chary } 1003e230dd2SCorentin Chary 1013e230dd2SCorentin Chary if (screenpad) 1023e230dd2SCorentin Chary delwin(screenpad); 1033e230dd2SCorentin Chary 1043e230dd2SCorentin Chary clear(); 1053e230dd2SCorentin Chary refresh(); 1063e230dd2SCorentin Chary 1073e230dd2SCorentin Chary screenpad = newpad(height, width); 1083e230dd2SCorentin Chary 1093e230dd2SCorentin Chary if (width > COLS) { 1103e230dd2SCorentin Chary px = (width - COLS) / 2; 1113e230dd2SCorentin Chary sminx = 0; 1123e230dd2SCorentin Chary smaxx = COLS; 1133e230dd2SCorentin Chary } else { 1143e230dd2SCorentin Chary px = 0; 1153e230dd2SCorentin Chary sminx = (COLS - width) / 2; 1163e230dd2SCorentin Chary smaxx = sminx + width; 1173e230dd2SCorentin Chary } 1183e230dd2SCorentin Chary 1193e230dd2SCorentin Chary if (height > LINES) { 1203e230dd2SCorentin Chary py = (height - LINES) / 2; 1213e230dd2SCorentin Chary sminy = 0; 1223e230dd2SCorentin Chary smaxy = LINES; 1233e230dd2SCorentin Chary } else { 1243e230dd2SCorentin Chary py = 0; 1253e230dd2SCorentin Chary sminy = (LINES - height) / 2; 1263e230dd2SCorentin Chary smaxy = sminy + height; 1273e230dd2SCorentin Chary } 1283e230dd2SCorentin Chary } 1293e230dd2SCorentin Chary 1307c20b4a3SGerd Hoffmann static void curses_resize(DisplayChangeListener *dcl, 1317c20b4a3SGerd Hoffmann int width, int height) 1323e230dd2SCorentin Chary { 133a93a4a22SGerd Hoffmann if (width == gwidth && height == gheight) { 1343e230dd2SCorentin Chary return; 135a93a4a22SGerd Hoffmann } 1363e230dd2SCorentin Chary 137a93a4a22SGerd Hoffmann gwidth = width; 138a93a4a22SGerd Hoffmann gheight = height; 1393e230dd2SCorentin Chary 1403e230dd2SCorentin Chary curses_calc_pad(); 1413e230dd2SCorentin Chary } 1423e230dd2SCorentin Chary 143032ac6f8SGerd Hoffmann #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE) 144032ac6f8SGerd Hoffmann static volatile sig_atomic_t got_sigwinch; 145032ac6f8SGerd Hoffmann static void curses_winch_check(void) 1463e230dd2SCorentin Chary { 1473e230dd2SCorentin Chary struct winsize { 1483e230dd2SCorentin Chary unsigned short ws_row; 1493e230dd2SCorentin Chary unsigned short ws_col; 1503e230dd2SCorentin Chary unsigned short ws_xpixel; /* unused */ 1513e230dd2SCorentin Chary unsigned short ws_ypixel; /* unused */ 1523e230dd2SCorentin Chary } ws; 1533e230dd2SCorentin Chary 154032ac6f8SGerd Hoffmann if (!got_sigwinch) { 1553e230dd2SCorentin Chary return; 156032ac6f8SGerd Hoffmann } 157032ac6f8SGerd Hoffmann got_sigwinch = false; 158032ac6f8SGerd Hoffmann 159032ac6f8SGerd Hoffmann if (ioctl(1, TIOCGWINSZ, &ws) == -1) { 160032ac6f8SGerd Hoffmann return; 161032ac6f8SGerd Hoffmann } 1623e230dd2SCorentin Chary 1633e230dd2SCorentin Chary resize_term(ws.ws_row, ws.ws_col); 1643e230dd2SCorentin Chary invalidate = 1; 1653e230dd2SCorentin Chary } 166032ac6f8SGerd Hoffmann 167032ac6f8SGerd Hoffmann static void curses_winch_handler(int signum) 168032ac6f8SGerd Hoffmann { 169032ac6f8SGerd Hoffmann got_sigwinch = true; 170032ac6f8SGerd Hoffmann } 171032ac6f8SGerd Hoffmann 172032ac6f8SGerd Hoffmann static void curses_winch_init(void) 173032ac6f8SGerd Hoffmann { 174032ac6f8SGerd Hoffmann struct sigaction old, winch = { 175032ac6f8SGerd Hoffmann .sa_handler = curses_winch_handler, 176032ac6f8SGerd Hoffmann }; 177032ac6f8SGerd Hoffmann sigaction(SIGWINCH, &winch, &old); 178032ac6f8SGerd Hoffmann } 179032ac6f8SGerd Hoffmann #else 180032ac6f8SGerd Hoffmann static void curses_winch_check(void) {} 181032ac6f8SGerd Hoffmann static void curses_winch_init(void) {} 1823e230dd2SCorentin Chary #endif 1833e230dd2SCorentin Chary 1847c20b4a3SGerd Hoffmann static void curses_cursor_position(DisplayChangeListener *dcl, 1857c20b4a3SGerd Hoffmann int x, int y) 1863e230dd2SCorentin Chary { 1873e230dd2SCorentin Chary if (x >= 0) { 1883e230dd2SCorentin Chary x = sminx + x - px; 1893e230dd2SCorentin Chary y = sminy + y - py; 1903e230dd2SCorentin Chary 1913e230dd2SCorentin Chary if (x >= 0 && y >= 0 && x < COLS && y < LINES) { 1923e230dd2SCorentin Chary move(y, x); 1933e230dd2SCorentin Chary curs_set(1); 1943e230dd2SCorentin Chary /* it seems that curs_set(1) must always be called before 1953e230dd2SCorentin Chary * curs_set(2) for the latter to have effect */ 19681c0d5a6SGerd Hoffmann if (!qemu_console_is_graphic(NULL)) { 1973e230dd2SCorentin Chary curs_set(2); 19881c0d5a6SGerd Hoffmann } 1993e230dd2SCorentin Chary return; 2003e230dd2SCorentin Chary } 2013e230dd2SCorentin Chary } 2023e230dd2SCorentin Chary 2033e230dd2SCorentin Chary curs_set(0); 2043e230dd2SCorentin Chary } 2053e230dd2SCorentin Chary 2063e230dd2SCorentin Chary /* generic keyboard conversion */ 2073e230dd2SCorentin Chary 2083e230dd2SCorentin Chary #include "curses_keys.h" 2093e230dd2SCorentin Chary 2103e230dd2SCorentin Chary static kbd_layout_t *kbd_layout = NULL; 2113e230dd2SCorentin Chary 212459a707eSSamuel Thibault static wint_t console_getch(enum maybe_keycode *maybe_keycode) 213459a707eSSamuel Thibault { 214459a707eSSamuel Thibault wint_t ret; 215459a707eSSamuel Thibault switch (get_wch(&ret)) { 216459a707eSSamuel Thibault case KEY_CODE_YES: 217459a707eSSamuel Thibault *maybe_keycode = CURSES_KEYCODE; 218459a707eSSamuel Thibault break; 219459a707eSSamuel Thibault case OK: 220459a707eSSamuel Thibault *maybe_keycode = CURSES_CHAR; 221459a707eSSamuel Thibault break; 222459a707eSSamuel Thibault case ERR: 223459a707eSSamuel Thibault ret = -1; 224459a707eSSamuel Thibault break; 225459a707eSSamuel Thibault } 226459a707eSSamuel Thibault return ret; 227459a707eSSamuel Thibault } 228459a707eSSamuel Thibault 229459a707eSSamuel Thibault static int curses2foo(const int _curses2foo[], const int _curseskey2foo[], 230459a707eSSamuel Thibault int chr, enum maybe_keycode maybe_keycode) 231459a707eSSamuel Thibault { 232459a707eSSamuel Thibault int ret = -1; 233459a707eSSamuel Thibault if (maybe_keycode == CURSES_CHAR) { 234459a707eSSamuel Thibault if (chr < CURSES_CHARS) { 235459a707eSSamuel Thibault ret = _curses2foo[chr]; 236459a707eSSamuel Thibault } 237459a707eSSamuel Thibault } else { 238459a707eSSamuel Thibault if (chr < CURSES_KEYS) { 239459a707eSSamuel Thibault ret = _curseskey2foo[chr]; 240459a707eSSamuel Thibault } 241459a707eSSamuel Thibault if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE && 242459a707eSSamuel Thibault chr < CURSES_CHARS) { 243459a707eSSamuel Thibault ret = _curses2foo[chr]; 244459a707eSSamuel Thibault } 245459a707eSSamuel Thibault } 246459a707eSSamuel Thibault return ret; 247459a707eSSamuel Thibault } 248459a707eSSamuel Thibault 249459a707eSSamuel Thibault #define curses2keycode(chr, maybe_keycode) \ 250459a707eSSamuel Thibault curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode) 251459a707eSSamuel Thibault #define curses2keysym(chr, maybe_keycode) \ 252459a707eSSamuel Thibault curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode) 253459a707eSSamuel Thibault #define curses2qemu(chr, maybe_keycode) \ 254459a707eSSamuel Thibault curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode) 255459a707eSSamuel Thibault 256bc2ed970SGerd Hoffmann static void curses_refresh(DisplayChangeListener *dcl) 2573e230dd2SCorentin Chary { 25899a9ef44SPeter Maydell int chr, keysym, keycode, keycode_alt; 259459a707eSSamuel Thibault enum maybe_keycode maybe_keycode; 2603e230dd2SCorentin Chary 261032ac6f8SGerd Hoffmann curses_winch_check(); 262032ac6f8SGerd Hoffmann 2633e230dd2SCorentin Chary if (invalidate) { 2643e230dd2SCorentin Chary clear(); 2653e230dd2SCorentin Chary refresh(); 2663e230dd2SCorentin Chary curses_calc_pad(); 2671dbfa005SGerd Hoffmann graphic_hw_invalidate(NULL); 2683e230dd2SCorentin Chary invalidate = 0; 2693e230dd2SCorentin Chary } 2703e230dd2SCorentin Chary 2711dbfa005SGerd Hoffmann graphic_hw_text_update(NULL, screen); 2723e230dd2SCorentin Chary 2733e230dd2SCorentin Chary while (1) { 2743e230dd2SCorentin Chary /* while there are any pending key strokes to process */ 275459a707eSSamuel Thibault chr = console_getch(&maybe_keycode); 2763e230dd2SCorentin Chary 277459a707eSSamuel Thibault if (chr == -1) 2783e230dd2SCorentin Chary break; 2793e230dd2SCorentin Chary 2803e230dd2SCorentin Chary #ifdef KEY_RESIZE 2813e230dd2SCorentin Chary /* this shouldn't occur when we use a custom SIGWINCH handler */ 282459a707eSSamuel Thibault if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) { 2833e230dd2SCorentin Chary clear(); 2843e230dd2SCorentin Chary refresh(); 2853e230dd2SCorentin Chary curses_calc_pad(); 286bc2ed970SGerd Hoffmann curses_update(dcl, 0, 0, width, height); 2873e230dd2SCorentin Chary continue; 2883e230dd2SCorentin Chary } 2893e230dd2SCorentin Chary #endif 2903e230dd2SCorentin Chary 291459a707eSSamuel Thibault keycode = curses2keycode(chr, maybe_keycode); 2923e230dd2SCorentin Chary keycode_alt = 0; 2933e230dd2SCorentin Chary 294633786feSSamuel Thibault /* alt or esc key */ 2953e230dd2SCorentin Chary if (keycode == 1) { 296459a707eSSamuel Thibault enum maybe_keycode next_maybe_keycode; 297459a707eSSamuel Thibault int nextchr = console_getch(&next_maybe_keycode); 2983e230dd2SCorentin Chary 299459a707eSSamuel Thibault if (nextchr != -1) { 3003e230dd2SCorentin Chary chr = nextchr; 301459a707eSSamuel Thibault maybe_keycode = next_maybe_keycode; 3023e230dd2SCorentin Chary keycode_alt = ALT; 303459a707eSSamuel Thibault keycode = curses2keycode(chr, maybe_keycode); 3043e230dd2SCorentin Chary 3053e230dd2SCorentin Chary if (keycode != -1) { 3063e230dd2SCorentin Chary keycode |= ALT; 3073e230dd2SCorentin Chary 3083e230dd2SCorentin Chary /* process keys reserved for qemu */ 3093e230dd2SCorentin Chary if (keycode >= QEMU_KEY_CONSOLE0 && 3103e230dd2SCorentin Chary keycode < QEMU_KEY_CONSOLE0 + 9) { 3113e230dd2SCorentin Chary erase(); 3123e230dd2SCorentin Chary wnoutrefresh(stdscr); 3133e230dd2SCorentin Chary console_select(keycode - QEMU_KEY_CONSOLE0); 3143e230dd2SCorentin Chary 3153e230dd2SCorentin Chary invalidate = 1; 3163e230dd2SCorentin Chary continue; 3173e230dd2SCorentin Chary } 3183e230dd2SCorentin Chary } 3193e230dd2SCorentin Chary } 3203e230dd2SCorentin Chary } 3213e230dd2SCorentin Chary 3223e230dd2SCorentin Chary if (kbd_layout) { 323459a707eSSamuel Thibault keysym = curses2keysym(chr, maybe_keycode); 3243e230dd2SCorentin Chary 3253e230dd2SCorentin Chary if (keysym == -1) { 326d03703c8SSamuel Thibault if (chr < ' ') { 327d03703c8SSamuel Thibault keysym = chr + '@'; 328d03703c8SSamuel Thibault if (keysym >= 'A' && keysym <= 'Z') 329d03703c8SSamuel Thibault keysym += 'a' - 'A'; 330d03703c8SSamuel Thibault keysym |= KEYSYM_CNTRL; 331d03703c8SSamuel Thibault } else 3323e230dd2SCorentin Chary keysym = chr; 3333e230dd2SCorentin Chary } 3343e230dd2SCorentin Chary 335abb4f2c9SGerd Hoffmann keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK, 33619c1b9fdSGerd Hoffmann NULL, false); 3373e230dd2SCorentin Chary if (keycode == 0) 3383e230dd2SCorentin Chary continue; 3393e230dd2SCorentin Chary 3403e230dd2SCorentin Chary keycode |= (keysym & ~KEYSYM_MASK) >> 16; 3413e230dd2SCorentin Chary keycode |= keycode_alt; 3423e230dd2SCorentin Chary } 3433e230dd2SCorentin Chary 3443e230dd2SCorentin Chary if (keycode == -1) 3453e230dd2SCorentin Chary continue; 3463e230dd2SCorentin Chary 34781c0d5a6SGerd Hoffmann if (qemu_console_is_graphic(NULL)) { 3483e230dd2SCorentin Chary /* since terminals don't know about key press and release 3493e230dd2SCorentin Chary * events, we need to emit both for each key received */ 350cd100328SGerd Hoffmann if (keycode & SHIFT) { 351cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, SHIFT_CODE, true); 3525a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 3533e230dd2SCorentin Chary } 354cd100328SGerd Hoffmann if (keycode & CNTRL) { 355cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, CNTRL_CODE, true); 3565a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 3573e230dd2SCorentin Chary } 358cd100328SGerd Hoffmann if (keycode & ALT) { 359cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, ALT_CODE, true); 3605a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 361cd100328SGerd Hoffmann } 362cd100328SGerd Hoffmann if (keycode & ALTGR) { 363cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); 3645a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 365cd100328SGerd Hoffmann } 366cd100328SGerd Hoffmann 367f5c0ab13SAndrew Oates qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true); 3685a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 369f5c0ab13SAndrew Oates qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false); 3705a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 371cd100328SGerd Hoffmann 372cd100328SGerd Hoffmann if (keycode & ALTGR) { 373cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); 3745a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 375cd100328SGerd Hoffmann } 376cd100328SGerd Hoffmann if (keycode & ALT) { 377cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, ALT_CODE, false); 3785a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 379cd100328SGerd Hoffmann } 380cd100328SGerd Hoffmann if (keycode & CNTRL) { 381cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, CNTRL_CODE, false); 3825a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 383cd100328SGerd Hoffmann } 384cd100328SGerd Hoffmann if (keycode & SHIFT) { 385cd100328SGerd Hoffmann qemu_input_event_send_key_number(NULL, SHIFT_CODE, false); 3865a165668SGerd Hoffmann qemu_input_event_send_key_delay(0); 387cd100328SGerd Hoffmann } 3883e230dd2SCorentin Chary } else { 389459a707eSSamuel Thibault keysym = curses2qemu(chr, maybe_keycode); 3903e230dd2SCorentin Chary if (keysym == -1) 3913e230dd2SCorentin Chary keysym = chr; 3923e230dd2SCorentin Chary 3933e230dd2SCorentin Chary kbd_put_keysym(keysym); 3943e230dd2SCorentin Chary } 3953e230dd2SCorentin Chary } 3963e230dd2SCorentin Chary } 3973e230dd2SCorentin Chary 3983e230dd2SCorentin Chary static void curses_atexit(void) 3993e230dd2SCorentin Chary { 4003e230dd2SCorentin Chary endwin(); 4013e230dd2SCorentin Chary } 4023e230dd2SCorentin Chary 403*2f8b7cd5SSamuel Thibault /* Setup wchar glyph for one UCS-2 char */ 404*2f8b7cd5SSamuel Thibault static void convert_ucs(int glyph, uint16_t ch, iconv_t conv) 405*2f8b7cd5SSamuel Thibault { 406*2f8b7cd5SSamuel Thibault wchar_t wch; 407*2f8b7cd5SSamuel Thibault char *pch, *pwch; 408*2f8b7cd5SSamuel Thibault size_t sch, swch; 409*2f8b7cd5SSamuel Thibault 410*2f8b7cd5SSamuel Thibault pch = (char *) &ch; 411*2f8b7cd5SSamuel Thibault pwch = (char *) &wch; 412*2f8b7cd5SSamuel Thibault sch = sizeof(ch); 413*2f8b7cd5SSamuel Thibault swch = sizeof(wch); 414*2f8b7cd5SSamuel Thibault 415*2f8b7cd5SSamuel Thibault if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) { 416*2f8b7cd5SSamuel Thibault fprintf(stderr, "Could not convert 0x%04x from UCS-2 to WCHAR_T: %s\n", 417*2f8b7cd5SSamuel Thibault ch, strerror(errno)); 418*2f8b7cd5SSamuel Thibault } else { 419*2f8b7cd5SSamuel Thibault vga_to_curses[glyph].chars[0] = wch; 420*2f8b7cd5SSamuel Thibault } 421*2f8b7cd5SSamuel Thibault } 422*2f8b7cd5SSamuel Thibault 423*2f8b7cd5SSamuel Thibault /* Setup wchar glyph for one font character */ 424*2f8b7cd5SSamuel Thibault static void convert_font(unsigned char ch, iconv_t conv) 425*2f8b7cd5SSamuel Thibault { 426*2f8b7cd5SSamuel Thibault wchar_t wch; 427*2f8b7cd5SSamuel Thibault char *pch, *pwch; 428*2f8b7cd5SSamuel Thibault size_t sch, swch; 429*2f8b7cd5SSamuel Thibault 430*2f8b7cd5SSamuel Thibault pch = (char *) &ch; 431*2f8b7cd5SSamuel Thibault pwch = (char *) &wch; 432*2f8b7cd5SSamuel Thibault sch = sizeof(ch); 433*2f8b7cd5SSamuel Thibault swch = sizeof(wch); 434*2f8b7cd5SSamuel Thibault 435*2f8b7cd5SSamuel Thibault if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) { 436*2f8b7cd5SSamuel Thibault fprintf(stderr, "Could not convert 0x%02x from %s to WCHAR_T: %s\n", 437*2f8b7cd5SSamuel Thibault ch, font_charset, strerror(errno)); 438*2f8b7cd5SSamuel Thibault } else { 439*2f8b7cd5SSamuel Thibault vga_to_curses[ch].chars[0] = wch; 440*2f8b7cd5SSamuel Thibault } 441*2f8b7cd5SSamuel Thibault } 442*2f8b7cd5SSamuel Thibault 443*2f8b7cd5SSamuel Thibault /* Convert one wchar to UCS-2 */ 444*2f8b7cd5SSamuel Thibault static uint16_t get_ucs(wchar_t wch, iconv_t conv) 445*2f8b7cd5SSamuel Thibault { 446*2f8b7cd5SSamuel Thibault uint16_t ch; 447*2f8b7cd5SSamuel Thibault char *pch, *pwch; 448*2f8b7cd5SSamuel Thibault size_t sch, swch; 449*2f8b7cd5SSamuel Thibault 450*2f8b7cd5SSamuel Thibault pch = (char *) &ch; 451*2f8b7cd5SSamuel Thibault pwch = (char *) &wch; 452*2f8b7cd5SSamuel Thibault sch = sizeof(ch); 453*2f8b7cd5SSamuel Thibault swch = sizeof(wch); 454*2f8b7cd5SSamuel Thibault 455*2f8b7cd5SSamuel Thibault if (iconv(conv, &pwch, &swch, &pch, &sch) == (size_t) -1) { 456*2f8b7cd5SSamuel Thibault fprintf(stderr, "Could not convert 0x%02x from WCHAR_T to UCS-2: %s\n", 457*2f8b7cd5SSamuel Thibault wch, strerror(errno)); 458*2f8b7cd5SSamuel Thibault return 0xFFFD; 459*2f8b7cd5SSamuel Thibault } 460*2f8b7cd5SSamuel Thibault 461*2f8b7cd5SSamuel Thibault return ch; 462*2f8b7cd5SSamuel Thibault } 463*2f8b7cd5SSamuel Thibault 464*2f8b7cd5SSamuel Thibault /* 465*2f8b7cd5SSamuel Thibault * Setup mapping for vga to curses line graphics. 466*2f8b7cd5SSamuel Thibault */ 467*2f8b7cd5SSamuel Thibault static void font_setup(void) 468*2f8b7cd5SSamuel Thibault { 469*2f8b7cd5SSamuel Thibault /* 470*2f8b7cd5SSamuel Thibault * Control characters are normally non-printable, but VGA does have 471*2f8b7cd5SSamuel Thibault * well-known glyphs for them. 472*2f8b7cd5SSamuel Thibault */ 473*2f8b7cd5SSamuel Thibault static uint16_t control_characters[0x20] = { 474*2f8b7cd5SSamuel Thibault 0x0020, 475*2f8b7cd5SSamuel Thibault 0x263a, 476*2f8b7cd5SSamuel Thibault 0x263b, 477*2f8b7cd5SSamuel Thibault 0x2665, 478*2f8b7cd5SSamuel Thibault 0x2666, 479*2f8b7cd5SSamuel Thibault 0x2663, 480*2f8b7cd5SSamuel Thibault 0x2660, 481*2f8b7cd5SSamuel Thibault 0x2022, 482*2f8b7cd5SSamuel Thibault 0x25d8, 483*2f8b7cd5SSamuel Thibault 0x25cb, 484*2f8b7cd5SSamuel Thibault 0x25d9, 485*2f8b7cd5SSamuel Thibault 0x2642, 486*2f8b7cd5SSamuel Thibault 0x2640, 487*2f8b7cd5SSamuel Thibault 0x266a, 488*2f8b7cd5SSamuel Thibault 0x266b, 489*2f8b7cd5SSamuel Thibault 0x263c, 490*2f8b7cd5SSamuel Thibault 0x25ba, 491*2f8b7cd5SSamuel Thibault 0x25c4, 492*2f8b7cd5SSamuel Thibault 0x2195, 493*2f8b7cd5SSamuel Thibault 0x203c, 494*2f8b7cd5SSamuel Thibault 0x00b6, 495*2f8b7cd5SSamuel Thibault 0x00a7, 496*2f8b7cd5SSamuel Thibault 0x25ac, 497*2f8b7cd5SSamuel Thibault 0x21a8, 498*2f8b7cd5SSamuel Thibault 0x2191, 499*2f8b7cd5SSamuel Thibault 0x2193, 500*2f8b7cd5SSamuel Thibault 0x2192, 501*2f8b7cd5SSamuel Thibault 0x2190, 502*2f8b7cd5SSamuel Thibault 0x221f, 503*2f8b7cd5SSamuel Thibault 0x2194, 504*2f8b7cd5SSamuel Thibault 0x25b2, 505*2f8b7cd5SSamuel Thibault 0x25bc 506*2f8b7cd5SSamuel Thibault }; 507*2f8b7cd5SSamuel Thibault 508*2f8b7cd5SSamuel Thibault iconv_t ucs_to_wchar_conv; 509*2f8b7cd5SSamuel Thibault iconv_t wchar_to_ucs_conv; 510*2f8b7cd5SSamuel Thibault iconv_t font_conv; 511*2f8b7cd5SSamuel Thibault int i; 512*2f8b7cd5SSamuel Thibault 513*2f8b7cd5SSamuel Thibault ucs_to_wchar_conv = iconv_open("WCHAR_T", "UCS-2"); 514*2f8b7cd5SSamuel Thibault if (ucs_to_wchar_conv == (iconv_t) -1) { 515*2f8b7cd5SSamuel Thibault fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n", 516*2f8b7cd5SSamuel Thibault strerror(errno)); 517*2f8b7cd5SSamuel Thibault exit(1); 518*2f8b7cd5SSamuel Thibault } 519*2f8b7cd5SSamuel Thibault 520*2f8b7cd5SSamuel Thibault wchar_to_ucs_conv = iconv_open("UCS-2", "WCHAR_T"); 521*2f8b7cd5SSamuel Thibault if (wchar_to_ucs_conv == (iconv_t) -1) { 522*2f8b7cd5SSamuel Thibault fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n", 523*2f8b7cd5SSamuel Thibault strerror(errno)); 524*2f8b7cd5SSamuel Thibault exit(1); 525*2f8b7cd5SSamuel Thibault } 526*2f8b7cd5SSamuel Thibault 527*2f8b7cd5SSamuel Thibault font_conv = iconv_open("WCHAR_T", font_charset); 528*2f8b7cd5SSamuel Thibault if (font_conv == (iconv_t) -1) { 529*2f8b7cd5SSamuel Thibault fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n", 530*2f8b7cd5SSamuel Thibault font_charset, strerror(errno)); 531*2f8b7cd5SSamuel Thibault exit(1); 532*2f8b7cd5SSamuel Thibault } 533*2f8b7cd5SSamuel Thibault 534*2f8b7cd5SSamuel Thibault /* Control characters */ 535*2f8b7cd5SSamuel Thibault for (i = 0; i <= 0x1F; i++) { 536*2f8b7cd5SSamuel Thibault convert_ucs(i, control_characters[i], ucs_to_wchar_conv); 537*2f8b7cd5SSamuel Thibault } 538*2f8b7cd5SSamuel Thibault 539*2f8b7cd5SSamuel Thibault for (i = 0x20; i <= 0xFF; i++) { 540*2f8b7cd5SSamuel Thibault convert_font(i, font_conv); 541*2f8b7cd5SSamuel Thibault } 542*2f8b7cd5SSamuel Thibault 543*2f8b7cd5SSamuel Thibault /* DEL */ 544*2f8b7cd5SSamuel Thibault convert_ucs(0x7F, 0x2302, ucs_to_wchar_conv); 545*2f8b7cd5SSamuel Thibault 546*2f8b7cd5SSamuel Thibault if (strcmp(nl_langinfo(CODESET), "UTF-8")) { 547*2f8b7cd5SSamuel Thibault /* Non-Unicode capable, use termcap equivalents for those available */ 548*2f8b7cd5SSamuel Thibault for (i = 0; i <= 0xFF; i++) { 549*2f8b7cd5SSamuel Thibault switch (get_ucs(vga_to_curses[i].chars[0], wchar_to_ucs_conv)) { 550*2f8b7cd5SSamuel Thibault case 0x00a3: 551*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_STERLING; 552*2f8b7cd5SSamuel Thibault break; 553*2f8b7cd5SSamuel Thibault case 0x2591: 554*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_BOARD; 555*2f8b7cd5SSamuel Thibault break; 556*2f8b7cd5SSamuel Thibault case 0x2592: 557*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_CKBOARD; 558*2f8b7cd5SSamuel Thibault break; 559*2f8b7cd5SSamuel Thibault case 0x2502: 560*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_VLINE; 561*2f8b7cd5SSamuel Thibault break; 562*2f8b7cd5SSamuel Thibault case 0x2524: 563*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_RTEE; 564*2f8b7cd5SSamuel Thibault break; 565*2f8b7cd5SSamuel Thibault case 0x2510: 566*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_URCORNER; 567*2f8b7cd5SSamuel Thibault break; 568*2f8b7cd5SSamuel Thibault case 0x2514: 569*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_LLCORNER; 570*2f8b7cd5SSamuel Thibault break; 571*2f8b7cd5SSamuel Thibault case 0x2534: 572*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_BTEE; 573*2f8b7cd5SSamuel Thibault break; 574*2f8b7cd5SSamuel Thibault case 0x252c: 575*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_TTEE; 576*2f8b7cd5SSamuel Thibault break; 577*2f8b7cd5SSamuel Thibault case 0x251c: 578*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_LTEE; 579*2f8b7cd5SSamuel Thibault break; 580*2f8b7cd5SSamuel Thibault case 0x2500: 581*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_HLINE; 582*2f8b7cd5SSamuel Thibault break; 583*2f8b7cd5SSamuel Thibault case 0x253c: 584*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_PLUS; 585*2f8b7cd5SSamuel Thibault break; 586*2f8b7cd5SSamuel Thibault case 0x256c: 587*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_LANTERN; 588*2f8b7cd5SSamuel Thibault break; 589*2f8b7cd5SSamuel Thibault case 0x256a: 590*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_NEQUAL; 591*2f8b7cd5SSamuel Thibault break; 592*2f8b7cd5SSamuel Thibault case 0x2518: 593*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_LRCORNER; 594*2f8b7cd5SSamuel Thibault break; 595*2f8b7cd5SSamuel Thibault case 0x250c: 596*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_ULCORNER; 597*2f8b7cd5SSamuel Thibault break; 598*2f8b7cd5SSamuel Thibault case 0x2588: 599*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_BLOCK; 600*2f8b7cd5SSamuel Thibault break; 601*2f8b7cd5SSamuel Thibault case 0x03c0: 602*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_PI; 603*2f8b7cd5SSamuel Thibault break; 604*2f8b7cd5SSamuel Thibault case 0x00b1: 605*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_PLMINUS; 606*2f8b7cd5SSamuel Thibault break; 607*2f8b7cd5SSamuel Thibault case 0x2265: 608*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_GEQUAL; 609*2f8b7cd5SSamuel Thibault break; 610*2f8b7cd5SSamuel Thibault case 0x2264: 611*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_LEQUAL; 612*2f8b7cd5SSamuel Thibault break; 613*2f8b7cd5SSamuel Thibault case 0x00b0: 614*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_DEGREE; 615*2f8b7cd5SSamuel Thibault break; 616*2f8b7cd5SSamuel Thibault case 0x25a0: 617*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_BULLET; 618*2f8b7cd5SSamuel Thibault break; 619*2f8b7cd5SSamuel Thibault case 0x2666: 620*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_DIAMOND; 621*2f8b7cd5SSamuel Thibault break; 622*2f8b7cd5SSamuel Thibault case 0x2192: 623*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_RARROW; 624*2f8b7cd5SSamuel Thibault break; 625*2f8b7cd5SSamuel Thibault case 0x2190: 626*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_LARROW; 627*2f8b7cd5SSamuel Thibault break; 628*2f8b7cd5SSamuel Thibault case 0x2191: 629*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_UARROW; 630*2f8b7cd5SSamuel Thibault break; 631*2f8b7cd5SSamuel Thibault case 0x2193: 632*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_DARROW; 633*2f8b7cd5SSamuel Thibault break; 634*2f8b7cd5SSamuel Thibault case 0x23ba: 635*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_S1; 636*2f8b7cd5SSamuel Thibault break; 637*2f8b7cd5SSamuel Thibault case 0x23bb: 638*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_S3; 639*2f8b7cd5SSamuel Thibault break; 640*2f8b7cd5SSamuel Thibault case 0x23bc: 641*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_S7; 642*2f8b7cd5SSamuel Thibault break; 643*2f8b7cd5SSamuel Thibault case 0x23bd: 644*2f8b7cd5SSamuel Thibault vga_to_curses[i] = *WACS_S9; 645*2f8b7cd5SSamuel Thibault break; 646*2f8b7cd5SSamuel Thibault } 647*2f8b7cd5SSamuel Thibault } 648*2f8b7cd5SSamuel Thibault } 649*2f8b7cd5SSamuel Thibault } 650*2f8b7cd5SSamuel Thibault 6513e230dd2SCorentin Chary static void curses_setup(void) 6523e230dd2SCorentin Chary { 6533e230dd2SCorentin Chary int i, colour_default[8] = { 6544083733dSOGAWA Hirofumi [QEMU_COLOR_BLACK] = COLOR_BLACK, 6554083733dSOGAWA Hirofumi [QEMU_COLOR_BLUE] = COLOR_BLUE, 6564083733dSOGAWA Hirofumi [QEMU_COLOR_GREEN] = COLOR_GREEN, 6574083733dSOGAWA Hirofumi [QEMU_COLOR_CYAN] = COLOR_CYAN, 6584083733dSOGAWA Hirofumi [QEMU_COLOR_RED] = COLOR_RED, 6594083733dSOGAWA Hirofumi [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA, 6604083733dSOGAWA Hirofumi [QEMU_COLOR_YELLOW] = COLOR_YELLOW, 6614083733dSOGAWA Hirofumi [QEMU_COLOR_WHITE] = COLOR_WHITE, 6623e230dd2SCorentin Chary }; 6633e230dd2SCorentin Chary 6643e230dd2SCorentin Chary /* input as raw as possible, let everything be interpreted 6653e230dd2SCorentin Chary * by the guest system */ 6663e230dd2SCorentin Chary initscr(); noecho(); intrflush(stdscr, FALSE); 6673e230dd2SCorentin Chary nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); 6683e230dd2SCorentin Chary start_color(); raw(); scrollok(stdscr, FALSE); 669633786feSSamuel Thibault set_escdelay(25); 6703e230dd2SCorentin Chary 6714083733dSOGAWA Hirofumi /* Make color pair to match color format (3bits bg:3bits fg) */ 672615220ddSOGAWA Hirofumi for (i = 0; i < 64; i++) { 6733e230dd2SCorentin Chary init_pair(i, colour_default[i & 7], colour_default[i >> 3]); 6743e230dd2SCorentin Chary } 6754083733dSOGAWA Hirofumi /* Set default color for more than 64 for safety. */ 676615220ddSOGAWA Hirofumi for (i = 64; i < COLOR_PAIRS; i++) { 677615220ddSOGAWA Hirofumi init_pair(i, COLOR_WHITE, COLOR_BLACK); 678615220ddSOGAWA Hirofumi } 679e2368dc9SOGAWA Hirofumi 680*2f8b7cd5SSamuel Thibault font_setup(); 681615220ddSOGAWA Hirofumi } 6823e230dd2SCorentin Chary 6833e230dd2SCorentin Chary static void curses_keyboard_setup(void) 6843e230dd2SCorentin Chary { 6853e230dd2SCorentin Chary #if defined(__APPLE__) 6863e230dd2SCorentin Chary /* always use generic keymaps */ 6873e230dd2SCorentin Chary if (!keyboard_layout) 6883e230dd2SCorentin Chary keyboard_layout = "en-us"; 6893e230dd2SCorentin Chary #endif 6903e230dd2SCorentin Chary if(keyboard_layout) { 691ab4f931eSFei Li kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout, 692ab4f931eSFei Li &error_fatal); 6933e230dd2SCorentin Chary } 6943e230dd2SCorentin Chary } 6953e230dd2SCorentin Chary 6967c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = { 6977c20b4a3SGerd Hoffmann .dpy_name = "curses", 6987c20b4a3SGerd Hoffmann .dpy_text_update = curses_update, 6997c20b4a3SGerd Hoffmann .dpy_text_resize = curses_resize, 7007c20b4a3SGerd Hoffmann .dpy_refresh = curses_refresh, 7017c20b4a3SGerd Hoffmann .dpy_text_cursor = curses_cursor_position, 7027c20b4a3SGerd Hoffmann }; 7037c20b4a3SGerd Hoffmann 704b0766612SGerd Hoffmann static void curses_display_init(DisplayState *ds, DisplayOptions *opts) 7053e230dd2SCorentin Chary { 7063e230dd2SCorentin Chary #ifndef _WIN32 7073e230dd2SCorentin Chary if (!isatty(1)) { 7083e230dd2SCorentin Chary fprintf(stderr, "We need a terminal output\n"); 7093e230dd2SCorentin Chary exit(1); 7103e230dd2SCorentin Chary } 7113e230dd2SCorentin Chary #endif 7123e230dd2SCorentin Chary 713*2f8b7cd5SSamuel Thibault setlocale(LC_CTYPE, ""); 714*2f8b7cd5SSamuel Thibault if (opts->u.curses.charset) { 715*2f8b7cd5SSamuel Thibault font_charset = opts->u.curses.charset; 716*2f8b7cd5SSamuel Thibault } 7173e230dd2SCorentin Chary curses_setup(); 7183e230dd2SCorentin Chary curses_keyboard_setup(); 7193e230dd2SCorentin Chary atexit(curses_atexit); 7203e230dd2SCorentin Chary 721032ac6f8SGerd Hoffmann curses_winch_init(); 7223e230dd2SCorentin Chary 723fedf0d35SMarkus Armbruster dcl = g_new0(DisplayChangeListener, 1); 7247c20b4a3SGerd Hoffmann dcl->ops = &dcl_ops; 7255209089fSGerd Hoffmann register_displaychangelistener(dcl); 7263e230dd2SCorentin Chary 7273e230dd2SCorentin Chary invalidate = 1; 7283e230dd2SCorentin Chary } 729b0766612SGerd Hoffmann 730b0766612SGerd Hoffmann static QemuDisplay qemu_display_curses = { 731b0766612SGerd Hoffmann .type = DISPLAY_TYPE_CURSES, 732b0766612SGerd Hoffmann .init = curses_display_init, 733b0766612SGerd Hoffmann }; 734b0766612SGerd Hoffmann 735b0766612SGerd Hoffmann static void register_curses(void) 736b0766612SGerd Hoffmann { 737b0766612SGerd Hoffmann qemu_display_register(&qemu_display_curses); 738b0766612SGerd Hoffmann } 739b0766612SGerd Hoffmann 740b0766612SGerd Hoffmann type_init(register_curses); 741