I'm trying to write a program to that acts as a marquee that uses the curses.h library to create a side-scrolling display.
What should happen is that my message "Hello" should appear to scroll from the right side of the terminal to the left, character by character.
"hello" should appear to scroll across the terminal like so:
| H| // fist frame of animation
| He| //2nd
| Hel| //3rd
...
| Hello | // some time in the middle of animation
|Hello | // finished.
Instead of appearing to scroll across the terminal my program simply outputs the "Hello" message on the left side of the terminal as if it is finished.
I thought that printing the appropriate number of spaces then the appropriate number of characters of the string each frame would work.
What am I doing wrong?
Below is my code so far:
#include <curses.h>
#include <string.h>
main()
{
char message[] = "Hello";
int max_y, max_x; // max dimensions of terminal window
int text_length;
int i,row=0,col=0,spaces=0;
// Get text length
text_length = strlen(message);
// Get terminal dimensions
getmaxyx(stdscr, max_y, max_x);
// num of spaces needed to print
spaces = max_x -1;
initscr(); // initialize curses
clear(); // clear screen to begin
while(1)
{
clear(); // clear last drawn iteration
move(5,col);
// print spaces as necessary
for(i=0;i<spaces;i++)
{
addch(' ');
}
refresh();
// print appropriate number of characters of the message
for(i=0;i<text_length || i<max_x; i++)
{
addch(message[i]);
}
refresh();
usleep(50000); // wait some time
spaces = spaces-1; //adjust spaces need for next iteration
}
}
The first problem is that you call getmaxyx() before initscr(). In this situation, stdscr has not been initialized, so the values returned by getmaxyx() are meaningless. (I get -1 for each value, aka ERR.)
That fixed, the program basically works, but prints junk after the "Hello" string. You can solve that by changing the for loop test, text_length || i<max_x, to text_length && i<max_x, although the result is still probably not quite what you want. But I'll leave it to you to figure that one out.
Finally, as a stylistic matter, I'd suggest using curses' own napms() function instead of usleep() (i.e., napms(50) instead of usleep(50000)). But if you do stick with usleep(), you should add #include <unistd.h> at the top.
Related
so here is the thing I am trying to make something like 1d particle simulator which could simulate two particle but the problem is it takes time proccess and in that time I want to show the user something like loading... and then the loading vanishes and and answer is shown so I wanted to know if is it possible to delete something which already in the output part if so please tell how.
I am pretty knew to this platform so if I did any mistake please tell.
You can use the following code. Example Command-line progress bar:
// C program to create loading bar
#include <stdio.h>
#include <windows.h>
// Function to creating loading bar
void loadingBar()
{
// 0 - black background,
// A - Green Foreground
system("color 0A");
// Initialize char for printing
// loading bar
char a = 177, b = 219;
printf("\n\n\n\n");
printf("\n\n\n\n\t\t\t\t\t"
+ "Loading...\n\n");
printf("\t\t\t\t\t");
// Print initial loading bar
for (int i = 0; i < 26; i++)
printf("%c", a);
// Set the cursor again starting
// point of loading bar
printf("\r");
printf("\t\t\t\t\t");
// Print loading bar progress
for (int i = 0; i < 26; i++) {
printf("%c", b);
// Sleep for 1 second
Sleep(1000);
}
}
// Driver Code
int main()
{
// Function Call
loadingBar();
return 0;
}
Before I make one of these posts, I look around at around 5-10 other forums to see if my question has been answered.
There are a lot of websites that explain that my compiler doesn't have blinking enabled, and that I just need to download the package to enable it, or something to that affect
However, of all the ones that I have seen, none of them go into detail about where and how to acquire the package I need to allow blinking, or if they do then it isn't with my compiler.
So if someone could assist me, how do I enable blinking on Ubuntu with a function such as
attron(A_BLINK);
I'm aware that similarly phrased questions will get down-votes. I do not care, I just want to know how to fix my problem.
Any feedback would be really appreciated
-Edit
#include <string.h>
#include <ncurses.h>
int main(void)
{
char text[] = "Please Blink";
size_t len = strlen(text);
int i, row, col;
initscr();
getmaxyx(stdscr, row,col);
keypad(stdscr, TRUE);
noecho();
curs_set(0);
move((row / 2), (col / 2) - (len / 2));
attron(A_BLINK);
for(i = 0; i < len; ++i)
{
printw("%c", text[i]);
}
refresh();
getch();
attroff(A_BLINK);
endwin();
return 0;
}
-Ryan
ansi escape sequences
as an example of usage:
from the terminal type:
echo -e "\x1b[5;32;43m"
this results in a blinking green foreground over a yellow background
then type:
echo "test"
to display the word test as green letters over a yellow background
then, if the screen has not returned to normal operation, type:
echo -e "\1b[0m"
to reset the screen to normal
Note: the same ansi escape sequences can be output using the printf() function via:
printf( "%s\n", "\x1b[5;32;43m" );
and so on.
Blinking is a property of your terminal emulator. If your terminal does not support it, it won't happen.
FWIW, a plain xterm window does support blinking for me.
However, if you want the text to blink, you can manually make it happen by periodically overwriting the text with blanks. Something along the lines of:
int toggle = 0;
halfdelay(5);
do {
toggle = !toggle;
move((row / 2), (col / 2) - (len / 2));
printw("%*.*s", len, len, toggle ? text : "");
refresh();
} while (getch() == ERR);
nocbreak();
The halfdelay() call will cause getch() to return after 5/10ths of a second. If no key was hit within that time, ERR is returned. The nocbreak() call will disable halfdelay().
It doesn't print at coordinates y=10, x=20.
#include <stdio.h>
#include <curses.h>
int main()
{
initscr();
refresh();
WINDOW *win;
wmove(win, 10, 20);
refresh();
printf("hi\n");
return 0;
}
When I execute it like this...
./a.out > op_file
This is what is op_file
[?1049h[1;24r(B[m[4l[?7h[H[2J-1
hi
Can someone explain...??
You must use initscr() function to initialize the screen and endwin() at the end to close the window...
If you move(), you must use refresh() or the cursor won't move physically.
To move the cursor to a new position on a window, use the function int wmove(WINDOW *win, int y, int x)
wmove(win, y, x);
where (x, y) are the coordinates of the new position in the window. If the window has nlines lines and ncolumns columns, then
0 <= y < nlines
0 <= x < ncolumns
Refresh. The actual cursor motion is not shown on the screen untill you do a wrefresh(win).
move(y, x) is equivalent to the wmove(stdscr, y, x).`
The move() and wmove() functions move the cursor associated with the current or specified window to (y, x) relative to the window's origin. This function does not move the terminal's cursor until the next refresh operation.
To move the logical cursor in the user-defined window my_window to the coordinates y = 5, x = 10, use :
#include <stdio.h>
#include <curses.h>
int main(){
refresh();//First refresh
WINDOW *my_window;
int a = wmove(my_window, 5, 10);
refresh();////Second refresh
printf("%d\n",a);
printf("hi\n");
return 0;
}
The output
[?1049h[1;24r(B[m[4l[?7h[H[2J-1
hi
shows the printable characters written. If you look at the complete text, e.g., in a text-editor, there'll be ASCII escape characters before the [ and ( characters since that's part of the escape sequence.
Your example doesn't show cursor movement (aside from the home position which you'd see as ^[[H near the end) because there's no reason for the curses library to actually move the cursor. If you had asked it to read a character, e.g., using getch, it would have to stop and decide where the cursor should be — and your wmove would do that — except that win is not initialized. The simplest thing to do is use stdscr (which is initialized by initscr).
The program quits curses calls without doing an endwin (which leaves the terminal in raw mode). The data does get written to the screen with the refresh call. The data written with printf happens to come out in the right order, but that's only coincidental since it does not use the same output buffering as ncurses.
Both of the other answers contain similar errors.
This works.
#include <stdio.h>
#include <curses.h>
int main()
{
initscr();
refresh();
WINDOW *win;
win = stdscr;
wmove(win, 10, 10);
refresh();
printf("hi\n");
return 0;
}
Thanks to #interjay.
CODE
while (1)
{
keycode = key_hook();
if (keycode == SPACE || keycode == BKSPACE)
{
render_again = 1;
}
if (keycode == ESC)
break;
if (render_again)
{
render_again = 0;
render(all);
}
dprintf(1, ""); //I have no idea why this prevents the program from freezing
}
int key_hook()
{
char buffer[4];
read(0, buffer, 4);
return (*(unsigned int *)buffer);
}
Alright, so this piece of code handles redrawing of text on screen. Some rows of text are underlined or highlighted using termcaps (tputs(tgetstr("us, NULL")......). Everything prints fine but after the first redraw of the text the while apparently freezes unless a dprintf/printf is present. The key_hook function just reads 4 bytes from the stdin and converts them to an int.
When I last did work here, my version of key_hook had a loop of single byte reads. This was broken by an alarm of 1 second and logic to whether the data so far was a key prefix.
The alarm interrupted the read, and stopped freeze
I was writing a command line program which will have a status bar, much like wget.
The main problem I'm facing is: how do I delete what I've already sent into stdout/stderr?
I had on idea: use the backspace char '\b' and erase the output I've sent. Is that the best way? Is it the only way? Is there a better way?
PS: I don't want to use anything like ncurses. Plain old C please.
Thanks
EDIT:
Can I also go up and/or down? Example: I have 10 lines of output, I want to change the 3rd line from Doing ABC to ABC: Done. How can I do that?
Also, can anyone post more details about what VT102 characters are? What are its capabilities? Please post good links on this if you have any.
Thanks
The basic formatting control characters are backspace (\b), tab (\t), newline (\n), and carriage return (\r). If you need more than that then you can use ANSI X3.64 / ISO/IEC 6429 / ECMA-48 escape sequences; at least the VT100 subset is recognized by most modern terminals and emulators. An advantage of using ncurses is that it will look up the capabilities of your particular terminal and so it will work even if your terminal uses a different set of escape sequences.
You have to remember that as far as the regular stdio routines are concerned, stdout is just a byte stream with no inherent display characteristics; that depends on the target device, which can be anything from a regular VT100-style terminal to a hardcopy terminal to a sheet-fed printer to a plotter to whatever.
IMO, you're far better off using a library like ncurses than trying to hack together your own display management code with VT100 escape codes, even for a relatively simple task like this. I know you want to stick with "plain old C", but this is a task that falls outside the bounds of plain old C.
Use '\r' to return to the beginning of the line and possibly rewrite the whole line.
Look for VT102 control sequences - these are character sequences ESC ... to control your terminal.
There is also the possiblity of using Ncurses, which is a library for Textual UI, where this kind of behaviour should have some support. However, it may be overkill for something like this.
A slight variation on your own solution:
You can also print a carriage return (\r), which will return you to the start of the line.
It is a progressbar for bash.
function gauge()
{
progress="$1"
total="$2"
width=`tput cols`
let gwidth=width-7
if [ "$total" == "0" ]; then
percent=100
else
set +e
let percent=progress*100/total;
set -e
fi
set +e
let fillcount=percent*gwidth/100
let nofillcount=gwidth-fillcount
set -e
fill="";
if [ "$fillcount" -gt "0" ]; then
for i in `seq $fillcount`; do
fill="$fill""|"
done
fi;
nofill=""
if [ "$nofillcount" -gt "0" ]; then
for i in `seq $nofillcount`; do
nofill="$nofill"" ";
done
fi
echo -e -n "\r[""$fill""$nofill""] ""$percent""%";
}
About the progress bar: something like this?
#include <stdio.h>
#include <unistd.h>
typedef enum
{
false=0,
true=!false
} bool;
typedef struct
{
/* Start delimiter (e.g. [ )*/
char StartDelimiter;
/* End Delimiter (e.g. ] )*/
char EndDelimiter;
/* Central block (e.g. = )*/
char Block;
/* Last block (e.g. > ) */
char CurBlock;
/* Width of the progress bar (in characters) */
unsigned int Width;
/* Maximum value of the progress bar */
double Max;
/* True if we have to print also the percentage of the operation */
bool PrintPercentage;
/* True if the bar must be redrawn;
note that this must be just set to false before the first call, the function then will change it by itself. */
bool Update;
} ProgressBarSettings;
/* Prints/updates the progress bar */
void PrintProgressBar(double Pos, ProgressBarSettings * Settings);
/* Inits the settings of the progress bar to the default values */
void DefaultProgressBar(ProgressBarSettings * Settings);
int main()
{
int i;
/* Init the bar settings */
ProgressBarSettings pbs;
DefaultProgressBar(&pbs);
pbs.Max=200;
pbs.Width=60;
printf("Progress: ");
/* Show the empty bar */
PrintProgressBar(0,&pbs);
for(i=0;i<=pbs.Max;i++)
{
/* Wait 50 msec */
usleep(50000);
/* Update the progress bar */
PrintProgressBar(i,&pbs);
}
puts(" Done");
return 0;
}
/* Inits the settings of the progress bar to the default values */
void DefaultProgressBar(ProgressBarSettings * Settings)
{
Settings->StartDelimiter='[';
Settings->EndDelimiter=']';
Settings->Block='=';
Settings->CurBlock='>';
Settings->PrintPercentage=true;
Settings->Update=false;
Settings->Max=100;
Settings->Width=40;
}
/* Prints/updates the progress bar */
void PrintProgressBar(double Pos, ProgressBarSettings * Settings)
{
/* Blocks to print */
unsigned int printBlocks=(unsigned int)(Settings->Width*Pos/Settings->Max);
/* Counter */
unsigned int counter;
/* If we are updating an existing bar...*/
if(Settings->Update)
{
/* ... we get back to its first character to rewrite it... */
for(counter=Settings->Width+2+(Settings->PrintPercentage?5:0);counter;counter--)
putchar('\b');
}
else
Settings->Update=true; /* next time we'll be updating it */
/* Print the first delimiter */
putchar(Settings->StartDelimiter);
/* Reset the counter */
counter=Settings->Width;
/* Print all the blocks except the last; in the meantime, we decrement the counter, so in the end we'll have
the number of spaces to fill the bar */
for(;printBlocks>1;printBlocks--,counter--)
putchar(Settings->Block);
/* Print the last block; if the operation ended, use the normal block, otherwise the one for the last block */
putchar((Settings->Max==Pos)?Settings->Block:Settings->CurBlock);
/* Another block was printed, decrement the counter */
counter--;
/* Fill the rest of the bar with spaces */
for(;counter;counter--)
putchar(' ');
/* Print the end delimiter */
putchar(Settings->EndDelimiter);
/* If asked, print also the percentage */
if(Settings->PrintPercentage)
printf(" %3d%%",(int)(100*Pos/Settings->Max));
/* Flush the output buffer */
fflush(stdout);
};
Note: the unistd.h and usleep thing is just to fake the progress of an operation, the progress bar code itself just uses the standard library. Its only assumptions about the output stream are that \b actually gets to the previous written character. I tried it successfully on Windows and Linux (with gnome-terminal), don't know if it doesn't work correctly with some terminal emulators.
Sorry for the excessive amount of comments, I wrote it for another forum where I needed to explain pratically every line of the code to a C newbie.