在linux下C语言实现对键盘事件的监听

我们知道,在windows下有个键盘测试函数,int kbhit(void)。使用该函数需要包含头文件conio.h。执行时,kbhit测试是否有键盘按键按下,若有则返回非零值,否则返回零。

在Unix/Linux下,并没有提供这个函数。在linux下开发控制台程序时,有时会遇到检测键盘是否有被按下的情况,这时就需要自己编写kbhit()实现的程序了。

#include <stdio.h>
#include <termios.h>

static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard(void);
void close_keyboard(void);
int kbhit(void);
int readch(void); 
void init_keyboard()
{
	tcgetattr(0,&initial_settings);
	new_settings = initial_settings;
	new_settings.c_lflag |= ICANON;
	new_settings.c_lflag |= ECHO;
	new_settings.c_lflag |= ISIG;
	new_settings.c_cc[VMIN] = 1;
	new_settings.c_cc[VTIME] = 0;
	tcsetattr(0, TCSANOW, &new_settings);
}

void close_keyboard()
{
	tcsetattr(0, TCSANOW, &initial_settings);
}

int kbhit()
{
	unsigned char ch;
	int nread;

	if (peek_character != -1) return 1;
	new_settings.c_cc[VMIN]=0;
	tcsetattr(0, TCSANOW, &new_settings);
	nread = read(0,&ch,1);
	new_settings.c_cc[VMIN]=1;
	tcsetattr(0, TCSANOW, &new_settings);
	if(nread == 1) 
	{
		peek_character = ch;
		return 1;
	}
	return 0;
}

int readch()
{
	char ch;

	if(peek_character != -1) 
	{
		ch = peek_character;
		peek_character = -1;
		return ch;
	}
	read(0,&ch,1);
	return ch;
}
 
int main()
{
	init_keyboard();
	while(1)
	{
		kbhit();
		printf("\n%d\n", readch());
	}
	close_keyboard();
	return 0;
}
 


 

下面是转载另外一篇博文,说明该结构体的

        linux termios结构             

        分类:             LINUX                   1344人阅读     评论(0)     收藏     举报    

 

 

termios 结构是在POSIX规范中定义的标准接口,它类似于系统V中的termio接口,通过设置termios类型的数据结构中的值和使用一小

组函数调用,你就可以对终端接口进行控制。

可以被调整来影响终端的值按照不同的模式被分为如下几组:

1.输入模式

2.输出模式

3.控制模式

4.本地模式

5.特殊控制模式

最小的termios结构的典型定义如下:

struct termios

{

           tcflag_t c_iflag;

           tcflag_t c_oflag;

           tcflag_t c_cflag;

           tcflag_t c_lflag;

           cc_t           c_cc[NCCS];

};

结构成员的名称与上面列出的5种参数类型相对应。

你可以调用函数tcgetattr来初始化一个终端对应的termios结构,该函数的原型如下:

  1. #include<termios.h>  
  2. int tcgetattr(int fd, struct termios *termios_p);  
#include<termios.h>
int tcgetattr(int fd, struct termios *termios_p);
这个函数调用把当前终端接口变量的值写入termios_p参数指向的结构。如果这些值其后被修改了,你可以通过调用函数tcsetattr来重新配置

终端接口。

  1. #include<termios.h>  
  2. int tcsetattr(int fd , int actions , const struct termios *termios_h);  
#include<termios.h>
int tcsetattr(int fd , int actions , const struct termios *termios_h);

参数actions控制修改方式,共有三种修改方式,如下所示。

1.TCSANOW:立刻对值进行修改

2.TCSADRAIN:等当前的输出完成后再对值进行修改。

3.TCSAFLUSH:等当前的输出完成之后,再对值进行修改,但丢弃还未从read调用返回的当前的可用的任何输入。


接下来我们将分别对五种模式进行介绍。

一 输入模式

输入模式控制输入数据在传递给程序之前的处理方式。你通过设置termios结构中的c_iflag成员的标志对它们进行控制。所有的标志都被定义为

宏,并可通过按位或的方式结合起来。

