오늘도 밤이야

[UNIX] 개행 없이 한 글자씩 입력받는 getch() 만들기 (터미널 제어) 본문

UNIX

[UNIX] 개행 없이 한 글자씩 입력받는 getch() 만들기 (터미널 제어)

hyeonski 2021. 3. 27. 06:12

프롬프트 또는 콘솔 환경을 만들기 위해서 키보드 입력을 한 자씩 받아야할 때가 있다. unistd.h의 read함수를 STDIN에서 1바이트만큼 받으면 되지 않을까? 해서 다음과 같은 코드를 실행해보았다.

#include <unistd.h>
#include <stdio.h>

int main(void)
{
	char c;

	while (read(0, &c, sizeof(c)) > 0)
	{
		printf("input: %c\n", c);
	}
}

????? 왜 안되지... 엔터(return)를 입력하고 나서야 한 자씩 출력되었다..

read는 분명 1byte씩 잘 읽고 있다. 그렇다면 왜 return이 입력되기 전에는 계속 입력만 받고 있을까?

이는 터미널의 canonical input 설정 때문이다. 한 line(line의 기준은 '\n'이다) 단위로 읽기 때문에 read()가 몇 byte로 읽든 newline이 입력되기 전까지 실시간으로 읽어주지 않는다. 

 

터미널 제어 함수를 통해 설정을 바꿔보자!

<termios.h>와 함께라면 터미널 설정을 바꿀 수 있다.

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

int main(void)
{
	char c;
	struct termios term;
    
	tcgetattr(STDIN_FILENO, &term);
	term.c_lflag &= ~ICANON;    // non-canonical input 설정
	term.c_lflag &= ~ECHO;      // 입력 시 터미널에 보이지 않게
	term.c_cc[VMIN] = 1;        // 최소 입력 버퍼 크기
	term.c_cc[VTIME] = 0;       //버퍼 비우는 시간 (timeout)
	tcsetattr(STDIN_FILENO, TCSANOW, &term);
    
	while (read(0, &c, sizeof(c)) > 0)
	{
		printf("input: %c\n", c);
	}
}

먼저 터미널 설정을 받아오고 수정할 termios 구조체를 선언한다. tcgetattr()를 통해 STDIN의 설정을 받아오고, lflag와 c_cc를 수정해준다. 수정된 값은 tcsetattr()를 통해 반영시킬 수 있다.

 

~ICANON, ~ECHO와 현재 lflag를 연산해주어 canonical input 설정과 ECHO (키보드 입력을 터미널에 보여주는) 설정을 비활성화 해준다.

non-canonical input에서는 VMIN과 VTIME을 가지고 입력 프로세스 기준을 설정하는데, VMIN은 최소 입력 버퍼의 크기이다. 만약 3의 값으로 설정할 경우 3자를 입력받고 나서 가장 먼저 입력된 것을 처리한다. VTIME은 버퍼를 비워주는 timeout 시간이다. 설정된 시간만큼 입력이 없으면 입력 버퍼에 있던 값들을 처리해준다. 지금은 한 자씩 즉시 처리해야하므로 VMIN과 VTIME을 각각 1과 0으로 설정해준다.

 

실행 결과

한 글자 입력될 때마다 바로바로 처리해주고 있다!

 

 

'긴 글 읽어주셔서 감사합니다. 잘못된 정보나 수정 사항, 질문 등은 댓글로 남겨주셔주시면 감사하겠습니다!'

 

참고 자료

 

Canonical or Not (The GNU C Library)

17.3 Two Styles of Input: Canonical or Not POSIX systems support two basic modes of input: canonical and noncanonical. In canonical input processing mode, terminal input is processed in lines terminated by newline ('\n'), EOF, or EOL characters. No input c

www.gnu.org

 

termios(3) - Linux manual page

termios(3) — Linux manual page TERMIOS(3) Linux Programmer's Manual TERMIOS(3) NAME         top termios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed, cfsetspeed - get and

man7.org

 

마음의 안정을 찾기 위하여 - 리눅스에서 getch() 함수 구현 그리고 터미널 제어

리눅스상에서 콘솔을 통해 키를 입력받을때, Return키를 입력받지 않고 키 값에 반응해 실시간(?)으로 처리되는 작업이 필요할때가 있다. 헌데 리눅스에서는 윈도우처럼 getch함수가 존재하지 않기

blog.dolba.net

 

termios 구조체

termios 구조체 - POSIX가 지정하는 표준 인터페이스 - 시스템 V 인터페이스 termio와 비슷하다. - t...

blog.naver.com

 

Comments