From: Bart Trojanowski Date: Tue, 8 Apr 2025 03:11:09 +0000 (-0400) Subject: add braille graph support with --displaymode 3 X-Git-Tag: v0.96~5^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=25fc5274ef49465cf0c3236ebf2445ddf49f4647;p=thirdparty%2Fmtr.git add braille graph support with --displaymode 3 --- diff --git a/configure.ac b/configure.ac index cc07d81..161c454 100644 --- a/configure.ac +++ b/configure.ac @@ -112,6 +112,20 @@ AS_IF([test "x$with_jansson" = "xyes"], [with_jansson=no]) ]) +# Find ncursesw +AC_ARG_WITH([ncursesw], + [AS_HELP_STRING([--without-ncursesw], [Build without the ncursesw interface])], + [], [with_ncursesw=yes]) +AS_IF([test "x$with_ncursesw" = "xyes"], + + # Prefer ncursesw, if available + [AC_SEARCH_LIBS( + [wprintw], [ncursesw], + [AC_DEFINE([HAVE_CURSESW], [1], [Define if a ncursesw library available])], + [with_ncursesw=no]) +]) +AM_CONDITIONAL([WITH_CURSESW], [test "x$with_ncursesw" = xyes]) + # Find ncurses AC_ARG_WITH([ncurses], [AS_HELP_STRING([--without-ncurses], [Build without the ncurses interface])], @@ -121,7 +135,7 @@ AS_IF([test "x$with_ncurses" = "xyes"], # Prefer ncurses over curses, if both are available. # (On Solaris 11.3, ncurses builds and links for us, but curses does not.) [AC_SEARCH_LIBS( - [initscr], [ncurses curses], + [initscr], [ncursesw ncurses curses], [AC_DEFINE([HAVE_CURSES], [1], [Define if a curses library available])], [with_ncurses=no]) ]) @@ -153,6 +167,15 @@ AS_IF([test "x$WANTS_IPV6" = "xyes"], [ USES_IPV6=yes ]) +AC_ARG_ENABLE([braille], + [AS_HELP_STRING([--disable-braille], [Do not enable barille character graphs])], + [WANTS_BRAILLE=$enableval], [WANTS_BRAILLE=yes]) + +AS_IF([test "x$WANTS_BRAILLE" = "xyes"], [ + AC_DEFINE([ENABLE_BRAILLE], [1], [Define to enable IPv6]) + USES_BRAILLE=yes +]) + AC_CHECK_FUNC([socket], [], [AC_CHECK_LIB([socket], [socket], [], [AC_MSG_ERROR([No socket library found])])]) @@ -266,15 +289,17 @@ AC_ARG_ENABLE([bash-completion], AM_CONDITIONAL([BUILD_BASH_COMPLETION], [test "x$enable_bash_completion" = xyes]) echo "build options:" echo "--------------" -echo "libasan :$with_libasan" -echo "ipv6 :$USES_IPV6" -echo "ipinfo :$with_ipinfo" -echo "ncurses :$with_ncurses" -echo "gtk :$with_gtk" -echo "jansson :$with_jansson" -echo "cap :$have_cap" -echo "libs :$LIBS" -echo "cflags :$CFLAGS" +echo "libasan :$with_libasan" +echo "ipv6 :$USES_IPV6" +echo "braille :$USES_BRAILLE" +echo "ipinfo :$with_ipinfo" +echo "ncursesw :$with_ncursesw" +echo "ncurses :$with_ncurses" +echo "gtk :$with_gtk" +echo "jansson :$with_jansson" +echo "cap :$have_cap" +echo "libs :$LIBS" +echo "cflags :$CFLAGS" echo "--------------" # Prepare config.h, Makefile, and output them. AC_CONFIG_HEADERS([config.h]) diff --git a/ui/curses.c b/ui/curses.c index 7fc201d..ab5e92e 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -20,6 +20,8 @@ #include "mtr.h" +#include +#include #include #include @@ -576,22 +578,178 @@ static void mtr_curses_init( block_map[NUM_FACTORS - 1] = '>'; } -static void mtr_print_scaled( +static int ms_to_factor( int ms) { int i; for (i = 0; i < NUM_FACTORS; i++) { - if (ms <= scale[i]) { - attrset(block_col[i + 1]); - printw("%c", block_map[i]); - attrset(A_NORMAL); - return; - } + if (ms <= scale[i]) + return i; + } + + return NUM_FACTORS; +} + +static void mtr_print_scaled( + int ms) +{ + int f = ms_to_factor(ms); + + if ((unsigned)f < NUM_FACTORS) { + attrset(block_col[f + 1]); + printw("%c", block_map[f]); + attrset(A_NORMAL); + return; } printw(">"); } +#ifdef WITH_BRAILLE_DISPLAY +static int current_host_range_low_ms = 1000000; +static int current_host_range_high_ms = -1; + +static void compute_current_host_range(const int *ms_data, size_t length) +{ + current_host_range_low_ms = 1000000; + current_host_range_high_ms = -1; + + for (int i=0; i ms) + current_host_range_low_ms = ms; + if (current_host_range_high_ms < ms) + current_host_range_high_ms = ms; + } +} + +static const int scale_ms_to_braille_factor(int ms) +{ + if (ms <= 0) + return 0; + + int ms_range = current_host_range_high_ms - current_host_range_low_ms; + if (ms_range < 1) + return 0; + + return (ms - current_host_range_low_ms) * 4 / ms_range; +} + +static const wchar_t *braille_char_lookup( + int ms, + const wchar_t *braille_set[5]) +{ + if (ms < 0) + return L"𜸲"; // this is an error in decoding + + int i = scale_ms_to_braille_factor(ms); + if ((unsigned)i >= 4) + return L"🮐"; // this is the max + + return braille_set[i]; +} + +// handle if left is not provided, but right is +static const wchar_t *braille_char_left( + int left_ms) +{ + static const wchar_t *braille_left_lookup[5] = { + L"⡀", L"⡄", L"⡆", L"⡇", + }; + + return braille_char_lookup(left_ms, braille_left_lookup); +} + + +// handle if right is not provided, but left is +static const wchar_t *braille_char_right( + int right_ms) +{ + static const wchar_t *braille_right_lookup[5] = { + L"⢀", L"⢠", L"⢰", L"⢸", + }; + + return braille_char_lookup(right_ms, braille_right_lookup); +} + +// handle both left and right being provided +static const wchar_t *braille_char_double( + int left_ms, + int right_ms) +{ + static const wchar_t *braille_double_lookup[5][5] = { + { L"⣀", L"⣠", L"⣰", L"⣸", }, + { L"⣄", L"⣤", L"⣴", L"⣼", }, + { L"⣆", L"⣦", L"⣶", L"⣾", }, + { L"⣇", L"⣧", L"⣷", L"⣿", } + }; + + int left_i = scale_ms_to_braille_factor(left_ms); + if ((unsigned)left_i >= 4) + return L"🮐"; // this is the max + + return braille_char_lookup(right_ms, braille_double_lookup[left_i]); +} + +static void mtr_print_braille( + int left_ms, + int right_ms) +{ + int ms_max = left_ms > right_ms ? left_ms : right_ms; + int f = ms_to_factor(ms_max); + f = ((unsigned)f < NUM_FACTORS) ? f : NUM_FACTORS - 1; + + const wchar_t *wstr; + if (left_ms > 0 && right_ms > 0) + wstr = braille_char_double(left_ms, right_ms); + else if (left_ms > 0) + wstr = braille_char_left(left_ms); + else if (right_ms > 0) + wstr = braille_char_right(right_ms); + else + wstr = L"▁"; + + attrset(block_col[f + 1]); + printw("%ls", wstr); + attrset(A_NORMAL); +} + +static void mtr_fill_graph_braille( + struct mtr_ctl *ctl, + int at, + int cols) +{ + const int *saved; + int i; + + saved = net_saved_pings(at); + + compute_current_host_range(saved, SAVED_PINGS); + + // we can pack twice as many entries into a braille line + + cols = cols * 2; + cols = cols <= SAVED_PINGS ? cols : SAVED_PINGS; + + for (i = SAVED_PINGS - cols; i < SAVED_PINGS; i+=2) { + int a = saved[i]; + int b = (i+1 < SAVED_PINGS) ? saved[i+1] : 0; + + if (a == -2 && b == -2) { + printw(" "); + } else if (a == -1 || b == -1) { + attrset(block_col[0]); + printw("%c", '?'); + attrset(A_NORMAL); + } else { + mtr_print_braille(a, b); + } + } + +} +#endif static void mtr_fill_graph( struct mtr_ctl *ctl, @@ -671,7 +829,14 @@ static void mtr_curses_graph( move(y, startstat); printw(" "); - mtr_fill_graph(ctl, at, cols); +#ifdef WITH_BRAILLE_DISPLAY + if (ctl->display_mode == DisplayModeBraille) { + mtr_fill_graph_braille(ctl, at, cols); + } else +#endif + { + mtr_fill_graph(ctl, at, cols); + } printw("\n"); } } @@ -810,6 +975,11 @@ void mtr_curses_open( int bg_col = 0; int i; +#ifdef WITH_BRAILLE_DISPLAY + // initialize all locale variables, before ncurses starts + setlocale(LC_ALL, ""); +#endif + initscr(); raw(); noecho(); diff --git a/ui/display.h b/ui/display.h index b4eca77..ad74e63 100644 --- a/ui/display.h +++ b/ui/display.h @@ -46,10 +46,18 @@ enum { #endif }; +// if we have libncursesw and braille graphs were enabled, build with them +#if HAVE_CURSESW && ENABLE_BRAILLE +#define WITH_BRAILLE_DISPLAY 1 +#endif + enum { DisplayModeDefault, DisplayModeBlockmap, DisplayModeBlockmapScale, +#ifdef WITH_BRAILLE_DISPLAY + DisplayModeBraille, +#endif DisplayModeMAX /* this must be the last DisplayMode entry */ }; diff --git a/ui/mtr.h b/ui/mtr.h index 7b4482b..dcf02d7 100644 --- a/ui/mtr.h +++ b/ui/mtr.h @@ -63,7 +63,7 @@ typedef int time_t; #define FLD_INDEX_SZ 256 /* net related definitions */ -#define SAVED_PINGS 200 +#define SAVED_PINGS 400 #define MAX_PATH 128 #define MaxHost 256 #define MinPort 1024