Implementing a KeyPress Event in C - 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 */
}

Related

C - how to poll() input without buffering?

I am trying to detect any character typed to stdin (without a newline character).
I tried :
setvbuf(stdin, NULL, _IONBF); //This returns 0
struct pollfd pfd = {STDIN_FILENO, POLLIN};
while (!poll(pfd, 1, ms)) {
/* do some thing, e.g. printf("n\n"); */
}
It appears not stop printing when I typed q, but did stop after I hit enter. The system I am working on is arch-linux, compiler is gcc.
This works for me but it may depend on your system/terminal
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
int main() {
int i = 0;
struct termios ts;
tcgetattr(0, &ts);
ts.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &ts);
while (!ioctl(0, FIONREAD, &i) && !i) {
printf("x");
fflush(stdout);
sync();
usleep(100);
}
printf("\n");
return 0;
}
Craig really answered this for you. I was just curious enough to find an implementation. The ioctl(0, FIONREAD, &i) gets the number of characters in the buffer and puts it into i. The man pages for termios and ioctl_tty contained all of the details needed to come up with this solution.
Honestly, though, if you are wanting to make something interactive like this, ncurses makes it somewhat simpler.
The q is being held up in the kernel's TTY layer driver/buffer because it is in "cooked" mode.
In this mode, it will only return something to the app when the driver sees a newline. It then gives back: q\n (i.e. q<newline>).
To have it return immediately on any character, you'll have to use ioctl calls to put the TTY layer into "raw" mode.
You'll need to use [recommended] the termios calls: tcgetattr and tcsetattr
UPDATE:
Will ioctl alone works? Which command is corresponding to change terminal into raw mode?
Look at man termios. It has full documentation for how to set raw mode (what is called "non-canonical" mode in the man page).
It's been a while since I've done this, but here's a skeleton function.
Although the function restores original state at the end, you might want to set non-canonical mode once at program start.
But, then, you'll have to handle all your own line editing (e.g. supporting backspace, etc.) for other sections of your program that want normal canonical line editing.
#include <termios.h>
#include <unistd.h>
void
change_tty(int fd)
{
struct termios orig;
struct termios raw;
// get original cooked/canonical mode values
tcgetattr(fd,&orig);
// set options for raw mode
raw = orig;
#if 0
raw.c_lflag &= ~ICANON;
raw.c_cc[VMIN] = ...
raw.c_cc[VTIME] = ...
#else
cfmakeraw(&raw);
#endif
// put unit into raw mode ...
tcsetattr(fd,TCSANOW,&raw);
// do stuff in raw mode ...
// restore original mode
tcsetattr(fd,TCSANOW,&orig);
}

How can i make getchar() function hold backspace

#include<stdio.h>
int main(void)
{
int c;
while((c=getchar())!=EOF)
{
if(c==8) // 8 is ASCII value of backspace
printf("\\b");
}
}
Now I want to enter backspace and want getchar() function to return ASCII of backspace to c(int variable)
Note- I am not asking about getch function i know that getch command is able to read backspace
I only want to know whether getchar is able to read backspace or not
If yes,How?
How to do it please explain me
I am new to C programming
If the stream stdin reads from a file, getchar() will handle backspace characters ('\b' or 8 in ASCII) like any other regular character and return it to the caller.
The reason it does not do that when reading from the console (aka terminal or tty) is related to the configuration of the console itself. The console is in cooked mode by default, so as to handle echo, backspace and line buffering, but also signals such as SIGINT sent for Ctrl-C and many more subtile settings.
The C Standard does not provide any way to change the terminal modes, but POSIX systems have the stty command and the termios system calls available to C programs to do this.
Once you configure the console in raw mode, also set stdin to unbuffered mode with setvbuf(stdin, NULL, _IONBF, 0) so getchar() reads each byte from the console as it is typed.
Configuring the console is a complex issue, you may want to read this first: http://www.linusakesson.net/programming/tty/
If your system supports termios as standardized in POSIX.1-2001, then you can manipulate the standard input terminal to not buffer your input. Consider the following example:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* SIGINT handler */
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = signum;
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
/* Reverting terminal back to original settings */
static struct termios terminal_config;
static void revert_terminal(void)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminal_config);
}
int main(void)
{
int c;
/* Set up INT (Ctrl+C), TERM, and HUP signal handlers. */
if (install_done(SIGINT) ||
install_done(SIGTERM) ||
install_done(SIGHUP)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Make terminal input noncanonical; not line buffered. Also disable echo. */
if (isatty(STDIN_FILENO)) {
struct termios config;
if (tcgetattr(STDIN_FILENO, &terminal_config) == 0 &&
tcgetattr(STDIN_FILENO, &config) == 0) {
config.c_lflag &= ~(ICANON | ECHO);
config.c_cc[VMIN] = 1; /* Blocking input */
config.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &config);
atexit(revert_terminal);
}
}
/* Set standard input unbuffered. */
setvbuf(stdin, NULL, _IONBF, 0);
printf("Press Ctrl+C to exit.\n");
fflush(stdout);
while (!done) {
c = fgetc(stdin);
if (c == EOF)
printf("Read EOF%s\n", ferror(stdin) ? " as an error occurred" : "");
else
printf("Read %d = 0x%02x\n", c, (unsigned int)c);
fflush(stdout);
}
return EXIT_SUCCESS;
}
The #define line tells your C library headers to expose POSIX.1 features for GNU-based systems.
The done flag is set whenever an INT (Ctrl+C), TERM, or HUP signal is received. (HUP signal is sent if you disconnect from the terminal, for example by closing the terminal window.)
The terminal_config structure will contain the original terminal settings, used by revert_terminal() registered as an at-exit function, to revert the terminal settings back to the original ones read at program startup.
The function isatty(STDIN_FILENO) returns 1 if standard input is a terminal. If so, we obtain the current terminal settings, and modify them for non-canonical mode, and ask that each read blocks until at least one character is read. (The I/O functions tend to get a bit confused if you set .c_cc[VMIN]=0 and .c_cc[VTIME]=0, so that if no input is pending, fgetc() returns 0. Typically it looks like an EOF to stdio.h I/O functions.)
Next, we tell the C library to not internally buffer standard input, using setvbuf(). Normally, the C library uses an input buffer for standard input, for efficiency. However, for us, it would mean the C library would buffer characters typed, and our program might not see them immediately when typed.
Similarly, standard output is also buffered for efficiency. The C library should flush all complete lines to the actual standard output, but we can use the fflush(stdout) call to ensure everything we've written to stdout is flushed to the actual standard output at that point.
In main(), we then have a simple loop, that reads keypresses, and prints them in decimal and hexadecimal.
Note that when a signal is delivered, for example the INT signal because you typed Ctrl+C, the delivery of the signal to our handle_done() signal handler interrupts the fgetc() call if one is pending. This is why you see Read EOF when you press Ctrl+C; if you check ferror(stdin) afterwards, you'll see it returns nonzero (which indicates an error occurred). The "error" in this case is EINTR, "interrupted by a signal".
Also note that when you press some certain keys, like cursor or function keys, you'll see multiple characters generated, usually beginning with 27 and 91 (decimal; 0x1B 0x5B in hexadecimal; "\033[" if expressed as a C string literal). These are usually, but not always, ANSI escape sequences. In general, they are terminal-specific codes that one can obtain via tigetstr(), tigetnum(), and tigetflag() using the terminfo database.
A much more portable way to do this, is to use a Curses library; either ncurses on most systems, or PDCurses on Windows machines. Not only do they provide a much easier interface, but it does it in a terminal-specific way, for maximum compatibility across systems.
C programs using the Curses functions can be compiled against any Curses library, so the same C source file can be compiled and run on Linux, Mac, and Windows machines. However, ncurses does contain quite a few extensions, which may not be provided by other Curses libraries like PDCurses.

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;
}

