Capturing input without \n - c

I am making a simple 2d game in the terminal, and I have been wondering how I could get stdin without having to return. So, instead of the user having to press w\n (\n for return), they would just press 'w' and it would go forwards.
scanf, gets, and getchar cannot do this, but I have seen it be done before in programs such as Vi. How would I achieve this?

You need to set your terminal to non-canonical mode. You can use functions like tcsetattr, and tcgetattr to set and get terminal attributes. Here is a trivial example:
int main(int argc, const char *argv[])
{
struct termios old, new;
if (tcgetattr(fileno(stdin), &old) != 0) // get terminal attributes
return 1;
new = old;
new.c_lflag &= ~ICANON; // turn off canonical bit.
if (tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0) // set terminal attributes
return 1;
// at this point, you can read terminal without user needing to
// press return
tcsetattr(fileno(stdin), TCSAFLUSH, &old); // restore terminal when you are done.
return 0;
}
For more info about these functions, see glibc documentation. Especially this part.

Related

Should I reset termios settings on SIGINT/SIGTERM?

I was playing around with termios and I figured out quickly that if I change the terminal settings and exit, my changes will persist and screw up my environment. So I setup my program to save the initial settings with tcgetattr and reset them before exiting.
I predicted, however, that if I hit Ctrl-C to send SIGINT while my program was running that it would cause the terminal to still have my modified settings since my program wouldn't have executed the code to reset them back to the old settings.
But that didn't happen. In both Ubuntu and macOS Sierra, my terminal settings were reverted as if I had reset them in the program.
So the question is: Is this behavior something I can count on in general? Or does it make sense to register signal handlers to catch SIGINT/SIGTERM and revert terminal settings before exiting?
Code
Answering this question probably doesn't require looking at code, but here is my example, in case you're curious:
#include <stdio.h>
#include <string.h>
#include <termios.h>
int main() {
// put terminal into non-canonical mode
struct termios old;
struct termios new;
tcgetattr(0, &old);
new = old;
new.c_lflag &= ~(ICANON | ECHO);
tcsetattr(0, TCSANOW, &new);
// loop: get keypress and display (exit via 'x')
char key;
printf("Enter a key to see the ASCII value; press x to exit.\n");
while (1) {
key = getchar();
printf("%i\n", (int)key);
if (key == 'x') { break; }
}
// set terminal back to canonical
tcsetattr(0, TCSANOW, &old);
return 0;
}
I was a bit surprised to see that in my Arch Linux terminal settings also "were reverted". But in fact they remained the same. When I changed your code I managed to track some anomaly.
//...
new.c_lflag &= ~(ICANON | ECHO);
new.c_cc[VMIN] = 0;
new.c_cc[VTIME] = 0;
//...
So here if you don't press any button the output is -1. If you hit Ctrl-C, recompile and launch the original program (from the same terminal), it will also print -1, so there is no automatic reset.
I don't know why ECHO is "hidden" and I'd like to know, but I suggest you to manually revert all terminal settings.

C in 2048,problems with moving [duplicate]

This question already has answers here:
How to avoid pressing Enter with getchar() for reading a single character only?
(14 answers)
Closed 5 years ago.
I am making 2048 game in C and I need help. Moves are made by pressing W,A,S,D keys e.g. W is for moving up, S for down.
However, after every letter you have to press enter to accept it. How can I make it work without pressing enter?
There is no standard library function in c to accomplish this; instead you will have to use termios functions to gain control over the terminal and then reset it back after reading the input.
I came across some code to read input from stdin without waiting for a delimiter here.
If you are on linux and using a standard c compiler then getch() will not be easily available for you. Hence i have implemented the code in the link and you just need to paste this code and use the getch() function normally.
#include <termios.h>
#include <stdio.h>
static struct termios old, new;
/* Initialize new terminal i/o settings */
void initTermios(int echo)
{
tcgetattr(0, &old); /* grab old terminal i/o settings */
new = old; /* make new settings same as old settings */
new.c_lflag &= ~ICANON; /* disable buffered i/o */
new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
}
/* Restore old terminal i/o settings */
void resetTermios(void)
{
tcsetattr(0, TCSANOW, &old);
}
/* Read 1 character - echo defines echo mode */
char getch_(int echo)
{
char ch;
initTermios(echo);
ch = getchar();
resetTermios();
return ch;
}
/* Read 1 character without echo */
char getch(void)
{
return getch_(0);
}
int main()
{
int ch;
ch = getch();//just use this wherever you want to take the input
printf("%d", ch);
return 0;
}
what you are asking for is function named kbhit() which returns true if user presses a key on the keyboard. you can use this function to get input from the use too like see
char c= ' ';
while(1){
if(kbhit())
c=getch();
if(c=='q')// condition to stop the infinite loop
break;
}

