At times, it's hard to forget your enthusiasm.
Material: H pencil, A4


저작자 표시
신고
2. 프로그램 확장하기


자, 잠깐의 휴식을 가졌으므로(?) 검은 화면에 뭔가를 채워볼 시간이다. 먼저 윈도우 내에 사각형을 하나 생성하고 색상을 칠할 것이다. 물론 EFL 라이브러리에서는 이를 쉽게 하기 위한 기능을 제공하고 있다.

예제 소스 코드부터 보도록 하자.

#include <Elementary.h>

int main(int argc, char **argv)
{
   elm_init(argc, argv);
 
   //윈도우를 생성하고 초기화 한다.
   Evas_Object *win = elm_win_add(NULL, "Elementary Window", ELM_WIN_BASIC);
   elm_win_title_set(win, "Elementary Window");
   evas_object_resize(win, 480, 400);
   evas_object_move(win, 100, 100);
   evas_object_show(win);
 
   //윈도우 오브젝트로부터 Evas 핸들을 반환받는다.
   Evas *e = evas_object_evas_get(win);
 
   //반환받은 Evas 핸들을 이용하여 화면에 출력할 사각형을 생성한다.
   Evas_Object *rect = evas_object_rectangle_add(e);
   evas_object_resize(rect, 480, 400);
   evas_object_color_set(rect, 0, 0, 255, 255);
   evas_object_show(rect);

   elm_run();

   elm_shutdown();

   return 0;
} 


사각형 오브젝트를 생성하기 전에, 먼저 우리는 윈도우 오브젝트로부터 Evas 핸들을 얻어오고 있다. 이를 위해, evas_object_evas_get() API를 호출하며, 이 API는 오브젝트 포인터를 요구하고, 해당 오브젝트로부터 오브젝트가 소속된 Evas 핸들을 반환한다.

Evas *evas_object_evas_get(const Evas_Object *obj);

Evas에 대해 아직은 충분한 설명이 없지만, 다음 장에서 Evas에 대해 구체적으로 살펴볼 예정이므로 지금은 간략하게만 요약해 보도록 하자.

우리는 Evas를 캔버스 엔진으로 정의할 수 있다. Evas는 내부적으로 출력 버퍼를 초기화하고 이를 대상으로 렌더링 작업을 수행한다. 렌더링을 위해서는 화면에 그려질 오브젝트들의 그래픽 정보를 실질적으로 보유하고 관리를 한다. 그 외에도 Evas는 오브젝트들의 Z 순서를 위한 계층(Layer) 정보 및 마우스 입력과 같은 사용자 입력에 대한 이벤트 등을 처리하고 있다. Evas는 사용자 입력 이벤트를 어떤 오브젝트로 전달해야 할지, 어떤 오브젝트를 화면에 그려야 할지, 오브젝트들의 라이프 사이클을 결정하고 이들을 렌더링하는 역할을 한다.

사실 이 예제의 윈도우 오브젝트 역시 Evas에서 관리하는 하나의 Evas_Object에 해당되기 때문에 우리는 윈도우 오브젝트를 통해 Evas인스턴스에 대한 핸들을 얻어올 수 있으며, Evas핸들을 이용하면 Evas에 사각형 오브젝트를 추가할 수 있다. 일부 독자들은 "그럼 우리가 언제 Evas인스턴스를 생성한 거지?" 라고 의아해 할 수 있겠지만, 사실 이 부분은 elm_win_add()가 호출될 시에 내부적으로 수행되었다. 이 말은 즉, 기본적으로 윈도우를 생성할 때마다 하나의 Evas인스턴스가 생성되며 각 윈도우마다 렌더링 출력물을 갖게 된다.


그림 3 윈도우마다 Evas 인스턴스가 존재한다.


우리는 화면에 출력할 사각형 오브젝트를 생성하기 위해 evas_object_rectangle_add() API를 이용하였다. 다시 한번 말하지만, 이 API 역시 evas 접두사로 시작하며 이 말은 해당 기능이 Evas에서 제공하는 API임을 알 수 있다. 호출 시, Evas인자를 넘겨준 후 Evas_Object 타입의 사각형 오브젝트를 반환 받는다. 이 후 우리는 반환받은 rect 핸들을 가지고 이 사각형 오브젝트에 대한 세부적인 설정을 수행할 수가 있다.

