How to make console output fixed in place - c

My LAME (v3.99.5) outputs progress in console by moving up x lines in the console and overwriting the previous lines. It's pretty cool.
I've read in a different post that such behavior for a single line can be achieved with a mere "\r" instead of "\n" - although the post was for Ruby, it seems to be the same for C on my system at least:
#include <stdio.h>
#include <time.h>
int main() {
time_t t;
time_t t2;
time(&t);
t2 = t;
printf("%u\r", (unsigned int)t);
fflush(stdout);
while (1) {
if (t2 - t > 0) {
time(&t);
printf("%u\r", (unsigned int)t);
fflush(stdout);
}
time(&t2);
}
return 0;
}
The post further suggests a curses library can be used to make the same behavior multi-line.
What would be a boilerplate example of such code in C?

According to http://falsinsoft.blogspot.com/2014/05/set-console-cursor-position-in-windows.html
Windows:
void SetCursorPos(int XPos, int YPos)
{
COORD Coord;
Coord.X = XPos;
Coord.Y = YPos;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Coord);
}
Linux:
void SetCursorPos(int XPos, int YPos)
{
printf("\033[%d;%dH", YPos+1, XPos+1);
}

You can use something like this:
/!\ Warning: If you stop your curses program without call endwin() before, the terminal used to launch the program will have a very strange behavior.
include <curses.h>
int main(int argc, char *argv[])
{
// init curses
Xinitscr(argc, argv);
// init time
time_t t = 0, t2;
time(&t2);
// main loop
while (1) {
if (t2 - t > 0)
{
time(&t);
clear();
mvprintw(1,1, "%u", (unsigned int)t);
refresh();
}
time(&t2);
}
// end curses mode
// warning: if you do not call this at the end of your program,
// your terminal won't be usable.
endwin();
return 0;
}

The example shown by #purplepsycho has some issues, addressed in this revision (works with "any" X/Open Curses implementation):
#include <curses.h>
int main(int argc, char *argv[])
{
filter();
initscr();
// init time
time_t t = 0, t2;
time(&t2);
// main loop
while (1) {
if (t2 - t > 0)
{
time(&t);
erase();
mvprintw(1,1, "%u", (unsigned int)t);
refresh();
}
time(&t2);
}
endwin();
return 0;
}
That is:
use initscr for initializing curses (PDCurses provides a non-standard function Xinitscr which is not what OP had in mind: it certainly is not often used).
use erase rather than clear (to avoid screen-flicker):
The clear and wclear routines are like erase and werase,
but they also call clearok, so that the screen is cleared
completely on the next call to wrefresh for that window
and repainted from scratch.
use filter to keep the output on a single line. If your terminal switches to the alternate screen, the screen will appear to be cleared. There is a workaround available with ncurses (see filter.c in ncurses-examples).
Better than erase() would be wclrtoeol(), called after the mvprintw.

Related

Outputed lines doesn't wrap correctlly within a terminal in Non Canonical mode

