curses 메뉴화면 만들어봤습니다.

angra의 이미지

회사에서 쓸일이 생겨서 첨으로 curses 메뉴 화면 만들어봤습니다
인터넷에 있는 소스를 가져다가 입맛에 맞게 수정했는데, 좀 더
좋은 방법들이 많을듯하나 지금은 잘 모르겠네요. curses에서
제공하는 menu 라이브러리가 있지만 생각보다 쓰기 편한건 아니더군요
그래서 그냥 메뉴화면을 만들었습니다.

^^

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

#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
 
#define ESCAPE 27
#define ENTER 10
 
char *seq_item[] = {"",
                    "SUB_메뉴1", 
                    "SUB_메뉴2", 
                    "SUB_메뉴3", 
                    "SUB_메뉴4",
                    "SUB_메뉴5"};
char *tr_item[] = {"",
                    "SUB_메뉴1", 
                    "SUB_메뉴2", 
                    "SUB_메뉴3", 
                    "SUB_메뉴4"};
char *proc_item[] = {"",
                    "SUB_메뉴1", 
                    "SUB_메뉴2", 
                    "SUB_메뉴3", 
                    "SUB_메뉴4"};
 
char *mng_item[] = {"", 
                    "SUB_메뉴1",
                    "SUB_메뉴2",
                    "SUB_메뉴3",
                    "quit"};
 
/* title, status will be globall refresh */
WINDOW *titlebar, *statusbar;
WINDOW  *menubar;
WINDOW *dataview;
 
extern void refresh_status(char*);
extern void Init_Mana(int, char**);
extern void view_tr(WINDOW *, int , int);
extern void view_proc(WINDOW *, int);
extern void view_seq(WINDOW *, int);
 
void init_screen()
{
    initscr();
    start_color();
    noecho();
    keypad(stdscr, TRUE);
}
 
void end_screen()
{
    delwin(titlebar);
    delwin(statusbar);
    delwin(dataview);
    delwin(menubar);
    endwin();
 
    exit(0);
}
 
static void end_proc(int p_signo)
{
    char        _Log_Buf [BUFSIZ];
 
    strcat(_Log_Buf, " 프로세스를 종료합니다.");
    refresh_status(_Log_Buf);
    end_screen();
}
 
static void        Setsigfatal(void)
{
    signal (SIGINT,     end_proc);
    signal (SIGKILL,    end_proc);
    signal (SIGQUIT,    end_proc);
    signal (SIGILL,     end_proc);
    signal (SIGTERM,    end_proc);
    signal (SIGBUS,     end_proc);
    signal (SIGSEGV,    end_proc);
    signal (SIGHUP,     end_proc);
 
    return;
}
 
void refresh_title()
{
    time_t      tloc;
    struct tm   *tp;
    char        date_time[60];
 
    time (&tloc);
    tp = localtime(&tloc);
 
    strftime(date_time, 22, "%Y/%m/%d  %H:%M:%S", tp);
 
    wmove(titlebar, 0, 2);
    waddstr(titlebar, "메뉴 테스트 시스템");
    wmove(titlebar, 0, 60);
    waddstr(titlebar, date_time);
    wrefresh(titlebar);
}
 
void refresh_status(char *msg)
{
    char blank[80];
 
    memset(blank, 0x20, sizeof(blank));
 
    wbkgd(statusbar, A_REVERSE);
    wmove(statusbar, 0, 1);
    wprintw(statusbar,"☞ ");
    wmove(statusbar, 0, 4);
    wprintw(statusbar, blank);
    wmove(statusbar, 0, 4);
    wprintw(statusbar, msg);
    wrefresh(statusbar);
}
 
void make_menubar(WINDOW *menubar)
{
    box(menubar, 0,0);
    wbkgd(menubar, A_REVERSE);
 
    wmove(menubar, 1, 5);
    wattron(menubar, A_REVERSE);
    waddstr(menubar, "메뉴1");
    waddstr(menubar, "(s)");
 
    wmove(menubar, 1, 25);
    wattron(menubar, A_REVERSE);
    waddstr(menubar, "메뉴2");
    waddstr(menubar, "(t)");
 
    wmove(menubar, 1, 45);
    wattron(menubar, A_REVERSE);
    waddstr(menubar, "메뉴3");
    waddstr(menubar, "(p)");
 
    wmove(menubar, 1, 65);
    wattron(menubar, A_REVERSE);
    waddstr(menubar, "메뉴4");
    waddstr(menubar, "(m)");
 
}
 
void menu_list(WINDOW **items, char **menu, int cnt, int start_col)
{
    int i;
 
    items[0]= newwin(cnt+1,19, 3,start_col);
    box(items[0], ACS_VLINE, ACS_HLINE);
 
    for (i =1; i < cnt; i++)
        items[i]=subwin(items[0], 1, 17, i+3, start_col+1);
 
    for (i =1; i < cnt; i++)
        wprintw((WINDOW *)items[i], "%s",menu[i]);
 
    wbkgd(items[1], A_UNDERLINE|A_STANDOUT);
    wrefresh(items[0]);
 
    return;
}
 