可用于c_iflag成员的宏如下所示:

BRKINT:当在输入行中检测到一个终止状态时,产生一个中断。

TGNBRK:忽略输入行中的终止状态。

TCRNL:将接受到的回车符转换为新行符。

TGNCR:忽略接受到的新行符。

INLCR:将接受到的新行符转换为回车符。

IGNPAR:忽略奇偶校检错误的字符。

INPCK:对接收到的字符执行奇偶校检。

PARMRK:对奇偶校检错误作出标记。

ISTRIP:将所有接收的字符裁减为7比特。

IXOFF:对输入启用软件流控。

IXON:对输出启用软件流控。

如果BRKINT和TGNBRK标志都未被设置,则输入行中的终止状态就被读取为NULL(0X00)字符。

三.输出模式

输出模式控制输出字符的处理方式,即由程序发出的字符在传递到串行口或屏幕之前如何处理.通过设置c_oflag成员的标识对输出模式进行控制.

OPSOT:打开输出处理功能

ONLCR:将输出中的换行符转换为回车符

OCRNL:将回车符转换为换行符

ONOCR:第0行不输出回车符

ONLRET:不输出回车符

NLDLY:换行符延时选择

CRDLY:回车符延时

TABDLY:制表符延时

...

输出模式用得也不多

四.控制模式

控制模式控制终端的硬件特性,通过c_cflag成员标识配置.

CLOCAL:忽略所有调制解调器的状态行

CREAD:启用字符接收器

CS5/6/7/8:发送或接收字符时使用5/6/7/8比特

CSTOPB:每个字符使用两停止位

HUPCL:关闭时挂断调制解调器

PARENB:启用奇偶校验码的生成和检测功能

PARODD:只使用奇检验而不用偶校验

一般也不用这种方式,通常直接修改终端配置文件来修改硬件特性要容易一些

五.本地模式

通过c_lflag成员控制终端的某些特性

ECHO:启用输入字符的本地回显功能

ECHONL:回显换行符

ICANON:启用标准输入处理

ISIG:启用信号

...

最常用的是ECHO和ICANON标志,前者抑制键入字符的回显(抑制??),后者如说明

六.特殊的控制字符

标准模式和非标准模式下,c_cc数组的下标有不同的值:

标准模式:

VEOF:EOF字符

VEOL:EOL字符

VERASE:ERASE字符

VINTR:INTR字符

VKILL:KILL字符

VQUIT:QUIT字符

VSTART:START字符 

VSTOP:STOP字符

非标准模式:

VINTR:INTR字符

VMIN:MIN值

VQUIT:QUIT字符

VSUSP:SUSP字符

VTIME:TIME值

VSTART:START字符 

VSTOP:STOP字符

1.字符

INTR:该字符使终端驱动程序向与终端相连的进程以送SIGINT信号

QUIT:该字符使终端驱动程序向与终端相连的进程发送SIGQUIT信号

EOF;该字符使终端驱动程序将输入行中的全部字符传递给正在读取输入的应用程序.如果输入行为空,read调用将返回0,就好像在文件尾调用read一样

...

2.TIME和MIN值

这两个值只用于非标准模式,两者结合共同控制对输入的读取方式,还能控制在一个程序试图与一个终端关联的文件描述符时将发生的情况

MIN = 0, TIME = 0时:read立即返回,如果有待处理的字符,它们就会被返回,如果没有,read调用返回0,且不读取任何字符

MIN = 0, TIME > 0时:有字符处理或经过TIME个0.1秒后返回

MIN > 0, TIME = 0时:read一直等待,直到有MIN个字符可以读取,返回值是字符的数量.到达文件尾时返回0

MIN > 0, TIME > 0时:read调用时,它会等待接收一个字符.在接收到第一个字符及其后续的每个字符后,启用一个字符间隔定时器.当有MIN个字符可读或两字符间的时间间隔超进TIME个0.1秒时,read返回

通过设置MIN和TIME值,我们可以逐个字符地对输入进行处理

3.通过shell访问终端模式

stty -a:这个命令用来查看当前终端的设置情况

