From 90159e5de3d22d80cdc42d2a15e70a138afcd57d Mon Sep 17 00:00:00 2001 From: karlstav Date: Tue, 26 May 2020 22:29:47 +0200 Subject: [PATCH] Reworked noncurses output mode and made it defualt. The reworked noncurses mode uses a custom framebuffer technique that only draws changes per frame and only writes to the terminal once per frame. This results in far less cpu usage then ncurses. It is also free of the flickering that we se in ncurses mode #196 Colors are no longer set by default so hopefully cava will change color automatically in noncurses mode when using tools such as pywall #340 #181 Unfortunently html colors, gradient and key controls are not supported in noncurses. --- README.md | 2 + config.c | 10 +- example_files/config | 13 ++- output/terminal_noncurses.c | 214 +++++++++++++++++++++++++++--------- 4 files changed, 176 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index d0a4806..58331e1 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,8 @@ If cava quits unexpectedly or is force killed, echo must be turned on manually w ### Controls +NOTE: only works in ncurses output mode. + | Key | Description | | --- | ----------- | | up / down| increase/decrease sensitivity | diff --git a/config.c b/config.c index 4c846a2..a27e3f9 100644 --- a/config.c +++ b/config.c @@ -59,8 +59,10 @@ int validate_color(char *checkColor, int om, void *err) { // If the output mode is not ncurses, tell the user to use a named colour instead of hex // colours. if (om != OUTPUT_NCURSES) { - write_errorf(error, "Only 'ncurses' output method supports HTML colors. Please change " - "the colours or the output method.\n"); + write_errorf( + error, "Only 'ncurses' output method supports HTML colors. Please change " + "the colours or the output method.\nAs of version 0.7.0 ncurses is no longer" + " the default output method\n"); return 0; } // 0 to 9 and a to f @@ -118,7 +120,7 @@ bool validate_colors(void *params, void *err) { } // In case color is not html format set bgcol and col to predefinedint values - p->col = 6; + p->col = -1; if (strcmp(p->color, "black") == 0) p->col = 0; if (strcmp(p->color, "red") == 0) @@ -406,7 +408,7 @@ bool load_config(char configPath[PATH_MAX], struct config_params *p, bool colors } #ifdef NCURSES - outputMethod = (char *)iniparser_getstring(ini, "output:method", "ncurses"); + outputMethod = (char *)iniparser_getstring(ini, "output:method", "noncurses"); #endif #ifndef NCURSES outputMethod = (char *)iniparser_getstring(ini, "output:method", "noncurses"); diff --git a/example_files/config b/example_files/config index 7578630..8350bec 100644 --- a/example_files/config +++ b/example_files/config @@ -77,7 +77,9 @@ [output] # Output method. Can be 'ncurses', 'noncurses' or 'raw'. -# 'noncurses' is for systems that does not suport ncurses. +# 'noncurses' uses a custom framebuffer technique and draws only changes +# from frame to frame. As of version 0.7.0 'noncurses' is default. +# # 'raw' is an 8 or 16 bit (configurable via the 'bit_format' option) data # stream of the bar heights that can be used to send to other applications. # 'raw' defaults to 200 bars, which can be adjusted in the 'bars' option above. @@ -112,10 +114,11 @@ [color] # Colors can be one of seven predefined: black, blue, cyan, green, magenta, red, white, yellow. -# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires a -# terminal that can change color definitions such as Gnome-terminal or rxvt. -; background = black -; foreground = cyan +# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires +# ncurses output method and a terminal that can change color definitions such as Gnome-terminal or rxvt. +# default is to keep current terminal color +; background = default +; foreground = default # Gradient mode, only hex defined colors are supported, background must also be defined in hex # or remain commented out. 1 = on, 0 = off. Warning: for certain terminal emulators cava will diff --git a/output/terminal_noncurses.c b/output/terminal_noncurses.c index 294044c..e8147e0 100644 --- a/output/terminal_noncurses.c +++ b/output/terminal_noncurses.c @@ -8,9 +8,14 @@ #include #include -wchar_t barstring[8][100]; -int ttybarstring[8]; -char spacestring[100]; +wchar_t *line_buffer; +wchar_t *barstring[8]; +wchar_t *wspacestring; +int buf_length; +char *ttyline_buffer; +char ttybarstring[8][100]; +char ttyspacestring[100]; +int ttybuf_length; int setecho(int fd, int onoff) { @@ -33,13 +38,24 @@ int setecho(int fd, int onoff) { int init_terminal_noncurses(int col, int bgcol, int w, int h, int bar_width) { int n, i; + + ttybuf_length = sizeof(char) * w * h * 10; + ttyline_buffer = (char *)malloc(ttybuf_length); + + buf_length = sizeof(wchar_t) * w * h * 10; + line_buffer = (wchar_t *)malloc(buf_length); + wspacestring = (wchar_t *)malloc(sizeof(wchar_t) * bar_width); + // clearing barstrings for (n = 0; n < 8; n++) { - - ttybarstring[n] = 0; + barstring[n] = (wchar_t *)malloc(sizeof(wchar_t) * bar_width); + ttybarstring[n][0] = '\0'; barstring[n][0] = '\0'; - spacestring[0] = '\0'; + ttyspacestring[0] = '\0'; } + wspacestring[0] = '\0'; + line_buffer[0] = '\0'; + ttyline_buffer[0] = '\0'; // creating barstrings for drawing for (n = 0; n < bar_width; n++) { @@ -52,11 +68,17 @@ int init_terminal_noncurses(int col, int bgcol, int w, int h, int bar_width) { wcscat(barstring[5], L"\u2585"); wcscat(barstring[6], L"\u2586"); wcscat(barstring[7], L"\u2587"); - strcat(spacestring, " "); - - for (i = 0; i < 8; i++) { - ttybarstring[i] += (i + 1) * pow(10, n); - } + wcscat(wspacestring, L" "); + + strcat(ttybarstring[0], "8"); + strcat(ttybarstring[1], "1"); + strcat(ttybarstring[2], "2"); + strcat(ttybarstring[3], "3"); + strcat(ttybarstring[4], "4"); + strcat(ttybarstring[5], "5"); + strcat(ttybarstring[6], "6"); + strcat(ttybarstring[7], "7"); + strcat(ttyspacestring, " "); } col += 30; @@ -68,9 +90,10 @@ int init_terminal_noncurses(int col, int bgcol, int w, int h, int bar_width) { printf("\033[0m\n"); system("clear"); - printf("\033[%dm", col); // setting color + if (col) + printf("\033[%dm", col); // setting color - printf("\033[1m"); // setting "bright" color mode, looks cooler... I think + // printf("\033[1m"); // setting "bright" color mode, looks cooler... I think if (bgcol != 0) { @@ -106,68 +129,151 @@ void get_terminal_dim_noncurses(int *w, int *h) { int draw_terminal_noncurses(int tty, int h, int w, int bars, int bar_width, int bs, int rest, int f[200], int flastd[200]) { - int c, move, n, o; + int current_char, last_char, n, o, same_line, new_line, cx; struct winsize dim; - // output: check if terminal has been resized - if (!tty) { + same_line = 0; + new_line = 0; + cx = 0; + + if (tty) { + + ttyline_buffer[0] = '\0'; + + for (n = h - 1; n >= 0; n--) { + + int same_bar = 0; + int center_adjusted = 0; + + for (o = 0; o < bars; o++) { + + current_char = f[o] - n * 8; + last_char = flastd[o] - n * 8; + + // same as last frame + if ((current_char < 1 && last_char < 1) || (current_char > 7 && last_char > 7) || + (current_char == last_char)) { + same_bar++; + } else { + if (same_line > 0) { + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, "\033[%dB", + same_line); // move down + new_line += same_line; + same_line = 0; + } + + if (same_bar > 0) { + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, "\033[%dC", + (bar_width + bs) * same_bar); // move forward + same_bar = 0; + } + + if (!center_adjusted) { + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, "\033[%dC", rest); + center_adjusted = 1; + } + + if (current_char < 1) + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, ttyspacestring); + else if (current_char > 7) + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, ttybarstring[0]); + else + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, + ttybarstring[current_char]); + + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, "\033[%dC", bs); + } + } + if (same_bar != bars) { + if (n != 0) { + cx += snprintf(ttyline_buffer + cx, ttybuf_length - cx, "\n"); + new_line++; + } + } else { + same_line++; + } + } + if (same_line != h) { + printf("%s\r\033[%dA", ttyline_buffer, + new_line); //\r\033[%dA", ttyline_buffer, h - same_line); + fflush(stdout); + } + } else if (!tty) { + + // output: check if terminal has been resized ioctl(STDOUT_FILENO, TIOCGWINSZ, &dim); if ((int)dim.ws_row != h || (int)dim.ws_col != w) { + free(line_buffer); + free(wspacestring); + for (int i = 0; i < 8; i++) + free(barstring[i]); return -1; } - } - - for (n = h - 1; n >= 0; n--) { - move = rest; // center adjustment - for (o = 0; o < bars; o++) { + line_buffer[0] = '\0'; - // output: draw and move to another one, check whether we're not too far + for (n = h - 1; n >= 0; n--) { - if (f[o] != flastd[o]) { // change? + int same_bar = 0; + int center_adjusted = 0; - if (move != 0) - printf("\033[%dC", move); - move = 0; + for (o = 0; o < bars; o++) { - c = f[o] - n * 8; + current_char = f[o] - n * 8; + last_char = flastd[o] - n * 8; - if (c < 1) { - if (n * 8 < flastd[o]) - printf("%s", spacestring); // blank - else - move += bar_width; - } else if (c > 7) { - if (n > flastd[o] / 8 - 1) { - if (tty) - printf("%d", ttybarstring[7]); // block tty - else - printf("%ls", barstring[0]); // block - } else - move += bar_width; + // same as last frame + if ((current_char < 1 && last_char < 1) || (current_char > 7 && last_char > 7) || + (current_char == last_char)) { + same_bar++; } else { - if (tty) - printf("%d", ttybarstring[c - 1]); // fractioned block tty + if (same_line > 0) { + cx += swprintf(line_buffer + cx, buf_length - cx, L"\033[%dB", + same_line); // move down + new_line += same_line; + same_line = 0; + } + + if (same_bar > 0) { + cx += swprintf(line_buffer + cx, buf_length - cx, L"\033[%dC", + (bar_width + bs) * same_bar); // move forward + same_bar = 0; + } + + if (!center_adjusted && rest) { + cx += swprintf(line_buffer + cx, buf_length - cx, L"\033[%dC", rest); + center_adjusted = 1; + } + + if (current_char < 1) + cx += swprintf(line_buffer + cx, buf_length - cx, wspacestring); + else if (current_char > 7) + cx += swprintf(line_buffer + cx, buf_length - cx, barstring[0]); else - printf("%ls", barstring[c]); // fractioned block vt - } + cx += swprintf(line_buffer + cx, buf_length - cx, barstring[current_char]); - } else - move += bar_width; // no change, moving along + cx += swprintf(line_buffer + cx, buf_length - cx, L"\033[%dC", bs); + } + } - move += bs; // move to next bar + if (same_bar != bars) { + if (n != 0) { + cx += swprintf(line_buffer + cx, buf_length - cx, L"\n"); + new_line++; + } + } else { + same_line++; + } + } + if (same_line != h) { + printf("%ls\r\033[%dA", line_buffer, + new_line); //\r\033[%dA", line_buffer, h - same_line); + fflush(stdout); } - - if (n != 0) - printf("\n"); - else - printf("\r"); } - - printf("\033[%dA", h); return 0; }