2010-11-27 6 views
2

Linuxでユーザーにプロンプ​​トを表示せずにCでキーボードイベントを検出する方法はありますか?つまり、実行中のプログラムは、いずれかのキーを押すことで終了するはずです。 誰でもこれを手伝ってもらえますか?ユーザにプロンプ​​トを表示せずにLinux C GUIプログラムでキー押下を検出するにはどうすればよいですか?

+2

これは端末またはGUIプログラムで行いますか?あるいは、SDLを使用しているような、フルスクリーンのフレームバッファのようなプログラムでしょうか?または、他の何か? –

+1

私はスクリーンセーバーのようなGUIプログラムのためにこれを望みます。私はいくつかのキーを押すとすぐに、アプリケーションは終了する必要があります。 –

+1

次に、次の質問があります:どのGUIフレームワークを使用していますか? – casablanca

答えて

1

これをグラフィカルアプリケーションで実行したい場合は、ライブラリを使用してこれを行う必要があります。

このような簡単な作業は、どのようなライブラリ(Xlibのような低レベルのものさえ)でも簡単に行うことができます。

キーボードイベントを処理する方法を示すチュートリアルを探してみてください。

1

ANSI Cを使用する方法はありません。ncurses libを見てください。

+0

Hmm。 termiosコード、まっすぐなC、およびいくつかのunix呼び出しを参照してください。 –

1

はこちら/usr/src/bin/stty/key.cからのコードです:

f_cbreak(struct info *ip) 
{ 

     if (ip->off) 
       f_sane(ip); 
     else { 
       ip->t.c_iflag |= BRKINT|IXON|IMAXBEL; 
       ip->t.c_oflag |= OPOST; 
       ip->t.c_lflag |= ISIG|IEXTEN; 
       ip->t.c_lflag &= ~ICANON; 
       ip->set = 1; 
     } 
} 

を最低でも、あなたはあなたのselect(2)システムコールの前にICANONモードから抜け出すために持っているか、あなたのFIONREAD ioctlが動作します。

私はこの方法でCBREAKとECHOモードをクリアする20歳の古いperl4プログラムを持っています。これは、cursesライブラリに頼ることなく呪いのものをやっている:

