How to read terminal's input buffer immediately after keypress - c

I want to read arrow keypresses in my c program and replace them(immediately in the terminal itself) by some other string. I am trying to implement the bash history functionality as in unix terminals. I wrote this code.
int
main(int argc, char *argv[])
{
char c;
char str[1024];
int i = 0;
while((c = fgetc(stdin)) != '\n'){
if(((int)c) == 27){
c=fgetc(stdin);
c=fgetc(stdin);
if (c == 'A')
{
printf("%c[A%c[2K",27, 27);
printf("UP");
}
}
str[i++] = c;
}
printf("\n");
return 0;
}
But, this doesn't work because terminals wait for a newline or EOF to send the input buffer to stdin. So, I have to press enter/return key to analyze the user input.
Here in this answer the user mentioned to use system ("/bin/stty raw"); but this replaces all the default terminal behaviours (e.g. backspace, delete etc).
So, is there any way I can read/manipulate the terminals input buffer directly and tweak the buffer itself if arrow keypress is detected?
Environment - Ubuntu (Linux)
Update 1: Is there a method to change the signal/interrupt (default is pressing enter key) that makes the terminal send the stored input to buffer? This may also help me achieve the same behaviour.
Final Code:
I found out the ASCII characters for specific keypresses by checking output of strace bash
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#define ESCAPE '\33'
#define BACKSPACE '\177'
#define DELETE '~'
int main(){
struct termios termios_p;
struct termios termios_save;
tcgetattr(0, &termios_p);
termios_save = termios_p;
termios_p.c_lflag &= ~(ICANON|ECHO);
tcsetattr(0,TCSANOW, &termios_p);
char buff;
while(read(0, &buff, 1) >= 0){
if(buff > 0){
if(buff == ESCAPE){
read(0, &buff, 1);
read(0, &buff, 1);
if(buff == 'A'){
write(2, "up", 2);
}else if(buff == 'B'){
write(2, "down", 4);
}else if(buff == 'C'){
write(2, "\33[C", 3);
}else if(buff == 'D'){
write(2, "\10", 2);
}
}else if(buff == BACKSPACE){
write(2, "\10\33[1P", 5);
}else if(buff == DELETE){
write(2, "\33[1P", 4);
}else{
write(2,&buff,1);
}
// write(2,&buff,1);
if(buff == 'q'){
break;
}
}
}
tcsetattr(0,TCSANOW, &termios_save);
return 0;
}

It seems you are looking for something like this.
The program essentially waits for user input. If up arrow key is pressed, the program prints "Arrow key pressed" and then exits. If anything else is pressed, it waits for the user to complete what he is typing and prints it, then exits.
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
struct termios oldt, newt;
char ch, command[20];
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);
while(1)
{
ch = getchar();
if (ch == '\033')
{ printf("Arrow key\n"); ch=-1; break;}
else if(ch == -1) // by default the function returns -1, as it is non blocking
{
continue;
}
else
{
break;
}
}
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF)
{
ungetc(ch,stdin);ith
putchar(ch);
scanf("%s",command);
printf("\n%s\n",command);
return 1;
}
return 0;
}

