]> git.ipfire.org Git - thirdparty/mtr.git/commitdiff
add braille graph support with --displaymode 3
authorBart Trojanowski <bart@jukie.net>
Tue, 8 Apr 2025 03:11:09 +0000 (23:11 -0400)
committerBart Trojanowski <bart@jukie.net>
Wed, 9 Apr 2025 12:36:16 +0000 (08:36 -0400)
configure.ac
ui/curses.c
ui/display.h
ui/mtr.h

index cc07d817cbd56847d4abb18eb4cbcbe04b06d225..161c45490be8c58cdbe7938d8fe19785c6ffad47 100644 (file)
@@ -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])
index 7fc201d77f7815bb5d55861a2823355c2c3466f0..ab5e92e45f959d43e7229bc60d2ec3a6cee19126 100644 (file)
@@ -20,6 +20,8 @@
 
 #include "mtr.h"
 
+#include <locale.h>
+#include <assert.h>
 #include <strings.h>
 #include <unistd.h>
 
@@ -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<length; ++i) {
+        int ms = ms_data[i];
+        if (ms < 0)
+            continue;
+        if (current_host_range_low_ms > 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();
index b4eca77c757d127e6e3ec4350220606d854345ad..ad74e63220f4e2e512b630d59370da5cf4acd501 100644 (file)
@@ -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 */
 };
 
index 7b4482b30b82d13c12b0c3246a60f88e1a3a1e4b..dcf02d705ed536343a61b9a86b1b97d54a44c415 100644 (file)
--- 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