Evas_Object *evas_object_rectangle_add(Evas *e);

우선 사각형의 사이즈를 윈도우와 같은 크기인 480 x 400로 변경하고 위치는 0, 0으로 화면의 좌측, 최상단으로 지정한다. 물론 기본 위치 값이 0, 0 이기 때문에 여기서는 애써 evas_object_move()를 호출하지 않았다. 이렇게 되면 사각형은 윈도우 내부에 가득 찬다. 다음으로 사각형의 색상을 지정한다. 이를 위해 evas_object_color_set() API를 호출하며 여기서는 파란색인 0, 0, 255, 255 (Red, Green, Blue, Alpha) 값을 인자로 전달한다.

void evas_object_color_set(Evas_Object *obj, int r, int g, int b, int a);

evas_object_move() 및 evas_object_resize()와 같이 색상 지정 API 역시 일반적으로 이용되는 API이기 때문에 다른 타입의 오브젝트에도 적용이 가능하다. 여기서 주의할 점은, 매개변수 r, g, b 값들은 알파 값에 의해 미리 계산된(pre-multiplied) 값을 요구하며 사용자는 직접 그 값을 계산해서 전달해야 한다.

예를 들어, 반투명한 흰색의 사각형을 원한다면,

evas_object_color_set(rect, 255, 255, 255, 127);

를 호출하는 것이 아니라,

evas_object_color_set(rect, 127, 127, 127, 127);


와 같이 호출해야 한다. 각 R, G, B 각 요소는

R = 255(R) * 127(A) / 255
G = 255(G) * 127(A) / 255
B = 255(B) * 127(A) / 255

와 같이 계산한 결과 값이다.

경험 상, 이 부분은 많은 개발자들이 오용하는 경우가 많았으므로 여러분들도 꼭 상기하여 실수하지 않기를 바란다.

다음으로는 evas_object_show() API를 호출하여 사각형 오브젝트가 화면에 보이도록 한다.

자, 그럼 이쯤에서 결과물을 확인해 보도록 하자. 만약 여러분의 애플리케이션이 검정색에서 파란색 배경으로 바뀌었다면 성공한 것이다.


4. 윈도우에 사각형 오브젝트를 추가한 결과


만약, 여러분이 배경 색을 다른 색상으로 바꾸고 싶다면 evas_object_color_set()의 r, g, b 의 인자 값을 변경해 보길 바란다.

마지막으로 콘솔이 아닌, 윈도우에 "Hello E World"를 출력해 보도록 하자. 다음 예제 소스 코드를 보면 이 역시 얼마나 쉬운지 알게 될 것이다.

#include <Elementary.h>
 
int main(int argc, char **argv)
{
   elm_init(argc, argv);

  //윈도우를 생성하고 초기화 한다.
   Evas_Object *win = elm_win_add(NULL, "Elementray Window", ELM_WIN_BASIC);
   elm_win_title_set(win, "Elementary Window");
   evas_object_resize(win, 480, 400);
   evas_object_move(win, 100, 100);
   evas_object_show(win);

   //윈도우 오브젝트로부터 Evas 핸들을 반환받는다.
   Evas *e = evas_object_evas_get(win);

   //반환받은 Evas 핸들을 이용하여 화면에 출력할 사각형을 생성한다.
   Evas_Object *rect = evas_object_rectangle_add(e);
   evas_object_resize(rect, 480, 400);
   evas_object_color_set(rect, 0, 0, 255, 255);
   evas_object_show(rect);

   //레이블 위젯을 생성한다. 이 위젯은 텍스트를 출력하는 기능을 수행한다.
   Evas_Object *label = elm_label_add(win);
   elm_object_text_set(label, "Hello E World!");
   evas_object_move(label, 100, 100);
   evas_object_resize(label, 200, 100);
   evas_object_show(label);

   elm_run();

   elm_shutdown();

   return 0;
}

이제 여러분은 EFL 프로그래밍 방식에 어느 정도 감이 잡혔을 것이라고 생각한다. 새로 추가한 볼드체 코드를 보도록 하자. 가장 먼저 elm_label_add()를 호출함으로써 Elementary에서 제공하는 레이블 위젯을 생성한다. 이 때 매개변수로 윈도우 오브젝트를 전달하는 것에 주목하도록 하자.

Evas_Object *elm_label_add(Evas_Object *parent);

