Robert
Robert

Reputation: 508

How to update a value in the terminal

There are linux terminal programs like ddrescue that updates the terminal output no matter of the current line position.

Means that currently the terminal output maybe already of some lines of commands

You run ddrescue and it updates Kb/sec current I/O position and in the latest version there is also an elapsed time updating itself. The values are updated but what it was already printed on the screen, remains there.

All of those values are updating without any screen fill made of new lines and any kind of scrolling up, as a matter of fact the terminal scroll up bar... doesn't shrink :-)

The question is ANSI C specific under Linux, since ddrescue is written in C++ while I'd like to be able to output in a similar manner but in C.

IF you have any sample code that will compile with gcc linux, thank you (e.g. a clock that will appear on the current line and updates itself on that exact position)

Upvotes: 3

Views: 1230

Answers (2)

David C. Rankin
David C. Rankin

Reputation: 84559

Here is a quick hack showing use of ANSI escapes to produce a wget progress bar like behavior. Additional constraint checking an adjustments can be made, but this is simply a bare bones example. Repeated calls to progress produces a progress bar given the current value n out of a total t in a terminal width sz (currently limited to 70 chars). When complete a call to progress_clear cleans up any characters that remain on the line. (or just print a newline to leave the completed meter on the line)

While the availability of escape code is terminal dependent, of the choices, the \033[s (save cursor position) and \033[u (restore saved position) are fairly universal among terminals that support escapes.

There are many ways to handle the formatting from printf fieldwidths or padding to memcpy to fill the char arrays. This just illustrates the principal of controlling output on a line with escapes to provide the meter:

#include <stdio.h>
#include <unistd.h>     /* for usleep */

#define MAXPM 120

/* progress meter called with current increment 'n' of a 
total number of increments 't' fit into a maximum terminal
width of 'sz'. (< MAXPM) It should be called and updated
without intervening output to stdout.
*/
static inline void progress (int n, int t, int sz)
{
    int pct =  (n * 100) / t;
    char mkr[] = { [0 ... MAXPM] = '=', [MAXPM+1] = 0 };
    char spc[] = { [0 ... MAXPM] = ' ', [MAXPM+1] = 0 };
    int nmkr = (pct * sz) / 100;
    int nspc = sz - nmkr;

    mkr[nmkr] = 0;      /* terminate at appropriate size */
    spc[nspc] = 0;

    printf ("\033[s\033[u %3d %% [%s%s]\033[u", pct, mkr, spc);
    fflush (stdout);    /* required due to no newline   */
}

/* simple clear to end of line - if desired, otherwise, just print newline */
void progress_clear ()
{ printf ("\033[K"); fflush (stdout); }

int main () {

    int i = 0;

    for (i = 0; i <= 50; i++) {
        /* do something that returns progress */
        progress (i, 50, 70);
        usleep (100000);
    }
    // progress_clear ();  /* clean up - remove meter (if wanted) */
    printf ("\n");

    return 0;
}

Example (with newlines added):

$ ./bin/progbar
   0 % [                                                                      ]
   2 % [=                                                                     ]
   4 % [==                                                                    ]
   6 % [====                                                                  ]
(snip)
  94 % [=================================================================     ]
  96 % [===================================================================   ]
  98 % [====================================================================  ]
 100 % [======================================================================]

Upvotes: 1

Andras
Andras

Reputation: 3055

terminals look for escape sequences (certain byte patterns) that support color, cursor positioning, bolding, etc. Programs hook into those to update the screen without scrolling

It's not C specific, it's built into the terminal (the window terminal emulator). Anything that sends output to the terminal can do it. Chech out the ncurses library; ncuses-base and ncurses-doc

As an example, here's a tcsh shell command that will set your window title to WINDOW:

echo -n "\e]0;WINDOW\a"

where \e is the ESC character (^[, 0x1B) and \a is the BEL (alarm, ^G, 0x07)

Edit: ok, that works in tcsh but not in sh or bash.

Upvotes: 1

Related Questions