int scroll_menu(WINDOW **items,int count,int menu_start_col, int index_num)
{
    int key;
    int selected=1, before = 1;
 
    while (1) 
    {
        halfdelay(100);
        switch((key=getch())) {
            case KEY_DOWN:
                selected=(selected+1) % count;
                if (selected == 0)
                    selected = 1;    
                break;
            case KEY_UP: 
                selected=(selected+count-1) % count;
                if (selected == 0)
                    selected = count - 1;    
                break;
            case KEY_LEFT: 
                return key;
            case KEY_RIGHT: 
                return key;
            case ESCAPE: 
                return -1;
            case ENTER: 
                return selected;
            default:
                return -1;
        }
 
        if (selected != before)
        {
            wbkgd(items[before], COLOR_PAIR(0));
            wnoutrefresh(items[before]);
            before = selected;     
        }
        wbkgd(items[selected],A_REVERSE);
        wnoutrefresh(items[selected]);
        doupdate();
    }
}
 
void delete_menu(WINDOW **items, int count)
{
    int i;
 
    for (i = 0; i < count; i++)
        delwin((WINDOW *)items[i]);
}
 
int check_quit()
{
    WINDOW *check;
    int key;
 
    check = newwin(3, 30, 10, 15);
    wmove(check, 1, 4);
    wprintw(check, " Exit program (y/n) ? "); 
    box(check, ACS_VLINE, ACS_HLINE);
    refresh();
    key = wgetch(check);
    delwin(check);
    if (key == 'y')
        return 1;
    else 
        return 0;
}
 
/* 사용자가 선택할 수 있는 메뉴의 갯수는 10개보다 작다는 가정하에 작업.
 * key 입력값은 항상 '0'보다 큰 값들이 발생한다고 가정.
 */
int move_menu(int presel, int key, WINDOW **menu_items)
{
    int sitem = key;
    int before_pos = '0';
 
    while (sitem > '0') {
        switch(sitem) {
            case KEY_RIGHT:
                if (before_pos < '4' && before_pos >= '1')
                    sitem = before_pos + 1;
                else if (before_pos == '4') 
                    sitem = '1';
                else
                    sitem = 0;
                break;
            case KEY_LEFT :
                if (before_pos > '1' && before_pos <= '4')
                    sitem = before_pos - 1;
                else if (before_pos == '1')
                    sitem = '4';
                else
                    sitem = 0;
 
                break;
            case '1':
            case 's':
                before_pos = '1';
                menu_list(menu_items, seq_item, 6, 5);
                sitem = scroll_menu(menu_items, 6, 5, 0); 
                delete_menu(menu_items, 6);
 
                if(sitem < 10 && sitem >= 0)
                    return 100+sitem;
                break;
            case '2':
            case 't':
                before_pos = '2';
                menu_list(menu_items, tr_item, 5, 25);
                sitem = scroll_menu(menu_items, 5, 20, 0); 
                delete_menu(menu_items, 5);
 
                if(sitem < 10 && sitem >= 0)
                    return 200+sitem;
                break;
            case '3':
            case 'p':
                before_pos = '3';
                menu_list(menu_items, proc_item, 5, 45);
                sitem = scroll_menu(menu_items, 5, 40, 0); 
                delete_menu(menu_items, 5);
 
                if(sitem < 10 && sitem >= 0)
                    return 300+sitem;
                break;
            case '4':
            case 'm':
                before_pos = '4';
                menu_list(menu_items, mng_item, 5, 60);
                sitem = scroll_menu(menu_items, 5, 60, 0); 
                delete_menu(menu_items, 5);
 
                if (sitem == 2)
                {
                    sitem = 'q';
                    break;
                }
                if(sitem < 10 && sitem >= 0)
                    return 400+sitem;
                break;
            case 'q':
                if( check_quit() )
                    end_screen();
            default:
                //usleep(100);
                return presel;
        }
        touchwin(stdscr);
        refresh_title();
        refresh();    
    }
    return presel;
}
 
int main(int argc, char **argv)
{
    WINDOW *menu_items[10];
    int key;
    int row, col;
    int sel;
 
    Setsigfatal ();
    init_screen();
 
    titlebar = subwin(stdscr, 1, 80, 0, 0);
    menubar = subwin(stdscr, 3, 80, 1, 0);
    dataview = subwin(stdscr, 36, 80, 4, 0);
    statusbar = subwin(stdscr, 1, 79, 40, 1);
 
    getmaxyx(stdscr,row,col);
    if (row < 40 || col < 80)
    {
        refresh_status("터미널의 사이즈가 너무 작습니다");
        end_screen();
        return 0;
    }
 
    refresh_title();
    make_menubar(menubar);
    refresh_status("메뉴를 선택하세요");
    refresh();
 
    while(1)
    {
        halfdelay(100);
        key = getch();
 
        if ( ( key == 'q' || key == ESCAPE ) 
            && check_quit() )
            break;
 
        if ( key != KEY_UP
            && key != KEY_DOWN
            && key != ERR)
            sel = move_menu(sel, key, menu_items);
 
        switch((int)sel/100) {
            case 1:     
                break;
            case 2:    
                break;
            case 3:    
                break;
            case 4:    
                break;
            default:
                refresh_status("메뉴를 선택하세요");
                break;
        }
 
        touchwin(stdscr);
        refresh_title();
        refresh();    
    }
 
    end_screen();
    return 0;
}
Forums: 
codebank의 이미지

