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