I am trying to implement a mini version of bash using Termcap capabilities, and now I am trying to read user's input and retype it in the terminal Stdout.
Every thing is working fine, but the problem occurs when trying to resize the terminal window, as you can see in the below gif, when I write a SINGLE line and then shrink the window, and expand it again the output wraps good as it should.
But when my text goes past the first terminal line (without pressing Enter), if I do the same steps as before, the output wrapping is different from what I want. What I need is that the last characters of the first line must join with the second line characters when shrinking the window, and vice versa when I expand the window the first characters of the second line join with the first line characters.
Is there a way to make the outputted lines wrap exactly the same as bash ? (joining lines with each other)
Here is my code so far, use -ltermcap flag when you want to compile it and Thanks in advance.
#include <ctype.h>
#include <termcap.h>
#include <termios.h>
# include <sys/ioctl.h>
# include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int g_screen_width = 0;
int g_cursor_colm = 0;
int g_printed = 0;
int ft_putchar(int c)
{
int len = write(1, &c, 1);
return (len);
}
int ft_isprint(int c)
{
return (c >= 32 && c < 127);
}
void update_cursor_position()
{
g_cursor_colm = g_printed % g_screen_width;
}
void update_screen_width()
{
struct winsize w;
ioctl(STDIN_FILENO, TIOCGWINSZ, &w);
g_screen_width = w.ws_col;
}
void sigwinch_handler(int signo)
{
if (signo == SIGWINCH)
{
update_screen_width();
update_cursor_position();
}
}
void move_cursor_to_colum(int col)
{
char *ch_cap;
ch_cap = tgetstr("ch", NULL);
tputs(tgoto(ch_cap, 0, col), 1, ft_putchar);
}
void move_cursor_down_vertically()
{
char *do_cap;
do_cap = tgetstr("do", NULL);
tputs(do_cap, 1, ft_putchar);
}
void move_cursor_to_next_line()
{
move_cursor_to_colum(0);
move_cursor_down_vertically();
}
void enable_raw_mode()
{
struct termios raw;
tcgetattr(STDIN_FILENO, &raw);
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
void disable_raw_mode(struct termios old_termios_state)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_termios_state);
}
int main()
{
struct termios original_termios_state;
char *term_type = getenv("TERM");
char c;
char *line;
tgetent(NULL, term_type);
tcgetattr(STDIN_FILENO, &original_termios_state);
enable_raw_mode();
update_screen_width();
signal(SIGWINCH, sigwinch_handler);
while (read(STDIN_FILENO, &c, 1))
{
if (ft_isprint(c))
{
ft_putchar(c);
g_printed++;
g_cursor_colm++;
if (g_cursor_colm == g_screen_width)
{
g_cursor_colm = 0;
move_cursor_to_next_line();
}
}
else if (c == 27) // ESC
{
disable_raw_mode(original_termios_state);
exit(0);
}
}
disable_raw_mode(original_termios_state);
return (0);
}
Your terminal remembers where there are actual line skips (the one that you do explicitly with cursor_down in move_cursor_down_vertically()), as opposed to line wraps the terminal does on its own due to its limited width.
So:
either you don't move (downwards) your cursor at all and do all your processing on only one virtual line of text, letting the terminal wrap that line according to its width (this is probably the easiest solution);
or you redraw the screen yourself in sigwinch_handler() when you receive the SIGWINCH signal.

problems utilitizing small pauses in c code using nanosleep

I am a C beginner and trying this and that.
I want to display a string letter by letter with tiny pauses in between. So my idea was a small pause using sleep or usleep after displaying each char but I read that using nanosleep in your own function makes more sense. So I put my little pauses in a function "msleep" to get microseconds pauses.
I output my string 3 times.
Once in the main(), then in a do-while-loop in a function (fancyOutput) char by char, and eventually in the same function with printf again to check, if it was handled over correctly.
My problem: I expected, that the middle output would work char by char and separated by 100/1000 seconds breaks, but what I experience is a long break before chowing any char and then a fast output if line two and three. It looks like the compiler "realized what I am planning to do and wants to modify the code to be more efficient." So all my pauses seemed to be combined in one long break.
Maybe you remeber the captions in the tv series "x files" - something like that I want to produce.
For sure there are better and more sophisticated ways to archieve what I am going to try but I want to learn and understand what is going on. Can someone help me with that?
I am using codeclocks on a debian-based distro with gcc.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int msleep(long tms);
void fancyOutput(char inputToOutput[]);
int msleep(long tms)
{
struct timespec ts;
int ret;
if (tms < 0)
{
return -1;
}
ts.tv_sec = tms / 1000;
ts.tv_nsec = (tms % 1000) * 1000000;
do
{
// printf("sleeping for %d", ret);
ret = nanosleep(&ts, &ts);
}
while (ret);
return ret;
}
void fancyOutput(char inputToOutput[])
{
int counter = 0;
do
{
printf("%c", inputToOutput[counter]);
msleep(100);
++counter;
}
while (!(inputToOutput[counter]=='\0'));
printf("\n");
printf("%s\n", inputToOutput); // only check, if string was properly handled over to function
}
char output[] = "This string shall appear char by char in the console.";
void main(void)
{
printf("%s\n", output); // only check, if string was properly set and initialized
fancyOutput(output); // here the function above is called to output the string char by cchar with tiny pauses between
}
You are getting problem with buffer.
When you use printf with no \n (new line) C is buffering the display in order to display information block by block (to optimize displaying speed).
Then you need to either add a \n to your printf or add a flush of the stdout.
An other solution will be to use stderr, which got no buffer, but stderr is meant for error not output :)
You can also check setvbuf in order to change the buffering.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int msleep(long tms);
void fancyOutput(char inputToOutput[]);
int msleep(long tms)
{
struct timespec ts;
int ret;
if (tms < 0)
{
return -1;
}
ts.tv_sec = tms / 1000;
ts.tv_nsec = (tms % 1000) * 1000000;
do
{
// printf("sleeping for %d", ret);
ret = nanosleep(&ts, &ts);
}
while (ret);
return ret;
}
void fancyOutput(char inputToOutput[])
{
int counter = 0;
do
{
printf("%c", inputToOutput[counter]);
flush(stdout);
msleep(100);
++counter;
}
while (!(inputToOutput[counter]=='\0'));
printf("\n");
printf("%s\n", inputToOutput); // only check, if string was properly handled over to function
}
char output[] = "This string shall appear char by char in the console.";
void main(void)
{
printf("%s\n", output); // only check, if string was properly set and initialized
fancyOutput(output); // here the function above is called to output the string char by cchar with tiny pauses between
}
So, I tried the solution to place fflush(stdout); directly after the char-output in the loop. It worked as intended.
Summarizing for those with similar problems (guess this also happens with usleep and similar self-made functions):
As I understaood, printf "collects" data in stdout until it "sees" \n, which indicates the end of a line. Then printf "releases" stdout. So in my initial post it "kept" each single char in stdout, made a pause after each char and finally released stdout in one fast output.
So fflush(stdout); after each char output via empties stdout char by char.
Hope it can help others.

