2012년 12월 31일 월요일

입력장치의 출력기능?

버티고 버티다 이제야 겨우 옆에 두게 된 스마트폰을 쓰다 보니,
신기하고 편리한 장점들을 비집고 불편한 점이 고개를 내밀기 시작합니다.

그 가운데에서 (아마도 많은 분들이 이미 오래전부터 수없이 지적했으리라 생각되는)
문제점은 입력의 어려움이 아닐까 싶습니다.

가상 키보드를 통한 문자의 입력과,
게임 등에서 조이스틱/조이패드를 흉내낸 가상패드에 의한 입력이 그것인데요...



캡콤의 레지던트이블 (좌측 하단이 가상 조이패드)


이러한 가상의 입력장치들이 문제가 되는 원인을 생각하다 보니,
스마트폰 혹은 터치스크린의 크기 제약에 따른 문제점이 아니라,
출력기능의 부족(?)이 원인이 아닌가 하는 생각이 들었습니다.


좀 헷갈릴 수도 있는데, 우선 정보의 전달 방향에 따라 다음과 같이 정의를 해 보겠습니다.
[입력]은 (사람 -> 장치)
[출력]은 (장치 -> 사람)

스마트폰의 가상키보드는 [입력] 기능에 [출력] 기능이 보태어져 있습니다.
위의 사진에서 보듯, 눌린 키가 어떤 것인지 시각적으로 보여주는 것이 [출력] 기능입니다.
(메시지 입력란에 출력된 문자는 가상키패드의 출력이 아니라 메시지앱의 처리 결과이므로 별개의 성격입니다.)

게임의 가상조이패드를 보면 문제가 더 명확해지는데, 이 조이패드의 [출력] 기능은 눌려진 방향으로 패드 중앙의 원이 움직이는 것인데, 통상 두툼한 엄지손가락에 가려져 잘 보이지 않아 출력 기능이 매우 취약한 편입니다.


그렇다면 원래 입력장치인 이런 장치들의 모범적인 [출력] 기능은 무엇일까요?
바로 "촉각"입니다.
데스크탑/노트북 컴퓨터에서 사용하는 키보드는 강도와 특성에 차이는 있지만 "키감"이라는 느낌을 [출력]하고 있습니다. (장치에 따라서는 소리도 한 몫 할 수 있습니다.)

조이스틱 혹은 조이패드는 이 촉각의 출력이 매우 명확하기에 사용자가 신뢰하는 장치가 되었는지도 모르겠습니다.


스마트폰 이전에는 고유한 [입력] 장치로만 인식되었던 키보드와 조이스틱/조이패드가, 사실은 그 위치를 유지할 수 있었던 것이 그들의 [출력] 기능 때문이 아니었나 새삼 돌아보게 됩니다.

2012년 12월 14일 금요일

GTK+ on MS-Windows

MS-Windows에서 GUI를 작성하는 방법은 Win32 SDK를 근본으로 합니다.

Win32 SDK에서 파생된 것으로 Microsoft의 MFC가 있으며, 그 외에도 여러가지 GUI 라이브러리들이 존재하는데, Linux에서 시작된 GTK+라는 라이브러리를 이용하는 방법을 알아보고자 합니다.

GTK+는 Gimp Tool Kit의 약자로 Gimp라는 유명한 공개 Graphic Software가 있는데,
아마도 이 software를 개발하면서 만든 라이브러리를 범용으로 확장한 것이 아닌가 추측이 됩니다.

Gimp의 특징은 Linux / MS-Windows / Mac 등에서 모두 사용이 가능하다는 점과 C 언어 인터페이스를 제공하므로 C와 C++에서 모두 사용이 가능하는 장점이 있습니다.

홈페이지는 http://www.gtk.org 입니다.

MS-Windows용 라이브러리는 다음에서 받을 수 있습니다.
http://www.gtk.org/download/win32.php

GTK+는 여러가지 라이브러리가 모여서 하나의 패키지를 이루고 있습니다.
이 모든 것을 별도로 받을 수도 있으나 번거로우므로 all-in-one bundle을 제공합니다.
http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip

다운로드 받아서 적당한 폴더에 압축을 풀면 다음과 같은 형태의 파일과 폴더가 나옵니다.
GTK+ 압축을 푼 결과

저기에 보이는 README 파일을 읽어 보면 해야할 일이 간단하게 나옵니다.

1) bin 폴더를 PATH에 추가한다.
2) "pkg-config --cflags gtk+-2.0"를 수행했을 때 적절한 문자열이 나오는지 확인한다.
3) "gtk-demo"가 제대로 수행되는지 확인한다.
4) MS-Windows theme engine을  사용하려면, etc/gtk-2.0/gtkrc에
    gtk-theme-name = "MS-Windows"
   라는 줄을 넣어 준다.

2번의 pkg-config는 여러가지 옵션이 있는데, 자세한 사용법은 "pkg-config --help"로 찾아 볼 수 있습니다.
몇가지 사용 결과는 다음과 같습니다.
pkg-config 사용 예

3번의 gtk-demo를 수행하면 다음과 같이 실행이 됩니다.

여러가지 GUI component에 대한 예제를 볼 수 있으며 그 소스도 참조가 가능합니다.

그런데 이 데모를 수행하면서 다음과 같은 경고/에러 메시지가 나오는군요.
라이브러리 소스를 따로 받아서 확인해야 할 듯 합니다.


4번째의 Theme Engine이라는 것은 효과가 있는지 없는지 아직은 잘 모르겠습니다.


이제는 실제 코딩하여 빌드하는 것이 가능한지 확인해 보겠습니다.

