diff mbox

[3/5] curses: use wide output functions

Message ID 20161015195308.20473-4-samuel.thibault@ens-lyon.org (mailing list archive)
State New, archived
Headers show

Commit Message

Samuel Thibault Oct. 15, 2016, 7:53 p.m. UTC
This makes use of cchar_t instead of chtype when using ncursesw, which
allows to store a wide char as well as the WACS values.

This also allows to complete the printable glyphs list beyond ascii and
the ACS values.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
---
 configure            |   5 +-
 hw/display/vga.c     |   4 +-
 include/ui/console.h |  16 ++-
 ui/curses.c          | 321 ++++++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 294 insertions(+), 52 deletions(-)
diff mbox

Patch

diff --git a/configure b/configure
index 29649d9..ebe0599 100755
--- a/configure
+++ b/configure
@@ -2901,14 +2901,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=:
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 2a88b3c..ce225c9 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -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;
                 }
diff --git a/include/ui/console.h b/include/ui/console.h
index d9c13d2..94e3133 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -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 {
diff --git a/ui/curses.c b/ui/curses.c
index 3599295..cd8ed5f 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -28,6 +28,9 @@ 
 #include <sys/ioctl.h>
 #include <termios.h>
 #endif
+#include <locale.h>
+#include <wchar.h>
+#include <langinfo.h>
 
 #include "qemu-common.h"
 #include "ui/console.h"
@@ -43,16 +46,17 @@  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 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();
@@ -358,45 +362,275 @@  static void curses_setup(void)
 
     /*
      * 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;
+
+    vga_to_curses['\0'].chars[0] = L' ';
+    vga_to_curses[0x01].chars[0] = L'\u263a';
+    vga_to_curses[0x02].chars[0] = L'\u263b';
+    vga_to_curses[0x03].chars[0] = L'\u2665';
+    vga_to_curses[0x04].chars[0] = L'\u2666';
+    vga_to_curses[0x05].chars[0] = L'\u2663';
+    vga_to_curses[0x06].chars[0] = L'\u2660';
+    vga_to_curses[0x07].chars[0] = L'\u2022';
+    vga_to_curses[0x08].chars[0] = L'\u25d8';
+    vga_to_curses[0x09].chars[0] = L'\u25cb';
+    vga_to_curses[0x0a].chars[0] = L'\u25d9';
+    vga_to_curses[0x0b].chars[0] = L'\u2642';
+    vga_to_curses[0x0c].chars[0] = L'\u2640';
+    vga_to_curses[0x0d].chars[0] = L'\u266a';
+    vga_to_curses[0x0e].chars[0] = L'\u266b';
+    vga_to_curses[0x0f].chars[0] = L'\u263c';
+    vga_to_curses[0x10].chars[0] = L'\u25ba';
+    vga_to_curses[0x11].chars[0] = L'\u25c4';
+    vga_to_curses[0x12].chars[0] = L'\u2195';
+    vga_to_curses[0x13].chars[0] = L'\u203c';
+    vga_to_curses[0x14].chars[0] = L'\u00b6';
+    vga_to_curses[0x15].chars[0] = L'\u00a7';
+    vga_to_curses[0x16].chars[0] = L'\u25ac';
+    vga_to_curses[0x17].chars[0] = L'\u21a8';
+    vga_to_curses[0x18].chars[0] = L'\u2191';
+    vga_to_curses[0x19].chars[0] = L'\u2193';
+    vga_to_curses[0x1a].chars[0] = L'\u2192';
+    vga_to_curses[0x1b].chars[0] = L'\u2190';
+    vga_to_curses[0x1c].chars[0] = L'\u221f';
+    vga_to_curses[0x1d].chars[0] = L'\u2194';
+    vga_to_curses[0x1e].chars[0] = L'\u25b2';
+    vga_to_curses[0x1f].chars[0] = L'\u25bc';
+
+    {
+        /* Hardcode CP437 to unicode */
+        vga_to_curses[0x80].chars[0] = L'\u00C7';
+        vga_to_curses[0x81].chars[0] = L'\u00FC';
+        vga_to_curses[0x82].chars[0] = L'\u00E9';
+        vga_to_curses[0x83].chars[0] = L'\u00E2';
+        vga_to_curses[0x84].chars[0] = L'\u00E4';
+        vga_to_curses[0x85].chars[0] = L'\u00E0';
+        vga_to_curses[0x86].chars[0] = L'\u00E5';
+        vga_to_curses[0x87].chars[0] = L'\u00E7';
+        vga_to_curses[0x88].chars[0] = L'\u00EA';
+        vga_to_curses[0x89].chars[0] = L'\u00EB';
+        vga_to_curses[0x8a].chars[0] = L'\u00E8';
+        vga_to_curses[0x8b].chars[0] = L'\u00EF';
+        vga_to_curses[0x8c].chars[0] = L'\u00EE';
+        vga_to_curses[0x8d].chars[0] = L'\u00EC';
+        vga_to_curses[0x8e].chars[0] = L'\u00C4';
+        vga_to_curses[0x8f].chars[0] = L'\u00C5';
+        vga_to_curses[0x90].chars[0] = L'\u00C9';
+        vga_to_curses[0x91].chars[0] = L'\u00E6';
+        vga_to_curses[0x92].chars[0] = L'\u00C6';
+        vga_to_curses[0x93].chars[0] = L'\u00F4';
+        vga_to_curses[0x94].chars[0] = L'\u00F6';
+        vga_to_curses[0x95].chars[0] = L'\u00F2';
+        vga_to_curses[0x96].chars[0] = L'\u00FB';
+        vga_to_curses[0x97].chars[0] = L'\u00F9';
+        vga_to_curses[0x98].chars[0] = L'\u00FF';
+        vga_to_curses[0x99].chars[0] = L'\u00D6';
+        vga_to_curses[0x9a].chars[0] = L'\u00DC';
+        vga_to_curses[0x9b].chars[0] = L'\u00A2';
+        vga_to_curses[0x9c].chars[0] = L'\u00A3';
+        vga_to_curses[0x9d].chars[0] = L'\u00A5';
+        vga_to_curses[0x9e].chars[0] = L'\u20A7';
+        vga_to_curses[0x9f].chars[0] = L'\u0192';
+        vga_to_curses[0xa0].chars[0] = L'\u00E1';
+        vga_to_curses[0xa1].chars[0] = L'\u00ED';
+        vga_to_curses[0xa2].chars[0] = L'\u00F3';
+        vga_to_curses[0xa3].chars[0] = L'\u00FA';
+        vga_to_curses[0xa4].chars[0] = L'\u00F1';
+        vga_to_curses[0xa5].chars[0] = L'\u00D1';
+        vga_to_curses[0xa6].chars[0] = L'\u00AA';
+        vga_to_curses[0xa7].chars[0] = L'\u00BA';
+        vga_to_curses[0xa8].chars[0] = L'\u00BF';
+        vga_to_curses[0xa9].chars[0] = L'\u2310';
+        vga_to_curses[0xaa].chars[0] = L'\u00AC';
+        vga_to_curses[0xab].chars[0] = L'\u00BD';
+        vga_to_curses[0xac].chars[0] = L'\u00BC';
+        vga_to_curses[0xad].chars[0] = L'\u00A1';
+        vga_to_curses[0xae].chars[0] = L'\u00AB';
+        vga_to_curses[0xaf].chars[0] = L'\u00BB';
+        vga_to_curses[0xb0].chars[0] = L'\u2591';
+        vga_to_curses[0xb1].chars[0] = L'\u2592';
+        vga_to_curses[0xb2].chars[0] = L'\u2593';
+        vga_to_curses[0xb3].chars[0] = L'\u2502';
+        vga_to_curses[0xb4].chars[0] = L'\u2524';
+        vga_to_curses[0xb5].chars[0] = L'\u2561';
+        vga_to_curses[0xb6].chars[0] = L'\u2562';
+        vga_to_curses[0xb7].chars[0] = L'\u2556';
+        vga_to_curses[0xb8].chars[0] = L'\u2555';
+        vga_to_curses[0xb9].chars[0] = L'\u2563';
+        vga_to_curses[0xba].chars[0] = L'\u2551';
+        vga_to_curses[0xbb].chars[0] = L'\u2557';
+        vga_to_curses[0xbc].chars[0] = L'\u255D';
+        vga_to_curses[0xbd].chars[0] = L'\u255C';
+        vga_to_curses[0xbe].chars[0] = L'\u255B';
+        vga_to_curses[0xbf].chars[0] = L'\u2510';
+        vga_to_curses[0xc0].chars[0] = L'\u2514';
+        vga_to_curses[0xc1].chars[0] = L'\u2534';
+        vga_to_curses[0xc2].chars[0] = L'\u252C';
+        vga_to_curses[0xc3].chars[0] = L'\u251C';
+        vga_to_curses[0xc4].chars[0] = L'\u2500';
+        vga_to_curses[0xc5].chars[0] = L'\u253C';
+        vga_to_curses[0xc6].chars[0] = L'\u255E';
+        vga_to_curses[0xc7].chars[0] = L'\u255F';
+        vga_to_curses[0xc8].chars[0] = L'\u255A';
+        vga_to_curses[0xc9].chars[0] = L'\u2554';
+        vga_to_curses[0xca].chars[0] = L'\u2569';
+        vga_to_curses[0xcb].chars[0] = L'\u2566';
+        vga_to_curses[0xcc].chars[0] = L'\u2560';
+        vga_to_curses[0xcd].chars[0] = L'\u2550';
+        vga_to_curses[0xce].chars[0] = L'\u256C';
+        vga_to_curses[0xcf].chars[0] = L'\u2567';
+        vga_to_curses[0xd0].chars[0] = L'\u2568';
+        vga_to_curses[0xd1].chars[0] = L'\u2564';
+        vga_to_curses[0xd2].chars[0] = L'\u2565';
+        vga_to_curses[0xd3].chars[0] = L'\u2559';
+        vga_to_curses[0xd4].chars[0] = L'\u2558';
+        vga_to_curses[0xd5].chars[0] = L'\u2552';
+        vga_to_curses[0xd6].chars[0] = L'\u2553';
+        vga_to_curses[0xd7].chars[0] = L'\u256B';
+        vga_to_curses[0xd8].chars[0] = L'\u256A';
+        vga_to_curses[0xd9].chars[0] = L'\u2518';
+        vga_to_curses[0xda].chars[0] = L'\u250C';
+        vga_to_curses[0xdb].chars[0] = L'\u2588';
+        vga_to_curses[0xdc].chars[0] = L'\u2584';
+        vga_to_curses[0xdd].chars[0] = L'\u258C';
+        vga_to_curses[0xde].chars[0] = L'\u2590';
+        vga_to_curses[0xdf].chars[0] = L'\u2580';
+        vga_to_curses[0xe0].chars[0] = L'\u03B1';
+        vga_to_curses[0xe1].chars[0] = L'\u00DF';
+        vga_to_curses[0xe2].chars[0] = L'\u0393';
+        vga_to_curses[0xe3].chars[0] = L'\u03C0';
+        vga_to_curses[0xe4].chars[0] = L'\u03A3';
+        vga_to_curses[0xe5].chars[0] = L'\u03C3';
+        vga_to_curses[0xe6].chars[0] = L'\u00B5';
+        vga_to_curses[0xe7].chars[0] = L'\u03C4';
+        vga_to_curses[0xe8].chars[0] = L'\u03A6';
+        vga_to_curses[0xe9].chars[0] = L'\u0398';
+        vga_to_curses[0xea].chars[0] = L'\u03A9';
+        vga_to_curses[0xeb].chars[0] = L'\u03B4';
+        vga_to_curses[0xec].chars[0] = L'\u221E';
+        vga_to_curses[0xed].chars[0] = L'\u03C6';
+        vga_to_curses[0xee].chars[0] = L'\u03B5';
+        vga_to_curses[0xef].chars[0] = L'\u2229';
+        vga_to_curses[0xf0].chars[0] = L'\u2261';
+        vga_to_curses[0xf1].chars[0] = L'\u00B1';
+        vga_to_curses[0xf2].chars[0] = L'\u2265';
+        vga_to_curses[0xf3].chars[0] = L'\u2264';
+        vga_to_curses[0xf4].chars[0] = L'\u2320';
+        vga_to_curses[0xf5].chars[0] = L'\u2321';
+        vga_to_curses[0xf6].chars[0] = L'\u00F7';
+        vga_to_curses[0xf7].chars[0] = L'\u2248';
+        vga_to_curses[0xf8].chars[0] = L'\u00B0';
+        vga_to_curses[0xf9].chars[0] = L'\u2219';
+        vga_to_curses[0xfa].chars[0] = L'\u00B7';
+        vga_to_curses[0xfb].chars[0] = L'\u221A';
+        vga_to_curses[0xfc].chars[0] = L'\u207F';
+        vga_to_curses[0xfd].chars[0] = L'\u00B2';
+        vga_to_curses[0xfe].chars[0] = L'\u25A0';
+        vga_to_curses[0xff].chars[0] = L'\u00A0';
+    }
+    if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
+        /* Non-Unicode capable, use termcap equivalents for those available */
+        for (i = 0; i <= 0xff; i++) {
+            switch (vga_to_curses[i].chars[0]) {
+            case L'\u00a3':
+                vga_to_curses[i] = *WACS_STERLING;
+                break;
+            case L'\u2591':
+                vga_to_curses[i] = *WACS_BOARD;
+                break;
+            case L'\u2592':
+                vga_to_curses[i] = *WACS_CKBOARD;
+                break;
+            case L'\u2502':
+                vga_to_curses[i] = *WACS_VLINE;
+                break;
+            case L'\u2524':
+                vga_to_curses[i] = *WACS_RTEE;
+                break;
+            case L'\u2510':
+                vga_to_curses[i] = *WACS_URCORNER;
+                break;
+            case L'\u2514':
+                vga_to_curses[i] = *WACS_LLCORNER;
+                break;
+            case L'\u2534':
+                vga_to_curses[i] = *WACS_BTEE;
+                break;
+            case L'\u252c':
+                vga_to_curses[i] = *WACS_TTEE;
+                break;
+            case L'\u251c':
+                vga_to_curses[i] = *WACS_LTEE;
+                break;
+            case L'\u2500':
+                vga_to_curses[i] = *WACS_HLINE;
+                break;
+            case L'\u253c':
+                vga_to_curses[i] = *WACS_PLUS;
+                break;
+            case L'\u256c':
+                vga_to_curses[i] = *WACS_LANTERN;
+                break;
+            case L'\u256a':
+                vga_to_curses[i] = *WACS_NEQUAL;
+                break;
+            case L'\u2518':
+                vga_to_curses[i] = *WACS_LRCORNER;
+                break;
+            case L'\u250c':
+                vga_to_curses[i] = *WACS_ULCORNER;
+                break;
+            case L'\u2588':
+                vga_to_curses[i] = *WACS_BLOCK;
+                break;
+            case L'\u03c0':
+                vga_to_curses[i] = *WACS_PI;
+                break;
+            case L'\u00b1':
+                vga_to_curses[i] = *WACS_PLMINUS;
+                break;
+            case L'\u2265':
+                vga_to_curses[i] = *WACS_GEQUAL;
+                break;
+            case L'\u2264':
+                vga_to_curses[i] = *WACS_LEQUAL;
+                break;
+            case L'\u00b0':
+                vga_to_curses[i] = *WACS_DEGREE;
+                break;
+            case L'\u25a0':
+                vga_to_curses[i] = *WACS_BULLET;
+                break;
+            case L'\u2666':
+                vga_to_curses[i] = *WACS_DIAMOND;
+                break;
+            case L'\u2192':
+                vga_to_curses[i] = *WACS_RARROW;
+                break;
+            case L'\u2190':
+                vga_to_curses[i] = *WACS_LARROW;
+                break;
+            case L'\u2191':
+                vga_to_curses[i] = *WACS_UARROW;
+                break;
+            case L'\u2193':
+                vga_to_curses[i] = *WACS_DARROW;
+                break;
+            case L'\u23ba':
+                vga_to_curses[i] = *WACS_S1;
+                break;
+            case L'\u23bb':
+                vga_to_curses[i] = *WACS_S3;
+                break;
+            case L'\u23bc':
+                vga_to_curses[i] = *WACS_S7;
+                break;
+            case L'\u23bd':
+                vga_to_curses[i] = *WACS_S9;
+                break;
+            }
+        }
+    }
 }
 
 static void curses_keyboard_setup(void)
@@ -430,6 +664,7 @@  void curses_display_init(DisplayState *ds, int full_screen)
     }
 #endif
 
+    setlocale(LC_CTYPE, "");
     curses_setup();
     curses_keyboard_setup();
     atexit(curses_atexit);