Ncurses flickers when using pipes or redirection

Ncurses flickers when using "unix pipes" and "redirection" for input. That is, it draws fine if I input myself but doesn't when using '|' or '<'.
I thought this might be due to getch() delay modes(no delay, half delay and infinite delay). So I explicitly tried setting nodelay(stdscr, FALSE); but as obvious, it didn't solve it.
This is the minimal working code :
#include <ncurses.h>
#include <stdlib.h>
#include <string.h>
/* Default assumptions */
#define BUFSIZE 100
#define SELINDICATOR ">>> "
#define MAXITEMS LINES /* Decides how many items are shown at a time. By default, it's (number of rows - 1) */
/* Declarations */
static void draw(char **data, short index, short selected);
static void handleInput(short *selected, short index);
int main(int argc, char *argv[]) {
char buf[BUFSIZE], **data;
short index = 0, selected = 1;
size_t curSize = 0;
/* Get the entries */
while(fgets(buf, BUFSIZE, stdin)) {
if(!(data = realloc(data, (curSize += sizeof(char *))))) {
fprintf(stderr, "error reallocating memory!\n");
exit(1);
}
if(!(data[index] = malloc(BUFSIZE))) {
fprintf(stderr, "error reallocating memory!\n");
exit(1);
}
strcpy(data[index], buf);
index++;
}
/* Start nCurses */
initscr();
noecho();
nodelay(stdscr, FALSE); // just tryin' it out if it works
while(1) {
draw(data, index, selected);
handleInput(&selected, index);
}
/* Quit nCurses */
endwin();
/* Free allocated memories */
for(short i = 0; i < index; i++)
free(data[i]);
free(data);
return 0;
}
void
draw(char **data, short index, short selected) {
static short posX = strlen(SELINDICATOR), posY; /* posY doesn't need to be static but it makes no difference and looks cleaner */
/* Clear old garbage */
clear();
posY = 0;
/* Draw line echoing inputs */
mvaddch(posY, 0, '>');
posY++;
/* Draw the entries */
for(short i = 0; posY < COLS && i < index; i++) {
if(posY == selected) {
mvprintw(posY, 0, SELINDICATOR);
}
mvprintw(posY, posX, "%s", data[i]);
refresh();
posY++;
}
/* Make the output visible */
refresh();
}
void
handleInput(short *selected, short numOfEntries) {
int input = getch();
/* A whole bunch of other stuff........ */
endwin();
exit(0);
}
Much thanks for your efforts!
Ncurses is designed and built as a tool for providing an interactive user interface. To the extent that it reads input from from the standard input (as opposed to directly from the terminal), it is possible for an ncurses-based program to have its input redirected from a file or pipe, but it's unclear why it would be important to actually display the UI in that case. If doing so causes unwanted visual effects then the easiest mitigation might be to disable the UI in that case.
In the program presented in the question, it appears that displaying the UI is cleanly separated from reading and processing input, and that reading input relies only minimally on ncurses. It should be very straightforward to modify such a program to enable it to switch between UI and no-UI modes, and my recommendation is that you do so. To that end, you may find the isatty() function useful for determining whether the standard input (and / or standard output) is a terminal.
The example is missing something, since this function
void
handleInput(short *selected, short numOfEntries) {
int input = getch();
/* A whole bunch of other stuff........ */
endwin();
exit(0);
}
will simply exit after running once. That leaves a lot of possibilities, the most likely being that you're running this program a lot of times, causing it to initialize the screen (and on a lot of terminals, switching to/from the alternate screen). That'll flicker every time...