Code:Blocks에서 Project 종류로 GTK+를 선택하면 몇가지 설정 중, GTK+의 경로를 물어보는 과정이 있습니다.
그리고 GTK+를 처음 사용하는 것일 경우에는 다음과 같이 GTK+에 대한 Global Variable 설정을 해 주어야 합니다.
GTK+에 대한 설정 화면
여기에 base, include, lib를 지정해 주고, cflags는 "pkg-config --cflags gtk+-2.0"에서 나온 문자열을 넣어 줍니다. (cflags는 효과가 있는지 의심스럽습니다.)

이렇게 만들어진 기본 소스를 빌드해 보면 헤더파일을 찾지 못해서 에러가 수없이 나오게 됩니다.
그 원인을 찾아 보니 프로젝트가 만들어지면서 GTK와 관련된 헤더파일의 경로가 자동으로 추가되는데 몇개의 경로는 추가가 안되기도 하고 쓸데없는 경로가 추가되는 등, 좀 어처구니 없이 만들어집니다.
처음에 만들어졌을 때, 추가된 헤더파일의 경로

추가된 헤더파일의 경로를 수정한 결과
아마도 이것은 Code:Blocks의 오류인 듯 싶습니다.

앞서의 Global Variable 설정에서 cflags에 있는 대로 여기에 경로가 추가되었다면 문제가 없었을 것인데, Code:Blocks 내부에서 임의로 경로를 추가하는 게 아닌지 의심스럽습니다.

암튼, 이렇게 수정하고 나면 문제 없이 빌드되고 다음과 같이 실행도 됩니다.

2012년 12월 3일 월요일

미로 생성



#ifndef MAZE_GEN_TREE_H

#ifndef TRUE
#define TRUE    1
#endif
#ifndef FALSE
#define FALSE   0
#endif

/* 미로의 크기, 폭과 높이를 가진 직사각형으로 제한 */
#define MAZE_WIDTH      20
#define MAZE_HEIGHT     20

/* 각 방이 가질 수 있는 방향에 대한 정의. 시각적으로 상하좌우를 갖는다고 가정함. */
typedef enum {
    MAZE_DIR_UP = 0,
    MAZE_DIR_DOWN,
    MAZE_DIR_LEFT,
    MAZE_DIR_RIGHT,
    MAZE_DIR_MAX
} MAZE_DIR;

/* 각 방을 중심으로 4가지 방향에 존재할 수 있는 종류. 미결정, 벽, 부모(들어오는 방향 표시), 자식 */
typedef enum {
    MS_UNDEF = 0,
    MS_WALL,
    MS_PARENT,
    MS_CHILD
} MAZE_STATUS;



/* 트리구조에 필요한 상수.
   4방향 중 하나는 미로로 들어오는 방향이므로 이를 부모 노드로 보고,
   최대 나머지 3방향으로 나갈 수 있으므로 이를 자식 노드로 본다. */
#define MAX_CHILD   3

typedef struct  _node {
    struct  _node *p_parent;

    int     maze_x, maze_y;
    MAZE_STATUS ms[MAZE_DIR_MAX];

    struct  _node *p_child[MAX_CHILD];
} node, *p_node;


#define INIT_NODE(node) \
    do {\
        (node)->p_parent = NULL;\
        (node)->maze_x = (node)->maze_y = 0;\
        (node)->ms[MAZE_DIR_UP] = (node)->ms[MAZE_DIR_DOWN] = (node)->ms[MAZE_DIR_LEFT] = (node)->ms[MAZE_DIR_RIGHT] = MS_UNDEF;\
        (node)->p_child[0] = (node)->p_child[1] = (node)->p_child[2] = NULL;\
    } while(0);\



int init_maze();
int generate_maze();
int gen_maze( node *mz );
node  *make_node();
int   delete_node( node *mz );


#endif  // MAZE_GEN_TREE_H





#include
#include
#include "maze_gen_tree.h"

char    isAlloc[MAZE_WIDTH][MAZE_HEIGHT];
node    mz_start;



int init_maze()
{
    int     x, y;

    INIT_NODE( &mz_start );

    for( x = 0 ; x < MAZE_WIDTH ; x++ )
        for( y = 0 ; y < MAZE_HEIGHT ; y++ )
            isAlloc[x][y] = FALSE;

    isAlloc[0][0] = TRUE;
    mz_start.maze_x = mz_start.maze_y = 0;
    mz_start.ms[MAZE_DIR_UP] = mz_start.ms[MAZE_DIR_LEFT] = MS_WALL;

    return  TRUE;
}



int generate_maze()
{
    node    *mz = &mz_start;

    init_maze();

    gen_maze( mz );

    return TRUE;
}



