본문 바로가기

About 배우고 익히는법/펌글

[Linux] shmat - 공유메모리 관리 연산


만약 이 강좌가 이해가 잘 안된다면

 http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10104&eid=pwktyoG0krHezT0E2H9jx8zQxNWLOxXT#BtnCLose 




오랜만에 C++ 상속에 대해 고민좀 하다가 조건부 컴파일이란 걸 보고 쓰게 됨.

  

#define, #ifdef, #ifndef, #endif 등 이것들을 '전처리기' 라고 합니다.

 

a. #define은 '매크로' 라고도 하며 값을 정의(치환)하는 역할을 합니다.

b. #ifdef는 'if define' = '만약 정의되어 있다면' 이란 뜻입니다.

c. #ifndef는 'if not define' = '만약 정의되어 있지 않다면' 이란 뜻입니다.

d. #endif는 꼭 b, c 끝에 써줘야 합니다.

 

 

 

전처리기 순서로 해석을 해보겠습니다.

 

#define STAR

'STAR' 라는 이름을 정의.

 

#ifdef STAR

      printf("Star ");

#endif

만약 'STAR' 라는 이름이 정의되어 있다면, "Star " 문자열을 출력하는 printf 함수 실행

 

#ifndef CRAFT

      printf("Craft ");

#endif

만약 'CTRAFT' 라는 이름이 정의되어 있지 않다면, 아래 명령 실행

 

#ifndef WAR

     printf("\n");

#endif

만약 'WAR' 라는 이름이 정의되어 있지 않다면, 아래 명령 실행

 

 

주석처리된 두 코드를 주석 없애도 됩니다.

 

 #ifndef STAR
 #define STAR
 #endif

만약 'STAR' 라는 이름이 정의되어 있지 않다면 'STAR'를 정의함.

 

저 두 코드를 주석처리하나 주석 안하나 결과는 똑같습니다.

 

 

 

 

※ 왜 저것들을 main 함수 안에 써넣었나 궁금하면 한번 밖으로 빼보세요.


저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

포인터....^^*

포인터는 C의 꽃이라고 할 수 있죠..

포인터를 못하면 C, C++에 아무런 의미가 없다고 해도 과언이 아니라 생각합니다.

 

일반적으로 int는 4바이트 double는 8바이트.... char은 1바이트.. 이렇게 됩니다.

이러한 변수들은 그 변수에 맞는 타입을 지정할 경우에 그렇게 크기가 정해지는 것이고요..

포인터 변수.. 즉! int*, char*, double*, long*등.. 포인터가 붙은 변수들은 모두가 다 4바이트가 됩니다.

이는 0x0000부터 0xffff까지의 메모리상의 주소값이 저장될 수 있는 변수이기 때문이죠..

예를 들어 int a = 5; 라고 선언을 하게 된다면 어디엔가 4바이트의 메모리가 잡히게 되고 그 곳의 이름은 a, 그리고 그 곳의 값은 5가 되는 것이겠죠..

그렇다면 이렇게 잡히게 된 메모리에도 주소가 있지 않을까요?

네.. 그렇겠죠? 물론 모든 메모리에는 메모리의 주소가 있습니다.

그리고 이렇게 메모리 주소를 저장할 수 있는 곳의 메모리가 4바이트가 되겠고요..  그곳 메모리의 주소는 4바이트의 메모리가 시작하는 곳의 주소를 말합니다.

 

여기서... 포인터 변수라 함은 이러한 주소를 저장할 수 있는 변수라 생각하시면 됩니다.

위의 a라는 변수가 잡혀있는 메모리의 주소가 0x2486이라면

int *pa;

pa = &a;

(&를 변수앞에 붙이면 해당 변수의 주소값이 되는것은 아시죠?)

이렇게 된다면 pa의 값을 printf로 찍어보면 0x2486이 되는 것이죠..

이렇게 포인터 변수는 어떤 메모리의 주소값을 가지고 있다고 생각하시면 됩니다.

 

char1바이트밖에 안되지만

char ch = 'a';

char *pch;

pch = *ca;

이런 식으로 pch에 저장을 하게 되어도 ca의 크기인 1바이트가 아닌 ca의 주소값은 4바이트이기때문에 pch의 크기는 1바이트가 아닌 4바이트가 되는 것이죠..

 

이와 반대로 변수의 크기가 큰 경우에도 그의 주소값은 언제나 4바이트가 되는 것이고요~^^*

이해가 잘 되셨는지 모르겠네요.. 아무튼 위의 내용은 주소값은 언제나 4바이트라는 것입니다.

 

그렇다면 구조체를 한 번 생각해 보지요..

만약 int, double, char, char[10]...이렇게 변수가 들어있는 구조체를 선언하게 된다면 크기가 무척 크겠죠? 이보다 더 커질 수도 있겠고요..

하지만 이러한 구조체타입의 변수를 선언해도 이 구조체 변수의 주소는 4바이트가 됩니다.

무척 긴 메모리의 주소값을 다 쓰는 것이 아니고 이 구조체가 들어가 있는 메모리의 시작점이 이 구조체 변수의 주소가 되는 것이니까요~

 

자 이제.. 함수가 하나 있습니다.

이 함수는 위에서 선언해 놓은 구조체 타입을 입력 매개변수로 받습니다.

만약 구조체 타입의 변수를 그냥 넘기게 된다면 상당히 큰 메모리가 하나 더 잡히게 됩니다.

넘겨받는 함수쪽에서도 같은 구조체 타입을 선언해 메모리에 잡아놓고 매개변수로 받아서 사용을 하기때문에 그만큼 메모리를 허비하게 되는 것이죠..

그런데 만약 이 구조체 전부를 넘기지 않고 이 구조체의 주소만 넘겨서 사용을 하면 받는쪽에서 4바이트의 포인터변수만 선언해서 사용을 할 수 있습니다.

그렇다면 4바이트의 메모리만 더 사용하면 된다는 얘기죠~^^*

이렇게 사용하면 메모리의 낭비가 많이 줄겠죠?

 

여기까지는 포인터에 관한 간략한 설명이었고요..

포인터를 사용하면 유용한 방법... 그리고 어떤 때 사용을 하는가..

이것은 여기 지식in에 질문이 많이 올라와 있을꺼엥요..

다른 것 다 필요없고요..

swap()함수에 관해 찾아보세요..

스왑함수에 관해 찾아보시면 포인터변수를 사용해 스왑하는 예제와

그냥 일반 변수를 사용해 스왑하는 예제가 있을 것이고

이를 비교하는 내용과 왜 이렇게 되는지 정말 무쟈게 많이 나와있을 것입니다.

그래서 구지 제가 쓰지는 않겠습니다.

한 번 더 찾아보는 것도 좋겠죠~^^*

저보다 더 설명 잘 해 놓았을테니까요..

 

스왑을 보신다면 위엣분이 써 놓으신 콜 바이 벨류와 콜 바이 어드레스에 관해 알게되실 것입니다.

프로그램을 조금 더 배우시다 보면 콜 바이 레퍼런스에 관해서도 알게되실 것이고~^^*

콜바이 레퍼런스는 콜바이 어드래스와 비슷하지만 C에서는 어드래스로 사용을 할 것이고.. 더 상위 프로그램에서는 레퍼런스로 사용합니다.

 

시간이 되신다면 이 내용도 한 번 찾아서 공부해 보세요.

그렇다면 포인터에 좀 더 가깝게 접근을 하시게 될 것입니다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

C언어에서 특정폴더의 파일을 모두 읽어서, 리스트를 만드는 함수가 따로 있나요?



ANSI C에는 그러한 함수에 대한 표준이 없는 걸로 알고 있습니다.

 

하지만... 각 C컴파일러 마다 그러한 함수를 가지고 있지요.

 

Visual C에서는 _findfirst()와 _findnext()  그리고 _findclose() 를 찾아 보시구요.

Unix/Linux에서는 opendir()와 readdir() 그리고 closedir()을 찾아 보십시오.

 

다음은 Visual C에서 Sample로 나오는 FileFind라는 소스입니다.

 

Example

/* FFIND.C: This program uses the 32-bit _find functions to print
 * a list of all files (and their attributes) with a .C extension
 * in the current directory.
 */

#include 
#include 
#include 

void main( void )
{
    struct _finddata_t c_file;
    long hFile;

    /* Find first .c file in current directory */
    if( (hFile = _findfirst( "*.c", &c_file )) == -1L )
       printf( "No *.c files in current directory!\n" );
   else
   {
            printf( "Listing of .c files\n\n" );
            printf( "\nRDO HID SYS ARC  FILE         DATE %25c SIZE\n", ' ' );
            printf( "--- --- --- ---  ----         ---- %25c ----\n", ' ' );
            printf( ( c_file.attrib & _A_RDONLY ) ? " Y  " : " N  " );
            printf( ( c_file.attrib & _A_SYSTEM ) ? " Y  " : " N  " );
            printf( ( c_file.attrib & _A_HIDDEN ) ? " Y  " : " N  " );
            printf( ( c_file.attrib & _A_ARCH )   ? " Y  " : " N  " );
            printf( " %-12s %.24s  %9ld\n",
               c_file.name, ctime( &( c_file.time_write ) ), c_file.size );

            /* Find the rest of the .c files */
            while( _findnext( hFile, &c_file ) == 0 )
            {
                printf( ( c_file.attrib & _A_RDONLY ) ? " Y  " : " N  " );
                printf( ( c_file.attrib & _A_SYSTEM ) ? " Y  " : " N  " );
                printf( ( c_file.attrib & _A_HIDDEN ) ? " Y  " : " N  " );
                printf( ( c_file.attrib & _A_ARCH )   ? " Y  " : " N  " );
                printf( " %-12s %.24s  %9ld\n",
                   c_file.name, ctime( &( c_file.time_write ) ), c_file.size );
            }

       _findclose( hFile );
   }
}

Output

Listing of .c files

RDO HID SYS ARC  FILE         DATE                           SIZE
--- --- --- ---  ----         ----                           ----
 N   N   N   Y   CWAIT.C      Tue Jun 01 04:07:26 1993       1611
 N   N   N   Y   SPRINTF.C    Thu May 27 04:59:18 1993        617
 N   N   N   Y   CABS.C       Thu May 27 04:58:46 1993        359
 N   N   N   Y   BEGTHRD.C    Tue Jun 01 04:00:48 1993       3726

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

다음 int형 자료의 압축된 자료로 부터 char형으로 추출하여 출력하고자 한다.

1번과 3번에는 문장을 넣고 2번과 4번에는 결과값을 쓰시오

  
   unsigned int p = 25300;
   char a, b;
  
   a = ________________;          ---> 1번  답: p >> 8
   printf("%d\n", a);               ---> 2번 답: 98
   b = ________________;          ---> 3번  답: (char)(p)
   printf("%d\n", b);               ---> 4번  답: -44





---------------------------------------------------------------------------

먼저 진수의 개념과 변환에 대해 잘 아신다는 전제 하에 답변을 쓰도록 하겠습니다.

그렇지 않다면 10진수와 2진수, 16진수 등에 대해 좀 찾아보시고 답변을 읽어보세요.

 

십진수 25300을 16진수로 바꾸면 0x62D4 입니다.

질문에 써 주신 코드는 이 숫자 0x62D4를 0x62(98)과 0xD4(-44)로 나누려는 코드입니다.

 

이 값을 32비트 변수인 unsigned int형 변수 p에 넣으므로

p의 값은 0x000062D4 가 됩니다.

그럼 여기서 62를 뽑아내려면 p를 오른쪽으로 8비트만큼 쉬프트 해야겠지요.

따라서 1번 행의 답은 p >> 8 이 됩니다.

 

그 값을 char형 변수 a에 집어 넣으므로 a의 값은 0x62가 되는 것이고요.

따라서 2번의 출력 결과는 98 (0x62의 십진수 표현)이 됩니다.

 

세 번째 줄에서는 최하위 8비트만 추출해 내면 우리가 원하는 값을 얻어낼 수 있습니다.

따라서 char형으로 형 변환하여 하위 8비트만 b 변수로 대입하게 되는 것입니다.

 

그러면 b에는 최하위 8비트의 값인 -44 (0xD4의 십진수 표현. 이 때 최상위 비트는 1이므로 음수입니다.)

가 네 번째 줄의 실행 결과가 되겠지요.




---------------------------------------------------------------------------

< char 타입의 변수의 유효범위에 대하여 >



unsigned int 타입의 변수 p는 4 바이트(32비트) 크기의 부호없는 변수입니다...

 

그리고, char 타입의 변수 b 는 1 바이트(8비트) 크기의 부호있는 변수입니다...

 

변수는 어떠한 값을 담을 수 있는 그릇을 의미한다고 치면, 이 그릇에도 역시, 용량이 있을 것입니다...

 

이 char 타입의 변수에 담을 수 있는 유효범위는  -128 ~ 127 입니다...(중요 !!)

 

그럼... p 변수의 값을 b 변수에 대입하려 한다면...큰 그릇의 값을 작은 그릇의 값으로 옮기는 것이죠 ??

(4바이트 -> 1바이트)

 

그럼,,,어떻게 될까요 ?? 당연히 작은 그릇인 b 변수의 크기만큼만 담게 되고(하위 8비트) 나머지

 

상위 8비트는 잘려 나가게 됩니다...

 

10진수 25300 을  2진수로 표현하면...

 

0110   0010  1101  0100  이라고 했습니다...

 

그럼,,,여기서, 하위 8비트만 남게 됩니다...

 

1101  0100

=> 10진수로 이 값을 바꾸면 212 라는 값이 도출됩니다...

 

1101  0100 (2)  ==  212  (10)

 

char 타입의 유효범위는 -128   ~ 127 이라고 했는데, 위 값 212 는 이 b 변수의 유효범위를 벗어나게

 

됩니다...

 

원래, 변수의 유효범위를 벗어나면. 오류로 처리가 되어야 하지만. C/C++ 에서는 이 유효범위를

 

벗어난 값 들을 다르게 표현합니다...

 

즉,,,자료형의 범위보다 더 큰 값이 입력되면 초과된 값 만큼 음수쪽(작은 쪽의 값) 으로 이동된 값이~

 

자료형의 범위보다 더 작은 값이 입력되면 초과된 값 만큼 양수쪽(큰 쪽의 값) 으로 이동된 값이

 

기억 되게 됩니다...

 

예)

-128  ~ 127  범위를 갖는 char 타입의 변수에  128 을 대입하려고 한다면 ??

=> -128 로 표현

 

129 을 대입하려고 한다면 ?

=> -127 로 표현

 

-129 를 대입하려고 한다면 ?

=> 127 로 표현

 

-130 을 대입하려고 한다면 ?

=> 126 으로 표현

 

그러므로,,,이러한 규칙에 의하여 212 라는 값은 b 변수의 최대 값인 127 을 벗어나므로,

 

음수쪽으로 표현된 값인 -44 로 표현되는 것이죠...

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

프로그램 짜는 사람이면 누구나 enum 을 알것이다.

열거형라고 하나?? 아마 잘안쓰는 타입임에 분명할 것이다.

나는 고수들의 소스에서 enum이 어떻게 이용되는지 보고나서 충격을 금치 못했다.

극에 달한 활용이란...

enum 으로 리턴 타입을 정해주고 바로 리턴 시켜주는 기법인것이다.

 

예제를 보면... 이해가 쉬울것이다.

 

#include <stdio.h>

enum TEST_ARGUMENT{RET1=-1, RET2, RET3, RET4};

TEST_ARGUMENT testFunc(void){
    TEST_ARGUMENT ret;

    ret=RET1;

    return ret;
}

int main(){
    testFunc();

    return 0;
}

소름 끼치지 않는가? 고수들의 스킬이란... 대단하다 정말,

위의 방법의 소스를 짠 사람은 러시아 사람이다. 같은 회사에 근무하는 ㅋ

리눅스 프로그래머라 그런지 윈도 위주로 공부하는 우리와는 차이가 나는 기법들이 많다.

틈틈히 그들의 소스를 보면 정말 잘 짜여졌다는 생각이 ;;

namespace /템플릿 / 클레스화는 거의 상상을 초월하는 수준인듯 ;;

배울게 너무나 많다 -0-;;

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

C언어 char 형 배열이 한글(unicode)을 어떻게 인식할까?

한글의 이 char에 들어갈 경우 사실 2바이트로 나눠서 들어가야 하니까..

char[2] 에 들어간다고 봐야한다.

각설하고 unicode가 16비트이니 8비트 8비트씩 해서 0번과 1번 인덱스에 들어간다고 하자.

그리고 실제로 ㄱ부터 ㅎ까지 int값을 찍어보면 다음과 같은 결과를 얻을 수 있다.


마이너스 값이다. 아마도 첫 비트가 1인가보다.

문자를 인식하는 경우에 1번째 비트가 0이면 ASCII로 인식하고 1이면 UNICODE로 인식한다고 한다.

ㄱ->ㅎ으로 갈수록  1씩 증가한다.
ㅏ->ㅣ으로 갈수록  1씩 증가한다.
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

시그널의 특징 : 시그널은 대기열이 없다

시그널은 한 프로세스에 대해서 발생하는 시그널을 큐잉 하지 못한다(대부분의 유닉스). 특정 프로세스에 보내는 시그널은 커널에서 관리하는데 이때 커널은 프로세스에게 보낼 시그널을 한개 이상 유지할수 없다.

시그널을 받게 되면, 프로세스는 시그널 핸들러(신호 처리기)를 이용해서 시그널에 대한 처리를 하게 된다. 이때 즉 시그널 에 대한 처리가 끝나지 않은 상태에서 시그널이 발생되면 어떻게 될까 ?

시그널 처리중 동일한 시그널이 들어온다면 이 시그널은 블럭(보류)되었다가 핸들러가 처리를 끝나면 바로 전달된다. 이유는 시그널 이 발생되어서 해당 시그널에 대한 핸들러가 실행되면, 핸들러 실행이 종료되기까지 발생된 시그널에 대해서 block 을 하기 때문이다. 그런데 동일한 시그널이 2개가 발생을 한다면?
커널은 시그널의 대기열을 유지할수 없으므로 마지막에 도착한 시그널은 사라지게 된다.

만약 다른 종류의 시그널이 발생한다면, 그 즉시 시그널이 전달된다. 기존 시그널 핸들러가 작업중이던 말던 그 시점에서 새로운 시그널을 받아들이고, 핸들러를 빠져나가게 된다. 그리고 다시 복귀하지 않는다.

그럼 시그널은 신뢰하기 힘들겠군요?

시그널이 큐잉 되지 않는다는 점은 짧은 시간에 여러개의 시그널이 발생할때 시그널을 잃어버릴수도 있다라는 것을 의미한다. 물론 하나의 프로세스에 대해서 매우 짧은 시간에 시그널이 다수 발생하는 일은 그리 흔하지 않긴 하겠지만 가끔은 문제가 될소지가 있다. 우리가 일반 시그널이 큐잉 되도록 커널을 뜯어 고칠수는 없는 문제이므로, 이걸 완벽하게 해결할수는 없다. 그러나 핸들러를 최소한 아토믹 한 코드로 만듬으로써 이러한 문제의 발생을 줄일수는 있을것이다. 그렇지 않고 커널차원에서 이러한 문제를 해결하고자 한다면 리얼타임 시그널을 사용해야 할것이다.

가장 큰 문제는 시그널핸들러 처리중에 다른 종류의 시그널이 발생했을때이다. 위에서 말했듯이 이럴경우 핸들러 처리도중에 빠져나가게 되고, 다시 핸들러로 복귀하지 않게 된다. 이건 꽤 심각할수 있는데, 시그널을 받아서 어떠한 파일 작업을 하고 있는데, 도중에 다른 시그널이 들어와 버리면, 제대로된 파일작업결과를 보증할수 없을것이다.

다행히 Unix 에서는 위의 문제들을 해결할수 있는 시그널 제어 관련 함수들을 제공한다. 이문서의 뒷부분에서 이에 대한 내용을 다루게 될것이다. 다음은 시그널의 이러한 특징을 테스트하기 위한 예제 코드이다.
예제: sigint.c
#include <signal.h> 
#include <stdio.h> 
#include <string.h> 

void sig_int();
void sig_usr();

int main()
{
    char buf[255];
    int i= 0;

    if ((signal(SIGINT, sig_int)) == SIG_ERR)
    {
        perror("signal setting error : ");
        exit(1);
    }
    if ((signal(SIGUSR2, sig_usr)) == SIG_ERR)
    {
        perror("signal setting error : ");
        exit(1);
    }

    while(1)
    {
        printf("%d\n", i);
        sleep(1);
        i++;
    }

}

void sig_int()
{
    fprintf(stderr, "SIGINT !!!!\n");
    sleep(5);
}

void sig_usr()
{
    fprintf(stderr, "SIGUSR !!!!\n");
}

위 프로그램을 컴파일해서 실행을 시켜보자
[yundream@localhost test]# ./sigtest
1
2
3
이제 CTRL+C 를 입력해서 SIGINT 를 발생시켜보자. 그러면 프로세스는 SIGINT 신호를 받게 되고 시그널핸들러인 sig_int 를 호출할것이다. sig_int 는 "SIGINT !!!!\n" 을 호출하고 5초동안 잠들게 되는데, 이때 계속 해서 CTRL+C 를 한 10번 정도 입력해보자. 그럼 5초후에 프로세스에 다시 SIGINT 가 발생함을 알수 있을것이다. 여기서 다시 5초를 기다리면 시그널이 다시 전달될까 ? 물론 전달되지 않는다. 단지 같은 시그널에 대해서 한번에 하나의 시그널만 block(대기) 할수 있기 때문에, 나머지 9개의 시그널은 전무 무시되어 버린다.

그럼 이제 sig_int 핸들러를 호출하고 있는도중에 SIGUSR2 시그널을 발생시키면 어떻게 될까 ? 이 테스트는 ./sigtest 를 실행시키고 CTRL+C 를 입력 SIGINT 를 발생시키고, 핸들러를 호출하는 중에 쉘에서 kill 명령을 써서 sigtest 의 pid 로 SIGUSR2 시그널을 보내면 될것이다.
[yundream@localhost test]# ps -aux | grep sigtest
yundream      2176  0.0  0.1  1348  344 pts/5    S    23:48   0:00 ./sigtest
[yundream@localhost test]# kill -SIGUSR2 2176 
물론 kill 을 통해 시그널을 발생시키기 전에 ./sigtest 에서 SIGINT를 발생시켜야 한다. 위와 같이 해서 SIGUSR2 시그널을 발생시키면 어떻게 될까 ? sig_int 핸들러가 작업을 끝낼때 까지 기다리고 나서 SIGUSR2 가 전달될까 ? 답은 그 즉시 전달된다 이다. sig_int 핸들러가 수행중이건 아니건 곧바로 SIGUSR2 가 전달되고 sig_usr 핸들러가 실행됨을 볼수 있을것이다.

이러한 문제들의 대한 해법은 이문서의 뒷부분에서 다루도록 하겠다.

signal 관렴함수

지금까지 시그널의 개론적인 면을 살펴봤으니 실제 시그널을 보내고/받고/제어하기 위한 어떤 함수들이 있는지 살펴보도록 하겠다.

신호 보내기 함수

Unix 에서는 다음과 같은 신호를 보내기 위한 함수를 제공한다.
#include 
#include 

int kill(pid_t pid, int sig);
int raise(int sig);
kill 은 프로세스 그룹 혹은 프로세스에 시그널을 보낼때 사용된다. pid 가 0보다 큰 양수이면 해당 pid 를 가지는 프로세스에게 sig 시그널을 보내며, pid 가 0이면 현재 프로세스가 속한 프로세스 그룹의 모든 프로세스에게 시그널을 보낸다. pid 가 -1이면 1번 프로세스를 제외한 모든 프로세스에게, -1 보다 작으면 자신이 가지는 프로세스 그룹의 모든 프로세스에게 시그널을 보낸다.

raise 는 자기자신에게 sig 시그널을 보내는데, kill(getpid(), sig)로 동일한 일을 할수 있다.

신호 제어 함수

지난번 기사인 signal다루기(1)에서 에제 sig_hup.c 를 컴파일 해서 테스트 해보았다면 새로 execl 된 프로세스에서는 시그널 작동이 제대로 되지 않는다는 것을 알수 있을것이다. 이유는 오늘 내용을 조금 생각해 보면서 읽었다면 충분히 알아낼수 있을것이다. sig_hup 에서 SIGHUP시그널을 전달받아 sig_handler 를 실행시키면, 핸들러가 끝나기 전가지 SIGHUP 를 블럭시키게 된다. 핸들러에서 execl 을 호출하므로 이 핸들러는 절대 종료될수가 없게 된다. 당연히 SIGHUP 시그널은 계속 블럭 된채로 남게 되고, 새로 들어오는 SIGHUP 는 모두 무시되게 된다.

이 문제를 해결하기 위해서는 코드가 시작될때 해당 시그널이 블럭되어 있는지 확인해서 블럭을 해제시켜 주면 될것이다. 또한 시그널을 그룹지워서 관리하면 여러개의 시그널을 동시에 관리할수 있음으로 편리할것이다. 이러한 시그널 제어와 그룹핑을 위해서 Unix 는 다음과 같은 함수들을 제공한다.
// 시그널 그룹관리를 위한 함수
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset-t *set, int signum);

// 시그널(그룹) 제어를 위한 함수
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *mask);


sigemptyset 은 set 이 가르키는 시그널 집합을 초기화 한다.
sigfillset 은 set 이 모든 신호를 포함하도록 초기화 한다.
sigaddset 은 set에 시그널 signum 을 추가한다.
sigdelset 은 set에서 시그널 signum 을 삭제한다.
sigismember 은 set에 시그널 signum 이 포함되어 있는지 검사한다.

sigprocmask 은 시그널 마스크를 검사하고 변경하기 위해서 사용된다. 간단히 말해서 해당 시그널에 대해서 BLOCK, UNBLOCK 를 하기 위해서 사용한다.
sigpending 은 전달된 시그널(대기하고 있는시그널)에 대한 검사를 하기 위해서 사용된다.
sigsuspend 해당 신호가 발생할때까지 프로세스를 중단시킨다.

이상 시그널 그룹관리와 이의 제어를 위한 함수를 알아봤는데, 이 사이트의 목적인 "예제를 통한 이해" 를 위해서 간단한 예제를 준비했다. 이 예제는 signal다루기(1) 의 sig_hup 에서 발견되었던 "시그널블럭" 문제를 위의 함수들을 이용해서 해결하도록 할것이다.
예제: sig_hup2.c
#include <signal.h> 
#include <unistd.h> 

void sig_handler(int signo);

int main()
{
    int i = 0;
    sigset_t newmask, oldmask;

    printf("Program start\n");

    if (signal(SIGHUP, (void *)sig_handler) == SIG_ERR)
    {
        perror("signal set error ");
        exit(0)    ;
    }

    sigemptyset(&newmask);
    sigaddset(&newmask, SIGHUP);
    if (sigprocmask(SIG_UNBLOCK, &newmask, &oldmask) < 0)
    {
        perror("sigmask error : ");
        exit(0);
    }

    while(1)
    {
        printf("%d\n", i);
        i++;
        sleep(1);
    }
    return 1;
}

void sig_handler(int signo)
{
    execl("./sig_hup2", 0);
}

코드는 간단하다. 먼저 sigemptyset를 이용해서 newmaskset 을 비우고, sigaddset 를 이용해서 여기에 SIGHUP를 추가 시켰다. 그리고 sigprocmask 를 이용해서 newmaskset 에 포함된 시그널들에 대해서 블럭을 해제하도록 했다. 그러므로 핸들러가 종료되지 않아서 시그널이 블럭된 상태라도, 블럭해제가 되고 코드는 문제없이 작동하게 될것이다.

신호 받기 함수

지금까지 우리는 신호를 받기 위해서 signal 이라는 함수를 사용했었다.
#include 
void (*signal(int signum, void (*handler)(int)))(int);
signal 의 원형은 위와 같다. 사용방법은 몇개의 예제에서 이미 보아왔음으로 따로 설명하지 않겠다.

그러나 현재는 위의 signal 은쓰지 않고 대신 sigaction 함수를 사용한다. signal 은 ANSI C 에 의해서 정의된 함수인데, 신호에 대한 정의가 애매한 불안정한 함수이다. 그러므로 예전쏘쓰와의 호환을 위한 목적이 아니면 사용하지 않도록 한다.

sigaction 은 POSIX.1 에 의해서 제안된 안정된 신호체제를 제공한다.
#include 
int sigaction(int signum,  const  struct  sigaction  *act, struct sigaction *oldact);
signum 은 명시될 시그널 번호이다. struct sigaction 은 다음과 같이 정의 된다.
struct sigaction
{
    void (*sa_handler)(int);     // signum 과 관련된 핸들러 함수
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;           // 시그널 처리동안 블럭되어야할 시그널의 마스크
    int sa_flags;               // 시그널의 처리 행위 조절을 위한 플래그
    void (*sa_restorer)(void);  // 사용되지 않는다. 
}
위의 구조체에서처럼 단지 시그널번호와 핸들러만을 넘겨주는 signal 과 달리 구조체 멤버를 통해서 다양한 정보를 넘겨주게 되며, 이러한 특성을 이용해서 시그널마스킹, 관리, 핸들러를 유기적으로 묶어줄수 있게 된다.

마지막으로 "예제: sigint.c" 를 sigaction 버젼으로 작성하고 sigint.c 의 문제점이였던, 시그널 핸들러 실행중 다른 시그널이 들어왔을경우 중단되어 버리는 문제를 해결하도록 코드를 재 작성하였다.
예제: sigint2.c
#include <signal.h> 
#include <unistd.h> 
#include <string.h> 
#include <stdio.h> 

void sig_int(int signo);
void sig_usr(int signo);

int main()
{
    int i = 0;
    struct sigaction intsig, usrsig;

    usrsig.sa_handler = sig_usr;
    sigemptyset(&usrsig.sa_mask);
    usrsig.sa_flags = 0;

    intsig.sa_handler = sig_int;
    sigemptyset(&intsig.sa_mask);
    intsig.sa_flags = 0;

    if (sigaction(SIGINT, &intsig, 0) == -1)
    {
        printf ("signal(SIGALRM) error");
        return -1;
    }    

    if (sigaction(SIGUSR2, &usrsig, 0) == -1)
    {
        printf ("signal(SIGUSR2) error");
        return -1;
    }    

    while(1)
    {
        printf("%d\n", i);
        i++;
        sleep(1);
    }
}

void sig_int(int signo)
{
    sigset_t sigset, oldset;
    sigfillset(&sigset);
    // 새로들어오는 모든 시그널에 대해서 block 한다. 
    if (sigprocmask(SIG_BLOCK, &sigset, &oldset) < 0)
    {
        printf("sigprocmask %d error \n", signo);
    }
    fprintf(stderr, "SIGINT !!!!\n");
    sleep(5);
}

void sig_usr(int signo)
{
    printf("sig_usr2\n");
}
sig_int 핸들러를 호출하게 되면 sigfillset 를 이용해서 모든 시그널을 sigset 에 입력하고, 여기에 대해서 블럭을 하게 된다. 그러므로 새로 도착한 시그널은 현재 핸들러가 끝나서 블럭을 해제하기 전까지 대기하게 된다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

시그널의 특징 : 시그널은 대기열이 없다

시그널은 한 프로세스에 대해서 발생하는 시그널을 큐잉 하지 못한다(대부분의 유닉스). 특정 프로세스에 보내는 시그널은 커널에서 관리하는데 이때 커널은 프로세스에게 보낼 시그널을 한개 이상 유지할수 없다.

시그널을 받게 되면, 프로세스는 시그널 핸들러(신호 처리기)를 이용해서 시그널에 대한 처리를 하게 된다. 이때 즉 시그널 에 대한 처리가 끝나지 않은 상태에서 시그널이 발생되면 어떻게 될까 ?

시그널 처리중 동일한 시그널이 들어온다면 이 시그널은 블럭(보류)되었다가 핸들러가 처리를 끝나면 바로 전달된다. 이유는 시그널 이 발생되어서 해당 시그널에 대한 핸들러가 실행되면, 핸들러 실행이 종료되기까지 발생된 시그널에 대해서 block 을 하기 때문이다. 그런데 동일한 시그널이 2개가 발생을 한다면?
커널은 시그널의 대기열을 유지할수 없으므로 마지막에 도착한 시그널은 사라지게 된다.

만약 다른 종류의 시그널이 발생한다면, 그 즉시 시그널이 전달된다. 기존 시그널 핸들러가 작업중이던 말던 그 시점에서 새로운 시그널을 받아들이고, 핸들러를 빠져나가게 된다. 그리고 다시 복귀하지 않는다.

그럼 시그널은 신뢰하기 힘들겠군요?

시그널이 큐잉 되지 않는다는 점은 짧은 시간에 여러개의 시그널이 발생할때 시그널을 잃어버릴수도 있다라는 것을 의미한다. 물론 하나의 프로세스에 대해서 매우 짧은 시간에 시그널이 다수 발생하는 일은 그리 흔하지 않긴 하겠지만 가끔은 문제가 될소지가 있다. 우리가 일반 시그널이 큐잉 되도록 커널을 뜯어 고칠수는 없는 문제이므로, 이걸 완벽하게 해결할수는 없다. 그러나 핸들러를 최소한 아토믹 한 코드로 만듬으로써 이러한 문제의 발생을 줄일수는 있을것이다. 그렇지 않고 커널차원에서 이러한 문제를 해결하고자 한다면 리얼타임 시그널을 사용해야 할것이다.

가장 큰 문제는 시그널핸들러 처리중에 다른 종류의 시그널이 발생했을때이다. 위에서 말했듯이 이럴경우 핸들러 처리도중에 빠져나가게 되고, 다시 핸들러로 복귀하지 않게 된다. 이건 꽤 심각할수 있는데, 시그널을 받아서 어떠한 파일 작업을 하고 있는데, 도중에 다른 시그널이 들어와 버리면, 제대로된 파일작업결과를 보증할수 없을것이다.

다행히 Unix 에서는 위의 문제들을 해결할수 있는 시그널 제어 관련 함수들을 제공한다. 이문서의 뒷부분에서 이에 대한 내용을 다루게 될것이다. 다음은 시그널의 이러한 특징을 테스트하기 위한 예제 코드이다.
예제: sigint.c
#include <signal.h> 
#include <stdio.h> 
#include <string.h> 

void sig_int();
void sig_usr();

int main()
{
    char buf[255];
    int i= 0;

    if ((signal(SIGINT, sig_int)) == SIG_ERR)
    {
        perror("signal setting error : ");
        exit(1);
    }
    if ((signal(SIGUSR2, sig_usr)) == SIG_ERR)
    {
        perror("signal setting error : ");
        exit(1);
    }

    while(1)
    {
        printf("%d\n", i);
        sleep(1);
        i++;
    }

}

void sig_int()
{
    fprintf(stderr, "SIGINT !!!!\n");
    sleep(5);
}

void sig_usr()
{
    fprintf(stderr, "SIGUSR !!!!\n");
}

위 프로그램을 컴파일해서 실행을 시켜보자
[yundream@localhost test]# ./sigtest
1
2
3
이제 CTRL+C 를 입력해서 SIGINT 를 발생시켜보자. 그러면 프로세스는 SIGINT 신호를 받게 되고 시그널핸들러인 sig_int 를 호출할것이다. sig_int 는 "SIGINT !!!!\n" 을 호출하고 5초동안 잠들게 되는데, 이때 계속 해서 CTRL+C 를 한 10번 정도 입력해보자. 그럼 5초후에 프로세스에 다시 SIGINT 가 발생함을 알수 있을것이다. 여기서 다시 5초를 기다리면 시그널이 다시 전달될까 ? 물론 전달되지 않는다. 단지 같은 시그널에 대해서 한번에 하나의 시그널만 block(대기) 할수 있기 때문에, 나머지 9개의 시그널은 전무 무시되어 버린다.

그럼 이제 sig_int 핸들러를 호출하고 있는도중에 SIGUSR2 시그널을 발생시키면 어떻게 될까 ? 이 테스트는 ./sigtest 를 실행시키고 CTRL+C 를 입력 SIGINT 를 발생시키고, 핸들러를 호출하는 중에 쉘에서 kill 명령을 써서 sigtest 의 pid 로 SIGUSR2 시그널을 보내면 될것이다.
[yundream@localhost test]# ps -aux | grep sigtest
yundream      2176  0.0  0.1  1348  344 pts/5    S    23:48   0:00 ./sigtest
[yundream@localhost test]# kill -SIGUSR2 2176 
물론 kill 을 통해 시그널을 발생시키기 전에 ./sigtest 에서 SIGINT를 발생시켜야 한다. 위와 같이 해서 SIGUSR2 시그널을 발생시키면 어떻게 될까 ? sig_int 핸들러가 작업을 끝낼때 까지 기다리고 나서 SIGUSR2 가 전달될까 ? 답은 그 즉시 전달된다 이다. sig_int 핸들러가 수행중이건 아니건 곧바로 SIGUSR2 가 전달되고 sig_usr 핸들러가 실행됨을 볼수 있을것이다.

이러한 문제들의 대한 해법은 이문서의 뒷부분에서 다루도록 하겠다.

signal 관렴함수

지금까지 시그널의 개론적인 면을 살펴봤으니 실제 시그널을 보내고/받고/제어하기 위한 어떤 함수들이 있는지 살펴보도록 하겠다.

신호 보내기 함수

Unix 에서는 다음과 같은 신호를 보내기 위한 함수를 제공한다.
#include 
#include 

int kill(pid_t pid, int sig);
int raise(int sig);
kill 은 프로세스 그룹 혹은 프로세스에 시그널을 보낼때 사용된다. pid 가 0보다 큰 양수이면 해당 pid 를 가지는 프로세스에게 sig 시그널을 보내며, pid 가 0이면 현재 프로세스가 속한 프로세스 그룹의 모든 프로세스에게 시그널을 보낸다. pid 가 -1이면 1번 프로세스를 제외한 모든 프로세스에게, -1 보다 작으면 자신이 가지는 프로세스 그룹의 모든 프로세스에게 시그널을 보낸다.

raise 는 자기자신에게 sig 시그널을 보내는데, kill(getpid(), sig)로 동일한 일을 할수 있다.

신호 제어 함수

지난번 기사인 signal다루기(1)에서 에제 sig_hup.c 를 컴파일 해서 테스트 해보았다면 새로 execl 된 프로세스에서는 시그널 작동이 제대로 되지 않는다는 것을 알수 있을것이다. 이유는 오늘 내용을 조금 생각해 보면서 읽었다면 충분히 알아낼수 있을것이다. sig_hup 에서 SIGHUP시그널을 전달받아 sig_handler 를 실행시키면, 핸들러가 끝나기 전가지 SIGHUP 를 블럭시키게 된다. 핸들러에서 execl 을 호출하므로 이 핸들러는 절대 종료될수가 없게 된다. 당연히 SIGHUP 시그널은 계속 블럭 된채로 남게 되고, 새로 들어오는 SIGHUP 는 모두 무시되게 된다.

이 문제를 해결하기 위해서는 코드가 시작될때 해당 시그널이 블럭되어 있는지 확인해서 블럭을 해제시켜 주면 될것이다. 또한 시그널을 그룹지워서 관리하면 여러개의 시그널을 동시에 관리할수 있음으로 편리할것이다. 이러한 시그널 제어와 그룹핑을 위해서 Unix 는 다음과 같은 함수들을 제공한다.
// 시그널 그룹관리를 위한 함수
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset-t *set, int signum);

// 시그널(그룹) 제어를 위한 함수
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *mask);


sigemptyset 은 set 이 가르키는 시그널 집합을 초기화 한다.
sigfillset 은 set 이 모든 신호를 포함하도록 초기화 한다.
sigaddset 은 set에 시그널 signum 을 추가한다.
sigdelset 은 set에서 시그널 signum 을 삭제한다.
sigismember 은 set에 시그널 signum 이 포함되어 있는지 검사한다.

sigprocmask 은 시그널 마스크를 검사하고 변경하기 위해서 사용된다. 간단히 말해서 해당 시그널에 대해서 BLOCK, UNBLOCK 를 하기 위해서 사용한다.
sigpending 은 전달된 시그널(대기하고 있는시그널)에 대한 검사를 하기 위해서 사용된다.
sigsuspend 해당 신호가 발생할때까지 프로세스를 중단시킨다.

이상 시그널 그룹관리와 이의 제어를 위한 함수를 알아봤는데, 이 사이트의 목적인 "예제를 통한 이해" 를 위해서 간단한 예제를 준비했다. 이 예제는 signal다루기(1) 의 sig_hup 에서 발견되었던 "시그널블럭" 문제를 위의 함수들을 이용해서 해결하도록 할것이다.
예제: sig_hup2.c
#include <signal.h> 
#include <unistd.h> 

void sig_handler(int signo);

int main()
{
    int i = 0;
    sigset_t newmask, oldmask;

    printf("Program start\n");

    if (signal(SIGHUP, (void *)sig_handler) == SIG_ERR)
    {
        perror("signal set error ");
        exit(0)    ;
    }

    sigemptyset(&newmask);
    sigaddset(&newmask, SIGHUP);
    if (sigprocmask(SIG_UNBLOCK, &newmask, &oldmask) < 0)
    {
        perror("sigmask error : ");
        exit(0);
    }

    while(1)
    {
        printf("%d\n", i);
        i++;
        sleep(1);
    }
    return 1;
}

void sig_handler(int signo)
{
    execl("./sig_hup2", 0);
}

코드는 간단하다. 먼저 sigemptyset를 이용해서 newmaskset 을 비우고, sigaddset 를 이용해서 여기에 SIGHUP를 추가 시켰다. 그리고 sigprocmask 를 이용해서 newmaskset 에 포함된 시그널들에 대해서 블럭을 해제하도록 했다. 그러므로 핸들러가 종료되지 않아서 시그널이 블럭된 상태라도, 블럭해제가 되고 코드는 문제없이 작동하게 될것이다.

신호 받기 함수

지금까지 우리는 신호를 받기 위해서 signal 이라는 함수를 사용했었다.
#include 
void (*signal(int signum, void (*handler)(int)))(int);
signal 의 원형은 위와 같다. 사용방법은 몇개의 예제에서 이미 보아왔음으로 따로 설명하지 않겠다.

그러나 현재는 위의 signal 은쓰지 않고 대신 sigaction 함수를 사용한다. signal 은 ANSI C 에 의해서 정의된 함수인데, 신호에 대한 정의가 애매한 불안정한 함수이다. 그러므로 예전쏘쓰와의 호환을 위한 목적이 아니면 사용하지 않도록 한다.

sigaction 은 POSIX.1 에 의해서 제안된 안정된 신호체제를 제공한다.
#include 
int sigaction(int signum,  const  struct  sigaction  *act, struct sigaction *oldact);
signum 은 명시될 시그널 번호이다. struct sigaction 은 다음과 같이 정의 된다.
struct sigaction
{
    void (*sa_handler)(int);     // signum 과 관련된 핸들러 함수
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;           // 시그널 처리동안 블럭되어야할 시그널의 마스크
    int sa_flags;               // 시그널의 처리 행위 조절을 위한 플래그
    void (*sa_restorer)(void);  // 사용되지 않는다. 
}
위의 구조체에서처럼 단지 시그널번호와 핸들러만을 넘겨주는 signal 과 달리 구조체 멤버를 통해서 다양한 정보를 넘겨주게 되며, 이러한 특성을 이용해서 시그널마스킹, 관리, 핸들러를 유기적으로 묶어줄수 있게 된다.

마지막으로 "예제: sigint.c" 를 sigaction 버젼으로 작성하고 sigint.c 의 문제점이였던, 시그널 핸들러 실행중 다른 시그널이 들어왔을경우 중단되어 버리는 문제를 해결하도록 코드를 재 작성하였다.
예제: sigint2.c
#include <signal.h> 
#include <unistd.h> 
#include <string.h> 
#include <stdio.h> 

void sig_int(int signo);
void sig_usr(int signo);

int main()
{
    int i = 0;
    struct sigaction intsig, usrsig;

    usrsig.sa_handler = sig_usr;
    sigemptyset(&usrsig.sa_mask);
    usrsig.sa_flags = 0;

    intsig.sa_handler = sig_int;
    sigemptyset(&intsig.sa_mask);
    intsig.sa_flags = 0;

    if (sigaction(SIGINT, &intsig, 0) == -1)
    {
        printf ("signal(SIGALRM) error");
        return -1;
    }    

    if (sigaction(SIGUSR2, &usrsig, 0) == -1)
    {
        printf ("signal(SIGUSR2) error");
        return -1;
    }    

    while(1)
    {
        printf("%d\n", i);
        i++;
        sleep(1);
    }
}

void sig_int(int signo)
{
    sigset_t sigset, oldset;
    sigfillset(&sigset);
    // 새로들어오는 모든 시그널에 대해서 block 한다. 
    if (sigprocmask(SIG_BLOCK, &sigset, &oldset) < 0)
    {
        printf("sigprocmask %d error \n", signo);
    }
    fprintf(stderr, "SIGINT !!!!\n");
    sleep(5);
}

void sig_usr(int signo)
{
    printf("sig_usr2\n");
}
sig_int 핸들러를 호출하게 되면 sigfillset 를 이용해서 모든 시그널을 sigset 에 입력하고, 여기에 대해서 블럭을 하게 된다. 그러므로 새로 도착한 시그널은 현재 핸들러가 끝나서 블럭을 해제하기 전까지 대기하게 된다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

NCURSES Programming HOWTO




첫 번역이라 많이 미흡합니다 모자란 점이 있으면 지적해 주시기 바랍니다. --gsong


번역 gsong



작성 Pradeep Padala



#!
 
v1.7.1, 2002-06-25
Revision History                                                             
Revision 1.7.1         2002-06-25          Revised by: ppadala               
Added a README file for building and instructions for building from source.  
Revision 1.7           2002-06-25          Revised by: ppadala               
Added "Other formats" section and made lot of fancy changes to the programs. 
Inlining of programs is gone.                                                
Revision 1.6.1         2002-02-24          Revised by: ppadala               
Removed the old Changelog section, cleaned the makefiles                     
Revision 1.6           2002-02-16          Revised by: ppadala               
Corrected lot of spelling mistakes, added ACS variables section              
Revision 1.5           2002-01-05          Revised by: ppadala               
Changed structure to present proper TOC                                      
Revision 1.3.1         2001-07-26          Revised by: ppadala               
Corrected maintainers paragraph, Corrected stable release number             
Revision 1.3           2001-07-24          Revised by: ppadala               
Added copyright notice(LDP license) to main document, Put copyright notice   
(GPL) for programs as well, Corrected printw_example.                        
Revision 1.2           2001-06-05          Revised by: ppadala               
Incorporated ravi's changes. Mainly to introduction, menu, form, justforfun  
sections                                                                     
Revision 1.1           2001-05-22          Revised by: ppadala               
Added "a word about window" section, Added scanw_example.                    



이 문서는 ncurses 와 그 자매 라이브러리로 프로그래밍을 하는 데 있어 "All in One" 가이드를 지향하고 있다. 간단한 "Hello World" 프로그램 부터 보다 더 복잡한 폼 처리 까지 다룬다. ncurses 의 선행경험은 필요치 않다. 이 문서의 최신 버전은 [http]나의 웹 사이트 에서 찾을 수 있다. Mp_padala_at_yahoo.com 이 주소로 비평을 보내주길 바란다.

목차

[-]
1 NCURSES Programming HOWTO
1.1 Introduction
1.1.1 NCURSES 란 무엇인가?
1.1.2 NCURSES 로 무엇을 할 수 있는가?
1.1.3 어디서 그것을 구하는가?
1.1.4 문서의 목적 및 범위
1.1.5 프로그램들에 대해
1.1.6 문서의 다른 포맷들
1.1.6.1 tldp.org 에 있는 포맷들
1.1.6.2 소스로 부터 빌드하기
1.1.7 크레디트
1.1.8 Wish List
1.1.9 Copyright
1.2 The Hello World 프로그램
1.2.1 Ncurses 라이브러리와 같이 컴파일 하기
1.2.2 분석
1.2.2.1 initscr() 에 대해
1.2.2.2 신기한 refresh()
1.2.2.3 endwin() 에 대해
1.3 The Gory Details
1.4 초기화
1.4.1 raw() 등의 초기화 함수에 대해서
1.4.2 raw() 와 cbreak()
1.4.3 echo() 와 noecho()
1.4.4 keypad()
1.4.5 halfdelay()
1.4.6 그외 잡다한 초기화 함수들
1.4.7 예제
1.5 윈도우에 관해
1.6 printw() 같은 출력 함수에 대해
1.6.1 addch() 류의 함수들
1.6.2 mvaddch(), waddch() 와 mvwaddch()
1.6.3 printw() 류의 함수들
1.6.3.1 printw() 와 mvprintw
1.6.3.2 wprintw() 와 mvwprintw
1.6.3.3 vwprintw()
1.6.3.4 간단한 printw 예제
1.6.4 addstr() 류의 함수들
1.6.5 주의 사항
1.7 scanw() 같은 입력함수에 대해
1.7.1 getch() 류의 함수들
1.7.2 scanw() 류의 함수들
1.7.2.1 scanw() 와 mvscanw
1.7.2.2 wscanw() 와 mvwscanw()
1.7.2.3 vwscanw()
1.7.3 getstr() 류의 함수들
1.7.4 몇몇 예제
1.8 속성
1.8.1 세부 사항
1.8.2 attron() 대 attrset()
1.8.3 attr_get()
1.8.4 attr_ 함수들
1.8.5 wattr 함수들
1.8.6 chgat() 함수들
1.9 윈도우 함수의 모든 것
1.9.1 개괄
1.9.2 윈도우가 있으라 !!!
1.9.3 설명
1.9.4 예제의 다른 부분
1.9.5 다른 테두리 함수들
1.10 색상에 대해서
1.10.1 개괄
1.10.2 색상 정의 바꾸기
1.10.3 색상 값
1.11 키 입력 다루기. 펑션키, 방향키 등을 어떻게 입력받는가.
1.11.1 개괄
1.11.2 간단한 키 사용 예제
1.12 마우스 처리하기
1.12.1 개괄
1.12.2 이벤트 받아오기
1.12.3 전부 실습해보자
1.12.4 그외 잡다한 함수들
1.13 화면 제어
1.13.1 getyx() 함수들
1.13.2 화면 덤프
1.13.3 윈도우 덤프
1.14 그외 기능들
1.14.1 curs_set()
1.14.2 일시적으로 curses 모드 나가기
1.14.3 ACS_ 변수들
1.15 그외 라이브러리들
1.16 패널 라이브러리
1.16.1 개괄
1.16.2 패널 라이브러리와 컴파일 하기
1.16.3 패널 윈도우 탐색
1.16.4 사용자 포인터 쓰기
1.16.5 패널 이동 및 크기 변경
1.16.6 패널 숨기기 및 보여주기
1.16.7 panel_above() 와 panel_below() 함수들
1.17 메뉴 라이브러리
1.17.1 개괄
1.17.2 메뉴 라이브러리와 컴파일 하기
1.17.3 Menu Driver: 메뉴 시스템의 큰 일꾼
1.17.4 메뉴 윈도우
1.17.5 메뉴 스크롤 하기
1.17.6 여러 열을 가진 메뉴
1.17.7 여러 값을 가진 메뉴
1.17.8 메뉴 옵션들
1.17.9 유용한 사용자 포인터
1.18 폼 라이브러리
1.18.1 개괄
1.18.2 폼 라이브러리와 컴파일 하기
1.18.3 필드 가지고 놀기
1.18.3.1 필드의 위치와 크기 가져오기
1.18.3.2 필드 이동하기
1.18.3.3 필드 정렬
1.18.3.4 필드 표현 속성
1.18.3.5 필드 옵션 비트
1.18.3.6 필드 상태
1.18.3.7 필드 사용자 포인터
1.18.3.8 크기가 변하는 필드
1.18.4 폼 윈도우
1.18.5 필드 인증
1.18.6 폼 드라이버: 폼 시스템의 큰 일꾼
1.18.6.1 페이지 탐색 리퀘스트
1.18.6.2 필드간 탐색 리퀘스트
1.18.6.3 필드 내 탐색 리퀘스트
1.18.6.4 스크롤 리퀘스트
1.18.6.5 에디트 리퀘스트
1.18.6.6 Order Requests
1.18.6.7 어플리케이션 명령들
1.19 툴과 위젯 라이브러리들
1.19.1 CDK (Curses Development Kit)
1.19.1.1 위젯 리스트
1.19.1.2 몇몇 매력적인 기능들
1.19.1.3 결론
1.19.2 다이얼로그
1.19.3 Perl Curses Modules CURSES::FORM and CURSES::WIDGETS
1.20 Just For Fun !!!
1.20.1 The Game of Life
1.20.2 Magic Square
1.20.3 Towers of Hanoi
1.20.4 Queens Puzzle
1.20.5 Shuffle
1.20.6 Typing Tutor


1.1 Introduction


예전 원거리 터미널은 컴퓨터로부터 떨어진 채 시리얼 케이블을 통해 연결되어 있었다. 그 터미널들에 어떤 특정한 바이트들을 보냄으로써 터미널을 설정할 수 있었다. 터미널의 이 모든 기능들 (커서를 새 위치로 옮기거나, 화면의 일부를 지우거나, 화면을 스크롤 하거나, 모드를 바꾸거나, 화면의 형태, 색, 밝기, 깜빡임, 밑줄, reverse video 등을 설정하는 등) 은 흔히 escape sequences 라 불리는 바이트들에 의해서 이뤄졌다. 이 바이트들은 escape(0x1B) 문자로 시작하기 때문에 escape sequences 라 불린다. 심지어 오늘날에도 적절한 에뮬레이션 기능이 있으면, 우리는 escape sequences 를 에뮬레이터에 보내어 터미널 창에서 똑같은 효과를 볼 수 있다. 큭 만약 당신이 컬러로 한 줄을 찍고 싶다면, 다음을 당신의 콘솔에서 실행해보아라.

echo "^[[0;31;40mIn Color"


^ 와 [ 의 두개의 문자처럼 보이는 첫번째 캐릭터는 이스케이프 캐릭터 이다. 저것을 찍어볼려면 CTRL+V키를 누르고나서 ESC 키를 누르면 된다. 다른 모든 문자들은 일반적으로 입력하는 것과 같다. 그리고 당신은 문자들인 붉은색으로 찍히는 걸 보게 될 것 이다. 따로 변경을 하지 않으면 계속 그 상태이고, 원래대로 돌아갈려면 다음을 입력하라.

echo "^[[0;37;40m"


자, 저 마술같은 캐릭터들이 뭘 의미할까? 이해하기 어려운가? 게다가 저 문자들은 터미널들이 다르다면 또 달라질 수도 있다. 그래서 UNIX 의 디자이너들은 termcap 이라 불리는 매커니즘을 발명했다. 그것은 각각의 터미널들의 기능과 거기에 따른 특정한 효과를 내기위한 escape sequence 들을 나열해 놓은 파일이다. 후에 이것은 terminfo 에 의해 대체되었다. 이 매커니즘의 개념을 간략히 얘기하자면, 어플리케이션 프로그램에서 terminfo 데이터베이스에 쿼리를 하여 터미널이나 터미널 에뮬레이터에 보낼 control 문자들을 얻어오게 하는 것이다.


1.1.1 NCURSES 란 무엇인가?


아마 당신은 이 모든 기술적인 횡수들이 뭘 얘기하고자 하는 것인지 궁금할 것이다. 앞서에서 모든 어플리케이션 프로그램은 terminfo 를 쿼리하고 필요한 일들을 수행한다고 얘기했었다(control 문자들을 보내는 일 등등.). 이 복잡한 것을 다루는 것은 금방 어려워지며 이로 인해 'CURSES' 가 탄생하게 된다. Curses 는 "cursor optimization"을 재밌게 발음한 것이다. Curses 라이브러리는 raw 터미널코드 위에 wrapper 를 형성하고 있으며, 상당히 유연하고 효율적인 API(Application Programing Interface) 를 제공한다. 그것은 커서를 움직이거나, 윈도우를 생성하고, 색깔을 만들고, 마우스를 가지고 노는것 등의 함수를 제공한다. 어플리케이션 프로그램은 그 근간을 이루는 터미널의 기능에 대해서는 걱정할 필요가 없다.

자 그럼 NCURSES 는 무엇인가? NCURSES 는 원래의 System V Release 4.0(SVr4) curses 의 클론이다. 그것은 자유롭게 배포될수 있는 라이브러리이며, curses 의 구버전과 완벽히 호환된다. 쉽게말해 어플리케이션의 문자표현 -cell terminals 을 다루는 함수들의 라이브러리이다. 이제 이후로는 curses 와 ncurses 는 같은 뜻으로 쓰일 것이다.

ncurses 패키지는 원래 Pavel Curtis 에 의해 개발되었다. 이 패키지의 원 관리자는 Zeyd Ben-Halim <Mzmbenhal_at_netcom.com> 이다. Eric S. Raymond <Mesr_at_snark.thyrsus.com> 는 1.8.1 버전에서 많은 새로운 기능을 추가하였다. Jürgen Pfeifer 는 menu 와 form 에 관련된 모든 코드는 물론 [http]Ada95 에 대한 바인딩을 추가하였다. 현재 Thomas Dickey <Mdickey_at_herndon4.his.com> 와 Jürgen Pfeifer <Mjuergen.pfeifer_at_gmx.net> 에 의해 개발이 진행되고 있다. Florian La Roche <Mflorian_at_gnu.org> 는 ncurses 의 copyright 를 가진 Free Software Foundation 의 관리자로 활동하고 있다. 현재 관리자에게 메일을 보내기 위해서는 Mbug-ncurses_at_gnu.org로 연락하기 바란다.


1.1.2 NCURSES 로 무엇을 할 수 있는가?


Ncurses 는 터미널 기능의 wrapper 를 만들어줄 뿐만 아니라 텍스트모드에서 멋진 UI (User Interface)를 만들수 있는 튼튼한 프레임웍도 제공한다. 그것은 윈도우등을 만들 수 있는 함수를 제공한다. 그것의 자매 라이브러리인 패널, 메뉴, 폼등은 기본적인 curses 라이브러리에 확장성을 제공한다. 이 라이브러리들은 보통 curses 와 같이 설치된다. 누구나 다중 윈도우와 메뉴, 패털 그리고 폼을 포함하고 있는 어플리케이션을 작성할 수 있다. 윈도우들은 각각 독립적으로 다뤄지며, 'scrollability' 기능도 있고, 심지어 hidden 될 수도 있다.

메뉴들은 사용자에게 쉬운 명령선택을 할 수 있게 해준다. 폼은 data entry 를 사용하는 것과 윈도우를 표현하는 것을 쉽게 해준다. 패널들은 겹쳐지거나 쌓인 윈도우들을 다룰 수 있게 ncurses 의 기능을 확장시켜 준다.

이것들은 우리가 ncurses 로 할 수 있는 기본적인 것들에 불과하다. 다음으로 넘어갈 수록 우리는 이 라이브러리들의 모든 기능들을 알게 될 것이다.


1.1.3 어디서 그것을 구하는가?


자, ncurses로 어떤 일이 가능한지 알았으니, 써보고 싶어 근질근질하실 것 같다. ncurses 는 보통 설치할때 자동으로 깔린다. 만약 그것이 없거나, 당신만의 라이브러리로 컴파일하고 싶다면 아래를 계속 읽어보아라.

패키지 컴파일하기

ncurses 는 [ftp]ftp://ftp.gnu.org/pub/gnu/ncurses/ncurses.tar.gz[http]http://www.gnu.org/order/ftp.html 에 나와있는 아무 ftp 사이트에서 얻을 수 있다. 최근 안정화 버전은 5.2 20001021 이다.

설치하는 방법을 자세히 알고자 한다면 README 와 INSTALL 파일을 읽어보아라. 보통 다음과 같은 작업들을 포함하고 있다.

    tar zxvf ncurses<version>.tar.gz  # unzip and untar the archive                  
    cd ncurses<version>               # cd to the directory                          
    ./configure                             # configure the build according to your  
                                            # environment                            
    make                                    # make it                                
    su root                                 # become root                            
    make install                            # install it                             


RPM 을 이용한 설치

ncurses RPM 은 [http]http://rpmfind.net 에서 찾아서 다운로드하면 된다. RPM 은 루트 권한으로 다음의 명령을 실행하면 설치가 된다.

    rpm -i <downloaded rpm>                                                  




1.1.4 문서의 목적 및 범위


이 문서는 ncurses 와 그것의 자매 라이브러리로 프로그래밍 하는 데 있어 통합 가이드가 되는 것을 목표로 하고 있다. 단순한 "Hello World" 프로그램부터 더 복잡한 기능의 프로그램까지 다룰 것이다. ncurses 의 선행경험을 필요로 하지는 않는다.


1.1.5 프로그램들에 대해


문서에 있는 모든 프로그램들은 [http]여기에서 압축된 형태로 받을 수 있다. 그것들의 압축을 풀고 tar 로 파일을 풀면 된다. 디렉토리 구조는 아래와 같다.

#!
 ncurses                                                                      
   |                                                                         
   |----> <a class='nonexistent' href='/wiki.php/JustForFun'>?</a>JustForFun     -- just for fun programs                            
   |----> basics         -- basic programs                                   
   |----> demo           -- output files go into this directory after make   
   |          |                                                              
   |          |----> exe -- exe files of all example programs                
   |----> forms          -- programs related to form library                 
   |----> menus          -- programs related to menus library                
   |----> panels         -- programs related to panels library               
   |----> Makefile       -- the top level Makefile                           
   |----> README         -- the top level README file. contains instructions 
   |----> COPYING        -- copyright notice                                 


각각의 디렉토리에 있는 파일들과 그 설명은 다음과 같다.


#!
 <a class='nonexistent' href='/wiki.php/JustForFun'>?</a>JustForFun                                                                       
    |                                                                            
    |----> hanoi.c   -- The Towers of Hanoi Solver                               
    |----> life.c    -- The Game of Life demo                                    
    |----> magic.c   -- An Odd Order Magic Square builder                        
    |----> queens.c  -- The famous N-Queens Solver                               
    |----> shuffle.c -- A fun game, if you have time to kill                     
    |----> tt.c      -- A very trivial typing tutor                              
                                                                                 
  basics                                                                         
    |                                                                            
    |----> acs_vars.c            -- ACS_ variables example                       
    |----> hello_world.c         -- Simple "Hello World" Program                 
    |----> init_func_example.c   -- Initialization functions example             
    |----> key_code.c            -- Shows the scan code of the key pressed       
    |----> mouse_menu.c          -- A menu accessible by mouse                   
    |----> other_border.c        -- Shows usage of other border functions apa    
    |                               -- rt from box()                             
    |----> printw_example.c      -- A very simple printw() example               
    |----> scanw_example.c       -- A very simple getstr() example               
    |----> simple_attr.c         -- A program that can print a c file with       
    |                               -- comments in attribute                     
    |----> simple_color.c        -- A simple example demonstrating colors        
    |----> simple_key.c          -- A menu accessible with keyboard UP, DOWN     
    |                               -- arrows                                    
    |----> temp_leave.c          -- Demonstrates temporarily leaving curses mode 
    |----> win_border.c          -- Shows Creation of windows and borders        
    |----> with_chgat.c          -- chgat() usage example                        
                                                                                 
  forms                                                                          
    |                                                                            
    |----> form_attrib.c     -- Usage of field attributes                        
    |----> form_options.c    -- Usage of field options                           
    |----> form_simple.c     -- A simple form example                            
    |----> form_win.c        -- Demo of windows associated with forms            
                                                                                 
  menus                                                                          
    |                                                                            
    |----> menu_attrib.c     -- Usage of menu attributes                         
    |----> menu_item_data.c  -- Usage of item_name() etc.. functions             
    |----> menu_multi_column.c    -- Creates multi columnar menus                
    |----> menu_scroll.c     -- Demonstrates scrolling capability of menus       
    |----> menu_simple.c     -- A simple menu accessed by arrow keys             
    |----> menu_toggle.c     -- Creates multi valued menus and explains          
    |                           -- REQ_TOGGLE_ITEM                               
    |----> menu_userptr.c    -- Usage of user pointer                            
    |----> menu_win.c        -- Demo of windows associated with menus            
                                                                                 
  panels                                                                         
    |                                                                            
    |----> panel_browse.c    -- Panel browsing through tab. Usage of user        
    |                           -- pointer                                       
    |----> panel_hide.c      -- Hiding and Un hiding of panels                   
    |----> panel_resize.c    -- Moving and resizing of panels                    
    |----> panel_simple.c    -- A simple panel example                           


메인 디렉토리에 최상위 Makefile 이 있다. 그것은 모든 파일들을 빌드하고, 사용할 준비가된 실행파일들을 demo/exe 디렉토리에 넣어둔다. 또, 각각의 디렉토리에서 선택적으로 파일들을 메이크 할 수도 있다. 각 디렉토리는 거기에 있는 파일들의 용도가 무엇인지 설명하는 README 파일들이 있다.

모든 예제에서 나는 ncurses 디렉토리에 대한 상대 경로명을 쓰고 있다.

만약 각각의 프로그램 별로 보기를 원한다면, [http]http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/ncurses_programs/ 에서 볼 수 있다.

모든 프로그램들은 [http]GPL 하에 배포되며 당신은 그것을 당신이 원하는 대로 쓸 수 있다.


1.1.6 문서의 다른 포맷들


이 하우투 문서는 tldp.org 사이트에 다양한 포맷이 있다. 아래는 이 문서의 다른 포맷들에 대한 링크이다.


1.1.6.2 소스로 부터 빌드하기


만약 위의 링크들이 깨졌거나 sgml 파일로 무언가를 해보고 싶다면 이 절을 읽어보기 바란다.

sgml 문서 소스와 tar 와 gzip 으로 묶인 프로그램을 아래의 링크에서 다운받는다.

[http]http://cvsview.tldp.org/index.cgi/LDP/howto/docbook/ NCURSES-HOWTO/NCURSES-Programming-HOWTO.sgml
[http]http://cvsview.tldp.org/index.cgi/LDP/howto/docbook/ NCURSES-HOWTO/ncurses_programs.tar.gz

ncurses_programs.tar.gz 파일의 압축을 푼다
 tar zxvf ncurses_programs.tar.gz 

jade 프로그램을 사용하여 다양한 포맷의 문서를 만들어 낸다. 예를 들어 여러 개의 문서로 나누어진 html 파일을 생성하고 싶다면 아래와 같이 입력해 보자.

jade -t sgml -i html -d <docbook html 스타일 시트의 경로> 
NCURSES-Programming-HOWTO.sgml                                           

pdf 문서를 생성하려면 먼저 하나의 문서로 된 html 파일을 생성한다.

jade -t sgml -i html -d <docbook html 스타일 시트의 경로> -V nochunks    
NCURSES-Programming-HOWTO.sgml > NCURSES-ONE-BIG-FILE.html               

then use htmldoc to get pdf file with 그리고 htmldoc 을 사용하여 pdf 파일을 생성한다.

htmldoc --size universal -t pdf --firstpage p1 -f <output file name.pdf> 
NCURSES-ONE-BIG-FILE.html                                                

ps 파일을 생성하기 위해서는 다음과 같이 입력한다.

htmldoc --size universal -t ps --firstpage p1 -f <출력 파일 이름>   
NCURSES-ONE-BIG-FILE.html                                                

자세한 사항은 [http]http://www.tldp.org/LDP/LDP-Author-Guide/ 문서를 참조하기 바란다. 만약 문서 생성에 실패한 경우에는 p_padala_at_yahoo.com 으로 메일을 보내주기 바란다.


1.1.7 크레디트


I thank Sharath <Msharath_1_at_usa.net> and Emre Akbas for helping me with few sections. The introduction was initially written by sharath. I rewrote it with few excerpts taken from his initial work. Emre helped in writing printw and scanw sections.

Then comes Ravi Parimi <Mparimi_at_ece.arizona.edu>, my dearest friend, who has been on this project before even one line was written. He constantly bombarded me with suggestions and patiently reviewed the whole text. He also checked each program on Linux and Solaris. See his notes to check on your problems.


1.1.8 Wish List


This is the wish list, in the order of priority. If you have a wish or you want to work on completing the wish, mail Mp_padala_at_yahoo.com me.

  • Add examples to last parts of forms section. (I am working on it)

  • Prepare a Demo showing all the programs and allow the user to browse

through description of each program. Let the user compile and see the program in action. A dialog based interface is preferred. (My friend Mashoknn_at_cisco.com N.N.Ashok is working on it)
  • Add debug info. _tracef, _tracemouse stuff.

  • Accessing termcap, terminfo using functions provided by ncurses package.

  • Working on two terminals simultaneously.

  • Add things in miscellaneous section.



1.1.9 Copyright


Copyright (c) 2001 by Pradeep Padala. This document may be distributed under the terms set forth in the LDP license at [[http]http://www.linuxdoc.org/ COPYRIGHT.html] linuxdoc.org/COPYRIGHT.html.

This HOWTO is free documentation; you can redistribute it and/or modify it under the terms of the LDP license. This document is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the LDP license for more details.


1.2 The Hello World 프로그램


curses 의 세계에 온 걸 환영한다. 라이브러리로 뛰어들어 그것의 다양한 특징들을 살펴보기 전에, bells and whistles, 세계에 안녕을 표하는 간단한 프로그램을 만들어보자.


1.2.1 Ncurses 라이브러리와 같이 컴파일 하기


ncurses 라이브러리의 함수들을 쓰기 위해선 ncurses.h 가 인클루드 되어야 하고 프로그램을 ncurses 와 링크시키는 -lncurses 플래그가 더해져야 한다. ncurses.h 는 stdio.h 에 이미 인클루드 되어 있다.

    #include <ncurses.h>                                                     
    .                                                                        
    .                                                                        
    .                                                                        
                                                                             
    compile and link: gcc <program file> -lncurses                           


Example 1. The Hello World !!! Program

#include <ncurses.h>                                                         
                                                                             
int main()                                                                   
{                                                                            
        initscr();                      /* Start curses mode              */ 
        printw("Hello World !!!");      /* Print Hello World              */ 
        refresh();                      /* Print it on to the real screen */ 
        getch();                        /* Wait for user input */            
        endwin();                       /* End curses mode                */ 
                                                                             
        return 0;                                                            
}  




1.2.2 분석


위 프로그램은 화면에 "Hello World !!!" 를 찍고 종료한다. 이 프로그램은 어떻게 curses 를 초기화 하고, 스크린을 다루는지 또 어떻게 curses 모드를 종료하는지 보여준다. 자 이제 한줄 한줄 분석해보자.


1.2.2.1 initscr() 에 대해


initscr() 함수는 curses mode 로 터미널을 초기화 한다. 몇몇 구현에서는 화면을 클리어하고 빈 화면을 보여주기도 한다. curses 패키지를 이용해서 스크린 처리를 할려면 이 함수를 반드시 먼저 콜해야 한다. 이 함수는 curses 시스템을 초기화 하고 'stdscr' 이라 불리는 우리의 현재 윈도우와 몇몇 데이터들-구조체- 들을 위한 메모리를 할당한다. 아주 극한 상황에서나 이 함수는 curses 라이브러리의 데이터 구조체를 위한 메모리를 할당실패로 에러는 낼 것이다.

이것이 수행된 후 우리는 curses 세팅을 편의에 맞추기위한 다양한 초기화를 할 수 있다. 여기에 대한 상세 설명은 차차 다루기로 한다.


1.2.2.2 신기한 refresh()


다음 줄의 printw 는 "Hello World !!!" 를 화면에 찍는다. 이 함수는 stdscr 이라 불리는 윈도우 내에 (y,x)위치에 데이터를 찍는다는 점을 제외하고는 printf 함수와 비슷하다. 위 프로그램에서는 커서위치가 0,0 이기 때문에 윈도우의 왼쪽 맨 위 구석에 스트링을 찍게 된다.

다음은 신비한 refresh() 함수를 볼 차례이다. 우리가 printw 함수를 호출하면 사실상 데이터는 stdscr 이란 불리는 가상의 윈도우에만 쓰여지고 화면에는 아직 나타나지 않는다. printw 가 하는 일은 몇몇 플래그들과 데이터 구조체들을 갱신하고 stdscr 에 해당하는 버퍼에 데이터들을 쓰는 것이다. 그것을 화면에 표시하기 위해서는 refresh() 함수를 호출하여 curses 시스템이 그 버퍼의 내용들을 화면에 나타내도록 해야 한다.

여기엔 프로그래머가 가상의 스크린이나 윈도우에 여러번 갱신을 한다음 한번의 refresh 로 화면을 전부 갱신한다는 철학이 깔려 있다. refresh() 함수는 윈도우를 확인하고 변화된 부분만 갱신한다. 이것은 빠른 반응속도와 훨씬 좋은 유연함을 제공한다. 그러나 초보자에게는 조금 혼란스러운 면이 있다. 초보자들이 행하는 일반적인 실수는 printw() 류의 함수들을 써서 작업을 한다음 refresh() 를 까먹곤 한다는 것이다. 나 또한 여전히 이걸 때때로 까먹곤 한다


1.2.2.3 endwin() 에 대해


그리고 최종적으로 curses 모드를 끝내는 걸 잊어서는 안된다. 그렇지않으면 프로그램 종료 후에 당신의 터미널은 이상하게 작동할 것이다. endwin() 함수는 curses 의 하위 시스템들과 데이터 구조체가 점유했던 메모리를 해제하고 터미널을 일반적인 모드로 바꿔 놓는다. 이 기능은 curses 모드에서 작업을 마치게 되면 반드시 호출해야 한다.


1.3 The Gory Details


자 이제 우리는 간단한 curses 프로그램을 어떻게 작성하는지 살펴보았다. 이제 좀 더 상세한 것들을 알아보자. 당신이 화면에서 보는 것들을 바꿀 수 있는 많은 함수들이 있으면 앞으로 많이 사용될 다양한 특징들이 있다.

자 시작합니다...


1.4 초기화


우리는 이제 curses 시스템을 초기화 하기 위해서는 initscr() 함수를 호출해야 한다는 것을 안다. 이 초기화 이후에 curses 세션을 설정하기 위해 호출할 수 있는 함수들이 더 있다. 이걸 통해 우리는 curses 시스템에 터미널을 raw 모드로 세팅해달라고 하거나 색을 초기화 하거나 마우스등을 초기화 할 수도 있다. 그럼 initscr() 함수 이후에 보통 호출되는 함수들에 어떤것들이 있는지 살펴보도록 하자.


1.4.1 raw() 등의 초기화 함수에 대해서




1.4.2 raw() 와 cbreak()


보통 터미널 드라이버는 new line 이나 캐리지리턴이 들어올 때까지 사용자가 입력한 내용을 버퍼링한다. 그러나 대부분의 프로그램들은 사용자가 타이핑을 하고나서 될 수 있는 한 빨리 그 문자들을 필요로 한다. 위의 두 함수는 라인 버퍼링을 비활성화 시키는 데 자주 사용된다. 이 두 함수의 차이점은 suspend (CTRL-Z) 나 interrupt 그리고 quit (CTRL-C) 와 같은 control 문자들이 프로그램에 전달되는 방법에 있다. raw() 모드에서 이 control 문자들은 signal 을 발생시키지 않고 바로 프로그램에 전달된다. cbreak() 모드에서 이 control 문자들은 터미널 드라이버에 의해 다른 어떤 문자로 번역된다. 나는 개인적으로 사용자들을 보다 더 잘 제어하는 연습을 하기 위해서 raw() 를 애용하는 편이다.


1.4.3 echo() 와 noecho()


이 함수들은 사용자들이 터미널에 입력한 문자들을 echoing 할 것인지 제어한다. echoing 에 대한 더 많은 제어를 하기 위해서나 또는 getch() 등의 함수로 사용자들에게서 입력을 받고 있을 때 불필요한 echoing 을 없애기 위해서 이 함수들이 필요할 것이다. 대부분의 인터랙티브한 프로그램들은 noecho() 를 초기화때 호출하고 필요할때면 제어를 통해 echoing 을 한다. 이렇게 하면 프로그래머는 윈도우의 (y,x) 의 위치를 굳이 갱신시키지 않더라도 필요한 위치에 문자들을 echoing 할수 있는 융통성을 가지게 된다.


1.4.4 keypad()


이 함수는 내가 좋아하는 초기화 함수중 하나이다. 이것은 F1, F2, 방향키 같은 기능키들을 입력받을 수 있게 한다. 방향키들은 어떠한 사용자 인터페이스에서도 큰 비중을 차지하듯이, 거의 모든 인터랙티브한 프로그램들은 이 기능들을 필요로 한다. keypad(stdscr, TRUE) 를 호출하는 것은 일반 화면인 (stdscr) 에서 이 기능을 쓰게 한다. 이 문서의 차후 섹션에서 더 많은 키 사용법을 배우게 될 것이다.


1.4.5 halfdelay()


이 함수는 자주 쓰이지는 않지만 때론 매우 유용하게 쓰인다. halfdelay() 함수는 타이핑된 문자들이 프로그램에서 바로 사용가능하게 하는 cbreak() 모드와 비슷한 half-delay 모드를 쓰게 한다. 그러나 이 함수는 입력에 대해서 십분의 'X' 초 만큼 기다리며 만약 아무 입력이 없다면 ERR 을 리턴한다. 'X' 는 halfdelay() 함수에 전달되는 타임아웃 값이다. 이 함수는 당신이 사용자에게 입력을 요구하고 만약 특정 시간 동안 입력이 없다면 다른 어떤 작업을 할때에 사용된다. 패스워드 프롬프트 등에서 타임아웃이 있는 것등이 그 좋은 예이다.


1.4.6 그외 잡다한 초기화 함수들


위에서 얘기한 것 말고도 커서의 움직임을 설정하는 초기화 함수들이 좀 더 있다. 그러나 그것들은 위에서 언급한 것들만큼 잘 쓰이지 않는다. 이 중 몇몇은 부록에서 설명할 것이다.


1.4.7 예제


이 함수들의 사용법을 명확히 이해할 수 있는 프로그램을 짜보자.

Example 2. Initialization Function Usage example

#include <ncurses.h>                                                           
                                                                               
int main()                                                                     
{       int ch;                                                                
                                                                               
        initscr();                      /* Start curses mode            */     
        raw();                          /* Line buffering disabled      */     
        keypad(stdscr, TRUE);           /* We get F1, F2 etc..          */     
        noecho();                       /* Don't echo() while we do getch */   
                                                                               
        printw("Type any character to see it in bold\n");                      
        ch = getch();                   /* If raw() hadn't been called         
                                         * we have to press enter before it    
                                         * gets to the program          */     
        if(ch == KEY_F(1))              /* Without keypad enabled this will */ 
                printw("F1 Key pressed");/*  not get to us either       */     
                                        /* Without noecho() some ugly escape   
                                         * charachters might have been printed 
                                         * on screen                    */     
        else                                                                   
        {       printw("The pressed key is ");                                 
                attron(A_BOLD);                                                
                printw("%c", ch);                                              
                attroff(A_BOLD);                                               
        }                                                                      
        refresh();                      /* Print it on to the real screen */   
        getch();                        /* Wait for user input */              
        endwin();                       /* End curses mode                */   
                                                                               
        return 0;                                                              
}                                                                              


이 프로그램은 자체 주석이 되어 있다. 그러나 아직 설명하지 않은 함수가 하나 있는데, 사용자로부터 문자를 입력받는 getch() 함수가 그것이다. 이 함수는 입력후 <enter> 를 피하기 위해 라인 버퍼링을 해제할 수 있다는 것을 제외하면 getchar() 함수와 동일하다. getch() 의 더 많은 정보와 키를 입력받는 것에 대해서는 key management 섹션을 참고하길 바란다. attron 과 attroff 함수는 어떤 특성을 끄고 켜기 위해 사용된다. 예제에서는 문자를 bold 체로 찍기위해 사용하였다. 이 함수들도 후에 자세히 설명한다.


1.5 윈도우에 관해


ncurses 의 무수한 함수들로 뛰어들기 전에, 윈도우 에 대해 몇가지만 명확히 하도록 하자. 윈도우의 자세한 사항에 대해서는 다음 섹션에서 설명할 것이다.

윈도우는 curses 시스템에 의해 정의된 가상 화면이다. 그러나 윈도우는 당신이 Win9X 플랫폼에서 흔히 보던 테두리가 있는 창을 뜻하지는 않는다. curses 가 초기화될 때, 당신의 80x25 (또는 현재 실행되는 창의 크기만큼) 스크린을 의미하는 stdscr 이라는 기본 윈도우가 생성된다. 만약 당신이 몇몇 스트링을 찍거나, 입력을 받는 등의 간단한 작업들을 할 것이라면, 이 기본 윈도우만 써도 된다. 반면 창을 생성해서, 그 특정 윈도우에서 깔끔히 작동할 함수들을 호출해도 된다.

예를 들어, 만약 당신이 아래를 호출한다면

    printw("Hi There !!!");                                                  
    refresh();                                                               


이것은 stdscr 의 현재 커서 위치에 스트링을 찍을 것이다. 비슷하게 refresh() 함수 호출 또한 stdscr 에서만 작동할 것이다.

당신이 만약 윈도우를 만들었다면, 보통함수 이름 앞에 'w' 가 붙은 함수들을 호출해야만 한다.

    wprintw(win, "Hi There !!!");                                            
    wrefresh(win);                                                           


문서의 나머지 부분에서 배울 것이지만, 함수의 이름은 같은 작명법을 따른다. 각각의 함수에는 보통 비슷한 세개의 함수가 더 있다.

    printw(string);        /* Print on stdscr at present cursor position */     
    mvprintw(y, x, string);/* Move to (y, x) then print string     */           
    wprintw(win, string);  /* Print on window win at present cursor position */ 
                           /* in the window */                                  
    mvwprintw(win, y, x, string);   /* Move to (y, x) relative to window */     
                                    /* co-ordinates and then print         */   


보통 w 가 붙지 않은 함수는 윈도우 파라메터로 stdscr 을 사용하는 w 가 붙은 함수들에 대한 매크로이다.


1.6 printw() 같은 출력 함수에 대해


아마 당신은 이제 무언가를 하고 싶어 안달이 났을 것 같다. curses 함수들의 모험으로 다시 돌아가자. 자 이제 curses 는 초기화 되었다면, 세계와 interact 를 해보자.

화면에 출력을 하는 함수에는 크게 세종류가 있다.

  • addch() class: Print single character with attributes

  • printw() class: Print formatted output similar to printf()

  • addstr() class: Print strings

이 함수들은 서로 바꿔가며 써도 되고, 어느 함수를 쓰느냐는 스타일에 따른 문제다. 각각을 상세히 알아보자.


1.6.1 addch() 류의 함수들


이 함수들은 현재 커서 위치에 한 문자를 찍고 커서의 위치를 다음으로 옮긴다. 당신은 이걸 통해 문자를 찍을 수도 있지만, 보통 이 함수들은 몇몇 속성이 있는 문자들을 찍을 때 쓰인다. 속성에 대한 자세한 설명은 뒤에 다루고 있다. 만약 문자에 어떠한 속성들 (굵은 글씨, 반전색 등등) 이 결합되어 있다면 curses 가 그 문자를 출력할때 그 속성에 맞게 출력하게 된다.

문자에 특정 속성들을 넣으려면 다음의 두가지 방법이 있다.

  • 한 문자에 원하는 속성 매크로들을 OR 해주는 방법이다. 이 속성 매크로들은 ncurses.h 에서 찾을 수 있다. 예를 들어 굵고 밑줄이 그어진 문자 ch 를 찍고 싶다면 addch() 함수를 다음과 같이 호출하면 된다.
        addch(ch | A_BOLD | A_UNDERLINE);


  • attrset(), attron(), attroff() 와 같은 함수를 쓰는 방법이다. 이 함수들은 Attributes 섹션에서 설명한다. 간단히 말하면, 이것들은 주어진 윈도우의 속성을 조절한다. 한번 설정되면, 그것이 꺼지기 전까지는 그 윈도우 안에서 찍히는 문자들은 주어진 속성대로 출력된다.

부가적으로 curses 는 문자 기반 그래픽을 위해서 몇몇 특수한 문자들을 제공한다. 테이블 또는 수평선, 수직선 등을 그릴 때 쓸 수 있다. 이렇게 쓰일수 있는 문자들은 ncurses.h 파일 안에서 찾을 수 있다. 헤더파일에서 ACS_ 로 시작하는 매크로를 찾아보도록 해라.


1.6.2 mvaddch(), waddch() 와 mvwaddch()


mvaddch() 함수는 커서를 주어진 위치로 옮기고 문자를 출력한다. 다음과 같이 쓴다.

    move(row,col);    /* moves the cursor to rowth row and colth column */   
    addch(ch);                                                               
can be replaced by
    mvaddch(row,col,ch);                                                     


waddch() 함수는 addch() 와 비슷하다. 단 주어진 윈도우에 문자를 찍는다는 점을 제외하곤 말이다. (addch() 함수는 stdscr 윈도우에 문자를 찍는다는 것에 주의하라.)

이와 유사하게 mvwaddch() 함수도 주어진 윈도우내에서 주어진 위치에 문자를 찍을 때 사용된다.

자 우리는 기본적인 출력 함수인 addch() 에 대해 알아보았다. 그런데 만약 우리가 스트링을 찍길 원한다면 한글자씩 찍는 것은 매우 번거로운 일이 될 것이다. 운좋게도 ncurses 는 printf 류나 puts 류의 함수들을 제공한다.


1.6.3 printw() 류의 함수들


이 함수들은 스크린의 아무 위치에나 출력할 수 있는 기능이 더해진 printf() 함수라고 보면 된다.


1.6.3.1 printw() 와 mvprintw


이 두개의 함수는 printf() 와 작동법이 유사하다. mvprintw() 는 커서를 특정위치로 옮기고 출력할때 쓰인다. 만약 당신이 커서를 일단 먼저 옮기고 그 후에 printw() 함수로 출력을 하고자 한다면, move() 함수를 먼저 쓰고 printw() 함수를 쓰면된다. 이런 경우엔 mvprintw() 함수를 쓰면 되겠지만, 아무튼 당신이 원하는 대로 융통성있게 작성할 수 있다.


1.6.3.2 wprintw() 와 mvwprintw


이 두 함수는 인자로 받은 윈도우에 출력한다는 점을 제외하곤 위의 두 함수와 동일하다.


1.6.3.3 vwprintw()


이 함수는 vprintf() 와 비슷하다. 함수 인자의 종류가 다양한 경우를 출력할때 쓸 수 있다.


1.6.3.4 간단한 printw 예제


Example 3. A Simple printw example

#include <ncurses.h>                    /* ncurses.h includes stdio.h */                    
#include <string.h>                                                                         
                                                                                            
int main()                                                                                  
{                                                                                           
 char mesg[]="Just a string";           /* message to be appeared on the screen */          
 int row,col;                           /* to store the number of rows and *                
                                         * the number of colums of the screen */            
 initscr();                             /* start the curses mode */                         
 getmaxyx(stdscr,row,col);              /* get the number of rows and columns */            
 mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);                                            
                                        /* print the message at the center of the screen */ 
 mvprintw(row-2,0,"This screen has %d rows and %d columns\n",row,col);                      
 printw("Try resizing your window(if possible) and then run this program again");           
 refresh();                                                                                 
 getch();                                                                                   
 endwin();                                                                                  
                                                                                            
 return 0;                                                                                  
}                                                                                           



위 프로그램은 printw 함수를 사용하는 것이 얼마나 쉬운지 보여주고 있다. 단지 위치와 화면에 나타날 메세지를 넘겨주기만 하면 된다.

위 프로그램에는 ncurses.h 에 정의된 매크로 함수 getmaxyx() 가 사용되었다. 이 함수는 주어진 윈도우의 행과 열의 최대값을 구해서 리턴한다. getmaxyx() 함수는 주어진 변수의 값을 갱신하게 되는데, 이유는 우리가 그 함수에 변수에대한 포인터를 넘겨준 것이 아니라 단지 변수를 넘겨줬기 때문이다.


1.6.4 addstr() 류의 함수들


addstr() 함수는 주어진 윈도우에 문자열을 찍을 때 사용한다. 이것은 주어진 문자열의 각 문자에 대해 addch() 를 매번 호출하는 것과 유사한데, 이는 모든 출력 함수들에 대해서도 마찬가지이다. 여기에도 curses 의 작명법을 따르는 mvaddstr(), mvwaddstr(), waddstr() 등의 유사함수들이 있다.(e.g. mvaddstr() 함수는 move() 를 호출하고나서 addstr() 을 호출하는 것과 같다.) 이외에도 정수 인자(n 이라하자) 를 추가로 받는 addnstr() 이라는 함수가 있는데, 이 함수는 최대 n 개의 문자를 화면에 출력한다. 만약 n 이 음수라면, 문자열을 모두 찍게된다.


1.6.5 주의 사항


이 함수들은 전부 y 위치 값을 먼저 인자로 받고 그 뒤에 x 를 받는다. 초보자가 흔히 하는 실수중 하나가 x,y 순서로 값을 전달하는 것이다. 만약 당신이 (y,x) 좌표로 많은 작업을 해야 한다면, 스크린을 윈도우로 나눠서 각각을 따로 처리하는 방법을 생각해 보도록 하여라. 윈도우즈는 windows 섹션에서 다루고 있다.


1.7 scanw() 같은 입력함수에 대해


입력 없이 출력만 하는 것은 지루하지 않은가? 이제 사용자로부터 입력을 받는 함수들을 알아보도록 하자. 이 함수들도 역시 세개의 큰 분류로 나눌 수 있다.

  • getch() class: Get a character

  • scanw() class: Get formatted input

  • getstr() class: Get strings



1.7.1 getch() 류의 함수들


이 함수들은 터미널로 부터 문자 하나를 입력받는다. 그러나 여기에는 반드시 짚고 넘어가야할 몇몇 문제가 있다. 예를 들어 만약 cbreak() 함수를 사용하지 않는다면, curses 는 당신의 입력문자들을 연속으로 읽지 않고 new line 이나 EOF 문자가 들어왔을 때만 입력을 읽기 시작할 것이다. 이것을 막기 위해서는 cbreak() 함수를 반드시 사용해서 프로그램에서 문자들을 즉각 활용하게 해야한다. 또 다른 널리 사용되는 함수는 noecho() 이다. 이름에서 알 수 있듯이, 이 함수가 설정되면 사용자들이 누른 키의 문자가 화면에 나타나지 않는다. cbreak() 와 noecho() 두 함수는 키 관리의 전형적인 예이다. 이런 류의 함수들은 key management 섹션에서 설명하고 있다.


1.7.2 scanw() 류의 함수들

이 함수들은 스크린의 아무 위치에서나 입력을 받을 수 있는 기능이 더해진 scanf() 함수와 유사하다.


1.7.2.1 scanw() 와 mvscanw


이 함수들의 사용법은 sscanf() 함수와 비슷한데, 입력받을 줄이 wgetstr() 함수에 의해 제공된다. 즉 이 함수들은 wgetstr() 함수 (아래에서 설명함) 을 호출하고 그 결과로 받은 줄을 스캔에 사용한다.


1.7.2.2 wscanw() 와 mvwscanw()


이 함수들은 입력을 인자로 받은 윈도우에서 읽는다는 것 말고는 위의 두 함수와 유사하다.


1.7.2.3 vwscanw()


이 함수는 vscanf() 와 비슷하다. 이것은 입력받을 인자의 종류가 다양할 경우 사용된다.


1.7.3 getstr() 류의 함수들


이 함수들은 터미널로 부터 문자열을 입력받을 때 사용된다. 본질적으론 이 함수는 getch() 함수를 newline, 캐리지리턴, EOF 를 받을때 까지 계속 호출하는 것과 같은 일을 한다. 결과 문자열은 사용자가 제공한 문자열 포인터인 str 변수에 저장된다.


1.7.4 몇몇 예제


Example 4. A Simple scanw example

#include <ncurses.h>                    /* ncurses.h includes stdio.h */                   
#include <string.h>                                                                        
                                                                                           
int main()                                                                                 
{                                                                                          
 char mesg[]="Enter a string: ";                /* message to be appeared on the screen */ 
 char str[80];                                                                             
 int row,col;                           /* to store the number of rows and *               
                                         * the number of colums of the screen */           
 initscr();                             /* start the curses mode */                        
 getmaxyx(stdscr,row,col);              /* get the number of rows and columns */           
 mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);                                           
                                /* print the message at the center of the screen */        
 getstr(str);                                                                              
 mvprintw(LINES - 2, 0, "You Entered: %s", str);                                           
 getch();                                                                                  
 endwin();                                                                                 
                                                                                           
 return 0;                                                                                 
}                                                                                          




1.8 속성


우리는 문자를 출력할때 특별한 효과를 주기위해서 속성을 어떻게 사용하는지 이미 알아본 적이 있다. 속성은 잘 사용하기만 하면 정보를 쉽고 이해하기 좋게 표현할 수가 있다. 다음 프로그램은 C 파일을 입력으로 읽어 주석들을 굵게 표현한다. 코드를 살펴보도록 하자.

Example 5. A Simple Attributes example

#include <ncurses.h>                                                         
                                                                             
int main(int argc, char *argv[])                                             
{                                                                            
    int ch, prev;                                                            
    FILE *fp;                                                                
    int goto_prev = FALSE, y, x;                                             
                                                                             
    if(argc != 2)                                                            
    {   printf("Usage: %s <a c file name>\n", argv[0]);                      
        exit(1);                                                             
    }                                                                        
    fp = fopen(argv[1], "r");                                                
    if(fp == NULL)                                                           
    {   perror("Cannot open input file");                                    
        exit(1);                                                             
    }                                                                        
                                                                             
    initscr();                      /* Start curses mode            */       
                                                                             
    prev = EOF;                                                              
    while((ch = fgetc(fp)) != EOF)                                           
    {   if(prev == '/' && ch == '*')    /* If it is / and * then olny        
                                         * switch bold on */                 
        {   attron(A_BOLD);                                                  
            goto_prev = TRUE;       /* Go to previous char / and             
                                     * print it in BOLD */                   
        }                                                                    
        if(goto_prev == TRUE)                                                
        {   getyx(stdscr, y, x);                                             
            move(y, x - 1);                                                  
            printw("%c%c", '/', ch); /* The actual printing is done          
                                      * here */                              
            ch = 'a';                /* 'a' is just a dummy                  
                                      * character to prevent */              
                                     // "/*/" comments.                      
            goto_prev = FALSE;      /* Set it to FALSE or every              
                                     * thing from here will be / */          
        } else                                                               
            printw("%c", ch);                                                
        refresh();                                                           
        if(prev == '*' && ch == '/')                                         
                attroff(A_BOLD);        /* Switch it off once we got *       
                                           and then / */                     
        prev = ch;                                                           
    }                                                                        
    getch();                                                                 
    endwin();                       /* End curses mode                */     
    return 0;                                                                
}                                                                            


저 초기화들과 다른 쓸데 없는 것들은 신경쓰지 말고, while 루프 에만 집중해라. 그것은 파일에서 문자들을 읽어서 /* 와 같은 패턴을 찾는다. 한번 패턴을 찾게되면 attron() 함수를 써서 BOLD 속성을 켠다. 그 후 */ 패턴을 찾으면 attroff() 로 속성을 다시 끄게 된다.

또한 위 프로그램은 getyx() 와 move() 라는 유용한 함수들도 소개해주고 있다. 첫번째 함수는 현재 커서의 위치에 해당하는 값을 변수 y, x 에 저장한다. getyx() 함수는 매크로 함수이기 때문에 변수에 대한 포인터를 전달할 필요가 없다. move() 함수는 커서를 주어진 위치로 옮기는 함수이다.

위 프로그램은 그다지 많은 일을 하지 않는 정말 간단한 것이다. 이 코드 위에 C 파일을 읽고 그것을 파싱해서 서로 다른 색으로 출력하는 보다 더 유용한 기능을 추가해 볼 수도 있을 것이다. 또한 저것을 다른 언어들에 대해서도 확장해 볼 수 있을 것이다.


1.8.1 세부 사항


속성에 대해 좀 더 자세히 알아보도록 하자. attron(), attroff(), attrset() 및 그외 attr_get() 등의 함수들은 속성을 끄고 켜거나, 속성을 가져오고 컬러풀한 화면을 만들어내는데 쓰인다.

attron 과 attroff 함수는 속성들에 대한 bit-mask 를 가지고 그것들을 껐다 켰다 한다. <curses.h> 에 정의되어 있는 다음의 비디오 속성들이 이 함수에 쓰일 수 있다.

#!
    A_NORMAL        Normal display (no highlight)                            
    A_STANDOUT      Best highlighting mode of the terminal.                  
    A_UNDERLINE     Underlining                                              
    A_REVERSE       Reverse video                                            
    A_BLINK         Blinking                                                 
    A_DIM           Half bright                                              
    A_BOLD          Extra bright or bold                                     
    A_PROTECT       Protected mode                                           
    A_INVIS         Invisible or blank mode                                  
    A_ALTCHARSET    Alternate character set                                  
    A_CHARTEXT      Bit-mask to extract a character                          
    COLOR_PAIR(n)   Color-pair number n                                      
                                                                             


마지막 것이 가장 화려한 것중 하나다. 색상들(#color)은 다음 섹션에서 설명한다.

여러 효과를 겹쳐낼려면 위의 아무 값들을 OR(|) 연산을 하면 된다. 만약 반전되고 깜빡이는 문자들을 원한다면 다음처럼 해라.

    attron(A_REVERSE | A_BLINK);                                             




1.8.2 attron() 대 attrset()


그럼 대체 attron() 함수와 attrset() 함수의 차이는 무엇일까? attron 이 그것에 주어진 속성만을 켤 수 있는데 반해 attrset 은 윈도우의 속성들을 설정할 수 있다. 즉 attset() 는 윈도우가 현재 어떤 속성을 가지고 있는지 상관없이 새 속성들을 설정할 수 있다. attroff() 또한 그것에 인자로 주어진 속성만을 끌 수 있다. 이런 기능은 속성을 쉽게 다룰 수 있는 유용함을 제공하는 반면, 만약 당신이 주의깊게 그것을 사용하지 않는다면 윈도우가 어떤 속성을 가지고 있는지 놓칠 수도 있고 화면표시를 모두 망칠 수도 있을 것이다. 이것 특히 메뉴를 다룰때에 잘 일어나는 문제다. 그러니 미리 속성에 대한 정책을 세워놓고 그것을 고수하도록 하라. 아 또한 모든 속성을 끄고 일반 모드로 돌려놓는 attrset(A_NORMAL) 함수와 동일한 기능의 standend() 함수를 언제든지 호출할 수도 있다.


1.8.3 attr_get()


attr_get() 함수는 윈도우의 현재 속성과 색상들 값을 얻어온다. 비록 위 함수들만큼 자주 쓰지는 않겠지만, 이것은 스크린의 영역들을 스캔하는데 유용하다. 예를들어 스크린에 복잡한 변경을 가해야 되는데, 각각의 문자들이 어떤속성을 가지고 있는지 확실하지 않다고 하자. 이 경우에 attrset 또는 attron 등의 원하는 효과를 내는 함수들과 함께 이 함수를 써서 해결할 수 있다.


1.8.4 attr_ 함수들


attr_set(), attr_on 과 같은 류의 함수들이 더 있다. 이것들은 attr_t 타입의 인자를 받는다는 걸 제외하면 위의 함수들과 비슷하다.


1.8.5 wattr 함수들


위에서 얘기한 각각의 함수에 대해서 특정 윈도우에서 작동하는 이름에 'w' 가 붙은 함수들이 있다. 위의 함수들은 stdscr 에서 작동하는 것들이다.


1.8.6 chgat() 함수들


chgat() 함수는 curs_attr 의 man 페이지의 맨 끝에 나열되어 있다. 그러나 사실 이건 유용한 함수중 하나다. 이 함수는 움직이지 않고 문자들의 그룹에 속성을 설정할 수 있다. 커서 말이다. 그것을 움직이지 않고 가능하다. 이 함수는 현재 커서 위치에서 시작하는 문자들에서 주어진 수만큼의 속성을 변경한다.

줄의 끝까지 변경할려면 문자의 갯수에 -1 을 주면된다. 만약 현재 위치에서 줄의 끝까지 문자들의 속성들 바꾸고자 한다면 다음 처럼 해라.

    chgat(-1, A_REVERSE, 0, NULL);                                           


이 함수는 이미 화면에 나타나 있는 글자들의 속성을 바꾸는데 유용하다. 바꾸고자 하는 것의 시작위치로 커서를 옮긴다음 속성을 바꿔라.

다른 wchgat(), mvchgat(), wchgat() 이런 함수들도 w 로 시작하는 함수들이 특정 윈도우에서 작동한다는 것을 제외하면 위와 비슷하게 돌아간다. mv 로 시작하는 함수는 먼저 커서를 옮기고 주어진 일을 한다. 사실 chgat 함수는 wchgat() 함수에 stdscr 을 윈도우로 넘겨주는 매크로 함수 이다. 대부분의 w 로 시작하지 않는 함수들은 매크로 함수들이다.

Example 6. Chgat() Usage example

#include <ncurses.h>                                                          
                                                                              
int main(int argc, char *argv[])                                              
{       initscr();                      /* Start curses mode            */    
        start_color();                  /* Start color functionality    */    
                                                                              
        init_pair(1, COLOR_CYAN, COLOR_BLACK);                                
        printw("A Big string which i didn't care to type fully ");            
        mvchgat(0, 0, -1, A_BLINK, 1, NULL);                                  
        /*                                                                    
         * First two parameters specify the position at which to start        
         * Third parameter number of characters to update. -1 means till      
         * end of line                                                        
         * Forth parameter is the normal attribute you wanted to give         
         * to the charcter                                                    
         * Fifth is the color index. It is the index given during init_pair() 
         * use 0 if you didn't want color                                     
         * Sixth one is always NULL                                           
         */                                                                   
        refresh();                                                            
        getch();                                                              
        endwin();                       /* End curses mode                */  
        return 0;                                                             
}                                                                             


이 예제는 curses 의 색상들에 대해 보여주고 있다. 색상에 관해서는 후에 자세히 다룬다. 색을 사용하지 않으려면 0 값을 넣으면 된다.


1.9 윈도우 함수의 모든 것


윈도우는 curses 에서 가장 중요한 개념이다. 이미 당신은 모든 함수들이 은연중에 작동하는 stdscr 이라는 표준 윈도우에 대해 알고 있을 것이다. 자 이제 가장 간단한 GUI 를 만든다고 해도 당신은 윈도우를 필요로 할 것이다. 윈도우를 써야하는 주된 이유중 하나는 스크린의 일부만 따로 처리함으로서 바뀌어야할 필요가 있는 윈도우들만 변경하게되어 효율성을 높일 수 있다는 것이다. 또 보다 더 나은 디자인이 필요할 때도 마찬가지 이다. 난 윈도우를 사용하는 제일 중요한 이유는 후자라고 말하고 싶다. 당신은 당신의 프로그램에서 항상 더 낫고 더 다루기 쉬운 디자인을 갈구할 것이다. 만약 당신이 크고 복잡한 GUI 를 만든다면 이것은 다른 무엇보다 선행하는 핵심적인 중요 요소이다.


1.9.1 개괄


윈도우는 newwin() 함수를 호출해서 만들 수가 있다. 이것은 사실상 화면에는 아무것도 생성하지 않는다. 이 함수는 윈도우를 처리하기 위한 구조체의 메모리를 할당받고 그 윈도우에 해당하는 크기, 시작x위치, 시작y위치 등의 정보를 갱신한다. 이렇기 때문에 curses 에서 윈도우는 단지 화면의 다른 부분과 독립적으로 처리할 수 있는 가상의 윈도우의 abstraction 이다. newwin() 함수는 WINDOW 에 대한 포인터를 리턴한다. 이 포인터는 wprintw() 등과 같이 윈도우와 관련된 함수에 인자로 넘겨질 수 있다. 최종적으로 윈도우는 delwin() 함수에 의해 파괴될 수 있다. 이 함수는 윈도우 구조체에 할당되어 있는 메모리 영역을 해제할 것이다.


1.9.2 윈도우가 있으라 !!!


윈도우가 생성되었고 우리가 그것을 볼 수가 없다면 웃기지 않을까. 이제 재밌는 윈도우 나타내기가 시작된다. box() 함수는 윈도우의 테두리를 그리는데 쓸 수 있다. 예제를 통해 이 함수들을 보다 자세하게 둘러보도록 하자.

Example 7. Window Border example

#include <ncurses.h>                                                                    
                                                                                        
                                                                                        
WINDOW *create_newwin(int height, int width, int starty, int startx);                   
void destroy_win(WINDOW *local_win);                                                    
                                                                                        
int main(int argc, char *argv[])                                                        
{       WINDOW *my_win;                                                                 
        int startx, starty, width, height;                                              
        int ch;                                                                         
                                                                                        
        initscr();                      /* Start curses mode            */              
        cbreak();                       /* Line buffering disabled, Pass on             
                                         * everty thing to me           */              
        keypad(stdscr, TRUE);           /* I need that nifty F1         */              
                                                                                        
        height = 3;                                                                     
        width = 10;                                                                     
        starty = (LINES - height) / 2;  /* Calculating for a center placement */        
        startx = (COLS - width) / 2;    /* of the window                */              
        printw("Press F1 to exit");                                                     
        refresh();                                                                      
        my_win = create_newwin(height, width, starty, startx);                          
                                                                                        
        while((ch = getch()) != KEY_F(1))                                               
        {       switch(ch)                                                              
                {       case KEY_LEFT:                                                  
                                destroy_win(my_win);                                    
                                my_win = create_newwin(height, width, starty,--startx); 
                                break;                                                  
                        case KEY_RIGHT:                                                 
                                destroy_win(my_win);                                    
                                my_win = create_newwin(height, width, starty,++startx); 
                                break;                                                  
                        case KEY_UP:                                                    
                                destroy_win(my_win);                                    
                                my_win = create_newwin(height, width, --starty,startx); 
                                break;                                                  
                        case KEY_DOWN:                                                  
                                destroy_win(my_win);                                    
                                my_win = create_newwin(height, width, ++starty,startx); 
                                break;                                                  
                }                                                                       
        }                                                                               
                                                                                        
        endwin();                       /* End curses mode                */            
        return 0;                                                                       
}                                                                                       
                                                                                        
WINDOW *create_newwin(int height, int width, int starty, int startx)                    
{       WINDOW *local_win;                                                              
                                                                                        
        local_win = newwin(height, width, starty, startx);                              
        box(local_win, 0 , 0);          /* 0, 0 gives default characters                
                                         * for the vertical and horizontal              
                                         * lines                        */              
        wrefresh(local_win);            /* Show that box                */              
                                                                                        
        return local_win;                                                               
}                                                                                       
                                                                                        
void destroy_win(WINDOW *local_win)                                                     
{                                                                                       
        /* box(local_win, ' ', ' '); : This won't produce the desired                   
         * result of erasing the window. It will leave it's four corners                
         * and so an ugly remnant of window.                                            
         */                                                                             
        wborder(local_win, ' ', ' ', ' ',' ',' ',' ',' ',' ');                          
        /* The parameters taken are                                                     
         * 1. win: the window on which to operate                                       
         * 2. ls: character to be used for the left side of the window                  
         * 3. rs: character to be used for the right side of the window                 
         * 4. ts: character to be used for the top side of the window                   
         * 5. bs: character to be used for the bottom side of the window                
         * 6. tl: character to be used for the top left corner of the window            
         * 7. tr: character to be used for the top right corner of the window           
         * 8. bl: character to be used for the bottom left corner of the window         
         * 9. br: character to be used for the bottom right corner of the window        
         */                                                                             
        wrefresh(local_win);                                                            
        delwin(local_win);                                                              
}                                                                                       




1.9.3 설명


괴성을 지르진마라. 나도 이게 양이 많은 예제란걸 알고 있다. 그러나 여기서 몇몇 중요한 것들을 설명해야만 한다 :-). 이 프로그램은 상하좌우의 방향키로 이동할 수 있는 직사각형의 윈도우를 생성한다. 그리고 사용자가 키를 누를때마다 반복적으로 윈도우를 없애고 다시 만든다. 화면의 한계 넘어까진 가지마라. 그 한계들을 체크하는 것은 독자들을 위한 연습으로 남겨두겠다. 이제 저것을 한줄한줄 분석해 보자.

create_newwin() 함수는 newwin() 을 통해 윈도우를 생성하고 box 함수로 그 테두리를 보여준다. destroy_win() 함수는 먼저 윈도우의 테두리를 ' ' 문자로 그림으로서 윈도우를 지우고 그 후 delwin() 함수를 호출해서 관련된 메모리를 해제한다. 사용자가 누르는 키에 따라 starty 또는 startx 값이 바뀌고 새 윈도우가 만들어진다.

보다시피 destroy_win 에서 box 함수 대신에 wborder 을 사용하였다. 이유는 주석에 쓰여있다. (읽지 않았을 것이다. 안다. 코드를 읽어보라 :-)). wborder 함수는 아래와 같이 호출할 경우 4개의 코너와 4개의 줄에 대해 주어진 문자로 윈도우를 그린다:

    wborder(win, '|', '|', '-', '-', '+', '+', '+', '+');                    


이렇게 생긴 창이 생성된다.

#!
     +-----------------------+                                                
    |            |                                                           
    |            |                                                           
    |            |                                                           
    |            |                                                           
    |            |                                                           
    |            |                                                           
    +-----------------------+                                                




1.9.4 예제의 다른 부분


또한 위의 예제에서 볼 수 있듯이, 난 COLS, LINES 라는 변수를 사용했다. 이것들은 initscr() 이후에 화면의 크기에 맞게 값이 초기화된다. 이 변수들은 화면의 넓이나 위 예제처럼 화면의 정중앙의 위치를 찾는데 유용하다. getch() 함수는 흔히 쓰듯 키보드로부터 입력을 받는다. 그리고 입력된 키에따라 해당하는 일을 하게 된다. 이런 류의 switch-case 문은 GUI 기반 프로그램에서 매운 일반적인 형태이다.


1.9.5 다른 테두리 함수들


위 프로그램은 키가 눌려질때마다 윈도우가 없어지고 다른것이 생기기 때문에 굉장히 비효율적이다. 이제 다른 테두리관련 함수를 써서 프로그램을 좀 더 효율적으로 만들어보자.

다음의 프로그램은 mvhline() 와 mvvline() 함수를 써서 같은 효과를 내었다. 이 두 함수는 단순하다. 이것들은 수평 또는 수직의 선을 특정 위치에서 특정 길이만큼 생성한다.

Example 8. More border functions

#include <ncurses.h>                                                         
                                                                             
typedef struct _win_border_struct {                                          
        chtype  ls, rs, ts, bs,                                              
                tl, tr, bl, br;                                              
}WIN_BORDER;                                                                 
                                                                             
typedef struct _WIN_struct {                                                 
                                                                             
        int startx, starty;                                                  
        int height, width;                                                   
        WIN_BORDER border;                                                   
}WIN;                                                                        
                                                                             
void init_win_params(WIN *p_win);                                            
void print_win_params(WIN *p_win);                                           
void create_box(WIN *win, int bool);                                         
                                                                             
int main(int argc, char *argv[])                                             
{       WIN win;                                                             
        int ch;                                                              
                                                                             
        initscr();                      /* Start curses mode            */   
        start_color();                  /* Start the color functionality */  
        cbreak();                       /* Line buffering disabled, Pass on  
                                         * everty thing to me           */   
        keypad(stdscr, TRUE);           /* I need that nifty F1         */   
        noecho();                                                            
        init_pair(1, COLOR_CYAN, COLOR_BLACK);                               
                                                                             
        /* Initialize the window parameters */                               
        init_win_params(&win);                                               
        print_win_params(&win);                                              
                                                                             
        attron(COLOR_PAIR(1));                                               
        printw("Press F1 to exit");                                          
        refresh();                                                           
        attroff(COLOR_PAIR(1));                                              
                                                                             
        create_box(&win, TRUE);                                              
        while((ch = getch()) != KEY_F(1))                                    
        {       switch(ch)                                                   
                {       case KEY_LEFT:                                       
                                create_box(&win, FALSE);                     
                                --win.startx;                                
                                create_box(&win, TRUE);                      
                                break;                                       
                        case KEY_RIGHT:                                      
                                create_box(&win, FALSE);                     
                                ++win.startx;                                
                                create_box(&win, TRUE);                      
                                break;                                       
                        case KEY_UP:                                         
                                create_box(&win, FALSE);                     
                                --win.starty;                                
                                create_box(&win, TRUE);                      
                                break;                                       
                        case KEY_DOWN:                                       
                                create_box(&win, FALSE);                     
                                ++win.starty;                                
                                create_box(&win, TRUE);                      
                                break;                                       
                }                                                            
        }                                                                    
        endwin();                       /* End curses mode                */ 
        return 0;                                                            
}                                                                            
void init_win_params(WIN *p_win)                                             
{                                                                            
        p_win->height = 3;                                                   
        p_win->width = 10;                                                   
        p_win->starty = (LINES - p_win->height)/2;                           
        p_win->startx = (COLS - p_win->width)/2;                             
                                                                             
        p_win->border.ls = '|';                                              
        p_win->border.rs = '|';                                              
        p_win->border.ts = '-';                                              
        p_win->border.bs = '-';                                              
        p_win->border.tl = '+';                                              
        p_win->border.tr = '+';                                              
        p_win->border.bl = '+';                                              
        p_win->border.br = '+';                                              
                                                                             
}                                                                            
void print_win_params(WIN *p_win)                                            
{                                                                            
#ifdef _DEBUG                                                                
        mvprintw(25, 0, "%d %d %d %d", p_win->startx, p_win->starty,         
                                p_win->width, p_win->height);                
        refresh();                                                           
#endif                                                                       
}                                                                            
void create_box(WIN *p_win, int bool)                                        
{       int i, j;                                                            
        int x, y, w, h;                                                      
                                                                             
        x = p_win->startx;                                                   
        y = p_win->starty;                                                   
        w = p_win->width;                                                    
        h = p_win->height;                                                   
                                                                             
        if(bool == TRUE)                                                     
        {       mvaddch(y, x, p_win->border.tl);                             
                mvaddch(y, x + w, p_win->border.tr);                         
                mvaddch(y + h, x, p_win->border.bl);                         
                mvaddch(y + h, x + w, p_win->border.br);                     
                mvhline(y, x + 1, p_win->border.ts, w - 1);                  
                mvhline(y + h, x + 1, p_win->border.bs, w - 1);              
                mvvline(y + 1, x, p_win->border.ls, h - 1);                  
                mvvline(y + 1, x + w, p_win->border.rs, h - 1);              
                                                                             
        }                                                                    
        else                                                                 
                for(j = y; j <= y + h; ++j)                                  
                        for(i = x; i <= x + w; ++i)                          
                                mvaddch(j, i, ' ');                          
                                                                             
        refresh();                                                           
                                                                             
}                                                                            




1.10 색상에 대해서


1.10.1 개괄


색이 없다면 삶은 참 우울할 것이다. curses 는 색을 다루는 멋진 메커니즘을 가지고 있다. 자 그럼 짧은 프로그램으로 천천히 한번 시작해보자.

Example 9. A Simple Color example

#include <ncurses.h>                                                                
                                                                                    
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string); 
int main(int argc, char *argv[])                                                    
{       initscr();                      /* Start curses mode            */          
        if(has_colors() == FALSE)                                                   
        {       endwin();                                                           
                printf("You terminal does not support color\n");                    
                exit(1);                                                            
        }                                                                           
        start_color();                  /* Start color                  */          
        init_pair(1, COLOR_RED, COLOR_BLACK);                                       
                                                                                    
        attron(COLOR_PAIR(1));                                                      
        print_in_middle(stdscr, LINES / 2, 0, 0, "Viola !!! In color ...");         
        attroff(COLOR_PAIR(1));                                                     
        getch();                                                                    
        endwin();                                                                   
}                                                                                   
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string)  
{       int length, x, y;                                                           
        float temp;                                                                 
                                                                                    
        if(win == NULL)                                                             
                win = stdscr;                                                       
        getyx(win, y, x);                                                           
        if(startx != 0)                                                             
                x = startx;                                                         
        if(starty != 0)                                                             
                y = starty;                                                         
        if(width == 0)                                                              
                width = 80;                                                         
                                                                                    
        length = strlen(string);                                                    
        temp = (width - length)/ 2;                                                 
        x = startx + (int)temp;                                                     
        mvwprintw(win, y, x, "%s", string);                                         
        refresh();                                                                  
}                                                                                   


보다시피, 색을 사용할려면 먼저 start_color() 함수를 호출해야 한다. 그 후 다양한 함수들을 통해 당신의 터미널의 색상기능들을 사용할 수 있다. 터미널이 색상 기능이 있는지 아닌지 알아볼려면, has_colors() 함수를 써라. 만약 터미널이 색을 지원하지 않는다면 FALSE 가 리턴될 것이다.

curses 는 start_color() 이 호출될 때 터미널에서 지원되는 모든 색들을 초기화해서 준비한다. 이 색들은 미리 정의된 COLOR_BLACK 등등의 상수를 통해 쓸 수 있다. 자 이제 실제로 색을 쓸려면, 색상짝을 정의해야 한다. 색들은 항상 색상짝의 꼴로 쓰여진다. 즉, init_pair() 함수를 통해 전경 및 후경색을 정의해야만 이 색상짝 값을 쓸 수 있단 말이다. 이렇게 한다음에야 색상짝의 값은 COLOR_PAIR() 함수와 함께 일반 속성 처럼 쓰여질 수 있다. 이는 처음엔 좀 성가셔 보이는 일들이나, 이 방법은 우리가 색상짝들을 매우 쉽게 쓸 수 있는 우아한 기법이다. 쉘 스크립트에서 다이얼로그 상자를 보여주는 유틸리티인 "dialog" 의 소스 코드를 보게 되면 이를 알 수 있다. 이것의 개발자는 필요한 모든 전경 및 후경 색상짝에 대해서 정의를 해놓고, 프로그램 시작시에 이를 초기화하고 있다. 이 방법은 우리가 미리 상수처럼 정의해놓은 색상짝을 씀으로써 색상 속성을 설정하는 것을 매우 쉽게 해준다.

다음의 색들은 curses.h 에 정의되어 있다. 당신은 이것들을 다양한 색관련 함수들에 쓸 수 있다.

#!
         COLOR_BLACK   0                                                      
        COLOR_RED     1                                                      
        COLOR_GREEN   2                                                      
        COLOR_YELLOW  3                                                      
        COLOR_BLUE    4                                                      
        COLOR_MAGENTA 5                                                      
        COLOR_CYAN    6                                                      
        COLOR_WHITE   7                                                      




1.10.2 색상 정의 바꾸기


init_color() 함수는 curses 에 의해 정의된 색상의 rgb 값을 변경할 수 있다. 예를 들어 당신이 빨간색의 강도를 아주 약간 줄이고 싶다고 하자. 그렇다면 이 함수를 다음과 같이 호출한다.

    init_color(COLOR_RED, 700, 0, 0);                                        
    /* param 1     : color name                                              
     * param 2, 3, 4 : rgb content min = 0, max = 1000 */                    


만약 당신의 터미널이 색상 정의를 변경하지 못한다면, 이 함수는 ERR 을 리턴한다. can_change_color() 함수는 터미널이 색상을 바꿀 수 있는 기능이 있는지 없는지 확인하는 함수이다. rgb 값의 범위는 0 부터 1000 까지이다. 처음에 RED 색이 1000(r), 0(g), 0(b) 로 정의된다.


1.10.3 색상 값


color_content() 와 pair_content() 함수는 색상짝의 전경 및 후경색들을 찾는데 사용한다.


1.11 키 입력 다루기. 펑션키, 방향키 등을 어떻게 입력받는가.


1.11.1 개괄


사용자와의 교류하기 위한 강력한 유저인터페이스 없이는 어떤 GUI 프로그램도 완성되지 않는다. curses 프로그램은 사용자가 하는 키눌림이나 마우스 움직임에 민감해야만 한다. 먼저 키에 대해서 다뤄보자.

여태 나온 대부분의 예제에서 보듯이, 사용자로부터 키 입력을 받는 것은 매우 쉽다. 키입력을 얻는 단순한 방법은 getch() 함수를 쓰는 것이다. 만약 당신이 완벽한 한줄의 입력 (주로 캐리지 리턴으로 끝난다) 보다 개별 문자를 입력받고자 한다면 cbreak 모드가 켜져 있어야 한다. 또한 펑션키나 방향키 등을 사용할려면 keypad 가 켜져있어야 한다. 자세한 것은 initialization 섹션을 참고하기 바란다.

getch() 함수는 눌려진 키에 해당하는 정수를 리턴한다. 만약 보통 문자라면 그 문자의 정수 값이 리턴된다. 그렇지 않은 경우에는 curses.h 에 정의되어 있는 상수값을 리턴한다. 예를 들어 사용자가 F1 키를 누르면, 265 라는 정수가 리턴된다. 이것은 curses.h 에 정의되어 있는 KEY_F() 라는 매크로 함수를 써서 알아온다. 이렇게 함으로써 입력받은 키들이 이식가능해지면 다루기 쉬워진다.

예를들어, getch() 함수를 다음과 같이 호출하면

    int ch;                                                                  
                                                                             
    ch = getch();                                                            


getch() 함수는 사용자가 키를 누르기를 계속 기다리게된다.(그렇게 되지 않을려면 타임아웃을 명시해야한다.) 그러다 사용자가 키를 누르게 되면, 거기에 맞는 정수 값이 리턴된다. 또한 당신은 원하는 키가 눌려졌는지 curses.h 에 정의된 상수를 통해 확인해 볼 수 있다.

다음의 일부 코드가 그 작업을 하는 것이다.

    if(ch == KEY_LEFT)                                                       
        printw("Left arrow is pressed\n");                                   


자 이제 위아래 방향키로 이동할 수 있는 메뉴가 있는 작은 프로그램을 만들어보자.


1.11.2 간단한 키 사용 예제


Example 10. A Simple Key Usage example

#include <stdio.h>                                                                                                      
#include <ncurses.h>                                                                                                    
                                                                                                                        
#define WIDTH 30                                                                                                        
#define HEIGHT 10                                                                                                       
                                                                                                                        
int startx = 0;                                                                                                         
int starty = 0;                                                                                                         
                                                                                                                        
char *choices[] = {                                                                                                     
                        "Choice 1",                                                                                     
                        "Choice 2",                                                                                     
                        "Choice 3",                                                                                     
                        "Choice 4",                                                                                     
                        "Exit",                                                                                         
                  };                                                                                                    
int n_choices = sizeof(choices) / sizeof(char *);                                                                       
void print_menu(WINDOW *menu_win, int highlight);                                                                       
                                                                                                                        
int main()                                                                                                              
{       WINDOW *menu_win;                                                                                               
        int highlight = 1;                                                                                              
        int choice = 0;                                                                                                 
        int c;                                                                                                          
                                                                                                                        
        initscr();                                                                                                      
        clear();                                                                                                        
        noecho();                                                                                                       
        cbreak();       /* Line buffering disabled. pass on everything */                                               
        startx = (80 - WIDTH) / 2;                                                                                      
        starty = (24 - HEIGHT) / 2;                                                                                     
                                                                                                                        
        menu_win = newwin(HEIGHT, WIDTH, starty, startx);                                                               
        keypad(menu_win, TRUE);                                                                                         
        mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice");                             
        refresh();                                                                                                      
        print_menu(menu_win, highlight);                                                                                
        while(1)                                                                                                        
        {       c = wgetch(menu_win);                                                                                   
                switch(c)                                                                                               
                {       case KEY_UP:                                                                                    
                                if(highlight == 1)                                                                      
                                        highlight = n_choices;                                                          
                                else                                                                                    
                                        --highlight;                                                                    
                                break;                                                                                  
                        case KEY_DOWN:                                                                                  
                                if(highlight == n_choices)                                                              
                                        highlight = 1;                                                                  
                                else                                                                                    
                                        ++highlight;                                                                    
                                break;                                                                                  
                        case 10:                                                                                        
                                choice = highlight;                                                                     
                                break;                                                                                  
                        default:                                                                                        
                                mvprintw(24, 0, "Charcter pressed is = %3d Hopefully it can be printed as '%c'", c, c); 
                                refresh();                                                                              
                                break;                                                                                  
                }                                                                                                       
                print_menu(menu_win, highlight);                                                                        
                if(choice != 0) /* User did a choice come out of the infinite loop */                                   
                        break;                                                                                          
        }                                                                                                               
        mvprintw(23, 0, "You chose choice %d with choice string %s\n", choice, choices[choice - 1]);                    
        clrtoeol();                                                                                                     
        refresh();                                                                                                      
        endwin();                                                                                                       
        return 0;                                                                                                       
}                                                                                                                       
                                                                                                                        
                                                                                                                        
void print_menu(WINDOW *menu_win, int highlight)                                                                        
{                                                                                                                       
        int x, y, i;                                                                                                    
                                                                                                                        
        x = 2;                                                                                                          
        y = 2;                                                                                                          
        box(menu_win, 0, 0);                                                                                            
        for(i = 0; i < n_choices; ++i)                                                                                  
        {       if(highlight == i + 1) /* High light the present choice */                                              
                {       wattron(menu_win, A_REVERSE);                                                                   
                        mvwprintw(menu_win, y, x, "%s", choices[i]);                                                    
                        wattroff(menu_win, A_REVERSE);                                                                  
                }                                                                                                       
                else                                                                                                    
                        mvwprintw(menu_win, y, x, "%s", choices[i]);                                                    
                ++y;                                                                                                    
        }                                                                                                               
        wrefresh(menu_win);                                                                                             
}                                                                                                                       




1.12 마우스 처리하기


자 이제 키를 어떤식으로 입력받는지는 알것이다. 마우스 입력에 대해서도 알아보자. 흔히 모든 UI 는 사용자로 하여금 마우스나 키보드 둘다 쓸 수 있게 배려하고 있다.


1.12.1 개괄


다른 것들을 하기에 앞서, mousemask() 함수를 써야만 마우스 입력을 받을 수 있다.

    mousemask(  mmask_t newmask,    /* The events you want to listen to */   
                mmask_t *oldmask)    /* The old events mask                */


이 함수의 첫번째 인자는 받아들이고자 하는 이벤트의 bit mask 이다. 기본적으로 모든 이벤트들이 꺼져있다. ALL_MOUSE_EVENTS 라는 bit mask 는 모든 이벤트를 얻어오는데 쓸 수 있다.

다음은 이벤트 mask 들이다:

#!
     Name            Description                                              
       --------------------------------------------------------------------- 
       BUTTON1_PRESSED          mouse button 1 down                          
       BUTTON1_RELEASED         mouse button 1 up                            
       BUTTON1_CLICKED          mouse button 1 clicked                       
       BUTTON1_DOUBLE_CLICKED   mouse button 1 double clicked                
       BUTTON1_TRIPLE_CLICKED   mouse button 1 triple clicked                
       BUTTON2_PRESSED          mouse button 2 down                          
       BUTTON2_RELEASED         mouse button 2 up                            
       BUTTON2_CLICKED          mouse button 2 clicked                       
       BUTTON2_DOUBLE_CLICKED   mouse button 2 double clicked                
       BUTTON2_TRIPLE_CLICKED   mouse button 2 triple clicked                
       BUTTON3_PRESSED          mouse button 3 down                          
       BUTTON3_RELEASED         mouse button 3 up                            
       BUTTON3_CLICKED          mouse button 3 clicked                       
       BUTTON3_DOUBLE_CLICKED   mouse button 3 double clicked                
       BUTTON3_TRIPLE_CLICKED   mouse button 3 triple clicked                
       BUTTON4_PRESSED          mouse button 4 down                          
       BUTTON4_RELEASED         mouse button 4 up                            
       BUTTON4_CLICKED          mouse button 4 clicked                       
       BUTTON4_DOUBLE_CLICKED   mouse button 4 double clicked                
       BUTTON4_TRIPLE_CLICKED   mouse button 4 triple clicked                
       BUTTON_SHIFT             shift was down during button state change    
       BUTTON_CTRL              control was down during button state change  
       BUTTON_ALT               alt was down during button state change      
       ALL_MOUSE_EVENTS         report all button state changes              
       REPORT_MOUSE_POSITION    report mouse movement                        




1.12.2 이벤트 받아오기


한번 마우스 이벤트쪽이 활성화되면, getch() 류의 함수들은 마우스 이벤트가 일어날때 마다 KEY_MOUSE 값을 리턴하게 된다. 그리고나서 getmouse() 함수등을 통해 마우스 이벤트를 받을 수 있게 된다.

코드를 써보자면 대략 다음과 같다:

    MEVENT event;                                                            
                                                                             
    ch = getch();                                                            
    if(ch == KEY_MOUSE)                                                      
        if(getmouse(&event) == OK)                                           
            .    /* Do some thing with the event */                          
            .                                                                
            .                                                                


getmouse() 함수는 주어진 포인터 안에 일어난 이벤트를 리턴한다. 그것의 구조체는 아래와 같다.

    typedef struct                                                           
    {                                                                        
        short id;         /* ID to distinguish multiple devices */           
        int x, y, z;      /* event coordinates */                            
        mmask_t bstate;   /* button state bits */                            
    }                                                                        


우리가 가장 흥미있는 주요 변수는 바로 bstate 이다. 이것은 마우스의 버튼 상태를 말해준다.

다음과 같은 코드일부를 보면 어떻게 작동하는지 알 수 있을 것이다.

    if(event.bstate & BUTTON1_PRESSED)                                       
        printw("Left Button Pressed");                                       




1.12.3 전부 실습해보자


마우스를 다루는 데는 해야할 것이 꽤 많다. 앞에서 했던 것과 같은 메뉴를 마우스를 다룰 수 있게 작성해보자. 프로그램을 간결히 하기 위해서 키를 다루는 것은 제외했다.

Example 11. Access the menu with mouse !!!

#include <ncurses.h>                                                                                                                   
                                                                                                                                       
#define WIDTH 30                                                                                                                       
#define HEIGHT 10                                                                                                                      
                                                                                                                                       
int startx = 0;                                                                                                                        
int starty = 0;                                                                                                                        
                                                                                                                                       
char *choices[] = {     "Choice 1",                                                                                                    
                        "Choice 2",                                                                                                    
                        "Choice 3",                                                                                                    
                        "Choice 4",                                                                                                    
                        "Exit",                                                                                                        
                  };                                                                                                                   
                                                                                                                                       
int n_choices = sizeof(choices) / sizeof(char *);                                                                                      
                                                                                                                                       
void print_menu(WINDOW *menu_win, int highlight);                                                                                      
void report_choice(int mouse_x, int mouse_y, int *p_choice);                                                                           
                                                                                                                                       
int main()                                                                                                                             
{       int c, choice = 0;                                                                                                             
        WINDOW *menu_win;                                                                                                              
        MEVENT event;                                                                                                                  
                                                                                                                                       
        /* Initialize curses */                                                                                                        
        initscr();                                                                                                                     
        clear();                                                                                                                       
        noecho();                                                                                                                      
        cbreak();       //Line buffering disabled. pass on everything                                                                  
                                                                                                                                       
        /* Try to put the window in the middle of screen */                                                                            
        startx = (80 - WIDTH) / 2;                                                                                                     
        starty = (24 - HEIGHT) / 2;                                                                                                    
                                                                                                                                       
        attron(A_REVERSE);                                                                                                             
        mvprintw(23, 1, "Click on Exit to quit (Works best in a virtual console)");                                                    
        refresh();                                                                                                                     
        attroff(A_REVERSE);                                                                                                            
                                                                                                                                       
        /* Print the menu for the first time */                                                                                        
        menu_win = newwin(HEIGHT, WIDTH, starty, startx);                                                                              
        print_menu(menu_win, 1);                                                                                                       
        /* Get all the mouse events */                                                                                                 
        mousemask(ALL_MOUSE_EVENTS, NULL);                                                                                             
                                                                                                                                       
        while(1)                                                                                                                       
        {       c = wgetch(menu_win);                                                                                                  
                switch(c)                                                                                                              
                {       case KEY_MOUSE:                                                                                                
                        if(getmouse(&event) == OK)                                                                                     
                        {       /* When the user clicks left mouse button */                                                           
                                if(event.bstate & BUTTON1_PRESSED)                                                                     
                                {       report_choice(event.x + 1, event.y + 1, &choice);                                              
                                        if(choice == -1) //Exit chosen                                                                 
                                                goto end;                                                                              
                                        mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]); 
                                        refresh();                                                                                     
                                }                                                                                                      
                        }                                                                                                              
                        print_menu(menu_win, choice);                                                                                  
                        break;                                                                                                         
                }                                                                                                                      
        }                                                                                                                              
end:                                                                                                                                   
        endwin();                                                                                                                      
        return 0;                                                                                                                      
}                                                                                                                                      
                                                                                                                                       
                                                                                                                                       
void print_menu(WINDOW *menu_win, int highlight)                                                                                       
{                                                                                                                                      
        int x, y, i;                                                                                                                   
                                                                                                                                       
        x = 2;                                                                                                                         
        y = 2;                                                                                                                         
        box(menu_win, 0, 0);                                                                                                           
        for(i = 0; i < n_choices; ++i)                                                                                                 
        {       if(highlight == i + 1)                                                                                                 
                {       wattron(menu_win, A_REVERSE);                                                                                  
                        mvwprintw(menu_win, y, x, "%s", choices[i]);                                                                   
                        wattroff(menu_win, A_REVERSE);                                                                                 
                }                                                                                                                      
                else                                                                                                                   
                        mvwprintw(menu_win, y, x, "%s", choices[i]);                                                                   
                ++y;                                                                                                                   
        }                                                                                                                              
        wrefresh(menu_win);                                                                                                            
}                                                                                                                                      
                                                                                                                                       
/* Report the choice according to mouse position */                                                                                    
void report_choice(int mouse_x, int mouse_y, int *p_choice)                                                                            
{       int i,j, choice;                                                                                                               
                                                                                                                                       
        i = startx + 2;                                                                                                                
        j = starty + 3;                                                                                                                
                                                                                                                                       
        for(choice = 0; choice < n_choices; ++choice)                                                                                  
                if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice]))                                    
                {       if(choice == n_choices - 1)                                                                                    
                                *p_choice = -1;                                                                                        
                        else                                                                                                           
                                *p_choice = choice + 1;                                                                                
                        break;                                                                                                         
                }                                                                                                                      
}                                                                                                                                      




1.12.4 그외 잡다한 함수들


mouse_trafo() 와 wmouse_trafo() 함수는 마우스의 위치 값을 화면의 위치값으로 바꾸는 함수이다. 자세한 것은 curs_mouse(3X) man 페이지를 참고하라.

마우스인터벌 함수는 클릭으로 인식될 수 있게 버튼을 누르고 떼는 것 사이의 최대간격을 ( 천분의 일초 단위) 로 조정 할 수 있다. 이 함수는 이전의 간격 값을 리턴한다. 기본 값은 5분의 1초이다.


1.13 화면 제어


이 장에선 화면을 효율적으로 다루고 예쁜 프로그램을 만들 수 있는 함수들에 대해서 살펴볼 것이다. 이는 특별히 게임등을 만들 때 중요하다.


1.13.1 getyx() 함수들


getyx() 함수는 현재 커서 위치를 알아올 수 있다. 인자로 받은 변수에 x 와 y 좌표의 값을 집어 넣을 것이다. getyx() 가 매크로 함수이기 때문에 변수의 주소를 넘겨줄 필요는 없다. 이 함수는 다음과 같이 쓴다.

    getyx(win, y, x);                                                        
    /* win: window pointer                                                   
     *   y, x: y, x co-ordinates will be put into this variables             
     */                                                                      


getparyx() 함수는 메인 윈도우에 대한 서브 윈도우의 상대적인 초기 위치를 알아온다. 이 함수는 서브 윈도우를 갱신하거나 할때 유용하다. 여러 메뉴가 있는 이쁜 프로그램을 디자인할때, 메뉴의 첫번째 옵션의 위치같은 것들을 저장하는 것은 까다로운 일이다. 여기에 대한 간단한 해결책은 메뉴를 서브 윈ㄷ우로 생성하고 후에 메뉴의 시작 위치를 getparyx() 함수등으로 얻어오는 방법이 있다.

getbegyx() 와 getmaxyx() 함수는 현재 윈도우의 시작 및 최대 좌표 값을 리턴한다. 이 함수들도 위와 마찬가지로 윈도우와 서브 윈도우를 효율적으로 다루는데 잘 쓰인다.


1.13.2 화면 덤프


게임을 작성하는 동안 때때로 화면의 상태를 저장해두고 그것을 나중에 그대로 복원하는 일이 필요할 때가 있다. scr_dump() 함수는 인자로 주어진 파일에 화면 정보를 덤프하는 함수이다. 그리고 나중에 scr_restore 함수로 그것을 복원할 수 있다. 이 두 간단한 함수는 장면이 바뀌며 빨리 움직이는 게임등에 효과적으로 사용될 수 있다.


1.13.3 윈도우 덤프


윈도우를 저장하고 복원하는데는 putwin() 과 getwin() 이라는 함수가 있다. putwin() 함수는 현재 윈도우의 상태를 파일로 저장하는데, 그것은 나중에 getwin() 함수로 복원 할 수 있다.

copywin() 함수는 윈도우를 완벽하게 다른 윈도우로 복사하는 함수이다. 인자로 원본 윈도우와 대상윈도우를 받으며 원본 윈도우에서 명시된 사각영역 만큼을 대상 윈도우로 복사한다. 이 함수의 마지막 인자는 대상 윈도우에 내용들을 덮어 쓸 건지 아니면 겹쳐 쓸 건지 결정하는 것이다. 만약 이 인지가 true 이면, 기존 것을 그대로 유지한채 복사가 된다.


1.14 그외 기능들


자 이제 당신은 좋은 curses 프로그램의 특징들을 충분히 알고 있을 것이다. with all bells and whistles. 이 외에도 다양한 경우에 유용하게 사용되는 약간 잡다한 함수들이 있다. 이것들을 조금 알아보도록 하자.


1.14.1 curs_set()


이 함수는 커서를 보이지 않게 한다. 이 함수에 대한 인자는 다음과 같다.

#!
     0 : invisible      or                                                    
    1 : normal    or                                                         
    2 : very visible.                                                        




1.14.2 일시적으로 curses 모드 나가기


때때로 당신은 잠시동안 cooked mode (보통의 라인 버퍼링 모드) 로 돌아가길 원할 것이다. 그런 경우 먼저 tty 모드를 def_prog_mode() 함수로 저장하고 endwin() 함수를 호출해서 curses 모드를 끝내면 된다. 이렇게 하게되면 원래의 tty 모드 상태가 된다. 그러다 그 전에 작업하던 curses 모드로 돌아갈려면 reset_prog_mode() 함수를 호출해라. 이 함수는 tty 를 def_prog_mode() 함수에 의해 저장된 상태로 돌려준다. 그 후 refresh() 를 하게되면 curses 모드로 완전히 돌아온 것이다. 여기에 이런 절차를 보여주는 예제가 있다.

Example 12. Temporarily Leaving Curses Mode

#include <ncurses.h>                                                              
                                                                                  
int main()                                                                        
{                                                                                 
        initscr();                      /* Start curses mode              */      
        printw("Hello World !!!\n");    /* Print Hello World              */      
        refresh();                      /* Print it on to the real screen */      
        def_prog_mode();                /* Save the tty modes             */      
        endwin();                       /* End curses mode temporarily    */      
        system("/bin/sh");              /* Do whatever you like in cooked mode */ 
        reset_prog_mode();              /* Return to the previous tty mode*/      
                                        /* stored by def_prog_mode()      */      
        refresh();                      /* Do refresh() to restore the    */      
                                        /* Screen contents                */      
        printw("Another String\n");     /* Back to curses use the full    */      
        refresh();                      /* capabilities of curses         */      
        endwin();                       /* End curses mode                */      
                                                                                  
        return 0;                                                                 
}                                                                                 




1.14.3 ACS_ 변수들


당신이 DOS 에서 프로그램해 본적이 있다면 확장된 캐릭터셋의 그 멋진 문자들에 대해 알고 있을 것이다. 그것들은 단지 몇몇 터미널에서만 출력이 가능하다. box() 와 같은 NCURSES 함수는 이 문자들을 사용한다. ACS 로 시작하는 이 변수들의 이름은 Alternative Character Set 을 뜻한다. 위에 나왔던 프로그램들 중에서 몇몇개가 이 문자들을 썼었다는 것을 아마 아는 독자도 있을 것이다. 여기에 모든 문자들을 보여주는 예제가 있다.

Example 13. ACS Variables Example

#include <ncurses.h>                                                                
                                                                                    
int main()                                                                          
{                                                                                   
        initscr();                                                                  
                                                                                    
        printw("Upper left corner           "); addch(ACS_ULCORNER); printw("\n");  
        printw("Lower left corner           "); addch(ACS_LLCORNER); printw("\n");  
        printw("Lower right corner          "); addch(ACS_LRCORNER); printw("\n");  
        printw("Tee pointing right          "); addch(ACS_LTEE); printw("\n");      
        printw("Tee pointing left           "); addch(ACS_RTEE); printw("\n");      
        printw("Tee pointing up             "); addch(ACS_BTEE); printw("\n");      
        printw("Tee pointing down           "); addch(ACS_TTEE); printw("\n");      
        printw("Horizontal line             "); addch(ACS_HLINE); printw("\n");     
        printw("Vertical line               "); addch(ACS_VLINE); printw("\n");     
        printw("Large Plus or cross over    "); addch(ACS_PLUS); printw("\n");      
        printw("Scan Line 1                 "); addch(ACS_S1); printw("\n");        
        printw("Scan Line 3                 "); addch(ACS_S3); printw("\n");        
        printw("Scan Line 7                 "); addch(ACS_S7); printw("\n");        
        printw("Scan Line 9                 "); addch(ACS_S9); printw("\n");        
        printw("Diamond                     "); addch(ACS_DIAMOND); printw("\n");   
        printw("Checker board (stipple)     "); addch(ACS_CKBOARD); printw("\n");   
        printw("Degree Symbol               "); addch(ACS_DEGREE); printw("\n");    
        printw("Plus/Minus Symbol           "); addch(ACS_PLMINUS); printw("\n");   
        printw("Bullet                      "); addch(ACS_BULLET); printw("\n");    
        printw("Arrow Pointing Left         "); addch(ACS_LARROW); printw("\n");    
        printw("Arrow Pointing Right        "); addch(ACS_RARROW); printw("\n");    
        printw("Arrow Pointing Down         "); addch(ACS_DARROW); printw("\n");    
        printw("Arrow Pointing Up           "); addch(ACS_UARROW); printw("\n");    
        printw("Board of squares            "); addch(ACS_BOARD); printw("\n");     
        printw("Lantern Symbol              "); addch(ACS_LANTERN); printw("\n");   
        printw("Solid Square Block          "); addch(ACS_BLOCK); printw("\n");     
        printw("Less/Equal sign             "); addch(ACS_LEQUAL); printw("\n");    
        printw("Greater/Equal sign          "); addch(ACS_GEQUAL); printw("\n");    
        printw("Pi                          "); addch(ACS_PI); printw("\n");        
        printw("Not equal                   "); addch(ACS_NEQUAL); printw("\n");    
        printw("UK pound sign               "); addch(ACS_STERLING); printw("\n");  
                                                                                    
        refresh();                                                                  
        getch();                                                                    
        endwin();                                                                   
                                                                                    
        return 0;                                                                   
}                                                                                   




1.15 그외 라이브러리들


curses 라이브러리 말고도 많은 기능성과 다양한 특징들이 있는 텍스트 모드 라이브러리들이 몇개 있다. 다음 섹션에서 curses 와 함께 배포되는 세개의 표준 라이브러리에 대해 설명할 것이다.


1.16 패널 라이브러리


자 이제 당신은 curses 에 익숙해졌으니, 뭔가 큰 걸 하나 해보고 싶을 것이다. 당신은 아마 좀더 프로페셔날한 윈도우 꼴로 만들기 위해서 여러 윈도우들을 겹쳐서 만들었을 것이다. 그러나 운나쁘게도 이것들을 다루는 것은 금새 난관에 부닥칠 것이다. 잦은 refresh 와 갱신은 그야말로 악몽이다. 당신이 적절한 순서로 윈도우들을 refresh 해주지 않으면 겹쳐 있는 윈도우들은 그 흔적을 화면에 만들어낼 것이다.

그렇다고 절망하긴 이르다. panels 라이브러리에서 제공하는 좋은 해결책이 있다. ncurses 의 개발자들의 말하길

당신의 인터페이스 디자인이 여러 겹친 윈도우의 속에 있다가 실행시에 맨위로 나와야 되는 그런 윈도우라면 그것들을 제대로 표시해서 관리하는 것은 매우 지루하고 힘든일이 될 것이다. 그래서 panels 라이브러리가 있다.

만약 겹치는 윈도우가 많다면, panels 라이브러리를 써야 한다. 이 함수는 wnoutrefresh(), doupdate() 함수를 계속 호출하고 또 제대로 (맨 밑바닥부터) 호출해야하는 부담을 미연에 방지해준다. 이 라이브러리는 윈도우의 순서와 겹친 것에 대한 정보를 가지고서 화면을 적절하게 갱신해준다. 그러니 뭘 주저하나? panels 에 대해 좀 더 제대로 봐보도록 하자.


1.16.1 개괄


패널 객체는 내부적으로 다른 패널 객체를 포함할 수 있는 판의 일부로 다뤄지는 윈도우의 일종이다. 이 판은 맨 위 패널은 완벽하게 보이고 그 외 패널은 그것의 위치에 따라 보일 수도 있고 안 보일 수도 있는 스택처럼 다뤄진다. 그래서 이것의 기본은 겹쳐진 패널들의 스택을 만들고 이것들을 표시해주기위해 패널 라이브러리를 제대로 사용하는 것이다. refresh() 와 비슷한 함수가 있는데, 이것은 호출되면 올바른 순서에 따라 패널들을 보여주게 된다. 함수들은 패널을 숨기거나 보여주고 또 패널을 이동하고 그것의 크기를 바꾸는 등의 기능을 가진 것들이 제공된다. 이 함수 들을 호출하는 동안 겹쳐진 것들의 문제는 panels 라이브러리에 의해 다뤄진다.

패널 프로그램의 일반적인 흐름은 다음과 같다:

  • 패널에 붙여질 윈도우를 생성한다. (newwin() 사용)

  • 이제 보여지는 순서대로 패널을 생성한다. 보여지기 원하는 순서대로 그것들을 쌓아라. new_panel() 함수를 쓰면 패널을 만들 수 있다.

  • update_panels() 함수를 호출하여 패널들을 가상의 화면에 맞는 순서대로 보여지도록 업데이트한다. 그 다음 doupdate() 를 호출하여 실제 화면에 나타나도록 한다.

  • show_panel(), hide_panel(), move_panel() 등의 함수를 사용하여 패널들을 다룬다. panel_hidden() 과 panel_window() 와 같은 보조 함수를 사용해라. 패널에 쓰이는 설정 정보들을 저장하기 위해서 사용자 포인터 를 사용하라. set_panel_useptr() 과 panel_userptr() 함수를 패널에 사용자 포인터를 설정하고 얻어오는데 쓸 수 있을 것이다.

  • 패널을 더 이상 쓰지 않게되면, del_panel() 을 사용하여 그것을 지운다.

이제 몇몇 프로그램들을 통해 개념을 명확히 해보자. 다음은 3개의 겹치는 패널을 만들고 그것을 화면에 보여주는 간단한 프로그램이다.


1.16.2 패널 라이브러리와 컴파일 하기


패널 라이브러리 함수를 쓰려면, panel.h 를 인클루드하고 -lncurses 다음에 -lpanel 플래그를 써서 프로그램을 링크시켜야한다.

#!
     #include <panel.h>                                                       
    .                                                                        
    .                                                                        
    .                                                                        
                                                                             
    compile and link: gcc <program file> -lpanel -lncurses                   


Example 14. Panel basics

#include <panel.h>                                                                
                                                                                  
int main()                                                                        
{       WINDOW *my_wins[3];                                                       
        PANEL  *my_panels[3];                                                     
        int lines = 10, cols = 40, y = 2, x = 4, i;                               
                                                                                  
        initscr();                                                                
        cbreak();                                                                 
        noecho();                                                                 
                                                                                  
        /* Create windows for the panels */                                       
        my_wins[0] = newwin(lines, cols, y, x);                                   
        my_wins[1] = newwin(lines, cols, y + 1, x + 5);                           
        my_wins[2] = newwin(lines, cols, y + 2, x + 10);                          
                                                                                  
        /*                                                                        
         * Create borders around the windows so that you can see the effect       
         * of panels                                                              
         */                                                                       
        for(i = 0; i < 3; +++i)                                                   
                box(my_wins[i], 0, 0);                                            
                                                                                  
        /* Attach a panel to each window */     /* Order is bottom up */          
        my_panels[0] = new_panel(my_wins[0]);   /* Push 0, order: stdscr-0 */     
        my_panels[1] = new_panel(my_wins[1]);   /* Push 1, order: stdscr-0-1 */   
        my_panels[2] = new_panel(my_wins[2]);   /* Push 2, order: stdscr-0-1-2 */ 
                                                                                  
        /* Update the stacking order. 2nd panel will be on top */                 
        update_panels();                                                          
                                                                                  
        /* Show it on the screen */                                               
        doupdate();                                                               
                                                                                  
        getch();                                                                  
        endwin();                                                                 
}                                                                                 


보다시피, 위 프로그램은 앞에서 설명했던 간단한 흐름을 그대로 따르고 있다. newwin() 을 통해 윈도우를 생성한 후 그것들을 new_panel() 로 패널에 붙인다. 우리가 패널을 하나씩 붙이고 있기 때문에 패널의 스택이 갱신된다. 이것들을 화면에 출력하기 위해서 update_panels() 와 doupdate()가 호출되었다.


1.16.3 패널 윈도우 탐색


약간 더 복잡한 예제가 아래에 있다. 이 프로그램은 탭키를 이용해 전환될 수 있는 세개의 윈도우를 만든다. 코드를 자세히 보도록 하자.

Example 15. Panel Window Browsing Example

#include <panel.h>                                                                                
                                                                                                  
#define NLINES 10                                                                                 
#define NCOLS 40                                                                                  
                                                                                                  
void init_wins(WINDOW **wins, int n);                                                             
void win_show(WINDOW *win, char *label, int label_color);                                         
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); 
                                                                                                  
int main()                                                                                        
{       WINDOW *my_wins[3];                                                                       
        PANEL  *my_panels[3];                                                                     
        PANEL  *top;                                                                              
        int ch;                                                                                   
                                                                                                  
        /* Initialize curses */                                                                   
        initscr();                                                                                
        start_color();                                                                            
        cbreak();                                                                                 
        noecho();                                                                                 
        keypad(stdscr, TRUE);                                                                     
                                                                                                  
        /* Initialize all the colors */                                                           
        init_pair(1, COLOR_RED, COLOR_BLACK);                                                     
        init_pair(2, COLOR_GREEN, COLOR_BLACK);                                                   
        init_pair(3, COLOR_BLUE, COLOR_BLACK);                                                    
        init_pair(4, COLOR_CYAN, COLOR_BLACK);                                                    
                                                                                                  
        init_wins(my_wins, 3);                                                                    
                                                                                                  
        /* Attach a panel to each window */     /* Order is bottom up */                          
        my_panels[0] = new_panel(my_wins[0]);   /* Push 0, order: stdscr-0 */                     
        my_panels[1] = new_panel(my_wins[1]);   /* Push 1, order: stdscr-0-1 */                   
        my_panels[2] = new_panel(my_wins[2]);   /* Push 2, order: stdscr-0-1-2 */                 
                                                                                                  
        /* Set up the user pointers to the next panel */                                          
        set_panel_userptr(my_panels[0], my_panels[1]);                                            
        set_panel_userptr(my_panels[1], my_panels[2]);                                            
        set_panel_userptr(my_panels[2], my_panels[0]);                                            
                                                                                                  
        /* Update the stacking order. 2nd panel will be on top */                                 
        update_panels();                                                                          
                                                                                                  
        /* Show it on the screen */                                                               
        attron(COLOR_PAIR(4));                                                                    
        mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)");             
        attroff(COLOR_PAIR(4));                                                                   
        doupdate();                                                                               
                                                                                                  
        top = my_panels[2];                                                                       
        while((ch = getch()) != KEY_F(1))                                                         
        {       switch(ch)                                                                        
                {       case 9:                                                                   
                                top = (PANEL *)panel_userptr(top);                                
                                top_panel(top);                                                   
                                break;                                                            
                }                                                                                 
                update_panels();                                                                  
                doupdate();                                                                       
        }                                                                                         
        endwin();                                                                                 
        return 0;                                                                                 
}                                                                                                 
                                                                                                  
/* Put all the windows */                                                                         
void init_wins(WINDOW **wins, int n)                                                              
{       int x, y, i;                                                                              
        char label[80];                                                                           
                                                                                                  
        y = 2;                                                                                    
        x = 10;                                                                                   
        for(i = 0; i < n; ++i)                                                                    
        {       wins[i] = newwin(NLINES, NCOLS, y, x);                                            
                sprintf(label, "Window Number %d", i + 1);                                        
                win_show(wins[i], label, i + 1);                                                  
                y += 3;                                                                           
                x += 7;                                                                           
        }                                                                                         
}                                                                                                 
                                                                                                  
/* Show the window with a border and a label */                                                   
void win_show(WINDOW *win, char *label, int label_color)                                          
{       int startx, starty, height, width;                                                        
                                                                                                  
        getbegyx(win, starty, startx);                                                            
        getmaxyx(win, height, width);                                                             
                                                                                                  
        box(win, 0, 0);                                                                           
        mvwaddch(win, 2, 0, ACS_LTEE);                                                            
        mvwhline(win, 2, 1, ACS_HLINE, width - 2);                                                
        mvwaddch(win, 2, width - 1, ACS_RTEE);                                                    
                                                                                                  
        print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));                        
}                                                                                                 
                                                                                                  
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)  
{       int length, x, y;                                                                         
        float temp;                                                                               
                                                                                                  
        if(win == NULL)                                                                           
                win = stdscr;                                                                     
        getyx(win, y, x);                                                                         
        if(startx != 0)                                                                           
                x = startx;                                                                       
        if(starty != 0)                                                                           
                y = starty;                                                                       
        if(width == 0)                                                                            
                width = 80;                                                                       
                                                                                                  
        length = strlen(string);                                                                  
        temp = (width - length)/ 2;                                                               
        x = startx + (int)temp;                                                                   
        wattron(win, color);                                                                      
        mvwprintw(win, y, x, "%s", string);                                                       
        wattroff(win, color);                                                                     
        refresh();                                                                                
}                                                                                                 




1.16.4 사용자 포인터 쓰기


위 예제에서 나는 순환시 다음의 윈도우가 어느 것인지를 찾기위해 사용자 포인터를 이용하였다. 우리는 원하는 어떤 정보든 그것을 저장하기 위해 사용자 포인터를 이용하여 패널에 그것을 붙일 수 있다. 이 경우엔 나는 순환에서 다음패널에 대한 포인터를 저장해뒀다. 패널에 대한 사용자 포인터는 set_panel_userptr() 함수를 통해 설정할 수 있다. panel_userptr() 함수를 호출하면 인자로 주어진 패널에 대한 사용자 포인터를 리턴받아 쓸 수 있다. 순환에서 다음의 패널을 찾은 후 top_panel() 함수에 의해 맨 위로 올라오게 된다. 이 함수는 인자로 주어진 패널을 패널 스택의 최상위로 옮긴다.


1.16.5 패널 이동 및 크기 변경


move_panel() 함수는 패널을 원하는 위치로 옮기는데 사용한다. 그러나 이것은 스택안의 패널 위치를 바꾸지는 않는다. 패널에 붙어 있는 윈도우를 옮기기 위해서는 mvwin() 함수를 쓰는 것이 아니라 move_panel() 함수를 써야 함을 명심하라.

패널 크기를 재조정 하는 것은 약간 복잡하다. 패널에 붙어 있는 윈도우의 크기를 한번에 재조정해주는 함수는 없다. 패널을 리사이즈하는 방법은 원하는 크기의 새 윈도우를 만들고 replace_panel() 함수를 이용하여 패널과 결합되어 있는 윈도우를 변경하는 것이다. 이전 윈도우를 지워야 한다는 것을 잊지마라. 패널에 붙어 있는 윈도우는 panel_window() 함수로 찾을 수 있다.

다음 프로그램은 이 개념을 보여준다. 아마 간단할 것이다. 역시 <TAB> 키를 통해 윈도우를 순환해볼 수 있고, 활성화된 패널에 대해 'r' 키를 누르면 크기변경을, 'm' 키를 누르면 이동이 가능하다. 방향키를 눌러서 재조정할 크기나 원하는 위치로 이동시킨 다음 엔터키를 눌러서 작업을 마치면 된다. 이 예제는 작업을 위해 필요한 데이터를 얻기위해서 사용자 데이터를 어떤식으로 써야 하는지 보여주고 있다.

Example 16. Panel Moving and Resizing example

#include <panel.h>                                                                                                                      
                                                                                                                                        
typedef struct _PANEL_DATA {                                                                                                            
        int x, y, w, h;                                                                                                                 
        char label[80];                                                                                                                 
        int label_color;                                                                                                                
        PANEL *next;                                                                                                                    
}PANEL_DATA;                                                                                                                            
                                                                                                                                        
#define NLINES 10                                                                                                                       
#define NCOLS 40                                                                                                                        
                                                                                                                                        
void init_wins(WINDOW **wins, int n);                                                                                                   
void win_show(WINDOW *win, char *label, int label_color);                                                                               
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);                                       
void set_user_ptrs(PANEL **panels, int n);                                                                                              
                                                                                                                                        
int main()                                                                                                                              
{       WINDOW *my_wins[3];                                                                                                             
        PANEL  *my_panels[3];                                                                                                           
        PANEL_DATA  *top;                                                                                                               
        PANEL *stack_top;                                                                                                               
        WINDOW *temp_win, *old_win;                                                                                                     
        int ch;                                                                                                                         
        int newx, newy, neww, newh;                                                                                                     
        int size = FALSE, move = FALSE;                                                                                                 
                                                                                                                                        
        /* Initialize curses */                                                                                                         
        initscr();                                                                                                                      
        start_color();                                                                                                                  
        cbreak();                                                                                                                       
        noecho();                                                                                                                       
        keypad(stdscr, TRUE);                                                                                                           
                                                                                                                                        
        /* Initialize all the colors */                                                                                                 
        init_pair(1, COLOR_RED, COLOR_BLACK);                                                                                           
        init_pair(2, COLOR_GREEN, COLOR_BLACK);                                                                                         
        init_pair(3, COLOR_BLUE, COLOR_BLACK);                                                                                          
        init_pair(4, COLOR_CYAN, COLOR_BLACK);                                                                                          
                                                                                                                                        
        init_wins(my_wins, 3);                                                                                                          
                                                                                                                                        
        /* Attach a panel to each window */     /* Order is bottom up */                                                                
        my_panels[0] = new_panel(my_wins[0]);   /* Push 0, order: stdscr-0 */                                                           
        my_panels[1] = new_panel(my_wins[1]);   /* Push 1, order: stdscr-0-1 */                                                         
        my_panels[2] = new_panel(my_wins[2]);   /* Push 2, order: stdscr-0-1-2 */                                                       
                                                                                                                                        
        set_user_ptrs(my_panels, 3);                                                                                                    
        /* Update the stacking order. 2nd panel will be on top */                                                                       
        update_panels();                                                                                                                
                                                                                                                                        
        /* Show it on the screen */                                                                                                     
        attron(COLOR_PAIR(4));                                                                                                          
        mvprintw(LINES - 3, 0, "Use 'm' for moving, 'r' for resizing");                                                                 
        mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)");                                                   
        attroff(COLOR_PAIR(4));                                                                                                         
        doupdate();                                                                                                                     
                                                                                                                                        
        stack_top = my_panels[2];                                                                                                       
        top = (PANEL_DATA *)panel_userptr(stack_top);                                                                                   
        newx = top->x;                                                                                                                  
        newy = top->y;                                                                                                                  
        neww = top->w;                                                                                                                  
        newh = top->h;                                                                                                                  
        while((ch = getch()) != KEY_F(1))                                                                                               
        {       switch(ch)                                                                                                              
                {       case 9:         /* Tab */                                                                                       
                                top = (PANEL_DATA *)panel_userptr(stack_top);                                                           
                                top_panel(top->next);                                                                                   
                                stack_top = top->next;                                                                                  
                                top = (PANEL_DATA *)panel_userptr(stack_top);                                                           
                                newx = top->x;                                                                                          
                                newy = top->y;                                                                                          
                                neww = top->w;                                                                                          
                                newh = top->h;                                                                                          
                                break;                                                                                                  
                        case 'r':       /* Re-Size*/                                                                                    
                                size = TRUE;                                                                                            
                                attron(COLOR_PAIR(4));                                                                                  
                                mvprintw(LINES - 4, 0, "Entered Resizing :Use Arrow Keys to resize and press <ENTER> to end resizing"); 
                                refresh();                                                                                              
                                attroff(COLOR_PAIR(4));                                                                                 
                                break;                                                                                                  
                        case 'm':       /* Move */                                                                                      
                                attron(COLOR_PAIR(4));                                                                                  
                                mvprintw(LINES - 4, 0, "Entered Moving: Use Arrow Keys to Move and press <ENTER> to end moving");       
                                refresh();                                                                                              
                                attroff(COLOR_PAIR(4));                                                                                 
                                move = TRUE;                                                                                            
                                break;                                                                                                  
                        case KEY_LEFT:                                                                                                  
                                if(size == TRUE)                                                                                        
                                {       --newx;                                                                                         
                                        ++neww;                                                                                         
                                }                                                                                                       
                                if(move == TRUE)                                                                                        
                                        --newx;                                                                                         
                                break;                                                                                                  
                        case KEY_RIGHT:                                                                                                 
                                if(size == TRUE)                                                                                        
                                {       ++newx;                                                                                         
                                        --neww;                                                                                         
                                }                                                                                                       
                                if(move == TRUE)                                                                                        
                                        ++newx;                                                                                         
                                break;                                                                                                  
                        case KEY_UP:                                                                                                    
                                if(size == TRUE)                                                                                        
                                {       --newy;                                                                                         
                                        ++newh;                                                                                         
                                }                                                                                                       
                                if(move == TRUE)                                                                                        
                                        --newy;                                                                                         
                                break;                                                                                                  
                        case KEY_DOWN:                                                                                                  
                                if(size == TRUE)                                                                                        
                                {       ++newy;                                                                                         
                                        --newh;                                                                                         
                                }                                                                                                       
                                if(move == TRUE)                                                                                        
                                        ++newy;                                                                                         
                                break;                                                                                                  
                        case 10:        /* Enter */                                                                                     
                                move(LINES - 4, 0);                                                                                     
                                clrtoeol();                                                                                             
                                refresh();                                                                                              
                                if(size == TRUE)                                                                                        
                                {       old_win = panel_window(stack_top);                                                              
                                        temp_win = newwin(newh, neww, newy, newx);                                                      
                                        replace_panel(stack_top, temp_win);                                                             
                                        win_show(temp_win, top->label, top->label_color);                                               
                                        delwin(old_win);                                                                                
                                        size = FALSE;                                                                                   
                                }                                                                                                       
                                if(move == TRUE)                                                                                        
                                {       move_panel(stack_top, newy, newx);                                                              
                                        move = FALSE;                                                                                   
                                }                                                                                                       
                                break;                                                                                                  
                                                                                                                                        
                }                                                                                                                       
                attron(COLOR_PAIR(4));                                                                                                  
                mvprintw(LINES - 3, 0, "Use 'm' for moving, 'r' for resizing");                                                         
                mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)");                                           
                attroff(COLOR_PAIR(4));                                                                                                 
                refresh();                                                                                                              
                update_panels();                                                                                                        
                doupdate();                                                                                                             
        }                                                                                                                               
        endwin();                                                                                                                       
        return 0;                                                                                                                       
}                                                                                                                                       
                                                                                                                                        
/* Put all the windows */                                                                                                               
void init_wins(WINDOW **wins, int n)                                                                                                    
{       int x, y, i;                                                                                                                    
        char label[80];                                                                                                                 
                                                                                                                                        
        y = 2;                                                                                                                          
        x = 10;                                                                                                                         
        for(i = 0; i < n; ++i)                                                                                                          
        {       wins[i] = newwin(NLINES, NCOLS, y, x);                                                                                  
                sprintf(label, "Window Number %d", i + 1);                                                                              
                win_show(wins[i], label, i + 1);                                                                                        
                y += 3;                                                                                                                 
                x += 7;                                                                                                                 
        }                                                                                                                               
}                                                                                                                                       
                                                                                                                                        
/* Set the PANEL_DATA structures for individual panels */                                                                               
void set_user_ptrs(PANEL **panels, int n)                                                                                               
{       PANEL_DATA *ptrs;                                                                                                               
        WINDOW *win;                                                                                                                    
        int x, y, w, h, i;                                                                                                              
        char temp[80];                                                                                                                  
                                                                                                                                        
        ptrs = (PANEL_DATA *)calloc(n, sizeof(PANEL_DATA));                                                                             
                                                                                                                                        
        for(i = 0;i < n; ++i)                                                                                                           
        {       win = panel_window(panels[i]);                                                                                          
                getbegyx(win, y, x);                                                                                                    
                getmaxyx(win, h, w);                                                                                                    
                ptrs[i].x = x;                                                                                                          
                ptrs[i].y = y;                                                                                                          
                ptrs[i].w = w;                                                                                                          
                ptrs[i].h = h;                                                                                                          
                sprintf(temp, "Window Number %d", i + 1);                                                                               
                strcpy(ptrs[i].label, temp);                                                                                            
                ptrs[i].label_color = i + 1;                                                                                            
                if(i + 1 == n)                                                                                                          
                        ptrs[i].next = panels[0];                                                                                       
                else                                                                                                                    
                        ptrs[i].next = panels[i + 1];                                                                                   
                set_panel_userptr(panels[i], &ptrs[i]);                                                                                 
        }                                                                                                                               
}                                                                                                                                       
                                                                                                                                        
/* Show the window with a border and a label */                                                                                         
void win_show(WINDOW *win, char *label, int label_color)                                                                                
{       int startx, starty, height, width;                                                                                              
                                                                                                                                        
        getbegyx(win, starty, startx);                                                                                                  
        getmaxyx(win, height, width);                                                                                                   
                                                                                                                                        
        box(win, 0, 0);                                                                                                                 
        mvwaddch(win, 2, 0, ACS_LTEE);                                                                                                  
        mvwhline(win, 2, 1, ACS_HLINE, width - 2);                                                                                      
        mvwaddch(win, 2, width - 1, ACS_RTEE);                                                                                          
                                                                                                                                        
        print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));                                                              
}                                                                                                                                       
                                                                                                                                        
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)                                        
{       int length, x, y;                                                                                                               
        float temp;                                                                                                                     
                                                                                                                                        
        if(win == NULL)                                                                                                                 
                win = stdscr;                                                                                                           
        getyx(win, y, x);                                                                                                               
        if(startx != 0)                                                                                                                 
                x = startx;                                                                                                             
        if(starty != 0)                                                                                                                 
                y = starty;                                                                                                             
        if(width == 0)                                                                                                                  
                width = 80;                                                                                                             
                                                                                                                                        
        length = strlen(string);                                                                                                        
        temp = (width - length)/ 2;                                                                                                     
        x = startx + (int)temp;                                                                                                         
        wattron(win, color);                                                                                                            
        mvwprintw(win, y, x, "%s", string);                                                                                             
        wattroff(win, color);                                                                                                           
        refresh();                                                                                                                      
} 


메인 while 루프에 집중하자. 먼저 입력받은 키의 종류를 알아내면 거기에 맞는 적절한 처리를 하게 되어 있다. 만약 'r' 키가 눌려지면 크기조정 모드가 시작된다. 그 후 사용자가 방향키를 누름에 따라 바뀐 크기가 갱신된다. 사용자가 <ENTER> 를 누르게 되면 현재 선택이 끝나게 되고 설명했듯이 패널이 바뀐 크기로 재조정 된다. 크기 변경 모드에 있는 동안 프로그램은 윈도우 크기가 어떻게 변경되고 있는지 보여주지 않는다. 새 위치로 크기가 바뀌는 동안 점으로된 테두리를 출력하는 것은 독자들의 연습으로 남겨둔다.

사용자가 'm' 키를 누르면 이동 모드가 시작된다. 이것은 크기변경보다 약간은 더 단순하다. 방향키가 눌려짐에 따라 새 위치가 갱신되고 <ENTER> 을 누르게되면 패널이 move_panel() 함수를 호출함으로서 이동하게 된다.

이 프로그램에서 PANEL_DATA 로 표시된 사용자 데이터는 패널에 결합된 정보를 찾아내는데 매우 중요한 역할을 한다. 주석에서도 썼듯이 PANEL_DATA 에는 패널의 크기, 레이블, 레이블 색 그리고 순환해야할 다음 패널에 대한 포인터를 저장하고 있다.


1.16.6 패널 숨기기 및 보여주기


패널은 hide_panel() 함수를 써서 숨길 수도 있다. 이 함수는 단시 패널의 스택에서 그것을 제거함으로서 당신이 update_panels() 와 doupdate() 를 호출하게되면 화면에서 그 패널을 보여주지 않게 된다. 그러나 이 함수는 숨겨진 패널의 PANEL 구조체를 해제하지는 않는다. show_panel() 함수를 씀으로서 다시 화면에 볼여줄 수도 있다.

다음 프로그램은 패널을 숨기는 것을 보여주고 있다. 'a' 나 'b' 나 'c' 키를 누르게 되면 각각 첫번째 두번째 세번재 윈도우를 숨기거나 보여준다. 여기에는 윈도우가 숨겨졌는지 여부를 알기 위해서 hide 라는 작은 변수를 사용자 데이터로 쓰고 있다. 몇몇 특정한 이유로 패널이 히든상태인지 아닌지를 알려주는 panel_hidden() 함수가 작동하지 않는다. Michael Andres 에 의해 버그리포트도 [http]여기(http://www.geocrawler.com/archives/3/344/1999/9/0/2643549/)에 제출되었다.

Example 17. Panel Hiding and Showing example

#include <panel.h>                                                                                                     
                                                                                                                       
typedef struct _PANEL_DATA {                                                                                           
        int hide;       /* TRUE if panel is hidden */                                                                  
}PANEL_DATA;                                                                                                           
                                                                                                                       
#define NLINES 10                                                                                                      
#define NCOLS 40                                                                                                       
                                                                                                                       
void init_wins(WINDOW **wins, int n);                                                                                  
void win_show(WINDOW *win, char *label, int label_color);                                                              
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);                      
                                                                                                                       
int main()                                                                                                             
{       WINDOW *my_wins[3];                                                                                            
        PANEL  *my_panels[3];                                                                                          
        PANEL_DATA panel_datas[3];                                                                                     
        PANEL_DATA *temp;                                                                                              
        int ch;                                                                                                        
                                                                                                                       
        /* Initialize curses */                                                                                        
        initscr();                                                                                                     
        start_color();                                                                                                 
        cbreak();                                                                                                      
        noecho();                                                                                                      
        keypad(stdscr, TRUE);                                                                                          
                                                                                                                       
        /* Initialize all the colors */                                                                                
        init_pair(1, COLOR_RED, COLOR_BLACK);                                                                          
        init_pair(2, COLOR_GREEN, COLOR_BLACK);                                                                        
        init_pair(3, COLOR_BLUE, COLOR_BLACK);                                                                         
        init_pair(4, COLOR_CYAN, COLOR_BLACK);                                                                         
                                                                                                                       
        init_wins(my_wins, 3);                                                                                         
                                                                                                                       
        /* Attach a panel to each window */     /* Order is bottom up */                                               
        my_panels[0] = new_panel(my_wins[0]);   /* Push 0, order: stdscr-0 */                                          
        my_panels[1] = new_panel(my_wins[1]);   /* Push 1, order: stdscr-0-1 */                                        
        my_panels[2] = new_panel(my_wins[2]);   /* Push 2, order: stdscr-0-1-2 */                                      
                                                                                                                       
        /* Initialize panel datas saying that nothing is hidden */                                                     
        panel_datas[0].hide = FALSE;                                                                                   
        panel_datas[1].hide = FALSE;                                                                                   
        panel_datas[2].hide = FALSE;                                                                                   
                                                                                                                       
        set_panel_userptr(my_panels[0], &panel_datas[0]);                                                              
        set_panel_userptr(my_panels[1], &panel_datas[1]);                                                              
        set_panel_userptr(my_panels[2], &panel_datas[2]);                                                              
                                                                                                                       
        /* Update the stacking order. 2nd panel will be on top */                                                      
        update_panels();                                                                                               
                                                                                                                       
        /* Show it on the screen */                                                                                    
        attron(COLOR_PAIR(4));                                                                                         
        mvprintw(LINES - 3, 0, "Show or Hide a window with 'a'(first window)  'b'(Second Window)  'c'(Third Window)"); 
        mvprintw(LINES - 2, 0, "F1 to Exit");                                                                          
                                                                                                                       
        attroff(COLOR_PAIR(4));                                                                                        
        doupdate();                                                                                                    
                                                                                                                       
        while((ch = getch()) != KEY_F(1))                                                                              
        {       switch(ch)                                                                                             
                {       case 'a':                                                                                      
                                temp = (PANEL_DATA *)panel_userptr(my_panels[0]);                                      
                                if(temp->hide == FALSE)                                                                
                                {       hide_panel(my_panels[0]);                                                      
                                        temp->hide = TRUE;                                                             
                                }                                                                                      
                                else                                                                                   
                                {       show_panel(my_panels[0]);                                                      
                                        temp->hide = FALSE;                                                            
                                }                                                                                      
                                break;                                                                                 
                        case 'b':                                                                                      
                                temp = (PANEL_DATA *)panel_userptr(my_panels[1]);                                      
                                if(temp->hide == FALSE)                                                                
                                {       hide_panel(my_panels[1]);                                                      
                                        temp->hide = TRUE;                                                             
                                }                                                                                      
                                else                                                                                   
                                {       show_panel(my_panels[1]);                                                      
                                        temp->hide = FALSE;                                                            
                                }                                                                                      
                                break;                                                                                 
                        case 'c':                                                                                      
                                temp = (PANEL_DATA *)panel_userptr(my_panels[2]);                                      
                                if(temp->hide == FALSE)                                                                
                                {       hide_panel(my_panels[2]);                                                      
                                        temp->hide = TRUE;                                                             
                                }                                                                                      
                                else                                                                                   
                                {       show_panel(my_panels[2]);                                                      
                                        temp->hide = FALSE;                                                            
                                }                                                                                      
                                break;                                                                                 
                }                                                                                                      
                update_panels();                                                                                       
                doupdate();                                                                                            
        }                                                                                                              
        endwin();                                                                                                      
        return 0;                                                                                                      
}                                                                                                                      
                                                                                                                       
/* Put all the windows */                                                                                              
void init_wins(WINDOW **wins, int n)                                                                                   
{       int x, y, i;                                                                                                   
        char label[80];                                                                                                
                                                                                                                       
        y = 2;                                                                                                         
        x = 10;                                                                                                        
        for(i = 0; i < n; ++i)                                                                                         
        {       wins[i] = newwin(NLINES, NCOLS, y, x);                                                                 
                sprintf(label, "Window Number %d", i + 1);                                                             
                win_show(wins[i], label, i + 1);                                                                       
                y += 3;                                                                                                
                x += 7;                                                                                                
        }                                                                                                              
}                                                                                                                      
                                                                                                                       
/* Show the window with a border and a label */                                                                        
void win_show(WINDOW *win, char *label, int label_color)                                                               
{       int startx, starty, height, width;                                                                             
                                                                                                                       
        getbegyx(win, starty, startx);                                                                                 
        getmaxyx(win, height, width);                                                                                  
                                                                                                                       
        box(win, 0, 0);                                                                                                
        mvwaddch(win, 2, 0, ACS_LTEE);                                                                                 
        mvwhline(win, 2, 1, ACS_HLINE, width - 2);                                                                     
        mvwaddch(win, 2, width - 1, ACS_RTEE);                                                                         
                                                                                                                       
        print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color));                                             
}                                                                                                                      
                                                                                                                       
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)                       
{       int length, x, y;                                                                                              
        float temp;                                                                                                    
                                                                                                                       
        if(win == NULL)                                                                                                
                win = stdscr;                                                                                          
        getyx(win, y, x);                                                                                              
        if(startx != 0)                                                                                                
                x = startx;                                                                                            
        if(starty != 0)                                                                                                
                y = starty;                                                                                            
        if(width == 0)                                                                                                 
                width = 80;                                                                                            
                                                                                                                       
        length = strlen(string);                                                                                       
        temp = (width - length)/ 2;                                                                                    
        x = startx + (int)temp;                                                                                        
        wattron(win, color);                                                                                           
        mvwprintw(win, y, x, "%s", string);                                                                            
        wattroff(win, color);                                                                                          
        refresh();                                                                                                     
} 




1.16.7 panel_above() 와 panel_below() 함수들


panel_above() 와 panel_below() 함수는 그 패널의 위와 아래에 있는 패널을 찾기 위해 쓴다. 만약 이 함수들에 인자가 NULL 로 들어가게 되면 각각 제일 위의 패널과 제일 밑의 패널에 대한 포인터를 리턴하게 된다.


1.17 메뉴 라이브러리


menus 라이브러리는 메뉴를 생성할 수 있는 멋진 확장을 기본 curses 에 제공한다. 이것은 메뉴를 생성할 수 있는 함수들의 집합이다. 그러나 보다 멋진 외양을 갖추려면 색상등을 설정해줘야만 한다. 좀 더 자세히 알아보도록 하자.

메뉴는 사용자로 하여금 주어진 아이템들에 대해서 그 선택을 할 수 있게 해주는 화면 표시이다. 간단히 말하면, 메뉴는 하나 또는 여러개의 아이템들이 선택될 수 있는 아이템들의 집합이다. 몇몇 독자들은 메뉴에서의 여러개의 아이템들을 선택할 수 있다는 것을 모를 수도 있을 것이다. 메뉴 라이브러리는 사용자의 기호에 따라 하나 이상을 선택할 수 있는 메뉴를 만들 수도 있다. 이것에 관해서는 뒤의 섹션에서 다룬다. 자 이젠 기초를 공부해볼 시간이다.


1.17.1 개괄


메뉴를 만들려면 먼저 아이템들을 만들어야 한다. 그후 메뉴를 화면에 표시힌다. 이렇게 한 후 사용자 응답의 모든 처리는 어떤 메뉴 프로그램에서도 그 일꾼이 되는 menu_driver() 라는 고상한 함수 안에서 행해진다.

메뉴 프로그램의 일반적인 흐름은 다음과 같다.

  • curses 를 초기화 한다.

  • new_item() 을 써서 아이템을 생성한다. 각 아이템에 대해 이름과 설명을 명시할 수도 있다.

  • new_menu() 에 결합될 아이템을 명시해서 메뉴를 만든다.

  • menu_post() 함수로 메뉴를 붙이고 화면을 리프레쉬 한다.

  • 루프를 돌며 사용자 요청을 처리하고 menu_driver 를 통해 메뉴에 필요한 갱신을 한다.

  • menu_unpost() 함수로 메뉴를 떼어낸다.

  • free_menu() 로 메뉴에 할당된 메모리를 해제한다.

  • free_item() 으로 아이템에 할당된 메모리를 해제한다.

  • curses 를 종료한다.

이제 간단한 메뉴를 출력하고 상하 방향키로 선택이 바뀌는 메뉴가 있는 프로그램을 살펴보도록 하자.


1.17.2 메뉴 라이브러리와 컴파일 하기


메뉴 라이브러리 함수를 쓰려면, menu.h 를 인클루드 해야하고 프로그램을 링크할때 -lncurses 뒤에 -lmenu 플래그를 더해서 링크해야 한다.

#!
     #include <menu.h>                                                        
    .                                                                        
    .                                                                        
    .                                                                        
                                                                             
    compile and link: gcc <program file> -lmenu -lncurses                    


Example 18. Menu Basics

#include <curses.h>                                                          
#include <menu.h>                                                            
                                                                             
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))                             
#define CTRLD   4                                                            
                                                                             
char *choices[] = {                                                          
                        "Choice 1",                                          
                        "Choice 2",                                          
                        "Choice 3",                                          
                        "Choice 4",                                          
                        "Exit",                                              
                  };                                                         
                                                                             
int main()                                                                   
{       ITEM **my_items;                                                     
        int c;                                                               
        MENU *my_menu;                                                       
        int n_choices, i;                                                    
        ITEM *cur_item;                                                      
                                                                             
                                                                             
        initscr();                                                           
        cbreak();                                                            
        noecho();                                                            
        keypad(stdscr, TRUE);                                                
                                                                             
        n_choices = ARRAY_SIZE(choices);                                     
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));           
                                                                             
        for(i = 0; i < n_choices; ++i)                                       
                my_items[i] = new_item(choices[i], choices[i]);              
        my_items[n_choices] = (ITEM *)NULL;                                  
                                                                             
        my_menu = new_menu((ITEM **)my_items);                               
        mvprintw(LINES - 2, 0, "F1 to Exit");                                
        post_menu(my_menu);                                                  
        refresh();                                                           
                                                                             
        while((c = getch()) != KEY_F(1))                                     
        {   switch(c)                                                        
            {   case KEY_DOWN:                                               
                        menu_driver(my_menu, REQ_DOWN_ITEM);                 
                                break;                                       
                        case KEY_UP:                                         
                                menu_driver(my_menu, REQ_UP_ITEM);           
                                break;                                       
                }                                                            
        }                                                                    
                                                                             
        free_item(my_items[0]);                                              
        free_item(my_items[1]);                                              
        free_menu(my_menu);                                                  
        endwin();                                                            
}                                                                            


이 프로그램은 메뉴를 만들고 메뉴 라이브러리 사용에 필요한 기본 개념의 예를 보여주고 있다. 먼저 우리는 new_item() 함수로 아이템을 생성하고 new_menu() 함수를 써서 그것을 메뉴에 붙인다. 메뉴를 포스팅하고 화면을 리프레쉬 한 다음 메인 처리 루프가 시작된다. 그것은 사용자 입력을 받아서 적절한 처리를 한다. menu_driver() 함수가 메뉴 시스템의 주된 일을 하는 함수이다. 이 함수의 두번째 인자는 메뉴에 어떤일을 했는지를 나타낸다. 인자에 따라 menu_driver() 함수는 거기에 맞는 작업을 한다. 이 값에는 물론 메뉴 탐색 요청, 아스키 문자, 마우스 이벤트에 따른 KEY_MOUSE 라는 특수 키도 있을 수 있다.

menu_driver 는 다음의 탐색 요청을 받을 수 있다.

#!
      REQ_LEFT_ITEM         Move left to an item.                                  
     REQ_RIGHT_ITEM      Move right to an item.                                   
     REQ_UP_ITEM         Move up to an item.                                      
     REQ_DOWN_ITEM       Move down to an item.                                    
     REQ_SCR_ULINE       Scroll up a line.                                        
     REQ_SCR_DLINE          Scroll down a line.                                   
     REQ_SCR_DPAGE          Scroll down a page.                                   
     REQ_SCR_UPAGE         Scroll up a page.                                      
     REQ_FIRST_ITEM     Move to the first item.                                   
     REQ_LAST_ITEM         Move to the last item.                                 
     REQ_NEXT_ITEM         Move to the next item.                                 
     REQ_PREV_ITEM         Move to the previous item.                             
     REQ_TOGGLE_ITEM     Select/deselect an item.                                 
     REQ_CLEAR_PATTERN     Clear the menu pattern buffer.                         
     REQ_BACK_PATTERN      Delete the previous character from the pattern buffer. 
     REQ_NEXT_MATCH     Move to the next item matching the pattern match.         
     REQ_PREV_MATCH     Move to the previous item matching the pattern match.     


옵션의 종류에 겁먹지 마라. 여기에 대해 하나하나 천천히 알아볼 것이다. 이 예제에서 관심을 가진 옵션은 REQ_UP_ITEM 과 REQ_DOWN_ITEM 이다. 이 두옵션이 menu_driver 에 넘겨지면 menu_driver 는 현재 아이템에서 위로 하나 올리거나 아래로 하나 내리거나 한다.


1.17.3 Menu Driver: 메뉴 시스템의 큰 일꾼


위의 예제에서 보았듯이, menu_driver 는 메뉴 갱신에 중요한 역할을 한다. 여기에 쓰이는 다양한 옵션과 그 기능을 이해하는 것은 매우 중요하다. 위에서 설명했듯이, menu_driver() 의 두번째 인자는 탐색 요청이나 출력할 수 있는 문자, KEY_MOUSE 키 등이 될 수 있다. 이제 각각의 다른 탐색 요청에 대해서 파헤쳐 보자.

  • REQ_LEFT_ITEM and REQ_RIGHT_ITEM

메뉴는 한가지 아이템 이상의 여러 열로 보여질 수 있다. menu_format() 함수를 써서 이것을 할 수 있는데, 여러 열로 된 메뉴가 표시될 때, 이 요청들이 메뉴 드라이버로 하여금 현재 선택을 좌우로 옮기게 한다.

  • REQ_UP_ITEM and REQ_DOWN_ITEM

위의 예제에서 본 두개의 옵션이다. 이 옵션들이 주어지면 menu_driver 가 현재의 선택을 한 아이템 위 또는 아래로 이동하게 된다.

  • REQ_SCR_* options

REQ_SCR_ULINE, REQ_SCR_DLINE, REQ_SCR_DPAGE, REQ_SCR_UPAGE 의 네가지 옵션은 스크롤과 관련이 있다. 만약 메뉴의 모든 아이템이 메뉴 서브 윈도우에서 전부 표시되지 못하면 메뉴에 스크롤 기능이 생기게 된다. 이 요청들은 menu_driver 로 하여금 각각 한줄 위로, 아래로 또는 한페이지 위로, 아래로 스크롤 하게 한다.

  • REQ_FIRST_ITEM, REQ_LAST_ITEM, REQ_NEXT_ITEM and REQ_PREV_ITEM

이 요청들은 굳이 설명하지 않아도 알 것이다.

  • REQ_TOGGLE_ITEM

이 요청이 주어지면 현재 선택을 하나씩 바꿔볼 수 있다. 이 옵션은 여러개의 선택이 있는 메뉴에만 쓸 수 있다. 그래서 이 값을 쓸려면 O_ONEVALUE 값이 꺼져 있어야 한다. 이 값은 set_menu_opts() 함수로 껐다 켰다 할 수 있다.

  • Pattern Requests

모든 메뉴는 패턴 버퍼를 가지고 있다. 이것은 사용자가 입력한 아스키 문자에 가장 근접한 아이템을 찾는다. menu_driver 에 아스키 문자가 주어질때면 항상 그 문자를 패턴 버퍼에 넣는다. 그리고 아이템 리스트에서 가장 근접한 패턴을 찾아서 그 아이템으로 선택을 이동한다. REQ_CLEAR_PATTERN 요청은 패턴 버퍼를 클리어한다. REQ_BACK_PATTERN 은 패턴 버퍼의 이전 문자를 삭제한다. 만약 하나 이상의 매칭된 아이템을 찾게되면 REQ_NEXT_MATCH 와 REQ_PREV_MATCH 를 통해 현재 선택을 다음 이나 이전의 결과로 이동 할 수 있다.

  • Mouse Requests

KEY_MOUSE 요청은 마우스의 위치에 따라서 일어나는 반응이 다르다. 맨 페이지에 설명된 반응은,

만약 두번째 인자가 KEY_MOUSE 라는 특수 키 이면, 일어난 마우스 이벤트가 위에서 이미 정의되어 있는 요청들 중 하나로 번역된다. 현재로서는 단지 사용자 윈도우 (메뉴 영역 안이나 그외 표시용 윈도우 안과 같은) 에서의 클릭 만이 다뤄지고 있다. 만약 메뉴 표시 영역의 위에 한번의 클릭을 한다면, REQ_SCR_ULINE 가 발생되고, 두번 클릭을 하게 되면, REQ_SCR_UPAGE 가 발생한다. 또 세번 클릭을 하게 되면 REQ_FIRST_ITEM 이 발생한다. 만약 메뉴 표시 영역의 아래에 한번의 클릭을 한다면 REQ_SCR_DLINE 가 발생하고, 두번 클릭시는 REQ_SCR_DPAGE 가 발생, 세벌 클릭 시에는 REQ_LAST_ITEM 이 발생한다. 만약 당신이 메뉴 안의 아이템을 클릭하게 되면 메뉴 커서가 그 아이템으로 이동하게 된다.

위 요청들 각각은 다음의 여러 예제에서 적절하게 설명을 하게 될 것이다.


1.17.4 메뉴 윈도우


생성된 모든 메뉴에는 윈도우와 그 하위 윈도우가 있다. 메뉴 윈도우는 메뉴의 테두리나 제목등을 표시한다. 메뉴 하위 윈도우는 현재 선택에 맞는 메뉴 아이템들을 표시한다. 그러나 우리는 짧은 예제에서 어떤 윈도우나 하위 윈도우도 명시하지 않았다. 윈도우가 명시되지 않으면, stdscr 이 주 윈도우로 쓰이고 메뉴 시스템에서 아이템들을 표시하기 위한 하위 윈도우들을 계산해낸다. 그후 이 계산된 하위 윈도우에서 아이템들이 표시되게 된다. 자 이제 이 윈도우들을 좀 가지고 놀아보자. 그리고 메뉴에 테두리와 제목도 표시해 보자.

Example 19. Menu Windows Usage example

#include <menu.h>                                                                                 
                                                                                                  
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))                                                  
#define CTRLD   4                                                                                 
                                                                                                  
char *choices[] = {                                                                               
                        "Choice 1",                                                               
                        "Choice 2",                                                               
                        "Choice 3",                                                               
                        "Choice 4",                                                               
                        "Exit",                                                                   
                        (char *)NULL,                                                             
                  };                                                                              
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); 
                                                                                                  
int main()                                                                                        
{       ITEM **my_items;                                                                          
        int c;                                                                                    
        MENU *my_menu;                                                                            
        WINDOW *my_menu_win;                                                                      
        int n_choices, i;                                                                         
                                                                                                  
        /* Initialize curses */                                                                   
        initscr();                                                                                
        start_color();                                                                            
        cbreak();                                                                                 
        noecho();                                                                                 
        keypad(stdscr, TRUE);                                                                     
        init_pair(1, COLOR_RED, COLOR_BLACK);                                                     
                                                                                                  
        /* Create items */                                                                        
        n_choices = ARRAY_SIZE(choices);                                                          
        my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));                                    
        for(i = 0; i < n_choices; ++i)                                                            
                my_items[i] = new_item(choices[i], choices[i]);                                   
                                                                                                  
        /* Crate menu */                                                                          
        my_menu = new_menu((ITEM **)my_items);                                                    
                                                                                                  
        /* Create the window to be associated with the menu */                                    
        my_menu_win = newwin(10, 40, 4, 4);                                                       
        keypad(my_menu_win, TRUE);                                                                
                                                                                                  
        /* Set main window and sub window */                                                      
        set_menu_win(my_menu, my_menu_win);                                                       
        set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));                                  
                                                                                                  
        /* Set menu mark to the string " * " */                                                   
        set_menu_mark(my_menu, " * ");                                                            
                                                                                                  
        /* Print a border around the main window and print a title */                             
        box(my_menu_win, 0, 0);                                                                   
        print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));                         
        mvwaddch(my_menu_win, 2, 0, ACS_LTEE);                                                    
        mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);                                               
        mvwaddch(my_menu_win, 2, 39, ACS_RTEE);                                                   
        mvprintw(LINES - 2, 0, "F1 to exit");                                                     
        refresh();                                                                                
                                                                                                  
        /* Post the menu */                                                                       
        post_menu(my_menu);                                                                       
        wrefresh(my_menu_win);                                                                    
                                                                                                  
        while((c = wgetch(my_menu_win)) != KEY_F(1))                                              
        {       switch(c)                                                                         
                {       case KEY_DOWN:                                                            
                                menu_driver(my_menu, REQ_DOWN_ITEM);                              
                                break;                                                            
                        case KEY_UP:                                                              
                                menu_driver(my_menu, REQ_UP_ITEM);                                
                                break;                                                            
                }                                                                                 
                wrefresh(my_menu_win);                                                            
        }                                                                                         
                                                                                                  
        /* Unpost and free all the memory taken up */                                             
        unpost_menu(my_menu);                                                                     
        free_menu(my_menu);                                                                       
        for(i = 0; i < n_choices; ++i)                                                            
                free_item(my_items[i]);                                                           
        endwin();                                                                                 
}                                                                                                 
                                                                                                  
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)  
{       int length, x, y;                                                                         
        float temp;                                                                               
                                                                                                  
        if(win == NULL)                                                                           
                win = stdscr;                                                                     
        getyx(win, y, x);                                                                         
        if(startx != 0)                                                                           
                x = startx;                                                                       
        if(starty != 0)                                                                           
                y = starty;                                                                       
        if(width == 0)                                                                            
                width = 80;                                                                       
                                                                                                  
        length = strlen(string);                                                                  
        temp = (width - length)/ 2;                                                               
        x = startx + (int)temp;                                                                   
        wattron(win, color);                                                                      
        mvwprintw(win, y, x, "%s", string);                                                       
        wattroff(win, color);                                                                     
        refresh();                                                                                
}                                                                                                 


이 예제는 타이틀과 테두리 그리고 타이틀과 아이템을 구분하는 예쁜 선이 있는 메뉴를 만든다. 보다시피 윈도우를 메뉴에 붙이려면 set_menu_win() 함수를 써야만 한다. 그러고나서 하위 윈도우도 역시 붙인다. 이것은 하위 윈도우에 있는 아이템들을 표시해준다. 또한 set_menu_mark() 함수를 이용해서 선택된 아이템의 왼쪽에 표시가 나타나게 할 수도 있다.


1.17.5 메뉴 스크롤 하기


만약 윈도우에 주어진 하위 윈도우가 아이템들을 전부 보여줄 만큼 크지 않다면, 메뉴는 스크롤 된다. 당신이 현재 보여지는 리스트의 마지막 아이템을 보고 있을 때 만약 REQ_DOWN_ITEM 을 보내면, 이것은 REQ_SCR_DLINE 로 번역되어 한 아이템 만큼 메뉴가 스크롤 된다. 당신은 스크롤을 하기 위해 REQ_SCR_ 을 수동적으로 넘겨줘도 된다. 어떻게 하는지 한번 알아보자.

Example 20. Scrolling Menus example

#include <curses.h>                                                                               
#include <menu.h>                                                                                 
                                                                                                  
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))                                                  
#define CTRLD   4                                                                                 
                                                                                                  
char *choices[] = {                                                                               
                        "Choice 1",                                                               
                        "Choice 2",                                                               
                        "Choice 3",                                                               
                        "Choice 4",                                                               
                        "Choice 5",                                                               
                        "Choice 6",                                                               
                        "Choice 7",                                                               
                        "Choice 8",                                                               
                        "Choice 9",                                                               
                        "Choice 10",                                                              
                        "Exit",                                                                   
                        (char *)NULL,                                                             
                  };                                                                              
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); 
                                                                                                  
int main()                                                                                        
{       ITEM **my_items;                                                                          
        int c;                                                                                    
        MENU *my_menu;                                                                            
        WINDOW *my_menu_win;                                                                      
        int n_choices, i;                                                                         
                                                                                                  
        /* Initialize curses */                                                                   
        initscr();                                                                                
        start_color();                                                                            
        cbreak();                                                                                 
        noecho();                                                                                 
        keypad(stdscr, TRUE);                                                                     
        init_pair(1, COLOR_RED, COLOR_BLACK);                                                     
        init_pair(2, COLOR_CYAN, COLOR_BLACK);                                                    
                                                                                                  
        /* Create items */                                                                        
        n_choices = ARRAY_SIZE(choices);                                                          
        my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));                                    
        for(i = 0; i < n_choices; ++i)                                                            
                my_items[i] = new_item(choices[i], choices[i]);                                   
                                                                                                  
        /* Crate menu */                                                                          
        my_menu = new_menu((ITEM **)my_items);                                                    
                                                                                                  
        /* Create the window to be associated with the menu */                                    
        my_menu_win = newwin(10, 40, 4, 4);                                                       
        keypad(my_menu_win, TRUE);                                                                
                                                                                                  
        /* Set main window and sub window */                                                      
        set_menu_win(my_menu, my_menu_win);                                                       
        set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));                                  
        set_menu_format(my_menu, 5, 1);                                                           
                                                                                                  
        /* Set menu mark to the string " * " */                                                   
        set_menu_mark(my_menu, " * ");                                                            
                                                                                                  
        /* Print a border around the main window and print a title */                             
        box(my_menu_win, 0, 0);                                                                   
        print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));                         
        mvwaddch(my_menu_win, 2, 0, ACS_LTEE);                                                    
        mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);                                               
        mvwaddch(my_menu_win, 2, 39, ACS_RTEE);                                                   
                                                                                                  
        /* Post the menu */                                                                       
        post_menu(my_menu);                                                                       
        wrefresh(my_menu_win);                                                                    
                                                                                                  
        attron(COLOR_PAIR(2));                                                                    
        mvprintw(LINES - 2, 0, "Use PageUp and PageDown to scoll down or up a page of items");    
        mvprintw(LINES - 1, 0, "Arrow Keys to navigate (F1 to Exit)");                            
        attroff(COLOR_PAIR(2));                                                                   
        refresh();                                                                                
                                                                                                  
        while((c = wgetch(my_menu_win)) != KEY_F(1))                                              
        {       switch(c)                                                                         
                {       case KEY_DOWN:                                                            
                                menu_driver(my_menu, REQ_DOWN_ITEM);                              
                                break;                                                            
                        case KEY_UP:                                                              
                                menu_driver(my_menu, REQ_UP_ITEM);                                
                                break;                                                            
                        case KEY_NPAGE:                                                           
                                menu_driver(my_menu, REQ_SCR_DPAGE);                              
                                break;                                                            
                        case KEY_PPAGE:                                                           
                                menu_driver(my_menu, REQ_SCR_UPAGE);                              
                                break;                                                            
                }                                                                                 
                wrefresh(my_menu_win);                                                            
        }                                                                                         
                                                                                                  
        /* Unpost and free all the memory taken up */                                             
        unpost_menu(my_menu);                                                                     
        free_menu(my_menu);                                                                       
        for(i = 0; i < n_choices; ++i)                                                            
                free_item(my_items[i]);                                                           
        endwin();                                                                                 
}                                                                                                 
                                                                                                  
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)  
{       int length, x, y;                                                                         
        float temp;                                                                               
                                                                                                  
        if(win == NULL)                                                                           
                win = stdscr;                                                                     
        getyx(win, y, x);                                                                         
        if(startx != 0)                                                                           
                x = startx;                                                                       
        if(starty != 0)                                                                           
                y = starty;                                                                       
        if(width == 0)                                                                            
                width = 80;                                                                       
                                                                                                  
        length = strlen(string);                                                                  
        temp = (width - length)/ 2;                                                               
        x = startx + (int)temp;                                                                   
        wattron(win, color);                                                                      
        mvwprintw(win, y, x, "%s", string);                                                       
        wattroff(win, color);                                                                     
        refresh();                                                                                
}                                                                                                 


이 프로그램은 따로 설명할 필요가 없어 보인다. 이 예제에서 선택의 갯수가 하위 윈도우가 감당할 수 있는 6개보다 많은 10개로 늘어났다. 이것은 set_menu_format() 함수를 통해 메뉴 시스템에 명확히 해둘 필요가 있다. 여기서 우리는 한 페이지에 표시되길 원하는 행과 열의 수를 명시한다. 이 열의 값에는 하위 윈도우의 높이보다 작다면 어떤값이 와도 무방하다. 또 만약 사용자가 누른 키가 PAGE UP 이나 PAGE DOWN 이면 menu_driver() 에 주어진 요청 (REQ_SCR_DPAGE 와 REQ_SCR_UPAGE) 에 따라 메뉴가 페이지별로 스크롤 된다.


1.17.6 여러 열을 가진 메뉴


위 예제에서 set_menu_format() 함수를 어떻게 쓰는지 알 수 있었을 것이다. 나는 cols 변수 (세번째 인자) 가 무엇을 하는지에 대해서는 언급하지 않았다. 음, 만약 당신의 하위 윈도우가 충분히 넓다면 한 열에 하나 이상의 아이템을 표시하도록 선택할 수 있다. 이 값이 cols 변수에 명시된다. 간단히 하기위해 다음예제에서는 아이템에 대한 설명은 보여주지 않도록 했다.

Example 21. Milt Columnar Menus Example

#include <curses.h>                                                                      
#include <menu.h>                                                                        
                                                                                         
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))                                         
#define CTRLD   4                                                                        
                                                                                         
char *choices[] = {                                                                      
                        "Choice 1", "Choice 2", "Choice 3", "Choice 4", "Choice 5",      
                        "Choice 6", "Choice 7", "Choice 8", "Choice 9", "Choice 10",     
                        "Choice 11", "Choice 12", "Choice 13", "Choice 14", "Choice 15", 
                        "Choice 16", "Choice 17", "Choice 18", "Choice 19", "Choice 20", 
                        "Exit",                                                          
                        (char *)NULL,                                                    
                  };                                                                     
                                                                                         
int main()                                                                               
{       ITEM **my_items;                                                                 
        int c;                                                                           
        MENU *my_menu;                                                                   
        WINDOW *my_menu_win;                                                             
        int n_choices, i;                                                                
                                                                                         
        /* Initialize curses */                                                          
        initscr();                                                                       
        start_color();                                                                   
        cbreak();                                                                        
        noecho();                                                                        
        keypad(stdscr, TRUE);                                                            
        init_pair(1, COLOR_RED, COLOR_BLACK);                                            
        init_pair(2, COLOR_CYAN, COLOR_BLACK);                                           
                                                                                         
        /* Create items */                                                               
        n_choices = ARRAY_SIZE(choices);                                                 
        my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));                           
        for(i = 0; i < n_choices; ++i)                                                   
                my_items[i] = new_item(choices[i], choices[i]);                          
                                                                                         
        /* Crate menu */                                                                 
        my_menu = new_menu((ITEM **)my_items);                                           
                                                                                         
        /* Set menu option not to show the description */                                
        menu_opts_off(my_menu, O_SHOWDESC);                                              
                                                                                         
        /* Create the window to be associated with the menu */                           
        my_menu_win = newwin(10, 70, 4, 4);                                              
        keypad(my_menu_win, TRUE);                                                       
                                                                                         
        /* Set main window and sub window */                                             
        set_menu_win(my_menu, my_menu_win);                                              
        set_menu_sub(my_menu, derwin(my_menu_win, 6, 68, 3, 1));                         
        set_menu_format(my_menu, 5, 3);                                                  
        set_menu_mark(my_menu, " * ");                                                   
                                                                                         
        /* Print a border around the main window and print a title */                    
        box(my_menu_win, 0, 0);                                                          
                                                                                         
        attron(COLOR_PAIR(2));                                                           
        mvprintw(LINES - 3, 0, "Use PageUp and PageDown to scroll");                     
        mvprintw(LINES - 2, 0, "Use Arrow Keys to navigate (F1 to Exit)");               
        attroff(COLOR_PAIR(2));                                                          
        refresh();                                                                       
                                                                                         
        /* Post the menu */                                                              
        post_menu(my_menu);                                                              
        wrefresh(my_menu_win);                                                           
                                                                                         
        while((c = wgetch(my_menu_win)) != KEY_F(1))                                     
        {       switch(c)                                                                
                {       case KEY_DOWN:                                                   
                                menu_driver(my_menu, REQ_DOWN_ITEM);                     
                                break;                                                   
                        case KEY_UP:                                                     
                                menu_driver(my_menu, REQ_UP_ITEM);                       
                                break;                                                   
                        case KEY_LEFT:                                                   
                                menu_driver(my_menu, REQ_LEFT_ITEM);                     
                                break;                                                   
                        case KEY_RIGHT:                                                  
                                menu_driver(my_menu, REQ_RIGHT_ITEM);                    
                                break;                                                   
                        case KEY_NPAGE:                                                  
                                menu_driver(my_menu, REQ_SCR_DPAGE);                     
                                break;                                                   
                        case KEY_PPAGE:                                                  
                                menu_driver(my_menu, REQ_SCR_UPAGE);                     
                                break;                                                   
                }                                                                        
                wrefresh(my_menu_win);                                                   
        }                                                                                
                                                                                         
        /* Unpost and free all the memory taken up */                                    
        unpost_menu(my_menu);                                                            
        free_menu(my_menu);                                                              
        for(i = 0; i < n_choices; ++i)                                                   
                free_item(my_items[i]);                                                  
        endwin();                                                                        
}                                                                                        


set_menu_format() 호출을 자세히 보라. 거기에는 컬럼의 수가 3이라고 명시했다. 즉 한 열에 3개의 아이템을 찍는다. 또 우리는 이미 menu_opts_off() 함수를 통해 아이템 상세 설명을 보여주는 기능은 껐다. set_menu_opts(), menu_opts_on() 과 menu_opts() 등의 메뉴 옵션을 조절하는 데 쓰이는 함수들이 좀 더 있다. 다음의 메뉴 옵션이 설정 가능하다.

#!
        O_ONEVALUE                                                            
            Only one item can be selected for this menu.                     
                                                                             
       O_SHOWDESC                                                            
            Display  the  item  descriptions  when  the  menu  is            
            posted.                                                          
                                                                             
       O_ROWMAJOR                                                            
            Display the menu in row-major order.                             
                                                                             
       O_IGNORECASE                                                          
            Ignore the case when pattern-matching.                           
                                                                             
       O_SHOWMATCH                                                           
            Move the cursor to within the item  name  while  pat­            
            tern-matching.                                                   
                                                                             
       O_NONCYCLIC                                                           
            Don't   wrap   around  next-item  and  previous-item,            
            requests to the other end of the menu.                           


기본적으로 모든 옵션이 켜져 있다. menu_opts_on() 과 menu_opts_off() 함수를 통해 각각의 속성들을 끄고 켤 수 있다. 또한 set_menu_opts() 함수를 이용해 직접적으로 옵션을 설정할 수 있다. 이 함수의 인자는 위 상수들의 몇개를 OR 연산 한 값이다. menu_opts() 함수는 메뉴의 현재 옵션 상태를 알아오는 데 쓴다.


1.17.7 여러 값을 가진 메뉴


당신은 아마 O_ONEVALUE 옵션을 끄면 어떻게 되는지 궁금했을 것이다. 그것을 끄면 메뉴가 multi-valued 가 된다. 이 말은 당신이 하나 이상의 아이템을 선택할 수 있다는 말이다. 여기서 REQ_TOGGLE_ITEM 요청을 쓰게 된다. 실제 작동법을 살펴보자.

Example 22. Multi Valued Menus example

#include <curses.h>                                                                    
#include <menu.h>                                                                      
                                                                                       
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))                                       
#define CTRLD   4                                                                      
                                                                                       
char *choices[] = {                                                                    
                        "Choice 1",                                                    
                        "Choice 2",                                                    
                        "Choice 3",                                                    
                        "Choice 4",                                                    
                        "Choice 5",                                                    
                        "Choice 6",                                                    
                        "Choice 7",                                                    
                        "Exit",                                                        
                  };                                                                   
                                                                                       
int main()                                                                             
{       ITEM **my_items;                                                               
        int c;                                                                         
        MENU *my_menu;                                                                 
        int n_choices, i;                                                              
        ITEM *cur_item;                                                                
                                                                                       
        /* Initialize curses */                                                        
        initscr();                                                                     
        cbreak();                                                                      
        noecho();                                                                      
        keypad(stdscr, TRUE);                                                          
                                                                                       
        /* Initialize items */                                                         
        n_choices = ARRAY_SIZE(choices);                                               
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));                     
        for(i = 0; i < n_choices; ++i)                                                 
                my_items[i] = new_item(choices[i], choices[i]);                        
        my_items[n_choices] = (ITEM *)NULL;                                            
                                                                                       
        my_menu = new_menu((ITEM **)my_items);                                         
                                                                                       
        /* Make the menu multi valued */                                               
        menu_opts_off(my_menu, O_ONEVALUE);                                            
                                                                                       
        mvprintw(LINES - 3, 0, "Use <SPACE> to select or unselect an item.");          
        mvprintw(LINES - 2, 0, "<ENTER> to see presently selected items(F1 to Exit)"); 
        post_menu(my_menu);                                                            
        refresh();                                                                     
                                                                                       
        while((c = getch()) != KEY_F(1))                                               
        {       switch(c)                                                              
                {       case KEY_DOWN:                                                 
                                menu_driver(my_menu, REQ_DOWN_ITEM);                   
                                break;                                                 
                        case KEY_UP:                                                   
                                menu_driver(my_menu, REQ_UP_ITEM);                     
                                break;                                                 
                        case ' ':                                                      
                                menu_driver(my_menu, REQ_TOGGLE_ITEM);                 
                                break;                                                 
                        case 10:        /* Enter */                                    
                        {       char temp[200];                                        
                                ITEM **items;                                          
                                                                                       
                                items = menu_items(my_menu);                           
                                temp[0] = '\0';                                        
                                for(i = 0; i < item_count(my_menu); ++i)               
                                        if(item_value(items[i]) == TRUE)               
                                        {       strcat(temp, item_name(items[i]));     
                                                strcat(temp, " ");                     
                                        }                                              
                                move(20, 0);                                           
                                clrtoeol();                                            
                                mvprintw(20, 0, temp);                                 
                                refresh();                                             
                        }                                                              
                        break;                                                         
                }                                                                      
        }                                                                              
                                                                                       
        free_item(my_items[0]);                                                        
        free_item(my_items[1]);                                                        
        free_menu(my_menu);                                                            
        endwin();                                                                      
}                                                                                      


휴, 처음 보는 함수들이 많을 것이다. 하나하나 살펴보도록 하자. 제일 먼저 REQ_TOGGLE_ITEM 부터다. 여러 값을 가질 수 있는 메뉴에서 사용자는 하나 이상의 아이템을 선택하거나 또는 선택을 취소할 수 있다. REQ_TOGGLE_ITEM 요청은 현재 선택을 변경한다. 이 예제에서는 스페이스가 눌려지면 REQ_TOGGLE_ITEM 요청이 menu_driver 로 보내져서 결과가 만들어진다.

그리고나서 사용자가 <ENTER> 을 누르게 되면 우리는 그가 이때까지 선택했던 아이템들을 보여주게 된다. 먼저 메뉴와 연결된 아이템들을 menu_items() 함수를 통해서 찾아낸다. 그후 그 아이템들의 선택여부를 루프를 돌며 찾게된다. item_value() 함수는 아이템이 선택된것이면 TRUE 를 리턴한다. item_count() 함수는 메뉴에 있는 아이템들의 개수를 리턴한다. 아이템 이름은 item_name() 함수로 찾을 수 있다. 또한 아이템의 설명 역시도 item_description() 함수로 찾을 수 있다.


1.17.8 메뉴 옵션들


아마 여태껏 당신의 메뉴는 여러 기능적인 면에서 좀 찝찝했을 것이다. 이해한다. 당신은 컬러를 원하고 있다!!! 당신은 저 텍스트 모드 [http]도스게임(http://www.jersey.net/~debinjoe/games/)과 비슷한 메뉴를 만들길 원하고 있다. set_menu_fore() 와 set_menu_back() 라는 함수가 선택된 아이템과 그렇지 않은 것의 속성을 변경할 때 쓰인다. 이름에 오해의 소지가 있는데, 이 함수들은 메뉴의 전경색이나 배경색을 변경하는 것이 아니다. 물론 이런 작업들은 무용하지만 말이다.

set_menu_grey() 함수는 메뉴에서 선택 불가능한 아이템들의 표현속성을 설정한다. 여기에는 아이템의 흥미로운 옵션인 단 하나뿐인 O_SELECTABLE 이 등장한다. item_opts_off() 함수로 이 속성을 끌 수 있고 그렇게 되면 그 아이템은 선택 불가능해진다. 저 예쁜 윈도우 메뉴중에 회색으로 처리되어 있는 아이템들과 같다. 이 개념들을 예제와 함께 연습해보자.

Example 23. Menu Options example

#include <menu.h>                                                                  
                                                                                   
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))                                   
#define CTRLD   4                                                                  
                                                                                   
char *choices[] = {                                                                
                        "Choice 1",                                                
                        "Choice 2",                                                
                        "Choice 3",                                                
                        "Choice 4",                                                
                        "Choice 5",                                                
                        "Choice 6",                                                
                        "Choice 7",                                                
                        "Exit",                                                    
                  };                                                               
                                                                                   
int main()                                                                         
{       ITEM **my_items;                                                           
        int c;                                                                     
        MENU *my_menu;                                                             
        int n_choices, i;                                                          
        ITEM *cur_item;                                                            
                                                                                   
        /* Initialize curses */                                                    
        initscr();                                                                 
        start_color();                                                             
        cbreak();                                                                  
        noecho();                                                                  
        keypad(stdscr, TRUE);                                                      
        init_pair(1, COLOR_RED, COLOR_BLACK);                                      
        init_pair(2, COLOR_GREEN, COLOR_BLACK);                                    
        init_pair(3, COLOR_MAGENTA, COLOR_BLACK);                                  
                                                                                   
        /* Initialize items */                                                     
        n_choices = ARRAY_SIZE(choices);                                           
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));                 
        for(i = 0; i < n_choices; ++i)                                             
                my_items[i] = new_item(choices[i], choices[i]);                    
        my_items[n_choices] = (ITEM *)NULL;                                        
        item_opts_off(my_items[3], O_SELECTABLE);                                  
        item_opts_off(my_items[6], O_SELECTABLE);                                  
                                                                                   
        /* Create menu */                                                          
        my_menu = new_menu((ITEM **)my_items);                                     
                                                                                   
        /* Set fore ground and back ground of the menu */                          
        set_menu_fore(my_menu, COLOR_PAIR(1) | A_REVERSE);                         
        set_menu_back(my_menu, COLOR_PAIR(2));                                     
        set_menu_grey(my_menu, COLOR_PAIR(3));                                     
                                                                                   
        /* Post the menu */                                                        
        mvprintw(LINES - 3, 0, "Press <ENTER> to see the option selected");        
        mvprintw(LINES - 2, 0, "Up and Down arrow keys to naviage (F1 to Exit)");  
        post_menu(my_menu);                                                        
        refresh();                                                                 
                                                                                   
        while((c = getch()) != KEY_F(1))                                           
        {       switch(c)                                                          
                {       case KEY_DOWN:                                             
                                menu_driver(my_menu, REQ_DOWN_ITEM);               
                                break;                                             
                        case KEY_UP:                                               
                                menu_driver(my_menu, REQ_UP_ITEM);                 
                                break;                                             
                        case 10: /* Enter */                                       
                                move(20, 0);                                       
                                clrtoeol();                                        
                                mvprintw(20, 0, "Item selected is : %s",           
                                                item_name(current_item(my_menu))); 
                                pos_menu_cursor(my_menu);                          
                                break;                                             
                }                                                                  
        }                                                                          
        unpost_menu(my_menu);                                                      
        for(i = 0; i < n_choices; ++i)                                             
                free_item(my_items[i]);                                            
        free_menu(my_menu);                                                        
        endwin();                                                                  
}                                                                                  




1.17.9 유용한 사용자 포인터


우리는 메뉴의 각각의 아이템에 사용자 포인터를 붙여둘 수 있다. 그것은 패널에서의 그것과 작동하는 방법이 같다. 메뉴 시스템은 여기에 관여하지 않는다. 당신이 저장하고자 하는 무엇이든지 그 안에 저장해 둘 수 있다. 나는 주로 그 메뉴 옵션이 선택되었을 때 (그것이 선택되고 아마 사용자가 <ENTER> 을 눌렀을 때) 실행될 함수를 저장하는 데 쓴다;

Example 24. Menu User Pointer Usage

#include <curses.h>                                                               
#include <menu.h>                                                                 
                                                                                  
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))                                  
#define CTRLD   4                                                                 
                                                                                  
char *choices[] = {                                                               
                        "Choice 1",                                               
                        "Choice 2",                                               
                        "Choice 3",                                               
                        "Choice 4",                                               
                        "Choice 5",                                               
                        "Choice 6",                                               
                        "Choice 7",                                               
                        "Exit",                                                   
                  };                                                              
void func(char *name);                                                            
                                                                                  
int main()                                                                        
{       ITEM **my_items;                                                          
        int c;                                                                    
        MENU *my_menu;                                                            
        int n_choices, i;                                                         
        ITEM *cur_item;                                                           
                                                                                  
        /* Initialize curses */                                                   
        initscr();                                                                
        start_color();                                                            
        cbreak();                                                                 
        noecho();                                                                 
        keypad(stdscr, TRUE);                                                     
        init_pair(1, COLOR_RED, COLOR_BLACK);                                     
        init_pair(2, COLOR_GREEN, COLOR_BLACK);                                   
        init_pair(3, COLOR_MAGENTA, COLOR_BLACK);                                 
                                                                                  
        /* Initialize items */                                                    
        n_choices = ARRAY_SIZE(choices);                                          
        my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));                
        for(i = 0; i < n_choices; ++i)                                            
        {       my_items[i] = new_item(choices[i], choices[i]);                   
                /* Set the user pointer */                                        
                set_item_userptr(my_items[i], func);                              
        }                                                                         
        my_items[n_choices] = (ITEM *)NULL;                                       
                                                                                  
        /* Create menu */                                                         
        my_menu = new_menu((ITEM **)my_items);                                    
                                                                                  
        /* Post the menu */                                                       
        mvprintw(LINES - 3, 0, "Press <ENTER> to see the option selected");       
        mvprintw(LINES - 2, 0, "Up and Down arrow keys to naviage (F1 to Exit)"); 
        post_menu(my_menu);                                                       
        refresh();                                                                
                                                                                  
        while((c = getch()) != KEY_F(1))                                          
        {       switch(c)                                                         
                {       case KEY_DOWN:                                            
                                menu_driver(my_menu, REQ_DOWN_ITEM);              
                                break;                                            
                        case KEY_UP:                                              
                                menu_driver(my_menu, REQ_UP_ITEM);                
                                break;                                            
                        case 10: /* Enter */                                      
                        {       ITEM *cur;                                        
                                void (*p)(char *);                                
                                                                                  
                                cur = current_item(my_menu);                      
                                p = item_userptr(cur);                            
                                p((char *)item_name(cur));                        
                                pos_menu_cursor(my_menu);                         
                                break;                                            
                        }                                                         
                        break;                                                    
                }                                                                 
        }                                                                         
        unpost_menu(my_menu);                                                     
        for(i = 0; i < n_choices; ++i)                                            
                free_item(my_items[i]);                                           
        free_menu(my_menu);                                                       
        endwin();                                                                 
}                                                                                 
                                                                                  
void func(char *name)                                                             
{       move(20, 0);                                                              
        clrtoeol();                                                               
        mvprintw(20, 0, "Item selected is : %s", name);                           
}                                                                                 




1.18 폼 라이브러리


만약 당신이 사용자로부터 웹페이지에서 입력을 받고 다양한 종류의 일을 하는 폼 이라는 것을 이미 본 적이 있다면, 아마 당신은 텍스트 모드화면에서는 그런 폼을 어떻게 만들 수 있는지 궁금할 것이다. 보통의 ncurses 에서 그런 멋진 폼을 만드는 것은 꽤나 어려운 일이다. 폼 라이브러리는 쉽게 폼을 작성하고 유지할 수 있는 기본적인 틀을 제공할려고 노력한다. 그것은 확인을 받거나 필드의 동적 확장등의 아무 많은 다양한 기능(함수들)을 가지고 있다. 그것들을 한번 쭉 살펴보도록 하자.

폼은 필드의 모음이다; 각각의 필드는 레이블(정적인 텍스트) 이거나 데이터가 입력되는 위치들이다. 또한 폼라이브러리는 여러 페이지에 폼을 나누는 함수도 제공한다.


1.18.1 개괄


폼은 메뉴랑 아주 비슷하게 만들어진다. 먼저 폼에 관련된 필드를 new_field() 함수로 생성한다. 물론 당신은 필드에 옵션을 설정하여 아주 예쁘게 만들어 필드가 포커스를 잃기전에 확인되게 하는 등의 일도 할 수 있다. 그 후 필드들을 폼에 결합시키게 된다. 이것을 한 후 폼은 화면에 전달되고 입력을 받을 준비를 하게 된다. menu_driver() 와 비슷한 개념으로 폼은 form_driver() 함수에 의해 다뤄진다. 우리는 form_driver 에 요청을 보내서 특정 필드로 포커스를 옮기거나, 필드의 끝으로 커서를 옮기는 등의 작업을 할 수 있다. 사용자가 필드에 값을 입력하고 확인을 하게 되면 폼은 떼어내고 할당받은 메모리를 해제할 수 있다.

폼 프로그램의 일반적인 흐름은 다음과 같다.

  • curses 를 초기화 한다.

  • new_field() 를 써서 필드를 생성한다. 필드의 넓이 와 높이 및 폼에서의 위치를 명시할 수 있다.

  • new_form() 에 붙여넣을 필드를 명시해서 폼을 생성한다.

  • form_post() 로 폼을 전달하고 화면을 리프레쉬한다.

  • 루프를 돌며 사용자 요청을 처리하고 form_driver 로 form 에 필요한 갱신등을 한다.

  • form_unpost() 폼을 떼어낸다.

  • free_form() 함수로 폼에 할당된 메모리를 해제한다.

  • free_field() 함수로 아이템에 할당된 메모리를 해제한다.

  • curses 를 종료한다.

보다시피 폼 라이브러리를 쓰는 것은 메뉴 라이브러리를 다루는 것과 유사하다. 다음의 예제는 폼 처리의 다양한 면들을 보여줄 것이다. 먼저 간단한 예제로 여행을 시작해보자.


1.18.2 폼 라이브러리와 컴파일 하기


폼라이브러리 함수를 쓰려면, form.h 를 인클루드 하고 -lncurses 뒤에 -lform 플래그를 붙여서 프로그램을 링크시켜야 한다.

#!
     #include <form.h>                                                        
    .                                                                        
    .                                                                        
    .                                                                        
                                                                             
    compile and link: gcc <program file> -lform -lncurses                    


Example 25. Forms Basics

#include <form.h>                                                                      
                                                                                       
int main()                                                                             
{       FIELD *field[3];                                                               
        FORM  *my_form;                                                                
        int ch;                                                                        
                                                                                       
        /* Initialize curses */                                                        
        initscr();                                                                     
        cbreak();                                                                      
        noecho();                                                                      
        keypad(stdscr, TRUE);                                                          
                                                                                       
        /* Initialize the fields */                                                    
        field[0] = new_field(1, 10, 4, 18, 0, 0);                                      
        field[1] = new_field(1, 10, 6, 18, 0, 0);                                      
        field[2] = NULL;                                                               
                                                                                       
        /* Set field options */                                                        
        set_field_back(field[0], A_UNDERLINE);  /* Print a line for the option  */     
        field_opts_off(field[0], O_AUTOSKIP);   /* Don't go to next field when this */ 
                                                /* Field is filled up           */     
        set_field_back(field[1], A_UNDERLINE);                                         
        field_opts_off(field[1], O_AUTOSKIP);                                          
                                                                                       
        /* Create the form and post it */                                              
        my_form = new_form(field);                                                     
        post_form(my_form);                                                            
        refresh();                                                                     
                                                                                       
        mvprintw(4, 10, "Value 1:");                                                   
        mvprintw(6, 10, "Value 2:");                                                   
        refresh();                                                                     
                                                                                       
        /* Loop through to get user requests */                                        
        while((ch = getch()) != KEY_F(1))                                              
        {       switch(ch)                                                             
                {       case KEY_DOWN:                                                 
                                /* Go to next field */                                 
                                form_driver(my_form, REQ_NEXT_FIELD);                  
                                /* Go to the end of the present buffer */              
                                /* Leaves nicely at the last character */              
                                form_driver(my_form, REQ_END_LINE);                    
                                break;                                                 
                        case KEY_UP:                                                   
                                /* Go to previous field */                             
                                form_driver(my_form, REQ_PREV_FIELD);                  
                                form_driver(my_form, REQ_END_LINE);                    
                                break;                                                 
                        default:                                                       
                                /* If this is a normal character, it gets */           
                                /* Printed                                */           
                                form_driver(my_form, ch);                              
                                break;                                                 
                }                                                                      
        }                                                                              
                                                                                       
        /* Un post form and free the memory */                                         
        unpost_form(my_form);                                                          
        free_form(my_form);                                                            
        free_field(field[0]);                                                          
        free_field(field[1]);                                                          
                                                                                       
        endwin();                                                                      
        return 0;                                                                      
}                                                                                      


위 예제는 꽤 직설적이다. new_field() 로 두개의 필드를 생성한다. new_field() 에는 높이, 넓이, starty, startx, offscreen 의 줄 수 그리고 추가적인 작업용 버퍼의 수 등이 필요하다. 다섯번째 인자인 offscreen 의 줄 수는 필드의 어느 정도를 보여줄 것인지 결정하는 값이다. 만약 0 이면, 필드의 일부가 화면에 표시되지 않아서 폼이 스크롤되야 되는 경우가 아니면 항상 전체 필드를 보여준다. 폼 라이브러리는 사용자가 입력하는 데이터를 저장하기 위해서 필드당 버퍼를 하나씩 할당한다. new_field() 의 마지막 인자를 명시해서 우리는 몇몇 부가적인 버퍼를 할당해 둘 수 있다. 이 버퍼들은 당신이 원하는 대로 쓰면 된다.

필드를 생성한 후, set_field_back() 함수로 두 필드모두 배경 속성을 UNDERLINE 로 맞췄다. AUTOSKIP 옵션은 field_opts_off() 함수로 기능을 껐다. 만약 이 옵션이 켜져 있으면, 활성화된 필드가 완전히 값이 채워지면 다음 필드로 자동으로 포커스가 이동하게 된다.

필드를 폼에 결합시키고 난 후, 폼이 전달된다. 여기서 사용자 입력이 while 루프에서 form_driver 에 해당하는 요청을 보내서 처리된다. form_driver() 로 넘어가는 모든 요청 값들은 뒤에 자세히 설명한다.


1.18.3 필드 가지고 놀기


각각의 폼 필드는 많은 속성을 가지고 있다. 그것들은 원하는 효과와 재밌게 만들기 위해 조작해 볼 수 있다 !!!. 뭘 주저하나?


1.18.3.1 필드의 위치와 크기 가져오기


필드 생성시에 넘겨준 인자는 field_info() 함수로 다시 받아올 수 있다. 그것은 생성시 주어진 높이, 넓이, starty, startx, 화면에 보이지않는 칸 수, 그리고 부가 버퍼의 수등을 리턴한다. new_field() 의 반대라고 보면 된다.

int field_info(     FIELD *field,              /* field from which to fetch */ 
                    int *height, *int width,   /* field size */                
                    int *top, int *left,       /* upper left corner */         
                    int *offscreen,            /* number of offscreen rows */  
                    int *nbuf);                /* number of working buffers */ 




1.18.3.2 필드 이동하기


move_field() 함수로 필드를 다른 위치로 옮길 수 있다.

int move_field(    FIELD *field,              /* field to alter */           
                   int top, int left);        /* new upper-left corner */    


보통 바뀐 위치는 field_infor() 함수로 알아낼 수 있다.


1.18.3.3 필드 정렬


set_field_just() 함수를 써서 필드를 정렬할 수 있다.

    int set_field_just(FIELD *field,          /* field to alter */           
               int justmode);         /* mode to set */                      
    int field_just(FIELD *field);          /* fetch justify mode of field */ 


이 함수들에 쓰이고 리턴되는 정렬모드는 다음과 같다. NO_JUSTIFICATION, JUSTIFY_RIGHT, JUSTIFY_LEFT, JUSTIFY_CENTER.


1.18.3.4 필드 표현 속성


위의 예제에서 봤다시피, 필드의 표현 속성은 set)field_fore() 와 set_field_ back() 함수로 설정한다. 이 함수들은 필드의 전경 및 배경 속성을 설정한다. 또한 필드의 여백 부분을 채울 패드 문자를 명시할 수도 있다. 패드 문자는 set_field_pad() 함수로 설정한다. 기본 패드 문자는 스페이스 이다. field_fore(), field_back(), field_pad() 함수는 현재 폼의 전경, 배경 속성과 패드 문자를 알아오는데 쓸 수 있다. 다음은 함수 사용법의 리스트 이다.

int set_field_fore(FIELD *field,        /* field to alter */                 
                   chtype attr);        /* attribute to set */               
                                                                             
chtype field_fore(FIELD *field);        /* field to query */                 
                                        /* returns foreground attribute */   
                                                                             
int set_field_back(FIELD *field,        /* field to alter */                 
                   chtype attr);        /* attribute to set */               
                                                                             
chtype field_back(FIELD *field);        /* field to query */                 
                                        /* returns background attribute */   
                                                                             
int set_field_pad(FIELD *field,         /* field to alter */                 
                  int pad);             /* pad character to set */           
                                                                             
chtype field_pad(FIELD *field);         /* field to query */                 
                                        /* returns present pad character */  


비록 위의 함수들이 꽤 간단해 보이기는 하지만 set_field_fore() 를 이용해서 컬러를 사용하는 것은 처음엔 좀 헷갈린다. 먼저 필드의 전경 및 배경 속성에 대해서 설명을 해보겠다. 전경 속성은 문자와 관련이 있다. 즉 필드의 글자가 set_field_fore() 에서 지정한 속성대로 보인다는 말이다. 배경 속성은 글자가 있든 말든 상관 없이 필드의 배경을 채우는 것과 관련이 있다. 자그럼 색상은 어떡하는가? 색상은 색상짝에 항상 정의되어 있다. 그렇다면 색이 있는 필드를 표현하는 제대로된 방법은 무엇인가? 여기 컬러 속성에 관해 명확히 하는 예제가 있다.

Example 26. Form Attributes example

#include <form.h>                                                                        
                                                                                         
int main()                                                                               
{       FIELD *field[3];                                                                 
        FORM  *my_form;                                                                  
        int ch;                                                                          
                                                                                         
        /* Initialize curses */                                                          
        initscr();                                                                       
        start_color();                                                                   
        cbreak();                                                                        
        noecho();                                                                        
        keypad(stdscr, TRUE);                                                            
                                                                                         
        /* Initialize few color pairs */                                                 
        init_pair(1, COLOR_WHITE, COLOR_BLUE);                                           
        init_pair(2, COLOR_WHITE, COLOR_BLUE);                                           
                                                                                         
        /* Initialize the fields */                                                      
        field[0] = new_field(1, 10, 4, 18, 0, 0);                                        
        field[1] = new_field(1, 10, 6, 18, 0, 0);                                        
        field[2] = NULL;                                                                 
                                                                                         
        /* Set field options */                                                          
        set_field_fore(field[0], COLOR_PAIR(1));/* Put the field with blue background */ 
        set_field_back(field[0], COLOR_PAIR(2));/* and white foreground (characters */   
                                                /* are printed in white         */       
        field_opts_off(field[0], O_AUTOSKIP);   /* Don't go to next field when this */   
                                                /* Field is filled up           */       
        set_field_back(field[1], A_UNDERLINE);                                           
        field_opts_off(field[1], O_AUTOSKIP);                                            
                                                                                         
        /* Create the form and post it */                                                
        my_form = new_form(field);                                                       
        post_form(my_form);                                                              
        refresh();                                                                       
                                                                                         
        set_current_field(my_form, field[0]); /* Set focus to the colored field */       
        mvprintw(4, 10, "Value 1:");                                                     
        mvprintw(6, 10, "Value 2:");                                                     
        mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields");      
        refresh();                                                                       
                                                                                         
        /* Loop through to get user requests */                                          
        while((ch = getch()) != KEY_F(1))                                                
        {       switch(ch)                                                               
                {       case KEY_DOWN:                                                   
                                /* Go to next field */                                   
                                form_driver(my_form, REQ_NEXT_FIELD);                    
                                /* Go to the end of the present buffer */                
                                /* Leaves nicely at the last character */                
                                form_driver(my_form, REQ_END_LINE);                      
                                break;                                                   
                        case KEY_UP:                                                     
                                /* Go to previous field */                               
                                form_driver(my_form, REQ_PREV_FIELD);                    
                                form_driver(my_form, REQ_END_LINE);                      
                                break;                                                   
                        default:                                                         
                                /* If this is a normal character, it gets */             
                                /* Printed                                */             
                                form_driver(my_form, ch);                                
                                break;                                                   
                }                                                                        
        }                                                                                
                                                                                         
        /* Un post form and free the memory */                                           
        unpost_form(my_form);                                                            
        free_form(my_form);                                                              
        free_field(field[0]);                                                            
        free_field(field[1]);                                                            
                                                                                         
        endwin();                                                                        
        return 0;                                                                        
}                                                                                        


색상짝을 가지고 이리저리 해보면서 전경과 배경 속성에 대해서 이해하도록 노력해보자. 컬러 속성을 쓰는 내 프로그램에서 나는 보통 배경을 set_field_back() 함수를 통해 설정했다. curses 에서는 단순히 개별 컬러 속성만 정의하는 건 허용하지 않는다.


1.18.3.5 필드 옵션 비트


물론 폼처리의 다양한 면을 제어하기 위한 많은 필드 옵션 비트들이 있다. 그것들은 다음의 함수로 조작 가능하다:

int set_field_opts(FIELD *field,          /* field to alter */               
                   int attr);             /* attribute to set */             
                                                                             
int field_opts_on(FIELD *field,           /* field to alter */               
                  int attr);              /* attributes to turn on */        
                                                                             
int field_opts_off(FIELD *field,          /* field to alter */               
                  int attr);              /* attributes to turn off */       
                                                                             
int field_opts(FIELD *field);             /* field to query */               


set_field_opts() 함수는 필드의 속성을 직접적으로 설정할때 쓰인다. 또 field_opts_on() 과 field_opts_off() 함수를 이용해 몇몇 속성들을 선택적으로 껐다 켰다 할 수 있다. 언제든지 field_opts() 함수로 필드의 속성을 얻어올수도 있다. 다음은 가능한 옵션들의 리스트이다. 기본적으로 모든 옵션들을 켜져있다.

O_VISIBLE

필드가 화면에 표시되는지 결정한다. 폼처리를 하는 동안 상위 필드의 값에 따라 필드를 숨기거나 나타나게 하는데 쓰인다.

O_ACTIVE

폼 처리중 (예를들어 폼 탐색키로 들어왔을때) 필드가 활성화되는지를 결정한다. 레이블을 만들거나, 사용자가 아니 폼 어플리케이션에 의해 버퍼의 값이 변경될 수 있는 필드를 골라낼 때 쓸 수 있다.

O_PUBLIC

필드에 값을 입력할 때 데이터가 화면에 표시될지를 결정한다. 만약 필드의 이 옵션이 꺼져 있으면, 라이브러리가 이 필드에서 값을 받아들이고 데이터를 수정하지만 화면에는 표시되지 않고 커서도 이동하지 않는다. 패스워드 필드등을 정의할 때 O_PUBLIC 옵션을 꺼두고 할 수 있다.

O_EDIT

필드의 데이터가 수정될 수 있는지 결정한다. 이 옵션이 꺼져 있으면 REQ_PREV_CHOICE 와 REQ_NEXT_CHOICE 를 제외한 모든 에디팅 요청은 실패할 것이다. 도움말 등의 읽기 전용 필드에 유용할 것이다.

O_WRAP

여러 줄이 있는 필드에서 word-wrapping 을 할지 결정한다. 보통 (공백으로 구분된) 단어의 어떤 문자가 현재 줄의 맨 끝에 걸리면, 전체 단어가 (다음 줄이 있다고 가정하고) 다음줄로 내려가게 된다. 이 옵션이 꺼져 있으면, 줄 바뀌는 곳에서 단어가 쪼개질 것이다.

O_BLANK

필드를 비우는 걸 결정한다. 이 옵션이 켜져 있으면, 필드의 첫번째 위치에 글자를 입력하는 순간 (방금 입력한 문자를 제외하고) 필드의 내용을 지우게 된다.

O_AUTOSKIP

한 필드가 내용이 다 차면 다음 필드로 자동으로 넘어갈지를 결정한다. 보통 폼 사용자가 거기에 맞는 데이터 보다 더 많은 데이터를 입력하고자 했다면, 수정 위치가 다음 필드로 점프하게 된다. 이 옵션이 꺼져 있으면, 사용자의 커서가 필드의 끝에 계속 남아있게 된다. 이 옵션은 크기 제한이 없는 동적 필드에서는 무시된다.

O_NULLOK

비어있는 필드에 대해서도 인증을 할지 결정한다. 보통 이 옵션을 켜지 않는다; 사용자가 필드를 나갈때 일반적인 확인요청을 하지 않기 때문에 필드를 비워놓을 수가 있다. 만약 필드의 이 옵션이 꺼져 있으면, 필드에서 나갈 시 확인 작업이 이루어지게 된다.

O_PASSOK

모든 필드에서 떠날때 마다 확인을 할지 아니면 그 필드가 수정되었을 때만 확인작업을 할지 결정한다. 보통 후자로 설정되어 있다. 폼 처리중 필드의 확인 함수가 변경될 수 있다면 O_PASSOK 를 설정해 놓는 것이 유용할 것이다.

O_STATIC

필드가 그것의 초기 크기로 고정되어 있을지 결정한다. 이 옵션을 꺼두게 되면, 필드가 동적으로 바뀌게 되고 입력한 데이터에 맞게 크기를 늘린다.

필드가 선택되어 있는 동안은 필드의 옵션을 바꿀 수 없다. 그러나, 현재 선택되지 않은 필드면 포스트되었더라도 옵션을 바꿀 수 있다.

옵션의 값들은 bit-mask 이고, OR 논리연산을 통해 쉽게 구성할 수 있다. O_AUTOSKIP 옵션을 끄는 방법을 봤을 것이다. 다음 예제는 몇몇 더 많은 옵션들의 사용을 잘 보여준다. 다른 옵션들은 적절할때 설명하도록 하겠다.

Example 27. Field Options Usage example

#include <form.h>                                                                    
                                                                                     
#define STARTX 15                                                                    
#define STARTY 4                                                                     
#define WIDTH 25                                                                     
                                                                                     
#define N_FIELDS 3                                                                   
                                                                                     
int main()                                                                           
{       FIELD *field[N_FIELDS];                                                      
        FORM  *my_form;                                                              
        int ch, i;                                                                   
                                                                                     
        /* Initialize curses */                                                      
        initscr();                                                                   
        cbreak();                                                                    
        noecho();                                                                    
        keypad(stdscr, TRUE);                                                        
                                                                                     
        /* Initialize the fields */                                                  
        for(i = 0; i < N_FIELDS - 1; ++i)                                            
                field[i] = new_field(1, WIDTH, STARTY + i * 2, STARTX, 0, 0);        
        field[N_FIELDS - 1] = NULL;                                                  
                                                                                     
        /* Set field options */                                                      
        set_field_back(field[1], A_UNDERLINE);  /* Print a line for the option  */   
                                                                                     
        field_opts_off(field[0], O_ACTIVE); /* This field is a static label */       
        field_opts_off(field[1], O_PUBLIC); /* This filed is like a password field*/ 
        field_opts_off(field[1], O_AUTOSKIP); /* To avoid entering the same field */ 
                                              /* after last character is entered */  
                                                                                     
        /* Create the form and post it */                                            
        my_form = new_form(field);                                                   
        post_form(my_form);                                                          
        refresh();                                                                   
                                                                                     
        set_field_just(field[0], JUSTIFY_CENTER); /* Center Justification */         
        set_field_buffer(field[0], 0, "This is a static Field");                     
                                                  /* Initialize the field  */        
        mvprintw(STARTY, STARTX - 10, "Field 1:");                                   
        mvprintw(STARTY + 2, STARTX - 10, "Field 2:");                               
        refresh();                                                                   
                                                                                     
        /* Loop through to get user requests */                                      
        while((ch = getch()) != KEY_F(1))                                            
        {       switch(ch)                                                           
                {       case KEY_DOWN:                                               
                                /* Go to next field */                               
                                form_driver(my_form, REQ_NEXT_FIELD);                
                                /* Go to the end of the present buffer */            
                                /* Leaves nicely at the last character */            
                                form_driver(my_form, REQ_END_LINE);                  
                                break;                                               
                        case KEY_UP:                                                 
                                /* Go to previous field */                           
                                form_driver(my_form, REQ_PREV_FIELD);                
                                form_driver(my_form, REQ_END_LINE);                  
                                break;                                               
                        default:                                                     
                                /* If this is a normal character, it gets */         
                                /* Printed                                */         
                                form_driver(my_form, ch);                            
                                break;                                               
                }                                                                    
        }                                                                            
                                                                                     
        /* Un post form and free the memory */                                       
        unpost_form(my_form);                                                        
        free_form(my_form);                                                          
        free_field(field[0]);                                                        
        free_field(field[1]);                                                        
                                                                                     
        endwin();                                                                    
        return 0;                                                                    
}                                                                                    


이 예제는 비록 그다지 쓸모 있어보이지는 않지만 여러 옵션의 사용법을 보여주고 있다. 적절히만 쓰게되면 폼에서 정보를 매우 효과적으로 표현할 수 있다. 두번째 필드는 O_PUBLIC 옵션을 껐는데, 이렇게 되면 당신이 타이핑 하는 글자들을 보여주지 않게 된다.


1.18.3.6 필드 상태


필드 상태에는 필드가 수정되었는지 아닌지를 명시해 둔다. 초기값은 FALSE로 되어 있고 사용자가 무엇가를 입력해서 데이터 버퍼가 수정되면 TRUE 로 바뀌게 된다. 그러므로 필드의 상태는 그것이 수정되었는지 아닌지 알아내는데 쓸 수 있다. 다음의 함수들이 그 기능들을 지원한다.

int set_field_status(FIELD *field,      /* field to alter */                 
                   int status);         /* status to set */                  
                                                                             
int field_status(FIELD *field);         /* fetch status of field */          


필드를 떠날때 데이터버퍼는 아직 갱신되지 않은 상태에서 인증을 하기 전에 필드의 상태를 체크해 보는 것이 좋을 것이다. 제대로된 상태가 리턴되는지 확증해볼려면 field_status() 함수를 (1) 필드의 인증확인 루틴을 나갈때 (2) 필드 또는 폼의 초기화나 종료 후크 등에서, (3) forms driver 에 REQ_VALIDATION 요청이 들어온 직 후에 호출을 해보아라.


1.18.3.7 필드 사용자 포인터


모든 필드 구조체는 사용자가 임의의 목적으로 쓸 수 있게 포인터 하나씩을 포함하고 있다. 이것은 폼 라이브러리에 의해서 사용되지 않으며 단지 사용자의 목적으로만 쓰여진다. 다음의 함수는 사용자 포인터를 설정하고 가져온다.

int set_field_userptr(FIELD *field,                                          
           char *userptr);      /* the user pointer you wish to associate */ 
                                /* with the field    */                      
                                                                             
char *field_userptr(FIELD *field);      /* fetch user pointer of the field */




1.18.3.8 크기가 변하는 필드


만약 당신이 동적으로 크기가 변하는 필드를 원하고 있다면, 이 기능이 바로 바라던 그것이다. 이것은 사용자로 하여금 필드의 원래 크기보다 더 많은 데이터를 입력하게 하며 필드 크기가 커지게 한다. 필드의 방침에 따르면 새로운 데이터 크기에 맞춰서 수평 또는 수직으로 스크롤이 될 것이다.

필드를 동적으로 키우려면, O_STATIC 옵션이 꺼져있어야 한다. 이 일은 다음을 통해 할 수 있다.

    field_opts_off(field_pointer, O_STATIC);                                 


그러나 보통 필드를 무한히 키울 수 있게 놔두는 것은 그다지 권할만 하지 않다. 필드가 커질 수 있는 최대 길이는 다음 함수를 이용해서 설정할 수 있다.

int set_max_field(FIELD *field,    /* Field on which to operate */           
                  int max_growth); /* maximum growth allowed for the field */


동적으로 커지는 필드에 대한 정보는 다음 함수로 받아온다.

int dynamic_field_info( FIELD *field,     /* Field on which to operate */    
            int   *prows,     /* number of rows will be filled in this */    
            int   *pcols,     /* number of columns will be filled in this*/  
            int   *pmax)      /* maximum allowable growth will be filled */  
                              /* in this */                                  


비록 field_info 함수도 보통처럼 작동하지만, 이 함수를 동적으로 바뀌는 필드의 적절한 속성들을 얻어올 때 쓰는 것은 권할만 하지가 않다.

new_field 라는 라이브러리 루틴을 잠시 떠올려보자; 높이를 1로 설정하고 만든 필드는 한줄 짜리 필드로 정의된다. 높이가 1 이상인 필드는 여러줄을 가진 필드로 정의될 것이다.

O_STATIC 옵션이 꺼진 (동적으로 커질 수 있는 필드) 한줄 짜리 필드는 단 한줄만을 가진다. 그러나 칸 수는 초기 값보다 사용자가 더 많이 입력할 수록 늘어난다. 화면에 나타나는 칸수는 고정되어 있을 것이며 남은 데이터들은 수평으로 스크롤 될 것이다.

O_STATIC 이 꺼진 (동적으로 커질 수 있는 필드) 여러줄의 필드는 고정된 칸을 가지고 있을 것이다. 그러나 줄 수는 초기 값보다 사용자가 더 많이 입력할 수록 늘어난다. 화면에 나타나는 줄수는 고정되어 있을 것이며 남은 데이터들은 수직으로 스크롤 될 것이다.

위 두 문단은 동적으로 커지는 필드의 특성을 꽤 잘 나타내고 있다. 폼 라이브러리의 남은 부분들의 특성은 아래에 기술되어 있다:

  • O_AUTOSKIP 필드 옵션은 O_STATIC 옵션이 꺼져있고 필드가 커질 수 있는 최대길이가 명시되어 있지 않으면 무시될 것이다. 보통은 사용자가 필드의 마지막 문자위치까지 입력하게되면 O_AUTOSKIP 이 REQ_NEXT_FIELD 를 form driver 에 전달하게 된다. 최대 길이값이 없는 동적 필드에서는 마지막 문자위치란 것이 없다. 만약 최대 길이값이 명시된다면, O_AUTOSKIP 옵션은 필드가 최대길이에 도달하면 보통처럼 작동할 것이다.

  • O_STATIC 옵션이 꺼져 있으면 필드 정렬이 무시될 것이다. 보통은 set_field_just 함수를 써서 한줄 필드의 내용을 JUSTIFY_LEFT, JUSTIFY_RIGHT, JUSTIFY_CENTER 할 수 있다. 동적으로 커지는 한줄 필드에서는, 정의에 따라 크기가 커지고 수평으로 스크롤 될 것이다. 그러므로 정렬할 데이터보다 더 많은 데이터를 포함하고 있게 된다. field_just 함수의 리턴 값은 바뀌지 않을 것이다.

  • 만약 O_STATIC 필드 옵션이 꺼져있고 필드가 커질 수 있는 최대길이가 명시되어 있지 않으면, O_NL_OVERLOAD 폼 옵션에 상관없이 폼 드라이버는 REQ_NEW_LINE 에 대해 똑같이 작동할 것이다. 보통은 O_NL_OVERLOAD 폼 옵션이 켜져 있으면, 필드의 마지막 줄에서 호출된 REQ_NEW_LINE 값은 내부적으로 REQ_NEXT_FIELD 를 부르게 된다. 반면 만약 필드가 한계없이 커질 수 있다면, 마지막 줄이라는 것이 없다. 그러므로 REQ_NEW_LINE 는 내부적으로 REQ_NEXT_FIELD 를 호출할 일이 없게 된다. 만약 최대길이값이 명시되어 있고 O_NL_OVERLOAD 폼옵션이 켜져 있으면, REQ_NEW_LINE 는 필드가 최대길이까지 커졌고 사용자가 마지막줄에 있을 때 호출됐을 경우, 내부적으로 REQ_NEXT_FIELD 만을 발생시키게 된다.

  • dup_field 라는 라이브러리 호출은 보통처럼 작동할 것이다; 그것은 필드를 복사한다. 현재 버퍼크기와 필드의 내용까지 모두 말이다. 커질 수 있는 최대길이가 명시되어 있다면 그것 또한 복사될 것이다.

  • link_field 라이브러리 호출도 보통처럼 작동할 것이다; 그것은 모든 필드속성은 복사하고, 원래 필드의 버퍼는 공유할 것이다. 만약 O_STATIC 필드옵션이 필드가 공유하는 버퍼에 의해 결과적으로 값이 변경되었다면, 버퍼보다 더 큰 데이터를 입력할때 시스템이 어떻게 작동하는가는 현재 작업하는 필드의 옵션 설정에 달려있게 된다.

  • field_info 라이브러리 호출도 보통처럼 작동할 것이다; nrow 변수에는 new_field 의 원래 호출때의 값을 가지고 있을 것이다. 위에서 언급했듯이, 현재의 버퍼 크기를 알아올려면 dynamic_field_info 함수를 사용해야만 한다.

위 요점 중 몇몇은 폼 드라이버에 대해 설명을 해야 쉽게 이해될 것이다. 다음 몇 섹션에서 그것에 대해 알아볼 것이다.


1.18.4 폼 윈도우


폼 윈도우는 메뉴 윈도우와 그 개념이 흡사하다. 모든 폼은 메인 윈도우와 하위 윈도우로 구성되어 있다. 폼 메인 윈도우는 타이틀 이나 테두리 또는 개발자가 바라는 아무것이나 표시할 수 있다. 하위 윈도우는 모든 필드를 포함하고 그들의 위치에 따라 그것들을 표시한다. 이것은 예쁜 폼을 매우 쉽게 만들 수 있는 유연함을 제공한다.

이것은 메뉴 윈도우와 매우 흡사하기 때문에, 예제에 그렇게 많은 설명을 하지는 않겠다. 함수들은 비슷하고 같은 방법으로 작동한다.

Example 28. Form Windows Example

#include <form.h>                                                                                 
                                                                                                  
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); 
                                                                                                  
int main()                                                                                        
{                                                                                                 
        FIELD *field[3];                                                                          
        FORM  *my_form;                                                                           
        WINDOW *my_form_win;                                                                      
        int ch, rows, cols;                                                                       
                                                                                                  
        /* Initialize curses */                                                                   
        initscr();                                                                                
        start_color();                                                                            
        cbreak();                                                                                 
        noecho();                                                                                 
        keypad(stdscr, TRUE);                                                                     
                                                                                                  
        /* Initialize few color pairs */                                                          
        init_pair(1, COLOR_RED, COLOR_BLACK);                                                     
                                                                                                  
        /* Initialize the fields */                                                               
        field[0] = new_field(1, 10, 6, 1, 0, 0);                                                  
        field[1] = new_field(1, 10, 8, 1, 0, 0);                                                  
        field[2] = NULL;                                                                          
                                                                                                  
        /* Set field options */                                                                   
        set_field_back(field[0], A_UNDERLINE);                                                    
        field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */              
                                              /* Field is filled up             */                
        set_field_back(field[1], A_UNDERLINE);                                                    
        field_opts_off(field[1], O_AUTOSKIP);                                                     
                                                                                                  
        /* Create the form and post it */                                                         
        my_form = new_form(field);                                                                
                                                                                                  
        /* Calculate the area required for the form */                                            
        scale_form(my_form, &rows, &cols);                                                        
                                                                                                  
        /* Create the window to be associated with the form */                                    
        my_form_win = newwin(rows + 4, cols + 4, 4, 4);                                           
        keypad(my_form_win, TRUE);                                                                
                                                                                                  
        /* Set main window and sub window */                                                      
        set_form_win(my_form, my_form_win);                                                       
        set_form_sub(my_form, derwin(my_form_win, rows, cols, 2, 2));                             
                                                                                                  
        /* Print a border around the main window and print a title */                             
        box(my_form_win, 0, 0);                                                                   
        print_in_middle(my_form_win, 1, 0, cols + 4, "My Form", COLOR_PAIR(1));                   
                                                                                                  
        post_form(my_form);                                                                       
        wrefresh(my_form_win);                                                                    
                                                                                                  
        mvprintw(LINES - 2, 0, "Use UP, DOWN arrow keys to switch between fields");               
        refresh();                                                                                
                                                                                                  
        /* Loop through to get user requests */                                                   
        while((ch = wgetch(my_form_win)) != KEY_F(1))                                             
        {       switch(ch)                                                                        
                {       case KEY_DOWN:                                                            
                                /* Go to next field */                                            
                                form_driver(my_form, REQ_NEXT_FIELD);                             
                                /* Go to the end of the present buffer */                         
                                /* Leaves nicely at the last character */                         
                                form_driver(my_form, REQ_END_LINE);                               
                                break;                                                            
                        case KEY_UP:                                                              
                                /* Go to previous field */                                        
                                form_driver(my_form, REQ_PREV_FIELD);                             
                                form_driver(my_form, REQ_END_LINE);                               
                                break;                                                            
                        default:                                                                  
                                /* If this is a normal character, it gets */                      
                                /* Printed                                */                      
                                form_driver(my_form, ch);                                         
                                break;                                                            
                }                                                                                 
        }                                                                                         
                                                                                                  
        /* Un post form and free the memory */                                                    
        unpost_form(my_form);                                                                     
        free_form(my_form);                                                                       
        free_field(field[0]);                                                                     
        free_field(field[1]);                                                                     
                                                                                                  
        endwin();                                                                                 
        return 0;                                                                                 
}                                                                                                 
                                                                                                  
void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)  
{       int length, x, y;                                                                         
        float temp;                                                                               
                                                                                                  
        if(win == NULL)                                                                           
                win = stdscr;                                                                     
        getyx(win, y, x);                                                                         
        if(startx != 0)                                                                           
                x = startx;                                                                       
        if(starty != 0)                                                                           
                y = starty;                                                                       
        if(width == 0)                                                                            
                width = 80;                                                                       
                                                                                                  
        length = strlen(string);                                                                  
        temp = (width - length)/ 2;                                                               
        x = startx + (int)temp;                                                                   
        wattron(win, color);                                                                      
        mvwprintw(win, y, x, "%s", string);                                                       
        wattroff(win, color);                                                                     
        refresh();                                                                                
}                                                                                                 




1.18.5 필드 인증


기본적으로 필드는 사용자가 입력하는 어떤 데이터든 받아들인다. 이 필드에 인증기능을 넣는 것도 가능하다. 사용자가 넣은 값들은 필드를 떠날때 인증에 맞지 않으면 실패할 것이다. 어떤 인증 형태들은 문자를 가지고 있기도 하다-필드에 글자를 입력할때마다 인증 체크를 한다.

인증은 다음의 함수들을 통해 필드에 덧붙일 수 있다.

int set_field_type(FIELD *field,          /* field to alter */               
                   FIELDTYPE *ftype,      /* type to associate */            
                   ...);                  /* additional arguments*/          


세팅된 후 필드에 대한 인증형태는 다음 함수로 알아올 수 있다.

FIELDTYPE *field_type(FIELD *field);      /* field to query */               


폼 드라이버는 최종사용자에 의해 데이터가 입력되었을 때만 필드의 값을 인증한다. 다음과 같은 경우는 인증이 일어나지 않는다.

  • 어플리케이션 프로그램에서 set_field_buffer 함수를 호출해서 필드의 값을 바꾼 경우

  • 링크되어 있는 필드의 값들이 간접적으로 바뀐 경우 -- 그 필드가 링크되어 있는 원래 필드의 값을 바꿔서 변경된 경우

다음은 미리 정의되어 있는 인증 형태들이다. 당신은 당신만의 인증을 명시할 수도 있다. 비록 좀 까다롭고 성가신 일이겠지만 말이다.

TYPE_ALPHA

이 필드 타입은 알파벳 데이터들만 입력받는다; 빈칸, 숫자, 특수 문자은 안된다(이 글자들은 입력시에 체크된다.). 다음과 같이 설정한다:

int set_field_type(FIELD *field,          /* field to alter */               
                   TYPE_ALPHA,            /* type to associate */            
                   int width);            /* maximum width of field */       


width 인자는 데이터의 최소 길이를 말한다. 사용자는 필드를 떠나기전 최소 width 만큼의 글자를 입력해야만 한다. 전형적으로 당신은 이 값을 필드 길이만큼 설정하길 원할 것이다; 만약 이 값이 필드 길이보다 크다면, 인증 작업이 항상 실패할 것이다. 이 값을 0 로 해두면 이 필드를 채울지 말지는 사용자 선택이다.

TYPE_ALNUM

이 필드 타입은 알파벳과 숫자 데이터들만 입력받는다; 빈칸, 특수문자는 안된다(이 문자들은 입력시에 체크된다.). 다음과 같이 설정한다.

int set_field_type(FIELD *field,          /* field to alter */               
                   TYPE_ALNUM,            /* type to associate */            
                   int width);            /* maximum width of field */       


width 인자는 데이터의 최소 길이를 말한다. TYPE_ALPHA 와 같이 전형적으로 당신은 이것을 필드 길이만큼 원할 것이다; 만약 필드 길이보다 크다면 인증 작업은 항상 실패한다. 이 값을 0 로 해두면 이 필드를 채울지 말지는 사용자 선택이다.

TYPE_ENUM

이 타입은 필드의 입력값을 스트링 값들의 집합중 하나가 되도록 제한한다. (예를 들어, U.S. states 의 두글자 우편번호등). 이것은 다음과 같이 설정한다.

int set_field_type(FIELD *field,          /* field to alter */               
                   TYPE_ENUM,             /* type to associate */            
                   char **valuelist;      /* list of possible values */      
                   int checkcase;         /* case-sensitive? */              
                   int checkunique);      /* must specify uniquely? */       


valuelist 인자는 NULL 로 끝나는 유효한 스트링 리스트를 가르키고 있어야 된다. checkcase 인자는 참이면 스트링 비교를 case-sensitive 하게 만든다.

사용자가 TYPE_ENUM 필드를 나갈때, 인증 절차가 버퍼의 데이터를 확인된 값으로 완성해놓으려 시도한다. 딱맞는 문자열이 입력되면, 당연히 인증을 통과한다. 그러나 또한 인증 문자열의 앞머리만 입력하고 나머지는 자동으로 완성되게 하는 것도 가능하다.

기본적으로 당신이 머리글자만 입력하고 문자열 리스트에서 거기에 맞는 것이 하나 이상일 경우, 첫번째 매칭되는 값으로 완성된다. 그러나 checkunique 인자가 참이면 인증받기 위해서는 머리글자에 매칭되는 것이 유일해야한다.

REQ_NEXT_CHOICE 와 REQ_PREV_CHOICE 입력 요청은 이 필드들에 특히 유용하다.

TYPE_INTEGER

이 필드 타입은 정수를 입력받는다. 그것은 다음과 같이 설정한다.:

int set_field_type(FIELD *field,          /* field to alter */               
                   TYPE_INTEGER,          /* type to associate */            
                   int padding,           /* # places to zero-pad to */      
                   int vmin, int vmax);   /* valid range */                  


인증받을 수 있는 문자는 마이너스 부호로 시작해도 되는 숫자다. 필드에서 나갈 때 범위 체크가 행해진다. 만약 범위의 최대값이 최소값보다 같거나 작다면, 범위는 무시된다.

만약 범위 체크를 통과한다면, 패딩 인자의 값만큼 필요한 양의 숫자 0 이 앞에 덧붙여진다.

TYPE_INTEGER 필드의 버퍼 값은 C 라이브러리함수인 atoi(3) 으로 편리하게 정수로 바뀔 수 있다.

TYPE_NUMERIC

이 필드는 실수들을 받는다. 그것은 다음과 같이 설정한다.:

int set_field_type(FIELD *field,          /* field to alter */               
                   TYPE_NUMERIC,          /* type to associate */            
                   int padding,           /* # places of precision */        
                   int vmin, int vmax);   /* valid range */                  


허용가능한 문자는 마이너스 부호로 시작해도 되는 숫자들이다. 소수점 부호도 포함해도 된다. 필드에서 나갈 때 범위 체크가 실행된다. 범위의 최대값이 최소값보다 작거나 같으면, 범위는 무시된다.

값이 범위 체크를 통과하면, 패딩 인자로 넘어온 수 만큼 필요한 0이 값의 뒤에 덧붙는다.

TYPE_NUMERIC 필드의 버퍼 값은 C 라이브러리함수인 atof(3) 으로 편리하게 실수로 바뀔 수 있다.

TYPE_REGEXP

이 필드는 정규식에 맞는 값을 받는다. 그것은 다음과 같이 설정한다.

int set_field_type(FIELD *field,          /* field to alter */               
                   TYPE_REGEXP,           /* type to associate */            
                   char *regexp);         /* expression to match */          


정규식에 대한 문법은 regcomp(3) 와 같다. 필드에서 나갈 때 정규식에 맞는지 검사를 하게 된다.


1.18.6 폼 드라이버: 폼 시스템의 큰 일꾼


메뉴 시스템에서 처럼 form_driver() 는 폼 시스템에서 매우 중요한 역할을 맡고 있다. 폼 시스템으로 가는 모든 종류의 요청은 form_driver() 를 통해야 한다.

int form_driver(FORM *form,     /* form on which to operate     */           
                int request)    /* form request code         */              


위의 몇몇 예제에서도 보았듯이, 사용자 입력을 처리하는 루프를 돌면서 그서이 필드 데이터 입력인지 아니면 폼 처리 요청인지를 결정해야만 한다. 그 후 폼 처리 요청은 form_driver() 로 전달되어 필요한 일을 하게 된다.

요청은 대략 다음의 분류로 나눠볼 수 있다. 각각의 요청과 사용법은 아래에 설명되어 있다.:


1.18.6.1 페이지 탐색 리퀘스트


이 리퀘스트들은 폼을 통한 페이지 레벨의 이동과 새 폼화면으로 화면을 이동시키는 것들이다. 폼은 여러 페이지로 만들 수 있다. 만약 당신이 많은 필드와 논리적인 영역이 있는 큰 폼을 가지고 있다면, 그 폼들을 페이지로 나눠 볼 수 있을 것이다. set_new_page() 함수가 명시한 필드에서 새페이지를 설정하는 함수이다.

int set_new_page(FIELD *field,/* Field at which page break to be set or unset */ 
         bool new_page_flag); /* should be TRUE to put a break */                


다음 리퀘스트들이 다른 페이지로 이동할때 쓰인다.

  • REQ_NEXT_PAGE Move to the next form page.

  • REQ_PREV_PAGE Move to the previous form page.

  • REQ_FIRST_PAGE Move to the first form page.

  • REQ_LAST_PAGE Move to the last form page.

이 리퀘스트들은 리스트를 순환한다; 즉, 마지막 페이지에서 REQ_NEXT_PAGE 리퀘스트는 첫번째 페이지로 이동하고, 첫번째 페이지에서 REQ_PREV_PAGE 이 리퀘스트는 마지막 페이지로 이동하게 된다.


1.18.6.2 필드간 탐색 리퀘스트


이 리퀘스트들은 같은 페이지내의 필드 사이를 왕래할때 쓰인다.

  • REQ_NEXT_FIELD Move to next field.

  • REQ_PREV_FIELD Move to previous field.

  • REQ_FIRST_FIELD Move to the first field.

  • REQ_LAST_FIELD Move to the last field.

  • REQ_SNEXT_FIELD Move to sorted next field.

  • REQ_SPREV_FIELD Move to sorted previous field.

  • REQ_SFIRST_FIELD Move to the sorted first field.

  • REQ_SLAST_FIELD Move to the sorted last field.

  • REQ_LEFT_FIELD Move left to field.

  • REQ_RIGHT_FIELD Move right to field.

  • REQ_UP_FIELD Move up to field.

  • REQ_DOWN_FIELD Move down to field.

이 리퀘스트들은 한 페이지의 필드들을 순환한다; 즉, 마지막 필드에서의 REQ_NEXT_FIELD 리퀘스트는 맨 처음 필드로 이동하고 맨 처음 필드에서의 REQ_PREV_FIELD 리퀘스트는 맨 마지막 필드로 이동하게 된다. 이 경우 (REQ_FIRST_FIELD 와 REQ_LAST_FIELD 도 마찬가지) 필드의 순서는 그냥 폼 배열 ( new_form() 이나 set_form_fields() 에 의해 설정되는 ) 의 순서이다.

또한 화면에 나와 있는 순서대로 정렬된 것처럼 필드들을 가로지르는 것도 가능하다. 왼쪽에서 오른쪽으로 라든지 위에서 아래로 처럼 말이다. 이것을 할려면, 두번째 그룹인 네개의 정렬된 이동 리퀘스트를 쓰면 된다.

마지막으로 화면에 보이는대로 상하좌우 방향으로 필드간 이동도 가능하다. 이것을 할려면 세번째 그룹의 네 리퀘스트를 쓰면된다. 단 주의해야 할 것은 이 리퀘스트를 쓸때 기본이 되는 폼의 위치는 그것의 왼쪽 위 구석이라는 것이다.

예를 들어, 당신이 어려줄의 필드인 B 를 중심으로 두개의 한줄짜리 필드인 A 와 C를 왼쪽, 오른쪽의 B 와 같은 라인에 뒀다고 하자. A 에서 REQ_MOVE_RIGHT 리퀘스트가 발생하면 A, B, C 의 첫째줄이 같을 때에만 B 로 이동할 것이다; 만약 그렇지 않으면 B를 건너뛰어 C 로 가게된다.


1.18.6.3 필드 내 탐색 리퀘스트


이 리퀘스트는 현재 선택된 필드 내에서 커서를 이동시킨다.

  • REQ_NEXT_CHAR Move to next character.

  • REQ_PREV_CHAR Move to previous character.

  • REQ_NEXT_LINE Move to next line.

  • REQ_PREV_LINE Move to previous line.

  • REQ_NEXT_WORD Move to next word.

  • REQ_PREV_WORD Move to previous word.

  • REQ_BEG_FIELD Move to beginning of field.

  • REQ_END_FIELD Move to end of field.

  • REQ_BEG_LINE Move to beginning of line.

  • REQ_END_LINE Move to end of line.

  • REQ_LEFT_CHAR Move left in field.

  • REQ_RIGHT_CHAR Move right in field.

  • REQ_UP_CHAR Move up in field.

  • REQ_DOWN_CHAR Move down in field.

앞 단어와 뒤 단어는 공백에 의해서 구분된다. 줄이나 필드의 맨 처음과 끝으로 이동하는 명령은 그들의 범위내에서 패딩 문자가 아닌 맨 처음 또는 끝의 글자를 찾는다.


1.18.6.4 스크롤 리퀘스트


필드 중에서 동적으로 크리가 변해서 화면에 보이지 않는 글자들이 있는 것은 스크롤이 된다. 한줄 짜리 필드는 수평으로 스크롤 된다; 여러줄의 필드는 수직으로 된다. 대부분의 스크롤은 에디팅 이동과 필드내 이동에 의해 행해진다. (라이브러리는 커서가 계속 보이도록 필드를 스크롤 한다.) 다음의 리퀘스트를 통해 스크롤기능을 쓸 수 있다.:

  • REQ_SCR_FLINE Scroll vertically forward a line.

  • REQ_SCR_BLINE Scroll vertically backward a line.

  • REQ_SCR_FPAGE Scroll vertically forward a page.

  • REQ_SCR_BPAGE Scroll vertically backward a page.

  • REQ_SCR_FHPAGE Scroll vertically forward half a page.

  • REQ_SCR_BHPAGE Scroll vertically backward half a page.

  • REQ_SCR_FCHAR Scroll horizontally forward a character.

  • REQ_SCR_BCHAR Scroll horizontally backward a character.

  • REQ_SCR_HFLINE Scroll horizontally one field width forward.

  • REQ_SCR_HBLINE Scroll horizontally one field width backward.

  • REQ_SCR_HFHALF Scroll horizontally one half field width forward.

  • REQ_SCR_HBHALF Scroll horizontally one half field width backward.

스크롤 시 필드의 페이지는 그것의 보이는 부분의 높이이다.


1.18.6.5 에디트 리퀘스트


당신이 폼 드라이버에 ASCII 문자를 넘겼을 때, 그것은 필드의 데이터 버퍼에 그 문자를 덧붙여라는 요청으로 처리된다. 이 작업이 추가 모드인지 수정 모드인지는 필드의 에디트 모드에 따라 다르다. (추가 모드가 기본 값이다.)

다음의 리퀘스트가 필드를 수정하고 필드의 에디트 모드를 변경할 때 쓰인다.

  • REQ_INS_MODE Set insertion mode.

  • REQ_OVL_MODE Set overlay mode.

  • REQ_NEW_LINE New line request (see below for explanation).

  • REQ_INS_CHAR Insert space at character location.

  • REQ_INS_LINE Insert blank line at character location.

  • REQ_DEL_CHAR Delete character at cursor.

  • REQ_DEL_PREV Delete previous word at cursor.

  • REQ_DEL_LINE Delete line at cursor.

  • REQ_DEL_WORD Delete word at cursor.

  • REQ_CLR_EOL Clear to end of line.

  • REQ_CLR_EOF Clear to end of field.

  • REQ_CLEAR_FIELD Clear entire field.

REQ_NEW_LINE 와 REQ_DEL_PREV 리퀘스트의 작동법은 아해하기가 쉽지 않고 부분적으로 폼 옵션들의 영향을 받는다. 이 특별한 케이스는 커서가 필드의 시작위치에 있거나 필드의 마지막 줄에 있을 때 일어난다.

먼저, REQ_NEW_LINE 부터 보자:

추가 모드에서 REQ_NEW_LINE 의 작동법은 먼저 현재 줄의 커서가 있는 곳에서 그 뒤 부분들을 새 줄로 내리고, 커서를 새줄의 시작 위치로 이동하는 것이다. ( 당신은 이것이 필드 버퍼에 newline 문자를 집어넣는 것이라는 걸 알아차렸을 것이다.

겹쳐 쓰는 수정모드에서 REQ_NEW_LINE 의 작동법은 커서뒤에서부터 줄의 끝까지 내용을 클리어한다. 그 후 커서를 다음 줄의 시작위치로 옮기게 된다.

그러나, 필드의 시작위치에서나 필드의 마지막 줄에서 REQ_NEW_LINE 는 REQ_NEXT_FIELD 가 대체한다. O_NL_OVERLOAD 옵션이 꺼져 있으면 이 특수 기능은 작동하지 않는다.

자 다음은 REQ_DEL_PREV 에 대해 알아보자:

REQ_DEL_PREV 의 일상적인 기능은 그 전의 문자를 지우는 것이다. 만약 추가 모드이고 커서가 줄의 맨 앞에 있고, 그 줄이 그 앞의 줄에 맞다면, 그 줄을 앞줄의 뒤에 합치고 그 줄을 없애게 된다. (당신은 이것이 필드 버퍼에서 newline 문자를 제거하는 것이란 걸 알아챘을 것이다.)

그러나, 필드의 시작점에서 REQ_DEL_PREV 리퀘스트는 REQ_PREV_FIELD 로 다뤄진다.

만약 O_BS_OVERLOAD 옵션이 꺼져 있으면, 이 특수 기능은 쓰이지 않고 폼 드라이버는 단지 E_REQUEST_DENIED 를 리턴한다.


1.18.6.6 Order Requests


만약 당신의 필드가 정렬된 상태이고 주어진 값의 타입과 같은 다음이나 그전의 값을 읽어올 수 있는 함수가 붙어 있다면, 필드 버퍼로 그 값을 가져올 수 있는 리퀘스트가 있다.

  • REQ_NEXT_CHOICE 버퍼의 현재 값의 다음에 해당하는 값을 가져온다.

  • REQ_PREV_CHOICE 버퍼의 현재 값의 이전에 해당하는 값을 가져온다.

내장된 필드형중에 TYPE_ENUM 만이 이전 값과 다음 값을 가져오는 함수가 미리 내장되어 있다. 만약 당신이 당신만의 필드형 ( Custom Validation 형 참고)을 정의한다면, 당신은 우리가 갖고 있는 순서를 맞춰주는 함수를 붙여 놓을 수 있다.


1.18.6.7 어플리케이션 명령들


폼 리퀘스트들은 KEY_MAX 보다 크고 MAX_COMMAND 보다 작거나 같은 curses 값의 정수로 나타난다. 이 범위 안의 값은 form_driver() 함수에 의해 무시된다. 그러므로 이것은 어플리케이션에 의해 어떤 용도로든 쓸 수 있다. 그것은 어플리케이션의 특정 기능 처럼 다뤄지며 해당 기능을 수행할 수 있다.


1.19 툴과 위젯 라이브러리들


자 이제 당신은 ncurses 와 그 자매 라이브러리들의 기능을 알았으니, 소매를 걷고 화면에 많은 처리를 해야하는 프로젝트를 할 준비를 해야할 것이다. 그러나 잠시 기다려라.. 심지어 더 부가적인 라이브러리가 있다고 해도 보통의 ncurses 에서 복잡한 GUI 위젯들을 만들고 유지한다는 것은 꽤나 어려운 일이 될 것이다. 그래서 여기에 당신이 당신만의 위젯을 만들 필요 없이 미리 만들어 놓은 툴과 위젯들이 몇몇 있다. 당신은 그것들을 써도 되고 그 코드에서 아이디어를 얻을 수도 있고 심지어 그것들을 확장해도 된다.


1.19.1 CDK (Curses Development Kit)


작가의 말 중

CDK 는 'Curses Development Kit' 의 약자이며 그것은 풀 스크린 curses 프로그램을 빨리 개발하기 위한 21 개의 위젯들이 있다.

이 키트는 당신의 프로그램에 바로 쓰일 수 있는 몇몇 유용한 위젯들을 제공한다. 이것은 꽤 잘만들어졌고 문서화가 매우 잘 되었다. examples 디렉토리안의 예제는 초보자에게 좋은 실습의 장이 될 수 있다. CDK 는 [http]http://www.vexus.ca/release/cdk.tar.gz(http://www.vexus.ca/release/cdk.tar.gz) 에서 다운로드 받을 수 있다. 설치를 할려면 README 안의 도움말을 잘 보기 바란다.


1.19.1.1 위젯 리스트


다음은 cdk 에서 제공하는 위젯과 그 설명들이다.

#!
 Widget Type           Quick Description                                      
===========================================================================  
Alphalist             Allows a user to select from a list of words, with     
                      the ability to narrow the search list by typing in a   
                      few characters of the desired word.                    
Buttonbox             This creates a multiple button widget.                 
Calendar              Creates a little simple calendar widget.               
Dialog                Prompts the user with a message, and the user          
                      can pick an answer from the buttons provided.          
Entry                 Allows the user to enter various types of information. 
File Selector         A file selector built from Cdk base widgets. This      
                      example shows how to create more complicated widgets   
                      using the Cdk widget library.                          
Graph                 Draws a graph.                                         
Histogram             Draws a histogram.                                     
Item List             Creates a pop up field which allows the user to select 
                      one of several choices in a small field. Very useful   
                      for things like days of the week or month names.       
Label                 Displays messages in a pop up box, or the label can be 
                      considered part of the screen.                         
Marquee               Displays a message in a scrolling marquee.             
Matrix                Creates a complex matrix with lots of options.         
Menu                  Creates a pull-down menu interface.                    
Multiple Line Entry   A multiple line entry field. Very useful               
                      for long fields. (like a description                   
                      field)                                                 
Radio List            Creates a radio button list.                           
Scale                 Creates a numeric scale. Used for allowing a user to   
                      pick a numeric value and restrict them to a range of   
                      values.                                                
Scrolling List        Creates a scrolling list/menu list.                    
Scrolling Window      Creates a scrolling log file viewer. Can add           
                      information into the window while its running.         
                      A good widget for displaying the progress of           
                      something. (akin to a console window)                  
Selection List        Creates a multiple option selection list.              
Slider                Akin to the scale widget, this widget provides a       
                      visual slide bar to represent the numeric value.       
Template              Creates a entry field with character sensitive         
                      positions. Used for pre-formatted fields like          
                      dates and phone numbers.                               
Viewer                This is a file/information viewer. Very useful         
                      when you need to display loads of information.         
=========================================================================== 




1.19.1.2 몇몇 매력적인 기능들


미리 만들어진 위젯들을 써서 편해진다는 점을 떠나서, cdk 는 여러 색의 문자열과 관련된 당혹스런 문제를 해결해서, 문자들을 멋지게 정렬했다. CDK 함수에서 처리되는 특수 포맷용 태그가 문자열안에 더해졌다. 예를 들어 만약

"</B/1>이 줄은 파란 배경에 노란 글씨로 찍혀야 된다.<!1>"

과 같은 인자가 newCKDLabel() 로 넘어가면, 그것은 이 줄을 파란배경에 노란 글씨로 찍게된다. 또한 스트링을 정렬하거나, 그리기에 쓰이는 특수 문자용의 태그들도 있다. 자세한 것은 cdk_display(3X) 의 맨 페이지를 참고하라. 맨 페이지에는 멋진 예제와 함께 사용법이 나와 있다.


1.19.1.3 결론


모든 면에서 CDK 는 적절히 사용하면 복잡한 GUI개발의 강력한 프레임웍을 만들 수 있는 잘 만들어진 위젯 패키지 이다.


1.19.2 다이얼로그


Long long ago, in September 1994, when few people knew linux, Jeff Tranter wrote an [http]http://www2.linuxjournal.com/lj-issues/issue5/2807.html article on dialog in Linux Journal. He starts the article with these words..

오래전인 1994년 9월에 겨우 몇몇 사람만이 리눅스에 대해 알던 때, Jeff Tranter는 [http]이 글(http://www2.linuxjournal.com/lj-issues/issue5/2807.html)을 Linux Journal 에 썼다. 그 글은 다음과 같이 시작한다..

리눅스는 유닉스에 그 기반을 두고있다. 게다가 유닉스에는 없었던 다양한 응용프로그램과 유일하고 유용한 커널의 기능도 가지고 있다. 잘 알려진 보석 중 하나는 쉘 스크립트 안에서 전문적인 모습의 다이얼로그 박스를 만들수 있는 유틸리티인 'dialog' 이다. 이 글은 다이얼로그 유틸리티에 대한 소개와 그것을 어떻게 사용하고 어떤 곳에 써야 되는지 예제를 담고 있다.

그가 설명하듯이, dialog 는 쉽게 전문적인 모습의 다이얼로그 박스를 만드는 진짜 보석이다. 그것은 다이얼로그 박스, 메뉴, 체크 리스트 등 다양한 것을 만들 수 있다. 그리고 그것은 기본적으로 설치된다. 만약 그렇지 않다면 ibiblio linux archive 에서 찾을 수 있다.

위에 언급된 글에는 그것의 사용과 기능에 대한 아주 좋은 개요가 나와 있다. 맨 페이지에는 보다 상세한 것이 있다. 그것은 다양한 상황에 쓸 수 있다. 좋은 예는 리눅스 커널을 텍스트 모드로 만들 때 이다. 리눅스 커널은 그것의 필요에 따라 약간 수정된 dialog 버전을 사용하고 있다.

dialog 는 애초에 쉘 스크립트에서 사용하도록 디자인 되었다. 만약 당신이 그 기능을 C 프로그램에서 쓰고 싶다면 libdialog 를 써라. 여기에 관련된 문서는 좀 빈약하다. 결정적인 참고 문서는 라이브러리에 있는 dialog.h 헤더파일이다. 당신은 원하는 것을 얻기 위해 여기저기를 해킹해보아야 할지도 모른다. 소스는 쉽게 수정해 볼 만 하다. 나는 그걸 수정해서 여러번 써먹기도 했다.


1.19.3 Perl Curses Modules CURSES::FORM and CURSES::WIDGETS


펄용 모듈인 Curses 와 Curses:Form, Curses::Widgets 는 펄에서 curses 를 쓸 수 있게 한다. 만약 당신이 curses 를 가지고 있고 기본 perl 이 설치되어 있다면, 이 모듈들을 CPAN All Modules page 에서 얻을 수 있다. Curses 카테고리에서 집으로 압축된 세개의 모듈을 구해라. 한번 인스톨 후에는 이 모듈들을 펄 스크립트에서 다른 모듈들 처럼 쓸 수 있다. 펄 모듈에 대한 더 많은 정보는 perlmod 맨 페이지를 참고해라. 위 모듈에는 문서화가 잘 되어 있고 기능을 테스트해볼 예제 스크립트도 있다. 비록 같이 제공되는 위젯이 아직은 미흡하지만, 이 모듈들은 펄에서 curses 라이브러리를 잘 쓸 수 있게 해주고 있다.

더 많은 정보는 Curses(3), Curses::Form(3) Curses::Widgets(3) 맨 페이지를 참고해라. 이 맨 페이지들은 위 모듈들을 받아서 설치해야만 볼 수 있다.


1.20 Just For Fun !!!


이 섹션에는 내가 그냥 재미로 만들어본 몇몇 프로그램들을 소개하고 있다. 아마 이것보다 더 나은 프로그램 이나 ncurses 를 더 잘 쓰는 방법들이 있을 것이다. 이 프로그램들은 초보자로 하여금 아이디어를 얻을 수 있게 하고 이 섹션에 더 많은 프로그램들을 제공할 수 있도록 하기 위해 여기 있는 것이다. 만약 당신이 멋지고 간단한 프로그램들을 curses 로 만들었고 그것을 여기에 포함시키고 싶다면 Mp_padala_at_yahoo.com 에게 연락해주기 바란다.


1.20.1 The Game of Life


Game of life 는 수학의 마법이다. [http]Paul Callahan의 말(http://www.math.com/students/wonders/life/life.html)에서 Game of Life (줄여서 Life)는 흔히 말하는 게임이 아니다. 플레이어도 없고, 이기거나 지는 것도 없다. 한번 그 "조각들" 이 시작 위치에 놓이게 되면 뒤에 일어날 모든 것들은 룰이 결정한다. 그럼에도 불구하고, Life 는 놀라운 일 투성이다. 대부분의 경우 시작 위치( 또는 패턴 ) 을 보고 앞으로 어떤일이 일어날지 예측하는 것은 불가능하다. 그것을 알아내는 단하나의 방법은 룰을 따라가 보는 것이다.

이 프로그램은 간단한 뒤집힌 U 패턴으로 시작해서 life 가 얼마나 경이롭게 동작하는지 보여준다. 프로그램안에는 개선해야할 여지가 많다. 당신은 사용자가 원하는대로 패턴을 입력하거나 파일에서 그것을 읽어오게 할 수도 있다. 또한 룰을 바꿀 수도 있으면 그것을 다양하게 만들어 볼 수도 있다. game of life 에 대한 더 흥미로운 정보들은 [http]구글(http://www.google.com) 에서 찾아보기 바란다.

File Path: JustForFun/life.c


1.20.2 Magic Square


또 다른 수학의 마법인 Magic Squre 는 이해하기는 매우 쉬우나 만들기가 매우 어렵다. magic square 에서 각 줄의 숫자의 합 각열의 숫자의 합은 같다. 심지어 대각선의 합도 같을 수 있다. 독특한 설정을 가지는 다양한 변종들이 있다.

이 프로그램은 홀수 크기의 간단한 magic square 를 만든다.

File Path: JustForFun/magic.c


1.20.3 Towers of Hanoi


유명한 하노이의 탑이다. 이 게임의 목표는 첫번째 장대의 판을 중간의 장대를 임시로 사용하여 마지막 장대까지 옮기는 것이다. 규칙은 밑에 있는 것보다 더 큰 판을 위에다 놓을 수 없다는 것이다.

File Path: JustForFun/hanoi.c


1.20.4 Queens Puzzle


유명한 N-Queen 퍼즐의 목적은 N X N 크기의 체스판에 N 명의 퀸을 서로 공격하지 않게 놔두는 것이다.

이 프로그램은 이 문제를 간단한 백트래킹 기술로 풀었다.

File Path: JustForFun/queens.c


1.20.5 Shuffle


시간을 보내야 할때 해볼 만한 게임이다.

File Path: JustForFun/shuffle.c


1.20.6 Typing Tutor


간단한 타이핑 도우미다. 나는 간단히 사용만하기보다 필요에따라 좀더 많은 기능을 만들었다. 만약 당신이 키보드의 운지법은 알지만 연습을 많이 못해봤다면 이것이 도움이 될 것이다.

File Path: JustForFun/tt.c
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

8. 문자단위 그래픽스 (Character Cell Graphics)

이장은 화소(pixel)를 기본으로 하지않는, 문자를 기본으로 하는 화면 입력과 출력을 다룬다. 우리가 문자(character)를 말할 때, 문자는 문자집합(charset)에 따라 변화되는 화소(pixel)의 조합을 의미한다. 텍스트(text)가 화소(pixel) 그래픽보다 매우 빨리 처리되기 때문에 그래픽 카드(graphic card)는 이미 한개이상의 문자집합(charset)을 제공하고, 기본적으로 텍스트(charset) 모드에서 동작한다. 간단(무딘)하고 지겨운 텍스트 표시하는 것 이상으로 단말기를 다룰 수 있다. 리눅스 단말기(terminal), 특히 콘솔(console)이 제공하는 특별한 특징들을 어떻게 사용할 것인가에 대해 설명하고자 한다.

  • printf, sprintf, fprintf, scanf, sscanf, fscaf
    이러한 함수들을 가지고 (표준출력 standard output), (표준에러 standard error), 또는 FILE *stream (files, 예를 들면)와 같은 다름 스트림(stream)에 형태가 지정된(formatted) 문자열을 출력할 수 있다. Scanf(...)는 비슷한 방법으로 형태가 지정된(formatted) 입력을 제공한다.
  • termcap
    단말기 능력 데이타베이스 (The TERMinal CAPabilites database)는 /etc/termcap 이라는 ASCII 파일에 들어있는 단말기 특징 개체(terminal description entries)들의 집합이다. 여기서 특수문자들을 어떻게 표시하는가, 어떻게 동작(delete,insert characters or line)들을 수행하는가 그리고 단말기를 어떻게 초기화 하는가에 대한 정보를 찾을 수 있다. 이 데이타베이스는, 예를들면, vi 에디터에 의해 사용된다. 단말기 능력(terminal capabilities) (termcap(3x))을 사용하고 읽을 수 있는 view 라이브러리 함수들이 있다. 이 데이타베이스를 가지고, 프로그램은 같은 코드를 가진 단말기에서 다양한 작업을 할 수 있다. termcap 데이타베이스와 라이브러리 함수들의 사용은 단말기에 대해 오직 하위 수준의 접근 (low level access)을 제공한다. 속성(attributes)과 색상(colors)과 매개변수화된(parameterized) 출력을 바꾸는 것은 프로그래머에 의해 수행되어야만 한다.
  • terminfo database
    단말기 정보 데이타베이스(the TERMinal INFOrmation database)는 termcap database를 기초로 하고 단말기 능력(terminal capabilities)을 설명하고 있지만, termcap보다는 더높은 수준(higher level)이다. terminfo를 사용하여, 프로그램은 화면 속성(screen attributes), 펑션키(function keys)와 같은 특별한 키의 사용과 그외의 것들을 쉽게 바꿀 수 있다. 데이타베이스는 /usr/lib/terminfo/[A-z,0-9]*에서 찾을 수 있다. 모든 파일은 한 단말기를 설명한다.
  • curses
    Terminfo는 프로그램안에서 단말기(terminal)를 다루기(handling) 위한 좋은 기초이다. (BSD-)CURSES 라이브러리는 단말기에 대해 상위 수준의 접근(high level access)을 가능하게 하며, terminfo database를 기초로 한다. curses는 화면상의 윈도우를 열고 조종할 수 있도록 하며, 입력과 출력 함수들의 완벽한 집합을 제공하고 150개 이상의 단말기에 대해 단말기의 독립적인 방식의 비디오 속성을 변경시킬 수 있다. curses 라이브러리는 /usr/lib/libcurses.a에서 찾을 수 있다. 이것은 curses의 BSD 버전이다.
  • ncurses
    Ncurses는 한단계 향상된 것이다. 1.8.6 버전에서는 SYSVR4에서 정의된 AT&T curses와 호환 되어야만 하고 색상 조종(color manipulation), 출력을 위한 특별한 최적화(special optimization for output), 단말기 지정 최적화(terminal specific optimizations) 등과 같은 확장을 가져야만 한다. 이것은 SUN-OS, HP, Linux와 같은 많은 시스템들에서 테스트 되어 왔다. 다른 것들을 대신해서 ncurses를 사용할 것을 추천한다. SYSV 유닉스 시스템(SUN의 Solaris같은) 상에서는 nurses와 같은 기능을 갖는 curses 라이브러리가 존재해야 한다. (실제로 solaris curses는 마우스 지원과 보다 많은 기능을 가지고 있다.)
다음의 장에서 단말기를 다루기 위해 다른 패키지들을 어떻게 사용하는가에 대해 설명하겠다. 리눅스는 termcap의 GNU-버전을 가지고 있고 우리는 curses 대신에 nurses를 사용할 수 있다.


8.1 libc안의 I/O 함수 (I/O Function in libc)


8.1.1 형식화된 출력 (Formatted Output)

  printf(...) 함수는 형식이 있는 출력을 제공하고 아규먼트의 변형을 허락한다.

  • int fprintf(FILE *stream, const char *format, ...),
    은 출력을 변형시키고 stream에 쓴다. format안에 정의된 형태(format)로 쓰여진다. 이 함수는 쓰여진 문자들의 갯수를 반환하거나 에러시는 음수값을 반환한다.

    format는 두 종류의 객체를 포함한다.

    1. 출력을 위한 정상적인 문자들과
    2. 아규먼트를 어떻게 변형시키고 형식화할 것인가의 정보
    형식 정보 (format information)는 %로 시작하여 뒤에 형식을 지정하는 값과 변형을 위한 문자가 따라와야 한다. (%자체를 인쇄하기 위해 %%를 사용한다.) 형식에서 사용가능한 값들:

    • Flags
      • -
        형식화된 아규먼트는 왼쪽 여백에 인쇄된다. (디폴트는 아규먼트 필드의 오른쪽 여백이다.)
      • +
        모든 숫자가 부호를 가지고 인쇄된다. (예, +12, -2.32)

       

    • Blank
      첫번째 문자가 부호를 가지고 있지 않으면, 공백이 삽입된다.
    • 0
      숫자 변환을 위해 필드의 폭은 왼쪽 편이 0으로 채워진다.
    • #
      아규먼트를 위한 변환에 따라 출력을 바꾼다.
      • o 에 대해, 첫번째 숫자는 0.
      • x, X 에 대해, 0x 또는 0X가 아규먼트의 앞에 인쇄된다.
      • e, E, f, F 에 대해, 출력은 소수점을 갖는다.
      • g, G 에 대해, 아규먼트의 끝에 제로(zeroes)가 인쇄된다.

       

    • 최소한의 필드 폭을 위한 수
      변형된 아규먼트는 아규먼트 자체의 가장 최소한의 크기를 갖는 필드안에 인쇄된다. 이 수(number)를 가지고 필드의 폭을 더 크게 만들 수 있다. 형식화된 아규먼트가 더 작은 경우, 필드의 폭은 제로 또는 공백으로 채워진다.
    • 필드의 폭과 정확도를 나누기 위한 점.
    • 정확도를 위한 수.

     

  • int printf(const char *format, ...)
    fprintf(stdout,...)와 같음
  • int sprintf(char *s, const char *format, ...)
    출력이 문자(character) 포인터인 s (끝에 \0를 갖는)에 쓰인다는 것을 제외하곤 printf(...)와 같음

    (Note: s를 위한 충분한 메모리를 할당해야한다.)

  • vprintf(const char *format, va_list arg)
    vfprintf(FILE *stream, const char *format, va_list arg)
    vsprintf(char *s, const char *format, va_list arg)
    오직 arg에 아규먼트 리스트가 지정된 것일뿐, 위의 함수들과 같음

8.1.2 형식화된 입력 (Formatted Input)

  형식화된 출력을 위해 printf(...)를 사용하는 것처럼 형식화된 입력을 위해 scanf(...)를 사용할 수 있다.

  • int fscanf(FILE *stream, const char *format, ...)
    fscanf(...)는 stream으로 부터 읽어서 format에서 정의된 규칙에 따라 입력을 변환한다. 결과는 주어진 아규먼트 안에 위치하게 된다. (Note:아규먼트들은 포인터여야만 한다.) format안에 더이상의 변환 규칙이 없을 때까지 읽는다. fscanf(..)는 첫번째 변환이 파일의 끝에 도달했거나 에러가 발생했을 때 EOF를 반환한다. 그렇지 않으면, 변환된 아규먼트의 수를 반환한다.

    format은 입력 아규먼트를 어떻게 형식화할 것인가에 대한 규칙을 포함한다. 이것은 또한 포함한다.:

    • 공백(spaces)이나 탭(tab)은 무시된다.
    • (%를 제외한) 일반적인 문자. 문자들은 연관된 위치의 입력안에 있어야 한다.
    • %, 부가적인 문자 *(이것은 fscanf(..)가 아규먼트를 지정하는 것을 허락한다), 부가적인 숫자, 부가적인 문자 h,l 또는 L (이것은 타겟의 길이이다) 과 변환 문자로 조합된 변환 규칙들
  • int scanf(const char * format, ...)
    fscanf(stdin,...)과 같음
  • int sscanf(char *str, const char *format, ...)
    scanf(...)와 같지만, 입력은 str로 부터 온다.

8.2 Termcap 라이브러리 (The Termcap Library)


8.2.1 소개 (Instruction)

termcap 라이브러리는 /etc/termcap/에서 찾을 수 있는 termcap database에 대한 API이다. 이 라이브러리 함수들은 다음의 동작을 허용한다.:

  • 현재 단말기의 설명(description)을 얻는다 : tgetent(...)
  • 정보를 위한 설명을 검색한다 : tgetnum(...), tgetflag(...), tgetstr(...)
  • 단말기의 지정 형태안에 숫자 파라미터들을 암호화한다 : tparam(...), tgoto(...)
  • 충전물을 계산하고 수행한다 : tputs(...)

termcap 라이브러리를 사용하는 프로그램은 .h를 포함(include)하고 연결(link)되어야만 한다.

Termcap 함수들은 단말기에 독립적인 루틴들이지만 프로그래머들에게 단말기에 대한 하위 수준의 접근을 가능하게 한다. 보다 높은 패키지를 위해서는, curses나 ncurses가 사용되어야만 한다.

 

8.2.2 단말기 내용 찾기 (Find a Terminal Description)

  • int tgetent(void *buffer, const char *termtype)
    리눅스 운영체제 시스템상에서 현재 단말기 이름은 환경변수 안에 포함된다. 그래서, termtype는 (3)의 호출의 결과이다.

    termcap의 GNU 버전을 사용할 때 버퍼를 위한 메모리의 할당은 없다. 이것은 리눅스 하에서 우리가 가정할 수 있는 것이다. 그렇지 않으면, 2048 바이트를 할당해야만 한다. (전에는 버퍼는 오직 1024 바이트만이 필요했었지만, 그 크기가 두배가 되었다.)

    tgetent(...)는 성공시 1을 반환하고 데이타베이스가 발경되었지만 TERM을 위한 개체(entry)가 없을 때 0을 반환한다. 나머지 에러들은 다른 값들을 반환한다.

    다음의 예제는 tgetent(...)를 어떻게 사용하는지 설명해야만 한다.:

    디폴트로 termcap은 데이타베이스처럼 /etc/termcap/을 사용한다. 한예로, 환경변수 TERMCAP이 $HOME/mytermcap로 지정되어 있으면, 모든 함수들은 /etc/termcap을 대신하여 사용한다. TERMCAP안에 선행하는 슬래쉬(slash)가 없는 정의된 값은 단말기에 대한 이름처럼 사용된다.

8.2.3 단말기 내용 살펴보기 (Look at a Terminal Description)

정보의 모든 조각들은 능력(capability)이라 불리어 진다. 모든 능력(capability)은 두 글자 코드이고, 모든 두 글자 코드는 능력(capability)을 위한 값에 의해 다음과 같다. 가능한 타입들:

  • 숫자(Numeric):예를 들어 co - 컬럼의 수
  • 논리적 자료 또는 플래그(Boolean or Flag):예를 들어 hc - 하드카피 단말기(hardcopy terminal)
  • 문자열(String):예를 들어 st - 탭 위치 지정

각각의 능력(capability)은 단일 값 타입과 연관되어있다. (co는 항상 숫자, hc는 항상 플래그 그리고 st는 항상 문자열) 값들에는 세게의 다른 타입이 있고 그것들에게 질문하기위한 세개의 함수가 있다. char *name은 능력(capability)를 위한 두 글자 코드이다.

  • int tgetnum(char *name)
    co와 같은 숫자(numeric) 능력(capability) 값(value)을 얻는다. tgetnum(...)는 능력을 사용가능하다면, 숫자값을 반환하고 그렇지 않으면 1을 반환한다. (Note:반환되는 값은 음수가 아니다.)
  • int tgetflag(char *name)
    논리적(boolean) 자료 (또는 플래그(flag))인 능력 값을 얻는다. 플래그가 존재하면 1, 그렇지 않으면 0을 반환한다.
  • char *tgetstr(char *name, char **area)
    문자열인 능력값을 얻는다. 문자열에 대한 포인터를 반환하고 존재하지 않으면 NULL을 반환한다. GNU 버전에서, 영역이 NULL이면, termcap은 스스로 메모리를 할당할 것이다. termcap은 결코 그 포인터를 다시 참조하지 않는다. 그러므로 프로그램을 떠나기 전에 name을 풀어주는 것을 잊지말아라. 우리가 포인터를 위해 얼마나 많은 공간이 필요한지를 모르기 때문에, termcap이 이러한 일을 하도록 하는 것이 더 좋다.

8.2.4 Termcap 능력들 (Termcap Capabilities)



Boolean Capabilities

5i 프린터가 화면에 echo를 하지 않는다. am 자동 줄 바꿈을 의미하는 자동 마진 bs Control-H(8 dec)는 backspace를 수행한다. bw 왼쪽 여백에 대한 backspace는 이전의 라인과 오른쪽 여백을 가린다. da 표시(display)가 화면 위로 유지된다. db 표시(display)가 화면 아래로 유지된다. eo 공백이 커서 위치의 모든 문자들을 지운다. es Escape sequences와 특수문자들은 상태줄(ststus line)에서 작업한다. gn 일반적인 장치 hc 이것은 hardcopy 단말기이다. HC 커서는 바닥줄에 있지 않을 때 보기 어렵다. hs 상태줄(status line)을 갖는다. hz Hazel tine bug, 단말기는 물결(tilde) 문자들을 인쇄할 수 없다. in 단말기는 흰색공백을 채우기위해 공백이 아닌 NULL을 삽입한다. km 단말기는 메타 키(meta key)를 갖는다. mi 커서의 움직임은 삽입 모드에서 동작한다. ms 커서의 움직임은 표준출력/밑줄 모드에서 동작한다. NP pad 문자는 없다. NR ti는 te로 바뀌지 않는다. nx padding 없이, XON/XOFF를 사용해야만 한다. os 단말기는 overstrike 할 수 있다. ul overstrike 될 수 없을 지라도 단말기는 밑줄 그어 진다. xb Beehive glitch, f1는 ESCAPE를 보낸다, f2는 ^C를 보낸다. xn Newline/wraparound glitch xo 단말기는 xon/xoff 프로토콜을 사용한다. xs 표준출력을 통한 텍스트는 표준출력으로 표시되어진다. xt Teleray glitch, destructive tabs and odd standout mode 


Numeric Capabilities


String Capacilies

 

8.3 Ncurses - 소개 (Introduction)

다음 용어들은 이 장에서 사용될 것이다.:

  • window - 화면의 부분 이미지를 포함하는 내부적 표현이다. WINDOW는 .h에 정의되어있다.
  • screen - 전체 화면(좌측 상단에서 부터 우측 하단까지) 크기를 갖는 창(window)이고 화면들이다.
  • terminal - 화면이 현재 무엇처럼 보이는지에 대한 정보를 가지고 있는 특별한 화면(screen)이다.
  • variables - 다음의 변수들과 상수들은 in.h에 정의되어있다.
    • WINDOW *cursor - 현재 화면(current screen)
    • WINDOW *stdscr - 표준 화면(standard screen)
    • int LINES - 단말기(terminal)상의 라인들
    • int COLS - 단말기(terminal)상의 컬럼들
    • bool TRUE - true flag, 1
    • bool FALSE - false flag, 0
    • int ERR - error flag, -1
    • int OK - ok flag, 0
  • functions - 함수안에서 아규먼트에 대한 묘사는 다음의 타입이 있다:
    • win - WINDOW*
    • bf - bool
    • ch - chtype
    • str - char*
    • chstr - chatype*
    • fmt - char*
    • otherwise int

대개 ncurses 라이브러리를 사용하는 프로그램은 이처럼 보인다.:

.h를 포함하는(including) 것은 WINDOW와 함수원형(function prototype)와 같이 ncurses를 위한 변수들과 타입들을 정의한다. 자동적으로 .h,.h,.h,.h를 포함한다.

initscr()는 ncurses 자료 구조들을 초기화하고 적당한 terminfo 파일을 읽는데 사용된다. 메모리는 할당되어진다. 에러가 발생한다면, initscr는 ERR를 반환한다 그렇지않으면 포인터가 반환되어진다. 부가적으로, 화면은 지워지게 되고 초기화 되어질 것이다.

endwin()은 ncurses로 부터 할당된 모든 자원들을 청소하고 tty 모드에 initscr()을 호출하기 전 상태를 저장한다. initscr()은 curses로 부터 다른 어떤 함수이전에 호출되어져야 하고 endwin()는 프로그램을 끝내기 전에 호출되어져야 한다. 한 단말기 이상에 출력을 하고자 할 때는 initscr() 대신에 newterm(...)를 사용할 수 있다.

프로그램 컴파일하기:

플래그 안에 당신이 좋아하는 것을 포함할 수 있다(gcc(1)). ncurses.h를 위한 경로가 수정되었으면 다음 줄을 포함(include)하여야 한다.

그렇지 않으면, ncurses.h,nterm.h,termcap.h,unctrl.h은 찾을 수 없을 것이다. Linux에서 사용가능한 다른 플래그들:

2는 gcc에게 최적화(optimization)하라고 말한다, -ansi는 ansi에 일치하는 c-code용이다, -Wall는 모든 경고(warning)를 인쇄할 것이다, -m486는 인텔 486를 위한 최적화된 code를 사용할 것이다.(binary는 인텔 386상에서도 사용될 수 있다.).

ncurses 라이브러리는 /usr/lib에서 찾을 수 있다. ncurses 라이브러리는 세 버전이 있다.

  • libncurses.a 표준 ncurses 라이브러리
  • libdcurses.a 디버깅(debugging)을 위한 ncurses
  • libpcurses.a profiling를 위한 ncurses (1.8.6 libpcurses.a 이래 더이상 존재하지 않는다 ?)
  • libcurses.a 네번째 버전은 아니지만, 최초의(original) BSD curses이다. (내 슬랙웨어 2.1.0에서는 bsd 패키지이다).

화면(screen)을 위한 자료구조는 .h에 정의된 것처럼 윈도우즈(Windows)라 불리어 진다. 윈도우는 프로그래머가 단말기에 출력하는 것없이 조종할 수 있는 메모리 안에 있는 문자 배열과 같은 것이다. 디폴트 윈도우는 단말기의 크기이다. newwin(...)를 가지고 다른 윈도우를 만들 수 있다.

최상으로 물리적인 단말기를 업데이트하기 위해, ncurses는 선언된 또 다른 윈도우를 가지고 있다. 이것은 단말기가 실제로 어떻게 보여지는가의 이미지이고 단말기가 어떻게 보여져야만 하는가의 이미지이다. refresh()가 호출될 때 출력되어질 것이다. ncurses는 안에 있는 정보를 가지고 물리적인 단말기를 업데이트할 것이다. 라이브러리 함수들은 업데이트 프로세스를 위해 내부의 최적된 것을 사용할 것이므로 가장 최적의 방법으로 다른 윈도우들을 변화시키고 한번에 화면을 업데이트할 수 있다.

ncurses 함수들을 가지고 자료 구조체 window를 조종할 수 있다. w로 시작하는 함수들은 당신이 윈도우(window)를 지정하는 것을 허용하는 반면, 다른 것들은 일반적으로 작용한다. mv로 시작하는 함수들은 커서를 처음의 y,x 위치로 이동시킨다.

문자는 속성(attributes)에 대한 부가적인 정보를 저장하는 long unsigned int값인 chtype 타입을 갖는다.

ncurses는 데이타베이스를 사용한다. 일반적으로 데이타베이스는 /lib/terminfo에 위치하고 ncurses는 지역 단말기 정의를 거기서 찾는다. 원래의 terminfo를 변화시키지 않고 단말기에 대한 몇몇 다른 정의들을 테스트하기를 원한다면, 환경변수(environment variable)를 지정(set)하라. ncurses는 이 변수들을 체크하고 /usr/lib/terminfo/를 대신하여 거기에 저장된 정의들을 사용할 것이다.

현재 ncurses 버전은 1.8.6()이다.

이장의 끝에서 BSD-Curses,ncurses와 Sun-OS 5.4의 curses에 대한 개관(overview)이 담긴 표를 발견할 수 있다. 지정된 함수와 그것이 구현된 곳을 찾고자 할 경우에 참조하라.

 

8.4 초기화 (Initializing)

  • WINDOW *initscr()
    이것은 ncurses를 사용하는 프로그램으로 부터 일반적으로 호출되는 첫번째 함수이다. 몇몇 경우에 있어서 slk_init(int), filter(), ripoffline(...) 또는 initscr() 이전의 use_env(bf) 호출은 유용하다. 여러개의 단말기를 사용할 때 (또는 능력(capabilities)을 테스트하는 경우), initscr()을 대신하여 newterm(...) 를 사용할 수 있다.

    initscr()는 적당한 terminfo 파일을 읽고 ncurses 자료 구조를 초기화하고 메모리를 할당하고 단말기가 가지고 있는 값들을 지정(set)할 것이다. 이것은 포인터를 반환하거나 에러가 발생했을 때 ERR를 반환한다. 포인터를 초기화할 필요는 없다.

    initscr()는 당신을 위해 이것을 할 것이다. 반환값이 ERR이면, 당신의 프로그램은 ncurses 함수가 작업할 수 없으므로 종료해야만 한다.

     

  • SCREEN *newterm(char *type, FILE *outfd, FILE *infd)
    다수의 단말기 출력을 위해 initscr()를 대신하여 ncurses를 가지고 접근하기 위해 각각의 단말기에 대해 newterm(...)를 호출한다. type은 $TERM안에 포함되어 있는 단말기의 이름(예:ansi, xterm, vt100)이고 outfd는 출력 포인터(output pointer)이고 infd는 입력을 위해 사용되는 포인터이다. newterm(...)에 의해 열려진 각 단말기에 대해 endwin()를 호출하라.

     

  • SCREEN *set_term(SCREEN *new)
    set_term(SCREEN)를 가지고 현재의 단말기를 바꿀(switch) 수 있다. 모든 함수들은 set_term(SCREEN)에 의해 지정된 현재의 단말기에 작용할 것이다.

     

  • int endwin()
    endwin()는 청소를 하고 initscr()를 호출하기 전의 상태로 단말기 모드를 저장하고 커서를 좌측하단으로 이동시킬 것이다. 프로그램을 종료하기 위해 endwin()을 호출하기 전에 열려진 모든 윈도우를 닫는 것을 잊지 말아라.

    endwin()후에 부가적으로 refresh()를 호출하는 것은 initscr()를 호출하기 전에 가지고 있는 상태로 단말기를 저장할 것이다(visual-mode). 그렇지 않으면 clear될 것이다(non-visual-mode).

     

  • int isendwin()
    다음에 refresh()를 가지고 endwin()이 호출되면 TRUE를 반환하고 그렇지 않으면 FALSE를 반환한다.

     

  • void delscreen(SCREEN* sp)
    SCREEN이 더이상 필요하지 않을 때, endwin() 후에 점유하고 있는 자원들을 모두 풀어주기 위해 delscreen(SCREEN)을 호출하라. (Note:아직 구현되지 않음)

8.5 윈도우 (Windows)

윈도우즈(windows)는 생성되고 삭제되고 이동되고 복사되고 사용되고 중복되어질 수 있다.

  • WINDOW *newwin(nlines, ncols, begy, begx)
    begy와 begx는 좌측상단 구석의 윈도우 좌표이다. nlines는 라인 수를 나타내는 정수이고 ncols는 컬럼 수를 나타내는 정수이다.

    그림 8.1:Ncurses - newwin을 위한 스키마(scheme)

    우리의 윈도우의 좌측상단 구석은 10라인의 10컬럼이고 위도우는 10라인과 60컬럼을 갖는다. nlines가 제로이면, 윈도우는 LINES-begy개의 row를 갖을 것이다. 같은 방법으로 ncols이 제로이면 윈도우는 COLS-begx개의 컬럼을 갖을 것이다.

    모든 아규먼트에 제로를 가지고 newwin(...)를 호출할 때:

    열려진 윈도우는 화면의 크기를 갖을 것이다.

    어떤 차원(demension)을 가지고 있건 화면의 중앙에 윈도우를 열고자 할 때:

    이것은 화면의 중앙에 22라인과 70컬럼을 가진 위도우를 열 것이다. 윈도우를 열기 전에 화면의 크기를 체크하라. 리눅스 콘솔에서 우리는 25 이상의 라인과 80이상의 컬럼을 갖지만, xterms에서는 이것은 일정하지 않다(resizable하다).

    더 자세한 설명을 위해 예제 디렉토리에 있는 .c를 봐라.

  • int delwin(win)
    윈도우 win을 삭제하라. 서브윈도우들이 있으면 win 전에 그것들을 삭제하라. win에 의해 점유되어진 모든 자원들은 자유로와 질 것이다. endwin()를 호출하기 전에 열려져 있는 모든 윈도우를 삭제하라.
  • int mvwin(win, by, bx)
    by,bx 좌표로 윈도우를 이동시킨다. 화면의 가장자리 밖으로 윈도우를 이동하고자 한다면, 아무것도 수행되지 않고 ERR가 반환된다.
  • WINDOW *subwin(origwin, nlines, ncols, begy,begx)
    origwin의 중앙에 서브윈도우를 반환한다. 두 윈도우들(origwin 또는 new one) 중 하나를 바꾸고자 할 때 이 변화는 양쪽 윈도우 모두에 반영될 것이다. 다음의 refresh() 전에 touchwin(origwin)를 호출하라.

    begy와 begx는 origwin이 아니라 화면에 비례한다.

  • WINDOW *derwin(origwin, nlines, ncols, begy, begx)
    subwin(...)와 같이 화면에서 begy, begx와 연관된 윈도우 origwin을 제외한다.
  • int mvderwin(win, y, x)
    win을 부모 윈도우 안으로 이동시킨다. (NOTE:아직 구현되지 않음)
  • WINDOW *dupwin(win)
    윈도우 win을 복사한다.
  • int syncok(win, bf)
    void wsyncup(win)
    void wcursyncup(win)
    void wsyncdown(win)
    (Note:아직 구현되지 않음)
  • int overlay(win1, win2)
    int overwrite(win1, win2)
    overlay(...)는 모든 텍스트를 공백은 복사하지 않고 win1에서 win2로 복사한다. overwrite(...)는 공백을 복사하는 것을 제외하고는 같다.
  • int copwin(win1, win2, sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol, overlay)
    overlay(...), overwrite(...)와 비슷하지만 윈도우의 어떤 영역으로 복사할 것인지 통제권(control)을 제공한다.

8.6 출력 (Output)

  • int addch(ch)
    int waddch(win, ch)
    int mvaddch(y, x, ch)
    int mvwaddch(win, y, x, ch)
    이러한 함수들은 윈도우에 문자 출력을 위해 사용된다. 함수들은 윈도우를 조종할 것이고 화면에 출력하기위해 refresh()을 호출해야 한다. addch(...)waddch(...)는 윈도우나 win안에 ch 문자를 출력한다. mvaddch(...)mvwaddch(...)은 y, x의 위치로 커서를 이동하는 것을 제외하고는 똑같이 동작한다.
  • int addstr(str)
    int addnstr(str, n)
    int waddstr(win, str)
    int waddnstr(win, str, n)
    int mvaddstr(y, x, str)
    int mvaddnstr(y, x, str, n)
    int mvwaddstr(win, y, x, str)
    int mvwaddnstr(win, y, x, str, n)
    이러한 함수들은 윈도우에 문자열을 쓰며 addch(...)의 호출을 연속적으로 수행하는 것과 같다. str은 널(null)로 끝나는 문자열이다("blafoo\0"). 다른 함수들이 그냥 쓰는 반면에 w를 가진 함수들은 윈도우 win에 문자열 str을 쓴다. n을 가진 함수들은 str 중 n개의 문자를 쓴다. n이 -1이면, 전체 문자열이 쓰여진다.
  • int addchstr(chstr)
    int addchnstr(chstr, n)
    int waddchstr(win, chstr)
    int waddchnstr(win, chstr, n)
    int mvaddchstr(y, x, chstr)
    int mvaddchnstr(y, x, chstr, n)
    int mvwaddchstr(win, y, x, chstr)
    int mvwaddchnstr(win, y, x, chstr, n)
    이러한 함수들은 chstr을 윈도우 이미지(또는 win)로 복사한다. 시작하는 위치는 현재 커서의 위치이다. n을 가진 함수들은 chstr 중 n개의 문자를 쓴다. n이 -1이면, 전체 문자열 chstr이 쓰여진다. 커서는 옮겨지지 않고 컨트롤 문자 체크(control character check)는 이루어 지지 않는다. 이러한 함수들은 addstr(...) 루틴들보다 더 빠르다. chstr은 chtype 배열의 포인터이다.
  • int echochar(ch)
    int wechochar(win, ch)
    refresh()(wrefresh(win))에 의해 따라오는 addch(...)(waddch(...)) 호출과 같다


8.6.1 형식화된 출력 (Formatted Output)

  • int printw(fmt, ...)
    int wprintw(win, fmt, ...)
    int mvprintw(y, x, fmt, ...)
    int mvwprintw(win, y, x, fmt, ...)
    int vwprintw(win, fmt, va_list)
    이러한 함수들은 printf(...)와 연관이 있고 아주 닮았다.

    패키지 안에서 printf(...)은 형식화된 출력을 위해 사용되어진다. 출력 문자열을 정의할 수 있고 그안에서 다른 타입의 변수들을 포함할 수 있다. 더 자세한 것은 8.1.1장을 참조하라

    vwprintw(...)을 사용하기 위해 .h를 포함(include)해야한다.

8.6.2 문자/줄 입력 (Insert Characters/Lines)

  • int insch(c)
    int winsch(win, c)
    int mvinsch(y, x, c)
    int mvwinsch(win, y, x, c)
    문자 ch는 커서의 왼쪽에 삽입되고 모든 문자들은 오른쪽으로 한자리씩 이동된다. 줄의 오른쪽 끝에 있는 문자를 잃어버릴 수도 있다.
  • int insertln()
    int winddelln(win, n)
    양수 n에 대해 이러한 함수들은 적절한 윈도우에 n개의 줄을 커서의 위쪽에 삽입한다.(그래서 n개의 바닥쪽의 줄을 잃어버리게 된다.) n이 음수인 경우, 커서 아래의 n개의 줄이 삭제되고 나머지가 위로 이동한다.
  • int insstr(str)
    int insnstr(str, n)
    int winsstr(win, str)
    int winsnstr(win, str, n)
    int mvinsstr(y, x, str)
    int mvinsnstr(y, x, str, n)
    int mvwinsstr(win, y, x, str)
    int mvwinsnstr(win, y, x, str, n)
    이러한 함수들은 현재 줄안에 커서의 왼쪽에 str을 삽입한다.(줄에 꼭 맞을 만큼의 문자들) 커서의 오른쪽에 있는 문자들은 오른쪽으로 이동하고 줄의 끝에 도달하게 되면 잃어 버리게 된다. 커서의 위치는 변하지 않는다.

    y와 x는 str이 삽입되기 전에 커서가 이동할 좌표이고 n은 삽입될 문자의 수이다. (n=0이면 전체 문자열이 삽입된다.)

8.6.3 문자/줄 지우기 (Delete Characters/Lines)

  • int delch()
    int wdelch(win)
    int mvdelch(y, x)
    int mvwdelch(win, y, x)
    커서 왼쪽의 문자를 지우고 커서 오른쪽의 남아있는 문자들을 왼쪽으로 한자리씩 위치를 이동시킨다.

    y와 x는 삭제하기 전에 커서가 이동할 좌표이다.

  • int deleteln()
    int wdeleteln(win)
    커서 아래의 줄을 지우고 모든 나머지 줄들을 한자리씩 위로 위치를 이동시킨다. 부가적으로, 윈도우의 바닥 줄은 지워질 것이다.

8.6.4 상자와 줄 (Boxes and Lines)

  • int border(ls, rs, ts, bs, tl, tr, bl, br)
    int wborder(win, ls, rs, ts, bs, tl, tr, bl, br)
    int box(win, vert, hor)
    윈도우(또는 win)의 가장자리 주위에 경계를 그린다. 다음의 테이블에서 box(...) 호출시 제로(zero)일 때, 그들의 디폴트 값과 문자를 볼 수 있다.

    Table 8.3:Ncurses - border characters

    Figure 8.2:Ncurses - box characters

     

  • int vline(ch, n)
    int wvline(win, ch, n)
    int hline(ch, n)
    int whline(win, ch, n)
    이러한 함수들은 현재 커서 위치에서 시작하는 수직선 또는 수평선을 그린다. ch는 사용하는 문자이고 n은 그려지는 문자의 수이다. 커서 위치는 변하지 않는다.

8.6.5 백그라운드 문자 (Background Character)

  • void bkgdset(ch)
    void wbkgdset(win, ch)
    백그라운드(background) 문자와 화면 또는 윈도우의 속성을 지정한다. ch안의 속성은 윈도우 안에 있는 모든 공백이 아닌 문자와 OR되어진다. 백그라운드는 윈도우의 일부이고 스크롤링, 입력, 출력 에 의해 변하지 않는다.
  • int bkgd(ch)
    int wbkgd(win, ch)
    백그라운드 문자를 ch의 속성으로 바꾼다.

8.7 입력 (Input)

  • int getch()
    int wgetch(win)
    int mvgetch(y, x)
    int mvwgetch(win, y, x)
    getch()는 지연 모드(delay mode)가 지정되었는지 아닌지에 따른 방식으로 단말기로 부터 입력을 읽는다. 지연(delay)이 on인 경우, getch()는 키가 눌려질 때까지 기다리고 그렇지 않으면 입력 버퍼안의 키를 반환하고 버퍼가 비어있으면 ERR을 반환한다. mvgetch(...)mvwgetch(...)는 먼저 y,x 위치로 커서를 이동시킨다. w 함수들은 윈도우 win과 연관된 단말기로 부터 입력을 읽는다.

    keypad(...)가 enable되어 있으면, getch()는 기능키(function key)가 눌려질 때 .h에 KEY_* 매크로 처럼 정의된 코드를 반환한다. ESCAPE가 눌려질 때(이것이 기능키의 시작일 수 있음) ncurses는 one second timer를 시작시킨다. 이 시간 안에 keystroke의 나머지가 끝나지 않는다면, 키는 반환된다. 그렇지 않으면, 기능키값이 반환된다. (필요하다면, second timer를 사용하지 않기위해 notimeout()을 사용하라)

  • int ungetch(ch)
    입력 버퍼의 뒤에 ch 문자를 넣는다.
  • int getstr(str)
    int wgetstr(win, str)
    int mvgetstr(y, x, str)
    int mvwgetstr(win, y, x, str)
    int wgwrnstr(win, str, n)
    이러한 함수들은 newline을 받을 때까지 getch()의 호출을 연속적으로 수행한다. 문자들은 str안에 놓여있다.(getstr(...)를 호출하기 전에 문자 포인터(character pointer)를 위해 메모리를 할당하는 것을 잊지 말아라) echo가 enable되어 있으면 문자열은 echo되어지고 (echo를 사용하지 않기위해 noecho()를 사용하라) 사용자의 kill과 문자들의 삭제는 해석되어진다.
  • chtype inch()
    chtype winch(win)
    chtype mvinch(y, x)
    chtype mvwinch(win, y, x)
    이러한 함수들은 화면이나 윈도우로 부터 문자를 반환한다. 반환값의 타입은 속성 정보를 포함하는 chtype이기 때문이다. 이 정보는 A_* 상수를 사용하는 문자로 부터 추출되어질 수 있다. (표 8.4를 참조하라)
  • int instr(str)
    int innstr(str, n)
    int winstr(win, str)
    int winnstr(win, str, n)
    int mvinstr(y, x, str)
    int mvinnstr(y, x, str, n)
    int mvwinstr(win, y, x, str)
    int mvwinnstr(win, y, x, str, n)
    화면 또는 윈도우로 부터 character string을 반환한다.(Note:아직 구현되지 않음)
  • int inchstr(chstr)
    int inchnstr(chstr, n)
    int winchstr(win, chstr)
    int winchnstr(win, chstr, n)
    int mvinchstr(y, x, chstr)
    int mvinchnstr(y, x, chstr, n)
    int mvwinchstr(win, y, x, chstr)
    int mvwinchnstr(win, y, x, chstr, n)
    화면 또는 윈도우로 부터 chtype string을 반환한다. 문자열안에 모든 문자에 대한 속성 정보가 포함되어있다. (Note:아빅 구현되지 않음. lib_inchstr은 ncurses lib에 포함되지 않음)

8.7.1 형식화된 입력 (Formatted Input)

  • int scanw(fmt, ...)
    int wscanw(win, fmt, ...)
    int mvscanw(y, x, fmt, ...)
    int mvwscanw(win, y, x, fmt, ...)
    int vwscanw(win, fmt, va_list)
    이것들은 scanf(...)와 비슷하다.(8.1.2장을 참조하라) wgetstr(...)은 호출되어지고 그 결과는 scan를 위한 입력처럼 사용되어진다.

8.8 선택사항 (Options)


8.8.1 출력 선택사항 (Output Options)

  • int idlok(win, bf)
    void idcok(win, bf)
    윈도우의 단말기의 삽입/삭제 특성을 사용 가능하게(enable) 하거나 사용 불가능하게(disable) 한다. (idlok(...)는 줄(line)에 대해 idcok(...)은 문자에 대해 사용) (Note:idcok(...)은 아직 구현되지 않음)
  • void immedok(win, bf)
    TRUE로 지정되면, 윈도우 win에 대한 모든 변화는 물리적인 화면을 refresh하게 만든다. 이것은 프로그램의 성능을 감소시킬 수 있다. 따라서 디폴트 값은 FALSE이다. (Note:아직 구현되지 않음)
  • int clearok(win, bf)
    bf가 TRUE이면, wrefresh(win)의 다음 호출은 화면을 clear하고 완벽하게 다시 그릴 것이다. (vi 에디터에서 CTRL+L을 눌렀을 때처럼)
  • int leaveok(win, bf)
    기본 동작(default behavior)은 ncurses가 물리적인 커서를 윈도우의 마지막 refresh가 발생한 같은 장소에 놓는 것이다. 커서를 사용하지 않는 프로그램들은 leaveok(...) TRUE를 지정할 수 있고 커서를 움직이는데 필요한 일반적인 시간을 줄일 수 있다. 게다가, ncurses는 단말기 커서를 안보이게 만들려고 할 것이다.
  • int nl()
    int nonl()
    newline을 위한 변환을 통제한다. nl()은 출력에서 carriage return과 line feed를 newline으로 변환시킨다. nonl()은 변환을 하지 않는다. 변환을 하지않토록된 ncurses는 커서 이동을 더 빠르게 할 수 있다.

8.8.2 입력 선택사항 (Input Options)

  • int keypad(win, bf)
    TRUE이면, 입력을 위해 기다리고 있을 때 사용자 단말기의 키보드 상에서 키패드의 사용이 가능하다. Ncurses는 키패드의 기능(function)과 화살표(arrow) 키들을 위해 KEY_* 처럼 .h에 정의된 키 코드를 반환할 것이다. 이것은 숫자 블록(numerical block)과 커서 키들을 사용할 수 있으므로 PC 키보드에서 매우 유용하다.
  • int meta(win, bf)
    TRUE이면, getch()로 부터 반환되는 키 코드들은 8-bit-clean이다. (최상위 비트는 떼어낼 수 없다.)
  • int cbreak()
    int nocbreak()
    int crmode()
    int nocrmode()
    cbreak()nocbreak()은 단말기의 CBREAK 모드를 on 또는 off로 전환시킬 것이다. CBREAK가 on일 때, 읽기로 부터의 입력은 즉시 프로그램에서 사용가능하고 off일 때, 입력은 newline이 발생할 때까지 버퍼에 저장될 것이다. (Note:crmode()nocrmode()은 향상된 호환성이 있지만, 그들을 사용하지 말아라)
  • int raw()
    int noraw()
    RAW 모드를 on 또는 off로 만든다. RAW는 RAW 모드에서는 특수 문자 처리가 이루어지지 않는다는 것을 제외하고는 CBREAK와 같다.
  • int echo()
    int noecho()
    사용자에 의해 타이핑된 입력을 echo하기위해 echo()를 지정하고 침묵하기 위해서는 noecho()를 지정한다.
  • int halfdelay(t)
    cbreak()처럼 t 초간 지연(delay)한다.
  • int nodelay(win, bf)
    단말기는 차단(blocking) 모드로 지정되지 않는다. cetch()는 입력이 준비되어있지 않으면 ERR을 반환할 것이다. FALSE가 지정되어 있으면, getch()는 키가 눌려질 때까지 기다릴 것이다.
  • int timeout(t)
    int wtimeout(win, t)
    halfdelay(t)nodelay(win,bf) 대신 이러한 함수들의 사용이 추천된다. getch()의 결과는 t의 값에 따라 결정된다. t가 양수이면, 읽기는 t milliseconds 간 차단되어진다(blocked). t가 제로이면, 차단(blocking)은 수행되지 않는다. t가 음수일 때 프로그램은 입력이 가능할 때까지 차단한다.
  • int notimeout(fd)
    bf가 TRUE이면, getch()는 ESCAPE과 같은 키로 시작하는 시퀀스(sequence)를 입력받고 번역하기 위해 특별한 (1초 길이의)타이머를 사용할 것이다.
  • int typeahead(fd)
    fd가 -1이면 typeahead 체크는 수행되어지지 않을 것이고, 그렇지 않으면 ncurses는 이러한 체크를 위해 파일 식별자(file descriptor) fd를 사용할 것이다.
  • int intrflush(win, bf)
    bf가 TRUE로 사용가능할 때 단말기 상에서 눌려진 인터럽트(interrupt) 키(quit, break, ...)는 tty 드라이버(driver) 큐(queue) 안의 모든 출력을 몰아낼 것이다.
  • void noqiflush()
    void qiflush()
    (Note:아직 구현되지 않음)

8.8.3 단말기 속성 (Terminal Attributes)

  • int baudrate()
    bps로 단말기의 속도를 반환한다.
  • char erasechar()
    현재 erase 문자를 반환한다.
  • char killchar()
    현재 kill 문자를 반환한다.
  • int has_ic()
    int has_il()
    has_ic()는 단말기가 문자를 삽입/삭제할 능력을 가지고 있으면 TRUE를 반환한다. has_il()는 단말기가 줄을 삽입/삭제할 능력을 가지고 있으면 TRUE를 반환한다. 그렇지 않으면 함수들은 ERR를 반환한다. (Note:아직 구현되지 않음)
  • char *longname()
    반환된 포인터는 현재의 단말기의 기술에 접근권을 준다.
  • chtype termattrs()
    (Note:아직 구현되지 않음)
  • char *termname()
    사용자 환경으로 부터 TERM의 내용을 반환한다.(Note:아직 구현되지 않음)

8.8.4 사용 선택사항 (Use Options)

윈도우와 단말기의 사용을 성멸하기 위해 윈도우 선택사항(option)과 단말기 모드에 대해 살펴 보았다.

먼저, 리눅스상에서 키패드를 사용할 수 있어야 한다. 이것은 PC 키보드 상에서 커서 키와 숫자 블럭의 사용을 허락한다.

입력의 중요한 두 가지 타입이 있다.:

  1. 프로그램은 사용자가 키를 입력(enter)하기를 원하고 이 키에 따라 함수를 호출할 것이다. (예를 들면, "끝내려면 'q'를 누르시오"와 같이 그리고 'q'를 기다린다.)

     

  2. 프로그램은 화면상의 마스크 안에서 사용자에 의해 타이핑된 문자들의 문자열을 원한다. 예를 들면: 데이타베이스 안의 디렉토리 또는 어드레스(address).

첫째로 우리는 다음의 선택사항(option)과 모드들을 사용하고 while loop는 정확하게 작업할 것이다.

프로그램은 키가 눌려질 때까지 멈추어 있을 것이다. 키가 q이면 우리는 끝내기(quit) 함수를 호출하고 그렇지 않으면 다른 입력을 기다린다.

switch 문장은 우리가 바라는 것과 일치하는 입력함수를 가질 때까지 확장될 수 있다. 한 예로, 특별한 키를 체크하기 위해 KEY_* 매크로들을 사용하라.

키보드에서 커서 키들을 위해. 파일 뷰어(viewer)를 위해 loop가 이것처럼 보일 수 있다.:

둘째로, 우리는 echo()를 지정할 필요가 있고 사용자에 의해 타이핑된 문자들은 화면에 인쇄되어질 것이다. 원하는 위치에 문자가 인쇄되도록 하기위해 move 또는 wmove(...)를 사용하라.

또는 우리는 마스크를 가지고 윈도우를 열 수 있고 (윈도우의 것들보다 다른 몇몇 색상들이 이것을 수행할 것이다) 사용자에게 문자열 입력을 요청한다.

보다 자세한 설명을 위해 예제 디렉토리에 있는 .c를 보아라.

 

8.9 윈도우와 줄 지우기 (Clear Window and Lines)

  • int erase()
    int werase(win)
    werase(...)erase()는 윈도우 win상의 모든 위치에 공백을 복사한다. 예를 들어, 윈도우에 색상 속성을 지정하고 werase()를 호출할 때, 윈도우는 color가 될 것이다. 흰색 위에 검정색처럼 다른 속성을 정의하여 자신만의 erase 함수를 만들때 COLOR_PAIRS에 약간의 문제가 있다. (이것은 WINDOW 구조에 접근하는 하위 수준(low level)이다.):

    문제는 ncurses가 때때로 화면이 공백일 때 윈도우 속성들을 사용할 수 없다는 것이다. 예를 들어, _clrtoeol.c 안에 BLANK는 정의되어 있다.

    그래서 다른 윈도우 속성들은 줄이 지워지는 동안에 잃어버린다.

  • int clear()
    int wclear(win)
    erase와 같지만 clearok()를 지정할 것이다. (화면은 다음 refresh에 의해 지워(clear)진다.)
  • int clrtobot()
    int wclrtobot(win)
    현재 커서 줄(시작점은 커서의 오른쪽 한 문자이다)과 커서 아래의 줄을 지운다(clearing).
  • int clrtoeol()
    int wclrtoeol(win)
    커서로 부터 끝까지 현재 줄의 오른쪽을 지운다.

8.10 단말기 업데이트 (Updating the Trminal)

개요에 쓰였던 것처럼, ncurses 윈도우들은 메모리 안에 있는 이미지들이다. 이것은 윈도우에서의 어떤 변화가 refresh가 수행될 때까지 물리적인 화면에 인쇄되지 않는다는 것을 의미한다. 이것은 많은 조종이 있을 수 있기 때문에 화면에 대한 출력을 최적화하고 화면에 그것을 인쇄하기 위해 refresh를 한번 호출한다. 그렇지 않으면, 모든 변화가 화면에 인쇄되어 지고 프로그램의 성능 (performance)를 감소시킨다.

  • int refresh()
    int wrefresh(win)
    refresh()는 단말기로 복사를 하고 wrefresh(win)는 윈도우 이미지로 복사한다.
  • int wnoutrefresh(win)
    int doupdate()
    wnoutrefresh(win)는 오직 윈도우 win으로 복사한다. 이것은 단말기에 대한 출력이 수행되지는 않지만 가상의 화면에 실제적으로 프로그래머가 원하는 대로 보여짐을 의미한다. doupdate()는 단말기에 출력을 수행할 것이다. 프로그램은 여러가지 윈도우들을 바꿀 수 있고 모든 윈도우에 대해 wnoutrefresh(win)를 호출할 수 있고 오직 한번 물리적인 화면을 업데이트하기 위해 doupdate()를 호출할 수 있다.

    예를 들어, 두개의 윈도우를 가지는 다음의 프로그램이 있다. 텍스트의 몇몇 줄을 변경하여 양쪽 윈도우들을 바꾼다. wrefresh(win)을 가지고 changewin(win)를 write할 수 있다.

    이것은 ncurses가 단말기를 두번 업데이트하게 하여 실행을 느리게 한다. doupdate()를 가지고 changewin(win)와 main 함수를 변경시키고 성능(performance)을 향상시킨다.

     

  • int redrawwin(win)
    int wredrawln(win, bline, nlines)
    몇몇 줄이나 전체 화면이 새로운 것을 쓰기 전에 없어져야 할 때, 이러한 함수들을 사용한다. (아마도 몇몇 줄이 쓸모없어 졌을 때)
  • int touchwin(win)
    int touchline(win, start, count)
    int wtouchln(win, y, n, changed)
    int untouchwin(win)
    ncurses에 전체 윈도우 win 또는 start up으로 부터 start+count까지 줄들이 조종되었음을 알린다. 예를 들어, 몇몇 윈도우가 중첩되었을 때, 한 윈도우에 대한 변화는 다른 것들로 부터 이미지에 영향을 받지 않을 것이다.

    wtouchln(...)은 y로 부터 시작하여 n개의 줄을 건드릴 것이다. change가 TRUE이면 그 줄들은 변경되고(touched,changed) 그렇지 않으면 변경되지 않는다(untouched,unchanged).

    untouchwin(win)refresh의 마지막 호출 이후 윈도우 win에 unchanged 표시를 할 것이다.

  • int is_linetouched(win, line)
    int is_wintouched(win)
    이러한 함수들을 가지고 줄 line 또는 윈도우 win이 refresh()의 마지막 호출 이후에 변경이 있었는지를 체크할 수 있다.

8.11 비디오 속성과 색상 (Video Attributes and Color)

속성은 화면에 문자를 인쇄할 때 사용되는 특별한 단말기 속성이다. 문자들은 굵게,밑줄,깜빡임 등으로 인쇄될 수 있다. ncurses에서 출력을 보다 좋게 보이도록 속성들을 on 또는 off로 전환할 수 있는 능력을 갖는다. 사용가능한 속성들이 다음의 표에 정리되어 있다.

표 8.4:Ncurses - 속성들

ncurses는 칼라를 지원하는 단말기에서 사용할 수 있는 8가지 색상을 정의한다. 먼저, start_color()를 가지고 색상 자료 구조(color data structures)를 초기화하고 has_colors()를 가지고 단말기의 능력을 체크한다. start_color()COLORS, 단말기가 지원하는 최대 색상수와 COLOR_PAIR, 정의할 수 있는 색상 쌍(color pairs)의 최대수 를 초기화한다.

표 8.5:Ncurses - colors

속성은 OR operator를 가지고 조합되어질 수 있다.

int color_content(color, r, g, b)
색상을 위해 r,g,b의 색상 구성요소(components)를 얻는다.

어떻게 속성과 색상을 어떻게 조합할 것인가? 리눅스에서의 콘솔처럼 몇몇 단말기는 칼라를 갖고 몇몇은 그렇지 않다(xterm,vs100 등등). 다음의 코드는 그 문제를 해결해야만 한다.

먼저, CheckColor 함수는 start_color()를 가지고 색상을 초기화한다. 현재 단말기가 색상을 가지고 있다면 has_colors() 함수는 TRUE를 반환할 것이다. 우리는 이것을 체크하고 포그라운드(foreground)와 백그라운드(background) 색상들을 조합하기 위해 init_pair(...) 와 지정된 윈도우를 위해 이러한 쌍(pairs)을 지정하기 위해 wattrset(...)를 호출한다. 또 한편으로, 흑백 단말기를 가지고 있다면, 속성을 지정하기 위해 wattrset(...)를 혼자 사용할 수 있다.

xterm에서 색상(color)을 얻기 위해 내가 발견한 가장 좋은 방법은 Midnight Commander로 부터 terminfo 개체를 얻는 ansi_xterm을 사용하는 것이다. ansi_xterm과 Midnight Commander (mc-x.x.tar.gz)의 소스를 구하라. ansi_xterm을 컴파일하고 mc-x.x.tar.gz acchive로 부터 xterm.ti와 vt100.ti를 가지고 tic을 사용하라. ansi_xterm을 실행시켜보고 테스트 해 보아라.

 

8.12 커서와 윈도우 좌표 (Cursor and Window Coordinates)

  • int move(y, x)
    int wmove(win, y, x)
    move()는 커서를 y, x로 이동시키고, wmove(win)는 윈도우 win으로 부터 커서를 이동시킨다. 입력/출력 함수들을 위해 지정된 함수들이 호출되기 전에 커서를 이동시키는 부가적인 매크로들이 정의된다.
  • int curs_set(bf)
    이것은 단말기아 이러한 능력을 가지고 있다면, 커서의 눈에 보이는 상태를 on 또는 off로 전환시킬 것이다.
  • void getyx(win, y, x)
    getyx(...)는 현재의 커서 위치를 반환할 것이다.(Note:이것은 매크로이다.)
  • void getparyx(win, y, x)
    win이 서브 윈도우일 때, getparyx(...)는 부모 윈도우와 연관된 윈도우 좌표를 y와 x에 저장할 것이다. 그렇지 않으면 y와 x는 -1이다. (Note:아직 구현되지 않음)
  • void getbegyx(win, y, x)
    void getmaxyx(win, y, x)
    int getmaxx(win)
    int getmaxy(win)
    win의 시작과 크기 좌표를 y와 x에 저장한다.
  • int getsyx(int y, int x)
    int setsyx(int y, int x)
    가상 화면의 커서을 y와 x에 저장하거나 커서를 지정한다. y와 x가 -1이고 getsyx(...)를 호출할 때, leaveok가 지정되어진다.

8.13 스크롤 (Scrolling)

  • int scrollok(win, bf)
    TRUE이면, 윈도우 win의 텍스트는 커서가 하단 오른쪽 구석에 있고 한문자가 타이핑 되어질 때 (또는 newline) 한 줄씩 위로 스크롤 되어질 것이다. FALSE이면, 커서는 같은 위치에 남겨진다.

    on으로 전환될 때 윈도우의 내용들은 다음의 함수들을 가지고 스크롤 되어질 수 있다. (Note:윈도우의 마지막 줄에 new line이 인쇄되면, 또한 스크롤 되어질 것이다. scrollok(...)는 주의해야 한다 그렇지 않으면 터무니없는 결과를 얻을 것이다.

  • int scroll(win)
    이 함수는 윈도우를(자료구조안에 있는 줄들을) 한줄씩 위로 스크롤할 것이다.
  • int scrl(n)
    int wscrl(win, n)
    이러한 함수들은 윈도우 또는 win을 정수 n의 값에 따라 위로 또는 아래로 스크롤할 것이다. n이 양수이면 윈도우는 n줄 위로 스크롤될 것이고 반대로 n이 음수이면 윈도우는 n줄 아래로 스크롤될 것이다.
  • int setscrreg(t, b)
    int wsetscrreg(win, t, b)
    소프트웨어 스크롤링 지역을 지정한다.

다음의 코드는 화면상에서 어떻게 스크롤링 효과를 얻을 것인가를 설명해야만 한다. 예제 디렉토리에 있는 .c를 살펴 보아라.

우리는 18줄과 66컬럼을 가진 윈도우가 있고 그 안의 텍스트를 스크롤하기를 원한다. S[]는 텍스트를 가지고 있는 문자 배열이다. Max_ss[]에 있는 마지막 줄의 번호이다. Clear_line는 현재 커서 위치로 부터 윈도우로 부터 현재의 속성을 가지는 줄의 끝까지 공백 문자를 인쇄할 것이다.(clrtoeol이 하는 것처럼 A_NORMAL이 아니다.) Beg는 현재 화면상에 보여지는 s[]로 부터의 마지막 줄이다. Scroll는 함수에 무엇을 하고, 텍스트로 부터 다음(NEXT)과 이전(PREVious) 줄을 보여 주도록 일일이 말한다.

 

8.14 패드 (Pads)

  • WINDOW *newpad(nlines, ncols)
  • WINDOW *subpad(orig, nlines, ncols, begy, begx)
  • int prefresh(pad, pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol)
  • int pnoutrefresh(pad, pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol)
  • int pechochar(pad, ch)

8.15 소프트-라벨 (Soft-labels)

  • int slk_init(int fmt)
  • int slk_set(int labnum, char *label, int fmt)
  • int slk_refresh()
  • int slk_noutrefresh()
  • char *slk_label(int labnum)
  • int slk_clear()
  • int slk_restore()
  • int slk_touch()
  • int slk_attron(chtype attr)
    int slk_attrset(chtype attr)
    int slk_attroff(chtype attr)
    이러한 함수들은 attron(attr), attrset(attr), attroff(attr)와 관련이 있다. 아직 구현되지 않음.

8.16 잡동사니 (Miscellaneous)

  • int beep()
  • int flash()
  • char *unctrl(chtype c)
  • char *keyname(int c)
  • int filter()
    (Note:아직 구현되지 않음.)
  • void use_env(bf)
  • int putwin(WINDOW *win, FILE *filep)
    (Note:아직 구현되지 않음.)
  • WINDOW *getwin(FILE *filep)
    (Note:아직 구현되지 않음.)
  • int delay_output(int ms)
  • int flushinp()

8.17 Low-level 접근 (Access)

  • int def_prog_mode()
  • int def_shell_mode()
  • int reset_prog_mode()
  • int reset_shell_mode()
  • int resetty()
  • int savetty()
  • int ripoffline(int line, int (*init)(WINDOW *, int))
  • int napms(int ms)

8.18 화면 덤프 (Screen Dump)

  • int scr_dump(char *filename)
    (Note:아직 구현되지 않음.)
  • int scr_restore(char *filename)
    (Note:아직 구현되지 않음.)
  • int scr_init(char *filename)
    (Note:아직 구현되지 않음.)
  • int scr_set(char *filename)
    (Note:아직 구현되지 않음.)

8.19 Termcap Emulation

  • int tgetent(char *bp, char *name)
  • int tgetflag(char id[2])
  • int tgetnum(char id[2])
  • char *tgetstr(char id[2], char **area)
  • char *tgoto(char *cap, int col, int row)
  • int tputs(char *str, int affcnt, int (*putc)())

8.20 Terminfo 함수들(Functions)

  • int setupterm(char *term, int fildes, int *errret)
  • int setterm(char *term)
  • int set_curterm(TERMINAL *nterm)
  • int del_curterm(TERMINAL *oterm)
  • int restartterm(char *term, int fildes, int *errret)
    (Note: 아직 구현되지 않음.)
  • char *tparm(char *str, p1, p2, p3, p4, p5, p6, p7, p8, p9)
    p1 - p9 long int.
  • int tputs(char *str, int affcnt, int (*putc)(char))
  • int putp(char *str)
  • int vidputs(chtype attr, int (*putc)(char))
  • int vidattr(chtype attr)
  • int mvcur(int oldrow, int oldcol, int newrow, int newcol)
  • int tigetflag(char *capname)
  • int tigetnum(char *capname)
  • int tigetstr(char *capname)

8.21 디버그 함수 (Debug Function)

  • void _init_trace()
  • void _tracef(char *, ...)
  • char *_traceattr(mode)
  • void traceon()
  • void traceoff()

8.22 Terminfo 능력 (Capabilities)


8.22.1 논리 능력 (Boolean Capabilities)

 

Variable Cap. Int. Description
Name Code
auto_left_margin bw bw cub1이 컬럼 0부터 마지막 컬럼까지 포함한다(wrap)
auto_right_margin am am 단말기가 자동적으로 여백을 갖는다.
back_color_erase bce ut 화면은 백그라운드 색상으로 지원진다.
can_change ccc cc 단말기는 존재하는 색상들로 재정의될 수 있다.
ceol_standout_glitch xhp xs 표준출력은 overwritong에 의해 지워지지 않는다.(hp)
col_addr_glitch xhpa YA 오직 hpa/mhpa 능력을 위한 적극적인 움직임
cpi_changes_res cpix YF 문자 pitch의 변화는 해상도를 변화시킨다.
cr_cancels_micro_mode crxm YB cr를 사용하여 매크로 모드를 off로 전환한다.
eat_newline_glitch xenl xn newline은 80cols 이후에는 무시된다.
erase_overstrike eo eo 공백을 가지고 overstrikes를 지울 수 있다.
generic_type gn gn 일반적인 줄(line) 타입 (e.g.,, dialup, switch)
hard_copy hc hc 단말기를 하드카피한다.
hard_cursor chts HC 커서를 보기 어렵다.
has_meta_key km km 메타 키를 갖는다 (패리티 비트가 지정된 shift)
has_print_wheel daisy YC 프린터는 문자 집합을 바꾸기위한 연산자(operator)가 필요하다
has_status_line hs hs 여분의 "상태 줄"을 갖는다
hue_lightness_saturation hls hl 단말기는 오직 HLS 색상 표기법만을 사용한다(Tektronix)
insert_null_glitch in in 삽입 모드는 널(null)을 구분한다.
lpi_changes_res lpix YG 줄(line) pitch의 변경은 해상도를 바꾼다.
memory_above da da Display는 화면 위에 계속 유지되어진다.
memory_below db db Display는 화면 아래에 계속 유지되어진다.
move_insert_mode mit mi 삽입 모드 안에서의 이동은 안전하다.
move_standout_mode msgr ms 표준출력 모드안에서의 이동은 안전하다.
needs_xon_xoff nxon nx padding은 작업하지 않을 것이고 xon/xoff가 요구된다.
no_esc_ctl_c xsb xb 붐비는 장소 (f1=escape, f2=ctrl+c)
non_rev_rmcup nrrmc NR smcup는 rmcup를 반대로 하지 않는다.
no_pad_char npc NP pad 문자는 존재하지 않는다.
non_dest_scroll_region ndscr ND 스크롤링 지역은 파괴적이지 않다.
over_strike os os 단말기 overstrikes
prtr_silent mc5i 5i 프린터는 화면에 반향(echo)하지 않는다.
row_addr_glitch xvpa YD vhp/mvpa 능력(caps)를 위한 오직 양의(positive) 움직임
semi_auto_right_margin sam YE 마지막 컬럼의 인쇄는 cr를 야기시킨다.
status_line_esc_ok eslok es Escape은 상태줄 상에서 사용되어질 수 있다.
dest_tabs_magic_smso xt xt Tabs ruin, magic so char (Teleray 1061)
tilde_glitch hz hz Hazel-tine; 's를 인쇄할 수 없다.
transparent_underline ul ul 밑줄 문자 overstrikes
xon_xoff xon xo 단말기는 xon/xoff handshaking을 사용한다.

 

 

 

8.22.2 숫자 (Numbers)

 

Variable Cap. Int. Description
Name Code
bit_image_entwining bitwin Yo SYSV에 문서화되어 있지 않음
buffer_capacity bufsz Ya 인쇄전에 버퍼에 저장된 바이트의 수
columns cols co 한 줄안의 컬럼의 숫자
dot_vert_spacing spinv Yb dots per inch안의 수평 dots의 공간(spacing)
dot_horz_spacing spinh Yc pins per inch안의 수직 pins의 공간(spacing)
init_tabs it it Tabs 최초로 모든 # 공간(spaces)
label_height lh lh 각각의 라벨안의 rows
label_width lw lw 각각의 라벨안의 컬럼(columns)
lines lines li 화면 또는 페이지의 줄(line) 수
lines_of_memory lm lm 양수이면 메모리안의 줄 수, 0은 변함을 의미.
magic_cookie_glitch xmc sg smso 또는 rmso에 의해 남겨진 공백 문자의 수
max_colors colors Co 화면상의 최대 색상의 수
max_micro_address maddr Yd micro_..._address안의 최대값
max_micro_jump mjump Ye parm_..._micro안의 최대값
max_pairs pairs pa 화면상의 색상-쌍(color-pairs)의 최대 수
micro_col_size mcs Yf micro 모드일 때, 문자의 step 크기(size)
micro_line_size mls Yg micro 모드일 때, 줄(line) step 크기(size)
no_color_video ncv NC 색상을 사용할 수 없는 비디오 속성
number_of_pins npins Yh print-head안의 pin의 수
num_labels nlab Nl 화면상의 라벨의 수
output_res_char orc Yi units per line 안의 수평 해상도
output_res_line orl Yj units per line 안의 수직 해상도
output_res_horz_inch orhi Yk units per inch 안의 수평 해상도
output_res_vert_inch orvi Yl units per inch 안의 수직 해상도
padding_baud_rate pb pb cr/nl padding이 필요한 곳의 가장 낮은 baud
virtual_terminal vt vt 가상의 단말기 번호 (UNIX system)
width_status_line wsl ws 상태줄안의 컬럼 번호

(다음의 숫자 능력들(numeric capabilities)은 SYSV 용어 구조안에 표현되어 있지만, 아직 주요 페이지에 문서화되어 있지 않다. 주석문들은 용어 구조 헤더에 있다.)

 

bit_image_type bitype Yp bit-image 장치의 타입
buttons btns BT 마우스 버튼의 수
max_attributes ma ma 단말기가 다룰 수 있는 최대한 조합된 속성
maximum_windows wnum MW 정의될 수 있는 윈도우의 최대 수
print_rate cps Ym char per second의 인쇄 비율
wide_char_size widcs Yn double wide 모드의 문자 step 크기(size)

 

 

 

8.22.3 문자열 (Strings)

Variable Cap. Int. Description

Name Code

acs_chars acsc ac 그래픽 문자집합 쌍들 - def=vt100

alt_scancode_esc scesa S8 scancode emulation을 위한 esc를 교체한다 (디폴트는 vt100)

back_tab cbt bt back tab (P)

bell bel bl 들을 수 있는 신호 (bell)

bit_image_repeat birep Xy bit image cell을 #1 #2 times 반복한다. (tparm 사용)

bit_image_newline binel Zz bit image의 다음 row로 이동한다. (tparm 사용)

bit_image_carriage_return bucr Yv 같은 row의 시작점으로 이동한다. (tparm 사용)

carriage_return cr cr Carriage return (P*)

change_char_pitch cpi ZA inch당 문자의 수를 변경한다.

change_line_pitch lpi ZB inch당 줄(line)의 수를 변경한다.

change_res_horz chr ZC 수평 해상도를 변경한다.

change_res_vert cvr ZD 수직 해상도를 변경한다.

change_scroll_region csr cs 줄(line) #1에서 부터 #2까지 변경한다. (vt100) (PG)

char_padding rmp rP ip와 같지만 삽입 모드일 때

char_set_names csnm Zy 문자 집합 이름들의 목록

clear_all_tabs tbc ct 모든 tab stop을 지운다 (P)

clear_margins mgc MC 모든 여백을 지운다 위,아래,옆)

clear_screen clear cl 화면을 지우고 커서를 home에 위치시킨다. (p*)

clr_bol el1 cb 줄의 시작점을 지운다.

clr_eol el ce 줄의 끝을 지운다. (P)

clr_eos ed cd display의 끝을 지운다 (P*)

code_set_init csin ci 다중 코드 집합들을 위한 Init sequence

color_names colornm Yw 색상 #1을 위한 이름을 준다.

column_address hpa ch 커서 컬럼을 지정한다. (PG)

command_character cmdch CC ptototype안에서 cmd 문자를 지정할 수 있는 단말기

cursor_address cup cm 화면에 연관된 row #1 col #2로의 커서의 움직임 (PG)

cursor_down cud1 do 한줄 아래로 내린다.

cursor_home home ho Home cursor (if no cup)

cursor_invisible civis vi 커서를 보이지 않게 만든다.

cursor_left cub1 le 커서를 한칸 왼쪽으로 이동한다.

cursor_mem_address mrcup CM 메모리와 연관된 커서 어드레싱(addressing)

cursor_normal cnorm ve 커서를 정상적으로 나타나게 만든다(undo vs/vi)

cursor_right cuf1 nd 파괴되지 않는 공간 (cursor right)

cursor_to_ll ll ll 마지막 줄의 첫번째 컬럼 (if no cup)

cursor_up cuu1 up Upline (cursor up)

cursor_visible cvvis vs 커서를 보이게 만든다

define_bit_image_region defbi Yx 사각형의 bit image 지역을 정의한다 (tparm 사용)

define_char defc ZE 문자 집합 안에서의 문자를 정의한다

delete_character dch1 dc 문자를 지운다 (P*)

delete_line dl1 dl 줄(line)을 지운다 (P*)

device_type devt dv language/codeset 지원을 표시한다

dis_status_line dsl ds 상태줄을 사용할 수 없게 한다

display_pc_char dispc S1 PC 문자를 표시한다

down_half_line hd hd 반-줄 아래로 (forward 1/2 linefeed)

ena_acs enacs eA 변경된 문자 집합을 사용가능하게 한다.

end_bit_image_region endbi Yy bit image 지역을 끝낸다 (tparm 사용)

enter_alt_charset_mode smacs as 변경된 문자 집합을 시작한다 (P)

enter_am_mode smam SA 자동 여백으로 전환한다

enter_blink_mode blink mb 깜빡거림으로 전환한다

enter_bold_mode bold md 굵은체(extra bright) 모드로 전환한다.

enter_ca_mode smcup ti cup를 사용하는 프로그램을 시작하기 위한 문자열

enter_delete_mode smdc dm 모드를 지운다(enter)

enter_dim_mode dim mh half-bright 모드로 전환한다

enter_doublewide_mode swidm ZF double-wide 모드를 사용가능하게 한다

enter_draft_quality sdrfq ZG draft-quality 인쇄를 지정한다

enter_insert_mode smir im 삽입 모드 (enter);

enter_italics_mode sitm ZH 이탤릭(italics) 모드를 사용가능하게 한다

enter_leftward_mode slm ZI 왼쪽 방향의 carriage 움직임을 가능하게 한다

enter_micro_mode smicm ZJ micro-motion 능력을 가능하게 한다

enter_near_letter_quality snlq ZK NLQ 인쇄를 지정한다

enter_normal_quality snrmq ZL 일반적인 질(quality)의 인쇄를 가능하게 한다

enter_pc_charset_mode smpch S2 PC 문자 표시(display) 모드로 들어간다

enter_protected_mode prot mp 보호(protected) 모드로 전환한다

enter_reverse_mode rev mr 반전 비디오 모드로 전환한다

enter_scancode_mode smsc S4 PC scancode 모드로 들어간다

enter_secure_mode invis mk 공백(blank) 모드로 전환한다 (문자들은 안 보임)

enter_shadow_mode sshm ZM 그림자 모드(shadow-mode) 인쇄를 가능하게 한다

enter_standout_mode smso so stand out 모드를 시작한다

enter_subscript_mode ssubm ZN 아래 첨자(subscript) 인쇄를 가능하게 한다

enter_superscript_mode ssupm ZO 위첨자(superscript) 인쇄를 가능하게 한다

enter_underline_mode smul us 밑줄(underscore) 모드를 가능하게 한다

enter_upward_mode sum ZP 위로의 carriage 움직임을 가능하게 한다

enter_xon_mode smxon SX xon/xoff handshaking를 전환한다

erase_chars ech ec Erase #1 characters (PG)

exit_alt_charset_mode rmacs ae 변경된 문자 집합을 끝낸다(P)

exit_am_mode rmam RA 자동 여백을 해지한다

exit_attribute_mode sgr0 me 모든 속성을 해지한다

exit_ca_mode rmcup te cup를 사용하는 프로그램들을 끝내기 위한 문자열

exit_delete_mode rmdc ed 삭제 모드를 끝낸다

exit_doublewide_mode rwidm ZQ doublewide 인쇄를 사용 불가능하게 한다

exit_insert_mode rmir ei 삽입 모드를 끝낸다

exit_italics_mode ritm ZR 이탤릭(italic) 인쇄를 불가능하게 한다

exit_leftward_mode rlm ZS 오른쪽으로의(일반적인) carriage 움직임을 가능하게 한다

exit_micro_mode rmicm ZT micro 움직임 능력들을 사용 불가능하게 한다

exit_pc_charset_mode rmpch S3 PC character 표시를 사용 불가능하게 한다

exit_scancode_mode rmsc S5 PC scancode 모드를 사용 불가능하게 한다

exit_shadow_mode rshm ZU 그림자(shadow) 인쇄를 사용 불가능하게 한다

exit_standout_mode rmso se stand out 모드를 끝낸다

exit_subscript_mode rsubm ZV 아래첨자(subscript) 인쇄를 사용 불가능하게 한다

exit_superscript_mode rsupm ue 밑줄(underscore) 모드를 끝낸다

exit_upward_mode rum ZX 아래로의(일반적인) carriage 움직임을 사용가능하게 한다

exit_xon_mode rmxon RX xon/xoff handshaking를 해지한다

flash_screen flash vb 보여지는 bell (커서는 움직이지 않을런지 모른다)

form_feed ff ff Hardcopy terminal page eject (P*)

from_status_line fsl fs 상태 줄로부터 반환한다

init_1string is1 i1 단말기 초기화 문자열

init_2string is2 i2 단말기 초기화 문자열

init_3string is3 i3 단말기 초기화 문자열

init_file if if 포함하고 있는 파일의 이름

init_prog iprog iP 초기화를 위한 프로그램의 패스 이름

initialize_color initc Ic 색상의 정의를 초기화한다

initialize_pair initp Ip 색상의 쌍을 초기화한다

insert_character ich1 ic 문자를 삽입한다(P)

insert_line il1 al 새로운 공백 줄을 더한다(P*)

insert_padding ip ip 삽입된 문자 뒤에 패드를 삽입한다(p*)

key_a1 ka1 K1 Upper left of keypad

key_a3 ka3 K3 Upper right of keypad

key_b2 kb2 K2 Center of keypad

key_backspace kbs kb backspace key에 의해 보내진다

key_beg kbeg 1 begin key

key_btab kcbt kB back-tab key

key_c1 kc1 K4 Lower left of keypad

key_c3 kc3 K5 Lower right of keypad

key_cancel kcan 2 cancel key

key_catab ktbc ka clear-all-tabs key에 의해 보내진다

key_clear kclr kC clear screen or erase key에 의해 보내진다

key_close kclo 3 close key

key_command kcmd 4 command key

key_copy kcpy 5 copy key

key_create kcrt 6 create key

key_ctab kctab kt clear-tab key에 의해 보내진다

key_dc kdch1 kD delete character key에 의해 보내진다

key_dl kdl1 kL delete line key에 보내진다

key_down kcud1 kd terminal down arrow key에 의해 보내진다

key_eic krmir kM 삽입 모드에서 rmir or smir에 의해 보내진다

key_end kend 7 end key

key_enter kent 8 enter/send key

key_eol kel kE clear-to-end-of-line key에 의해 보내진다

key_eos ked kS clear-to-end-of-screen key에 의해 보내진다

key_exit kext 9 exit key

key_find kfnd 0 find key

key_help khlp %1 help key

key_home khome kh home key에 의해 보내진다

key_ic kich1 kI ins char/enter ins mode key에 의해 보내진다

key_il kil1 kA insert line에 의해 보내진다

key_left kcub1 kl terminal left arrow key에 의해 보내진다

key_ll kll kH home-down key에 의해 보내진다 key_mark kmrk %2 mark key

key_message kmsg %3 message key

key_move kmov %4 move key

key_next knxt %5 next key

key_npage knp kN next-page key에 의해 보내진다

key_open kopn %6 open key

key_options kopt %7 options key

key_ppage kpp %8 previous key

key_print kprt %9 print key

key_redo krdo %0 redo key

key_reference kref &1 reference key

key_refresh krfr &2 refresh key

key_replace krpl &3 replace key

key_restart krst &4 restart key

key_resume kres &5 resume key

key_right kcuf1 kr terminal right arrow key에 의해 보내진다

key_save ksav &6 save key

key_sbeg kBEG &9 shifted begin key

key_scancel kCAN &0 shifted cancel key

key_scommand kCMD *1 shifted command key

key_scopy kCPY *2 shifted copy key

key_screate kCRT *3 shifted create key

key_sdc kDC *4 shifted delete char key

key_sdl kDL *5 shifted delete line key

key_select kslt *6 select key

key_send kEND *7 shifted end key

key_seol kEOL *8 shifted end of line key

key_sexit kEXT *9 shifted exit key

key_sf kind kF Sent by scroll-forward/down key

key_sfind kFND *0 shifted find key

key_shelp kHLP #1 shifted help key

key_shome kHOM #2 shifted home key

key_sic kIC #3 shifted insert char key

key_sleft kLFT #4 shifted left key

key_smessage kMSG %a shifted message key

key_smove kMOV %b shifted move key

key_snext kNXT %c shifted next key

key_soptions kOPT %d shifted options key

key_sprevious kPRV %e shifted previous key

key_sprint kPRT %f shifted print key

key_sr kri kR scroll-backward/up key에 의해 보내진다

key_sredo kRDO %g shifted redo key

key_sreplace kRPL %h shifted replace key

key_sright kRIT %i shifted right key

key_srsume kRES %j shifted resume key

key_ssave kSAV !1 shifted save key

key_ssuspend kSPD !2 shifted suspend key

key_stab khts kT Sent by set-tab key

key_sundo kUND !3 shifted undo key

key_suspend kspd &7 suspend key

key_undo kund &8 undo key

key_up kcuu1 ku terminal up arrow key에 의해 보내진다

keypad_local rmkx ke Out of "keypad transmit" mode

keypad_xmit smkx ks Put terminal in "keypad transmit" mode

lab_f0 lf0 l0 Labels on function key f0 if not f0

lab_f1 lf1 l1 Labels on function key f1 if not f1

lab_f2 lf2 l2 Labels on function key f2 if not f2

lab_f3 lf3 l3 Labels on function key f3 if not f3

lab_f4 lf4 l4 Labels on function key f4 if not f4

lab_f5 lf5 l5 Labels on function key f5 if not f5

lab_f6 lf6 l6 Labels on function key f6 if not f6

lab_f7 lf7 l7 Labels on function key f7 if not f7

lab_f8 lf8 l8 Labels on function key f8 if not f8

lab_f9 lf9 l9 Labels on function key f9 if not f9

lab_f10 lf10 la Labels on function key f10 if not f10

label_on smln LO soft labels로 전환한다

label_off rmln LF soft labels를 해지한다

meta_off rmm mo "meta mode"를 해지한다

meta_on smm mm "meta mode" (8th bit)로 전환한다

micro_column_address mhpa ZY Like column_address for micro adjustment

micro_down mcud1 ZZ Like cursor_down for micro adjustment

micro_left mcub1 Za Like cursor_left for micro adjustment

micro_right mcuf1 Zb Like cursor_right for micro adjustment

micro_row_address mvpa Zc Like row_address for micro adjustment

micro_up mcuu1 Zd Like cursor_up for micro adjustment

newline nel nw Newline (lf에 의해 따라오는 cr과 같이 행동한다)

order_of_pins porder Ze Matches software buts to print-head pins

orig_colors oc oc 모든 색상 쌍들을 Reset한다

orig_pair op op 원래의 것으로 디폴트 색상-쌍을 지정한다

pad_char pad pc Pad character (rather than null)

parm_dch dch DC #1 chars (PG*)를 지운다

parm_delete_line dl DL #1 lines (PG*)을 지운다

parm_down_cursor cud DO 커서를 #1 lines (PG*) 아래로 이동시킨다

parm_down_micro mcud Zf Like cud for micro adjust

parm_ich ich IC #1 blank chars (PG*)를 삽입한다

parm_index indn SF 앞으로 #1 lines (PG)을 스크롤한다

parm_insert_line il AL #1 new blank lines (PG*)을 더한다

parm_left_cursor cub LE 커서를 왼쪽으로 #1 spaces (PG)만큼 이동시킨다

parm_left_micro mcub Zg Like cul for micro adjust

parm_right_cursor cuf RI 커서를 오른쪽으로 #1 spaces (PG*)만큼 이동시킨다

parm_right_micro mcuf Zh Like cuf for micro adjust

parm_rindex rin SR 뒤로 #1 lines (PG)만큼 스크롤한다

parm_up_cursor cuu UP 커서를 위로 #1 lines (PG*)만큼 이동시킨다

parm_up_micro mcuu Zi Like cuu for micro adjust

pkey_key pfkey pk Prog funct key #1 to type string #2

pkey_local pfloc pl Prog funct key #1 to execute string #2

pkey_xmit pfx px Prog funct key #1 to xmit string #2

pkey_plab pfxl xl Program key #1 to xmit #2 and show #3

plab_norm pln pn program label #1 to show string #2

print_screen mc0 ps 화면의 내용을 인쇄한다

prtr_non mc5p pO Turn on the printer for #1 bytes

prtr_off mc4 pf 프린터를 끈다

prtr_on mc5 po 프린터를 켠다

repeat_char rep rp 문자를 #1 #2 times 반복한다. (PG*)

req_for_input rfi RF 입역을 위해 요청한다

reset_1string rs1 r1 Reset terminal completely to sane modes.

reset_2string rs2 r2 Reset terminal completely to sane modes.

reset_3string rs3 r3 Reset terminal completely to sane modes.

reset_file rf rf reset 문자열을 포함하고 있는 파일의 이름

restore_cursor rc rc 마지막 sc의 위치로 커서를 재저장한다

row_address vpa cv Vertical position absolute (set row) (PG)

save_cursor sc sc 커서의 위치를 저장한다(P)

scancode_escape scesc S7 Escape for scancode emulation

scroll_forward ind sf 텍스트를 위로 스크롤한다 (P)

scroll_reverse ri sr 텍스트를 아래로 스크롤한다 (P)

select_char_set scs Zj 문자 집합을 선택한다

set0_des_seq s0ds s0 Shift to codeset 0 (EUC set 0, ASCII)

set1_des_seq s1ds s1 Shift to codeset 1

set2_des_seq s2ds s2 Shift to codeset 2

set3_des_seq s3ds s3 Shift to codeset 3

set_a_background setab AB ANSI escape를 사용하는 background 색상을 지정한다

set_a_foreground setaf AF ANSI escape를 사용하는 foreground 색상을 지정한다

set_attributes sgr sa 비디오 속성을 정의한다(PG9)

set_background setb Sb 현재 background 색상을 지정한다

set_bottom_margin smgb Zk 현재 줄의 아래 여백(bottom margin)을 지정한다

set_bottom_margin_parm smgbp Zl 바닥으로 부터 at line #1 or #2 lines에 바닥 줄을 지정한다

set_color_band setcolor Yz 리본 색상을 #1로 바꾼다

set_color_pair scp sp 현재의 색상 쌍을 지정한다

set_foreground setf Sf 현재의 foreground 색상을 지정한다

set_left_margin smgl ML 현재 줄에 왼쪽 여백(left margin)을 지정한다

set_left_margin_parm smglp Zm #1 (#2)에 왼쪽(오른쪽)여백을 지정한다

set_lr_margin smglr ML 왼쪽과 오른쪽 여백 모두를 지정한다

set_page_length slines YZ 페이지의 길이를 #1 lines로 지정한다 (tparm 사용)

set_right_margin smgr MR 현재의 컬럼에 오른쪽 여백을 지정한다

set_right_margin_parm smgrp Zn 컬럼 #1에 오른쪽 여백을 지정한다

set_tab hts st 모든 row의 현재 컬럼에 tab을 지정한다

set_tb_margin smgtb MT 위와 아래 여백 모두를 지정한다

set_top_margin smgt Zo 현재 줄에 위쪽 여백을 지정한다

set_top_margin_parm smgtp Zp line #1에 위 여백을 지정한다

set_window wind wi 현재의 윈도우는 lines #1-#2 cols #3-#4이다

start_bit_image sbim Zq bit image graphics 인쇄를 시작한다

start_char_set_def scsd Zr 문자 집합의 정의를 시작한다

stop_bit_image rbim Zs bit image graphics 인쇄를 끝낸다

stop_char_set_def rcsd Zt 문자 집합 정의를 끝낸다

subscript_characters subcs Zu subscriptable 문자들의 목록

superscript_characters supcs Zv superscriptable 문자들의 목록

tab ht ta Tab to next 8 space hardware tab stop

these_cause_cr docr Zw 이러한 문자들은 CR을 야기시킨다

to_status_line tsl ts 상태줄의 column #1로 간다

underline_char uc uc Underscore one char and move past it

up_half_line hu hu Half-line up (reverse 1/2 linefeed)

xoff_character xoffc XF XON character

xon_character xonc XN XOFF character

(다음의 문자열 능력들은 SYSVr 용어 구조 안에 표현되지만 man page안에는 문서화되어 있지 않다. 주석들은 용어 구조 헤더에 있다.) label_format fln Lf ??

set_clock sclk SC time-of-day 시계를 지정한다

display_clock dclk DK time-of-day 시계를 표시한다

remove_clock rmclk RC time-of-day 시계를 제거한다??

create_window cwin CW Define win #1 to go from #2,#3 to #4,#5

goto_window wingo WG 윈도우 #1로 간다

hangup hup HU Hang up phone

dial_phone dial DI Dial phone number #1

quick_dial qdial QD Dial phone number #1, without progress detection

tone tone TO touch tone dialing을 선택한다

pulse pulse PU pulse dialing을 선택한다

flash_hook hook fh Flash the switch hook

fixed_pause pause PA 2 ~ 3초간 정지한다

wait_tone wait WA Wait for dial tone

user0 u0 u0 사용자 문자열 # 0

user1 u1 u1 사용자 문자열 # 1

user2 u2 u2 사용자 문자열 # 2

user3 u3 u3 사용자 문자열 # 3

user4 u4 u4 사용자 문자열 # 4

user5 u5 u5 사용자 문자열 # 5

user6 u6 u6 사용자 문자열 # 6

user7 u7 u7 사용자 문자열 # 7

user8 u8 u8 사용자 문자열 # 8

user9 u9 u9 사용자 문자열 # 9

get_mouse getm Gm Curses 버튼 이벤트를 가져야만 한다

key_mouse kmous Km ??

mouse_info minfo Mi 마우스 상태 정보

pc_term_options pctrm S6 PC 단말기 선택사항

req_mouse_pos reqmp RQ 마우스 위치에 대한 보고를 요청한다

zero_motion zerom Zx 뒤따라오는 문자를 위한 움직임은 없다

 

8.23 [N]Curses 함수 개관 ([N]Curses Function Overview)

다음의 텍스트에서 다른 (n)curses 패키지를 통한 개요(overview)를 찾을 수 있을 것이다. 첫번째 컬럼은 bsd-curses이고(슬랙웨어 2.1.0과 Sun-OS 4.x에서 처럼) 두번째는 sysv-curses (Sun-OS 5.4/Solaris 2)이고 세번째는 ncurses(버전 1.8.6)이다.

네번째는 함수가 (실제로 설명되어있다면) 설명되어있는 텍스트의 페이지를 참조한다.

x
패키지는 이 함수를 가진다.
n
함수는 아직 구현되지 않음.

계속......

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

int main()
{
      int shmid;
      char *shared_memory;

      shmid = shmget(7530, 1028, IPC_CREAT|0666);


      shared_memory = shmat(shmid, NULL, 0);

      printf("talk is : %s\n", shared_memory);
      return 0;
}

리눅스용으로 만들어진 소스구요..

주석좀 자세히 알려주시면 감사하겠습니다.

shmget,shmat에 관해서 자세히 설명해주시고요.

공유메모리 관련이라고 하는데 도저히 설명을 못찾아서 여기 올립니다.




< 답 변 >


shmat, shmget 는 공유 메모리 관련 함수 입니다.

 

이 함수들은 서로 다른 Process 들끼리 공통으로 사용할 수 있는 메모리를

 

지원합니다.

 

shmget 는 메모리를 할당하는 역할을 하고



shmat 는 할당된 메모리를 시스템이 공통으로 사용할 수 있는 영역에 붙이는 작업을 합니다.

 

일반적으로 사용하는 방법은 이렇게 사용하는 것 보다 다르게 사용 합니다.

 

1. 파일 Open (open 사용)

 

2. 파일 메모리 할당 (mmap 사용)

 

3. 파일 핸들을 공유 메모리영역에 할당 (shmat 사용)

 

차례로 설명하겠습니다.

 

1번에서는 open 함수를 써서 파일을 open 합니다.

 

2 에서는 파일의 모든 내용을 읽어오기 위해서 mmap 함수를 써서 파일의

 

모든 내용을 메모리와 동기화 합니다.

 

mmap 함수는 파일과 메모리를 서로 동기화하여 파일을 write 할 때도 메모리에

 

write 하는 것 처럼 사용하고, 시스템에서는 메모리와 파일을 동기화 작업을 합니다.

 

하지만 동기화는 자주 일어나는 것이 아니기 때문에 아주 정확한 데이터가 파일에

 

저장되길 원하면 msync 함수를 써서 일정 시간마다 동기화 합니다.

 

참, mmap 함수를 사용할 때 SHM_MAP 을 꼭 사용하여야 나중에 다른 프로세서와

 

공유가능 합니다.

 

3에서는 1번에서 사용한 핸들을 공유메모리에 붙입니다. 그러면 open 한 파일은

 

특정 프로세서를 기준으로 보면

 

1. 파일과 메모리

2. 파일과 메모리, Shared 메모리

 

와 모두 동기화 됩니다.

 

물론 다른 프로세서에서도 동일한 작업을 하게 됩니다. 그러면 다른 프로세서에서도

 

3. 파일과 메모리, Shared 메모리, 타 프로세서에서의 동기화가 모두 이루어 집니다.

 

이것의 장점은 데이터의 크기가 큰 것들을 다른 프로세서와 모두 동기화가 가능하다

는 것 입니다.

 

그리고 속도도 어떤 것 들보다 빠릅니다.

 

일반적으로 프로세서와의 통신은

 

메시지큐, FIFO, 파이프, 소켓등이 있습니다만 이것만큼 빠르게 큰 데이터를 공유할

 

있는 것이 없을 겁니다.

 

아주 좋은 것들이니 잘 익혀서 사용하시면 좋을 것 같군요 ....

 

 

도움이 도시길 ....

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

1장. shmget(2)

공유메모리 영역을 할당한다.


1.1. 사용법

 

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

 


1.2. 설명

shmget()은 주어진 인자 key를 접근번호로 하는 공유메모리 공간할당을 커널에 요청한다. 커널에서 성공적으로 공유메모리 공간을 할당하게 되면 공유메모리를 가르키는 식별자를 리턴하게 된다. 생성될 공유메모리 공간의 크기는 size를 통해서 byte 단위 크기로 지정할 수 있다. 공간의 할당은 shmflg가 IPC_PRIVATE이거나 key 를 가지는 공유메모리영역이 존재하지 않거나, IPC_CREAT가 지정되었을 경우 (shmflg&IPC_CREAT가 0이 아닌)에 이루어진다.

다음은 사용가능한 shmflg값들이다.

IPC_CREAT

새로운 영역을 할당한다. 만약 이 값이 사용되지 않았다면, shmget()은 key로 이미 생성된 접근 가능한 공유메모리 영역이 있는지 확인하고 이에 대한 식별자를 되돌려줄 것이다.

IPC_EXCL

IPC_CREAT와 함께 사용하며 공유메모리 영역이 이미 존재하면 에러를 리턴한다.

mode_flags(하위 9bit)

접근 권한의 지정을 위해서 사용한다. 실행권한은 사용하지 않는다.

만약 새로운 영역이 생성되었다면 shmflg의 권한정보는 영역에 대한 정보가 정의되어 있는 shmid_ds 구조체의 멤버인 shm_perm으로 복사된다. shmid_ds 구조체는 아래와 같이 정의되어 있다.

struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
time_t shm_atime; /* last attach time */
time_t shm_dtime; /* last detach time */
time_t shm_ctime; /* last change time */
unsigned short shm_cpid; /* pid of creator */
unsigned short shm_lpid; /* pid of last operator */
short shm_nattch; /* no. of current attaches */
};

struct ipc_perm {
key_t key;
ushort uid; /* owner euid and egid */
ushort gid;
ushort cuid; /* creator euid and egid */
ushort cgid;
ushort mode; /* lower 9 bits of shmflg */
ushort seq; /* sequence number */
};
만약 공유 메모리 영역이 이미 존재한다면 접근권한은 수정된다.

 


1.3. 시스템 호출에 대한 반응

 

fork()

attache된 공유 메모리 공간은 자식 프로세스에게 상속된다.

exec()

exec()가 호출된 후에는 모든 공유 메모리 공간은 detache된다.

exit()

exit()후 모든 공유 메모리 공간은 detache된다 (없어 지지는 않는다).


1.4. 반환값

성공하면 shmid를 반환, 실패하면 -1을 반환한다.


1.5. 에러

 

EINVAL

공유 영역생성시 너무 작은 공간을 할당 하거나(size < SHMMIN) 너무 큰 공간(size > SHMMAX)을 할당했을 경우

EEXIST

 


EACCESS

IPC_CREAT | IPC_EXCL 로 생성을 요청했는데, 이미 공유 메모리 영역이 존재하고 있을 경우,

ENOSPC

size만큼의 공간을 할당할 수 없을 경우. 사용가능한 공간은 SHMALL값으로 커널전역적으로 정의되어 있다. (/usr/include/linux/shm.h 참고)

ENOENT

IPC_CREAT를 사용하지 않았는데, 해당 key를 가지는 공유 메모리 영역이 존재 하지 않는 경우

EACCESS

공유 메모리 영역에 대한 접근권한이 주어져 있지 않다.

ENOMEM

이용할 수 있는 커널 메모리가 충분하지 않다.


1.7. 참고문헌

 

  1. 공유 메모리를 이용한 프로세스간 데이터 교환

  2. ftok(3)

  3. ipc(5)

  4. shmctl(2)

  5. shmat(2)

  6. shmdt(2)

:::
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 스마트 돌고래 Smart Dolphine

1장. shmat(2)

차례
1.1. 사용법
1.2. 설명
1.3. 반환값
1.4. 에러
1.5. 예제
1.6. 참고문헌

공유메모리 관련 연산


1.1. 사용법

 

#include <sys/ipc.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);

 


1.2. 설명

shmat()는 공유메모리 식별자인shmid에 공유 메모리 세그먼트를 붙이기 위해서 사용한다. 붙이는 영역은 shmaddr로 결정할 수 있다.

만약 shmaddr가 NULL이라면 시스템은 적당한 사용하지 않는 메모리 영역을 붙이게 된다.

만약 shmaddr가 NULL이 아니고 SHM_RND가 shmflg로 지정되어 있다면 shmaddr은 SHMLBA의 배수의 주소로 연결이 발생한다. 그렇지 않으면 shmaddr은 연결할 수 있는 정렬된 페이지 주소여야 한다.

만일 SHM_RDONLY가 shmflg에 지정되었다면, 세그먼트는 읽기전용이 되며 공유메모리 영역에 접근하고자 하는 프로세스는 읽기전용허가 접근권을 가져야 한다. 그렇지 않을경우 세그먼트는 읽기/쓰기로 붙여지며 프로세스는 (반드시)읽기/쓰기 허가권을 가져야한다. 쓰기전용 공유메모리 세그먼트를 위한 플래그는 없다.

프로세스가 종료되면 연결된 세그먼트는 자동적으로 분리된다. 동일한 세그먼트는 읽기와 읽기/쓰기로 한번이상 연결시킬 수 있다.

shmat가 성공하면 시스템은 shmid_ds구조체의 멤버들을 아래와 같이 업데이트 시킨다.

  1. shm_atime을 현재 시간으로 수정한다.

  2. shm_lpid를 현재 호출한 프로세스의 ID로 설정한다.

  3. shm_nattch는 1 증가 시킨다.

공유메모리 세그먼트가 삭제로 표시될 때에도 마찬가지로 분리된다.

shmdt()는 공유 메모리 영역으로 부터 shmaddr 주쇼를 분리 시키기 위해서 사용한다. 공유메모리 영역의 분리는 shmat 시스템 콜로 연결된 값과 동일한 shmaddr을 가지고 있는 연결된 영역들중 하나여야 한다.

shmdt()가 성공적으로 호출되면 shmid_ds구조체의 멤버를 다음과 같이 변경한다.

  1. shm_dtime을 현재 시간으로 변경한다.

  2. shm_lpid를 현재 호출한 프로세스의 ID로 변경한다.

  3. shm_nattch을 1 감소 시킨다. 만약 값이 0이되고 세그먼트에 삭제표시가 되어 있다면 세그먼트는 삭제된다.

호출 프로세스의 유저공간에 점유된 영역은 대응이 풀리지 않는다.

1.3. 반환값

성공하면 attach된 shared memory segment를 반환하고 실패하면 -1을 반환한다.


1.4. 에러

 

EACCES

호출한 프로세스가 붙이기 원하는 영역에 대해서 권한을 가지고 있지 않다.

EINVAL

잘못된 shmid 값, 혹은 잘못된 shmaddr 값을 가지고 있다.

ENOMEM

메모리할당을 할 수 없다.


1.5. 예제

 

#include <sys/ipc.h> 
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>


int main()
{
int shmid;
int pid;

int *cal_num;
void *shared_memory = (void *)0;


// 공유메모리 공간을 만든다.
// 크기는 4byte로 한다.
shmid = shmget((key_t)1234, sizeof(int), 0666|IPC_CREAT);

if (shmid == -1)
{
perror("shmget failed : ");
exit(0);
}

// 프로세스 메모리를 공유메모리영역에 붙인다.
shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1)
{
perror("shmat failed : ");
exit(0);
}

cal_num = (int *)shared_memory;
pid = fork();
if (pid == 0)
{
shmid = shmget((key_t)1234, sizeof(int), 0);
if (shmid == -1)
{
perror("shmget failed : ");
exit(0);
}
shared_memory = shmat(shmid, (void *)0, 0666|IPC_CREAT);
if (shared_memory == (void *)-1)
{
perror("shmat failed : ");
exit(0);
}
cal_num = (int *)shared_memory;
*cal_num = 1;

while(1)
{
*cal_num = *cal_num + 1;
printf("child %d\n", *cal_num);
sleep(1);
}
}

// 부모 프로세스로 공유메모리의 내용을 보여준다.
else if(pid > 0)
{
while(1)
{
sleep(1);
printf("%d\n", *cal_num);
}
}
}

 


1.6. 참고문헌

 

  1. 공유 메모리를 이용한 프로세스간 데이터 교환

  2. ftok(3)

  3. ipc(5)

  4. shmctl(2)

  5. shmat(2)

  6. shmdt(2)

이 문서는 수정될 수 있습니다. 최신문서는 Joinc Wiki에서