Basic Ncurses Menu

Im trying to do a basic menu in C. I'm supposed to do this with ncurses lib. I was working with this tutorial:
Video On YouTube
But mine version has some problems:
1)The menu will not print properly, it will reveal only while choosing menu items. Then the highlight won't go off
2)Option made on menu won't print on the top
Can you help me? Is that idea of Menu good or should i look for other tutorial (any help ?).
#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
int main(int argc, char **argv)
{
int i, c;
char powitanie[]="SLOWNIK UNIWERSALNY";
int szer, dlug; //wartosci dlugosci i szerokosci terminalu
initscr(); //Inizjalizacja całości ncurses, kolory itp
raw();
noecho();
keypad(stdscr, TRUE);
start_color();
//init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
//attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
printw(powitanie); //Drukujemy powitanie
//attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
refresh();//Odswiezamy (inaczej się nie wyswietli)
WINDOW * menuwin=newwin(6, dlug-12, szer-8, 6); //Definiujemy i tworzymy 'okno'
box(menuwin, 0, 0);
refresh();//ponownie odświeżamy aby okno się pojawiło
wrefresh(menuwin);//odświeżamy samo okno
keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
char *opcje[] = {
"Tlumacz z Polskiego na Angielski",
"Tlumacz z Angielskiego na Polski",
"Edystuj slownik",
"Wybierz slownik",
"Wyjdz",
};
int wybor;
int zaznacz=0;
while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
{
for(i=0; i<5; i++)
{
if(i==zaznacz)
{
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, opcje[i]);
wattroff(menuwin, A_REVERSE);
}
wybor = wgetch(menuwin);
switch(wybor)
{
case KEY_UP:
zaznacz--;
if(zaznacz==-1) zaznacz=0;//zabezpieczenie przed wyjsciem "poza" menu
break;
case KEY_DOWN:
zaznacz++;
if(zaznacz==5) zaznacz=4;
break;
default:
break;
}
if(wybor==10) break;
}
printw("Wybrano:%s", opcje[zaznacz]);
}
return(0);
}
PS: Code comments are not in English but i hope the won't be necessary
There are quite a few problems here. I have included a modified version of your code that works, and I will attempt to describe the changes.
There were some unused variables, namely argc, argv, and c, so I cast these to void in order to silence compiler warnings. You can remove the c and change to int main(void), if you like, removing these variables altogether.
I have added the stdlib.h header file to your #includes for the exit() function. This is used in the new error function, fail(), that I added to your code. You should always check the return values of any function that you call when programming in C. Here it is particularly important to check, first if the terminal supports color with the has_colors() function, and then if the call to start_color() is successful. If either of these fail, the fail() function is called with an error message, and the program exits with the EXIT_FAILURE value. The function has_colors() returns a bool, and the start_color() function returns an int (OK if successful, otherwise ERR).
Now that colors have been initialized, I see that the lower border of your menu selection window is being overwritten by the menu text. To fix this, I changed the size of your window, making it one line taller:
WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6);
The fundamental problem of improper printing that you reported was because of a misplaced brace in the for loop controlling the printing of the menu items. I took the opportunity to reorganize the loop a bit; now there is only one call to mvwprintw(). The A_REVERSE attribute is set before printing if the current item is also the selected item, and it is again unset after printing.
I also changed the limit tests in the switch statement from equalities to inequalites. It is better practice to use , e.g., if (zaznacz < 0) instead of if (zaznacz == -1) in such cases.
I added a newline character to the beginning of the format string in the final printw(), since some of the selections are too long to fit in the window at the end of the title. You can move this output wherever you like.
Finally, I added a refresh() after the final printw() statement, and a getch() to wait for the user to hit ENTER before exiting the program. It is very important to cleanup by calling endwin() before exiting an NCurses program. This function reverses changes made to your terminal by NCurses while your program was running, and failure to do this can lead to terminal unpleasantries.
#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
#include <stdlib.h> // added for exit() function
void fail(char *msg) {
endwin();
puts(msg);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
/* Commandline argument currently unused */
(void) argc;
(void) argv;
int i, c;
(void) c; // c is currently unused
char powitanie[]="SLOWNIK UNIWERSALNY";
int szer, dlug; //wartosci dlugosci i szerokosci terminalu
initscr(); //Inizjalizacja całości ncurses, kolory itp
raw();
noecho();
keypad(stdscr, TRUE);
/* Test to see if terminal has colors */
if (has_colors() == false) {
fail("Colors unavailable\n");
}
if (start_color() != OK) {
fail("Unable to start colors\n");
}
//init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
//attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
printw(powitanie); //Drukujemy powitanie
//attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
refresh();//Odswiezamy (inaczej się nie wyswietli)
WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6); //Definiujemy i tworzymy 'okno'
box(menuwin, 0, 0);
refresh();//ponownie odświeżamy aby okno się pojawiło
wrefresh(menuwin);//odświeżamy samo okno
keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
char *opcje[] = {
"Tlumacz z Polskiego na Angielski",
"Tlumacz z Angielskiego na Polski",
"Edystuj slownik",
"Wybierz slownik",
"Wyjdz",
};
int wybor;
int zaznacz=0;
while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
{
for(i = 0; i < 5; i++) {
if(i == zaznacz)
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, opcje[i]);
if (i == zaznacz)
wattroff(menuwin, A_REVERSE);
}
wybor = wgetch(menuwin);
switch(wybor)
{
case KEY_UP:
zaznacz--;
if(zaznacz < 0) zaznacz = 0;//zabezpieczenie przed wyjsciem "poza" menu
break;
case KEY_DOWN:
zaznacz++;
if(zaznacz > 4) zaznacz = 4;
break;
default:
break;
}
if(wybor==10) break;
}
printw("\nWybrano:%s", opcje[zaznacz]);
refresh();
/* Wait for user to press enter to exit */
getch();
/* Need to cleanup before exit */
endwin();
return 0;
}