int gen_maze( node *mz )
{
    int         i, j, rnd, num_child = 0;
    node        *n;
    MAZE_DIR    dir[MAZE_DIR_MAX] = { MAZE_DIR_MAX, MAZE_DIR_MAX, MAZE_DIR_MAX, MAZE_DIR_MAX };

    /* 현재 노드 '*mz'의 주변 상태를 설정한다. 위,아래,좌,우가 벽인지 아닌지, 출발점인지 도착점인지 */
    if( mz->maze_x == 0 )
    {
        if( mz->maze_y == 0 )   // Special Case : Maze Start
            mz->ms[MAZE_DIR_UP] = mz->ms[MAZE_DIR_LEFT] = MS_WALL;
        else if( mz->maze_y == MAZE_HEIGHT-1 )
            mz->ms[MAZE_DIR_DOWN] = mz->ms[MAZE_DIR_LEFT] = MS_WALL;
        else
            mz->ms[MAZE_DIR_LEFT] = MS_WALL;
    }
    else if( mz->maze_x == MAZE_WIDTH-1 )
    {
        if( mz->maze_y == 0 )
            mz->ms[MAZE_DIR_UP] = mz->ms[MAZE_DIR_RIGHT] = MS_WALL;
        else if( mz->maze_y == MAZE_HEIGHT-1 )  // Special Case : Maze Exit
        {
            mz->ms[MAZE_DIR_DOWN] = mz->ms[MAZE_DIR_RIGHT] = MS_WALL;

            return num_child;
        }
        else
            mz->ms[MAZE_DIR_RIGHT] = MS_WALL;
    }
    else
    {
        if( mz->maze_y == 0 )
            mz->ms[MAZE_DIR_UP] = MS_WALL;
        else if( mz->maze_y == MAZE_HEIGHT-1 )
            mz->ms[MAZE_DIR_DOWN] = MS_WALL;
    }

    /*
        재귀함수이기에 depth-first의 방식으로 미로를 만들어 나가게 된다.
        이 경우에 첫번째에 선택되는 방향이 출구로 향하는 경로가 되므로, 선택하는 방향의 순서를 난수화 해야 한다.
        따라서 dir[]에 4가지 방향을 위한 배열을 만들고 각 방향의 순서를 난수로 만들되, 중복되지 않도록 하는 것이다.
    */
    for( i = MAZE_DIR_UP ; i < MAZE_DIR_MAX ; i++ )
    {
        while( 1 )
        {
            rnd = rand()%MAZE_DIR_MAX;
            if( dir[rnd] == MAZE_DIR_MAX )
            {
                dir[rnd] = i;
                break;
            }
        }
    }

    for( i = 0 ; i < MAZE_DIR_MAX ; i++ )
    {
        if( mz->ms[dir[i]] != MS_UNDEF )
            continue;

        switch( dir[i] )
        {
            case MAZE_DIR_UP :
                if( isAlloc[mz->maze_x][mz->maze_y-1] == FALSE )
                {
                    n = make_node();
                    if( n == NULL )
                    {
                        fprintf( stderr, "make_node() failed : %s(%d)\n", __FILE__, __LINE__ );
                        return num_child;
                    }

                    n->maze_x = mz->maze_x;
                    n->maze_y = mz->maze_y-1;
                    n->p_parent = mz;
                    n->ms[MAZE_DIR_DOWN] = MS_PARENT;

                    j = 0;
                    while( mz->p_child[j] ) j++;
                    mz->p_child[j] = n;
                    mz->ms[MAZE_DIR_UP] = MS_CHILD;

                    isAlloc[mz->maze_x][mz->maze_y-1] = TRUE;
                    num_child++;

                    gen_maze( n );
                }
                break;

            case MAZE_DIR_DOWN :
                if( isAlloc[mz->maze_x][mz->maze_y+1] == FALSE )
                {
                    n = make_node();
                    if( n == NULL )
                    {
                        fprintf( stderr, "make_node() failed : %s(%d)\n", __FILE__, __LINE__ );
                        return num_child;
                    }

                    n->maze_x = mz->maze_x;
                    n->maze_y = mz->maze_y+1;
                    n->p_parent = mz;
                    n->ms[MAZE_DIR_UP] = MS_PARENT;

                    j = 0;
                    while( mz->p_child[j] ) j++;
                    mz->p_child[j] = n;
                    mz->ms[MAZE_DIR_DOWN] = MS_CHILD;

                    isAlloc[mz->maze_x][mz->maze_y+1] = TRUE;
                    num_child++;

                    gen_maze( n );
                }
                break;

            case MAZE_DIR_LEFT :
                if( isAlloc[mz->maze_x-1][mz->maze_y] == FALSE )
                {
                    n = make_node();
                    if( n == NULL )
                    {
                        fprintf( stderr, "make_node() failed : %s(%d)\n", __FILE__, __LINE__ );
                        return num_child;
                    }

                    n->maze_x = mz->maze_x-1;
                    n->maze_y = mz->maze_y;
                    n->p_parent = mz;
                    n->ms[MAZE_DIR_RIGHT] = MS_PARENT;

                    j = 0;
                    while( mz->p_child[j] ) j++;
                    mz->p_child[j] = n;
                    mz->ms[MAZE_DIR_LEFT] = MS_CHILD;

                    isAlloc[mz->maze_x-1][mz->maze_y] = TRUE;
                    num_child++;

                    gen_maze( n );
                }
                break;

            case MAZE_DIR_RIGHT :
                if( isAlloc[mz->maze_x+1][mz->maze_y] == FALSE )
                {
                    n = make_node();
                    if( n == NULL )
                    {
                        fprintf( stderr, "make_node() failed : %s(%d)\n", __FILE__, __LINE__ );
                        return num_child;
                    }

                    n->maze_x = mz->maze_x+1;
                    n->maze_y = mz->maze_y;
                    n->p_parent = mz;
                    n->ms[MAZE_DIR_LEFT] = MS_PARENT;

                    j = 0;
                    while( mz->p_child[j] ) j++;
                    mz->p_child[j] = n;
                    mz->ms[MAZE_DIR_LEFT] = MS_CHILD;

                    isAlloc[mz->maze_x+1][mz->maze_y] = TRUE;
                    num_child++;

                    gen_maze( n );
                }
                break;

            default :
                fprintf( stderr, "gen_maze() : dir[] has invalid value : %s(%d)\n", __FILE__, __LINE__ );
        }  // switch(dir[i])
    } // for(i)

    return num_child;
}



node  *make_node()
{
    node  *mz;

    mz = (p_node)malloc(sizeof(node));
    if(!mz)
        return NULL;

    INIT_NODE(mz);

    return mz;
}



int   delete_node( node *mz )
{
    free(mz);

    return TRUE;
}

2012년 11월 21일 수요일

Doxygen - 소스 분석 툴

다른 사람이 작성한 소스코드를 분석하고자 할 때에 유용할 수도(?) 있는 도구 입니다.

C/C++로 작성된 소스 코드를 분석하여 다양한 포맷의 문서로 출력을 해 준다고 합니다.

최근에 소스를 받아서 빌드한 프로그램이 잘 작동하지 않아, 디버깅을 하기 위해 찾아보던 중에 알게 된 것인데 그 사용 방법 및 결과를 간단히 써 보겠습니다.

