How do I use getch from curses without clearing the screen? - c

I'm learning to program in C and want to be able to type characters into the terminal while my code is running without pressing return. My program works, however when I call initscr(), the screen is cleared - even after calling filter(). The documentation for filter suggests it should disable clearing - however this is not the case for me.
#include <stdio.h>
#include <curses.h>
#include <term.h>
int main(void) {
int ch;
filter();
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
while((ch = getch()) != EOF);
endwin();
return 0;
}
Why does the above code still clearr the screen, and what could be done to fix it?
I'm using Debian Lenny (stable) and gnome-terminal if that helps.

You would see your screen cleared in a curses application for one of these reasons:
your program calls initscr (which clears the screen) or newterm without first calling filter, or
the terminal initialization clears the screen (or makes it appear to clear, by switching to the alternate screen).
In the latter case, you can suppress the alternate screen feature in ncurses by resetting the enter_ca_mode and exit_ca_mode pointers to NULL as done in dialog. Better yet, choose a terminal description which does what you want.
Further reading:
Why doesn't the screen clear when running vi? (xterm FAQ)

Extending the answer by mike.dld, this works for me on MacOS X 10.6.6 (GCC 4.5.2) with the system curses library - without clearing the screen. I added the ability to record the characters typed (logged to a file "x"), and the ability to type CONTROL-D and stop the program rather than forcing the user to interrupt.
#include <stdio.h>
#include <curses.h>
#include <term.h>
#define CONTROL(x) ((x) & 0x1F)
int main(void)
{
FILE *fp = fopen("x", "w");
if (fp == 0)
return(-1);
SCREEN *s = newterm(NULL, stdin, stdout);
if (s == 0)
return(-1);
cbreak();
noecho();
keypad(stdscr, TRUE);
int ch;
while ((ch = getch()) != EOF && ch != CONTROL('d'))
fprintf(fp, "%d\n", ch);
endwin();
return 0;
}

Basically, curses is designed to take over the screen (or window, in the case of a windowed terminal). You can't really mix curses with stdio, and you can't really use curses to just input or output something without messing with the rest of the screen. There are partial workarounds, but you're never really going to be able to make it work the way that it sounds like you want to. Sorry.
I'd suggest either rewriting your program to use curses throughout, or investigating alternatives like readline.

Use newterm() instead of initscr(), you should be fine then. And don't forget about delscreen() if you follow this advice.

Related

How portable is the use of getch outside curses mode?

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).

Using 'CLS' command in C causes screen blink

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).

getch returns -1?

They asked how to capture keys such as F11 or insand getchr does not return anything for those keys, and there is nothing I can find working that accepts raw input from input events..
I am now trying ncurses/curses in a C++ program to capture these keys.
My program to test is simple, it is basically:
#include <stdlib.h>
#include <stdio.h>
#include <curses.h>
int main() {
int car;
while(c != '\b') {
c = getch();
printf("%i", c);
}
return 0;
}
I use it of course the same as another getch() function, but it returns -1 infinite times.. I am using a recent kernel in Arch linux, in a standard terminal (tested in xterm as well)
Is there a certain switch I need to switch on in order to use this getch() in the libraries?
You need to call initscr(); to initialise curses before calling getch().
In addition, you probably want non-line-buffered mode, so you should also call cbreak(); noecho(); (echo mode should not be used with cbreak mode).

Ignore backspace key from stdin

I want to make a program that forces it's user to input text but doesn't allow him to erase any of it, what's a simple way of doing it in C?
The only thing I've got is (c = getchar()) != EOF && c != '\b' which doesn't work. Any ideas?
POSIX - unix version
#include <sys/types.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
int
main()
{
int fd=fileno(stdin);
struct termios oldtio,newtio;
tcgetattr(fd,&oldtio); /* save current settings */
memcpy(&newtio, &oldtio, sizeof(oldtio));
newtio.c_lflag = ICANON;
newtio.c_cc[VERASE] = 0; /* turn off del */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
/* process user input here */
tcsetattr(fd,TCSANOW,&oldtio); /* restore setting */
return 0;
}
You can't do it with portable code -- essentially every OS has some sort of minimal buffering/editing built into the standard input stream.
Depending on the OSes you need to target, there's a good change you'll have a getch available that will do unbuffered reading. On Windows, you include <conio.h> and go for it. On most Unix, you'll need to include (and link to) curses (or ncurses) for it.
This is likely more complicated than you imagine. To do this, you'll presumably need to take over control of echoing the characters the user is typing etc.
Have a look at the curses library. The wgetch function should be what you need, but first you'll need to initialise curses etc. Have a read of the man pages - if you're lucky you'll find ncurses or curses-intro man pages. Here's a snippet:
To initialize the routines, the routine initscr or newterm must be
called before any of the other routines that deal with windows and
screens are used. The routine endwin must be called before exiting.
To get character-at-a-time input without echoing (most interactive,
screen oriented programs want this), the following sequence should be
used:
initscr(); cbreak(); noecho();
Most programs would additionally use the sequence:
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
If you've not got that manpage / for further info, look up the individual function man pages.

Is there a way to get text as soon as possible without waiting for a newline?

I'm using C. I wrote a very simpe program which prints back the input, using getchar() and putchar() or printf(). Is there any way to make it so as soon as the user types one key, the program registers it, without waiting for an Enter? Let me show:
Currently, if the user types "abc" and then presses Enter, the program prints "abc" and a newline and keeps waiting for more input. I want to make it so as soon as the user types "a", the program prints "a" and waits for more input. I'm not sure whether this has to be done inside the source code or if something has to be changed in the Windows command line.
Just in case, here's the source code:
#include <stdio.h>
int main()
{
int c;
while ((c = getchar()) != EOF) {
putchar(c);
}
return 0;
}
if you are using Visual Studio, there is a library called conio (#include <conio.h>) which defines a kbhit() function and getch().
otherwise, on Windows, there is still the possibility of using functions from the Windows SDK (ReadConsoleInput() and the like), but that would require a little bit more code (although, once done and if done properly, it can be reused any time you want)
If you're using Visual Studio, you can use getch().
In this simple case, the other answers should suit you fine.
The general solution is to disable line buffering. This depends on the particular console; the following example is Windows-only (untested):
#include <windows.h>
int main() {
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
GetConsoleMode(hConsole, &mode);
SetConsoleMode(hConsole, mode & ~ENABLE_LINE_INPUT);
// ...
}
I assume that the standard C library functions are implemented in terms of ReadConsole and friends; if not, this might not even work. (I'm currently on Linux, so I cannot test this.)
On Linux you can take over the terminal:
#include <stdio.h>
#include <ctype.h>
#include <termios.h>
system("stty raw"); /* raw output to terminal, direct feedback */
system("clear"); /* clear screen */
printf("Press a key");
answer = getchar();
system("stty cooked"); /* revert back*/

Resources