Home > Software design >  Detect "Shift Arrow Key" in Windows C Console Application (win32)
Detect "Shift Arrow Key" in Windows C Console Application (win32)

Time:09-11

I found an old VT100 Terminal editor written in C that I wanted to convert into a Windows Terminal console application, since the new windows terminal supports VT100 escape codes.

One of the questions I had while porting the Application to C under windows was:

How do I detect key sequences such as "Shift Arrow Key" and "Shift Ctrl Arrow Key". When I try to use getch() it doesn't seem to detect the shift key in combination with arrow keys.

Here's How I'm currently detecting key presses in my Console Application:

#define KEY_BACKSPACE        0x101
#define KEY_ESC              0x102
#define KEY_INS              0x103
#define KEY_DEL              0x104
#define KEY_LEFT             0x105
#define KEY_RIGHT            0x106
#define KEY_UP               0x107
#define KEY_DOWN             0x108
#define KEY_HOME             0x109
#define KEY_END              0x10A
#define KEY_ENTER            0x10B
#define KEY_TAB              0x10C
#define KEY_PGUP             0x10D
#define KEY_PGDN             0x10E

#define KEY_CTRL_LEFT        0x10F
#define KEY_CTRL_RIGHT       0x110
#define KEY_CTRL_UP          0x111
#define KEY_CTRL_DOWN        0x112
#define KEY_CTRL_HOME        0x113
#define KEY_CTRL_END         0x114
#define KEY_CTRL_TAB         0x115

#define KEY_SHIFT_LEFT       0x116
#define KEY_SHIFT_RIGHT      0x117
#define KEY_SHIFT_UP         0x118
#define KEY_SHIFT_DOWN       0x119
#define KEY_SHIFT_PGUP       0x11A
#define KEY_SHIFT_PGDN       0x11B
#define KEY_SHIFT_HOME       0x11C
#define KEY_SHIFT_END        0x11D
#define KEY_SHIFT_TAB        0x11E

#define KEY_SHIFT_CTRL_LEFT  0x11F
#define KEY_SHIFT_CTRL_RIGHT 0x120
#define KEY_SHIFT_CTRL_UP    0x121
#define KEY_SHIFT_CTRL_DOWN  0x122
#define KEY_SHIFT_CTRL_HOME  0x123
#define KEY_SHIFT_CTRL_END   0x124

#define KEY_F1               0x125
#define KEY_F3               0x126
#define KEY_F5               0x127

#define KEY_ALT_LEFT         0x128
#define KEY_ALT_RIGHT        0x129
#define KEY_ALT_UP           0x130
#define KEY_ALT_DOWN         0x131
#define KEY_ALT_HOME         0x132
#define KEY_ALT_END          0x133
#define KEY_ALT_TAB          0x134
#define KEY_ALT_PGUP         0x135
#define KEY_ALT_PGDN         0x136

#define KEY_UNKNOWN          0xFFF
#if defined(WIN32) || defined(_WIN64)
    #include "windows.h"
    #include "conio.h"
#endif