사용환경은 우분투(Ubuntu) 10.04
doxygen 1.6.3
graphviz 2.20

프로그램의 목적이나 용도만 보아도 꽤 많은 옵션이 있을 수 있는 프로그램입니다.
그런 프로그램이 GUI가 아닌 커맨드 라인 프로그램이니 익히기 껄끄럽겠죠.
혹시나 하고 찾아보니 프론트엔드(front-end) 프로그램으로 doxywizard라는 것이 있습니다.


프로젝트 이름, 소스 디텍토리, 출력 파일 포맷, 분석 옵션 등을 지정해 주면 지정된 출력 포맷으로 분석 결과를 내 줍니다.

특히 분석 옵션 가운데, graphviz 패키지의 기능을 이용하여 함수의 호출관계를 보여주는 옵션을 선택하는 부분이, 가장 많은 시간과 가장 많은 출력 결과물의 크기를 좌우하게 됩니다.

극단적이긴 하지만 약 1.2M byte의 소스 파일을 풀 옵션으로 분석한 결과 시간도 상당히 걸렸지만(수십분 정도), 출력된 파일들의 크기 또한 250M byte에 달하는 것으로 나타났습니다.


그렇게 분석한 결과들의 예를 보면,

메인페이지
클래스 리스트

클래스 계층도

그림으로 나타낸 클래스 계층도

메쏘드의 호출 그래프1
메쏘드의 호출 그래프2

메쏘드의 호출/피호출 그래프


프로젝트를 마치고 프로젝트에 대한 문서를 작성하는 것이라면 꽤나 유용하게 사용할 수 있지 않나 싶습니다만, 소스를 분석하고자 하는 사람들의 일반적인 목적에서의 유용성은 의심이 듭니다.

왜냐하면,
전체적인 프로그램의 흐름을 파악할 방법이 거의 없습니다.
특정한 일부분을 분석하기 위해서도 메쏘드와 멤버들의 유기적인 연관 관계를 보아야 하는데 그런 측면이 약합니다.

즉, 부분 혹은 하나의 개체들에 대한 분석은 완벽할 지 모르나, 그것들을 유기적으로 연결하여 큰 것으로 묶는 것에 있어서는 취약하다는 것입니다.
애초에 그런 역할은 사람에게 남겨진 몫이긴 하나 그 과정을 도와줄 도구 또한 필요합니다.
doxygen의 결과물은 도구의 역할은 하지 못하지만 좋은 참고자료는 될 것 입니다.

따라서 소스를 분석하고자 하는 사람에게 doxygen이 유용한 도구임에는 틀림 없으나 추가적인 도구가 필요함을 잊지 말아야 할 것입니다.

Code:Blocks Debian Package 설치

컴퓨터의 사양이 낮은 관계로 아직까지 Ubuntu 10.04에 머무르고 있는 상황입니다.

Ubuntu는 Software의 설치가 간편하다는 것이 큰 장점입니다.
우분투 소프트웨어 센터를 통해 간단하게 필요한 소프트웨어를 검색 및 설치할 수 있고, 설치된 소프트웨어를 검토하고 제거할 수도 있습니다. 여기에 시냅틱 패키지 관리자는 좀 더 광범위한 소프트웨어를 제공하기에, 어지간한 경우를 제외하고는 목적에 부합하는 소프트웨어를 찾는 것이 어렵지 않습니다.

그러나, 내가 원하는 소프트웨어가 우분투 소프웨어 센터나 시냅틱 패키지 관리자에서 제공되지 않으면 설치하는 데 애를 먹곤 합니다.

현재 사용하고 있는 크롬 브라우저도 비슷한 경우에 해당합니다.
크롬 부라우저를 다운로드 받아서 압축을 풀고 나면 다음과 같은 파일이 나옵니다.
google-chrome-stable_current_i386.deb

이와 같이 확장자가 .deb인 파일은 데비안 패키지를 의미하는 것으로써 /usr/bin/gdebi*를 이용하여 설치가 가능합니다.


리눅스에서 C/C++용으로 쓸만한 IDE 가운데 하나인 Code:Blocks라는 것이 있습니다.
현재 릴리즈된 최신 버전은 10.05입니다만, Ubuntu 10.04의 소프트웨어 센터에서는 그 이전의 버전인 8.02만을 제공하고 있습니다.

그래서 Code:Blocks의 홈페이지에서 10.04 버전을 직접 다운로드 받았습니다.
http://www.codeblocks.org/downloads/26

다운로드 받은 파일의 압축을 풀면 다음과 같이 여러개의 데비안 패키지들이 나옵니다.

데비안 패키지가 하나인 경우에는 문제가 없지만 여러개인 경우에는 설치 순서에 문제가 있을 수 있습니다.

파일의 이름으로 미루어 가장 적절한 패키지를 살펴 보면, 우측상단에서 두번째 파일인 codeblocks_10.05-1_i386.deb입니다. 이걸 gdebi에서 열어 보면,
 libcodeblocks0 패키지를 먼저 설치해야 하는군요.


그럼 libcodeblocks0_10.05-1_i386.deb를 열어보면,

오류는 없으나 패키지 2개를 설치해야 한다는 문구네요.
상세정보를 보면,

혹시나 해서 이름이 비슷한 libwxsmithlib0와 wxsmith*를 열어보니 맨 처음의 codeblocks와 같은 오류가 뜰 뿐입니다.

갑자기 탁 막혀버린 느낌이고 똥개훈련 받고 있는 느낌이고 ㅇㄴ마ㅓ로이ㅑ맨붕ㅠㅠ

저런 경우에는 apt-get으로 패키지를 설치해 주어야 한답니다.

그래서 터미널에서 패키지들을 먼저 설치해 줍니다.
libwxbase2.8-0 설치