C sleep method obstructs output to console

I have a C program, where I just wanted to test if I could reproduce a console spinner used in npm install while it installs a module. This particular spinner simply spins in this order:
|
/
-
\
on the same space, so I use the following program:
#include <stdio.h>
int main() {
char sequence[4] = "|/-\\";
while(1) {
for(int i = 0; i < 4; i++) {
// \b is to make the character print to the same space
printf("\b%c", sequence[i]);
// now I want to delay here ~0.25s
}
}
}
So I found a way to make it rest for that long from <time.h> documentation and made this program:
#include <stdio.h>
#include <time.h>
void sleep(double seconds) {
clock_t then;
then = clock();
while(((double)(clock() - then) / CLOCKS_PER_SEC) < seconds); //do nothing
}
int main() {
char sequence[4] = "|/-\\";
while(1) {
for(int i = 0; i < 4; i++) {
printf("\b%c", sequence[i]);
sleep(0.25);
}
}
}
But now nothing prints to the console. Does anyone know how I can go about producing the behavior I want?
EDIT According to what appears to be popular opinion, I've updated my code above to be the following:
#include <stdio.h>
#include <unistd.h>
int main() {
char sequence[4] = "|/-\\";
while(1) {
for(int i = 0; i < 4; i++) {
printf("\b%c", sequence[i]);
/* fflush(stdout); */
// commented out to show same behavior as program above
usleep(250000); // 250000 microseconds = 0.25 seconds
}
}
}
You will need to flush after you wrote to the console. Otherwise, the program will buffer your output:
fflush(stdout);
Things do get printed to console, it's just does not get flushed. Add fflush(stdout) to see the results, or set the console in an unbuffered mode by calling setbuf:
setbuf(stdout, NULL);
A bigger problem with your code is that your sleep method runs a busy loop, which burns CPU cycles for no good reason. A better alternative would be to call usleep, which takes the number of microseconds:
usleep(25000);
The sleep function isn't really your problem. The issue is that the output is buffered. The simplest thing to do will be to research ncurses.
For now:
fflush(stdout);

Resources