윈도우즈는 WaitForSingleObject() API 함수를 이용하면 아주 간단히 끝낼 수 있지만, 맥을 비롯한 POSIX 쪽으로 넘어가면 상황이 좀 더 복잡해진다. 기본적으로 유닉스는 stdin을 통해 비동기 입출력을 할 수 없기 때문이다.

하지만 기본적으로 안 된다는 거지, 불가능하다는 소린 아니다.
여기 도움이 될 만한 링크가 하나 있다.

Non-blocking user input in loop without ncurses.

이 글을 참고하면 앞 포스팅에서 말했던 nonblocking 입력을 구현해 낼 수 있지만, 윈도우즈에서와 다른 점이 하나 있다. echo가 없어지지 않는다는 점이다. 이 문제는 termios 설정 과정에서 플래그를 조금만 조작해 주면 해결할 수 있다.

/**
 @a http://cc.byexamples.com/20070408/non-blocking-user-input-in-loop-without-ncurses/
 
 with few modification.
 */

#include<sys/time.h>  // sys time.h
#include<sys/types.h> // sys types.h
#include<termios.h> // termios.h
#include<unistd.h> // unistd.h
#include<cstdio> // C라면 그냥 stdio.h로 바꿔 주면 된다..

#define NB_ENABLE 0
#define NB_DISABLE 1

void nonblock(int state)  
{
    struct termios ttystate;

    // 터미널 상태를 읽어온다.
    // STDIN_FILENO = fileno(stdin) (정수형이다) 
    tcgetattr(STDIN_FILENO, &ttystate);  

    if (state==NB_ENABLE)  
    {
        //turn off canonical mode  
        ttystate.c_lflag &= ~ICANON;
        // 에코를 끄는 방법은 간단하다
        ttystate.c_lflag &= ~ECHO;
        // 최소로 읽어올 글자수를 정함
        ttystate.c_cc[VMIN] = 1;  
    }  
    else if (state==NB_DISABLE)  
    {
        // Canonical 모드를 다시 사용한다
        ttystate.c_lflag |= ICANON;  
        // 에코도 복구해줘야 한다... ㅡㅡ
        ttystate.c_lflag |= ECHO;
    }  
    // 지정한 옵션대로 터미널을 설정한다.
    tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}  

int kbhit()  
{  
    struct timeval tv;  
    fd_set fds;  
    tv.tv_sec = 0;  
    tv.tv_usec = 0;  
    FD_ZERO(&fds);  
    FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0  
    select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);  
    return FD_ISSET(STDIN_FILENO, &fds);  
}

int main()
{
    char c;  
    int i=0;  

    nonblock(NB_ENABLE);  
    while(!i)  
    {  
        usleep(1);
        i=kbhit();
        if (i!=0)
        {
            c=fgetc(stdin);
            if (c=='q')
                i=1;
            else
                i=0;
        }
    }
    printf("\n you hit %c. \n",c);  
    nonblock(NB_DISABLE);

    return 0; 
}

이건 여담인데.. BSD 계열의 개선된 폴링 메커니즘인 kqueue는 애초에 stdin, tty 등 콘솔 장치와 연계되지가 않고, poll의 경우는 타이거에서 stdin과 연계가 안되는 '버그'가 있다. 결국 select 뿐이다...

확인은 안해봤지만 위 코드는 아마 POSIX 표준을 만족하는 운영체제에서는 다 실행될 것으로 본다.

이건 여담 2인데, 사파리에선 텍스트큐브 위지윅 편집기가 동작하지 않는 것 같다.


에코 관련해서 버그가 있다. 손좀 봐야겠다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2010/01/29 17:51 2010/01/29 17:51
Posted by 호빵