How to instantly react to key pressing in C? - c

I'm trying to call a function when a user presses a key, somewhat like sending a signal with Ctrl-C. The idea is to be doing something, say in a while loop, and actively listen for, say, the 'f' key being pressed. When it is pressed, the program will stop performing the loop and do something else because the 'f' key was pressed.
Is there a way to customize the signal mapping? I didn't have much luck with this. There also seemed to be 1 or 2 available signals for customization, but I need 3.
If I use getchar(), the user needs to press the 'f' key AND THEN press the enter key. I would like for them to just press the 'f' key and not have to press the enter key.
It is very similar to using unix's more program where the user can just press the space bar to go through pages.
Any help is greatly appreciated!

If for some reason you do not like ncurses, you can try something like the following.
You can use a separate thread which uses select() on stdin then performs a read() on stdin and parses user input; if the key is what you are looking for, send a signal (i.e. USER1 or USER2 are unused) to your main thread. In your signal handler, perform whatever operation you are looking to do on the interrupt. Note that doing this means that your code must be interruptible so the interrupt does not break your computation.

By far the simplest option is to use ncurses.
However, if that's not acceptable, POSIX (and therefore Linux too) defines a series of routines for 'terminal control' — the names start with tc and include:
tcdrain()
tcflow()
tcflush()
tcgetattr()
tcgetpgrp()
tcgetsid()
tcsendbreak()
tcsetattr()
tcsetpgrp()
In this context, you primarily need tcgetattr() and tcsetattr(). You will need to study the strictures in the definitions for tcgetattr() and tcsetattr(), and will need to study the structures and flags in the <termios.h> header. Look at the ICANON local mode, for example: you will probably want to turn that off so that characters are available to read as soon as they are typed.

Related

Is it possible to refresh screen while waiting for user to input a string in ncurses? [duplicate]

This is what says on http://invisible-island.net/ncurses/ncurses.faq.html#multithread
If you have a program which uses curses in more than one thread, you will almost certainly see odd behavior. That is because curses relies upon static variables for both input and output. Using one thread for input and other(s) for output cannot solve the problem, nor can extra screen updates help. This FAQ is not a tutorial on threaded programming.
Specifically, it mentions it is not safe even if input and output are done on separate threads. Would it be safe if we further use a mutex for the whole ncurses library so that at most one thread can be calling any ncurses function at a time? If not, what would be other cheap workarounds to use ncurses safely in multi-thread application?
I'm asking this question because I notice a real application often has its own event loop but relies on ncurses getch function to get keyboard input. But if the main thread is block waiting in its own event loop, then it has no chance to call getch. A seemingly applicable solution is to call getch in a different thread, which hasn't caused me a problem yet, but as what says above is actually not safe, and was verified by another user here. So I'm wondering what is the best way to merge getch into an application's own event loop.
I'm considering making getch non-blocking and waking up the main thread regularly (every 10-100 ms) to check if there is something to read. But this adds an additional delay between key events and makes the application less responsive. Also, I'm not sure if that would cause any problems with some ncurses internal delay such as ESCDELAY.
Another solution I'm considering is to poll stdin directly. But I guess ncurses should also be doing something like that and reading the same stream from two different places looks bad.
The text also mentions the "ncursest" or "ncursestw" libraries, but they seem to be less available, for example, if you are using a different language binding of curses. It would be great if there is a viable solution with the standard ncurses library.
Without the thread-support, you're out of luck for using curses functions in more than one thread. That's because most of the curses calls use static or global data. The getch function for instance calls refresh which can update the whole screen—using the global pointers curscr and stdscr. The difference in the thread-support configuration is that global values are converted to functions and mutex's added.
If you want to read stdin from a different thread and run curses in one thread, you probably can make that work by checking the file descriptor (i.e., 0) for pending activity and alerting the thread which runs curses to tell it to read data.

How and when are interrupt key combination,such as CTRL-C, translated to signals?

Using a Linux OS, I need to transparently pass all keystrokes from remote connections to local hardware connections. This is fairly straight forward for everything but signals. I know I can register handlers for each signal that capture and pass the keystroke, but this seems like a hack that might not work for edge cases (what if someone changes the interrupt key combo mapping, would I be double passing the keystrokes, etc.).
In order to come up with a better solution, I really need to know more about how the key combos, IE: CTRL-C, become signals, IE: SIGINT.
How does the keycombo become a Signal (in the kernel, client application, runtime)?
Is the keystroke still passed? If CTRL-C has a null handler, does STDIN still get CTRL-C?
What is the best way to not interpret any keystroke as a signal combination? Can this be done in TTY or Environment settings?
The key combo is turned into a signal by the terminal's line discipline, a layer in the TTY subsystem. Here's the description from Wikipedia:
For example, the standard line discipline processes the data it receives from the hardware driver and from applications writing to the device according to the requirements of a terminal on a Unix-like system. On input, it handles special characters such as the interrupt character (typically Control-C) and the erase and kill characters (typically backspace or delete, and Control-U, respectively) and, on output, it replaces all the LF characters with a CR/LF sequence.
The keypress is not passed, it's suppressed when the signal is raised. If the process has no SIGINT handler, the kernel will terminate it.
You can obviously override this: that's how tools like ssh work. You can easily do this by setting the terminal to "raw" mode, e.g. with the shell command stty raw. If you then run cat -vE you can hit any keys you want and see them printed in caret notation. Make sure to plan on a way to close the terminal because you can obviously not Ctrl-C/Z/D out of it anymore.
How does the keycombo become a Signal (in the kernel, client application, runtime)?
This translation is performed as part of the kernel's TTY line discipline. There are a number of other translations performed as part of this process, like end-of-file (^D), stop signalling (^Z), character echo, basic line editing (backspace, ^U, etc), and DEL/BS and CR/CRLF translation.
Is the keystroke still passed? If CTRL-C has a null handler, does STDIN still get CTRL-C?
No. Control characters which are handled by the line discipline are not passed to the foreground process.
What is the best way to not interpret any keystroke as a signal combination? Can this be done in TTY or Environment settings?
Use tcgetattr() and tcsetattr() to change the terminal attributes. The c_cc element of struct termios contains settings for control characters; you can disable some or all elements by setting them to _POSIX_VDISABLE.
(You can also use cfmakeraw() -- documented at the same page as above -- to clear most elements of a struct termios, giving you a mostly "raw" terminal. However, this may disable more terminal behaviors than you expect.)