이번에는 Evas가 아닌 Elementary API를 이용하여 레이블을 추가하고 있는 점에 주목하자. 사실, Evas에서도 텍스트를 출력하는 텍스트 오브젝트가 존재하며 글자를 출력한다는 측면에서 레이블 위젯과 기능적으로 동일하나, Evas에서 제공하는 원초적인 오브젝트에 비해 Elementary의 위젯들은 애플리케이션에 있어서 필요한 기반 기능을 보다 많이 가지고 있다. 다음은 대표적인 장점들을 나열한다.

포커스(Focus): 각 위젯 간의 포커스 이동. 포커스를 통해 어떠한 오브젝트가 키 이벤트를 전달받을 지 결정하며, 부모에서 자식으로, 자식에서 부모로 또는 자식에서 자식으로 포커스가 이동될 수 있다.
테마(Theme): 동일한 애플리케이션일지라도 다양한 룩앤필(Look & Feel)를 지원하기 위한 테마 설정이 가능하다.
스케일(Scale): 동일한 애플리케이션으로부터 다양한 화면 해상도에서 동일한 화면 구성을 유지하기 위한 GUI 확장성을 제공한다.
미러(Mirror): 여러 나라의 언어 특성에 맞게 각 위젯이 좌/우 대칭으로 반전되는 기능을 제공한다.
언어(Language): 다국어 변경을 지원하기 위해 Elementary는 언어와 관련된 기반 기능을 제공하여 애플리케이션에서 보다 쉽게 언어를 변경할 수 있도록 도와준다.

간략하게나마 설명을 덧붙여 놨는데 자세한 사항은 6장의 “위젯 툴킷: Elementary”에서 살펴보도록 하며 여기서는 일단 이러한 기능들을 Elementary 위젯으로 이용 가능하다는 사실만 알고 넘어가자.

앞서 윈도우를 생성할 때, Elementary에서는 부모-자식 관계가 존재한다고 언급했었는데 elm_label_add() API에서는 매개변수로 부모 오브젝트를 요구하고 있다는 점을 주목하길 바란다. 실제로 모든 Elementary 위젯들을 생성하는 API들은 부모 오브젝트를 인자로 요구하며 이 예제의 경우 윈도우는 레이블의 부모가 되며, 레이블은 윈도우의 자식이 될 것이다. 내부적으로 부모-자식 간의 트리 구조가 성립되어 자식은 부모에게 종속되는데 여기서 우리가 기억해야 하는 점은 부모 오브젝트가 제거될 시 자식도 같이 제거된다는 사실이다.

다음으로 elm_object_text_set() API를 호출함으로써 레이블 위젯이 출력할 텍스트를 지정하고 있다. elm_object_text_set()은 레이아웃을 상속받은 Elementary 위젯이라면 모두 이용할 수 있는 공용 API인데, 사실 텍스트를 출력하는 기능이 있는 위젯이라면 이 API를 통해 화면에 출력할 텍스트를 지정할 수 있다. API의 접두사가 "elm_label"이 아닌, "elm_object"로 시작되고 있다는 점을 주목하면 보다 기억하기 쉬울 것이다.

어쨌든 간에, 우리는 elm_object_text_set() API를 이용하여 "Hello E World!"를 출력하도록 지정하였다.

void elm_object_text_set(Evas_Object *obj, const char *label);  

이 API는 첫 번째 인자로 대상 오브젝트를, 두 번째 인자로 출력할 문자열을 요구한다.

다음으로 텍스트가 출력될 영역이 100, 100에서 200, 100이 되도록 지정하고 화면 상에 출력되도록 Evas API들을 이용하였다. 자, 그럼 결과물을 확인해 보도록 하자.


5. 윈도우에 Hello E World!를 출력한 결과


만약 여러분이100, 100에서 200, 100 영역에 "Hello E World!" 문자열이 가득 채워져 출력되길 바랬다면 오산이다. 해당 영역은 단지 레이블 위젯이 차지할 영역일 뿐이며, 실제로 출력될 텍스트는 레이블 위젯의 폰트명과 폰트 크기를 기반으로 출력될 뿐이다.

