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