Implementing commands history triggered by arrow up/down keys in C

I have implemented my own shell which executes all existing UNIX commands and several custom commands. What I need to do is, to access previous commands by using arrow up/down keys in the way exactly how UNIX terminal does.
I have found that I could use getch() method of <curses.h> library. Even if I couldn't figure out how to use that method to expect non-blocking keyboard input, the real problem with that is that I need to compile the program to be able use it as following: gcc myShell.c -lcurses
What I have to do is probably using a signal to listen background input without expecting Enter input. I have also tried to fork() to create another process which will be responsible of waiting arrow keys inputs and then used getch() method to be able to catch the key but it didn't work either.
Since I already use read() method to read the command line inserted by the user. This arrow key input should be completely independent from the existing input reading.
Some answers in Stackoverflow points the <conio.h> library which does not exist in UNIX, hence unfortunately platform dependent.
To summarize briefly, I need to read arrow up/down keys in the background in my own shell and I have to do that platform independent and without being have to type something other than gcc myShell.c for compilation. It also should be captured without Enter press.
If any details about my question is not clear enough, please let me know to explain with more details as I could.
Thanks in advance!
The problem here is with the terminal you're using, not your program. For example, Windows command prompt will buffer input before even sending it to your program, so there is no platform-independent way to force the terminal to give you that data. I suspect curses has a platform-dependent way to turn off that buffering, and thus can get the character.
See answers to this question for more details: How to avoid press enter with any getchar()

How do you impose a time limit for input in ncurses when using getstr?

Suppose I would like to limit the amount of time a user has to enter text when using the getstr function in ncurses. How would I achieve this?
From this site:
timeout(milliseconds) --- By default, if you call getch(), it will wait forever for the user to press a key. If you call timeout(0) before calling getch(), it won't wait at all -- if the user has already typed a character, it will be returned; if not, the predefined constant ERR will be returned instead. If you specify a positive value for the parameter to timeout(), then getch() will be willing to wait that many milliseconds before returning. For example, if you say timeout(1000) and then call getch(), it will wait up to one second. If the user has typed a character by that time, it will be returned; otherwise, the return value will be ERR. You can call timeout(-1) to reset getch() to its default behavior, waiting forever. You can use timeout() to write an event-driven program that does some kind of animation continually, but still responds when the user presses a key. The main() function would typical look something like:

Simple Linux Shell. stop command

I am trying to implement a simple shell in linux and one of the features it should have is to enable to user to press <ctrl+D> and make it stop whatever it is doing - Basicly exactly what <ctrl+C> does in Bash.
Now I was wondering on the easiest way to do this, and I was thinking of some kind of key listener which would make the current task stop. Now the only way I could think of doing this would be to have a separate thread which would force stop and return the main thread to the base state, where it can take new input.
A separate thread would be the only way to "constantly" listen for the correct keypress.
I was hoping for some thoughts on this and possibly a better way to go about it.
[Edit]
I am trying to make a simple shell which can execute external programs, print/change directory, print/set path, print command history, invoke commands from history and print/set/remove command aliases. The CTRL-D is meant to be the equivalent of the CTRL-C in Bash, which allows the user to immediately exit a currently running program, not to exit the shell itself. [/Edit]
Why don't you just handle Ctrl-C?
Here is just one of many SO disussions on trapping the signal: Catch Ctrl-C in C
Ctrl-D generally represents EOF on standard input. You shouldn't mess with it.
If you want the Control-D character to generate an interrupt for you, then:
You need to map the EOF character to something other than Control-D.
You need to map the interrupt character to Control-D.
You do this in POSIX with the <termios.h> header and the functions:
tcgetattr()
tcsetattr()
You'd retrieve the current attributes in a struct termios using tcgetattr(). You'd make a copy of the structure, and modify (for sake of argument) the copy, changing the elements of the c_cc array indexed by VINTR and VEOF (plus any other changes you want to make), and then setting the new attributes using tcsetattr(). You'd also arrange to ensure that you restore the original terminal settings (by another call to tcsetattr() using the original set of attributes retrieved with tcgetattr()) before your shell exits. This might be done by a handler registered with atexit(), or by other mechanisms. You should endeavour to reset the terminal attributes under all circumstances. You can't do anything about a SIGKILL killing you.
While you're testing this, make a little script for yourself:
echo stty $(stty -g) > sane
chmod u+x sane
That records the current (presumably sane) terminal settings in a form that is designed for stty to read reliably. If (when) you have problems with your shell, you can use Control-JsaneControl-J to run the script and reset your terminal back to the known sane settings. This is also useful if you're developing programs that use the curses library.
Unless my comment on the other answer is incorrect, I think what you should do is:
if (!fgets(input, sizeof(input), stdin) == NULL)
{
... do cleanup here ...
exit(0);
}
or something equivalent to that.

Resources