자, 이쯤이면 첫 번째 예제가 거의 완성되었다. 하지만 할 일이 하나 더 남아있다. 우리는 Ctrl+C를 통한 애플리케이션 종료가 아닌, 어떤 키 입력이 왔을 때 애플리케이션을 종료할 수 있어야 한다. 이를 위해, 키 입력 사실을 알 수 있어야 하며 어떤 키가 눌러졌는지도 파악할 수 있어야 한다. 이번 예제에서는 Evas를 통해 이를 구현할 것이며 Esc 키가 눌렸을 때 애플리케이션을 종료하도록 할 것이다. 소스 코드를 보도록 하자.

#include <Elementary.h>

void key_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Evas_Event_Key_Down *ev = event_info;

   if (!strcmp(ev->keyname, "Escape"))
     {
        printf("Good Bye!\n");
        elm_exit();
     }
}

int main(int argc, char **argv)
{
   elm_init(argc, argv);

   //윈도우를 생성하고 초기화 한다.
   Evas_Object *win = elm_win_add(NULL, "Elementary Window", ELM_WIN_BASIC);
   elm_win_title_set(win, "Elementary Window");
   evas_object_resize(win, 480, 400);
   evas_object_move(win, 100, 100);
   evas_object_show(win);

   //윈도우 오브젝트에 키 입력 이벤트를 추가한다.
   evas_object_event_callback_add(win, EVAS_CALLBACK_KEY_DOWN, key_down_cb, NULL);

   //윈도우 오브젝트로부터 Evas 핸들을 반환받는다.
   Evas *e = evas_object_evas_get(win);

   //반환받은 Evas 핸들을 이용하여 화면에 출력할 사각형을 생성한다.
   Evas_Object *rect = evas_object_rectangle_add(e);

   //...

   return 0;
}

새로 추가된 부분은 볼드체로 표시하였다. 먼저 main() 함수에서 윈도우 오브젝트를 생성하는 부분을 보면 윈도우 오브젝트에 이벤트 콜백 함수를 등록하는 부분이 추가되었다.

void evas_object_event_callback_add(Evas_Object *obj, Evas_Callback_Type type, Evas_Object_Event_Cb func, const void *data);

첫 번째 매개변수는 이벤트 대상이 되는 오브젝트를 가리키며 예제에서는 윈도우 오브젝트에 해당한다. 두 번째 매개변수는 이벤트 타입을 전달 받는데, 우리는 받고자 하는 이벤트 종류를 두 번째 인자로 결정할 수가 있다. 예제에서는 키가 눌렸을 때의 이벤트를 받고자 하기 때문에 EVAS_CALLBACK_KEY_DOWN을 전달한다. 이 외에도 우리가 확인할 수 있는 이벤트 종류는 다양하며 다음과 같은 이벤트들에 대해서 우리는 콜백 함수를 등록할 수 있다.

EVAS_CALLBACK_MOUSE_IN - 마우스 커서가 대상 안에 들어온 순간
EVAS_CALLBACK_MOUSE_OUT - 마우스 커서가 대상 밖으로 벗어날 때
EVAS_CALLBACK_MOUSE_DOWN - 마우스 버튼이 눌렸을 때
EVAS_CALLBACK_MOUSE_UP - 마우스 버튼이 해제되었을 때
EVAS_CALLBACK_MOUSE_MOVE - 마우스가 이동할 때
EVAS_CALLBACK_MOUSE_WHEEL - 마우스 휠이 작동할 때
EVAS_CALLBACK_MULTI_DOWN - 멀티 터치와 같은 환경에서 동시에 여러 터치 입력이 발생했을 때
EVAS_CALLBACK_MULTI_UP - 여러 터치 입력 중에서 첫 번째를 제외한 나머지 터치 입력들이 해제될 때
EVAS_CALLBACK_MULTI_MOVE - 여러 터치 입력 중에서 첫 번째를 제외한 나머지 터치 입력들이 이동할 때
EVAS_CALLBACK_FREE – 삭제 요청을 받은 후, Evas에서 완전히 제거될 때
EVAS_CALLBACK_KEY_DOWN - 키 입력이 발생할 때
EVAS_CALLBACK_KEY_UP - 키 입력이 해제될 때
EVAS_CALLBACK_FOCUS_IN – 포커스 상태가 될 때
EVAS_CALLBACK_FOCUS_OUT – 포커스 상태에서 해제될 때
EVAS_CALLBACK_SHOW - evas_object_show()가 호출될 때
EVAS_CALLBACK_HIDE - evas_object_hide()가 호출될 때
EVAS_CALLBACK_MOVE - evas_object_move()가 호출될 때
EVAS_CALLBACK_RESIZE - evas_object_resize()가 호출될 때
EVAS_CALLBACK_RESTACK - evas_object_stack_below(), evas_object_stack_above()등이 호출되어 레이어의 Z 오더의 순서가 바뀔 때
EVAS_CALLBACK_DEL - evas_object_del()이 호출될 때
EVAS_CALLBACK_HOLD - 이벤트 전달 상태가 변경될 때
EVAS_CALLBACK_CHANGED_SIZE_HINTS – evas_object_size_hint_weight_set(), evas_object_size_hint_align_set()과 같은 오브젝트의 크기 힌트 값이 변경될 때
EVAS_CALLBACK_IMAGE_PRELOADED - 이미지 오브젝트의 경우 이미지의 PRELOAD가 완료되었을 때

