忘備録-備忘録

技術的な備忘録

キー入力を受け取る

2020-04-30 20:47:04 | Linux
C言語ではキー入力を受け取るときに最後に[Enter]キーを押す必要があります。[Enter]キーを押さなくてもキー入力を受け取るプログラムのサンプルです。同じことはncurses ライブラリでもできますが、キー入力のみでよい場合は、このプログラムを組み込むだけでOKです。プログラムの作成はRaspberry Pi4で行っています。 

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>

/*
 * 押されたキーの値を返す
 * ブロックしない。キーが押されていない場合は戻り値は0となる。
 */
int getkeyNB(void)
{
    struct termios oldt, newt;
    int ch0,ch1,ch2,ch3,ch4;
    int ret,oldf;

    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_iflag = ~( BRKINT | ISTRIP | IXON  );
    newt.c_lflag = ~( ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL );
    newt.c_cc[VTIME]    = 0;     /* キャラクタ間タイマを使わない */
    newt.c_cc[VMIN]     = 1;     /* 1文字来るまで,読み込みをブロックする */
    //newt.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
    //newt.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
    //newt.c_cc[VERASE]   = 0;     /* del */
    //newt.c_cc[VKILL]    = 0;     /* @ */
    //newt.c_cc[VEOF]     = 4;     /* Ctrl-d */
    //newt.c_cc[VSWTC]    = 0;     /* '\0' */
    //newt.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
    //newt.c_cc[VSTOP]    = 0;     /* Ctrl-s */
    //newt.c_cc[VSUSP]    = 0;     /* Ctrl-z */
    //newt.c_cc[VEOL]     = 0;     /* '\0' */
    //newt.c_cc[VREPRINT] = 0;     /* Ctrl-r */
    //newt.c_cc[VDISCARD] = 0;     /* Ctrl-u */
    //newt.c_cc[VWERASE]  = 0;     /* Ctrl-w */
    //newt.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
    //newt.c_cc[VEOL2]    = 0;     /* '\0' */    
    if(tcsetattr(STDIN_FILENO, TCSANOW, &newt)==-1) {
        fprintf(stderr,"error tcsetattr\n");
        exit(EXIT_FAILURE);
    }
    /* ノンブロックモードに設定 */
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
    if(oldf<0) {
        fprintf(stderr,"error fcntl\n");
        exit(EXIT_FAILURE);
    }   
    if(fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK)<0) {
        fprintf(stderr,"error fcntl\n");
        exit(EXIT_FAILURE);
    }   
    
    ch0 = getchar();
    if(ch0==0x1B) {  //矢印キー Fnキーなどの取得 取得できないキーもある
        ch1 = getchar();
        ch2 = getchar();
        if(ch2==0x32) {
            ch3 = getchar();
            if(ch3==0x7e) {
                ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
            } else {
                ch4 = getchar();
                ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
            }
        } else if(ch2==0x31) {
            ch3 = getchar();
            ch4 = getchar();
            ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
        } else if((ch2==0x33)||(ch2==0x35)||(ch2==0x36)) {
            ch3 = getchar();
            ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
        } else {
            ret = (ch0<<16) | (ch1<<8) | ch2;
        }
    } else if(ch0 != EOF) {
        ret = ch0;
    } else {
        ret = 0;
    }

    if(tcsetattr(STDIN_FILENO, TCSANOW, &oldt)==-1) {
        fprintf(stderr,"error tcsetattr\n");
        exit(EXIT_FAILURE);
    }
    if(fcntl(STDIN_FILENO, F_SETFL, oldf)<0) {
        fprintf(stderr,"error fcntl\n");
        exit(EXIT_FAILURE);
    }           
    if(ret == 0x03) { //[Ctrl]+[C] で割り込み発生
        pid_t pid = getpid();
        kill(pid, SIGINT);
    }
    /*if(ret == 0x1a) { //[Ctrl]+[Z] で割り込み発生
        pid_t pid = getpid();
        kill(pid, SIGTSTP);
    }*/
    return ret;
}


/*
 * 押されたキーの値を返す
 * ブロックする。キーが押されるまで戻らない。
 */
int getkey(void)
{
    struct termios oldt, newt;
    int ch0,ch1,ch2,ch3,ch4;
    int ret;

    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_iflag = ~( BRKINT | ISTRIP | IXON  );
    newt.c_lflag = ~( ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL );
    newt.c_cc[VTIME]    = 0;     /* キャラクタ間タイマを使わない */
    newt.c_cc[VMIN]     = 1;     /* 1文字来るまで,読み込みをブロックする */
    if(tcsetattr(STDIN_FILENO, TCSANOW, &newt)==-1) {
        fprintf(stderr,"error tcsetattr\n");
        exit(EXIT_FAILURE);
    }
    
    ch0 = getchar();
    if(ch0==0x1B) {  //矢印キー Fnキーなどの取得 取得できないキーもある
        ch1 = getchar();
        ch2 = getchar();
        if(ch2==0x32) {
            ch3 = getchar();
            if(ch3==0x7e) {
                ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
            } else {
                ch4 = getchar();
                ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
            }
        } else if(ch2==0x31) {
            ch3 = getchar();
            ch4 = getchar();
            ret = (ch1<<24) | (ch2<<16) | (ch3<<8) | ch4;
        } else if((ch2==0x33)||(ch2==0x35)||(ch2==0x36)) {
            ch3 = getchar();
            ret = (ch0<<24) | (ch1<<16) | (ch2<<8) | ch3;
        } else {
            ret = (ch0<<16) | (ch1<<8) | ch2;
        }
    } else if(ch0 != EOF) {
        ret = ch0;
    } else {
        ret = 0;
    }

    if(tcsetattr(STDIN_FILENO, TCSANOW, &oldt)==-1) {
        fprintf(stderr,"error tcsetattr\n");
        exit(EXIT_FAILURE);
    }
    if(ret == 0x03) { //[Ctrl]+[C] で割り込み発生
        pid_t pid = getpid();
        kill(pid, SIGINT);
    }

    return ret;
}

int main(int argc,char *argv[])
{
    int key;

    while (1) {
        //key = getkeyNB();
        key = getkey();
        if (key!=0) {
            printf("key code %4x\n",key);
        }else{
            printf("wait\n");
            usleep(100000);
        }
            
    }

    return 0;
}

参考

最新の画像もっと見る

コメントを投稿