sub BSD_cbreak { 
    local($on) = shift; 
    local(@sb); 
    local($sgttyb); 
    # global $sbttyb_t 

    $sgttyb_t = &sgttyb'typedef() unless defined $sgttyb_t; 

    # native BSD stuff by author (tsc) 

    ioctl(TTY,&TIOCGETP,$sgttyb) 
     || die "Can't ioctl TIOCGETP: $!"; 

    @sb = unpack($sgttyb_t,$sgttyb); 
    if ($on) { 
     $sb[&sgttyb'sg_flags] |= &CBREAK; 
     $sb[&sgttyb'sg_flags] &= ~&ECHO; 
    } else { 
     $sb[&sgttyb'sg_flags] &= ~&CBREAK; 
     $sb[&sgttyb'sg_flags] |= &ECHO; 
    } 
    $sgttyb = pack($sgttyb_t,@sb); 
    ioctl(TTY,&TIOCSETN,$sgttyb) 
      || die "Can't ioctl TIOCSETN: $!"; 
} 


sub SYSV_cbreak { 
    # SysV code contributed by Jeff Okamoto <[email protected]> 

    local($on) = shift; 
    local($termio,@termio); 
    # global termio_t ??? 

    $termio_t = &termio'typedef() unless defined $termio_t; 

    ioctl(TTY,&TCGETA,$termio) 
     || die "Can't ioctl TCGETA: $!"; 

    @termio = unpack($termio_t, $termio); 
    if ($on) { 
     $termio[&termio'c_lflag] &= ~(&ECHO | &ICANON); 
     $termio[&termio'c_cc + &VMIN] = 1; 
     $termio[&termio'c_cc + &VTIME] = 1; 
    } else { 
     $termio[&termio'c_lflag] |= (&ECHO | &ICANON); 

     # In HP-UX, it appears that turning ECHO and ICANON back on is 
     # sufficient to re-enable cooked mode. Therefore I'm not bothering 
     # to reset VMIN and VTIME (VEOF and VEOL above). This might be a 
     # problem on other SysV variants. 

    } 
    $termio = pack($termio_t, @termio); 
    ioctl(TTY, &TCSETA, $termio) 
     || die "Can't ioctl TCSETA: $!"; 

} 


sub POSIX_cbreak { 
    local($on) = shift; 
    local(@termios, $termios, $bitmask); 

    # "file statics" for package cbreak: 
    #  $savebits, $save_vtime, $save_vmin, $is_on 

    $termios_t = &termios'typedef() unless defined $termios_t; 
    $termios = pack($termios_t,()); # for Sun SysVr4, which dies w/o this 

    ioctl(TTY,&$GETIOCTL,$termios) 
     || die "Can't ioctl GETIOCTL ($GETIOCTL): $!"; 

    @termios = unpack($termios_t,$termios); 

    $bitmask = &ICANON | &IEXTEN | &ECHO; 
    if ($on && $cbreak'ison == 0) { 
     $cbreak'ison = 1; 
     $cbreak'savebits = $termios[&termios'c_lflag] & $bitmask; 
     $termios[&termios'c_lflag] &= ~$bitmask; 
     $cbreak'save_vtime = $termios[&termios'c_cc + &VTIME]; 
     $termios[&termios'c_cc + &VTIME] = 0; 
     $cbreak'save_vmin = $termios[&termios'c_cc + &VMIN]; 
     $termios[&termios'c_cc + &VMIN] = 1; 
    } elsif (!$on && $cbreak'ison == 1) { 
     $cbreak'ison = 0; 
     $termios[&termios'c_lflag] |= $cbreak'savebits; 
     $termios[&termios'c_cc + &VTIME] = $cbreak'save_vtime; 
     $termios[&termios'c_cc + &VMIN] = $cbreak'save_vmin; 
    } else { 
     return 1; 
    } 
    $termios = pack($termios_t,@termios); 
    ioctl(TTY,&$SETIOCTL,$termios) 
     || die "Can't ioctl SETIOCTL ($SETIOCTL): $!"; 
} 

sub DUMB_cbreak { 
    local($on) = shift; 

    if ($on) { 
     system("stty cbreak -echo"); 
    } else { 
     system("stty -cbreak echo"); 
    } 
} 

そして、それは他の場所でPOSIXのために、元のCに

($GETIOCTL, $SETIOCTL) = (TIOCGETA, TIOCSETA); 

RE-翻訳は、読者のための運動として残っているという20年前の私が元々どこから来たのかを覚えていないからです。 :(あなたが正確にどのように多くのバイトを発見するFIONREAD ioctlを行うselect年代は、その記述子が準備ができていることをマスクリターンを読んで、あなたがtty上ICANONモードの外出たら、今あなたのselect(2)システムコールが再び正常に動作

は、今ではそれはもはや必要ではないはずですが、それは、あなたが好ましくO_NONBLOCKディスクリプタに、ちょうどその多くのバイトをread(2)システムコールを行うことができました。そのファイルディスクリプタであなたを待っている。

フム、ここ予感です/usr/src/usr.bin/vi/cl/README.signalの注記:

Run in cbreak mode. There are two problems in this area. First, the 
    current curses implementations (both System V and Berkeley) don't give 
    you clean cbreak modes. For example, the IEXTEN bit is left on, turning 
    on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with 
    the exception that flow control and signals are turned on, and curses 
    cbreak mode doesn't give you this. 

    We can either set raw mode and twiddle the tty, or cbreak mode and 
    twiddle the tty. I chose to use raw mode, on the grounds that raw 
    mode is better defined and I'm less likely to be surprised by a curses 
    implementation down the road. The twiddling consists of setting ISIG, 
    IXON/IXOFF, and disabling some of the interrupt characters (see the 
    comments in cl_init.c). This is all found in historic System V (SVID 
    3) and POSIX 1003.1-1992, so it should be fairly portable. 

カーネル以外の部分に\b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\bの再帰的なgrep/usr/src/とすると、使用できるものが見つかるはずです。たとえば:

% grep -Pr '\b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\b' /usr/src/{{s,}bin,usr.{s,}bin,etc,gnu} 

私はダウンraw_mode()機能では、/usr/src/usr.bin/less/screen.cになります。 ifdefで悩んでいますが、それは移植性を追求していますが、これはあなたがしたいことのための最もクリーンなコードのように見えます。 GNUに潜んでいるものもあります。

OH MY/usr/src/gnu/usr.bin/perl/h2pl/cbreak.plをご覧ください!それは私が上に投稿した私の古いコードでなければなりません。興味深いことに、それは世界のすべてのsrcシステムに波及しています。それはから20年古いですから、恐ろしいです。まあ、若い自分のエコーを見るのは変です。 20年前からこのようなことを覚えているのは本当に難しい。termioまたはtermios可用性を推測しようとしているifdef Sの束に

#define tcgetattr(fd, arg) ioctl(fd, TCGETA, arg) 

は私も/usr/src/lib/libcurses/term.hにこの行を参照してください。

これで十分です。

6

termiosを使用して端末設定を変更する必要があります。 Stevens & Rago 2nd Ed「UNIX環境での高度なプログラミング」を参照してください。なぜtcsetattr()がすべての端末特性を設定せずにsuccessfulyを返すことができ、なぜtcsetattr()の呼び出しが冗長であるかがわかります。

これは、UNIXでANSI Cである:

#include <sys/types.h> 
#include <sys/time.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <termios.h> 
#include <errno.h> 

int checktty(struct termios *p, int term_fd) 
{ 
    struct termios ck; 
    return (
     tcgetattr(term_fd, &ck) == 0 && 
     (p->c_lflag == ck.c_lflag) && 
     (p->c_cc[VMIN] == ck.c_cc[VMIN]) && 
     (p->c_cc[VTIME] == ck.c_cc[VMIN]) 
    ); 
} 


int 
keypress(int term_fd) 
{ 
    unsigned char ch; 
    int retval=read(term_fd, &ch, sizeof ch); 
    return retval; 
} 

int /* TCSAFLUSH acts like fflush for stdin */ 
flush_term(int term_fd, struct termios *p) 
{ 
    struct termios newterm; 
    errno=0; 
    tcgetattr(term_fd, p); /* get current stty settings*/ 

    newterm = *p; 
    newterm.c_lflag &= ~(ECHO | ICANON); 
    newterm.c_cc[VMIN] = 0; 
    newterm.c_cc[VTIME] = 0; 

    return( 
     tcgetattr(term_fd, p) == 0 && 
     tcsetattr(term_fd, TCSAFLUSH, &newterm) == 0 && 
     checktty(&newterm, term_fd) != 0 
    ); 
} 
void 
term_error(void) 
{ 
    fprintf(stderr, "unable to set terminal characteristics\n"); 
    perror("");             
    exit(1);             
} 


void 
wait_and_exit(void) 
{ 
    struct timespec tsp={0,500}; /* sleep 500 usec (or likely more) */ 
    struct termios attr; 
    struct termios *p=&attr; 
    int keepon=0; 
    int term_fd=fileno(stdin); 

    fprintf(stdout, "press any key to continue:"); 
    fflush(stdout); 
    if(!flush_term(term_fd, p)) 
     term_error(); 
    for(keepon=1; keepon;) 
    { 
     nanosleep(&tsp, NULL); 
     switch(keypress(term_fd)) 
     { 
       case 0: 
       default: 
       break; 
      case -1: 
       fprintf(stdout, "Read error %s", strerror(errno)); 
       exit(1); 
       break; 
      case 1:  /* time to quit */ 
       keepon=0; 
       fprintf(stdout, "\n"); 
       break;     
     } 
    } 
    if(tcsetattr(term_fd, TCSADRAIN, p) == -1 && 
      tcsetattr(term_fd, TCSADRAIN, p) == -1) 
      term_error(); 
    exit(0); 
} 

int main() 
{ 
     wait_and_exit(); 
     return 0; /* never reached */ 
} 

にnanosleepコールがシステムリソースをgobblingからコードを防ぐためにそこにあります。 nice()を呼び出し、nanosleep()を使わないでください。すべてこれは座ってキーストロークを待ってから終了します。

+0

OS X 10.6で動作することを確認できます。 – febeling

関連する問題