stty sane:如果不小心设错了终端模式,可用这个命令恢复,另一种恢复办法是在设置之前保存当前stty设置,在需要时再读出

stty -g > save_stty:将当前设置保存到文件save_atty中

stty $(cat save_stty):读出save_atty文件,恢复原终端设置

第三种恢复的办法是重新打下一个终端模拟器.查看死掉的终端进程,kill掉它

4.在命令行模式下设置终端模式

比如想让shell脚本读取单个字符,就需要关闭标准模式,同时将MIN设为1,TIME设为0:

stty -icanon min1 time 0

另一个例子是关闭输入密码时的回显功能:

atty -echo

使用完这个命令后要执行atty echo,将回显功能再次恢复

5.终端速度

termios结构中没有关于终端速度的成员和标识,但我们可以通过一组函数来实现.注意输入和输出是分开的,应使用不同的函数

#include <termios.h>

speed_t cfgetispeed(const struct termios *);

speed_t cfgetospeed(const struct termios *);

int cfsetispeed(struct termios *, speed_t speed);

int cfseospeed(struct termios *, speed_t speed);

这些函数只作用于termios结构,因此需要先调用tcgetattr()获得termios结构,再调用以上函数之一设置终端速度,最后调用tcsetattr()使设置生效

上面的speed参数可设的值,其中比较重要的几个:

B0:挂起终端

B1200:1200波特

B2400:2400波特

B9600:9600波特

B19200:19200波特

B38400:38400波特

6.其他函数

这些函数直接作用于文件描述符,不需要读写termios结构:

#include <termios.h>

int tcdrain(int fd);让调用程序一直等待,直到所有排队的输出都发送完毕

int tcflow(int, int flowtype);暂停或重新开始输出

int tcflush(int fd, int in_out_selector);清空输入,输出或两者都清华空

使用termios结构的密码程序

  1. #include <termios.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #define PASSWORD_LEN 8  
  5. int main()  
  6. {  
  7.     struct termios initialrsettings, newrsettings;  
  8.     char password[PASSWORD_LEN + 1];  
  9.     tcgetattr(fileno(stdin), &initialrsettings);  
  10.     newrsettings = initialrsettings;  
  11.     newrsettings.c_lflag &= ~ECHO;  
  12.     printf("Enter password: ");  
  13.     if(tcsetattr(fileno(stdin), TCSAFLUSH, &newrsettings) != 0) {  
  14.         fprintf(stderr,"Could not set attributes\n");  
  15.     }  
  16.     else {  
  17.         fgets(password, PASSWORD_LEN, stdin);  
  18.         tcsetattr(fileno(stdin), TCSANOW, &initialrsettings);  
  19.         fprintf(stdout, "\nYou entered %s\n", password);  
  20.     }  
  21.     exit(0);  
  22. }  
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#define PASSWORD_LEN 8
int main()
{
    struct termios initialrsettings, newrsettings;
    char password[PASSWORD_LEN + 1];
    tcgetattr(fileno(stdin), &initialrsettings);
    newrsettings = initialrsettings;
    newrsettings.c_lflag &= ~ECHO;
    printf("Enter password: ");
    if(tcsetattr(fileno(stdin), TCSAFLUSH, &newrsettings) != 0) {
        fprintf(stderr,"Could not set attributes\n");
    }
    else {
        fgets(password, PASSWORD_LEN, stdin);
        tcsetattr(fileno(stdin), TCSANOW, &initialrsettings);
        fprintf(stdout, "\nYou entered %s\n", password);
    }
    exit(0);
}

