我正在尝试编写一个简单的程序,该程序在不按任何键时执行特定任务,而在按任意键时执行另一个任务。这是我到目前为止的内容:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
int kbhit (void);
int main()
{
char c;
while(1)
{
while (!kbhit())
{
printf("%d", 0);
}
c = getchar();
printf("%c", c);
if(c == 'x' || c == 'X')
break;
sleep(0);
}
return 0;
}
int kbhit (void)
{
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 1000;
FD_ZERO(&rdfs);
FD_SET (STDIN_FILENO, &rdfs);
select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &rdfs);
}
此实现非常适合反复检测单个按键,但是如果您按住键盘按键,则在检测按键之间会有很大的差距。即使一直按下该键,无论如何控制台都会打印0。例如,如果我按住“ W”,则输出看起来像这样:000000000000000WWWWWWWW00000000000000000WWWWWWWW00000000...
如何解决此问题,以便在按住键盘键时kbhit()始终返回0?
答案 0 :(得分:2)
您的kbhit
(显然,甚至Windows的原始kbhit
)也不会检测是否按下了某个键,而只会检测到stdin
上是否有新内容要读取。这仅是每秒25次的情况,具体取决于您的自动重复设置。在示例代码中将stdout
设置为无缓冲将更加明显(000000W00000000W000000000W
)
如何解决此问题,以便在按住键盘键时kbhit()始终返回0?
这是不可能做到的。在Linux中,可以使用/dev/input
下的设备文件来完成
请参见下面的示例程序。它将注册所有密钥,甚至是一个单独的 SHIFT 。请注意,这实际上是一个键盘记录程序,因此您将必须以root用户身份运行(或使程序setuid运行)。然后,即使没有键盘焦点,它也会注册所有击键。 kes!
注意:以下示例基于Kevin Cox的keystate.c
#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);
}
}