标题
    
    
        请问在linux环境下应该能使用kbhit这个函数吗?[zt]
    
 
    
 	 
       
    
    clq
    
    
    
		
        浏览(0) + 
        2008-04-12 18:01:49 发表
        
编辑
         
        
        
        
        关键字: 
        
 
        
        
        
        
                
        请问在linux环境下应该能使用kbhit这个函数吗? 
来自  http://www.lslnet.com/linux/dosc1/63/linux-413619.htm
		 Re: 请问在linux环境下应该能使用kbhit这个函数吗? 
c的标准函数,应该没有什么问题 
		 Re: 请问在linux环境下应该能使用kbhit这个函数吗? 
我本来也是这么想的。可是在我的RH7。2中我用了这个函数,并加了头文件conio.h,可是我的系统根本就不认这个头文件啊。更不用说用这个函数了。请问有办法解决吗 
		 Re: 请问在linux环境下应该能使用kbhit这个函数吗? 
發信人: lmy@cis_nctu (Daemon of Andromeda), 信區: programming 
標 題: kbhit() in UNIX 補充 (was: Re: 一些問題 (UNIX C)) 
發信站: 交大資科_BBS (Mar 20 15:30:37 1995) 
轉信站: cis_nctu 
==>[作者]: lmy@cis_nctu(Daemon of Andromeda) 在 programming 討論區中提到: 
> 若不用 select, 用 signal SIGIO 也可以達成. 但純用 ioctl 的話, 
> 我看了一下 linux kernel 的 source, 發現 linux 可以這樣用 
> kbhit() 
> { 
> int i; 
> ioctl(0, TIOCINQ, &i); 
> return i!=0; 
> } 
> 不過 porting 到 Sun 上就掛了... 顯然不太 standard... 
今天碰巧發現了完全符合 wdlin 兄規定只用 ioctl 的規格的 kbhit() 
並且可 porting (at least on SunOS, linux) 
#include  
kbhit() 
{ 
int n; 
ioctl(0, FIONREAD, &n); 
return n; 
} 
		 Re: 请问在linux环境下应该能使用kbhit这个函数吗? 
按楼上的方法好像不行,在dos下,kbhit只要按了键盘就能马上返回键值,而楼上的方法要回车才能返回。 
		 Re: 请问在linux环境下应该能使用kbhit这个函数吗? 
这和终端模式有关,缺省的是行模式,所以要回车。改成raw模式即可。 
		 Re: 请问在linux环境下应该能使用kbhit这个函数吗? 
在程序里如何该成raw模式并且在程序退出时恢复? 
		 Re: 请问在linux环境下应该能使用kbhit这个函数吗? 
可以用 
system("stty raw"); 
... 
system("stty cooked"); 
来切换。 
		 看看置顶贴 
struct termios new_settings; 
tcgetattr(0,&stored_settings); 
new_settings = stored_settings; 
/* Disable canonical mode, and set buffer size to 1 byte */ 
new_settings.c_lflag &= (~ICANON); 
new_settings.c_cc[VTIME] = 0; 
new_settings.c_cc[VMIN] = 1; 
tcsetattr(0,TCSANOW,&new_settings); 
		 19.5 从stdin立即获取按键 
19.5 从stdin立即获取按键
Q: Linux/C编程环境,从标准输入stdin读取内容时,有无办法立即获取按键,而不
   必等待换行。事实上我需要MS-DOS下的kbhit()、getch()函数。
   有些人总是建议进入(n)curses环境,可我不想使用这种多此一举的技术。