libwxgtk2.8-0 설치
위의 패키지들은 시냅틱 패키지 관리자에서도 설치가 가능합니다.

그 후에 libcodeblocks0를 다시 열어보면,
아무 불만이 없나 봅니다^^

이후의 설치 순서는,
libwxsmith0
codeblocks-common
codeblocks-contrib-common

이후에 codeblocks-contrib를 설치하는 부분에서 다시 오류가 뜹니다.

그 이유를 찾아보니, 이전에 설치한 두개의 패키지에 이상한 점이 있습니다.
codeblocks-common과 codeblocks-contrib-common의 패키지 제어 데이터를 보니,


아직 설치하지도 않은 codeblocks와 codeblocks-contrib가 충돌이라고 오류를 낸 것인지, 아니면 저 패키지들과의 충돌 조건이 저러하다고 명시를 한 것인지 모르겠습니다.

무언가 이상하다 생각하고 일단 codeblcoks-common과 codeblocks-contrib-common을 제거하려고 보니, gdebi에서는 패키지를 삭제할 수가 없습니다.

페키지 삭제는 dpkg 혹은 aptitude를 사용하면 됩니다.

이 aptitude를 사용해서 패키지를 찾다 보니, codeblocks의 8.02 패키지에 대한 정보들이 존재합니다. 비록 설치되어 있지는 않지만 8.02 패키지와의 충돌로 이런 문제가 발생한 것이 아닌가 싶습니다.

Code:Blocks의 홈페이지에 있는 포럼에 들어가서 같은 문제를 겪고 있는 사람들의 해결 방안을 찾아 보았습니다.
마침 비슷한 질문이 있었고, 답변이 달려있습니다.

http://forums.codeblocks.org/index.php/topic,16569.0.html


개발자 중의 하나로 보이는데, 패키지 소스 리스트에 서버 URL 하나만 추가하면 된다고 답변이 있습니다.


반신반의 하면서 APT 소스 리스트에 추가를 했습니다.

그리고 시냅틱 패키지 관리자에서 "패키지 정보 새로 고침"이라는 메뉴 항목을 선택하자, 프로그레스 바가 몇 번 진행을 하고나서 Code:Blocks의 정보들이 8.02에서 10.05로 모두 바뀌었습니다!!


우분투 소프트웨어 센터에서도 버전이 바뀐 것이 확인 됩니다만, 설치하려고 하면 인증되지 않은 패키지라는 메시지와 함께 설치가 중단되어 버립니다.

시냅틱 패키지 관리자에서는 같은 경고 메시지가 나오지만 강제로 설치할 수 있습니다.

처음에 길게 썼지만.... 결국엔 리포지터리(저장소) 하나 추가하면 말끔하게 해결되는 일이었습니다.ㅠㅠ

기념으로 시작시에 나오는 타이틀화면

2012년 11월 17일 토요일

XU4 : Ultima4 recreated

Ultima 시리즈 가운데 4번째인 Ultima4 : Quest of the Avatar의 업그레이드 버전인 XU4에 도전해 보았습니다.

이 외에도 각 시리즈별 업그레이드는 다음과 같습니다.
Ultima5 : Lazarus
Ultima6 : Nuvie
Ultima7 : Exult
Ultima8 : Pentagram

도전할 게 뭐가 있냐고 생각할 수도 있지만,  XU4는 윈도우즈와 맥用의 바이너리만 배포합니다. 그러니 우분투에서 사용해 보려면 소스를 컴파일 해야 합니다.

일단 필요한 것을 나열해 보면,
xu4 웹사이트 : http://xu4.sourceforge.net/
xu4 소스 : http://prdownloads.sourceforge.net/xu4/xu4-1.0beta3.tar.gz?download
ultima4(DOS) : http://www.thatfleminggent.com/ultima/u4download.html
ultima4 업그레이드 파일 : http://prdownloads.sourceforge.net/xu4/u4upgrad.zip?download


현재 사용중인 우분투는 10.04 버전입니다.
기본적인 설치를 마친 직후라 추가로 설치해야 하는 패키지들이 있습니다.

libxml2-dev
libsdl1.2-dev
libsdl-mixer1.2-dev
zlib1g-dev
g++

위의 패키지를 시냅틱 패키지 관리자에서 선택해 추가하면, 함께 필요한 패키지들이 자동으로 선택되니 모두 설치하면 됩니다.


이제 소스를 컴파일해 봅니다.
중간에 에러가 나는 경우가 종종 있었는데, 소스를 약간 수정해야만 했습니다.

다음이 수정한 내역입니다.
=================================================================

vc6.h
line 11에 추가
#include <stdlib.h>
#include <string.h>

imagemgr.h
line 105 SubImage *ImageMgr::getSubImage(const std::string &name);
==> SubImage *getSubImage(const std::string &name);

menuitem.cpp
line 7에 추가
#include <stdio.h>

u4file.h
line 13에 추가
#undef putc

lzw/u6decode.cpp
line 36에 추가
#define EXIT_SUCCESS 0
#define EXIT_FAILURE -1
=================================================================

컴파일이 완료되면 몇개의 실행 파일이 생성됩니다.
u4
u4enc
u4dec
tlkconv
dumpsavegame

이 디렉토리에 위에서 받은 ultima4(DOS) 와 Ultima4  업그레이드 파일을 저장해 둡니다.
압축된 상태 그대로 두면 되고 파일의 이름음 ultima4.zip과 u4upgrad.zip으로 되어 있을 것입니다.

압축파일을 다루는데 문제가 좀 있는지 잠깐 로딩 바가 보이다가 종료가 됩니다.
프로그램의 구조를 확인하고 디버깅을 해야 문제를 고칠 수 있지 않을까 싶습니다.