세 번째 매개변수와 네 번째 매개변수는 각각 등록할 콜백 함수 포인터와 콜백 함수에 전달될 사용자 데이터 포인터를 전달 받는다. 이번 예제에서는 콜백 함수에 전달할 데이터가 딱히 없으므로 네 번째 인자로 NULL을 전달하고 있다. 다음은 콜백 함수의 원형이다.

void (*Evas_Object_Event_Cb)(void *data, Evas *e, Evas_Object *obj, void *event_info);

첫 번째 매개변수로 앞서 콜백 함수 등록 시 네 번째로 전달한 사용자 데이터 포인터가 전달되며, 두 번째는 Evas 핸들, 세 번째는 이벤트 대상 오브젝트, 네 번째는 이벤트 타입에 해당하는 부가적인 정보가 전달된다.

예제에서는 key_down_cb()을 main() 앞에 구현하였고 이를 이벤트 콜백 함수로서 추가하였다. 실제로 키 입력이 발생할 시 이 콜백 함수가 호출될 것이며 첫 번째 매개변수로는 NULL 값이, 두 번째는 Evas 핸들, 세 번째 매개변수로 윈도우 오브젝트가 전달되고 네 번째 매개변수로는 EVAS_CALLBACK_KEY_DOWN에 해당하는 부가적인 정보가 전달된다. 여기서 우리는 네 번째 인자 event_info를 Evas_Event_Key_Down 포인터 형으로 변환하고 우리가 원하는 정보를 확인할 수가 있다. 물론 EVAS_CALLBACK_MOUSE_IN, EVAS_CALLBACK_MULTI_UP와 같은 이벤트 콜백을 등록했다면, 그에 해당하는 데이터로 변환하여 이용해야 한다. 실제로 어떠한 데이터 형으로 변환해야 할지는 Evas 문서를 확인하거나 다음 장, “캔버스 엔진: Evas” 편을 확인해 보길 바란다.

우리는 event_info 인자를 Evas_Event_Key_Down 타입의 포인터 형으로 변환함으로써 구조체가 담고 있는 데이터에 접근할 수 있으며 이 중, keyname을 통해 현재 어떤 키가 눌려졌는지 확인할 수 있다. 예제에서는 Escape (ESC) 키가 눌러졌는지 확인하기 위해서 단순히 문자열 비교를 하고 있으며, 실제로 키 이름이 동일할 시 elm_exit()를 호출하여 elm_run()에 의해 가동 중인 메인 루프를 종료시킨다.

여기서 매우 추상적으로만 보았던 elm_run()의 메인 루프는 다음과 같은 코드로 표현할 수 있을 것이다.

#include <Elementary.h>
 
void key_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   //...
}

int main(int argc, char **argv)
{
   elm_init(argc, argv);

   //...

   //레이블 위젯을 생성한다. 이 위젯은 텍스트를 출력하는 기능을 제공한다.
   Evas_Object *label = elm_label_add(win);
   elm_object_text_set(label, "Hello E World!");
   evas_object_move(label, 100, 100);
   evas_object_resize(label, 200, 100);
   evas_object_show(label);
   
   //elm_run()이 호출되면 메인 루프가 가동되며 내부적으로 여러 작업들이 수행될 것이다.
   while(1)
   {
    //윈도우, 사각형, 레이블 등, 오브젝트들의 위치 및 크기 계산 후 렌더링
    //만약 시스템으로부터 키 입력이 발생했다면 사용자가 등록한 key_down_cb 호출
    //만약 elm_exit()가 호출 되었다면 while문 종료
   }
   
   elm_shutdown();

   return 0;
}

