Why in this case, printw displays "Blah" ? I use nocbreak. So printw is not supposed to produce output normally, because the output is line-buffered.
int main(int ac, char **av)
{
initscr();
nocbreak();
printw("Blah");
refresh();
while (1);
}
Actually, printw is not line-buffered. ncurses initializes the terminal to raw mode and simulates cooked mode as needed. But that applies only to input. For output, ncurses will immediately write the relevant updates to the screen as noted in the manual page:
The refresh and wrefresh routines (or wnoutrefresh and doupdate) must
be called to get actual output to the terminal, as other routines merely manipulate data structures. The routine wrefresh copies the named
window to the physical screen, taking into account what is already
there to do optimizations. The refresh routine is the same, using stdscr as the default window. Unless leaveok has been enabled, the physical cursor of the terminal is left at the location of the cursor for
that window.
The physical screen is your terminal, of course. ncurses remembers what's there by recording it in curscr:
This implementation of curses uses a special window curscr to record
its updates to the terminal screen.
This is referred to as the "physical screen" in the curs_refresh(3x)
and curs_outopts(3x) manual pages.
From ncurses' viewpoint, the terminal (that you see) and curscr are the same thing.
For printw, the manual page says it acts as if it calls waddstr, and that in turn calls waddch:
These functions write the (null-terminated) character string str on the
given window. It is similar to calling waddch once for each character
in the string.
It is because of the call to refresh.
The refresh man page does not explicitly state it, but it seems to apply the buffered outputs as well.
Without the call to refresh, no output is shown.
If you add a call to getch instead of refresh, you get the output too, because getch does a wrefresh. Man page:
If the window is not a pad, and it has been moved or modified since the last call to wrefresh, wrefresh will be called before another character is read.
To see the different behavior for inputs in cbreak/nocbreak mode, you can use this program:
int main(int ac, char **av)
{
char c, i;
initscr();
noecho(); // switch off display of typed characters by the tty
printw("cbreak\n");
cbreak();
for (i = 0; i < 5; ++i) {
c = getch();
printw("%c", c);
}
printw("\nnocbreak\n");
nocbreak();
for (i = 0; i < 5; ++i) {
c = getch();
printw("%c", c);
}
return 0;
}
In cbreak mode, the program sees the five input characters as you type them (and outputs immediately due to getch). In nocbreak mode, they will be received and output only after you press return.
Related
I am redirecting the ncurses hmi to a different, already existing terminal. While the output part works fine (and is therefore not shown here), the input misses keys which then appear in the terminal as though they had been entered without ncurses.
#include <stdio.h>
#include <curses.h>
int main(int argc, char *argv[])
{
FILE *fd = fopen(argv[1], "r+");
SCREEN *scr = newterm(nullptr, fd, fd);
set_term(scr);
noecho();
keypad(stdscr, TRUE);
while (true) {
int ch = wgetch(stdscr);
printf("In %d\r\n", ch);
}
return 0;
}
I create two terminals on Ubuntu and get the name of one (let's call it the 'curses-terminal') using 'tty'. This name is then used as argument when starting the above in the other terminal.
When typing in the curses-terminal, I expect the codes of the keys to appear in the other terminal without seeing anything in the curses-terminal.
Instead, I see some of the characters diffuse into the curses-terminal without their code being displayed in the other one. This happens with normal characters when typing more quickly, but it happens especially with arrow keys and ALT- combinations, where the error rate is >> 50%.
Are there any settings which I have forgotten?
Using G.M.'s hint, I was able to reliably get all input.
Run
tail -f /dev/null
in the curses-terminal before attaching the ncurses app.
Should you be tempted (like me) though to send this command from within your app after fopen, you may end up frustrated.
I'm working on a terminal program to recognize individual key presses, including keypad keys, but I'd rather not do it in curses/program mode if possible. Rather than reinvent the wheel using terminfo and some sort of mapping or tree structure for fast keypad key matching, I figured I might just leverage curses and use tcgetattr() and tcsetattr() to do what I want outside curses mode while still using curses I/O functions to do the translation of keypad keys for me. Much to my surprise, this works (Linux, ncurses 6.1.20180127):
/**
* Most error checking elided for brevity.
*/
#include <stdio.h> // printf
#include <string.h> // memcpy
#include <curses.h>
#include <termios.h> // tcgetattr, tcsetattr
int main(void)
{
struct termios curr, new_shell_mode;
int c, fd;
SCREEN *sp;
FILE *ttyf;
/*
* Initialize desired abilities in curses.
* This unfortunately clears the screen, so
* a refresh() is required, followed by
* endwin().
*/
ttyf = fopen("/dev/tty", "r+b");
fd = fileno(ttyf);
sp = newterm(NULL, ttyf, ttyf);
raw();
noecho();
nonl();
keypad(stdscr, TRUE);
refresh();
// Save the current curses mode TTY attributes for later use.
tcgetattr(fd, &curr);
endwin();
/*
* Set the shell/non-curses mode TTY attributes to
* match those of program/curses mode (3 attempts).
*/
memcpy(&new_shell_mode, &curr, sizeof curr);
for (c = 0; c < 3; c++) {
tcsetattr(fd, TCSADRAIN, &new_shell_mode);
tcgetattr(fd, &curr);
if (0 == memcmp(&new_shell_mode, &curr, sizeof curr))
break;
}
// If new shell mode could fully be set, get a key press.
if (c != 3)
c = getch();
reset_shell_mode();
delscreen(sp);
fclose(ttyf);
printf("%02X\n", c);
return 0;
}
However, given that I've exited curses mode, is it actually safe/portable to still use getch() in the manner shown?
Or do I need to take the more difficult path of using setupterm() to load the terminfo DB and loop through the strnames array, calling tigetstr() for each, plus set my own termios flags manually and deal with reading the keypress myself?
Nothing in the XSI Curses spec seems to forbid this, provided stdscr remains valid, which seems to be either until the program exits or delwin() is called, I can continue using it, and since stdscr is connected to my ttyf file, which is the terminal, I can use it to get a keypress without resorting to handling everything myself.
You initialized curses using newterm, and it doesn't matter that you called endwin: curses will resume full-screen mode if it does a refresh as a side-effect of calling getch.
That's not just ncurses, but any curses implementation (except for long-obsolete BSD versions from the 1980s). X/Open Curses notes
If the current or specified window is not a pad, and it has been moved or modified since the last refresh operation, then it will be refreshed before another character is read.
In your example, nothing was "moved or modified". But getch checks. (There's probably nothing to be gained by the endwin/termios stuff, since newterm doesn't do a clear-screen until the first refresh).
I am using XUBUNTU 16.04 and Geany.
I am starting to test the library ncurses. Bu I am not able to show characters. When I run the program a window appears, but the characters "a" and "*" does not appear.
This is my code:
#include <ncurses.h>
void line(char ch, int n)
{
int i;
for( i = 1; i<=n; i++ )
addch(ch);
}
int main()
{
clear();
line("a", 50);
line("*", 8);
return 0;
}
man curs_refresh:
The refresh and wrefresh routines (or wnoutrefresh and doupdate) must be called to get actual output to the terminal, as other routines merely manipulate data structures.
So your program is missing a refresh();.
It also looks like you're missing initialization/cleanup, i.e. calling initscr() at the beginning and endwin() at the end of your program.
Two things are missing:
initialization
something to keep the text on the screen momentarily
Initialization begins with initscr (or newterm, if you read the manual page). As written, the program would print something to the screen, and exit without pausing (and if your terminal uses the alternate screen, the text would vanish). A getch (read a character from the keyboard) does that, as well as doing refresh. By the way, the clear is unnecessary, because initscr does that:
The initscr code determines the terminal type and initializes all curses data structures. initscr also causes the first call to refresh(3x)
to clear the screen. If errors occur, initscr writes an appropriate
error message to standard error and exits; otherwise, a pointer is returned to stdscr.
Try this:
#include <curses.h>
void line(char ch, int n)
{
int i;
for( i = 1; i<=n; i++ )
addch(ch);
}
int main()
{
initscr();
cbreak();
noecho();
line("a", 50);
line("*", 8);
getch();
endwin();
return 0;
}
I am trying to clear my console each time I am going to printf something in it (Windows environment with GCC compiler). I am using CygWin and the only way I could manage to do it was with system("cmd /c cls");. That works fine but it causes the screen to blink for a fraction of a second which is obviously annoying.
Is there any alternative way of clearing console screen?
First thing I'd do is stop using cmd to do it. CygWin, assuming you're running somewhere within the shell and not a Windows console, has a "native" option in that you can use either of:
clear
tput clear
to clear the screen, without invoking the external cmd interpreter.
So, from within a program running in CygWin, you can clear the screen with a simple:
system("clear");
Of course, if you don't want to run any external executables, you can achieve the same end with curses. By way of example, the following program clears the screen for you (make sure you include -lcurses at the end of the compilation command):
#include <curses.h>
int main (void) {
WINDOW *w = initscr();
clear(); refresh(); sleep(2);
endwin();
return 0;
}
Don't get hung up on the fact that it's restored on exit, you wouldn't be using this program as a screen clearing stand-alone thing. Instead, the statements would be incorporated into your own program, between the initscr() and endwin() calls, something like:
#include <curses.h>
int main (void) {
char buf[2],
*msg = "Now is the time for all good men to come to lunch.";
WINDOW *w = initscr();
buf[1] = '\0';
clear();
refresh();
while (*msg != '\0') {
buf[0] = *msg++; addstr(buf);
if ((buf[0] == ' ') || (buf[0] == '.')) {
refresh();
sleep(1);
}
}
endwin();
return 0;
}
This program clears the screen using curses then outputs a message in word-sized chunks.
this web page:
http://man7.org/linux/man-pages/man4/console_codes.4.html
contains the common ESC sequences for handling the terminal screen/cursor position, etc
this part of the linked info is probably what you want to implement.
These escape sequences can be placed at the beginning of the buffer that you are using to output your data/text
Of special interest is ESC [ 2 j: which erases the whole screen
J ED Erase display (default: from cursor to end of display).
ESC [ 1 J: erase from start to cursor.
ESC [ 2 J: erase whole display.
ESC [ 3 J: erase whole display including scroll-back
buffer (since Linux 3.0).
In most modern shells, you can hit the up and down arrows and it will put, at the prompt, previous commands that you have executed. My question is, how does this work?!
It seems to me that the shell is somehow manipulating stdout to overwrite what it has already written?
I notice that programs like wget do this as well. Does anybody have any idea how they do it?
It's not manipulating stdout -- it's overwriting the characters which have already been displayed by the terminal.
Try this:
#include <stdio.h>
#include <unistd.h>
static char bar[] = "======================================="
"======================================>";
int main() {
int i;
for (i = 77; i >= 0; i--) {
printf("[%s]\r", &bar[i]);
fflush(stdout);
sleep(1);
}
printf("\n");
return 0;
}
That's pretty close to wget's output, right? \r is a carriage-return, which the terminal interprets as "move the cursor back to the start of the current line".
Your shell, if it's bash, uses the GNU Readline library, which provides much more general functionality, including detecting terminal types, history management, programmable key bindings, etc.
One more thing -- when in doubt, the source for your wget, your shell, etc. are all available.
To overwrite the current standard output line (or parts of it) use \r (or \b.) The special character \r (carriage return) will return the caret to the beginning of the line, allowing you to overwrite it. The special character \b will bring the caret back one position only, allowing you to overwrite the last character, e.g.
#include <stdio.h>
#include <unistd.h>
int i;
const char progress[] = "|/-\\";
for (i = 0; i < 100; i += 10) {
printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */
fflush(stdout);
sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);
printf("Processing: ");
for (i = 0; i < 100; i += 10) {
printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */
fflush(stdout);
sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);
Use fflush(stdout); because standard output is usually buffered and the information may not otherwise be immediately printed on the output or terminal
In addition to \r and \b, take a look at ncurses for some advanced control over what's on the console screen. (Including columns, moving around arbitrarily, etc).
A program running in a text terminal / console can manipulate the text displayed in its console in various ways (make text bold, move cursor, clear screen etc.). This is accomplished by printing special character sequences, called "escape sequences" (because they usually start with Escape, ASCII 27).
If stdout goes to a terminal which understands these escape sequences, the display of the terminal will change accordingly.
If you redirect stdout to a file, the escape sequences will appear in the file (which is usually not what you want).
There is no complete standard for escape sequences, but most terminals use the sequences introduced by VT100, with many extensions. This is what most terminals under Unix/Linux (xterm, rxvt, konsole) and others like PuTTY understand.
In practice, you would not directly hardcode escape sequences into your software (though you could), but use a library to print them, such as ncurses or GNU readline mentioned above. This allows compatibility with different terminal types.
It's done with the readline library... I'm not sure how it works behind the scenes but I don't think it has anything to do with stdout or streams. I suspect readline uses some sort of cryptic (to me, at least) terminal commands - that is, it cooperates with the terminal program that actually displays your shell session. I don't know that you can get readline-like behavior just by printing output.
(Think about this: stdout can be redirected to a file, but the up/down-arrow keys trick doesn't work on files.)
You can use carriage return to simulate this.
#include <stdio.h>
int main(int argc, char* argv[])
{
while(1)
{
printf("***********");
fflush(stdout);
sleep(1);
printf("\r");
printf("...........");
sleep(1);
}
return 0;
}
The program does this by printing special characters that the terminal interprets in a special way. The most simple version of this is (on most linux/unix terminals) to print '\r' (carriage return) to the normal stdout which resets the cursor position to the first character in the current line. So the thing you write next will overwrite the line you wrote previously. This can be used for simple progress indicators, for example.
int i = 0;
while (something) {
i++;
printf("\rprocessing line %i...", i);
...
}
But there are more complicated escape characters sequences that are interpreted in various ways. All kinds of things can be done with this, like positioning the cursor at a specific position on the screen or setting the text color. If or how these character sequences are interpreted depends on your terminal, but a common class supported by most terminals are ansi escape sequences. So if you want red text, try:
printf("Text in \033[1;31mred\033[0m\n");
The simplest way is to print to stdout the carriage return character ('\r').
The cursor will be moved to the start of the line, allowing you to overwrite its contents.