프로그램에 문제가 발생할 경우, 디버깅 방법에 대해 고민을 하곤 합니다.
소스의 구조를 파악해서 논리적으로 문제가 발생한 지점을 찾아낼 것인가?
대략 에러가 난 곳과 의심되는 곳에 메시지를 넣어서 범위를 최소로 줄일 것인가?
디버거를 이용해서 스텝을 따라가면서 문제를 추적할 것인가?

위와 같이 문제가 발생하는 시점과 항상 일정하고 항상 재현이 되는 경우에는 마지막 방법이 좋을 것입니다.

하지만, 내가 만든 소스도 아니고, 소스를 살펴보거나 배우기 위한 것이 아니라....
소스에 포함된 문서를 읽어 보다가, ultima4.zip의 압축을 풀어서 ultima4라는 디렉토리에 넣어 두어도 된다는 문구가 있어 시험 삼아 압축을 풀어 보았습니다.

그냥 실행이 되었습니다.^^
울어야 할지 웃어야 할지.....

암튼 실행 화면이라도 감상해야죠.





=================================================================

디버깅 결과 압축파일에서 원하는 파일을 찾는 부분에 문제가 있었습니다.
그 이유는 ultima4.zip에서 원하는 파일을 찾을 때, <"ultima4/"+파일이름>과 같이 앞 부분에 ultima4라는 디렉토리 이름을 붙여서 찾으려 하고 있기 때문에 원하는 파일을 찾지 못하게 됩니다.
업그레이드 파일인 u4upgrad.zip일 경우에는 디렉토리 이름을 붙이지 않았기에 문제가 없었습니다.

수정해야 할 부분은 u4file.cpp의 238 line입니다.
초기에 압축파일에 대한 정보를 지정하는 부분에서 서브 디렉토리 이름을 지정하지 않도록 수정하는 것입니다.


지금 캡쳐한 사진을 보니, ultima4-1.01.zip인 파일을 사용하는 경우에도 문제가 발생하겠네요.
소스를 수정할 수 없는 상황이라면, 실행 파일이 있는 곳에 ultima4라는 서브디렉토리를 만들고 이곳에 압축 파일을 넣어도 됩니다.

간만에 소스 들여다 보며 디버깅하려니 힘드는군요^^
=================================================================

2012년 9월 22일 토요일

완벽한지 판단하는 방법

"더할 것이 없을 때가 아니라, 뺄 것이 없을 때, 비로소 완벽해졌음을 알 수 있다." - 생떽쥐베리

"You know you've achieved perfection in design, not when you have nothing more to add, but when you have nothing more to take away." --A. de Saint-Exupery

2012년 9월 21일 금요일

크롬의 웹 어플 만들기 __ 2

https://developers.google.com/chrome/apps/docs/

웹 어플의 개발자 문서입니다.
첫페이지는 개요정도 뿐이고 하단에 2개의 링크가 존재합니다.

앱은 2가지로 나뉜다

웹 어플은 Hosted Apps와 Packaged Apps로 구분이 되는 것 같습니다.
후자인 Packaged Apps는 크롬에서 확장프로그램(Extension)이라고 불리는 것이라 합니다.

그런데, 첫번째 링크는 정상이지만 두번째 링크는 404 Not Found입니다.
그래서 좀 헤멘 결과, 정상으로 보이는 링크를 찾아냈습니다.

두개의 링크를 여기에 다시 기록하겠습니다.
Hosted Apps : https://developers.google.com/chrome/apps/docs/developers_guide
Packaged Apps : http://developer.chrome.com/extensions/docs.html

크롬의 웹 어플 만들기 __ 1

크롬 브라우저는 웹 스토어를 통해 다양한 웹 어플을 제공하고 있습니다.

특정 사이트의 링크 정도로 보이는 단순한 어플에서부터 사무용 프로그램, 멀티미디어, 게임에 이르기까지 이런 것이 웹브라우저에서 가능할까 싶은 것들도 많습니다.

지금까지는 신기하다 생각하고 사용만 해 왔었는데,
문득 나도 만들어 보고 싶다는 생각이 들어 시작을 해 보기로 했습니다.

검색해 보니 별 고민할 필요가 없는 공식 사이트가 나타납니다.
https://developers.google.com/chrome/apps/

구글 디벨로퍼의 웹 어플 페이지

개발을 시작하기 위한 링크들

개발자용 문서 : https://developers.google.com/chrome/apps/docs/index
개발자들을 위한 게시판(Groups) : http://groups.google.com/a/chromium.org/group/chromium-apps
크롬의 최신 정보를 위한 블로그 : http://blog.chromium.org/

이후의 블로깅은 개발자용 문서로부터 시작할 계획입니다.

2012년 9월 5일 수요일

C에서의 Enumerator

enum의 용도는, 일련의 상수들을 나열하여 선언하고, 이들에 순차적으로 상수값을 할당하는 데 있습니다.

주로 같은 목적으로 사용될 상수들이 많이 필요한 경우,
혹은 상수들에 순차적으로 값을 할당해야 하는 경우에 사용되곤 합니다.

애초에 C언어를 만든 커닝핸과 리치가 어떤 목적으로 enum을 만들었는지는 모르겠으나,
순차적인 값으로 할당되는 상수들을 만들기 위해서라기 보다는,
특정한 목적에 사용될 상수들을 명시적으로 분류하기 위해서 만들지 않았을까 생각됩니다.

이러한 목적(명시적인 분류)을 위해서는 아무런 이름도 없이 선언된 enum 보다는 식별자(identifier)가 할당된 enum을 사용하는 것이 바람직합니다.

다음에 있는 코드를 통해 enum을 사용해 보겠습니다.

enum 사용예 (1)

컴파일 결과 (1)
이 코드에서는 enum의 사용에서 궁금했던 것을 모두 시도해 보았습니다.

