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