Add option -9 / --conemu to switch on OSC 9;4 sequences without having to use a custom --format string (#202).

This commit is contained in:
Andrew Wood
2026-08-01 14:51:15 +01:00
parent 7387b80843
commit 390cf01ce1
11 changed files with 97 additions and 14 deletions
+7
View File
@@ -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.
+8
View File
@@ -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
+1
View File
@@ -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 */
+2
View File
@@ -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);
+1
View File
@@ -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);
+3
View File
@@ -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} },
+1
View File
@@ -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);
+5 -1
View File
@@ -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;
+30 -3
View File
@@ -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;
}
+34 -10
View File
@@ -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);
+5
View File
@@ -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;