Detect arrow keys pressure under Linux



Manage pressure of keyboard arrow keys under Linux is not a very easy task. This because, contrary to other keys having a specific unique code, the pressure of one of the arrow keys return not a single scancode but a sequence of three codes.



The scancodes sequences connected to each arrow keys are the following:

UP    -> [27][91][65]
DOWN  -> [27][91][66]
LEFT  -> [27][91][68]
RIGHT -> [27][91][67]

The problem is the first scancode 27 is the same code returned by the pressure of ESC key. This mean if you receive the code 27 from keyboard you'll be not sure if this 27 is the pressure of ESC key or is the first byte of a sequence informing you an arrow key was pressed. The Linux API for get key pressure code is getchar() but if you call this API from your code and no new key code will be available in the buffer the API will no return until new code will be ready.

Now the dilemma is if I receive the code 27 and try to read new code for verify if the second sequence code 91 I'll can expect two results:
1)   An arrow key was pressed then my call to getchar() will return immediately and I'll get the code 91 informing me about the pressure of arrow key
2)   The ESC key was pressed, in this case the second call to getchar() will lock my code
Follow my solution to this problem. Basically the trick is to change the "behaviour" of the getchar() to return immediately if no new scancodes was found into the keyboard buffer. This allow to make a quick check if the additional codes of arrow key sequence are present in the buffer.

unsigned int Character = getchar();

// Code 27 found, proceed to check if additional codes are available
if(Character == 27)
{
    struct termios original_ts, nowait_ts;
    unsigned int SpecialKeyCode;

    // Configure getchar() to return immediately
    tcgetattr(STDIN_FILENO, &original_ts);
    nowait_ts = original_ts;
    nowait_ts.c_lflag &= ~ISIG;
    nowait_ts.c_cc[VMIN]  = 0;
    nowait_ts.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &nowait_ts);

    // Short delay since slow system take some time 
    // to receive additional sequence codes
    usleep(100*1000);

    // Check if additional code is available after 27
    if((SpecialKeyCode = getchar()) != EOF)
    {
        // New code found, check if is part of arrow key sequence
        if(SpecialKeyCode == 91)
        {
            // Sequence code found, now next code will inform us
            // regarding the arrow key pressed (65,66,67,68) 
        }
        else
        {
            // Is not a special code, this could be a normal code
            // of a new key pression
        }
    }
    else
    {
        //There isn't new code, this mean 27 came from ESC key pression
    }
 
    // Restore getchar() normal wait for new codes
    tcsetattr(STDIN_FILENO, TCSANOW, &original_ts);
}

The code is quite simple and it work for me. However if someone will have a better solution I'll be welcomed to each new improvements.

0 Comments:

Post a Comment