From a52969145a3d2111e7ab277383c36b393acf8eea Mon Sep 17 00:00:00 2001 From: Eduardo Alonso Date: Wed, 15 May 2024 10:51:19 +0200 Subject: [PATCH] monitor: Use UTF-8 instead of ACS (#77) Some terminal emulators such as PuTTY or the macOS terminal don't have full VT100 ACS support. For this reason, the RTS2 monitor borders and lines are not rendered properly. Some applications rely on using directly the equivalent UTF-8 character, making sure the terminal supports UTF-8 encoding. This way, the monitor appareance is consistent between terminal emulators. --- configure.ac | 22 ++++++++++---------- src/focusc/Makefile.am | 6 +++--- src/monitor/Makefile.am | 8 +++---- src/monitor/ndevicewindow.cpp | 8 +++---- src/monitor/nlayout.cpp | 2 ++ src/monitor/nlayout.h | 3 +++ src/monitor/nmenu.cpp | 4 ++-- src/monitor/nmenu.h | 9 ++++---- src/monitor/nmonitor.cpp | 11 ++++++++++ src/monitor/nmsgwindow.cpp | 14 ++++++------- src/monitor/nwindow.cpp | 16 +++++++------- src/monitor/nwindow.h | 2 +- src/monitor/utf8chars.h | 39 +++++++++++++++++++++++++++++++++++ src/plan/Makefile.am | 8 +++---- 14 files changed, 105 insertions(+), 47 deletions(-) create mode 100644 src/monitor/utf8chars.h diff --git a/configure.ac b/configure.ac index bc8c6f909..f4a667bf0 100644 --- a/configure.ac +++ b/configure.ac @@ -181,28 +181,28 @@ AC_ARG_WITH(ncurses, *) ncurses=${withval} ;; esac],[ncurses=yes]) -PKG_CHECK_MODULES([NCURSES],[ncurses],,[ +PKG_CHECK_MODULES([NCURSESW],[ncursesw],,[ AC_MSG_RESULT(not found) - AS_IF([test "x${ncurses}" !- "xyes"], [LDFLAGS="${LDFLAGS} -L${ncurses}"]) - AC_CHECK_LIB([ncurses], [mvwprintw], [ - NCURSES_LIBS="-lncurses" + AS_IF([test "x${ncursesw}" != "xyes"], [LDFLAGS="${LDFLAGS} -L${ncursesw}"]) + AC_CHECK_LIB([ncursesw], [mvwprintw], [ + NCURSESW_LIBS="-lncursesw" ],[ cat< getHeight () ? maxrow + 1 : getHeight ())); + mvwvline_set (getWriteWindow (), 0, valueBegins, &utf8Chars.getChar ("VLINE"), (maxrow > getHeight () ? maxrow + 1 : getHeight ())); if (isActive ()) wattron (window, A_REVERSE); - mvwaddch (window, 0, valueBegins + 1, ACS_TTEE); - mvwaddch (window, getHeight () - 1, valueBegins + 1, ACS_BTEE); + mvwadd_wch (window, 0, valueBegins + 1, &utf8Chars.getChar ("TTEE")); + mvwadd_wch (window, getHeight () - 1, valueBegins + 1, &utf8Chars.getChar ("BTEE")); // Scrollbar if (maxrow > 1) - mvwaddch (window, 1 + (getHeight () - 3)*getSelRow () / (maxrow - 1), getWidth()-1, ACS_DIAMOND); + mvwadd_wch (window, 1 + (getHeight () - 3)*getSelRow () / (maxrow - 1), getWidth()-1, &utf8Chars.getChar ("DIAMOND")); // Value list filtering if (filterMode == 1) diff --git a/src/monitor/nlayout.cpp b/src/monitor/nlayout.cpp index bf5b79498..137c3b10b 100644 --- a/src/monitor/nlayout.cpp +++ b/src/monitor/nlayout.cpp @@ -21,6 +21,8 @@ using namespace rts2ncurses; +Utf8Chars Layout::utf8Chars; + void LayoutBlock::resize (int x, int y, int w, int h) { int tmp_i; diff --git a/src/monitor/nlayout.h b/src/monitor/nlayout.h index e834d0c7a..84500ab64 100644 --- a/src/monitor/nlayout.h +++ b/src/monitor/nlayout.h @@ -19,6 +19,7 @@ #ifndef __RTS2_NLAYOUT__ #define __RTS2_NLAYOUT__ +#include "utf8chars.h" namespace rts2ncurses { @@ -28,6 +29,8 @@ namespace rts2ncurses */ class Layout { + protected: + static Utf8Chars utf8Chars; public: Layout () { diff --git a/src/monitor/nmenu.cpp b/src/monitor/nmenu.cpp index fc8e7f8b1..57b17a381 100644 --- a/src/monitor/nmenu.cpp +++ b/src/monitor/nmenu.cpp @@ -31,12 +31,12 @@ void NActionBool::draw (WINDOW *window, int y) { if (isActive) { - mvwaddch (window, y, 0, ACS_DIAMOND); + mvwadd_wch (window, y, 0, &utf8Chars.getChar ("DIAMOND")); wprintw (window, " %s ", text); } else { - mvwaddch (window, y, 0, ACS_BULLET); + mvwadd_wch (window, y, 0, &utf8Chars.getChar ("BULLET")); wprintw (window, " %s ", text_unactive); } } diff --git a/src/monitor/nmenu.h b/src/monitor/nmenu.h index 1595afdae..7283124da 100644 --- a/src/monitor/nmenu.h +++ b/src/monitor/nmenu.h @@ -33,7 +33,7 @@ namespace rts2ncurses class NAction { public: - NAction (const char *in_text, int in_code) + NAction (const char *in_text, int in_code, Utf8Chars& chars) : utf8Chars (chars) { text = in_text; code = in_code; @@ -58,6 +58,7 @@ class NAction protected: const char *text; + Utf8Chars& utf8Chars; private: int code; @@ -69,7 +70,7 @@ class NAction class NActionBool:public NAction { public: - NActionBool (const char *_text_active, const char *_text_unactive, int _code):NAction (_text_active, _code) { isActive = true; text_unactive = _text_unactive; } + NActionBool (const char *_text_active, const char *_text_unactive, int _code, Utf8Chars chars):NAction (_text_active, _code, chars) { isActive = true; text_unactive = _text_unactive; } virtual void draw (WINDOW *window, int y); void setActive (bool _active) { isActive = _active; } @@ -100,7 +101,7 @@ class NSubmenu:public NSelWindow */ NAction * createAction (const char *in_text, int in_code) { - NAction *ret = new NAction (in_text, in_code); + NAction *ret = new NAction (in_text, in_code, utf8Chars); addAction (ret); grow (strlen (in_text) + 4, 1); return ret; @@ -108,7 +109,7 @@ class NSubmenu:public NSelWindow NActionBool * createActionBool (const char *in_text, const char *_unactive, int in_code) { - NActionBool *ret = new NActionBool (in_text, _unactive, in_code); + NActionBool *ret = new NActionBool (in_text, _unactive, in_code, utf8Chars); addAction (ret); grow (strlen (in_text) + 5, 1); return ret; diff --git a/src/monitor/nmonitor.cpp b/src/monitor/nmonitor.cpp index 829c7ca4c..6168c004c 100644 --- a/src/monitor/nmonitor.cpp +++ b/src/monitor/nmonitor.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -509,6 +510,7 @@ int NMonitor::repaint () int NMonitor::init () { int ret; + const char *s; ret = rts2core::Client::init (); if (ret) return ret; @@ -516,6 +518,15 @@ int NMonitor::init () rts2core::Configuration::instance ()->loadFile (); // init ncurses + if (setlocale (LC_CTYPE, "en_US.UTF-8") == NULL && + setlocale (LC_CTYPE, "C.UTF-8") == NULL) { + if (setlocale (LC_CTYPE, "") == NULL) + std::cerr << "invalid LC_ALL, LC_CTYPE or LANG" << std::endl; + + s = nl_langinfo (CODESET); + if (strcasecmp (s, "UTF-8") != 0 && strcasecmp (s, "UTF8") != 0) + std::cerr << "need UTF-8 locale (LC_CTYPE) but have " << s << std::endl; + } cursesWin = initscr (); if (!cursesWin) { diff --git a/src/monitor/nmsgwindow.cpp b/src/monitor/nmsgwindow.cpp index e19866099..dbbcb487a 100644 --- a/src/monitor/nmsgwindow.cpp +++ b/src/monitor/nmsgwindow.cpp @@ -90,18 +90,18 @@ void NMsgWindow::draw () } wcolor_set (getWriteWindow (), CLR_DEFAULT, NULL); - mvwvline (getWriteWindow (), 0, 12, ACS_VLINE, (maxrow > getHeight ()? maxrow : getHeight ())); - mvwvline (getWriteWindow (), 0, 14, ACS_VLINE, (maxrow > getHeight ()? maxrow : getHeight ())); + mvwvline_set (getWriteWindow (), 0, 12, &utf8Chars.getChar ("VLINE"), (maxrow > getHeight ()? maxrow : getHeight ())); + mvwvline_set (getWriteWindow (), 0, 14, &utf8Chars.getChar ("VLINE"), (maxrow > getHeight ()? maxrow : getHeight ())); if (isActive ()) wattron (window, A_REVERSE); - mvwaddch (window, 0, 15, ACS_TTEE); - mvwaddch (window, getHeight () - 1, 15, ACS_BTEE); - mvwaddch (window, 0, 13, ACS_TTEE); - mvwaddch (window, getHeight () - 1, 13, ACS_BTEE); + mvwadd_wch (window, 0, 15, &utf8Chars.getChar ("TTEE")); + mvwadd_wch (window, getHeight () - 1, 15, &utf8Chars.getChar ("BTEE")); + mvwadd_wch (window, 0, 13, &utf8Chars.getChar ("TTEE")); + mvwadd_wch (window, getHeight () - 1, 13, &utf8Chars.getChar ("BTEE")); // Scrollbar if (maxrow > 1) - mvwaddch (window, 1 + (getHeight () - 3)*getSelRow () / (maxrow - 1), getWidth()-1, ACS_DIAMOND); + mvwadd_wch (window, 1 + (getHeight () - 3)*getSelRow () / (maxrow - 1), getWidth()-1, &utf8Chars.getChar ("DIAMOND")); wattroff (window, A_REVERSE); winrefresh (); diff --git a/src/monitor/nwindow.cpp b/src/monitor/nwindow.cpp index c3ebac985..304a9aa3d 100644 --- a/src/monitor/nwindow.cpp +++ b/src/monitor/nwindow.cpp @@ -57,14 +57,16 @@ void NWindow::draw () if (haveBox ()) { if (isActive ()) - wborder (window, A_REVERSE | ACS_VLINE, A_REVERSE | ACS_VLINE, - A_REVERSE | ACS_HLINE, A_REVERSE | ACS_HLINE, - A_REVERSE | ACS_ULCORNER, - A_REVERSE | ACS_URCORNER, - A_REVERSE | ACS_LLCORNER, - A_REVERSE | ACS_LRCORNER); + { + wattron (window, A_REVERSE); + wborder_set (window, &utf8Chars.getChar ("VLINE"), &utf8Chars.getChar ("VLINE"), + &utf8Chars.getChar ("HLINE"), &utf8Chars.getChar ("HLINE"), + &utf8Chars.getChar ("ULCORNER"), &utf8Chars.getChar ("URCORNER"), + &utf8Chars.getChar ("LLCORNER"), &utf8Chars.getChar ("LRCORNER")); + wattroff (window, A_REVERSE); + } else - box (window, 0, 0); + box_set (window, &utf8Chars.getChar ("VLINE"), &utf8Chars.getChar ("HLINE")); } if (!title.empty ()) diff --git a/src/monitor/nwindow.h b/src/monitor/nwindow.h index c9e0c725b..22323dbdd 100644 --- a/src/monitor/nwindow.h +++ b/src/monitor/nwindow.h @@ -26,7 +26,7 @@ #ifdef RTS2_HAVE_CURSES_H #include #elif defined(RTS2_HAVE_NCURSES_CURSES_H) -#include +#include #endif #include "nlayout.h" diff --git a/src/monitor/utf8chars.h b/src/monitor/utf8chars.h new file mode 100644 index 000000000..45e9d0070 --- /dev/null +++ b/src/monitor/utf8chars.h @@ -0,0 +1,39 @@ +#include +#include +#include + +namespace rts2ncurses { + +class Utf8Chars { + private: + std::unordered_map charMap; + + public: + Utf8Chars() { + initUtf8(); + } + + void initUtf8() { + setChar("HLINE", L"\u2500", A_NORMAL); + setChar("VLINE", L"\u2502", A_NORMAL); + setChar("ULCORNER", L"\u250c", A_NORMAL); + setChar("URCORNER", L"\u2510", A_NORMAL); + setChar("LLCORNER", L"\u2514", A_NORMAL); + setChar("LRCORNER", L"\u2518", A_NORMAL); + setChar("BULLET", L"\u2022", A_NORMAL); + setChar("DIAMOND", L"\u2666", A_NORMAL); + setChar("BTEE", L"\u2534", A_NORMAL); + setChar("TTEE", L"\u252c", A_NORMAL); + } + + void setChar(const std::string& name, const wchar_t* wch, attr_t attr) { + cchar_t ch; + setcchar(&ch, wch, attr, 0, NULL); + charMap[name] = ch; + } + + const cchar_t& getChar(const std::string& name) { + return charMap.at(name); + } +}; +} diff --git a/src/plan/Makefile.am b/src/plan/Makefile.am index 860e004c6..2f3859bfb 100644 --- a/src/plan/Makefile.am +++ b/src/plan/Makefile.am @@ -8,7 +8,7 @@ CLEANFILES = selector.cpp rts2devcliphot.cpp PLAN_STDLIBS = @LIBPG_CFLAGS@ @NOVA_CFLAGS@ @CFITSIO_CFLAGS@ @MAGIC_CFLAGS@ @LIBXML_CFLAGS@ rts2_scriptexec_SOURCES = scriptexec.cpp -rts2_scriptexec_CXXFLAGS = @NOVA_CFLAGS@ @CFITSIO_CFLAGS@ @MAGIC_CFLAGS@ @LIBXML_CFLAGS@ @NCURSES_CFLAGS@ -I../../include +rts2_scriptexec_CXXFLAGS = @NOVA_CFLAGS@ @CFITSIO_CFLAGS@ @MAGIC_CFLAGS@ @LIBXML_CFLAGS@ @NCURSESW_CFLAGS@ -I../../include # LDFLAGS are defined in PGSQL/nonPGSQL @@ -20,7 +20,7 @@ if PGSQL PG_LDADD = -L../../lib/rts2script -lrts2script -L../../lib/rts2db -lrts2db -L../../lib/pluto -lpluto -L../../lib/xmlrpc++ -lrts2xmlrpc -L../../lib/rts2fits -lrts2imagedb -L../../lib/rts2 -lrts2 @LIBXML_LIBS@ @LIBPG_LIBS@ @LIB_ECPG@ @LIB_CRYPT@ @LIB_NOVA@ @CFITSIO_LIBS@ @LIB_M@ @MAGIC_LIBS@ rts2_scriptexec_CXXFLAGS += @LIBPG_CFLAGS@ -I../../include -rts2_scriptexec_LDADD = ${PG_LDADD} @NCURSES_LIBS@ +rts2_scriptexec_LDADD = ${PG_LDADD} @NCURSESW_LIBS@ rts2_scriptor_CXXFLAGS += @LIBPG_CFLAGS@ -I../../include rts2_scriptor_LDADD = ${PG_LDADD} @@ -42,7 +42,7 @@ rts2_selector_LDADD = ${PG_LDADD} nodist_rts2_seltest_SOURCES = selector.cpp rts2_seltest_SOURCES = seltest.cpp -rts2_seltest_CXXFLAGS = @NCURSES_CFLAGS@ ${PLAN_STDLIBS} -I../../include +rts2_seltest_CXXFLAGS = @NCURSESW_CFLAGS@ ${PLAN_STDLIBS} -I../../include rts2_seltest_LDADD = ${PG_LDADD} rts2_marchive_SOURCES = marchive.cpp @@ -54,7 +54,7 @@ rts2_marchive_LDADD = ${PG_LDADD} else -rts2_scriptexec_LDFLAGS = -L../../lib/rts2script -lrts2script -L../../lib/rts2fits -lrts2image -L../../lib/rts2 -lrts2 @LIBXML_LIBS@ @LIB_NOVA@ @CFITSIO_LIBS@ @LIB_M@ @MAGIC_LIBS@ @NCURSES_LIBS@ +rts2_scriptexec_LDFLAGS = -L../../lib/rts2script -lrts2script -L../../lib/rts2fits -lrts2image -L../../lib/rts2 -lrts2 @LIBXML_LIBS@ @LIB_NOVA@ @CFITSIO_LIBS@ @LIB_M@ @MAGIC_LIBS@ @NCURSESW_LIBS@ rts2_scriptor_LDADD = -L../../lib/rts2script -lrts2script -L../../lib/rts2fits -lrts2image -L../../lib/rts2 -lrts2 @LIBXML_LIBS@ @LIB_NOVA@ @CFITSIO_LIBS@ @LIB_M@ @MAGIC_LIBS@ rts2_imgproc_SOURCES = imgproc.cpp