@@ -2964,14 +2964,17 @@ if test "$curses" != "no" ; then
#include <locale.h>
#include <curses.h>
#include <wchar.h>
+#include <langinfo.h>
int main(void) {
const char *s = curses_version();
+ const char *codeset;
wchar_t wch = L'w';
setlocale(LC_ALL, "");
resize_term(0, 0);
addwstr(L"wide chars\n");
addnwstr(&wch, 1);
- return s != 0;
+ codeset = nl_langinfo(CODESET);
+ return s != 0 && codeset != 0;
}
EOF
IFS=:
@@ -1966,7 +1966,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
for (i = 0; i < size; src ++, dst ++, i ++) {
console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src)));
- if (*dst != val) {
+ if (memcmp(dst, &val, sizeof(val))) {
*dst = val;
c_max = i;
break;
@@ -1975,7 +1975,7 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
c_min = i;
for (; i < size; src ++, dst ++, i ++) {
console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src)));
- if (*dst != val) {
+ if (memcmp(dst, &val, sizeof(val))) {
*dst = val;
c_max = i;
}
@@ -145,6 +145,7 @@ extern int graphic_height;
extern int graphic_depth;
extern int display_opengl;
extern const char *keyboard_layout;
+extern const char *font_charset;
extern int win2k_install_hack;
extern int alt_grab;
extern int ctrl_grab;
@@ -338,8 +338,8 @@ static inline pixman_format_code_t surface_format(DisplaySurface *s)
#ifdef CONFIG_CURSES
#include <curses.h>
-typedef chtype console_ch_t;
-extern chtype vga_to_curses[];
+typedef cchar_t console_ch_t;
+extern console_ch_t vga_to_curses[];
#else
typedef unsigned long console_ch_t;
#endif
@@ -347,16 +347,20 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
{
uint8_t c = ch;
#ifdef CONFIG_CURSES
- if (vga_to_curses[c]) {
- ch &= ~(console_ch_t)0xff;
- ch |= vga_to_curses[c];
+ if (vga_to_curses[c].chars[0]) {
+ *dest = vga_to_curses[c];
+ } else {
+ dest->chars[0] = c;
+ dest->chars[1] = 0;
+ dest->attr = 0;
}
+ dest->attr |= ch & ~0xff;
#else
if (c == '\0') {
ch |= ' ';
}
-#endif
*dest = ch;
+#endif
}
typedef struct GraphicHwOps {
@@ -930,7 +930,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
" [,window_close=on|off][,gl=on|off]|curses|none|\n"
"-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n"
"-display vnc=<display>[,<optargs>]\n"
- "-display curses\n"
+ "-display curses[,charset=<encoding>]\n"
"-display none"
" select display type\n"
"The default display is equivalent to\n"
@@ -961,6 +961,9 @@ support a text mode, QEMU can display this output using a
curses/ncurses interface. Nothing is displayed when the graphics
device is in graphical mode or if the graphics device does not support
a text mode. Generally only the VGA device models support text mode.
+The font charset used by the guest can be specified with the
+@code{charset} option, for example @code{charset=CP850} for IBM CP850
+encoding. The default is @code{CP437}.
@item none
Do not display video output. The guest will still see an emulated
graphics card, but its output will not be displayed to the QEMU
@@ -28,6 +28,10 @@
#include <sys/ioctl.h>
#include <termios.h>
#endif
+#include <locale.h>
+#include <wchar.h>
+#include <langinfo.h>
+#include <iconv.h>
#include "qemu-common.h"
#include "ui/console.h"
@@ -49,7 +53,7 @@ static WINDOW *screenpad = NULL;
static int width, height, gwidth, gheight, invalidate;
static int px, py, sminx, sminy, smaxx, smaxy;
-chtype vga_to_curses[256];
+console_ch_t vga_to_curses[256];
static wint_t console_getch(int *maybe_keycode)
{
@@ -71,11 +75,12 @@ static wint_t console_getch(int *maybe_keycode)
static void curses_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
- chtype *line;
+ console_ch_t *line;
- line = ((chtype *) screen) + y * width;
- for (h += y; y < h; y ++, line += width)
- mvwaddchnstr(screenpad, y, 0, line, width);
+ line = ((console_ch_t *) screen) + y * width;
+ for (h += y; y < h; y ++, line += width) {
+ mvwadd_wchnstr(screenpad, y, 0, line, width);
+ }
pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
refresh();
@@ -375,6 +380,252 @@ static void curses_atexit(void)
endwin();
}
+/* Setup wchar glyph for one UCS-2 char */
+static void convert_ucs(int glyph, uint16_t ch, iconv_t conv)
+{
+ wchar_t wch;
+ char *pch, *pwch;
+ size_t sch, swch;
+
+ pch = (char *) &ch;
+ pwch = (char *) &wch;
+ sch = sizeof(ch);
+ swch = sizeof(wch);
+
+ if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) {
+ fprintf(stderr, "Could not convert 0x%04x from UCS-2 to WCHAR_T: %s\n",
+ ch, strerror(errno));
+ } else {
+ vga_to_curses[glyph].chars[0] = wch;
+ }
+}
+
+/* Setup wchar glyph for one font character */
+static void convert_font(unsigned char ch, iconv_t conv)
+{
+ wchar_t wch;
+ char *pch, *pwch;
+ size_t sch, swch;
+
+ pch = (char *) &ch;
+ pwch = (char *) &wch;
+ sch = sizeof(ch);
+ swch = sizeof(wch);
+
+ if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) {
+ fprintf(stderr, "Could not convert 0x%02x from %s to WCHAR_T: %s\n",
+ ch, font_charset, strerror(errno));
+ } else {
+ vga_to_curses[ch].chars[0] = wch;
+ }
+}
+
+/* Convert one wchar to UCS-2 */
+static uint16_t get_ucs(wchar_t wch, iconv_t conv)
+{
+ uint16_t ch;
+ char *pch, *pwch;
+ size_t sch, swch;
+
+ pch = (char *) &ch;
+ pwch = (char *) &wch;
+ sch = sizeof(ch);
+ swch = sizeof(wch);
+
+ if (iconv(conv, &pwch, &swch, &pch, &sch) == (size_t) -1) {
+ fprintf(stderr, "Could not convert 0x%02x from WCHAR_T to UCS-2: %s\n",
+ wch, strerror(errno));
+ return 0xFFFD;
+ }
+
+ return ch;
+}
+
+/*
+ * Setup mapping for vga to curses line graphics.
+ */
+static void font_setup(void)
+{
+ /* Control characters are normally non-printable, but VGA does have
+ * well-known glyphs for them. */
+ static uint16_t control_characters[0x20] = {
+ 0x0020,
+ 0x263a,
+ 0x263b,
+ 0x2665,
+ 0x2666,
+ 0x2663,
+ 0x2660,
+ 0x2022,
+ 0x25d8,
+ 0x25cb,
+ 0x25d9,
+ 0x2642,
+ 0x2640,
+ 0x266a,
+ 0x266b,
+ 0x263c,
+ 0x25ba,
+ 0x25c4,
+ 0x2195,
+ 0x203c,
+ 0x00b6,
+ 0x00a7,
+ 0x25ac,
+ 0x21a8,
+ 0x2191,
+ 0x2193,
+ 0x2192,
+ 0x2190,
+ 0x221f,
+ 0x2194,
+ 0x25b2,
+ 0x25bc
+ };
+
+ iconv_t ucs_to_wchar_conv;
+ iconv_t wchar_to_ucs_conv;
+ iconv_t font_conv;
+ int i;
+
+ ucs_to_wchar_conv = iconv_open("WCHAR_T", "UCS-2");
+ if (ucs_to_wchar_conv == (iconv_t) -1) {
+ fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ wchar_to_ucs_conv = iconv_open("UCS-2", "WCHAR_T");
+ if (wchar_to_ucs_conv == (iconv_t) -1) {
+ fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ font_conv = iconv_open("WCHAR_T", font_charset);
+ if (font_conv == (iconv_t) -1) {
+ fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
+ font_charset, strerror(errno));
+ exit(1);
+ }
+
+ /* Control characters */
+ for (i = 0; i <= 0x1F; i++) {
+ convert_ucs(i, control_characters[i], ucs_to_wchar_conv);
+ }
+
+ for (i = 0x20; i <= 0xFF; i++) {
+ convert_font(i, font_conv);
+ }
+
+ /* DEL */
+ convert_ucs(0x7F, 0x2302, ucs_to_wchar_conv);
+
+ if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
+ /* Non-Unicode capable, use termcap equivalents for those available */
+ for (i = 0; i <= 0xFF; i++) {
+ switch (get_ucs(vga_to_curses[i].chars[0], wchar_to_ucs_conv)) {
+ case 0x00a3:
+ vga_to_curses[i] = *WACS_STERLING;
+ break;
+ case 0x2591:
+ vga_to_curses[i] = *WACS_BOARD;
+ break;
+ case 0x2592:
+ vga_to_curses[i] = *WACS_CKBOARD;
+ break;
+ case 0x2502:
+ vga_to_curses[i] = *WACS_VLINE;
+ break;
+ case 0x2524:
+ vga_to_curses[i] = *WACS_RTEE;
+ break;
+ case 0x2510:
+ vga_to_curses[i] = *WACS_URCORNER;
+ break;
+ case 0x2514:
+ vga_to_curses[i] = *WACS_LLCORNER;
+ break;
+ case 0x2534:
+ vga_to_curses[i] = *WACS_BTEE;
+ break;
+ case 0x252c:
+ vga_to_curses[i] = *WACS_TTEE;
+ break;
+ case 0x251c:
+ vga_to_curses[i] = *WACS_LTEE;
+ break;
+ case 0x2500:
+ vga_to_curses[i] = *WACS_HLINE;
+ break;
+ case 0x253c:
+ vga_to_curses[i] = *WACS_PLUS;
+ break;
+ case 0x256c:
+ vga_to_curses[i] = *WACS_LANTERN;
+ break;
+ case 0x256a:
+ vga_to_curses[i] = *WACS_NEQUAL;
+ break;
+ case 0x2518:
+ vga_to_curses[i] = *WACS_LRCORNER;
+ break;
+ case 0x250c:
+ vga_to_curses[i] = *WACS_ULCORNER;
+ break;
+ case 0x2588:
+ vga_to_curses[i] = *WACS_BLOCK;
+ break;
+ case 0x03c0:
+ vga_to_curses[i] = *WACS_PI;
+ break;
+ case 0x00b1:
+ vga_to_curses[i] = *WACS_PLMINUS;
+ break;
+ case 0x2265:
+ vga_to_curses[i] = *WACS_GEQUAL;
+ break;
+ case 0x2264:
+ vga_to_curses[i] = *WACS_LEQUAL;
+ break;
+ case 0x00b0:
+ vga_to_curses[i] = *WACS_DEGREE;
+ break;
+ case 0x25a0:
+ vga_to_curses[i] = *WACS_BULLET;
+ break;
+ case 0x2666:
+ vga_to_curses[i] = *WACS_DIAMOND;
+ break;
+ case 0x2192:
+ vga_to_curses[i] = *WACS_RARROW;
+ break;
+ case 0x2190:
+ vga_to_curses[i] = *WACS_LARROW;
+ break;
+ case 0x2191:
+ vga_to_curses[i] = *WACS_UARROW;
+ break;
+ case 0x2193:
+ vga_to_curses[i] = *WACS_DARROW;
+ break;
+ case 0x23ba:
+ vga_to_curses[i] = *WACS_S1;
+ break;
+ case 0x23bb:
+ vga_to_curses[i] = *WACS_S3;
+ break;
+ case 0x23bc:
+ vga_to_curses[i] = *WACS_S7;
+ break;
+ case 0x23bd:
+ vga_to_curses[i] = *WACS_S9;
+ break;
+ }
+ }
+ }
+}
+
static void curses_setup(void)
{
int i, colour_default[8] = {
@@ -404,47 +655,7 @@ static void curses_setup(void)
init_pair(i, COLOR_WHITE, COLOR_BLACK);
}
- /*
- * Setup mapping for vga to curses line graphics.
- * FIXME: for better font, have to use ncursesw and setlocale()
- */
-#if 0
- /* FIXME: map from where? */
- ACS_S1;
- ACS_S3;
- ACS_S7;
- ACS_S9;
-#endif
- /* ACS_* is not constant. So, we can't initialize statically. */
- vga_to_curses['\0'] = ' ';
- vga_to_curses[0x04] = ACS_DIAMOND;
- vga_to_curses[0x18] = ACS_UARROW;
- vga_to_curses[0x19] = ACS_DARROW;
- vga_to_curses[0x1a] = ACS_RARROW;
- vga_to_curses[0x1b] = ACS_LARROW;
- vga_to_curses[0x9c] = ACS_STERLING;
- vga_to_curses[0xb0] = ACS_BOARD;
- vga_to_curses[0xb1] = ACS_CKBOARD;
- vga_to_curses[0xb3] = ACS_VLINE;
- vga_to_curses[0xb4] = ACS_RTEE;
- vga_to_curses[0xbf] = ACS_URCORNER;
- vga_to_curses[0xc0] = ACS_LLCORNER;
- vga_to_curses[0xc1] = ACS_BTEE;
- vga_to_curses[0xc2] = ACS_TTEE;
- vga_to_curses[0xc3] = ACS_LTEE;
- vga_to_curses[0xc4] = ACS_HLINE;
- vga_to_curses[0xc5] = ACS_PLUS;
- vga_to_curses[0xce] = ACS_LANTERN;
- vga_to_curses[0xd8] = ACS_NEQUAL;
- vga_to_curses[0xd9] = ACS_LRCORNER;
- vga_to_curses[0xda] = ACS_ULCORNER;
- vga_to_curses[0xdb] = ACS_BLOCK;
- vga_to_curses[0xe3] = ACS_PI;
- vga_to_curses[0xf1] = ACS_PLMINUS;
- vga_to_curses[0xf2] = ACS_GEQUAL;
- vga_to_curses[0xf3] = ACS_LEQUAL;
- vga_to_curses[0xf8] = ACS_DEGREE;
- vga_to_curses[0xfe] = ACS_BULLET;
+ font_setup();
}
static void curses_keyboard_setup(void)
@@ -478,6 +689,7 @@ void curses_display_init(DisplayState *ds, int full_screen)
}
#endif
+ setlocale(LC_CTYPE, "");
curses_setup();
curses_keyboard_setup();
atexit(curses_atexit);
@@ -133,7 +133,8 @@ const char *bios_name = NULL;
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
int request_opengl = -1;
int display_opengl;
-const char* keyboard_layout = NULL;
+const char *keyboard_layout;
+const char *font_charset = "CP437";
ram_addr_t ram_size;
const char *mem_path = NULL;
int mem_prealloc = 0; /* force preallocation of physical target memory */
@@ -2184,6 +2185,24 @@ static DisplayType select_display(const char *p)
} else if (strstart(p, "curses", &opts)) {
#ifdef CONFIG_CURSES
display = DT_CURSES;
+ while (*opts) {
+ const char *nextopt, *name;
+
+ if (strstart(opts, ",charset=", &name)) {
+ nextopt = strchr(name, ',');
+ if (nextopt) {
+ font_charset = g_strndup(name, nextopt - name);
+ nextopt++;
+ } else {
+ font_charset = g_strdup(name);
+ nextopt = name + strlen(name);
+ }
+ } else {
+ error_report("invalid curses option string");
+ exit(1);
+ }
+ opts = nextopt;
+ }
#else
error_report("curses or iconv support is disabled");
exit(1);
This uses iconv to convert glyphs from the specified VGA font encoding to unicode, and makes use of cchar_t instead of chtype when using ncursesw, which allows to store all wide char as well as the WACS values. Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org> --- configure | 5 +- hw/display/vga.c | 4 +- include/sysemu/sysemu.h | 1 + include/ui/console.h | 16 ++- qemu-options.hx | 5 +- ui/curses.c | 304 ++++++++++++++++++++++++++++++++++++++++-------- vl.c | 21 +++- 7 files changed, 299 insertions(+), 57 deletions(-)