line 3 : 상수들 사이에 하나의 콤마를 추가하면 할당되는 상수값이 증가하지 않을까 생각했는데 컴파일 에러가 발생했습니다.

line 7 : enum으로 선언된 상수들이 사용될 변수의 타입을 enum의 식별자로 선언해 보니 컴파일 에러가 발생했습니다.

이후에는 변수 자체가 선언되지 않은 것으로 간주되어 에러가 줄줄이 나옵니다.

이 두가지 문제를 다음과 같이 수정하였습니다.

수정한 enum의 사용예 (2)
실행결과
컴파일은 문제없이 완료되었고, 실행도 잘 되었습니다.


하지만, 한가지 짚고 넘어가야 할 문제가 있습니다.
line 10 & line 14에서 아무런 문제가 발생하지 않았다는 점입니다.

분명히 array는 "enum SET"이라는 타입으로 선언이 되었음에도 불구하고, enum SET에는 선언되지 않은 상수인 0과 4를 할당하는 것에 아무런 문제가 없었다는 점입니다.
(C++에서는 이런 결점이 보완되어 있는 것으로 알고 있습니다.)


line 7에서 보듯이 enum의 사용법은 struct, union의 사용법과 흡사합니다.

전적으로 프로그래머의 편의를 위한 것이지만,
enum도 typedef를 사용해서 조금이나마 쉽게 쓰도록 수정해 보았습니다.

typedef를 사용한 enum의 예 (3)

2012년 7월 24일 화요일

MS-Windows를 원격 제어하는 방법?

떨어져 사는 부모님댁에 컴퓨터와 관련된 문제가 일어날 때가 종종 있다.

컴퓨터는 아버지께서만 사용하시니, 문제가 생기거나 하면 아버지께서 내게 도움을 요청하시곤 한다.

하지만...여러 가지로 도움을 드리긴 어렵다.
어떤 문제는 대체 왜 그런 일이 일어났는지 경험도 못 해 본 일인 경우도 있고,
대략 몇가지 가능성을 예상할 수 있는 경우도 있고,
명확하고 자주 겪는 문제인 경우도 있다.

마지막의 경우에도 전화통화만으로 쉽게 해결되지 않는 경우가 있으니,
나는 너무도 당연하게 생각하는 일이 칠순 노인인 아버지께는 그러하지 않은 경우가 많기 때문이다.
일일이 과정 과정을 설명하고, 과정마다의 결과를 확인하고 하면 진이 빠지는 경우도 많다.

그나마 이건 다행인 경우이고,
몇가지 원인이 가능한 경우에는, 각각의 원인을 확인해서 문제의 원인을 밝혀 내야 하는데, 다시 위와 같은 과정을 몇번씩 반복해야 하며, 만일에 말로 설명하기 곤란하거나 확인하기 곤란하다면 이건 그야말로 인내심의 바닥을 확인하게 되기도 한다.

차라리 상상도 못할 문제가 일어난 경우에는 A/S를 부르시라고 권하고, 애초에 포기해 버리니 오히려 부담도 없다.


이런 문제들의 원인은 여러가지가 있으나,
모르는 사람에게 지시를 내리기 어렵다는 것이 근본적인 한계가 아닌가 싶다.

이런 어려움을 피하기 위해서 Remote Desktop을 가능하도록 몇가지 설정이나 툴을 아버지의 컴퓨터에 설치해 두었으나 이를 사용하는 것에도 어려움이 많았다.
가령, 문제가 발생한 경우에, Remote Desktop으로 연결하기 위해서는 IP address를 알아야 가능한데, 아버지께 IP address가 무엇인지 알아내는 방법을 설명하는 것 조차 쉽지 않은 일이다.
여기에, 문제가 쉽게 해결이 되면 좋겠으나, 비교적 장시간이 소요될 수도 있다. 그러면 아버지께 시간이 걸리니 다른 일 보시라 말씀을 드리고 싶으나, rebooting이 필요한 작업의 경우에는 rebooting 후에 다시 IP address가 무엇인지 알아내는 과정부터 반복을 해야 하는 고로 이만 저만 귀찮은 일이 아니다.

무언가 간단하게 상대방과 연결하여 조치를 하거나 확인할 수 있는 방법은 없는걸까?

6502 CPU의 Instruction Set

Apple II, Commodore64 등에 사용된 MOS Technology의 6502 CPU의 Instruction Set에 대한 문서입니다.

http://www.6502.org/tutorials/6502opcodes.html

Instruction은 56개밖에 안됩니다.
operand type에 따라 code가 달라져서 Instruction Code의 수는 훨씬 많지만요.

Flag라던지 Operand의 형식에 관한 설명이 없고 이에 대한 별도의 문서도 눈에 띄지 않습니다.

구닥다리라 이제는 사용되지도 않는 CPU의 Instruction을 참조하는 이유는, 에뮬레이터에 대한 궁금증 때문입니다. 과연 에뮬레이터는 어떤 식으로 에뮬레이션을 구현하는지 알고 싶어서 입니다.
에뮬레이터의 레지스터 구조체 및 함수들

ADC Instruction의 예

어드레싱모드에 관한 매크로


ADC Instruction에 관한 매크로



2012년 7월 19일 목요일

진화(進化)의 계단

이전에도 간혹 어렴풋이 들던 생각이,
최근에 다시, 좀 더 또렷하게 들고 있습니다.

프로그래밍 언어들에 대한 구분이 저수준(Low-Level)과 고수준(High-Level)이 되는 경우가 있는데, 이런 구분은 그 언어의 역할 및 용도를 보다 명확히 해 줍니다.
저수준의 언어는 근본적으로 프로그램이 작동하게 될 컴퓨터의 구조와 밀접하게 연관이 되며 군더더기 없고 간결하며 빠르게 작동하는 장점을 가집니다.
반면에 고수준의 언어는 컴퓨터의 구조에 대한 고민을 많이 줄여주고 인간의 복잡한 논리를 구현하는데 더 적합한 장점을 지니게 됩니다.
리버스 엔지니어링을 하다보면, 고수준의 언어로 작성된, 복잡한 알고리즘의 프로그램은 그 자체가 안티-리버싱의 역할도 하지 않나 생각될 정도입니다.

