From c73270ccfb74cf2dddfe9526f68645630b43f9f5 Mon Sep 17 00:00:00 2001 From: Edgar Bonet Date: Mon, 13 Nov 2023 13:36:03 +0100 Subject: [PATCH 1/4] Support plotting with mutibyte characters The options -c, -e and -E only support ASCII characters. Make them support multibyte characters too, as box drawing and block elements can make nice plotting glyphs. This requires wide character support from the "ncursesw" version of the ncurses library. Only characters supported by the user's locale can be used, which includes the full Unicode repertoire on UTF-8 locales. As the wide character API of ncurses does not support ACS characters, the default plotting character on locales lacking mutibyte support is now "|" (U+007C vertical line). --- Makefile | 2 +- ttyplot.c | 47 ++++++++++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 72ba50d..7404f01 100755 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ DESTDIR ?= PREFIX ?= /usr/local MANPREFIX ?= $(PREFIX)/man CFLAGS += -Wall -Wextra -LDLIBS += `pkg-config --libs ncurses` +LDLIBS += `pkg-config --libs ncursesw` torture: LDLIBS = -lm all: ttyplot diff --git a/ttyplot.c b/ttyplot.c index 6ac2d3f..8aa5659 100644 --- a/ttyplot.c +++ b/ttyplot.c @@ -5,12 +5,14 @@ // Apache License 2.0 // +#define _XOPEN_SOURCE 500 // Get ncurses wchar_t support from SUSv2 (UNIX 98) #include #include #include #include #include #include +#include #include #include #include @@ -45,7 +47,7 @@ #endif sigset_t sigmsk; -chtype plotchar, max_errchar, min_errchar; +cchar_t plotchar, max_errchar, min_errchar; time_t t1,t2,td; struct tm *lt; double max=FLT_MIN; @@ -121,19 +123,26 @@ void draw_axes(int h, int ph, int pw, double max, double min, char *unit) { mvaddch(h-3, 2, T_LLCR); } -void draw_line(int x, int ph, int l1, int l2, chtype c1, chtype c2, chtype hce, chtype lce) { +void draw_line(int x, int ph, int l1, int l2, cchar_t *c1, cchar_t *c2, cchar_t *hce, cchar_t *lce) { + static cchar_t space = { + .attr = A_REVERSE, + .chars = {' ', '\0'} + }; + cchar_t c1r = *c1, c2r = *c2; + c1r.attr |= A_REVERSE; + c2r.attr |= A_REVERSE; if(l1 > l2) { - mvvline(ph+1-l1, x, c1, l1-l2 ); - mvvline(ph+1-l2, x, c2|A_REVERSE, l2 ); + mvvline_set(ph+1-l1, x, c1, l1-l2 ); + mvvline_set(ph+1-l2, x, &c2r, l2 ); } else if(l1 < l2) { - mvvline(ph+1-l2, x, (c2==hce || c2==lce) ? c2|A_REVERSE : ' '|A_REVERSE, l2-l1 ); - mvvline(ph+1-l1, x, c1|A_REVERSE, l1 ); + mvvline_set(ph+1-l2, x, (c2==hce || c2==lce) ? &c2r : &space, l2-l1 ); + mvvline_set(ph+1-l1, x, &c2r, l1 ); } else { - mvvline(ph+1-l2, x, c2|A_REVERSE, l2 ); + mvvline_set(ph+1-l2, x, &c2r, l2 ); } } -void plot_values(int ph, int pw, double *v1, double *v2, double max, double min, int n, chtype pc, chtype hce, chtype lce, double hm) { +void plot_values(int ph, int pw, double *v1, double *v2, double max, double min, int n, cchar_t *pc, cchar_t *hce, cchar_t *lce, double hm) { const int first_col=3; int i=(n+1)%pw; int x; @@ -199,7 +208,7 @@ void paint_plot(void) { asctime_r(lt, ls); mvaddstr(height-2, width-strlen(ls), ls); - mvvline(height-2, 5, plotchar|A_NORMAL, 1); + mvvline_set(height-2, 5, &plotchar, 1); mvprintw(height-2, 7, "last=%.1f min=%.1f max=%.1f avg=%.1f %s ", values1[n], min1, max1, avg1, unit); if(rate) printw(" interval=%llds", (long long int)td); @@ -209,7 +218,7 @@ void paint_plot(void) { mvprintw(height-1, 7, "last=%.1f min=%.1f max=%.1f avg=%.1f %s ", values2[n], min2, max2, avg2, unit); } - plot_values(plotheight, plotwidth, values1, values2, max, hardmin, n, plotchar, max_errchar, min_errchar, hardmax); + plot_values(plotheight, plotwidth, values1, values2, max, hardmin, n, &plotchar, &max_errchar, &min_errchar, hardmax); draw_axes(height, plotheight, plotwidth, max, hardmin, unit); @@ -251,9 +260,13 @@ int main(int argc, char *argv[]) { int show_ver; int show_usage; - plotchar=T_VLINE; - max_errchar='e'; - min_errchar='v'; + setlocale(LC_ALL, ""); + if (MB_CUR_MAX > 1) // if non-ASCII characters are supprted: + plotchar.chars[0]=0x2502; // U+2502 box drawings light vertical + else + plotchar.chars[0]='|'; // U+007C vertical line + max_errchar.chars[0]='e'; + min_errchar.chars[0]='v'; cached_opterr = opterr; opterr=0; @@ -303,16 +316,16 @@ int main(int argc, char *argv[]) { break; case '2': two=1; - plotchar='|'; + plotchar.chars[0]='|'; break; case 'c': - plotchar=optarg[0]; + mbtowc(&plotchar.chars[0], optarg, MB_CUR_MAX); break; case 'e': - max_errchar=optarg[0]; + mbtowc(&max_errchar.chars[0], optarg, MB_CUR_MAX); break; case 'E': - min_errchar=optarg[0]; + mbtowc(&min_errchar.chars[0], optarg, MB_CUR_MAX); break; case 's': softmax=atof(optarg); From 61c2cf5ccc4a951d5b6bf4f9caf7ee5f75c64d3e Mon Sep 17 00:00:00 2001 From: Edgar Bonet Date: Mon, 13 Nov 2023 17:10:19 +0100 Subject: [PATCH 2/4] Rely on pkg-config to find the proper ncurses.h On Ubuntu, /usr/include/ncursesw/ncurses.h is a symlink to ../ncurses.h. On Gentoo, however, /usr/include/ncurses.h and /usr/include/ncursesw/ncurses.h are different files, and only the latter defines the wide character functions: we need to give the compiler the proper -I flag. For the sake of portability, ask pgk-config for the correct compiler flags (including -I). Default to the flags used on Gentoo, which also work on Ubuntu. --- Makefile | 1 + ttyplot.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7404f01..0059265 100755 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ DESTDIR ?= PREFIX ?= /usr/local MANPREFIX ?= $(PREFIX)/man CFLAGS += -Wall -Wextra +CFLAGS += `pkg-config --cflags ncursesw` LDLIBS += `pkg-config --libs ncursesw` torture: LDLIBS = -lm diff --git a/ttyplot.c b/ttyplot.c index 8aa5659..6237d8a 100644 --- a/ttyplot.c +++ b/ttyplot.c @@ -5,7 +5,6 @@ // Apache License 2.0 // -#define _XOPEN_SOURCE 500 // Get ncurses wchar_t support from SUSv2 (UNIX 98) #include #include #include From 3c41c23631ab6959cd8c213de36be585846cf538 Mon Sep 17 00:00:00 2001 From: Edgar Bonet Date: Sun, 19 Nov 2023 18:40:06 +0100 Subject: [PATCH 3/4] Always use ASCII characters for the arrow tips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling multi-byte support changes the arrow tips from the ASCII characters '^'/'>' to the Unicode arrows '↑'/'→' (U+2191 Upwards arrow and U+2192 Rightwards arrow). The ASCII characters look better on the graph: use them unconditionally. --- ttyplot.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ttyplot.c b/ttyplot.c index 6237d8a..a4fb80a 100644 --- a/ttyplot.c +++ b/ttyplot.c @@ -31,17 +31,15 @@ #define VERSION_STR STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_PATCH) #endif +#define T_RARR '>' +#define T_UARR '^' #ifdef NOACS #define T_HLINE '-' #define T_VLINE '|' -#define T_RARR '>' -#define T_UARR '^' #define T_LLCR 'L' #else #define T_HLINE ACS_HLINE #define T_VLINE ACS_VLINE -#define T_RARR ACS_RARROW -#define T_UARR ACS_UARROW #define T_LLCR ACS_LLCORNER #endif From 98ae048536a66f4714e1f20bcd14804f674c72ad Mon Sep 17 00:00:00 2001 From: Edgar Bonet Date: Thu, 23 Nov 2023 13:22:57 +0100 Subject: [PATCH 4/4] Define _XOPEN_SOURCE_EXTENDED on macOS This macro enables the wide character API. Although it has been deprecated in favor of _XOPEN_SOURCE, macOS 14 ships a version of ncurses that does not understand the later. Furthermore, pkg-config fails to define it. --- ttyplot.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ttyplot.c b/ttyplot.c index a4fb80a..47b80ad 100644 --- a/ttyplot.c +++ b/ttyplot.c @@ -5,6 +5,11 @@ // Apache License 2.0 // +// This is needed on macOS to get the ncurses widechar API, and pkg-config fails to define it. +#ifdef __APPLE__ +#define _XOPEN_SOURCE_EXTENDED +#endif + #include #include #include