53

I'm writing a programm that's using getch() to scan for arrow keys. My code so far is:

switch(getch()) {
    case 65:    // key up
        break;
    case 66:    // key down
        break;
    case 67:    // key right
        break;
    case 68:    // key left
        break;
}

Problem is that when I press 'A', 'B', 'C' or 'D' the code will also executed, because 65 is the decimal code for 'A', etc...

Is there a way to check for an arrow key without call others?

Thanks!

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
qwertz
  • 14,614
  • 10
  • 34
  • 46
  • 2
    It's been ages since I played with this, it's not the least bit standardized... but back when I played with `getch()`, for "special" keys it actually returned twice. The first time it returned a 0, then a code for the special key, so that you could tell it apart from other keys. – FatalError May 05 '12 at 15:31
  • `65` is only for character `A`. You have to use the control codes to receive these keys. See this post. http://stackoverflow.com/questions/2876275/what-are-the-ascii-values-of-up-down-left-right – P.P May 05 '12 at 15:40
  • @FatalError Sorry, I'm around 7 years late, but what you said has piqued my curiosity. How is it possible that getch() can return twice? A function can only return once, right? – Anchith Acharya Nov 06 '19 at 16:10
  • 3
    @AnchithAcharya: You're in luck, because I'm still around 7 years later ;-). What I meant is that to read a "special key" you had to actually call `getch()` twice. The first call would return 0 (i.e. to say that the next value would be a special key). Then on the second call to `getch()` it would return a distinct value indicating which special key had been pressed. Mind you, `getch()` is a non-standard artifact from the DOS days, so it could well have varied by compiler. But here's an example on MSDN: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getch-getwch?view=vs-2019 – FatalError Nov 06 '19 at 16:40
  • @FatalError Ah, I got it now. Thanks! – Anchith Acharya Nov 06 '19 at 19:01
  • @FatalError Urgh. I was thinking what are you on about with DOS and then it occurred to me that this question doesn't refer to `ncurses`. That explains a lot. I did dabble with DOS console IO decades ago but I don't remember having to use `getch()` twice. Then again as much as I loved C (and still do) with DOS I really loved assembly - well both but you know. Anyway the point here is that this post could be ambiguous since it doesn't have any header files (answer does below) and doesn't specify OS/library etc. Even if I was okay editing questions/answers I wouldn't know what to specify it as! – Pryftan Nov 17 '19 at 17:46
  • [Solution](https://stackoverflow.com/a/75499310/6013016) – Scott Feb 19 '23 at 23:29

11 Answers11

89

By pressing one arrow key getch will push three values into the buffer:

  • '\033'
  • '['
  • 'A', 'B', 'C' or 'D'

So the code will be something like this:

if (getch() == '\033') { // if the first value is esc
    getch(); // skip the [
    switch(getch()) { // the real value
        case 'A':
            // code for arrow up
            break;
        case 'B':
            // code for arrow down
            break;
        case 'C':
            // code for arrow right
            break;
        case 'D':
            // code for arrow left
            break;
    }
}
Fred Larson
  • 60,987
  • 18
  • 112
  • 174
qwertz
  • 14,614
  • 10
  • 34
  • 46
  • 5
    Is there some Unix-land version of `getch` where this answer is correct? The codes here are what you'd get from most terminals when using `getchar`. They're not what you'd get from `getch` from ``. – Cheers and hth. - Alf May 17 '16 at 05:28
  • 1
    Do you know why in my terminal I see a 'O' instead of '[' as second character? FYI, I am running zsh inside a tmux session. Thanks! – Muffo Jul 21 '17 at 23:08
  • 1
    @Cheersandhth.-Alf Yes, this is Unix-land `getch`, specifically [`getch` from the ncurses library](https://invisible-island.net/ncurses/man/curs_getch.3x.html) (with "keypad" mode disabled). – zwol Jul 28 '19 at 14:12
  • `getch() ->getchar()` – EsmaeelE Jan 27 '20 at 15:50
  • On Windows, _getch (from conio.h) must be called twice to handle an arrow key. The first return value is 224, the second is 'A' , 'B', etc. as above. – yoyo Jul 08 '20 at 18:03
  • @Cheersandhth.-Alf you can use ncurses however if you don't want to then you have to enable terminal raw mode. Have a look at this [link](https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html) – DarthCucumber Apr 23 '21 at 07:03
  • [Solution](https://stackoverflow.com/a/75499310/6013016) – Scott Feb 19 '23 at 23:30
25

getch () function returns two keycodes for arrow keys (and some other special keys), as mentioned in the comment by FatalError. It returns either 0 (0x00) or 224 (0xE0) first, and then returns a code identifying the key that was pressed.

For the arrow keys, it returns 224 first followed by 72 (up), 80 (down), 75 (left) and 77 (right). If the num-pad arrow keys (with NumLock off) are pressed, getch () returns 0 first instead of 224.

Please note that getch () is not standardized in any way, and these codes might vary from compiler to compiler. These codes are returned by MinGW and Visual C++ on Windows.

A handy program to see the action of getch () for various keys is:

#include <stdio.h>
#include <conio.h>

int main ()
{
    int ch;

    while ((ch = _getch()) != 27) /* 27 = Esc key */
    {
        printf("%d", ch);
        if (ch == 0 || ch == 224)
            printf (", %d", _getch ()); 
        printf("\n");
    }

    printf("ESC %d\n", ch);

    return (0);
}

This works for MinGW and Visual C++. These compilers use the name _getch () instead of getch () to indicate that it is a non-standard function.

So, you may do something like:

ch = _getch ();
if (ch == 0 || ch == 224)
{
    switch (_getch ())
    {
        case 72:
            /* Code for up arrow handling */
            break;

        case 80:
            /* Code for down arrow handling */
            break;

        /* ... etc ... */
    }
}
7

So, after alot of struggle, I miraculously solved this everannoying issue ! I was trying to mimic a linux terminal and got stuck at the part where it keeps a command history which can be accessed by pressing up or down arrow keys. I found ncurses lib to be painfuly hard to comprehend and slow to learn.

char ch = 0, k = 0;
while(1)
{
  ch = getch();
  if(ch == 27)                  // if ch is the escape sequence with num code 27, k turns 1 to signal the next
    k = 1;
  if(ch == 91 && k == 1)       // if the previous char was 27, and the current 91, k turns 2 for further use
    k = 2;
  if(ch == 65 && k == 2)       // finally, if the last char of the sequence matches, you've got a key !
    printf("You pressed the up arrow key !!\n");
  if(ch == 66 && k == 2)                             
    printf("You pressed the down arrow key !!\n");
  if(ch != 27 && ch != 91)      // if ch isn't either of the two, the key pressed isn't up/down so reset k
    k = 0;
  printf("%c - %d", ch, ch);    // prints out the char and it's int code

It's kind of bold but it explains alot. Good luck !

kundrata
  • 651
  • 3
  • 12
  • 24
3

The keypad will allow the keyboard of the user's terminal to allow for function keys to be interpreted as a single value (i.e. no escape sequence).

As stated in the man page:

The keypad option enables the keypad of the user's terminal. If enabled (bf is TRUE), the user can press a function key (such as an arrow key) and wgetch returns a single value representing the function key, as in KEY_LEFT. If disabled (bf is FALSE), curses does not treat function keys specially and the program has to interpret the escape sequences itself. If the keypad in the terminal can be turned on (made to transmit) and off (made to work locally), turning on this option causes the terminal keypad to be turned on when wgetch is called. The default value for keypad is false.

tijko
  • 7,599
  • 11
  • 44
  • 64
2

Actually, to read arrow keys one need to read its scan code. Following are the scan code generated by arrow keys press (not key release)

When num Lock is off

  • Left E0 4B
  • Right E0 4D
  • Up E0 48
  • Down E0 50

When Num Lock is on these keys get preceded with E0 2A

  • Byte E0 is -32
  • Byte 48 is 72 UP
  • Byte 50 is 80 DOWN

    user_var=getch();
    if(user_var == -32)
    {
        user_var=getch();
        switch(user_var)
        {
        case 72:
            cur_sel--;
            if (cur_sel==0)
                cur_sel=4;
            break;
        case 80:
            cur_sel++;
            if(cur_sel==5)
                cur_sel=1;
            break;
    
        }
    }
    

In the above code I have assumed programmer wants to move 4 lines only.

Shubham
  • 172
  • 8
2

how about trying this?

void CheckKey(void) {
int key;
if (kbhit()) {
    key=getch();
    if (key == 224) {
        do {
            key=getch();
        } while(key==224);
        switch (key) {
            case 72:
                printf("up");
                break;
            case 75:
                printf("left");
                break;
            case 77:
                printf("right");
                break;
            case 80:
                printf("down");
                break;
        }
    }
    printf("%d\n",key);
}

int main() {
    while (1) {
        if (kbhit()) {
            CheckKey();
        }
    }
}

(if you can't understand why there is 224, then try running this code: )

#include <stdio.h>
#include <conio.h>

int main() {
    while (1) {
        if (kbhit()) {
            printf("%d\n",getch());
        }
    }
}

but I don't know why it's 224. can you write down a comment if you know why?

m4ndeokyi
  • 21
  • 2
  • 2
    Please, do a little reformatting of your code. Also, add explanations of _why_ and _how_ does your code work, especially _magic numbers_ like `224`. – lolbas Mar 21 '18 at 13:53
1

for a solution that uses ncurses with working code and initialization of ncurses see getchar() returns the same value (27) for up and down arrow keys

ralf htp
  • 9,149
  • 4
  • 22
  • 34
0

I have written a function using getch to get arrow code. it's a quick'n'dirty solution but the function will return an ASCII code depending on arrow key : UP : -10 DOWN : -11 RIGHT : -12 LEFT : -13

Moreover,with this function, you will be able to differenciate the ESCAPE touch and the arrow keys. But you have to press ESC 2 time to activate the ESC key.

here the code :

char getch_hotkey_upgrade(void)
{
  char ch = 0,ch_test[3] = {0,0,0};

    ch_test[0]=getch();

    if(ch_test[0] == 27)
    {
        ch_test[1]=getch();

        if (ch_test[1]== 91)
        {
            ch_test[2]=getch();

            switch(ch_test[2])
            {
            case 'A':
                //printf("You pressed the up arrow key !!\n");
                //ch = -10;
                ch = -10;
                break;
            case 'B':
                //printf("You pressed the down arrow key !!\n");
                ch = -11;
                break;
            case 'C':
                //printf("You pressed the right arrow key !!\n");
                ch = -12;
                break;
            case 'D':
                //printf("You pressed the left arrow key !!\n");
                ch = -13;
                break;
            }
        }
        else
         ch = ch_test [1];
    }
    else
        ch = ch_test [0];
  return ch;
}
0

I'm Just a starter, but i'v created a char(for example "b"), and I do b = _getch(); (its a conio.h library's command) And check

If (b == -32)
b = _getch();

And do check for the keys (72 up, 80 down, 77 right, 75 left)

Rahul
  • 10,830
  • 4
  • 53
  • 88
0
    void input_from_key_board(int &ri, int &ci)
{
    char ch = 'x';
    if (_kbhit())
    {
        ch = _getch();
        if (ch == -32)
        {
            ch = _getch();
            switch (ch)
            {
            case 72: { ri--; break; }
            case 80: { ri++; break; }
            case 77: { ci++; break; }
            case 75: { ci--; break; }

            }
        }
        else if (ch == '\r'){ gotoRowCol(ri++, ci -= ci); }
        else if (ch == '\t'){ gotoRowCol(ri, ci += 5); }
        else if (ch == 27) { system("ipconfig"); }
        else if (ch == 8){ cout << " "; gotoRowCol(ri, --ci); if (ci <= 0)gotoRowCol(ri--, ci); }
        else { cout << ch; gotoRowCol(ri, ci++); }
        gotoRowCol(ri, ci);
    }
}
-1

Try this...

I am in Windows 7 with Code::Blocks

while (true)
{
    char input;
    input = getch();

    switch(input)
    {
    case -32: //This value is returned by all arrow key. So, we don't want to do something.
        break;
    case 72:
        printf("up");
        break;
    case 75:
        printf("left");
        break;
    case 77:
        printf("right");
        break;
    case 80:
        printf("down");
        break;
    default:
        printf("INVALID INPUT!");
        break;
    }
}
  • This doesn't really answer OP question, as your "UP" will be triggered by the H character for example as well. – Gar Jun 03 '19 at 09:53