자, 그럼 실제로 애플리케이션을 가동 후 Esc 키를 눌러 종료해 보길 바란다. 이제, 윈도우가 생성되고 윈도우 내에 색상 및 텍스트가 출력되며 키 이벤트에 반응하는 그럴듯한 첫 번째 애플리케이션이 완성되었다!


저작자 표시
신고
1. 첫 번째 프로그램


자, 그럼 이번 절에서는 본격적으로 EFL 애플리케이션을 작성해 보도록 하자. 프로그래밍 관례에 따라 "Hello E World"를 출력하는 매우 단순한 예제로 시작한다.

#include <Elementary.h>

int main(int argc, char **argv)
{
   elm_init(argc, argv);
 
   printf("Hello E World!\n");
 
   elm_run();
 
   elm_shutdown();
 
   return 0;
}

무엇보다도, 여러분이 당황하지 않도록 최소한의 API를 이용하여 예제를 완성해 보았다. 우선 Elementary의 헤더파일을 추가한다. 당연하겠지만, Elementary API를 이용하기 위해선 API가 선언되어 있어야 하며 이는 Elementary.h 파일 내부에 모두 명시되어 있다. 다음으로 매우 익숙한 main() 함수가 존재하고 main() 함수 안에는 "Hello E World!"를 출력하는 printf() 함수가 존재한다. 이외로 생소한 세 개의 API 호출이 보인다. 이들이 바로 Elementary에서 제공하는 API들이며, 하는 역할은 매우 직관적이다. elm_init()은 라이브러리를 초기화한다. Elementary의 기능들을 사용하기 위한 기본 설정들을 내부적으로 수행할 것이다.

int elm_init(int argc, char **argv);

이 API는 두 개의 인자를 전달받는데, 이들은 프로그램 실행 시 시스템을 통해 전달받는 인자의 개수와 그 개수에 해당하는 문자열 배열의 이중 포인터이다. main() 함수에서 전달받은 매개변수들을 그대로 넘겨주면 된다.

elm_run()은 프로그램의 메인 루프를 작동시킨다. 일부 라이브러리들과는 다르게 Elementary는 라이브러리 내부적으로 프로그램 기본 루프를 제공하고 있으며 이 루프는 애플리케이션에서 필요한 메인 루프의 역할을 대신 담당하게 될 것이다. 이해하기 쉽게 비유하자면, 다음과 같은 코드와 동일하다.

#include <Elementary.h>
 
int main(int argc, char **argv)
{
   elm_init(argc, argv);

   printf("Hello E World!\n");

   while(1)
   { 
      //애플리케이션은 여기서 어떤 작업을 수행할 것이다.
   }

   elm_shutdown();

   return 0;
}

물론, elm_run()이 수행하는 메인 루프 내부에서는 앞선 예제와 다르게 매우 중요한 작업들을 수행한다. 여기서는 단지 여러분의 이해를 돕기 위해 매우 추상적으로 설명했음을 알아두자. 어찌되었던 간에, 다음을 살펴보면 elm_shutdown()을 호출하고 있다. elm_shutdown()은 elm_init()과 반대로, 메인 루프 내부에서 작동 중이던 모든 기능들을 중단하고 할당했던 모든 리소스를 해제한다. 따라서 elm_shutdown()은 애플리케이션이 종료될 시점에는 반드시 호출되어야 하며 항상 elm_init()과 한 쌍으로 호출되어야 한다.

그런데, 이 예제에서는 elm_run()에 의해 가동된 메인 루프가 무한 루프와 같이 계속 작동하기 때문에 elm_shutdown()에는 도달하지 못할 것이다. 물론 어떤 이벤트에 의해 루프는 종료될 수 있지만, 그 부분은 좀 더 뒤에서 살펴보도록 하고, 일단 이 프로그램을 가동해 보자. 미안한 말이지만, 만약 여러분이 뭔가 그럴 듯한 결과물을 기대하고 있었다면 실망도 그만큼 클 것이다. 심지어, 아직은 프로그램을 중단할 방법이 없기 때문에 Ctrl+C를 이용하여 프로그램을 중단해야 한다.

그림 1. 첫 번째 예제 실행 결과


