diff --git a/docs/pv.1 b/docs/pv.1 index 252cc06..8ee3b40 100644 --- a/docs/pv.1 +++ b/docs/pv.1 @@ -238,6 +238,13 @@ For example, \*(lq\fB\-x\~'window,process:%t\~%b\~%r'\fR\*(rq will show the elapsed time, bytes transferred, and rate, in both the window title and the process title. .TP +.B \-9, \-\-conemu +As well as displaying progress to the terminal, also emit the ConEmu +"progress bar" sequences (OSC 9;4) to show the progress percentage in the +terminal's title tab, on terminals which support it. +This is equivalent to adding \*(lq%{progress\-conemu}\*(rq to the +\fB\-\-format\fR string. +.TP .B \-v, \-\-stats At the end of the transfer, write an additional line showing the transfer rate minimum, mean, maximum, and standard deviation. diff --git a/docs/pv.1.md b/docs/pv.1.md index c37ac4a..3a86714 100644 --- a/docs/pv.1.md +++ b/docs/pv.1.md @@ -248,6 +248,14 @@ are explicitly switched on will be shown. bytes transferred, and rate, in both the window title and the process title. +**-9, \--conemu** + +: As well as displaying progress to the terminal, also emit the ConEmu + \"progress bar\" sequences (OSC 9;4) to show the progress percentage + in the terminal\'s title tab, on terminals which support it. This is + equivalent to adding "%{progress-conemu}" to the **\--format** + string. + **-v, \--stats** : At the end of the transfer, write an additional line showing the diff --git a/src/include/options.h b/src/include/options.h index 5c27f59..1c7c185 100644 --- a/src/include/options.h +++ b/src/include/options.h @@ -111,6 +111,7 @@ struct opts_s { bool direct_io; /* set if O_DIRECT is to be used */ bool sparse_output; /* set if we leave holes in the output */ bool discard_input; /* set to write nothing to output */ + bool use_osc94; /* set to enable OSC 9;4 sequences */ bool show_stats; /* set to write statistics at the end */ bool width_set_manually; /* width was set manually, not detected */ bool height_set_manually; /* height was set manually, not detected */ diff --git a/src/include/pv-internal.h b/src/include/pv-internal.h index 3ab1486..c728ddf 100644 --- a/src/include/pv-internal.h +++ b/src/include/pv-internal.h @@ -247,6 +247,7 @@ struct pvstate_s { bool sparse_output; /* convert runs of null bytes into holes */ bool discard_input; /* write nothing to stdout */ bool show_stats; /* show statistics on exit */ + bool use_osc94; /* enable OSC 9;4 sequences */ bool width_set_manually; /* width was set manually, not detected */ bool height_set_manually; /* height was set manually, not detected */ bool rate_limit_specified; /* whether a rate limit value was given */ @@ -629,6 +630,7 @@ pvdisplay_bytecount_t pv_formatter_segmentcontent(char *, pvformatter_args_t); pvdisplay_bytecount_t pv_formatter_progress(pvformatter_args_t); pvdisplay_bytecount_t pv_formatter_progress_bar_only(pvformatter_args_t); pvdisplay_bytecount_t pv_formatter_progress_amount_only(pvformatter_args_t); +size_t pv_osc94_format(char *, size_t, readonly_pvcontrol_t, readonly_pvtransfercalc_t); pvdisplay_bytecount_t pv_formatter_progress_conemu(pvformatter_args_t); pvdisplay_bytecount_t pv_formatter_bar_default(pvformatter_args_t); pvdisplay_bytecount_t pv_formatter_bar_plain(pvformatter_args_t); diff --git a/src/include/pv.h b/src/include/pv.h index c6d8abd..0ceb169 100644 --- a/src/include/pv.h +++ b/src/include/pv.h @@ -250,6 +250,7 @@ void pv_state_append_to_default_format(pvstate_t, /*@null@ */ const char *); */ extern void pv_state_force_set(pvstate_t, bool); extern void pv_state_cursor_set(pvstate_t, bool); +extern void pv_state_use_osc94_set(pvstate_t, bool); extern void pv_state_show_stats_set(pvstate_t, bool); extern void pv_state_numeric_set(pvstate_t, bool); extern void pv_state_wait_set(pvstate_t, bool); diff --git a/src/main/help.c b/src/main/help.c index ff4a69d..ce119bb 100644 --- a/src/main/help.c +++ b/src/main/help.c @@ -378,6 +378,9 @@ void display_help(void) { "-x", "--extra-display", N_("SPEC"), N_("also send progress to SPEC"), { 0, 0, 0, 0} }, + { "-9", "--conemu", NULL, + N_("set terminal progress state with OSC 9;4 (ConEmu)"), + { 0, 0, 0, 0} }, { "-v", "--stats", NULL, N_("output transfer statistics at the end"), { 0, 0, 0, 0} }, diff --git a/src/main/main.c b/src/main/main.c index a7b2e3b..1146e29 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -889,6 +889,7 @@ int main(int argc, char **argv) pv_state_no_display_set(state, opts->no_display); pv_state_force_set(state, opts->force); pv_state_cursor_set(state, opts->cursor); + pv_state_use_osc94_set(state, opts->use_osc94); pv_state_show_stats_set(state, opts->show_stats); pv_state_numeric_set(state, opts->numeric); pv_state_wait_set(state, opts->wait); diff --git a/src/main/options.c b/src/main/options.c index 16a2aad..259470f 100644 --- a/src/main/options.c +++ b/src/main/options.c @@ -709,6 +709,7 @@ opts_t opts_parse(unsigned int argc, char **argv) { "name", 1, NULL, (int) 'N' }, { "bar-style", 1, NULL, (int) 'u' }, { "format", 1, NULL, (int) 'F' }, + { "conemu", 0, NULL, (int) '9' }, { "extra-display", 1, NULL, (int) 'x' }, { "stats", 0, NULL, (int) 'v' }, { "rate-limit", 1, NULL, (int) 'L' }, @@ -739,7 +740,7 @@ opts_t opts_parse(unsigned int argc, char **argv) /*@+nullassign@ */ int option_index = 0; #endif /* HAVE_GETOPT_LONG */ - char *short_options = "hVpteIrab8kTA:fvnqcWD:s:gl0i:w:H:N:u:F:x:L:B:CEZ:J:SYKOXU:R:Q:P:d:m:o:M:" + char *short_options = "hVpteIrab8kTA:fvnqcWD:s:gl0i:w:H:N:u:F:9x:L:B:CEZ:J:SYKOXU:R:Q:P:d:m:o:M:" #ifdef ENABLE_DEBUGGING "!:" #endif @@ -975,6 +976,9 @@ opts_t opts_parse(unsigned int argc, char **argv) case 'f': opts->force = true; break; + case '9': + opts->use_osc94 = true; + break; case 'v': opts->show_stats = true; break; diff --git a/src/pv/display.c b/src/pv/display.c index fa97aee..237c2dd 100644 --- a/src/pv/display.c +++ b/src/pv/display.c @@ -989,6 +989,9 @@ static void pv__format_init(pvprogramstatus_t status, readonly_pvcontrol_t contr * If "final" is true, this is the final update so the rate is given as an * an average over the whole transfer; otherwise the current rate is shown. * + * If "is_primary" is true, this is the primary display, not an extra one, + * which means extra terminal control codes like OSC 9;4 may be inserted. + * * Returns true if the display buffer can be used, false if not. * * When returning true, this function will have also set @@ -1001,7 +1004,7 @@ static void pv__format_init(pvprogramstatus_t status, readonly_pvcontrol_t contr */ static bool pv_format(pvprogramstatus_t status, readonly_pvcontrol_t control, readonly_pvtransferstate_t transfer, readonly_pvtransfercalc_t calc, /*@null@ */ const char *format_supplied, pvdisplay_t display, - bool reinitialise, bool final) + bool reinitialise, bool final, bool is_primary) { struct pvdisplay_component_s *format_component_array; char display_segments[PV_SIZEOF_FORMAT_SEGMENTS_BUF]; /* flawfinder: ignore - always bounded */ @@ -1232,6 +1235,29 @@ static bool pv_format(pvprogramstatus_t status, readonly_pvcontrol_t control, re segment->bytes, display->display_buffer + display_buffer_offset - segment->bytes); } + /* + * If OSC 9;4 is enabled globally but wasn't present in the format + * string, and this is the primary display, run its formatter to add + * the sequences. + */ + if (is_primary && control->use_osc94 && !display->using_osc94) { + char content[128]; /* flawfinder: ignore */ + size_t bytes_added; + + /* flawfinder - null-terminated and bounded with pv_snprintf(). */ + + memset(content, 0, sizeof(content)); + + debug("%s", "OSC 9;4 enabled globally but not in format string - adding sequence"); + bytes_added = pv_osc94_format(content, sizeof(content), control, calc); + memmove(display->display_buffer + display_buffer_offset, content, bytes_added); + display_buffer_offset += bytes_added; + display_buffer_remaining -= bytes_added; + new_display_string_bytes += bytes_added; + + display->using_osc94 = true; + } + /* If the SGR active codes flag is set, emit an SGR reset. */ if (display->sgr_code_active) { debug("%s", "SGR codes still active - adding reset"); @@ -1324,12 +1350,13 @@ void pv_display(pvprogramstatus_t status, readonly_pvcontrol_t control, pvtransi flags->reparse_display = 0; } - if (!pv_format(status, control, transfer, calc, control->format_string, display, reinitialise, final)) + if (!pv_format(status, control, transfer, calc, control->format_string, display, reinitialise, final, true)) return; if ((NULL != extra_display) && (0 != control->extra_displays)) { if (!pv_format - (status, control, transfer, calc, control->extra_format_string, extra_display, reinitialise, final)) + (status, control, transfer, calc, control->extra_format_string, extra_display, reinitialise, final, + false)) return; } diff --git a/src/pv/format/conemu.c b/src/pv/format/conemu.c index 61776c6..d29c16e 100644 --- a/src/pv/format/conemu.c +++ b/src/pv/format/conemu.c @@ -18,25 +18,28 @@ /* - * Produce OSC 9;4 progress bar codes for terminal title tabs. + * Populate a buffer with OSC 9;4 progress bar codes for terminal title + * tabs, returning the length of the string. */ -pvdisplay_bytecount_t pv_formatter_progress_conemu(pvformatter_args_t args) +size_t pv_osc94_format(char *buffer, size_t bufsize, readonly_pvcontrol_t control, readonly_pvtransfercalc_t calc) { - char content[128]; /* flawfinder: ignore */ + int formatted_size = 0; - /* flawfinder - null-terminated and bounded with pv_snprintf(). */ + if (NULL == buffer) + return 0; + if (bufsize < 1) + return 0; + buffer[0] = '\0'; - memset(content, 0, sizeof(content)); - - if (args->control->size > 0 || args->control->rate_gauge) { + if (control->size > 0 || control->rate_gauge) { /* Known size or rate gauge - percentage progress. */ - (void) pv_snprintf(content, sizeof(content), "\033]9;4;1;%.0f\033\\", args->calc->percentage); + formatted_size = pv_snprintf(buffer, bufsize, "\033]9;4;1;%.0f\033\\", calc->percentage); } else { /* Unknown size - indeterminate progress. */ /* See pv_formatter_progress_unknownsize() in progressbar.c. */ double indicator_position; - indicator_position = args->calc->percentage; + indicator_position = calc->percentage; if (indicator_position > 200.0) #if HAVE_FMOD indicator_position = fmod(indicator_position, 200.0); @@ -53,9 +56,30 @@ pvdisplay_bytecount_t pv_formatter_progress_conemu(pvformatter_args_t args) indicator_position = 0.0; } - (void) pv_snprintf(content, sizeof(content), "\033]9;4;3;%.0f\033\\", indicator_position); + formatted_size = pv_snprintf(buffer, bufsize, "\033]9;4;3;%.0f\033\\", indicator_position); } + if (formatted_size < 0) + return 0; + if (formatted_size >= (int) bufsize) + return bufsize - 1; + return (size_t) formatted_size; +} + + +/* + * Produce OSC 9;4 progress bar codes for terminal title tabs. + */ +pvdisplay_bytecount_t pv_formatter_progress_conemu(pvformatter_args_t args) +{ + char content[128]; /* flawfinder: ignore */ + + /* flawfinder - null-terminated and bounded with pv_snprintf(). */ + + memset(content, 0, sizeof(content)); + + (void) pv_osc94_format(content, sizeof(content), args->control, args->calc); + args->display->using_osc94 = true; return pv_formatter_segmentcontent(content, args); diff --git a/src/pv/state.c b/src/pv/state.c index 72a36bc..fe2a2de 100644 --- a/src/pv/state.c +++ b/src/pv/state.c @@ -565,6 +565,11 @@ void pv_state_cursor_set(pvstate_t state, bool val) state->control.cursor = val; } +void pv_state_use_osc94_set(pvstate_t state, bool val) +{ + state->control.use_osc94 = val; +} + void pv_state_show_stats_set(pvstate_t state, bool val) { state->control.show_stats = val;