On my Linux Machine I use ncurses:
#include<stdio.h>
#include<ncurses.h>
void checkArrow(){
int ch;
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
printw("Press q to Exit\t");
do{
switch(ch){
case KEY_UP: {
printw("\n\nUp Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_DOWN:{
printw("\n\nDown Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_LEFT:{
printw("\n\nLeft Arrow\n");
printw("Press q to Exit\t");
break;
}
case KEY_RIGHT:{
printw("\n\nRight Arrow\n");
printw("Press q to Exit\t");
break;
}
}
}while((ch = getch()) != 'q');
printw("\n\n\t\t\t-=GoodBye=-!\n");
refresh();
endwin();
}
int main(){
checkArrow();
return 0;
}
Up Arrow
Press q to Exit
Down Arrow
Press q to Exit
Left Arrow
Press q to Exit
Right Arrow
Press q to Exit

Related

How to execute the key-corresponding option without an Enter? (C programming) [duplicate]

This question already has answers here:
Capture characters from standard input without waiting for enter to be pressed
(21 answers)
Closed last year.
I'm trying to write a "more"(just like "more" instruction in Linux) program in C.
Quit when "q" is pressed and show one more page when " " is pressed.
But everytime I have to press Enter after the command.
I tried to use setbuf() and setvbuf() but it doesn't work.Why?How should I do?
FILE *fp_tty; //read from keyboard
fp_tty = fopen("/dev/tty", "r");
setbuf(fp_tty, NULL);
see_more(fp_tty);
......
int see_more(FILE *cmd) {
int c;
printf("\033[7m more? \033[m");
while ((c = getc(cmd)) != EOF) {
if (c == 'q') {
return 0; //quit
}
if (c == ' ') {
return PAGELEN; //next page
}
if (c == '\n') {
return 1; //1 line
}
}
return 0;
}
Linux shares the tty rules of most Unix systems. When a device is seen as a terminal input meaning here something that receive key presses from a human being, the input is by default line oriented. The rationale is that the user is allowed to press a wrong key, cancel it, and finaly press the correct key. In that case, the tty driver handles the fix and only present the final string to the application.
That works fine for line oriented application, but would be foolish for plain screen ones like emacs or vi (yeah, they are from the 70's too...), so the tty driver can be set in raw mode to immediately pass any character to the application (other options exist...). That what the curses library use under the hood to be able to immediately react to any key press.
TL/DR: you must put the tty driver in raw mode to be able to immediately process a character. The details are on the tcflush man page.
As pointed out by #rici in comments, termios can help switching the terminal to non-canonical mode.
In your case you can handle those keys using:
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#define KEY_ESCAPE 0x001b
#define KEY_ENTER 0x000a
static struct termios term, oterm;
static int getch(void);
static int kbhit(void);
static int kbesc(void);
static int kbget(void);
static int getch(void)
{
int c = 0;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
return c;
}
static int kbhit(void)
{
int c = 0;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(0, TCSANOW, &term);
c = getchar();
tcsetattr(0, TCSANOW, &oterm);
if (c != -1) ungetc(c, stdin);
return ((c != -1) ? 1 : 0);
}
static int kbesc(void)
{
int c;
if (!kbhit()) return KEY_ESCAPE;
c = getch();
while (kbhit()) getch();
return c;
}
static int kbget(void)
{
int c;
c = getch();
return (c == KEY_ESCAPE) ? kbesc() : c;
}
int main(void)
{
int c;
while (1) {
c = kbget();
if (c == 'q')
{
return 0; //quit
}
if (c == ' ') {
return PAGELEN; //next page
}
if (c == '\n') {
return 1; //1 line
}
}
return 0;
}

Why is the input displayed after the enter key has been pressed?

#include <stdio.h>
/* copy input to output */
main()
{
int c;
c = getchar();
while(c != EOF)
{
putchar(c);
c = getchar();
}
return 0;
}
In the given code, the program displays the input character.
It reads every character one by one (into the variable 'c') and outputs the same read characters simultaneously. The program terminates when the EOF character is given as input.
When I ran the code in my IDE (Code::Blocks 16.01) and input a string,
eg: Hi! My name is C.\n
The output is displayed after '\n' and not simultaneously.
Isn't the output supposed to be as - "HHii!! MMyy nnaammee iiss CC.."?
Bold letters indicate the output.
include:
#include <unistd.h>
#include <termios.h>
before your code in main insert:
struct termios term, term_orig;
if(tcgetattr(STDIN_FILENO, &term_orig)) {
printf("tcgetattr failed\n");
return -1;
}
term = term_orig;
term.c_lflag &= ~ICANON;
if (tcsetattr(STDIN_FILENO, TCSANOW, &term)) {
printf("tcsetattr failed\n");
return -1;
}
after your code insert:
if (tcsetattr(STDIN_FILENO, TCSANOW, &term_orig)) {
printf("tcsetattr failed\n");
return -1;
}
Because by default, input from terminal is line-buffered. So your program get a whole line after you press Enter.
To disable buffering in Unix & Linux systems, try this:
#include <unistd.h>
#include <termios.h>
int disableLineBuffer(void){
struct termios term, oldterm;
if (tcgetattr(STDIN_FILENO, &term)) return -1;
oldterm = term;
term.c_lflag &= ~(ECHO | ICANON);
if (tcsetattr(STDIN_FILENO, TCSANOW, &term)) return -1;
return 0;
}
In Windows, go and do this instead:
#include <windows.h>
BOOL WINAPI DisableLineBuffer(void){
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
if (!GetConsoleMode(hInput, &mode)) return FALSE;
mode &= ~ENABLE_LINE_INPUT;
if (!SetConsoleMode(hInput, mode)) return FALSE;
return TRUE;
}
Be sure to revert changes to the console before your program exits, or you may have trouble doing anything else in your terminal.

How to use C programming language creating a shell that can implement history capture up arrow?

I have saved the history of the previous commands in a two-dimensional array. But I don't know how to check the up arrow.
How to use the C programming language implementing this feature (in Linux)?
Thanks to n.m. for good advice.
The following is the example of usage libreadline:
// rltest.c
#include <stdio.h>
#include <stdlib.h>
#include <readline.h>
#include <history.h>
int main(void)
{
char* input, shell_prompt[100];
// Configure readline to auto-complete paths when the tab key is hit.
rl_bind_key('\t', rl_complete);
// while work is not 0 program executes the loop
int work = 1;
printf("Commands to use: name, ver, exit \n");
// loop for working with commands
while(work) {
// Build prompt string.
snprintf(shell_prompt, sizeof(shell_prompt), "your command $ ");
// Display prompt and read input
input = readline(shell_prompt);
// Check for EOF.
if (!input)
break;
// Add input to history.
add_history(input);
// Command analysis and execution
if( 0 == strcmp(input, "exit") )
{
printf("Bye!\n");
work = 0;
}
if( 0 == strcmp(input, "name") )
{
printf("I'm readline example\n");
}
if( 0 == strcmp(input, "ver") )
{
printf("My version is 0.1\n");
}
// ...
// Free input for future use
free(input);
}
return 0;
}
to compile this example:
1) install readline library
apt-get install libreadline-dev
2) compile program as
gcc rltest.c -I/usr/include/readline -lreadline
Try the following example:
#include <stdio.h>
#include <termios.h>
// constants for sizes
#define LINES 5
#define MAX_LEN 50
// this is just for demonstration
// read more about screen cleaning http://www.cplusplus.com/articles/4z18T05o/
void ClearScreen()
{
printf("\033[2J");
printf("\033[0;0f");
}
// function based on termios library
// see more at http://stackoverflow.com/questions/7469139/what-is-equivalent-to-getch-getche-in-linux
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;
}
int main(void)
{
char strBuff[LINES][MAX_LEN] = {"The first line",
"This is the longest line in this buffer",
"Just a middle line",
"Short",
"The last line"};
char ch; // one character input buffer
int cnt = 0; // number of current line to be shown
// loop for showing lines
ClearScreen();
do{
// show current line
printf("%s", strBuff[cnt]);
// read input (arrows. characters, etc.
ch = getch_(0);
// arrows detection in input
if(ch == 27)
{
ch = getch_(0);
if( ch == 91)
{
ch = getch_(0);
if(ch == 66) // up arrow
{
cnt++;
if(cnt >= LINES)
cnt = 0;
}
else
if(ch == 65) // down arrow
{
cnt--;
if(cnt < 0)
cnt = LINES-1;
}
}
}
// cleaning screen before next output
ClearScreen();
}while(ch != 'q'); // press 'q' for exit
}