读取每一个字符

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <termios.h>  
  5. char *menu[] = {  
  6.     "a - add new record",  
  7.     "d - delete record",  
  8.     "q - quit",  
  9.     NULL,  
  10. };  
  11. int getchoice(char *greet, char *choices[], FILE *in, FILE *out);  
  12. int main()  
  13. {  
  14.     int choice = 0;  
  15.     FILE *input;  
  16.     FILE *output;  
  17.     struct termios initial_settings, new_settings;  
  18.     if (!isatty(fileno(stdout))) {  
  19.         fprintf(stderr,"You are not a terminal, OK.\n");  
  20.     }  
  21.     input = fopen("/dev/tty""r");  
  22.     output = fopen("/dev/tty""w");  
  23.     if(!input || !output) {  
  24.         fprintf(stderr, "Unable to open /dev/tty\n");  
  25.         exit(1);  
  26.     }  
  27.     tcgetattr(fileno(input),&initial_settings);  
  28.     new_settings = initial_settings;  
  29.     new_settings.c_lflag &= ~ICANON;  
  30.     new_settings.c_lflag &= ~ECHO;  
  31.     new_settings.c_cc[VMIN] = 1;  
  32.     new_settings.c_cc[VTIME] = 0;  
  33.     new_settings.c_lflag &= ~ISIG;  
  34.     if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {  
  35.         fprintf(stderr,"could not set attributes\n");  
  36.     }  
  37.   
  38.     do {  
  39.         choice = getchoice("Please select an action", menu, input, output);  
  40.         printf("You have chosen: %c\n", choice);  
  41.     } while (choice != 'q');  
  42.     tcsetattr(fileno(input),TCSANOW,&initial_settings);  
  43.     exit(0);  
  44. }  
  45. int getchoice(char *greet, char *choices[], FILE *in, FILE *out)  
  46. {  
  47.     int chosen = 0;  
  48.     int selected;  
  49.     char **option;  
  50.     do {  
  51.         fprintf(out, "Choice: %s\n",greet);  
  52.         option = choices;  
  53.         while(*option) {  
  54.             fprintf(out, "%s\n",*option);  
  55.             option++;  
  56.         }  
  57.         do {  
  58.             selected = fgetc(in);  
  59.         } while (selected == '\n' || selected == '\r');  
  60.         option = choices;  
  61.         while(*option) {  
  62.             if(selected == *option[0]) {  
  63.                 chosen = 1;  
  64.                 break;  
  65.             }  
  66.             option++;  
  67.         }  
  68.         if(!chosen) {  
  69.             fprintf(out, "Incorrect choice, select again\n");  
  70.         }  
  71.     } while(!chosen);  
  72.     return selected;  
  73. }  
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
char *menu[] = {
    "a - add new record",
    "d - delete record",
    "q - quit",
    NULL,
};
int getchoice(char *greet, char *choices[], FILE *in, FILE *out);
int main()
{
    int choice = 0;
    FILE *input;
    FILE *output;
    struct termios initial_settings, new_settings;
    if (!isatty(fileno(stdout))) {
        fprintf(stderr,"You are not a terminal, OK.\n");
    }
    input = fopen("/dev/tty", "r");
    output = fopen("/dev/tty", "w");
    if(!input || !output) {
        fprintf(stderr, "Unable to open /dev/tty\n");
        exit(1);
    }
    tcgetattr(fileno(input),&initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= ~ICANON;
    new_settings.c_lflag &= ~ECHO;
    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VTIME] = 0;
    new_settings.c_lflag &= ~ISIG;
    if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
        fprintf(stderr,"could not set attributes\n");
    }

    do {
        choice = getchoice("Please select an action", menu, input, output);
        printf("You have chosen: %c\n", choice);
    } while (choice != 'q');
    tcsetattr(fileno(input),TCSANOW,&initial_settings);
    exit(0);
}
int getchoice(char *greet, char *choices[], FILE *in, FILE *out)
{
    int chosen = 0;
    int selected;
    char **option;
    do {
        fprintf(out, "Choice: %s\n",greet);
        option = choices;
        while(*option) {
            fprintf(out, "%s\n",*option);
            option++;
        }
        do {
            selected = fgetc(in);
        } while (selected == '\n' || selected == '\r');
        option = choices;
        while(*option) {
            if(selected == *option[0]) {
                chosen = 1;
                break;
            }
            option++;
        }
        if(!chosen) {
            fprintf(out, "Incorrect choice, select again\n");
        }
    } while(!chosen);
    return selected;
}


更多

 

  • 6
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值