A: Floyd Davidson 
我们就作者所提供的原始代码进行了一些移植、修正,手头系统有限,未做更广泛的
可移植性测试。
--------------------------------------------------------------------------
/*
 * For x86/Linux Kernel 2.4.7-10
 * gcc -DLinux -Wall -pipe -O3 -o input_demo input_demo.c
 *
 * For x86/FreeBSD 4.5-RELEASE
 * gcc -DFreeBSD -Wall -pipe -O3 -o input_demo input_demo.c
 *
 * For SPARC/Solaris 8
 * gcc -DSolaris -Wall -pipe -O3 -o input_demo input_demo.c
 *
 * kbhit() -- a keyboard lookahead monitor
 * getch() -- a blocking single character input from stdin
 *
 * Plus a demo main() to illustrate usage.
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#ifdef Solaris
#include 
#endif
#undef  TERMIOSECHO
#define TERMIOSFLUSH
/*
 * getch() -- a blocking single character input from stdin
 *
 * Returns a character, or -1 if an input error occurs
 *
 * Conditionals allow compiling with or without echoing of the input
 * characters, and with or without flushing pre-existing buffered input
 * before blocking.
 */
static int getch ( void )
{
    struct termios old_termios, new_termios;
    int            error;
    char           c;
    fflush( stdout );
    tcgetattr( 0, &old_termios );
    new_termios              = old_termios;
    /*
     * raw mode, line settings
     */
    new_termios.c_lflag     &= ~ICANON;
#ifdef TERMIOSECHO
    /*
     * enable echoing the char as it is typed
     */
    new_termios.c_lflag     |=  ECHO;
#else
    /*
     * disable echoing the char as it is typed
     */
    new_termios.c_lflag     &= ~ECHO;
#endif
#ifdef TERMIOSFLUSH
    /*
     * use this to flush the input buffer before blocking for new input
     */
    #define OPTIONAL_ACTIONS TCSAFLUSH
#else
    /*
     * use this to return a char from the current input buffer, or block
     * if no input is waiting
     */
    #define OPTIONAL_ACTIONS TCSANOW
#endif
    /*
     * minimum chars to wait for
     */
    new_termios.c_cc[VMIN]   = 1;
    /*
     * minimum wait time, 1 * 0.10s
     */
    new_termios.c_cc[VTIME]  = 1;
    error                    = tcsetattr( 0, OPTIONAL_ACTIONS, &new_termios );
    if ( 0 == error )
    {
        /*
         * get char from stdin
         */
        error  = read( 0, &c, 1 );
    }
    /*
     * restore old settings
     */
    error                   += tcsetattr( 0, OPTIONAL_ACTIONS, &old_termios );
    return( error == 1 ? ( int )c : -1 );
}  /* end of getch */
/*
 * kbhit() -- a keyboard lookahead monitor
 *
 * returns the number of characters available to read
 */