그렇다고 해서 이런 시시한 예제에 분노하기엔 아직 이르다. 다음 예제를 계속 보도록 하자. 이번에는 콘솔에 단순한 글자를 출력하는 것이 아닌 하나의 윈도우가 존재하는 진짜 프로그램(?)이 될 것이다.

#include <Elementary.h>

int main(int argc, char **argv)
{
   elm_init(argc, argv);
 
   //윈도우를 생성하고 초기화 한다.
   Evas_Object *win = elm_win_add(NULL, "Elementary Window", ELM_WIN_BASIC);
   elm_win_title_set(win, "Elementary Window");
   evas_object_resize(win, 480, 400);
   evas_object_move(win, 100, 100);
   evas_object_show(win);

   elm_run();

   elm_shutdown();

   return 0;
}

자, 단 다섯 라인을 추가하여 윈도우를 생성하는 프로그램을 완성하였다. 추가한 부분은 볼드체로 표기하였는데 한 라인씩 살펴보도록 하자.

우선, 첫 번째 라인에서는 윈도우를 생성하고 생성된 윈도우 오브젝트를 반환받는다. 이 때, 반환되는 윈도우 오브젝트의 데이터 타입은 Evas_Object의 포인터 형이다. Evas_Object는 EFL에서 오브젝트를 가리키는 단일 인터페이스이며 반드시 포인터 형으로만 쓸 수 있다. 우리는 Evas_Object 타입을 통해 다양한 종류의 오브젝트들을 일관된 방식으로 이용할 수 있다는 장점을 얻을 수 있다.

윈도우를 생성하는 API는 elm_win_add()인데, 이 API는 세 개의 매개변수를 갖는다.

Evas_Object *elm_win_add(Evas_Object *parent, const char *name, Elm_Win_Type type);

첫 번째 매개변수는 부모 윈도우 오브젝트를 전달받는다. 만약 부모가 없는 독립 윈도우라면 예제와 같이 NULL을 넘겨주면 된다. 만약 여러분이 타 윈도우 프로그래밍에 대한 경험이 있다면 윈도우엔 부모-자식 관계가 있다는 사실을 알고 있을 지도 모른다. EFL에서도 윈도우 간에는 부모-자식 간의 관계가 존재하며 이들의 개념은 타 윈도우 프로그래밍의 부모-자식 개념과 대체로 유사하다. 일반적으로 자식 윈도우의 기능 동작은 부모 윈도우의 기능 동작에 영향을 받도록 되어 있다. 일부 독자들에겐 생소한 개념일 지도 모르나, 당장은 중요하지 않으므로 일단은 부모-자식 메커니즘이 준비되어 있다는 사실만 알아두고 넘어가자.

두 번째 매개변수는 윈도우 이름을 전달받는다. elm_win_add()를 통해 윈도우를 생성하는 과정에서는 내부적으로 윈도우 관리자가 개입하며, 윈도우 관리자는 전달받은 윈도우 이름을 식별하여 경우에 따라 어떤 작업을 수행할 수도 있지만 일반 애플리케이션에서는 큰 의미는 없다. 여러분의 독자적인 이름을 정하여 전달하면 된다.

마지막 세 번째 매개변수는 윈도우 타입을 전달받는다. Elm_Win_Type 이라는 열거형이 선언되어 있는데, 우리는 다음과 같은 값들을 이용할 수 있다.