breaking loop with keypress in linux c

I need to write a program in C language which will be doing something like this:
For example, when I will press "a", terminal will be writing that typed character in the unending loop like this: aaaaaaaaaaaaaaaa...until another key, for example "b" will be pressed. Final output should look like this: aaaaaaaaabbbbbbq (q should terminate the program).
My code here:
int main(int argc, char** argv) {
int c;
static struct termios staryTermios, novyTermios;
tcgetattr(STDIN_FILENO, &staryTermios);
novyTermios = staryTermios;
novyTermios.c_lflag &= ~(ICANON);
tcsetattr(STDIN_FILENO, TCSANOW, &novyTermios);
while ((c = getchar()) != 'q') {
putchar(c);
}
tcsetattr( STDIN_FILENO, TCSANOW, &staryTermios);
return 0;
}
this version writes the typed characters only once and then it waits for another keypress
To achieve the result you want, you need to make the standard input non-blocking. You can do that with this minor adaptation of your code. It worked fine on Mac OS X 10.7.5. Note that getchar() returns EOF when there's no character ready to read (which will be most of the time; neither you nor I can type fast enough to matter to a modern computer). I'm mildly concerned that on some systems, once getchar() has returned EOF once when there was no character to read, it might never return anything other than EOF again, but that wasn't a problem for Mac OS X.
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
static void err_exit(const char *msg);
int main(void)
{
int c;
int oc = '\0';
struct termios staryTermios, novyTermios;
int oflags, nflags;
if (tcgetattr(STDIN_FILENO, &staryTermios) != 0)
err_exit("tcgetattr() failed");
novyTermios = staryTermios;
novyTermios.c_lflag &= ~(ICANON);
if (tcsetattr(STDIN_FILENO, TCSANOW, &novyTermios) != 0)
err_exit("tcsetattr() failed to set standard input");
oflags = fcntl(STDIN_FILENO, F_GETFL);
if (oflags < 0)
err_exit("fcntl() F_GETFL failed");
nflags = oflags;
nflags |= O_NONBLOCK;
if (fcntl(STDIN_FILENO, F_SETFL, nflags) == -1)
err_exit("fcntl() F_SETFL failed");
while ((c = getchar()) != 'q')
{
if (c != EOF)
oc = c;
if (oc != '\0')
putchar(oc);
}
if (tcsetattr(STDIN_FILENO, TCSANOW, &staryTermios) != 0)
err_exit("tcsetattr() failed to reset standard input");
putchar('\n');
return 0;
}
static void err_exit(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
I guess you will need two threads which will both be accessing a common variable. The job of one thread would be to continuously print the common variable. The job of the second would be to wait for input from the keyboard and update the variable accordingly.

non blocking "Key pressed" test in C

I'm looking for a pretty simple thing:
I want to test if a key was pressed. any key.
If not, the program should continue its business.
So it must be a "non blocking" call.
I guess this question is probably equivalent to checking if the keyboard buffer has anything into it.
I guess such a function must exist in C for that, but I can't quite find it yet. All standard functions I find are "blocking" type, waiting for a key to be pressed before answering.
Note - I'm planning to use it for a Windows console program.
In windows you can use '_kbhit()' in conio.h
It is a non-standard function and may not be available on other platforms.
I know this is a bit old but here is some Linux code that works
kbhit.h:
#ifndef KBHIT_H__
#define KBHIT_H__
void init_keyboard(void);
void close_keyboard(void);
int kbhit(void);
int readch(void);
#endif
kbhit.c:
#include "kbhit.h"
#include <termios.h>
#include <unistd.h> // for read()
static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard(void)
{
tcgetattr(0,&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &new_settings);
}
void close_keyboard(void)
{
tcsetattr(0, TCSANOW, &initial_settings);
}
int kbhit(void)
{
unsigned char ch;
int nread;
if (peek_character != -1) return 1;
new_settings.c_cc[VMIN]=0;
tcsetattr(0, TCSANOW, &new_settings);
nread = read(0,&ch,1);
new_settings.c_cc[VMIN]=1;
tcsetattr(0, TCSANOW, &new_settings);
if(nread == 1)
{
peek_character = ch;
return 1;
}
return 0;
}
int readch(void)
{
char ch;
if(peek_character != -1)
{
ch = peek_character;
peek_character = -1;
return ch;
}
read(0,&ch,1);
return ch;
}
main.c:
#include "kbhit.h"
#define Esc 27
int main(void)
{
init_keyboard(); // for kbhit usage
do {
if(kbhit())
{
ch = tolower(readch());
if(ch == Esc || ch == 'q') break;
if(ch=='s') GetNewTimerValue(TIMER_1);
if(ch=='f') GetNewTimerValue(TIMER_2);
if(ch=='l') {rotateFields();}
}
usleep(330000);
} while(1);
close_keyboard();
return 0;
}

Resources