Understanding read + write in c

char buf[1];
if (argc == 1) {
while (read(STDIN_FILENO, buf, 1) > 0) {
write(1, buf, sizeof(buf));
}
}
I have a few things I'd like to clarify about this snippet. We run it, ./exec_file Let's say we just press Enter. We move to the next line and read 1 byte '\n' then write it to stdout bringing us down one more line... simple enough. Now lets say we type h, then Enter. The program spits out h on the next line with an invisible '\n'.
Looking at the code after we type h it reads it into the buffer then writes it to stdout but somehow the program waits to spit it out on the next line till after I've pressed Enter..how?
Lastly, when we first hit the while loop wouldn't read initially return 0 since we haven't typed anything in initially??
stdin behaves a bit different than most other streams.
First, input is line buffered. That means that input isn't available until you press enter. this explains while the h won't appear until you press enter.
Since it is a stream it doesn't really have an end. Instead of failing when there is no data to read, the call will block until some data is available (or until the program receives a signal). A socket works the same way.
The blocking behaviour can be turned off using fcntl :
int fd = STDIN_FILENO;
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
The terminal is by default line buffered, because it is in canonical mode. From Linux manuals tcgetattr(3):
Canonical and noncanonical mode
The setting of the ICANON canon
flag in c_lflag determines whether the terminal is operating in
canonical mode (ICANON set) or noncanonical mode (ICANON unset).
By default, ICANON set.
In canonical mode:
Input is made available line by line. An input line is
available
when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at
the start of line). Except in the case of EOF, the line delimiter
is included in the buffer returned by read(2).
Line editing is enabled (ERASE, KILL; and if the IEXTEN flag is
set:
WERASE, REPRINT, LNEXT). A read(2) returns at most one line of
input; if the read(2) requested fewer bytes than are available in
the current line of input, then only as many bytes as requested are
read, and the remaining characters will be available for a future
read(2).
You can switch off canonical mode on the terminal by calling tcgetattr with proper flags. First of all disable the canonical mode; then set the timeout to 0; set minimum read to 1 for blocking reads or 0 for non-blocking reads. Usually it is customary to also disable local echo, otherwise everything you type would still be automatically visible (and displayed twice in your program):
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
int main() {
struct termios old_settings, new_settings;
int is_terminal;
// check whether the stdin is a terminal to begin with
if (is_terminal = isatty(STDIN_FILENO)) {
// get the old settings
tcgetattr(STDIN_FILENO, &old_settings);
new_settings = old_settings;
// disable canonical mode and echo
new_settings.c_lflag &= (~ICANON & ~ECHO);
// at least one character must be written before read returns
new_settings.c_cc[VMIN] = 1;
// no timeout
new_settings.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &new_settings);
}
while (read(STDIN_FILENO, buf, 1) > 0) {
// add this here so that you can verify that it is character by character,
// and not the local echo from the terminal
write(STDOUT_FILENO, ">", 1);
write(STDOUT_FILENO, buf, sizeof(buf));
}
// finally restore the old settings if it was a terminal
if (is_terminal) {
tcsetattr(STDIN_FILENO, TCSANOW, &old_settings);
}
return 0;
}
If you still want the blocking to happen, but want to read character by character, you can use termios to configure how the input will be given to your program. See the code below.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
int main()
{
char buf[1];
struct termios term, term_orig;
if (tcgetattr(0, &term_orig)) {
printf("tcgetattr failed\n");
exit(-1);
}
term = term_orig;
term.c_lflag &= ~ICANON;
term.c_lflag |= ECHO;
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &term)) {
printf("tcsetattr failed\n");
exit(-1);
}
while (read(0, buf, 1) > 0) {
write(1, buf, sizeof(buf));
}
return 0;
}