ELM_WIN_BASIC - 일반 윈도우 타입. 일반적으로는 이 값을 이용한다.
ELM_WIN_DIALOG_BASIC - 다이얼로그 윈도우 타입
ELM_WIN_DESKTOP – 데스크탑 배경 화면처럼 아이콘이 배치될 수 있는 백그라운드 윈도우 타입
ELM_WIN_DOCK - 도킹이나 패널 윈도우 타입. 보통 다른 윈도우들 보다 상단에 존재한다.
ELM_WIN_TOOLBAR - 툴바와 같이 위치가 고정되어 있지 않고 분리되어 이동할 수 있는 타입
ELM_WIN_MENU - ELM_WIN_TOOLBAR와 거의 유사
ELM_WIN_NOTIFICATION - 경고 메시지나 이메일 도착을 알려주는 알림 윈도우 타입
ELM_WIN_DND - 타 윈도우와 드래그 앤 드롭(Drag & Drop) 기능을 지원하기 위한 윈도우 타입. elm_win_override_set() API를 이용해야 한다.
ELM_WIN_INLINED_IMAGE - 어떤 특정 이미지 버퍼에 그려질 윈도우. 윈도우뿐만 아니라 윈도우 내의 콘텐츠 역시 동일한 버퍼에 그려질 것이다. 주의할 점은 반드시 부모 윈도우를 가져야 하며 이 타입의 윈도우는 자식 윈도우로서 버퍼에 그려진다. 사용자는 이 버퍼의 데이터를 조작하거나 Evas_Map등을 이용하여 GUI 이펙트를 적용할 수 있다.
ELM_WIN_SOCKET_IMAGE - ELM_WIN_INLINE_IMAGE 타입과 동일하게 이 윈도우는 이미지 버퍼에 그려진다. 하지만 이 이미지 버퍼는 다른 EFL 애플리케이션과 공유되며, 플러그(Plug) 오브젝트가 해당 이미지를 대신 그려줄 수 있다. 달리 말하면, 윈도우 출력물을 타 프로세스들과 공유할 때 쓰인다.

추후에는 더 많은 윈도우 타입이 지원될 수 있으므로 필요하다면 최신 Elementary 문서를 참고하길 바란다. 일반적인 애플리케이션이라면 ELM_WIN_BASIC 타입의 윈도우가 널리 쓰이므로 여러분은 예제와 같이 ELM_WIN_BASIC을 전달해야 할 것이다.

자, 다음 API를 살펴보면 생성한 윈도우에 타이틀을 지정한다. 이 타이틀은 보통 윈도우 관리자에 의해 윈도우 상단 타이틀 바에 표시될 것이다.

void elm_win_title_set(Evas_Object *obj, const char *title);

예제와 같이, 이 API는 윈도우 오브젝트와 표시하고자 하는 윈도우 타이틀 문자열을 전달받는다.

다음에 이어지는 세 개의 API들은 윈도우 크기와 위치를 지정하고 화면 상에 보이겠다고 명시하는 API들이다.

void evas_object_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y);

void evas_object_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h);

void evas_object_show(Evas_Object *obj);

각 API들의 이름 및 매개변수는 상당히 직관적이라서 추가적인 설명이 필요 없을 듯 하다. 다만 앞서 살펴본 Elementary API들과 비교하여 뭔가 다른 점이 보였을 텐데, 이들의 API들은 접두사가 elm이 아닌 evas로 시작하고 있다. 말 그대로, 앞서 살펴봤던 API들은 Elementary 라이브러리에서 제공하던 API들이고 이 세 개의 API들은 Evas 라이브러리에서 제공하는 API들이다. 실제로 Elementary는 Evas를 기반으로 작성된 라이브러리이며 Evas_Object 인터페이스 역시 Evas에서 정의하고 있다. Evas에서는 이러한 추상화된 Evas_Object 인터페이스를 정의함과 동시에 이 인터페이스로 사용할 수 있는 일반적인 기능들을 제공하는데 그러한 기능들 중 일부가 바로 move, resize, show와 같은 API들이다.

이 후에도 여러분은 이 책의 예제들을 통해, Evas, Elementary와 같은 서로 다른 EFL 라이브러리 간의 기능들이 혼재되어 사용되는 경우를 종종 목격할 수 있을 것이다. 그리고 여러분이 어느 정도 EFL에 숙달된다면, 필요한 기능들을 적절한 EFL 라이브러리에서 가져다 쓸 수 있을 것이다.

좌우지간, 이렇게 함으로써 우리는 480 x 400 픽셀 사이즈의 윈도우를 하나 생성하고 100, 100 좌표에 이 윈도우를 위치시켜 화면에 보여주도록 프로그래밍을 했다. 자 그럼, 결과물을 확인해 보도록 하자.


그림 2. 윈도우 생성 결과 화면


만약 프로그램을 가동시킨 콘솔 창 이외에 윈도우가 하나 더 추가적으로 생성되었다면 여러분은 이번 예제를 성공적으로 수행했음을 확신할 수 있다. 물론 윈도우 내에는 빈 영역만 존재하므로 다소 싱겁긴 마찬가지일 수도 있다. 필자도 동의하므로 잠시 휴식을 갖은 후, 다음 절에서는 이 윈도우 내부에 뭔가를 조금 채워보도록 하자.


저작자 표시
신고