int getkey_win32() {
    int c1;
    int c2;

    c1 = (int)getch();

    // unmapable  How to detect Shift   Arrow Key??
    //#define KEY_SHIFT_LEFT       0x116
    //#define KEY_SHIFT_RIGHT      0x117
    //#define KEY_SHIFT_UP         0x118
    //#define KEY_SHIFT_DOWN       0x119
    //#define KEY_SHIFT_PGUP       0x11A
    //#define KEY_SHIFT_PGDN       0x11B
    //#define KEY_SHIFT_HOME       0x11C
    //#define KEY_SHIFT_END        0x11D
    //#define KEY_SHIFT_CTRL_LEFT  0x11F
    //#define KEY_SHIFT_CTRL_RIGHT 0x120
    //#define KEY_SHIFT_CTRL_UP    0x121
    //#define KEY_SHIFT_CTRL_DOWN  0x122
    //#define KEY_SHIFT_CTRL_HOME  0x123
    //#define KEY_SHIFT_CTRL_END   0x124


    if (c1 == 0) {
        c2 = (int)getch();

        switch(c2) {
            case 0x9b: return KEY_ALT_LEFT;
            case 0x9d: return KEY_ALT_RIGHT;
            case 0x98: return KEY_ALT_UP;
            case 0xa0: return KEY_ALT_DOWN;
            case 0x99: return KEY_ALT_PGUP;
            case 0xa1: return KEY_ALT_PGDN;
            case 0x97: return KEY_ALT_HOME;
            case 0x9F: return KEY_ALT_END;
            case 0x3b: return KEY_F1;
            case 0x3d: return KEY_F3;
            case 0x3f: return KEY_F5;

            default:   return KEY_UNKNOWN;
        }
    }
    else if (c1 == 0xe0) {
        c2 = (int)getch();

        switch(c2) {
            case 0x73: return KEY_CTRL_LEFT;
            case 0x74: return KEY_CTRL_RIGHT;
            case 0x8d: return KEY_CTRL_UP;
            case 0x91: return KEY_CTRL_DOWN;
            case 0x77: return KEY_CTRL_HOME;
            case 0x75: return KEY_CTRL_END;
            case 0x52: return KEY_INS;
            case 0x53: return KEY_DEL;
            case 0x4b: return KEY_LEFT;
            case 0x4d: return KEY_RIGHT;
            case 0x48: return KEY_UP;
            case 0x50: return KEY_DOWN;
            case 0x47: return KEY_HOME;
            case 0x4f: return KEY_END;
            case 0x49: return KEY_PGUP;
            case 0x51: return KEY_PGDN;
            default:   return KEY_UNKNOWN;
        }

    }
    else {
        switch(c1) {
            case 0x08: return KEY_BACKSPACE;
            case 0x1b: return KEY_ESC;
            case 0x0d: return KEY_ENTER;
            case 0x09: return KEY_TAB;
            default: return c1;
        }

    }

}

CodePudding user response:

Alas, getch() is the wrong tool. You can use it, but you need to keep track of the shift state yourself. You can also use the GetKeyState() or GetAsyncKeyState() functions as exampled in the other answer.

The correct way to do this, until recently, was to use the Windows Console API function ReadConsoleInput(), and again keep track.

These days, Windows Terminal / Windows Console applications are expected to follow the new Microsoft Ecosystem Roadmap which basically says “use virtual terminal sequences”. It isn’t a perfect replacement (not by any means) but it does have the added bonus of making your terminal application easily portable to *nixen.

There are two main references for virtual terminal input sequences:

There is one bit left: you must properly initialize the Windows Terminal / Windows Console to use the virtual terminal sequences. At the very minimum, I recommend something like:

HANDLE hstdin  = GetStdHandle( STD_INPUT_HANDLE )
HANDLE hstdout = GetStdHandle( STD_OUTPUT_HANDLE )

DWORD input_mode, output_mode;
GetConsoleMode( hstdin,  &input_mode );
GetConsoleMode( hstdout, &output_mode );

SetConsoleMode( hstdin,  ENABLE_VIRTUAL_TERMINAL_INPUT | ENABLE_EXTENDED_FLAGS );
SetConsoleMode( hstdout, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING );

UINT input_codepage = GetConsoleCP();
SetConsoleCP( 65001 );
setlocale( LC_ALL, ".UTF-8" );

printf(
  "\033[\?1049h"    // use alternate buffer
  "\033[\?25l" );   // hide caret

You can add ENABLE_MOUSE_INPUT to the input flags as well, if you wish. Read up on SetConsoleMode() for more.

Finally, don’t forget to install an at_exit() handler to restore things when you terminate.

printf(
  "\033[\?1049l"    // done with alternate buffer
  "\033[\?25h"  );  // show caret
SetConsoleCP( input_codepage );
SetConsoleMode( hstdin,  input_mode );
SetConsoleMode( hstdout, output_mode );

There are all kinds of horrible ways your application can terminate as well. It is up to you whether or not you wish to install various other handlers for the random abnormal exit conditions you may encounter. Windows is pretty good about cleaning up after your program, but not perfect.

For a full-window application, it is also useful to track whether or not the user has resized the window.

There are a gazillion other details to worry about, but that should get you started.

Finally, as an unrelated but useful note, it is nice to know whether your console application was run from Explorer or not.

CodePudding user response:

Here's a program that I used to figure out how to detect Shift ArrowKey and Shift Ctrl ArrowKey. Just run the program and type the key combo to see the codes for it.


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

using namespace std;

