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 "sysemu/sysemu.h"
40
41 #if defined(__APPLE__) || defined(__OpenBSD__)
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 int chr, keysym, keycode, keycode_alt;
269 enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
270
271 curses_winch_check();
272
273 if (invalidate) {
274 clear();
275 refresh();
276 curses_calc_pad();
277 graphic_hw_invalidate(dcl->con);
278 invalidate = 0;
279 }
280
281 graphic_hw_text_update(dcl->con, screen);
282
283 while (1) {
284 /* while there are any pending key strokes to process */
285 chr = console_getch(&maybe_keycode);
286
287 if (chr == -1)
288 break;
289
290 #ifdef KEY_RESIZE
291 /* this shouldn't occur when we use a custom SIGWINCH handler */
292 if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
293 clear();
294 refresh();
295 curses_calc_pad();
296 curses_update(dcl, 0, 0, width, height);
297 continue;
298 }
299 #endif
300
301 keycode = curses2keycode(chr, maybe_keycode);
302 keycode_alt = 0;
303
304 /* alt or esc key */
305 if (keycode == 1) {
306 enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
307 int nextchr = console_getch(&next_maybe_keycode);
308
309 if (nextchr != -1) {
310 chr = nextchr;
311 maybe_keycode = next_maybe_keycode;
312 keycode_alt = ALT;
313 keycode = curses2keycode(chr, maybe_keycode);
314
315 if (keycode != -1) {
316 keycode |= ALT;
317
318 /* process keys reserved for qemu */
319 if (keycode >= QEMU_KEY_CONSOLE0 &&
320 keycode < QEMU_KEY_CONSOLE0 + 9) {
321 QemuConsole *con = qemu_console_lookup_by_index(keycode - QEMU_KEY_CONSOLE0);
322 if (con) {
323 erase();
324 wnoutrefresh(stdscr);
325 unregister_displaychangelistener(dcl);
326 dcl->con = con;
327 register_displaychangelistener(dcl);
328
329 invalidate = 1;
330 }
331 continue;
332 }
333 }
334 }
335 }
336
337 if (kbd_layout) {
338 keysym = curses2keysym(chr, maybe_keycode);
339
340 if (keysym == -1) {
341 if (chr < ' ') {
342 keysym = chr + '@';
343 if (keysym >= 'A' && keysym <= 'Z')
344 keysym += 'a' - 'A';
345 keysym |= KEYSYM_CNTRL;
346 } else
347 keysym = chr;
348 }
349
350 keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
351 NULL, false);
352 if (keycode == 0)
353 continue;
354
355 keycode |= (keysym & ~KEYSYM_MASK) >> 16;
356 keycode |= keycode_alt;
357 }
358
359 if (keycode == -1)
360 continue;
361
362 if (qemu_console_is_graphic(dcl->con)) {
363 /* since terminals don't know about key press and release
364 * events, we need to emit both for each key received */
365 if (keycode & SHIFT) {
366 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, true);
367 qemu_input_event_send_key_delay(0);
368 }
369 if (keycode & CNTRL) {
370 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, true);
371 qemu_input_event_send_key_delay(0);
372 }
373 if (keycode & ALT) {
374 qemu_input_event_send_key_number(dcl->con, ALT_CODE, true);
375 qemu_input_event_send_key_delay(0);
376 }
377 if (keycode & ALTGR) {
378 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, true);
379 qemu_input_event_send_key_delay(0);
380 }
381
382 qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, true);
383 qemu_input_event_send_key_delay(0);
384 qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, false);
385 qemu_input_event_send_key_delay(0);
386
387 if (keycode & ALTGR) {
388 qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, false);
389 qemu_input_event_send_key_delay(0);
390 }
391 if (keycode & ALT) {
392 qemu_input_event_send_key_number(dcl->con, ALT_CODE, false);
393 qemu_input_event_send_key_delay(0);
394 }
395 if (keycode & CNTRL) {
396 qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, false);
397 qemu_input_event_send_key_delay(0);
398 }
399 if (keycode & SHIFT) {
400 qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, false);
401 qemu_input_event_send_key_delay(0);
402 }
403 } else {
404 keysym = curses2qemu(chr, maybe_keycode);
405 if (keysym == -1)
406 keysym = chr;
407
408 qemu_text_console_put_keysym(QEMU_TEXT_CONSOLE(dcl->con), keysym);
409 }
410 }
411 }
412
curses_atexit(void)413 static void curses_atexit(void)
414 {
415 endwin();
416 g_free(vga_to_curses);
417 g_free(screen);
418 }
419
420 /*
421 * In the following:
422 * - fch is the font glyph number
423 * - uch is the unicode value
424 * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
425 * - mbch is the native local-dependent multibyte representation
426 */
427
428 /* Setup wchar glyph for one UCS-2 char */
convert_ucs(unsigned char fch,uint16_t uch,iconv_t conv)429 static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
430 {
431 char mbch[MB_LEN_MAX];
432 wchar_t wch[2];
433 char *puch, *pmbch;
434 size_t such, smbch;
435 mbstate_t ps;
436
437 puch = (char *) &uch;
438 pmbch = (char *) mbch;
439 such = sizeof(uch);
440 smbch = sizeof(mbch);
441
442 if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
443 fprintf(stderr, "Could not convert 0x%04x "
444 "from UCS-2 to a multibyte character: %s\n",
445 uch, strerror(errno));
446 return;
447 }
448
449 memset(&ps, 0, sizeof(ps));
450 if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
451 fprintf(stderr, "Could not convert 0x%04x "
452 "from a multibyte character to wchar_t: %s\n",
453 uch, strerror(errno));
454 return;
455 }
456
457 wch[1] = 0;
458 setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
459 }
460
461 /* Setup wchar glyph for one font character */
convert_font(unsigned char fch,iconv_t conv)462 static void convert_font(unsigned char fch, iconv_t conv)
463 {
464 char mbch[MB_LEN_MAX];
465 wchar_t wch[2];
466 char *pfch, *pmbch;
467 size_t sfch, smbch;
468 mbstate_t ps;
469
470 pfch = (char *) &fch;
471 pmbch = (char *) &mbch;
472 sfch = sizeof(fch);
473 smbch = sizeof(mbch);
474
475 if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
476 fprintf(stderr, "Could not convert font glyph 0x%02x "
477 "from %s to a multibyte character: %s\n",
478 fch, font_charset, strerror(errno));
479 return;
480 }
481
482 memset(&ps, 0, sizeof(ps));
483 if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
484 fprintf(stderr, "Could not convert font glyph 0x%02x "
485 "from a multibyte character to wchar_t: %s\n",
486 fch, strerror(errno));
487 return;
488 }
489
490 wch[1] = 0;
491 setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
492 }
493
494 /* Convert one wchar to UCS-2 */
get_ucs(wchar_t wch,iconv_t conv)495 static uint16_t get_ucs(wchar_t wch, iconv_t conv)
496 {
497 char mbch[MB_LEN_MAX];
498 uint16_t uch;
499 char *pmbch, *puch;
500 size_t smbch, such;
501 mbstate_t ps;
502 int ret;
503
504 memset(&ps, 0, sizeof(ps));
505 ret = wcrtomb(mbch, wch, &ps);
506 if (ret == -1) {
507 fprintf(stderr, "Could not convert 0x%04lx "
508 "from wchar_t to a multibyte character: %s\n",
509 (unsigned long)wch, strerror(errno));
510 return 0xFFFD;
511 }
512
513 pmbch = (char *) mbch;
514 puch = (char *) &uch;
515 smbch = ret;
516 such = sizeof(uch);
517
518 if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
519 fprintf(stderr, "Could not convert 0x%04lx "
520 "from a multibyte character to UCS-2 : %s\n",
521 (unsigned long)wch, strerror(errno));
522 return 0xFFFD;
523 }
524
525 return uch;
526 }
527
528 /*
529 * Setup mapping for vga to curses line graphics.
530 */
font_setup(void)531 static void font_setup(void)
532 {
533 iconv_t ucs2_to_nativecharset;
534 iconv_t nativecharset_to_ucs2;
535 iconv_t font_conv;
536 int i;
537 g_autofree gchar *local_codeset = g_get_codeset();
538
539 /*
540 * Control characters are normally non-printable, but VGA does have
541 * well-known glyphs for them.
542 */
543 static const uint16_t control_characters[0x20] = {
544 0x0020,
545 0x263a,
546 0x263b,
547 0x2665,
548 0x2666,
549 0x2663,
550 0x2660,
551 0x2022,
552 0x25d8,
553 0x25cb,
554 0x25d9,
555 0x2642,
556 0x2640,
557 0x266a,
558 0x266b,
559 0x263c,
560 0x25ba,
561 0x25c4,
562 0x2195,
563 0x203c,
564 0x00b6,
565 0x00a7,
566 0x25ac,
567 0x21a8,
568 0x2191,
569 0x2193,
570 0x2192,
571 0x2190,
572 0x221f,
573 0x2194,
574 0x25b2,
575 0x25bc
576 };
577
578 ucs2_to_nativecharset = iconv_open(local_codeset, "UCS-2");
579 if (ucs2_to_nativecharset == (iconv_t) -1) {
580 fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
581 strerror(errno));
582 exit(1);
583 }
584
585 nativecharset_to_ucs2 = iconv_open("UCS-2", local_codeset);
586 if (nativecharset_to_ucs2 == (iconv_t) -1) {
587 iconv_close(ucs2_to_nativecharset);
588 fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
589 strerror(errno));
590 exit(1);
591 }
592
593 font_conv = iconv_open(local_codeset, font_charset);
594 if (font_conv == (iconv_t) -1) {
595 iconv_close(ucs2_to_nativecharset);
596 iconv_close(nativecharset_to_ucs2);
597 fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
598 font_charset, strerror(errno));
599 exit(1);
600 }
601
602 /* Control characters */
603 for (i = 0; i <= 0x1F; i++) {
604 convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
605 }
606
607 for (i = 0x20; i <= 0xFF; i++) {
608 convert_font(i, font_conv);
609 }
610
611 /* DEL */
612 convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
613
614 if (strcmp(local_codeset, "UTF-8")) {
615 /* Non-Unicode capable, use termcap equivalents for those available */
616 for (i = 0; i <= 0xFF; i++) {
617 wchar_t wch[CCHARW_MAX];
618 attr_t attr;
619 short color;
620 int ret;
621
622 ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
623 if (ret == ERR)
624 continue;
625
626 switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
627 case 0x00a3:
628 vga_to_curses[i] = *WACS_STERLING;
629 break;
630 case 0x2591:
631 vga_to_curses[i] = *WACS_BOARD;
632 break;
633 case 0x2592:
634 vga_to_curses[i] = *WACS_CKBOARD;
635 break;
636 case 0x2502:
637 vga_to_curses[i] = *WACS_VLINE;
638 break;
639 case 0x2524:
640 vga_to_curses[i] = *WACS_RTEE;
641 break;
642 case 0x2510:
643 vga_to_curses[i] = *WACS_URCORNER;
644 break;
645 case 0x2514:
646 vga_to_curses[i] = *WACS_LLCORNER;
647 break;
648 case 0x2534:
649 vga_to_curses[i] = *WACS_BTEE;
650 break;
651 case 0x252c:
652 vga_to_curses[i] = *WACS_TTEE;
653 break;
654 case 0x251c:
655 vga_to_curses[i] = *WACS_LTEE;
656 break;
657 case 0x2500:
658 vga_to_curses[i] = *WACS_HLINE;
659 break;
660 case 0x253c:
661 vga_to_curses[i] = *WACS_PLUS;
662 break;
663 case 0x256c:
664 vga_to_curses[i] = *WACS_LANTERN;
665 break;
666 case 0x256a:
667 vga_to_curses[i] = *WACS_NEQUAL;
668 break;
669 case 0x2518:
670 vga_to_curses[i] = *WACS_LRCORNER;
671 break;
672 case 0x250c:
673 vga_to_curses[i] = *WACS_ULCORNER;
674 break;
675 case 0x2588:
676 vga_to_curses[i] = *WACS_BLOCK;
677 break;
678 case 0x03c0:
679 vga_to_curses[i] = *WACS_PI;
680 break;
681 case 0x00b1:
682 vga_to_curses[i] = *WACS_PLMINUS;
683 break;
684 case 0x2265:
685 vga_to_curses[i] = *WACS_GEQUAL;
686 break;
687 case 0x2264:
688 vga_to_curses[i] = *WACS_LEQUAL;
689 break;
690 case 0x00b0:
691 vga_to_curses[i] = *WACS_DEGREE;
692 break;
693 case 0x25a0:
694 vga_to_curses[i] = *WACS_BULLET;
695 break;
696 case 0x2666:
697 vga_to_curses[i] = *WACS_DIAMOND;
698 break;
699 case 0x2192:
700 vga_to_curses[i] = *WACS_RARROW;
701 break;
702 case 0x2190:
703 vga_to_curses[i] = *WACS_LARROW;
704 break;
705 case 0x2191:
706 vga_to_curses[i] = *WACS_UARROW;
707 break;
708 case 0x2193:
709 vga_to_curses[i] = *WACS_DARROW;
710 break;
711 case 0x23ba:
712 vga_to_curses[i] = *WACS_S1;
713 break;
714 case 0x23bb:
715 vga_to_curses[i] = *WACS_S3;
716 break;
717 case 0x23bc:
718 vga_to_curses[i] = *WACS_S7;
719 break;
720 case 0x23bd:
721 vga_to_curses[i] = *WACS_S9;
722 break;
723 }
724 }
725 }
726 iconv_close(ucs2_to_nativecharset);
727 iconv_close(nativecharset_to_ucs2);
728 iconv_close(font_conv);
729 }
730
curses_setup(void)731 static void curses_setup(void)
732 {
733 int i, colour_default[8] = {
734 [QEMU_COLOR_BLACK] = COLOR_BLACK,
735 [QEMU_COLOR_BLUE] = COLOR_BLUE,
736 [QEMU_COLOR_GREEN] = COLOR_GREEN,
737 [QEMU_COLOR_CYAN] = COLOR_CYAN,
738 [QEMU_COLOR_RED] = COLOR_RED,
739 [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
740 [QEMU_COLOR_YELLOW] = COLOR_YELLOW,
741 [QEMU_COLOR_WHITE] = COLOR_WHITE,
742 };
743
744 /* input as raw as possible, let everything be interpreted
745 * by the guest system */
746 initscr(); noecho(); intrflush(stdscr, FALSE);
747 nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
748 start_color(); raw(); scrollok(stdscr, FALSE);
749 set_escdelay(25);
750
751 /* Make color pair to match color format (3bits bg:3bits fg) */
752 for (i = 0; i < 64; i++) {
753 init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
754 }
755 /* Set default color for more than 64 for safety. */
756 for (i = 64; i < COLOR_PAIRS; i++) {
757 init_pair(i, COLOR_WHITE, COLOR_BLACK);
758 }
759
760 font_setup();
761 }
762
curses_keyboard_setup(void)763 static void curses_keyboard_setup(void)
764 {
765 #if defined(__APPLE__)
766 /* always use generic keymaps */
767 if (!keyboard_layout)
768 keyboard_layout = "en-us";
769 #endif
770 if(keyboard_layout) {
771 kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
772 &error_fatal);
773 }
774 }
775
776 static const DisplayChangeListenerOps dcl_ops = {
777 .dpy_name = "curses",
778 .dpy_text_update = curses_update,
779 .dpy_text_resize = curses_resize,
780 .dpy_refresh = curses_refresh,
781 .dpy_text_cursor = curses_cursor_position,
782 };
783
curses_display_init(DisplayState * ds,DisplayOptions * opts)784 static void curses_display_init(DisplayState *ds, DisplayOptions *opts)
785 {
786 #ifndef _WIN32
787 if (!isatty(1)) {
788 fprintf(stderr, "We need a terminal output\n");
789 exit(1);
790 }
791 #endif
792
793 setlocale(LC_CTYPE, "");
794 if (opts->u.curses.charset) {
795 font_charset = opts->u.curses.charset;
796 }
797 screen = g_new0(console_ch_t, 160 * 100);
798 vga_to_curses = g_new0(cchar_t, 256);
799 curses_setup();
800 curses_keyboard_setup();
801 atexit(curses_atexit);
802
803 curses_winch_init();
804
805 dcl = g_new0(DisplayChangeListener, 1);
806 dcl->con = qemu_console_lookup_default();
807 dcl->ops = &dcl_ops;
808 register_displaychangelistener(dcl);
809
810 invalidate = 1;
811 }
812
813 static QemuDisplay qemu_display_curses = {
814 .type = DISPLAY_TYPE_CURSES,
815 .init = curses_display_init,
816 };
817
register_curses(void)818 static void register_curses(void)
819 {
820 qemu_display_register(&qemu_display_curses);
821 }
822
823 type_init(register_curses);
824