I've been working with C for a while and am reasonably proficient at simple command-line interfaces. I've also had a play with the curses
library, for terminal applications that do more than write text to stdout
. However, I can't figure out where the half-way point is - applications like wget
or make
have, for example, the ability to update the text they have output (like wget
's bouncing download meter and progress bar), without taking over the entire screen.
Is this sort of interface something I should be using curses
for, or is there a step in between? Preferably one that is cross-platform.
You can do some simple things by just printing backspace characters '\b'
and raw carriage returns '\r'
(without newlines). Backspaces move the cursor backwards one character, allowing you to overwrite your output, and carriage returns move the cursor back to the start of the current line, allowing you to overwrite the current line.
Here's a simple example of a progress bar:
int progress = 0;
while(progress < 100)
{
// Note the carriage return at the start of the string and the lack of a
// newline
printf("\rProgress: %d%%", progress);
fflush(stdout);
// Do some work, and compute the new progress (0-100)
progress = do_some_work();
}
printf("\nDone\n");
Note that you should only do this if you're writing to an actual terminal (as opposed to being redirected to a file or pipe). You can test for that with if(isatty(fileno(stdout)) { ... }
. Of course, the same would be true if you were using any other library such as curses or ncurses.
Between stdio
and curses
is the standard Unix/POSIX termios
library. Trivial example program that turns character echoing off and reads a line (note, no error checking whatsoever):
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
void munch_line()
{
int c;
while ((c = getchar()) != EOF && c != '\n')
;
}
int main()
{
int fd;
struct termios tio;
printf("Enter something: ");
tcgetattr(fileno(stdin), &tio);
tio.c_lflag &= ~ECHO;
tcsetattr(fileno(stdin), TCSANOW, &tio);
munch_line();
putchar('\n');
}
Don't forget to turn echoing back on before exiting the program ;)
If your terminal supports VT100 escape sequences, you can use them to move the cursor around:
printf("\x1b[%uD", n); /* move cursor n steps to the left */
printf("\x1b[%uC", n); /* move cursor n steps to the right */
printf("\x1b[K"); /* clear line from cursor to the right */
printf("\x1b[1K"); /* clear line from cursor to the left */
printf("\x1b[2K"); /* clear entire line */
A quick example (curtest.c
):
#include <stdio.h>
int main(void)
{
printf("ZZZZZZZZZZ");
printf("\x1b[%dD", 10U); /* move cursor 10 steps to the left */
printf("YYYYYYYYY");
printf("\x1b[%dD", 9U); /* move cursor 9 steps to the left */
printf("XXXXXXXX");
printf("\x1b[%dD", 2U); /* move cursor 2 steps to the left */
printf("\x1b[1K"); /* clear line from cursor to the left */
printf("\r\n");
return 0;
}
If your terminal supports these escape codes, it should print
mizo@host:~/test> gcc curtest.c
mizo@host:~/test> ./a.out
XYZ
mizo@host:~/test>
The Windows command line does not have built-in VT100 support.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With