int main()
{
    int shiftkey;
    int i1;
    int i2;


    while(1) {
        i1 = (int)getch();
        shiftkey = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);

        printf("shift:%d i1:x ", shiftkey, i1);
        if (i1 == 0xe0) {
            i2 = (int)getch();
            printf(" i2:x", i2);
        }
        else if (i1 == 0) {
            i2 = (int)getch();
            printf(" i2:x", i2);
        }
        printf("\n");
    }

    return 0;
}

CodePudding user response:

#define KEY_BACKSPACE        0x101
#define KEY_ESC              0x102
#define KEY_INS              0x103
#define KEY_DEL              0x104
#define KEY_LEFT             0x105
#define KEY_RIGHT            0x106
#define KEY_UP               0x107
#define KEY_DOWN             0x108
#define KEY_HOME             0x109
#define KEY_END              0x10A
#define KEY_ENTER            0x10B
#define KEY_TAB              0x10C
#define KEY_PGUP             0x10D
#define KEY_PGDN             0x10E

#define KEY_CTRL_LEFT        0x10F
#define KEY_CTRL_RIGHT       0x110
#define KEY_CTRL_UP          0x111
#define KEY_CTRL_DOWN        0x112
#define KEY_CTRL_HOME        0x113
#define KEY_CTRL_END         0x114
#define KEY_CTRL_TAB         0x115

#define KEY_SHIFT_LEFT       0x116
#define KEY_SHIFT_RIGHT      0x117
#define KEY_SHIFT_UP         0x118
#define KEY_SHIFT_DOWN       0x119
#define KEY_SHIFT_PGUP       0x11A
#define KEY_SHIFT_PGDN       0x11B
#define KEY_SHIFT_HOME       0x11C
#define KEY_SHIFT_END        0x11D
#define KEY_SHIFT_TAB        0x11E


#define KEY_SHIFT_CTRL_LEFT  0x11F
#define KEY_SHIFT_CTRL_RIGHT 0x120
#define KEY_SHIFT_CTRL_UP    0x121
#define KEY_SHIFT_CTRL_DOWN  0x122
#define KEY_SHIFT_CTRL_HOME  0x123
#define KEY_SHIFT_CTRL_END   0x124


#define KEY_F1                0x125
#define KEY_F3                0x126
#define KEY_F5                0x127

#define KEY_SHIFT_F1          0x200
#define KEY_SHIFT_F3          0x201
#define KEY_SHIFT_F5          0x202

#define KEY_ALT_LEFT          0x203
#define KEY_ALT_RIGHT         0x204
#define KEY_ALT_UP            0x205
#define KEY_ALT_DOWN          0x206
#define KEY_ALT_HOME          0x207
#define KEY_ALT_END           0x208
#define KEY_ALT_TAB           0x209
#define KEY_ALT_PGUP          0x20A
#define KEY_ALT_PGDN          0x20B

#define KEY_SHIFT_ALT_LEFT    0x20C
#define KEY_SHIFT_ALT_RIGHT   0x20D
#define KEY_SHIFT_ALT_UP      0x20E
#define KEY_SHIFT_ALT_DOWN    0x20F
#define KEY_SHIFT_ALT_HOME    0x210
#define KEY_SHIFT_ALT_END     0x211
#define KEY_SHIFT_ALT_TAB     0x212
#define KEY_SHIFT_ALT_PGUP    0x213
#define KEY_SHIFT_ALT_PGDN    0x214

#define KEY_SHIFT_INS         0x221
#define KEY_SHIFT_DEL         0x222

#define KEY_SHIFT_CTRL_PGUP   0x223
#define KEY_SHIFT_CTRL_PGDN   0x224
#define KEY_CTRL_PGUP         0x225
#define KEY_CTRL_PGDN         0x226
#define KEY_SHIFT_CTRL_INS    0x227
#define KEY_CTRL_INS          0x228
#define KEY_SHIFT_CTRL_DEL    0x229
#define KEY_CTRL_DEL          0x22A
#define KEY_SHIFT_ESC         0x22B
#define KEY_SHIFT_ENTER       0x22C

#define KEY_UNKNOWN           0xFFF
#if defined(WIN32) || defined(_WIN64)

#include "windows.h"
#include "conio.h"

