xref: /openbmc/qemu/ui/curses.c (revision dad1c8ecc707a0f3fe6963b364477ce0a92670fa)
1  /*
2   * QEMU curses/ncurses display driver
3   *
4   * Copyright (c) 2005 Andrzej Zaborowski  <balrog@zabor.org>
5   *
6   * Permission is hereby granted, free of charge, to any person obtaining a copy
7   * of this software and associated documentation files (the "Software"), to deal
8   * in the Software without restriction, including without limitation the rights
9   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10   * copies of the Software, and to permit persons to whom the Software is
11   * furnished to do so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in
14   * all copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22   * THE SOFTWARE.
23   */
24  #include "qemu/osdep.h"
25  
26  #ifndef _WIN32
27  #include <sys/ioctl.h>
28  #include <termios.h>
29  #endif
30  #include <locale.h>
31  #include <wchar.h>
32  #include <langinfo.h>
33  #include <iconv.h>
34  
35  #include "qapi/error.h"
36  #include "qemu-common.h"
37  #include "ui/console.h"
38  #include "ui/input.h"
39  #include "sysemu/sysemu.h"
40  
41  /* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
42  #undef KEY_EVENT
43  #include <curses.h>
44  #undef KEY_EVENT
45  
46  #define FONT_HEIGHT 16
47  #define FONT_WIDTH 8
48  
49  enum maybe_keycode {
50      CURSES_KEYCODE,
51      CURSES_CHAR,
52      CURSES_CHAR_OR_KEYCODE,
53  };
54  
55  static DisplayChangeListener *dcl;
56  static console_ch_t screen[160 * 100];
57  static WINDOW *screenpad = NULL;
58  static int width, height, gwidth, gheight, invalidate;
59  static int px, py, sminx, sminy, smaxx, smaxy;
60  
61  static const char *font_charset = "CP437";
62  static cchar_t vga_to_curses[256];
63  
64  static void curses_update(DisplayChangeListener *dcl,
65                            int x, int y, int w, int h)
66  {
67      console_ch_t *line;
68      cchar_t curses_line[width];
69      wchar_t wch[CCHARW_MAX];
70      attr_t attrs;
71      short colors;
72      int ret;
73  
74      line = screen + y * width;
75      for (h += y; y < h; y ++, line += width) {
76          for (x = 0; x < width; x++) {
77              chtype ch = line[x] & 0xff;
78              chtype at = line[x] & ~0xff;
79              ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
80              if (ret == ERR || wch[0] == 0) {
81                  wch[0] = ch;
82                  wch[1] = 0;
83              }
84              setcchar(&curses_line[x], wch, at, 0, NULL);
85          }
86          mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
87      }
88  
89      pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
90      refresh();
91  }
92  
93  static void curses_calc_pad(void)
94  {
95      if (qemu_console_is_fixedsize(NULL)) {
96          width = gwidth;
97          height = gheight;
98      } else {
99          width = COLS;
100          height = LINES;
101      }
102  
103      if (screenpad)
104          delwin(screenpad);
105  
106      clear();
107      refresh();
108  
109      screenpad = newpad(height, width);
110  
111      if (width > COLS) {
112          px = (width - COLS) / 2;
113          sminx = 0;
114          smaxx = COLS;
115      } else {
116          px = 0;
117          sminx = (COLS - width) / 2;
118          smaxx = sminx + width;
119      }
120  
121      if (height > LINES) {
122          py = (height - LINES) / 2;
123          sminy = 0;
124          smaxy = LINES;
125      } else {
126          py = 0;
127          sminy = (LINES - height) / 2;
128          smaxy = sminy + height;
129      }
130  }
131  
132  static void curses_resize(DisplayChangeListener *dcl,
133                            int width, int height)
134  {
135      if (width == gwidth && height == gheight) {
136          return;
137      }
138  
139      gwidth = width;
140      gheight = height;
141  
142      curses_calc_pad();
143  }
144  
145  #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
146  static volatile sig_atomic_t got_sigwinch;
147  static void curses_winch_check(void)
148  {
149      struct winsize {
150          unsigned short ws_row;
151          unsigned short ws_col;
152          unsigned short ws_xpixel;   /* unused */
153          unsigned short ws_ypixel;   /* unused */
154      } ws;
155  
156      if (!got_sigwinch) {
157          return;
158      }
159      got_sigwinch = false;
160  
161      if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
162          return;
163      }
164  
165      resize_term(ws.ws_row, ws.ws_col);
166      invalidate = 1;
167  }
168  
169  static void curses_winch_handler(int signum)
170  {
171      got_sigwinch = true;
172  }
173  
174  static void curses_winch_init(void)
175  {
176      struct sigaction old, winch = {
177          .sa_handler  = curses_winch_handler,
178      };
179      sigaction(SIGWINCH, &winch, &old);
180  }
181  #else
182  static void curses_winch_check(void) {}
183  static void curses_winch_init(void) {}
184  #endif
185  
186  static void curses_cursor_position(DisplayChangeListener *dcl,
187                                     int x, int y)
188  {
189      if (x >= 0) {
190          x = sminx + x - px;
191          y = sminy + y - py;
192  
193          if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
194              move(y, x);
195              curs_set(1);
196              /* it seems that curs_set(1) must always be called before
197               * curs_set(2) for the latter to have effect */
198              if (!qemu_console_is_graphic(NULL)) {
199                  curs_set(2);
200              }
201              return;
202          }
203      }
204  
205      curs_set(0);
206  }
207  
208  /* generic keyboard conversion */
209  
210  #include "curses_keys.h"
211  
212  static kbd_layout_t *kbd_layout = NULL;
213  
214  static wint_t console_getch(enum maybe_keycode *maybe_keycode)
215  {
216      wint_t ret;
217      switch (get_wch(&ret)) {
218      case KEY_CODE_YES:
219          *maybe_keycode = CURSES_KEYCODE;
220          break;
221      case OK:
222          *maybe_keycode = CURSES_CHAR;
223          break;
224      case ERR:
225          ret = -1;
226          break;
227      }
228      return ret;
229  }
230  
231  static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
232                        int chr, enum maybe_keycode maybe_keycode)
233  {
234      int ret = -1;
235      if (maybe_keycode == CURSES_CHAR) {
236          if (chr < CURSES_CHARS) {
237              ret = _curses2foo[chr];
238          }
239      } else {
240          if (chr < CURSES_KEYS) {
241              ret = _curseskey2foo[chr];
242          }
243          if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
244              chr < CURSES_CHARS) {
245              ret = _curses2foo[chr];
246          }
247      }
248      return ret;
249  }
250  
251  #define curses2keycode(chr, maybe_keycode) \
252      curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
253  #define curses2keysym(chr, maybe_keycode) \
254      curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
255  #define curses2qemu(chr, maybe_keycode) \
256      curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
257  
258  static void curses_refresh(DisplayChangeListener *dcl)
259  {
260      int chr, keysym, keycode, keycode_alt;
261      enum maybe_keycode maybe_keycode;
262  
263      curses_winch_check();
264  
265      if (invalidate) {
266          clear();
267          refresh();
268          curses_calc_pad();
269          graphic_hw_invalidate(NULL);
270          invalidate = 0;
271      }
272  
273      graphic_hw_text_update(NULL, screen);
274  
275      while (1) {
276          /* while there are any pending key strokes to process */
277          chr = console_getch(&maybe_keycode);
278  
279          if (chr == -1)
280              break;
281  
282  #ifdef KEY_RESIZE
283          /* this shouldn't occur when we use a custom SIGWINCH handler */
284          if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
285              clear();
286              refresh();
287              curses_calc_pad();
288              curses_update(dcl, 0, 0, width, height);
289              continue;
290          }
291  #endif
292  
293          keycode = curses2keycode(chr, maybe_keycode);
294          keycode_alt = 0;
295  
296          /* alt or esc key */
297          if (keycode == 1) {
298              enum maybe_keycode next_maybe_keycode;
299              int nextchr = console_getch(&next_maybe_keycode);
300  
301              if (nextchr != -1) {
302                  chr = nextchr;
303                  maybe_keycode = next_maybe_keycode;
304                  keycode_alt = ALT;
305                  keycode = curses2keycode(chr, maybe_keycode);
306  
307                  if (keycode != -1) {
308                      keycode |= ALT;
309  
310                      /* process keys reserved for qemu */
311                      if (keycode >= QEMU_KEY_CONSOLE0 &&
312                              keycode < QEMU_KEY_CONSOLE0 + 9) {
313                          erase();
314                          wnoutrefresh(stdscr);
315                          console_select(keycode - QEMU_KEY_CONSOLE0);
316  
317                          invalidate = 1;
318                          continue;
319                      }
320                  }
321              }
322          }
323  
324          if (kbd_layout) {
325              keysym = curses2keysym(chr, maybe_keycode);
326  
327              if (keysym == -1) {
328                  if (chr < ' ') {
329                      keysym = chr + '@';
330                      if (keysym >= 'A' && keysym <= 'Z')
331                          keysym += 'a' - 'A';
332                      keysym |= KEYSYM_CNTRL;
333                  } else
334                      keysym = chr;
335              }
336  
337              keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
338                                        NULL, false);
339              if (keycode == 0)
340                  continue;
341  
342              keycode |= (keysym & ~KEYSYM_MASK) >> 16;
343              keycode |= keycode_alt;
344          }
345  
346          if (keycode == -1)
347              continue;
348  
349          if (qemu_console_is_graphic(NULL)) {
350              /* since terminals don't know about key press and release
351               * events, we need to emit both for each key received */
352              if (keycode & SHIFT) {
353                  qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
354                  qemu_input_event_send_key_delay(0);
355              }
356              if (keycode & CNTRL) {
357                  qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
358                  qemu_input_event_send_key_delay(0);
359              }
360              if (keycode & ALT) {
361                  qemu_input_event_send_key_number(NULL, ALT_CODE, true);
362                  qemu_input_event_send_key_delay(0);
363              }
364              if (keycode & ALTGR) {
365                  qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
366                  qemu_input_event_send_key_delay(0);
367              }
368  
369              qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
370              qemu_input_event_send_key_delay(0);
371              qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
372              qemu_input_event_send_key_delay(0);
373  
374              if (keycode & ALTGR) {
375                  qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
376                  qemu_input_event_send_key_delay(0);
377              }
378              if (keycode & ALT) {
379                  qemu_input_event_send_key_number(NULL, ALT_CODE, false);
380                  qemu_input_event_send_key_delay(0);
381              }
382              if (keycode & CNTRL) {
383                  qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
384                  qemu_input_event_send_key_delay(0);
385              }
386              if (keycode & SHIFT) {
387                  qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
388                  qemu_input_event_send_key_delay(0);
389              }
390          } else {
391              keysym = curses2qemu(chr, maybe_keycode);
392              if (keysym == -1)
393                  keysym = chr;
394  
395              kbd_put_keysym(keysym);
396          }
397      }
398  }
399  
400  static void curses_atexit(void)
401  {
402      endwin();
403  }
404  
405  /*
406   * In the following:
407   * - fch is the font glyph number
408   * - uch is the unicode value
409   * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
410   * - mbch is the native local-dependent multibyte representation
411   */
412  
413  /* Setup wchar glyph for one UCS-2 char */
414  static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
415  {
416      char mbch[MB_LEN_MAX];
417      wchar_t wch[2];
418      char *puch, *pmbch;
419      size_t such, smbch;
420      mbstate_t ps;
421  
422      puch = (char *) &uch;
423      pmbch = (char *) mbch;
424      such = sizeof(uch);
425      smbch = sizeof(mbch);
426  
427      if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
428          fprintf(stderr, "Could not convert 0x%04x "
429                          "from UCS-2 to a multibyte character: %s\n",
430                          uch, strerror(errno));
431          return;
432      }
433  
434      memset(&ps, 0, sizeof(ps));
435      if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
436          fprintf(stderr, "Could not convert 0x%04x "
437                          "from a multibyte character to wchar_t: %s\n",
438                          uch, strerror(errno));
439          return;
440      }
441  
442      wch[1] = 0;
443      setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
444  }
445  
446  /* Setup wchar glyph for one font character */
447  static void convert_font(unsigned char fch, iconv_t conv)
448  {
449      char mbch[MB_LEN_MAX];
450      wchar_t wch[2];
451      char *pfch, *pmbch;
452      size_t sfch, smbch;
453      mbstate_t ps;
454  
455      pfch = (char *) &fch;
456      pmbch = (char *) &mbch;
457      sfch = sizeof(fch);
458      smbch = sizeof(mbch);
459  
460      if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
461          fprintf(stderr, "Could not convert font glyph 0x%02x "
462                          "from %s to a multibyte character: %s\n",
463                          fch, font_charset, strerror(errno));
464          return;
465      }
466  
467      memset(&ps, 0, sizeof(ps));
468      if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
469          fprintf(stderr, "Could not convert font glyph 0x%02x "
470                          "from a multibyte character to wchar_t: %s\n",
471                          fch, strerror(errno));
472          return;
473      }
474  
475      wch[1] = 0;
476      setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
477  }
478  
479  /* Convert one wchar to UCS-2 */
480  static uint16_t get_ucs(wchar_t wch, iconv_t conv)
481  {
482      char mbch[MB_LEN_MAX];
483      uint16_t uch;
484      char *pmbch, *puch;
485      size_t smbch, such;
486      mbstate_t ps;
487      int ret;
488  
489      memset(&ps, 0, sizeof(ps));
490      ret = wcrtomb(mbch, wch, &ps);
491      if (ret == -1) {
492          fprintf(stderr, "Could not convert 0x%04lx "
493                          "from wchar_t to a multibyte character: %s\n",
494                          (unsigned long)wch, strerror(errno));
495          return 0xFFFD;
496      }
497  
498      pmbch = (char *) mbch;
499      puch = (char *) &uch;
500      smbch = ret;
501      such = sizeof(uch);
502  
503      if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
504          fprintf(stderr, "Could not convert 0x%04lx "
505                          "from a multibyte character to UCS-2 : %s\n",
506                          (unsigned long)wch, strerror(errno));
507          return 0xFFFD;
508      }
509  
510      return uch;
511  }
512  
513  /*
514   * Setup mapping for vga to curses line graphics.
515   */
516  static void font_setup(void)
517  {
518      iconv_t ucs2_to_nativecharset;
519      iconv_t nativecharset_to_ucs2;
520      iconv_t font_conv;
521      int i;
522  
523      /*
524       * Control characters are normally non-printable, but VGA does have
525       * well-known glyphs for them.
526       */
527      static uint16_t control_characters[0x20] = {
528        0x0020,
529        0x263a,
530        0x263b,
531        0x2665,
532        0x2666,
533        0x2663,
534        0x2660,
535        0x2022,
536        0x25d8,
537        0x25cb,
538        0x25d9,
539        0x2642,
540        0x2640,
541        0x266a,
542        0x266b,
543        0x263c,
544        0x25ba,
545        0x25c4,
546        0x2195,
547        0x203c,
548        0x00b6,
549        0x00a7,
550        0x25ac,
551        0x21a8,
552        0x2191,
553        0x2193,
554        0x2192,
555        0x2190,
556        0x221f,
557        0x2194,
558        0x25b2,
559        0x25bc
560      };
561  
562      ucs2_to_nativecharset = iconv_open(nl_langinfo(CODESET), "UCS-2");
563      if (ucs2_to_nativecharset == (iconv_t) -1) {
564          fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
565                          strerror(errno));
566          exit(1);
567      }
568  
569      nativecharset_to_ucs2 = iconv_open("UCS-2", nl_langinfo(CODESET));
570      if (nativecharset_to_ucs2 == (iconv_t) -1) {
571          iconv_close(ucs2_to_nativecharset);
572          fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
573                          strerror(errno));
574          exit(1);
575      }
576  
577      font_conv = iconv_open(nl_langinfo(CODESET), font_charset);
578      if (font_conv == (iconv_t) -1) {
579          iconv_close(ucs2_to_nativecharset);
580          iconv_close(nativecharset_to_ucs2);
581          fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
582                          font_charset, strerror(errno));
583          exit(1);
584      }
585  
586      /* Control characters */
587      for (i = 0; i <= 0x1F; i++) {
588          convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
589      }
590  
591      for (i = 0x20; i <= 0xFF; i++) {
592          convert_font(i, font_conv);
593      }
594  
595      /* DEL */
596      convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
597  
598      if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
599          /* Non-Unicode capable, use termcap equivalents for those available */
600          for (i = 0; i <= 0xFF; i++) {
601              wchar_t wch[CCHARW_MAX];
602              attr_t attr;
603              short color;
604              int ret;
605  
606              ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
607              if (ret == ERR)
608                  continue;
609  
610              switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
611              case 0x00a3:
612                  vga_to_curses[i] = *WACS_STERLING;
613                  break;
614              case 0x2591:
615                  vga_to_curses[i] = *WACS_BOARD;
616                  break;
617              case 0x2592:
618                  vga_to_curses[i] = *WACS_CKBOARD;
619                  break;
620              case 0x2502:
621                  vga_to_curses[i] = *WACS_VLINE;
622                  break;
623              case 0x2524:
624                  vga_to_curses[i] = *WACS_RTEE;
625                  break;
626              case 0x2510:
627                  vga_to_curses[i] = *WACS_URCORNER;
628                  break;
629              case 0x2514:
630                  vga_to_curses[i] = *WACS_LLCORNER;
631                  break;
632              case 0x2534:
633                  vga_to_curses[i] = *WACS_BTEE;
634                  break;
635              case 0x252c:
636                  vga_to_curses[i] = *WACS_TTEE;
637                  break;
638              case 0x251c:
639                  vga_to_curses[i] = *WACS_LTEE;
640                  break;
641              case 0x2500:
642                  vga_to_curses[i] = *WACS_HLINE;
643                  break;
644              case 0x253c:
645                  vga_to_curses[i] = *WACS_PLUS;
646                  break;
647              case 0x256c:
648                  vga_to_curses[i] = *WACS_LANTERN;
649                  break;
650              case 0x256a:
651                  vga_to_curses[i] = *WACS_NEQUAL;
652                  break;
653              case 0x2518:
654                  vga_to_curses[i] = *WACS_LRCORNER;
655                  break;
656              case 0x250c:
657                  vga_to_curses[i] = *WACS_ULCORNER;
658                  break;
659              case 0x2588:
660                  vga_to_curses[i] = *WACS_BLOCK;
661                  break;
662              case 0x03c0:
663                  vga_to_curses[i] = *WACS_PI;
664                  break;
665              case 0x00b1:
666                  vga_to_curses[i] = *WACS_PLMINUS;
667                  break;
668              case 0x2265:
669                  vga_to_curses[i] = *WACS_GEQUAL;
670                  break;
671              case 0x2264:
672                  vga_to_curses[i] = *WACS_LEQUAL;
673                  break;
674              case 0x00b0:
675                  vga_to_curses[i] = *WACS_DEGREE;
676                  break;
677              case 0x25a0:
678                  vga_to_curses[i] = *WACS_BULLET;
679                  break;
680              case 0x2666:
681                  vga_to_curses[i] = *WACS_DIAMOND;
682                  break;
683              case 0x2192:
684                  vga_to_curses[i] = *WACS_RARROW;
685                  break;
686              case 0x2190:
687                  vga_to_curses[i] = *WACS_LARROW;
688                  break;
689              case 0x2191:
690                  vga_to_curses[i] = *WACS_UARROW;
691                  break;
692              case 0x2193:
693                  vga_to_curses[i] = *WACS_DARROW;
694                  break;
695              case 0x23ba:
696                  vga_to_curses[i] = *WACS_S1;
697                  break;
698              case 0x23bb:
699                  vga_to_curses[i] = *WACS_S3;
700                  break;
701              case 0x23bc:
702                  vga_to_curses[i] = *WACS_S7;
703                  break;
704              case 0x23bd:
705                  vga_to_curses[i] = *WACS_S9;
706                  break;
707              }
708          }
709      }
710      iconv_close(ucs2_to_nativecharset);
711      iconv_close(nativecharset_to_ucs2);
712      iconv_close(font_conv);
713  }
714  
715  static void curses_setup(void)
716  {
717      int i, colour_default[8] = {
718          [QEMU_COLOR_BLACK]   = COLOR_BLACK,
719          [QEMU_COLOR_BLUE]    = COLOR_BLUE,
720          [QEMU_COLOR_GREEN]   = COLOR_GREEN,
721          [QEMU_COLOR_CYAN]    = COLOR_CYAN,
722          [QEMU_COLOR_RED]     = COLOR_RED,
723          [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
724          [QEMU_COLOR_YELLOW]  = COLOR_YELLOW,
725          [QEMU_COLOR_WHITE]   = COLOR_WHITE,
726      };
727  
728      /* input as raw as possible, let everything be interpreted
729       * by the guest system */
730      initscr(); noecho(); intrflush(stdscr, FALSE);
731      nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
732      start_color(); raw(); scrollok(stdscr, FALSE);
733      set_escdelay(25);
734  
735      /* Make color pair to match color format (3bits bg:3bits fg) */
736      for (i = 0; i < 64; i++) {
737          init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
738      }
739      /* Set default color for more than 64 for safety. */
740      for (i = 64; i < COLOR_PAIRS; i++) {
741          init_pair(i, COLOR_WHITE, COLOR_BLACK);
742      }
743  
744      font_setup();
745  }
746  
747  static void curses_keyboard_setup(void)
748  {
749  #if defined(__APPLE__)
750      /* always use generic keymaps */
751      if (!keyboard_layout)
752          keyboard_layout = "en-us";
753  #endif
754      if(keyboard_layout) {
755          kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
756                                            &error_fatal);
757      }
758  }
759  
760  static const DisplayChangeListenerOps dcl_ops = {
761      .dpy_name        = "curses",
762      .dpy_text_update = curses_update,
763      .dpy_text_resize = curses_resize,
764      .dpy_refresh     = curses_refresh,
765      .dpy_text_cursor = curses_cursor_position,
766  };
767  
768  static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
769  {
770  #ifndef _WIN32
771      if (!isatty(1)) {
772          fprintf(stderr, "We need a terminal output\n");
773          exit(1);
774      }
775  #endif
776  
777      setlocale(LC_CTYPE, "");
778      if (opts->u.curses.charset) {
779          font_charset = opts->u.curses.charset;
780      }
781      curses_setup();
782      curses_keyboard_setup();
783      atexit(curses_atexit);
784  
785      curses_winch_init();
786  
787      dcl = g_new0(DisplayChangeListener, 1);
788      dcl->ops = &dcl_ops;
789      register_displaychangelistener(dcl);
790  
791      invalidate = 1;
792  }
793  
794  static QemuDisplay qemu_display_curses = {
795      .type       = DISPLAY_TYPE_CURSES,
796      .init       = curses_display_init,
797  };
798  
799  static void register_curses(void)
800  {
801      qemu_display_register(&qemu_display_curses);
802  }
803  
804  type_init(register_curses);
805