Re: [Qemu-devel] [PATCHv2 2/2] curses: add option to specify VGA font encoding

2019-03-04 Thread Markus Armbruster
Samuel Thibault  writes:

> 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. The default
> charset is made CP437 since that is the charset of the hardware default VGA
> font. This also makes the curses backend set the LC_CTYPE locale to "" to
> allow curses to emit wide characters.
>
> Signed-off-by: Samuel Thibault 
> Cc: Eddie Kohler 

For the QAPI / CLI part:
Acked-by: Markus Armbruster 



[Qemu-devel] [PATCHv2 2/2] curses: add option to specify VGA font encoding

2019-03-04 Thread Samuel Thibault
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. The default
charset is made CP437 since that is the charset of the hardware default VGA
font. This also makes the curses backend set the LC_CTYPE locale to "" to
allow curses to emit wide characters.

Signed-off-by: Samuel Thibault 
Cc: Eddie Kohler 
---
 configure   |   5 +-
 qapi/ui.json|  14 +++
 qemu-options.hx |   5 +-
 ui/curses.c | 315 
 4 files changed, 290 insertions(+), 49 deletions(-)

diff --git a/configure b/configure
index 9979ca708d..1270dc8dc0 100755
--- a/configure
+++ b/configure
@@ -3449,14 +3449,17 @@ if test "$curses" != "no" ; then
 #include 
 #include 
 #include 
+#include 
 int main(void) {
+  const char *codeset;
   wchar_t wch = L'w';
   setlocale(LC_ALL, "");
   resize_term(0, 0);
   addwstr(L"wide chars\n");
   addnwstr(, 1);
   add_wch(WACS_DEGREE);
-  return 0;
+  codeset = nl_langinfo(CODESET);
+  return codeset != 0;
 }
 EOF
   IFS=:
diff --git a/qapi/ui.json b/qapi/ui.json
index c5d1d7f099..59e412139a 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1080,6 +1080,19 @@
  { 'enum': 'DisplayGLMode',
'data': [ 'off', 'on', 'core', 'es' ] }
 
+##
+# @DisplayCurses:
+#
+# Curses display options.
+#
+# @charset:   Font charset used by guest (default: CP437).
+#
+# Since: 4.0
+#
+##
+{ 'struct'  : 'DisplayCurses',
+  'data': { '*charset'   : 'str' } }
+
 ##
 # @DisplayType:
 #
@@ -1142,6 +1155,7 @@
 '*gl': 'DisplayGLMode' },
   'discriminator' : 'type',
   'data': { 'gtk': 'DisplayGTK',
+'curses' : 'DisplayCurses',
 'egl-headless'   : 'DisplayEGLHeadless'} }
 
 ##
diff --git a/qemu-options.hx b/qemu-options.hx
index 1cf9aac1fe..4bc4e736bb 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1216,7 +1216,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
 "[,window_close=on|off][,gl=on|core|es|off]\n"
 "-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n"
 "-display vnc=[,]\n"
-"-display curses\n"
+"-display curses[,charset=]\n"
 "-display none\n"
 "-display egl-headless[,rendernode=]"
 "select display type\n"
@@ -1248,6 +1248,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
diff --git a/ui/curses.c b/ui/curses.c
index 6861539b20..99eb026a9a 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -27,6 +27,10 @@
 #include 
 #include 
 #endif
+#include 
+#include 
+#include 
+#include 
 
 #include "qapi/error.h"
 #include "qemu-common.h"
@@ -54,25 +58,30 @@ static WINDOW *screenpad = NULL;
 static int width, height, gwidth, gheight, invalidate;
 static int px, py, sminx, sminy, smaxx, smaxy;
 
-static chtype vga_to_curses[256];
+static const char *font_charset = "CP437";
+static cchar_t vga_to_curses[256];
 
 static void curses_update(DisplayChangeListener *dcl,
   int x, int y, int w, int h)
 {
 console_ch_t *line;
-chtype curses_line[width];
+cchar_t curses_line[width];
 
 line = screen + y * width;
 for (h += y; y < h; y ++, line += width) {
 for (x = 0; x < width; x++) {
 chtype ch = line[x] & 0xff;
 chtype at = line[x] & ~0xff;
-if (vga_to_curses[ch]) {
-ch = vga_to_curses[ch];
+if (vga_to_curses[ch].chars[0]) {
+curses_line[x] = vga_to_curses[ch];
+} else {
+curses_line[x].chars[0] = ch;
+curses_line[x].chars[1] = 0;
+curses_line[x].attr = 0;
 }
-curses_line[x] = ch | at;
+curses_line[x].attr |= at;
 }
-mvwaddchnstr(screenpad, y, 0, curses_line, width);
+mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
 }
 
 pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
@@ -391,6 +400,254 @@ 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 *) 
+pwch = (char *) 
+sch = sizeof(ch);
+swch = sizeof(wch);
+
+if (iconv(conv, , , , ) == (size_t) -1) {
+