int getkey__windows_console_app() {
    int c1;
    int c2;
    int shift;

    c1     = (int)getch();
    shift  = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);

    if (c1 == 0) {
        c2     = (int)getch();
        switch(c2) {
            case 0x3b: return (shift) ? KEY_SHIFT_F1          : KEY_F1;
            case 0x3d: return (shift) ? KEY_SHIFT_F3          : KEY_F3;
            case 0x3f: return (shift) ? KEY_SHIFT_F5          : KEY_F5;

            case 0x9b: return (shift) ? KEY_SHIFT_ALT_LEFT    : KEY_ALT_LEFT;
            case 0x9d: return (shift) ? KEY_SHIFT_ALT_RIGHT   : KEY_ALT_RIGHT;
            case 0x98: return (shift) ? KEY_SHIFT_ALT_UP      : KEY_ALT_UP;;
            case 0xa0: return (shift) ? KEY_SHIFT_ALT_DOWN    : KEY_ALT_DOWN;
            case 0x99: return (shift) ? KEY_SHIFT_ALT_PGUP    : KEY_ALT_PGUP;
            case 0xa1: return (shift) ? KEY_SHIFT_ALT_PGDN    : KEY_ALT_PGDN;
            case 0x97: return (shift) ? KEY_SHIFT_ALT_HOME    : KEY_ALT_HOME;
            case 0x9F: return (shift) ? KEY_SHIFT_ALT_END     : KEY_ALT_END;

            default:   return KEY_UNKNOWN;
        }
    }
    else if (c1 == 0xe0) {
        c2 = (int)getch();

        switch(c2)  {
            case 0x4b: return (shift) ? KEY_SHIFT_LEFT        : KEY_LEFT;
            case 0x4d: return (shift) ? KEY_SHIFT_RIGHT       : KEY_RIGHT;
            case 0x48: return (shift) ? KEY_SHIFT_UP          : KEY_UP;
            case 0x50: return (shift) ? KEY_SHIFT_DOWN        : KEY_DOWN;
            case 0x49: return (shift) ? KEY_SHIFT_PGUP        : KEY_PGUP;
            case 0x51: return (shift) ? KEY_SHIFT_PGDN        : KEY_PGDN;
            case 0x52: return (shift) ? KEY_SHIFT_INS         : KEY_INS;
            case 0x53: return (shift) ? KEY_SHIFT_DEL         : KEY_DEL;
            case 0x47: return (shift) ? KEY_SHIFT_HOME        : KEY_HOME;
            case 0x4f: return (shift) ? KEY_SHIFT_END         : KEY_END;

            case 0x73: return (shift) ? KEY_SHIFT_CTRL_LEFT   : KEY_CTRL_LEFT;
            case 0x74: return (shift) ? KEY_SHIFT_CTRL_RIGHT  : KEY_CTRL_RIGHT;
            case 0x8d: return (shift) ? KEY_SHIFT_CTRL_UP     : KEY_CTRL_UP;
            case 0x91: return (shift) ? KEY_SHIFT_CTRL_DOWN   : KEY_CTRL_DOWN;
            case 0x86: return (shift) ? KEY_SHIFT_CTRL_PGUP   : KEY_CTRL_PGUP;
            case 0x76: return (shift) ? KEY_SHIFT_CTRL_PGDN   : KEY_CTRL_PGDN;
            case 0x92: return (shift) ? KEY_SHIFT_CTRL_INS    : KEY_CTRL_INS;
            case 0x93: return (shift) ? KEY_SHIFT_CTRL_DEL    : KEY_CTRL_DEL;
            case 0x77: return (shift) ? KEY_SHIFT_CTRL_HOME   : KEY_CTRL_HOME;
            case 0x75: return (shift) ? KEY_SHIFT_CTRL_END    : KEY_CTRL_END;

            default:   return KEY_UNKNOWN;
        }

    }
    else {
        switch(c1) {
            case 0x08: return KEY_BACKSPACE;
            case 0x1b: return (shift) ? KEY_SHIFT_ESC   : KEY_ESC;
            case 0x0d: return (shift) ? KEY_SHIFT_ENTER : KEY_ENTER;
            case 0x09: return (shift) ? KEY_SHIFT_TAB   : KEY_TAB;

            default: return c1;
        }

    }

}
#endif

  • Related