Get Keyboard Interrupt in C

Program:
#include<stdio.h>
void main()
{
int time=1800;
while(1){
system("clear");
time-=1;
printf("%d\n",time);
sleep(1);
if(time==0)
pause();
}
}
The above program stops when the time reaches 0. My requirement is during the runtime of the program, If I press any key like spacebar or any other key, the program gets paused and once again I press the key, the program gets resumed. So for doing this, before execution of
while condition, we submit the signal handler for keyboard interrupt. In C how to do this.
What is the function used to get keyboard interrupt. I dont want to get input from the user, I want to handle the interrupt generated by the user through keyboard.
Thanks in Advance..,
You need conio.h for your requirement.It defines kbhit() and getch() both wait for input from keyboard.
Whenever kbhit() is called, it checks the keyboard buffer and returns a nonzero value if the buffer has any keypress otherwise 0 is returned.
The conio.h is used by MSDOS compilers and is not the part of standard C libraries (ISO). It is also not defined in POSIX.
#include<stdio.h>
#include<conio.h>
int main()
{
while(1)
{
while(!kbhit())
{
//works continuously until interrupted by keyboard input.
printf("M Tired. Break Me\n");
}
getch();
}
return 0;
}
For linux you may use the following snippet to implement kbhit() by using fnctl() from fnctl.h for signal handling:
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
int kbhit(void)
{
struct termios oldt, newt;
int ch;
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF)
{
ungetc(ch, stdin);
return 1;
}
return 0;
}
The keyboard does not exist in purely standard C99 or C11 (stdin is not a keyboard, and could be a pipe(7) so is not always a tty(4); you might read from /dev/tty ...).
So it is much less simple that what you want it to be, and it is operating system specific. I am focusing on Linux.
Read much more about ttys, notably read the tty demystified. Notice that tty are usually in cooked mode, where the kernel is buffering lines (in addition of stdin being line buffered).
The reasonable way is to use a terminal library like ncurses or readline. These libraries are setting the tty in raw mode (which you might do by yourself, see this and termios(3); you'll probably need also to poll(2)). Be sure to exit properly and reset the tty to cooked mode before exiting.
But your life is probably too short to dive into termios(3) and tty_ioctl(4) so just use ncurses or readline
You could also consider some GUI application (e.g. above X11 or Wayland). Then use a toolkit (GTK, Qt, ...).
My requirement is during the runtime of the program, If I press any key like spacebar or any other key, the program gets paused and once again I press the key, the program gets resumed.
You can achieve this with this type of code
#include <stdio.h>
int main()
{
char i;int y=0;
while(1)
{
if(!(_kbhit()))
{
y=0;
printf("//key is not pressed");
//do what you want
}
else
{
printf("key pressed (execution pause) , press again to resume");
i=_getch();
y=1;
}
if(y==1)
{
getch();
}
}
return 0;
}

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