Implementing a KeyPress Event in C

I have a infinite loop like the following one, and within this loop, I want to continuously check the keyboard to see if the escape key (ESC) has been pressed or not. If it is pressed, then the loop should be broken. How I can do this in C? (I am using gcc, and do access to pthreads as well in case this must be done via threads)
while(1){
//do something
//check for the ESC key
}
This is heavily system dependent. In Unix/Linux systems, the default terminal handler gathers lines and only notifies the program when a full line is available (after Enter is hit.) If you instead want keystrokes immediately, you need to put the terminal into non-canonical mode:
#include <termios.h>
struct termios info;
tcgetattr(0, &info); /* get current terminal attirbutes; 0 is the file descriptor for stdin */
info.c_lflag &= ~ICANON; /* disable canonical mode */
info.c_cc[VMIN] = 1; /* wait until at least one keystroke available */
info.c_cc[VTIME] = 0; /* no timeout */
tcsetattr(0, TCSANOW, &info); /* set immediately */
Once you've done that, you can use any calls that read from stdin and they will return keys without waiting for the end of the line. You can in addition set c_cc[VMIN] = 0 to cause it to not wait for keystrokes at all when you read from stdin.
If, however, you're reading stdin with stdio FILE related calls (getchar, etc), setting VMIN = 0 will make it think you've reached EOF whenever there are no keys available, so you'll have to call clearerr after that happens to try to read more characters. You can use a loop like:
int ch;
while((ch = getchar()) != 27 /* ascii ESC */) {
if (ch < 0) {
if (ferror(stdin)) { /* there was an error... */ }
clearerr(stdin);
/* do other stuff */
} else {
/* some key OTHER than ESC was hit, do something about it? */
}
}
After you're done, you probably want to be sure to set the terminal back into canonical mode, lest other programs (such as your shell) get confused:
tcgetattr(0, &info);
info.c_lflag |= ICANON;
tcsetattr(0, TCSANOW, &info);
There are also other things you can do with tcsetattr -- see then manual page for details. One thing that might suffice for your purposes is setting an alternative EOL character.
If the main job you're doing can be placed within this main loop, you could go for using STDIN in non-blocking mode. You still have a problem with the terminal which does line-buffering normally. You shall put the terminal to raw mode as well.
What about using Ctrl-C (interrupt)?
Non-blocking means that the read() system call always returns immediately even if there are no new bytes in the file. On Linux/Unix you can make STDIN nonblocking this way:
#include <unistd.h>
#include <fcntl.h>
fcntl(0, F_SETFL, O_NONBLOCK); /* 0 is the stdin file decriptor */
This is what you want:
#include <stdio.h>
#include <conio.h>
void main() {
int c;
while((c = getch()) != EOF )
if(c == 27) break;
/* 27 is the ASCII code for Esc */
}

Reading a Stop Signal within a loop

I am using a while loop which doesnt terminate, for reproducing Tail command of unix using C code. I need a way to stop the loop apart from Ctrl + C which quits the process i believe. Is there any way to read Keyboard commands when used within the code ? The problem with using getchar() is that it stops the loop from running until a char is entered. Is there any alternative solution to this issue ?
You need to turn off blocking and line buffering. Turn off blocking so getc() returns right away. It will return -1 until it has a real character. Turn off line buffering so the OS sends the char right away instead of buffering it up until it has a full line which occurs when you press return.
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <termios.h> /* POSIX terminal control definitions */
int main(void) {
// Turn off blocking
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
struct termios options, oldoptions;
tcgetattr(STDIN_FILENO, &options);
// Disable line buffering
options.c_lflag &= ~( ICANON);
// Set the new options for the port...
tcsetattr(STDIN_FILENO, TCSANOW, &options);
while(1) {
char c = getc(stdin);
if(c != -1) break;
}
// Make sure you restore the options otherwise you terminal will be messed up when you exit
tcsetattr(STDIN_FILENO, TCSANOW, &oldoptions);
return 0;
}
I agree with the other posters that you should use signals, but this is the answer to what you asked.
This sounds very much like this question from the comp.lang.c FAQ.
Q: How can I read a single character from the keyboard without waiting for the RETURN key? How can I stop characters from being echoed on the screen as they're typed?

Resources