본문 바로가기

About 배우고 익히는법/펌글

[Linux] NCURSES Programming HOWTO

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