I'm currently designing a CLI interface for linux, and for various reasons I am not able to use ncurses. I am using exclusively C++ and the Qt framework.
Therefore, in order to have a user-friendly interface, I have to run this getch loop in a separate thread:
https://stackoverflow.com/a/912796/3605689
Which basically means I have to implement all basic functionalities (such as backspace) by myself. I have already implemented command completion and command history(like when you press tab or uparrow/downarrow in linux), but I can't figure out how to implement leftarrow/rightarrow (aka seeking through the typeahead).
Normally, I implement it like this: upon every gech which is not equal to -1, I check whether the user has pressed a special key (one that modifies the typeahead somehow). I then clear the stdout using the following function:
void inputobject::clear_line(int nletters)
{
QTextStream(stdout) << "\033[2K";
for(int i = 0; i < nletters;i++){
QTextStream(stdout) << "\b";
}
rewind(stdout);
}
And replace it with something else, effectively simulating the typeahead. For example, in the case of backspace, I would save the command call clear_line, and print the command out again, just with one less letter, behaving exactly as a normal console application would.
My real problem is with the cursor, in the case of left/rightarrow, I need to move the cursor visual in order to be able to indicate where in the text is the user seeking:
Because of the nature of how I rewrite the given stdout line to simulate the typeahead, it does not really matter where the cursor REALLY is, as long as it stays on the same line - it is just the visual that matters. How can I achieve moving the cursor visual on linux?
The answer was provided in the comment by Evilruff:
Cursor Movement
ANSI escape sequences allow you to move the cursor around the screen at will. This is more useful for full screen user interfaces generated by shell scripts, but can also be used in prompts. The movement escape sequences are as follows:
Position the Cursor:
\033[;H
Or
\033[L;Cf
puts the cursor at line L and column C.
Move the cursor up N lines:
\033[NA
Move the cursor down N lines:
\033[NB
Move the cursor forward N columns:
\033[NC
Move the cursor backward N columns:
\033[ND
Clear the screen, move to (0,0):
\033[2J
Erase to end of line:
\033[K
Save cursor position:
\033[s
Restore cursor position:
\033[u
Not using ncurses and co is a serious limitation.
It is hell to make correct input/output on shell for displaying anything.
The only others real solutions (I can't think as a solution to reimplement a ncurse-like library) I think of are:
making call to dialog (for some example www.linuxjournal.com/article/2807 and for the doc: http://linux.die.net/man/1/dialog)
using the framebuffer mecanism with Qt4 (here)
Related
I'm currently designing a CLI interface for linux, and for various reasons I am not able to use ncurses. I am using exclusively C++ and the Qt framework.
Therefore, in order to have a user-friendly interface, I have to run this getch loop in a separate thread:
https://stackoverflow.com/a/912796/3605689
Which basically means I have to implement all basic functionalities (such as backspace) by myself. I have already implemented command completion and command history(like when you press tab or uparrow/downarrow in linux), but I can't figure out how to implement leftarrow/rightarrow (aka seeking through the typeahead).
Normally, I implement it like this: upon every gech which is not equal to -1, I check whether the user has pressed a special key (one that modifies the typeahead somehow). I then clear the stdout using the following function:
void inputobject::clear_line(int nletters)
{
QTextStream(stdout) << "\033[2K";
for(int i = 0; i < nletters;i++){
QTextStream(stdout) << "\b";
}
rewind(stdout);
}
And replace it with something else, effectively simulating the typeahead. For example, in the case of backspace, I would save the command call clear_line, and print the command out again, just with one less letter, behaving exactly as a normal console application would.
My real problem is with the cursor, in the case of left/rightarrow, I need to move the cursor visual in order to be able to indicate where in the text is the user seeking:
Because of the nature of how I rewrite the given stdout line to simulate the typeahead, it does not really matter where the cursor REALLY is, as long as it stays on the same line - it is just the visual that matters. How can I achieve moving the cursor visual on linux?
The answer was provided in the comment by Evilruff:
Cursor Movement
ANSI escape sequences allow you to move the cursor around the screen at will. This is more useful for full screen user interfaces generated by shell scripts, but can also be used in prompts. The movement escape sequences are as follows:
Position the Cursor:
\033[;H
Or
\033[L;Cf
puts the cursor at line L and column C.
Move the cursor up N lines:
\033[NA
Move the cursor down N lines:
\033[NB
Move the cursor forward N columns:
\033[NC
Move the cursor backward N columns:
\033[ND
Clear the screen, move to (0,0):
\033[2J
Erase to end of line:
\033[K
Save cursor position:
\033[s
Restore cursor position:
\033[u
Not using ncurses and co is a serious limitation.
It is hell to make correct input/output on shell for displaying anything.
The only others real solutions (I can't think as a solution to reimplement a ncurse-like library) I think of are:
making call to dialog (for some example www.linuxjournal.com/article/2807 and for the doc: http://linux.die.net/man/1/dialog)
using the framebuffer mecanism with Qt4 (here)
I'm studing now and I got this homework / tasks to do:
1) If you press the CTRL + L key, all numeric symbols should change the color.
2) If you press the CTRL + S key, you will get the length of the word, left from the cursor.
I found this function int bioskey(int cmd);
So now I can check if the key is pressed, but how to change the color only of numeric symbols, or read words from console to get their length ?
Some of us still remember the MS-DOS (let it rest in peace or pieces...)
if you are really in MS-DOS then you can not expect that the content of the console would be changed in colors for only specific areas. You need to do that your self. The problem is we do not know anything about your project background so we do not know what and how yours stuff is represented,rendered/outputed/inputed etc...
I assume EGA/VGA BIOS text mode is used so you can exploit direct access to the VRAM. So you need to set pointer to the address B800:0000 and handle it as array where each character on screen has 2 BYTEs. one is color attribute and the other is ASCII code (not sure in which order anymore)...
So for already rendered stuff you just:
loop through whole screen
usually 80x25x2 Bytes
test each ASCII for alpha numeric value
so ASCII code >= '0' and code<='9' for numbers or add all the stuff you are considering as alphanumeric like code>' ' and code<='9'.
change colors for selected characters
just by changing the attribute byte.
When you put it together for numbers it will look like this:
char far *scr=(char far*)0x0B0000000;
int x,y,a;
for (a=0,y=0;y<25;y++)
for (x=0;x<80;x++,a+=2)
if ((scr[a+0]>='0')&&((scr[a+0]<='9'))
{
scr[a+1]=7; //attribute with the different color here
}
if it does not work than try swap scr[a+0] and scr[a+1]. If an exception occur then you are not in MS-DOS and you do not have access to VRAM. In that case use DOS-BOX or driver that allows access to memory like dllportio ...
For more info see some more or less related QA's:
Display an array of color in C
What is the best way to move an object on the screen?
If you got problem with the CTRL+Key detection not sure if in-build function in TC++ allows CTRL (was too long ago) then you can exploit BIOS or even hook up the keyboard ISR. See the second link where ISR for keyboard handler is there present... You can port it to C++ or google there must be a lot of examples out there especially TP7.0 (which is pascal but easily portable to TC++)
I use the following lines to output my simulation's progress info in my c++ program,
double N=0;
double percent=0;
double total = 1000000;
for (int i; i<total; ++i)
{
percent = 100*i/total;
printf("\r[%6.4f%%]",percent);
}
It works fine!
But the problem is I see the terminal cursor keeps blinking cyclically through the numbers, this is very annoying, anyone knows how to get rid of this?
I've seen some programs like wget or ubuntu apt, they use progress bar or percentages too, but they seems no blinking cursor issue, I am wondering how did they do that?
Thanks!
You can hide and show the cursor using the DECTCEM (DEC text cursor enable mode) mode in DECSM and DECRM:
fputs("\e[?25l", stdout); /* hide the cursor */
fputs("\e[?25h", stdout); /* show the cursor */
Just a guess: try to use a proper number of '\b' (backspace) characters instead of '\r'.
== EDIT ==
I'm not a Linux shell wizard, but this may work:
system("setterm -cursor off");
// ...display percentages...
system("setterm -cursor on");
Don't forget to #include <cstdlib> or <iostream>.
One way to avoid a blinking cursor is (as suggested) to hide the cursor temporarily.
However, that is only part of the solution. Your program should also take this into account:
after hiding the cursor and modifying the screen, before showing the cursor again move it back to the original location.
hiding/showing the cursor only keeps the cursor from noticeably blinking when your updates take only a small amount of time. If you happened to mix this with some time-consuming process, your cursor will blink.
The suggested solution using setterm is not portable; it is specific to the Linux console. And running an executable using system is not really necessary. But even running
system("tput civis");
...
system("tput cnorm");
is an improvement over using setterm.
Checking the source-code for wget doesn't find any cursor-hiding escape sequences. What you're seeing with its progress bar is that it leaves the cursor in roughly the same place whenever it does something time-consuming. The output to the terminal takes so little time that you do not notice the momentary rewrite of the line (by printing a carriage return, then writing most of the line over again). If it were slower, then hiding the cursor would help — up to a point.
By the way — this cursor-hiding technique is used in the terminal drivers for some editors (vim and vile).
Those apps are probably using ncurses. See mvaddstr
The reason the cursor jumps around is because stdout is buffered, so you don't know actually how many characters are being printed at some point in time. The reason wget does not have a jumping cursor is that they are actually printing to stderr instead, which is unbuffered. Try the following:
fprintf(stderr, "\r[%6.4f%%]", percent);
This also has the advantage of not cluttering the file if you are saving the rest of the output somewhere using a pipe like:
$ ./executable > log.data
Press insert key...if that doesn't work then press the fn key in your keyboard.
This will definitely work
Hope this helps
Is it possible to set how many lines the console will print before it starts to erase the top ones? For example, is it possible to set it to 3 and only make the last 3 lines be visible? So:
std::cout<<"line 1!"<<std::endl;
std::cout<<"line 2!"<<std::endl;
std::cout<<"line 3!"<<std::endl;
std::cout<<"line 4!"<<std::endl;
system("pause");
Would output:
line 3!
line 4!
Press any key to continue...
^without creating a scroll bar on the side.
I've been trying to use Console::BufferHeight but I can't seem to get it to work. This is the only thing I've been able to find that seems to be close to what I want to do: http://msdn.microsoft.com/en-us/library/system.console.bufferheight.aspx But It just shows how to read it, not how to set the size. And for some reason typing just std::cout<<System::Console::BufferHeight; gives me scope errors. Any help would be greatly appreciated. Thanks!
I think you want this:
SetConsoleWindowInfo
Example,
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT rect = {0,0, 100, 100};
SetConsoleWindowInfo(hConsole, TRUE, &rect)
Have a look at these as well (experiment with them):
SetConsoleScreenBufferSize()
SetConsoleScreenBufferInfoEx
There is an example on MSDN here.
Im new to programming also, but I tried:
while(!cin.get())
{
}
and it worked,try to place it in the bottom of the code. It will work wonders on your console!
Try changing its position (put it in the middle of the code)
I'd suggest keeping the lines yourself, in a quick class, say that cycles where to put the next line by using an iterator that is always set to point at the next line to be input into.
Next, use FillConsoleOutputCharacter() to print blanks over the lines you previously had printed there.
Then, use SetConsoleCursorPosition() to four(or however many lines you wanted) lines above where you want your input to start, and output each line in you cycle, starting at the one after your iterator. This prints all the lines in order from eldest to youngest.It's been a while, so my knowledge of C++ is kinda hazy, but this should be pretty simple with the standard library and win32 library.
I am trying to make the copies of the builtin functions and adding a x to their name so i can understand each functions working.While writing a function for clrscr() i am confused about how it works.Does it use 2 nested loops and print (" ") i.e space all over the screen or it prints("\n") over the screen?Or what?
I tried this:
#include<stdio.h>
#include<conio.h>
void main(void)
{
printf("press any key to make clrscr() work");
getch();
for(int i=0;i<50;i++)
{
printf("\n");
}
// to make the screen come to 1,1
gotoxy(1,1);
getch();
}
clrscr() implementation may depend on the environment your console application runs. Usually it sends the ClearScreen control character (0x0C) to the console driver, that actually clears the screen.
The driver knows about character space to clear as well as all attributes (blink, underline,...) to reset.
If you dont want the driver to handle 0x0C, you can mimic this with 50 times calling printf("\n"). but calling 50x80 calling poutchar(' ') is not similar to calling clrsrc(), since the cursor will be advanced by one what may put it in the next line after scrolling the screen content.
Further you should regard, that the behaviour of the screen depends on the implementation. When the cursor position is in the right column and you output one character the cursor position may stay at the right edge or it may cause a new line. Whe you cursor position is in the lower right corner the next character may cause a new line including scrolling the screen content by one line.
The best way would be to imaging what clrscr() would do and let it make it's job.