1
while(ch != 'q') 
{
  printf("looping\n");
  sleep(1);
  if(kbhit()) 
   {
    ch = readch();
    printf("you hit %c\n",ch);
   }
}

This code gives me a blocking getch() like functionality. I am trying to use this code to capture up down arrow keys.

Added: Trying to capture key codes of up arrow gives me 3 chars 27, 91 and 65. Using if/else I am trying pattern matching but I only get 2 chars. Next one is captured when next key is pressed.

I want to capture full words using getchar() while always looking for certain keys all the time(esc, del etc.).

Alex Xander
  • 3,903
  • 14
  • 36
  • 43
  • Arrow keys (and even more, function keys) often generate multiple bytes in a terminal emulator. You could look at the terminal definitions in /usr/share/lib/terminfo (using infocmp to get a text representation) to see the diversity of sequences that used to be sent. These days, there are fewer genuine green-screens around (most terminals are windows running a terminal emulator instead), so there are fewer oddball terminal types to deal with. – Jonathan Leffler Oct 03 '09 at 13:59
  • 1
    Closely related (but not identical) question: http://stackoverflow.com/questions/267250/; and this is related too: http://stackoverflow.com/questions/905060/ – Jonathan Leffler Oct 03 '09 at 14:14
  • Character code 27 (aka 033 or 0x1B) is ESC or escape. Character code 91 is '['; character code 65 is A. Your terminal generates that 3-character sequence for an up arrow - so it is more or less emulating an ANSI terminal. – Jonathan Leffler Oct 03 '09 at 14:18

2 Answers2

3

I can't reproduce Your problem:

#include <unistd.h> 
#include <stdio.h> 
#include <ctype.h> 

#include "kbhit.h" /* http://linux-sxs.org/programming/kbhit.html */

int main(){
  init_keyboard();
  char ch='x';
  while( ch != 'q' ){
    printf("looping\n");
    sleep(1);
    if( kbhit() ){
      printf("you hit");
      do{
        ch = readch();
        printf(" '%c'(%i)", isprint(ch)?ch:'?', (int)ch );
      }while( kbhit() );
      puts("");
    }
  }
  close_keyboard();
}
sambowry
  • 2,436
  • 1
  • 16
  • 13
1

This example could help:

raw.c - raw mode demonstration

/* Raw mode demo */
/* See exactly what is being transmitted from the terminal. To do this
   we have to be more careful. */

#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>

struct termios oldtermios;

int ttyraw(int fd)
{
    /* Set terminal mode as follows:
       Noncanonical mode - turn off ICANON.
       Turn off signal-generation (ISIG)
        including BREAK character (BRKINT).
       Turn off any possible preprocessing of input (IEXTEN).
       Turn ECHO mode off.
       Disable CR-to-NL mapping on input.
       Disable input parity detection (INPCK).
       Disable stripping of eighth bit on input (ISTRIP).
       Disable flow control (IXON).
       Use eight bit characters (CS8).
       Disable parity checking (PARENB).
       Disable any implementation-dependent output processing (OPOST).
       One byte at a time input (MIN=1, TIME=0).
    */
    struct termios newtermios;
    if(tcgetattr(fd, &oldtermios) < 0)
        return(-1);
    newtermios = oldtermios;

    newtermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    /* OK, why IEXTEN? If IEXTEN is on, the DISCARD character
       is recognized and is not passed to the process. This 
       character causes output to be suspended until another
       DISCARD is received. The DSUSP character for job control,
       the LNEXT character that removes any special meaning of
       the following character, the REPRINT character, and some
       others are also in this category.
    */

    newtermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    /* If an input character arrives with the wrong parity, then INPCK
       is checked. If this flag is set, then IGNPAR is checked
       to see if input bytes with parity errors should be ignored.
       If it shouldn't be ignored, then PARMRK determines what
       character sequence the process will actually see.

       When we turn off IXON, the start and stop characters can be read.
    */

    newtermios.c_cflag &= ~(CSIZE | PARENB);
    /* CSIZE is a mask that determines the number of bits per byte.
       PARENB enables parity checking on input and parity generation
       on output.
    */

    newtermios.c_cflag |= CS8;
    /* Set 8 bits per character. */

    newtermios.c_oflag &= ~(OPOST);
    /* This includes things like expanding tabs to spaces. */

    newtermios.c_cc[VMIN] = 1;
    newtermios.c_cc[VTIME] = 0;

    /* You tell me why TCSAFLUSH. */
    if(tcsetattr(fd, TCSAFLUSH, &newtermios) < 0)
        return(-1);
    return(0);
}

int ttyreset(int fd)
{
    if(tcsetattr(fd, TCSAFLUSH, &oldtermios) < 0)
        return(-1);

    return(0);
}

void sigcatch(int sig)
{
    ttyreset(0);
    exit(0);
}

int main()
{
    int i;
    char c;

    /* Catch the most popular signals. */
    if((int) signal(SIGINT,sigcatch) < 0)
    {
        perror("signal");
        exit(1);
    }
    if((int)signal(SIGQUIT,sigcatch) < 0)
    {
        perror("signal");
        exit(1);
    }
    if((int) signal(SIGTERM,sigcatch) < 0)
    {
        perror("signal");
        exit(1);
    }

    /* Set raw mode on stdin. */
    if(ttyraw(0) < 0)
    {
        fprintf(stderr,"Can't go to raw mode.\n");
        exit(1);
    }

    while( (i = read(0, &c, 1)) == 1)
    {
        if( (c &= 255) == 0177) /* ASCII DELETE */
            break;
        printf( "%o\n\r", c);
    }

    if(ttyreset(0) < 0)
    {
        fprintf(stderr, "Cannot reset terminal!\n");
        exit(-1);
    }

    if( i < 0)
    {
        fprintf(stderr,"Read error.\n");
        exit(-1);
    }

    return 0;
}

(backspace to stop the demo, origin)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    I have seen this code and My code gives me similar codes. But how do i do the pattern matching with three codes for UP key. My waits for the next key to be pressed. – Alex Xander Oct 03 '09 at 14:05
  • modifying the example to: char pp = 0; char p = 0; while( (i = read(0, &c, 1)) == 1) { if (pp == 033 && p == 0133 && (c &= 255) == 0102) /* DOWN */ break; if (c == 0177) /* ASCII DELETE */ break; printf( "%o, %o, %o\t%s\n\r", pp, p, c, &c); pp = p; p = c; } works on my linux box, but I would advise curse if you can use it (see http://www.linuxselfhelp.com/HOWTO/NCURSES-Programming-HOWTO/keys.html) –  Oct 03 '09 at 15:09
  • 1
    This makes use of read(), after which I can not use getchar(). I am using getchar() to capture full word. Also looking for certain keys all the time(esc, del etc.) – Alex Xander Oct 03 '09 at 16:29