한참 자체한글 열풍이 불고 있을 당시에 몇몇 책에서 메뉴와 관련된 함수들이나 화면제어에
관련한 소스들이 있었고 몇몇분들이 통신에 자신들이 만든 소스를 올려놓으셨지만 결국
쓰기편한건 자신이 만든 방법이었었죠.
저도 나름 머리를 쓴다고 범용적인 메뉴를 만들기는 했었는데 소스를보니 그때 생각이
나네요. 어딘가 찾아보면 플로피 디스크에 메뉴에 관련된 소스가 있을텐데 요즘은 귀찮은것이
많네요. 예전에는 엄청 부지런했었다고 생각하는데... :-)

그나저나 #include에 관련된 코드가 깨져보이네요. '<'나 '>'는 HTML에서도 사용되는
코드라서 '& l t ;'나 '& g t ;'(물론 공백이 없어야합니다. :-))이런식으로 표시를
해줘야하는데...
------------------------------
좋은 하루 되세요.

------------------------------
좋은 하루 되세요.

bushi의 이미지

[ code]
[/ code]


&lt;code&gt;
&lt;/code&gt;

는 다릅니다.

글 입력 상자 바로 밑에 "입력형식" 링크가 존재합니다.
시간나면 호기심으로라도 한번 눌러보세요.

OTL

madman93의 이미지

다음에 유용하게 써 먹을 수 있겠군요!!!

---------------------------------------------
svn + trac + my project --> success ???
---------------------------------------------

---------------------------------------------
git init
git add .
git commit -am "project init"
---------------------------------------------

doodoo의 이미지

컴파일 한번 해보려니깐...에러 발생입니다

cc -o cu cu.c -lcurses
cu.c: In function ‘refresh_title’:
cu.c:93: warning: assignment makes pointer from integer without a cast
cu.c:95: warning: incompatible implicit declaration of built-in function ‘strftime’

libncurses5-dev libncurses5 두개 패키지를 apt-get으로 설치했구요...
    time_t      tloc;
    struct tm   *tp;
    char        date_time[60];
 
    time (&tloc);
    tp = localtime(&tloc);
 
    strftime(date_time, 22, "%Y/%m/%d  %H:%M:%S", tp);

tp = localtime(&tloc); 가 93줄 인데..에러 날것이 있었나요? 이상하네요
그리고 strftime도 없을껏 같은데...씨! 를 잘 모르니...ㅠㅠ
doodoo의 이미지

소스에 #include <time.h> 붙여서(순전히 통밥으로 찍었습니다....) 컴파일은 성공했지만
프로그램을 실행하니 아무것도 안뜨고 그냥 프롬프트로 나오네요..

무언가 화면에서 실행되어야 하는것 아닌가요?

revizes의 이미지

추가

#include <locale.h>
#include <ncurses.h>

삭제

#include <curses.h>

main함수에 추가

setlocale(LC_ALL, "ko_KR.utf8");
setlocale(LC_CTYPE, "ko_KR.utf8");

빌드

gcc -o menu menu.c -lncursesw

그리고 창의 크기를 80x40 이상으로...

다 덤벼! 다 받아줄께!!

다 덤벼! 다 받아줄께!!

doodoo의 이미지

doodoo@doodoo:~$ !cc
cc -o cu cu.c -lncursesw
doodoo@doodoo:~$ ls -l cu*
-rwxr-xr-x 1 doodoo doodoo 18540 2008-12-18 10:24 cu
-rw-r--r-- 1 doodoo doodoo 9478 2008-12-18 09:40 cu.c
doodoo@doodoo:~$
doodoo@doodoo:~$ ./cu
doodoo@doodoo:~$

여전히 아무것도 안나오고 프롬프트만 나오는 군요...

doodoo@doodoo:~$ ldd ./cu
linux-vdso.so.1 => (0x00007fff48bfe000)
libncursesw.so.5 => /lib/libncursesw.so.5 (0x00007f95406de000)
libc.so.6 => /lib/libc.so.6 (0x00007f954037c000)
libdl.so.2 => /lib/libdl.so.2 (0x00007f9540178000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9540925000)
doodoo@doodoo:~$
libncursesw.so.5 가 걸려있는건 맞는것 같은데...흠....

혹시 바이너리 파일 사이즈좀 알려 주실수 있나요?

sakuragi의 이미지

터미널 사이즈가 작으면 아무것도 안나오고 프롬프트만 나오더군요.

----------

http://sakuragi.org

doodoo의 이미지

그랬었군요...ㅠㅠ
감사합니다...

RS의 이미지

Thanks for the code. It was very useful. By the way, there is quite a lot of good information at shared files SE http://www.sharedshares.com .

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.