New format string "%{progress-conemu}" to emit OSC 9;4 (ConEmu progress bar) codes (#202).
This commit is contained in:
@@ -25,6 +25,7 @@ src/pv/format/averagerate.c \
|
||||
src/pv/format/barstyle.c \
|
||||
src/pv/format/bufferpercent.c \
|
||||
src/pv/format/bytes.c \
|
||||
src/pv/format/conemu.c \
|
||||
src/pv/format/eta.c \
|
||||
src/pv/format/fineta.c \
|
||||
src/pv/format/lastwritten.c \
|
||||
|
||||
@@ -119,6 +119,7 @@ is acknowledged and greatly appreciated:
|
||||
* [oech3](https://codeberg.org/oech3) - suggested and tested performance improvements using `splice()` in more situations on Linux ([#188](https://codeberg.org/ivarch/pv/issues/188), [#191](https://codeberg.org/ivarch/pv/issues/191), [#192](https://codeberg.org/ivarch/pv/issues/192))
|
||||
* [Barak A. Pearlmutter](https://codeberg.org/barak) - suggested that "**--rate-limit**" should accept non-integer values ([#193](https://codeberg.org/ivarch/pv/issues/193))
|
||||
* [dirkmueller](https://codeberg.org/dirkmueller) - fixed the potential for stdout closure under a combination of error conditions in **--monitor** mode ([#201](https://codeberg.org/ivarch/pv/pulls/201))
|
||||
* [InkstainTheBat](https://codeberg.org/InkstainTheBat) - suggested ConEmu OSC 9;4 support ([#202](https://codeberg.org/ivarch/pv/issues/202))
|
||||
|
||||
Translations provided through [Codeberg Weblate](https://translate.codeberg.org/projects/pv/):
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
### UNRELEASED
|
||||
|
||||
* *feature:* new **--format** string **%{progress-conemu}** to show progress in the terminal title tab on terminals with ConEmu OSC 9;4 support ([#202](https://codeberg.org/ivarch/pv/issues/202))
|
||||
* *cleanup:* eliminate potential for closure of standard output under a combination of error conditions when using **--monitor** mode ([#201](https://codeberg.org/ivarch/pv/pulls/201))
|
||||
|
||||
### 1.11.0 - 11 June 2026
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH PV 1 2026-06-11 pv-1.11.0 "User Commands"
|
||||
.TH PV 1 2026-07-30 pv-1.11.0 "User Commands"
|
||||
.\"
|
||||
.SH NAME
|
||||
pv \- monitor and manage the progress of data through a pipe
|
||||
@@ -505,6 +505,10 @@ Expands to fill the remaining space unless prefixed by a number.
|
||||
The percentage completion (or maximum rate, with \*(lq\fB\-\-gauge\fR\*(rq
|
||||
when the size is unknown).
|
||||
.TP
|
||||
.B %{progress-conemu}
|
||||
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.
|
||||
.TP
|
||||
.B %{bar\-plain}
|
||||
Progress bar in the standard plain format, without any sides, and without
|
||||
any percentage displayed afterwards.
|
||||
@@ -675,6 +679,11 @@ Show only the elapsed time - useful as a simple timer, such as
|
||||
.B pv \-pterb
|
||||
The default behaviour: progress bar, elapsed time, estimated completion
|
||||
time, current rate, and byte counter.
|
||||
.TP
|
||||
.B pv \-\-format='%{progress-conemu}%{bytes} %{timer} %{rate} %{progress} %{eta}'
|
||||
Show the byte counter, elapsed time, current rate, progress bar, and ETA,
|
||||
and show the percentage progress in the terminal's tab bar, on terminals
|
||||
that support ConEmu OSC 9;4 sequences.
|
||||
.PP
|
||||
On macOS, it may be useful to specify \*(lq\fB\-\-buffer\-size\~1024\fR\*(rq
|
||||
in a pipeline, as this may improve performance.
|
||||
|
||||
@@ -519,6 +519,12 @@ contain the following sequences:
|
||||
: The percentage completion (or maximum rate, with "**\--gauge**" when
|
||||
the size is unknown).
|
||||
|
||||
**%{progress-conemu}**
|
||||
|
||||
: 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.
|
||||
|
||||
**%{bar-plain}**
|
||||
|
||||
: Progress bar in the standard plain format, without any sides, and
|
||||
@@ -679,6 +685,12 @@ Some suggested common switch combinations:
|
||||
: The default behaviour: progress bar, elapsed time, estimated
|
||||
completion time, current rate, and byte counter.
|
||||
|
||||
**pv \--format=\'%{progress-conemu}%{bytes} %{timer} %{rate} %{progress} %{eta}\'**
|
||||
|
||||
: Show the byte counter, elapsed time, current rate, progress bar, and
|
||||
ETA, and show the percentage progress in the terminal\'s tab bar, on
|
||||
terminals that support ConEmu OSC 9;4 sequences.
|
||||
|
||||
On macOS, it may be useful to specify "**\--buffer-size 1024**" in a
|
||||
pipeline, as this may improve performance.
|
||||
|
||||
|
||||
@@ -348,6 +348,7 @@ struct pvstate_s {
|
||||
bool format_uses_colour; /* set if the format string uses colours */
|
||||
bool colour_permitted; /* whether colour is permitted for this display */
|
||||
bool sgr_code_active; /* set while SGR code is active in a display line */
|
||||
bool using_osc94; /* set if the format contains an OSC 9;4 code */
|
||||
bool final_update; /* set internally on the final update */
|
||||
bool output_produced; /* set once anything written to terminal */
|
||||
|
||||
@@ -628,6 +629,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);
|
||||
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);
|
||||
pvdisplay_bytecount_t pv_formatter_bar_block(pvformatter_args_t);
|
||||
|
||||
@@ -543,6 +543,7 @@ pvdisplay_bytecount_t pv_formatter_segmentcontent(char *content, pvformatter_arg
|
||||
{ "{progress}", &pv_formatter_progress, true },
|
||||
{ "{progress-amount-only}", &pv_formatter_progress_amount_only, false },
|
||||
{ "{progress-bar-only}", &pv_formatter_bar_default, true },
|
||||
{ "{progress-conemu}", &pv_formatter_progress_conemu, false },
|
||||
{ "{bar-plain}", &pv_formatter_bar_plain, true },
|
||||
{ "{bar-block}", &pv_formatter_bar_block, true },
|
||||
{ "{bar-granular}", &pv_formatter_bar_granular, true },
|
||||
@@ -1093,6 +1094,9 @@ static bool pv_format(pvprogramstatus_t status, readonly_pvcontrol_t control, re
|
||||
/* Clear the SGR active codes flag, for the SGR formatter. */
|
||||
display->sgr_code_active = false;
|
||||
|
||||
/* Clear the OSC 9;4 active flag. */
|
||||
display->using_osc94 = false;
|
||||
|
||||
/*
|
||||
* Populate the internal segments buffer with each component's
|
||||
* output, in two passes.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Formatter function for OSC 9;4 - ConEmu progress bar - codes.
|
||||
*
|
||||
* Copyright 2024-2026 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_MATH_H
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
if (args->control->size > 0 || args->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);
|
||||
} else {
|
||||
/* Unknown size - indeterminate progress. */
|
||||
/* See pv_formatter_progress_unknownsize() in progressbar.c. */
|
||||
double indicator_position;
|
||||
|
||||
indicator_position = args->calc->percentage;
|
||||
if (indicator_position > 200.0)
|
||||
#if HAVE_FMOD
|
||||
indicator_position = fmod(indicator_position, 200.0);
|
||||
#else
|
||||
{
|
||||
while (indicator_position > 200.0)
|
||||
indicator_position -= 200.0;
|
||||
}
|
||||
#endif
|
||||
if (indicator_position > 100.0) {
|
||||
indicator_position = 200.0 - indicator_position;
|
||||
}
|
||||
if (indicator_position < 0.0) {
|
||||
indicator_position = 0.0;
|
||||
}
|
||||
|
||||
(void) pv_snprintf(content, sizeof(content), "\033]9;4;3;%.0f\033\\", indicator_position);
|
||||
}
|
||||
|
||||
args->display->using_osc94 = true;
|
||||
|
||||
return pv_formatter_segmentcontent(content, args);
|
||||
}
|
||||
@@ -62,6 +62,15 @@ static long ldsqrt(long double value)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Turn off the OSC 9;4 (ConEmu) progress bar.
|
||||
*/
|
||||
static void pv_clear_osc94(pvstate_t state)
|
||||
{
|
||||
pv_tty_write(&(state->flags), "\033]9;4;0;0\033\\", 11);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If the flag is set to say that a terminal resize signal was received,
|
||||
* clear the flag, resize the display, and return true.
|
||||
@@ -465,6 +474,8 @@ int pv_main_loop(pvstate_t state)
|
||||
if (input_fd < 0) {
|
||||
if (state->control.cursor)
|
||||
pv_crs_fini(&(state->cursor), &(state->control), &(state->flags));
|
||||
if (state->display.using_osc94)
|
||||
pv_clear_osc94(state);
|
||||
return state->status.exit_status;
|
||||
}
|
||||
#if HAVE_POSIX_FADVISE
|
||||
@@ -638,6 +649,8 @@ int pv_main_loop(pvstate_t state)
|
||||
debug("%s: %s", "write error from pv_transfer", strerror(errno));
|
||||
if (state->control.cursor)
|
||||
pv_crs_fini(&(state->cursor), &(state->control), &(state->flags));
|
||||
if (state->display.using_osc94)
|
||||
pv_clear_osc94(state);
|
||||
return state->status.exit_status;
|
||||
}
|
||||
|
||||
@@ -891,6 +904,9 @@ int pv_main_loop(pvstate_t state)
|
||||
pv_tty_write(&(state->flags), "\n", 1);
|
||||
}
|
||||
|
||||
if (state->display.using_osc94)
|
||||
pv_clear_osc94(state);
|
||||
|
||||
/* Tell the error routines that progress bar display has finished. */
|
||||
pv_end_display();
|
||||
|
||||
@@ -1585,6 +1601,9 @@ int pv_query_loop(pvstate_t state, pid_t query)
|
||||
pv_tty_write(&(state->flags), "\n", 1);
|
||||
}
|
||||
|
||||
if (state->display.using_osc94)
|
||||
pv_clear_osc94(state);
|
||||
|
||||
/* Tell the error routines that progress bar display has finished. */
|
||||
pv_end_display();
|
||||
|
||||
|
||||
+33
-16
@@ -321,12 +321,12 @@ void *pv_memrchr(const void *buffer, int match, size_t length)
|
||||
* Return the number of display columns needed to show the
|
||||
* non-null-terminated string "string" whose length in bytes is "bytes".
|
||||
*
|
||||
* Skips ECMA-48 CSI (ESC [ ...) sequences, but any other control characters
|
||||
* are treated as printable.
|
||||
* Skips ECMA-48 CSI (ESC [ ...) sequences, and OSC (ESC ] ...) sequences,
|
||||
* but any other control characters are treated as printable.
|
||||
*
|
||||
* Internally, after skipping CSI sequences, the string is converted to a
|
||||
* wide character string, and each wide character's width is checked with
|
||||
* "wcswidth()".
|
||||
* Internally, after skipping CSI or OSC sequences, the string is converted
|
||||
* to a wide character string, and each wide character's width is checked
|
||||
* with "wcswidth()".
|
||||
*
|
||||
* If NLS is disabled, or the string cannot be converted, this just returns
|
||||
* the number of bytes in the string that aren't part of CSI sequences.
|
||||
@@ -364,19 +364,36 @@ size_t pv_strwidth(const char *string, size_t bytes)
|
||||
raw_string = allocated_raw;
|
||||
}
|
||||
|
||||
/* Copy the original string, skipping ECMA-48 CSI sequences. */
|
||||
/*
|
||||
* Copy the original string, skipping ECMA-48 CSI and OSC sequences.
|
||||
*/
|
||||
for (read_pos = 0, write_pos = 0; read_pos < bytes; read_pos++) {
|
||||
if ((string[read_pos] != '\033') || (read_pos >= bytes - 1) || (string[read_pos + 1] != '[')) {
|
||||
if ((string[read_pos] == '\033') && (read_pos < bytes - 1) && (string[read_pos + 1] == '[')) {
|
||||
/* Skip CSI - ends with anything other than 0-9 or ';'. */
|
||||
read_pos += 2;
|
||||
while ((read_pos < bytes - 1)
|
||||
&& ((string[read_pos] >= '0' && string[read_pos] <= '9')
|
||||
|| (';' == string[read_pos])
|
||||
)
|
||||
) {
|
||||
read_pos++;
|
||||
}
|
||||
} else if ((string[read_pos] == '\033') && (read_pos < bytes - 1) && (string[read_pos + 1] == ']')) {
|
||||
/* Skip OSC - ends with BEL or ST (ESC \). */
|
||||
read_pos += 2;
|
||||
while (read_pos < bytes - 1) {
|
||||
if (string[read_pos] == '\007') {
|
||||
break;
|
||||
}
|
||||
if ((string[read_pos] == '\033') && (string[1 + read_pos] == '\\')) {
|
||||
read_pos++;
|
||||
break;
|
||||
}
|
||||
read_pos++;
|
||||
}
|
||||
} else {
|
||||
/* Don't skip, copy across. */
|
||||
raw_string[write_pos++] = string[read_pos];
|
||||
continue;
|
||||
}
|
||||
read_pos += 2;
|
||||
while ((read_pos < bytes - 1)
|
||||
&& ((string[read_pos] >= '0' && string[read_pos] <= '9')
|
||||
|| (';' == string[read_pos])
|
||||
)
|
||||
) {
|
||||
read_pos++;
|
||||
}
|
||||
}
|
||||
raw_string[write_pos] = '\0';
|
||||
|
||||
Reference in New Issue
Block a user