static int kbhit ( void )
{
    struct timeval tv;
    struct termios old_termios, new_termios;
    int            error;
    int            count = 0;
    tcgetattr( 0, &old_termios );
    new_termios              = old_termios;
    /*
     * raw mode
     */
    new_termios.c_lflag     &= ~ICANON;
    /*
     * disable echoing the char as it is typed
     */
    new_termios.c_lflag     &= ~ECHO;
    /*
     * minimum chars to wait for
     */
    new_termios.c_cc[VMIN]   = 1;
    /*
     * minimum wait time, 1 * 0.10s
     */
    new_termios.c_cc[VTIME]  = 1;
    error                    = tcsetattr( 0, TCSANOW, &new_termios );
    tv.tv_sec                = 0;
    tv.tv_usec               = 100;
    /*
     * insert a minimal delay
     */
    select( 1, NULL, NULL, NULL, &tv );
    error                   += ioctl( 0, FIONREAD, &count );
    error                   += tcsetattr( 0, TCSANOW, &old_termios );
    return( error == 0 ? count : -1 );
}  /* end of kbhit */
int main ( int argc, char * argv[] )
{
    struct termios old_termios, new_termios;
    int            count;
    int            c;
    tcgetattr( 0, &old_termios );
    printf( "You must enter 10 characters to get this program to continue:" );
    fflush( stdout );
    /*
     * collect 10 characters
     */
    for ( count = kbhit(); count < 10; count = kbhit() )
    {
        if ( -1 == count )
        {
            return( EXIT_FAILURE );
        }
    }
    new_termios          = old_termios;
    /*
     * disable echoing of further input
     */
    new_termios.c_lflag &= ~ECHO;
    tcsetattr( 0, TCSANOW, &new_termios );
    printf( "\nStop, now type  to continue" );
    fflush( stdout );
    c = getchar();
    /*
     * enable echoing of further input
     */
    tcsetattr( 0, TCSANOW, &old_termios );
    printf( "\nThe first five characters are: [" );
    /*
     * print a few chars
     */
    for ( count = 0; count < 4; count++ )
    {
        printf( "%c", ( char )c );
        c = getchar();
    }
    printf( "%c]\n\n", ( char )c );
    printf( "****** Demo Menu ******\n\n" );
    printf( "Option       Action\n" );
    printf( "  A          Action_A\n" );
    printf( "  B          Action_B\n" );
    printf( "  C          Action_C\n" );
    printf( "  Q          Exit\n\n" );
    printf( "Enter your choice: [ ]\b\b" );
    fflush( stdout );
    /*
     * note that calling getch() will flush remaining buffered input
     */
    switch ( c = getch() )
    {
    case 'a':
    case 'A':
        printf( "%c\nAction_A\n", ( char )toupper( ( int )c ) );
        break;
    case 'b':
    case 'B':
        printf( "%c\nAction_B\n", ( char )toupper( ( int )c ) );
        break;
    case 'c':
    case 'C':
        printf( "%c\nAction_C\n", ( char )toupper( ( int )c ) );
        break;
    case 'q':
    case 'Q':
        printf( "%c\nExit\n", ( char )toupper( ( int )c ) );
        break;
    default:
        printf( "%c\n", ( char )toupper( ( int )c ) );
        break;
    }
    tcsetattr( 0, TCSANOW, &old_termios );
    return( EXIT_SUCCESS );
}  /* end of main */
--------------------------------------------------------------------------
D: 小四 
c_cc[VTIME]以0.10秒为单位指定一个字节间的读取超时,所对应的计时器在接收到
第一个字节之后才启动,因此c_cc[VMIN]为1的时候,c_cc[VTIME]是多少都无所谓。
关于这方面的详细讨论参看APUE 11.11小节。
input_demo.c:main()中第一个getchar()调用,其本意是在"规范模式"下阻塞,等待
一个换行。SPARC/Solaris 8下语义与x86/Linux Kernel 2.4.7-10、x86/FreeBSD
4.5-RELEASE不同,在此处未能阻塞住,我怀疑是该版本实现上的一个BUG。
        
        
        
        		
		        
                
                
     
    
 
	
	
     
    
       
    
    clq
    
    
    
    
    
    		    
    
          
              
    	Linux下kbhit()的实现
    我们知道,在windows下有个键盘测试函数,int kbhit(void)。使用该函数需要包含头文件conio.h。执行时,kbhit测试是否有键盘按键按下,若有则返回非零值,否则返回零。
    在Unix/Linux下,并没有提供这个函数。在linux下开发控制台程序时,有时会遇到检测键盘是否有被按下的情况,这时就需要自己编写kbhit()实现的程序了。下面是kbhit在Unix/Linux下的一个实现。用到了一种终端操作库termios。
    下面是头文件kbhit.h:
    #ifndef KBHITh
    #define KBHITh
    void   init_keyboard(void);
    void   close_keyboard(void);
    int      kbhit(void);
    int     readch(void); 
    #endif
    下面式源程序kbhit.c:
    #include "kbhit.h"
    #include 
    #include    // for read()
    static struct termios initial_settings, new_settings;
    static int peek_character = -1;
    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;
    }
    
    
     
 
	 
	
    NEWBT官方QQ群1: 276678893
    可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
    但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
    验证问题说明申请入群原因即可.