이런 프로그래밍 언어의 고수준화는 하나의 "진화의 계단" 역할을 합니다.
복잡하고 까다로운 논리를 저수준의 언어로 작성하는 것은 무척이나 어려웠을 것입니다.
기본적으로 저수준의 언어를 사용하는 프로그래머의 머리에는 컴퓨터의 구조, 레지스터, 스택, 메모리 사용 등에 대한 지식과 고려가 가득 차 있었을 것이므로 새로운 생각을 할 여유가 그 만큼 적었을 것이기 때문입니다.

비슷한 예로,
플랫폼 또한 하나의 "진화의 계단" 역할을 했을 겁니다.
'플랫폼'이라는 단어 자체가 무언가를 딛고 올라설 수 있는 평평한 곳을 의미하니 너무도 당연하기도 합니다.
많이 알려진 자바가 그렇고, 브라우저도 하나의 플랫폼이며, 플래쉬 또한 하나의 플랫폼이 됩니다.

좀 더 위로 올라가 보면,
MS-Office도 하나의 "계단"이 되지 싶습니다.
LaTex이나 PostScript도 제한적이지만 "계단"이 될 수 있으며,
PhotoShop이나 그림판도 "계단"이 될 수 있지 않을까요?

어쩌면 모든 프로그램은 하나의 "계단"이 아닐까 싶습니다.
무언가를 만들거나 만드는게 도움이 되는...

...
다시 아래로 내려가 볼까요?
Operating System은 확실하게 "진화의 계단" 역할을 했습니다.
이만큼 광범위하고 다양하게 컴퓨터의 자원과 기능들을 사용할 수 있는 편의장치(facility)를 제공해 주는 건, 확실히 쉽지 않은 일입니다.

좀 더 내려가 보면,
컴퓨터의 모든 부품들과 컴퓨터 그 자체 또한 너무나도 당연히 "진화의 계단"입니다.


초기의 컴퓨터를 설계한던 사람들은 그들이 만든 이 "계단"이 지금 이만큼까지 올라올걸 상상이나 했을까요?
아마도 이만큼은 절대 상상하지 못했을 겁니다.
단지 그들은 좀 더 위로 올라가기 위한 하나의 "계단"을 만들었을 뿐이겠죠.
그리고 하나의 계단을 딛고 올라선 사람들은 다시 또 하나의 계단을 만들었을 것입니다.






좀 엉뚱한 상상이지만,
진화론의 측면에서 바라보면, 인간 또한 하나의 "계단"은 아닐까요?

2012년 7월 12일 목요일

Time Wave Zero

2012년 지구의 종말? 대변혁? 과 함께 자주 언급되는 Time Wave Zero.

언제나 그래프가 그려진 화면만을 접하기에,
내가 보고 싶은 부분을 확인하고픈 욕구가 생겼다.

인터넷 검색을 하면 가장 먼저 검색되는 사이트는
Terrence McKenna라는 사람이 운영하는 것으로 보이는 사이트이다.
Terrence McKenna's Timewave Zero
Timewave Zero and the Fractal Time Software

온통 영어니 그 내용이 얼마나 유용한지는 필요한 사람이 직접 알아보길....

두번째 링크의 좌측 메뉴에
online timewave calculator를 이용하면 제한적이나마
자신이 원하는 부분의 Time Wave Zero 그래프를 볼 수 있다.

Timewave Calculator

그러나 이 또한 기능의 제약이 많아서,
1개월 혹은 1년 단위의 그래프만 가능하다.


결국, 원래의 프로그램을 구하고 싶어 다시 인터넷 검색을 한 결과
아래의 사이트에서 프로그램을 구하게 되었다.
http://deoxy.org/download/software/timewavezero/

이 프로그램은 MS-DOS상에서 작동되도록 만들어진 프로그램으로
Math Co-Processor가 없는 경우, CGA 카드가 장착되지 않은 컴퓨터를 위한 고려까지 된 아주 오래된 프로그램이었다.

따라서 저 프로그램을 바로 구동하기 위해서는 DOSBox와 같은 MS-DOS 에뮬레이터가 필요하다.

DOSBox에서 파일 확인. TWZERO87.EXE를 실행시킨다.

TWZERO87.EXE 실행 첫 화면

아무 키나 눌러 진입한 화면

'F'키를 눌러 그래프를 그린다

Time Wave Zero에 대한 이론적 배경이 없다면 이 그래프가 무엇을 의미하는지에 대한 정확한 이해도 불가능하다.
위의 화면에서 우측에 있는 item들 가운데, B.Zero Date, P.Wave Factor 등은 중요한 요소이나 아직 이해는 부족하다.(나 또한...)


다운로드 받은 파일 중, frt.zip에는 FRT.EXE라는 실행 파일 하나 뿐이지만 보기에는 좀 더 편하다.
Fractal Time, 컬러풀^^

TWZ와 비슷하지만 조금 다른 FRT
FRT는 Fractal Time이라는 이름의 프로그램인데,
Time Wave Zero보다 깔끔하다는 점 외에도,
우측의 item에서 D.Number Set에서 선택 가능한 Data Set이 4가지가 있었다.


다운로드 받은 파일 중, 저자의 웹사이트가 소개된 부분들이 있었으나
워낙에 많은 시간이 지난지라 모두 폐쇠되거나 없어진 상태였다.

따라서, Time Wave Zero의 이론적인 배경이나 의미를 제대로 이해하기는 어려우며,
Wave Factor나 Data Set의 활용도 또한 어려운 상황이다.