How to Check If a Key Was Pressed in Linux

Detecting keyboard key press and release on Linux

Your kbhit (and, apparently, even Windows' original kbhit) doesn't detect whether a key is pressed, but only whether there is something new to read on stdin. This will only be the case 25 times a second or so, depending on your autorepeat setting. Making stdout unbuffered in your example code will make that more obvious (000000W00000000W000000000W)

How can I fix this so that when a keyboard key is kept pressed, kbhit() always returns 0?

This is impossible to do portably. In Linux, it can be done using the device files under /dev/input
See the example program below. It will register all keys, even a lone SHIFT Note that this is effectively a keylogger, so you will have to run as root (or make the program setuid). It will then register all keystrokes, even when it doesn't have keyboard focus. Yikes!

Note: the example below is based on keystate.c by Kevin Cox

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <glob.h>
#include <linux/input.h>
#include <sys/stat.h>
#include <fcntl.h>

/* Return -1 if no key is being pressed, or else the lowest keycode
(c.f. linux/input-event-codes.h) of all the keys that are being pressed */
int keycode_of_key_being_pressed() {
FILE *kbd;
glob_t kbddev; // Glob structure for keyboard devices
glob("/dev/input/by-path/*-kbd", 0, 0, &kbddev); // Glob select all keyboards
int keycode = -1; // keycode of key being pressed
for (int i = 0; i < kbddev.gl_pathc ; i++ ) { // Loop through all the keyboard devices ...
if (!(kbd = fopen(kbddev.gl_pathv[i], "r"))) { // ... and open them in turn (slow!)
perror("Run as root to read keyboard devices");
exit(1);
}

char key_map[KEY_MAX/8 + 1]; // Create a bit array the size of the number of keys
memset(key_map, 0, sizeof(key_map)); // Fill keymap[] with zero's
ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map); // Read keyboard state into keymap[]
for (int k = 0; k < KEY_MAX/8 + 1 && keycode < 0; k++) { // scan bytes in key_map[] from left to right
for (int j = 0; j <8 ; j++) { // scan each byte from lsb to msb
if (key_map[k] & (1 << j)) { // if this bit is set: key was being pressed
keycode = 8*k + j ; // calculate corresponding keycode
break; // don't scan for any other keys
}
}
}

fclose(kbd);
if (keycode)
break; // don't scan for any other keyboards
}
return keycode;
}

void main()
{
setvbuf(stdout, NULL, _IONBF, 0); // Set stdout unbuffered
while (1) {
int key = keycode_of_key_being_pressed();
printf((key < 0 ? "no key\n" : "keycode: %d\n"), key);
if (key == KEY_X)
exit(0);
}
}

Test whether key is pressed without blocking on Linux

I found a solution :

int khbit() const
{
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}

void nonblock(int state) const
{
struct termios ttystate;
tcgetattr(STDIN_FILENO, &ttystate);

if ( state == 1)
{
ttystate.c_lflag &= (~ICANON & ~ECHO); //Not display character
ttystate.c_cc[VMIN] = 1;
}
else if (state == 0)
{
ttystate.c_lflag |= ICANON;
}
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}

bool keyState(int key) const //Use ASCII table
{
bool pressed;
int i = khbit(); //Alow to read from terminal
if (i != 0)
{
char c = fgetc(stdin);
if (c == (char) key)
{
pressed = true;
}
else
{
pressed = false;
}
}

return pressed;
}

int main()
{
nonblock(1);
int i = 0;
while (!i)
{
if (cmd.keyState(32)) //32 in ASCII table correspond to Space Bar
{
i = 1;
}
}
nonblock(0);

return 0;
}

It works well. Thanks for helping me. I hope it will help someone :)

Is there a way to detect if a key has been pressed?

I use the following function for kbhit(). It works fine on g++ compiler in Ubuntu.

#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
int kbhit(void)
{
struct termios oldt, newt;
int ch;
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);

ch = getchar();

tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);

if(ch != EOF)
{
ungetc(ch, stdin);
return 1;
}

return 0;
}

Check if the Enter key is pressed in a linux bash script

The \n at the end of the user's input is stripped off before assigning the value to the shell variable. If the user just presses ENTER, the value read will be the empty string.

read VAR
if [[ -z $VAR ]]; then echo "User pressed ENTER with no input text"; fi

shell script respond to keypress


read -rsn1

Expect only one letter (and don't wait for submitting) and be silent (don't write that letter back).



Related Topics



Leave a reply



Submit