Hermet Park is a senior software engineer who 's worked on Tizen platform in Samsung Electronics since 2009. He specializes in Tizen UI framework and has contributed to improvement of Tizen UX and UI, Tizen graphics system and convenient tools for application development. Also, he involved in projects such as Samsung mobile, wearable and TV products. He has also worked in EFL open source community under the nick name 'Hermet' for years as well as runs EFL Korea community as an evangelist to spread it in Korea. Most of the times, he hangs out at home and plays video games or investigate IT trends and SW technology but at times, he is such a dreamer. He loves reading books, writing essays, drawings, playing the piano so on.

Email hermetpark@gmail.com
LinkedIn linkedin.com/in/hermetpark


신고


This is a my first piano song that I've practiced.
The title is "Stones" which is the theme of the Ultima games which I'd been totally into.
That melody touches me whenever I listen, it makes me feel if I go back in old days.
Peaceful, Sad, Lovely and Soothing.
Even the playing is a little awful, I believe you guys love this song if you were also a big pan of Ultima.
신고



EFL provides multiple widget categories and a vast collection of low level and high level APIs to create UI layouts tailored to specific application requirements.
In addition, the programming model also uses a script language called EDC (Edje Data Collection), so that the application logic can be separated from the UI design. Using EDC, application developers can also make complex and dynamic UI layouts. The Tizen SDK has rich collection of UI creation tools and documentation for application developers to make use of the above facilities.
During the course of this webinar, Hermet will introduce the concepts involved in creating complex UI layouts using EFL. The topics covered include EDC, API interactions and different widget classes and how all of these can be combined to build a complex UI layout for an application.
In addition, the usage of dynamic EDC editor tool, Enventor, is discussed. If you are an application developer building native applications for Tizen devices or in general want to understand the native UI programming model of Tizen, this webinar is for you.
저작자 표시
신고


Please be there gently in my memory.
Material: H pencil, A4
저작자 표시
신고

어릴 적, 인적이 드문 대로의 인도를 따라가다 보면 지하로 내려가는 계단이 있었는데 계단 끝에는 작은 공터가 있었다. 비라도 내리면 침수될 것만 같은 그곳은 언제나 누추했고 온갖 잡동사니와 쓰레기들이 나뒹굴며 지저분하게 뒤엉켜 있었다.

공터의 한쪽엔 좁은 길목이 있었다. 원채 좁고 협소해서 눈에 잘 띄지도 않았다. 그 길목의 끝엔 내가 살던 작은 동굴이 자리잡고 있었는데 밤이라도 되면 세상과 단절된 듯 동굴 입구는 암흑 그 자체였다. 암흑을 주시하자니, 마치 동굴 구석 곳곳에 온갖 잡귀들이 누군가가 들어오기만을 숨죽이며 기다리는 듯 했다.

동굴 입구에 들어가기 전 난 항상 초조했다. 어둠을 직시하고 동굴 안으로 들어갈 것인가, 아니면 뒤돌아 화려한 네온사인들이 반짝이는 세상의 거리로 되돌아갈 것인가. 물론, 초조할 뿐 선택은 항상 어둠이었다. 그 곳은 어둠이었지만 그 어둠의 저편엔 또 다른 세상이 분명 존재했다. 그리고 그 곳에서 나는 나의 의지와 생각으로 뭐든 새로운 것을 이룩해 낼 수 있었다. 내 심장으로부터 고동치는 자유에 대한 열망과 새로운 세상을 갈망하는 나의 몸부림에, 드넑은 초원이 눈앞에 펼쳐지고 멀리 실개천이 은빛 아지랑이처럼 반짝이는 곳. 마치 새처럼 푸른 초원 위를 지유롭게 날아다니면 이름모를 상쾌한 풀내음이 콧끝을 스쳐 지나가고 신선한 저녁바람이 귓가에 멤돌며, 초원 너머 에메랄드 빛 바다와 황금빛 모래사장은 눈부시게 반짝였다.

그 황금빛 모래사장엔 내가 살던 테라스가 있는 이층 집이 있었는데 나는 항상 문을 개방해 두었다. 종종 나는 테라스에 놓인 벤치에 앉아 류트를 연주하며 따사로운 봄날의 정취를 느꼈다. 내 손끝에서 울려퍼지는 류트의 멜로디는... 매우 흥겹고 신났으며 때로는 신비롭기까지 했다!

뜨거운 태양과 마주한 어느날, 난 후끈 달아오른 나머지 버닝 피엔드의 깃털이 달린 녹색 모자를 쓴 채 라마를 타고 길을 떠났다. 언제나 그랬듯, 미지의 세계를 향할 때마다 난 흥분되어 설레발을 쳤지.

그 후, 낯선 지역의 길을 떠나던 중 한 소년을 만났다. 당시 나는 다소 낯을 가리는 내성적인 반면, 그 소년는 나보다 더 어렸지만 털털하고 매우 밝은 쾌활한 소년이었으며 -유머가 뭔지 아는 녀석이었어!- 공교롭게도 우리은 매우 잘 어울렸다. 성격은 달랐지만 우리는 악기를 연주하는 법을 알았다. 하프와 류트가 우리가 지나온 길목에 멜로디로 자취를 남겼지. 정반대의 성격의 두 사람은 춤을 출 줄 알았고 멜로디의 선율을 느낄 수 있는 감성이 있었다.

지역 이곳 저곳 순방하며 우리는 긴 시간을 함께 했다. 궂은 날씨의 힘든 순간도 함께 극복했으며 북쪽 산맥의 아름다운 정경에 매료된 채, 미친듯이 수다를 떨며 즐거운 나날을 보내기도 했다. 잿빛 구름이 낀 어느 날엔 아무것도 남지 않은 공허한 벌판을 둘이 말없이 지나기도 했는데, 그 모든 순간은 최소한 나에게 파노라마 같은 영상의 미를 남겨준 듯 하다.

그 친구와 함께 여행을 떠난 후, 여덟 달 가량이 지난 시점에 우리는 우리의 과거에 대해 이야기를 나눈 적이 있었다. 어느 선술집에서 에일주를 마시곤 취기에 조금 들떠 있었지.

"난 한 때 여행을 매우 싫어했어. 그건 불편하기만 했다구. 여행을 떠나면 편하게 쉬질 못하잖아? 그리고 오랜 친구를 만날 수도 없지. 난 고향을 떠나기 전에, 내가 살던 집을 떠나는 일은 상상조차 하질 못했지. 그래, 난 머물기를 좋아했고 안락하고 편안한 공간을 선호했어. 집보다 더할 나위없는 공간은 없었지. 아, 하루 일과를 끝내고 집에 들어가 샤워를 한 후, 편안한 소파에 앉아 네가 좋아하는 음악을 연주한다고 생각해봐! 뉴젤름의 파도소리와 소금기 가득한 바다향, 그리고 선선한 저녁 바람이 머무는 집! 그 곳에 있으면 뭐든지 상상할 수 있었고 난 그 상상 속에 삶을 키워 나갔어. 어쩌면 그 상상이 나의 인생이 될 지도 모른다고 생각했지. 그래, 그냥 상상 자체의 이야기가 말이야. 그게 바로 나의 삶이고 내가 삶을 마감할 시점에 그 상상을 그리워하는 거지. 난 그것만으로도 행복한 녀석이었어. 그런 성격 탓에 주위에 친구는 별로 없었지만, 그럼에도 불구하고 내 친구들은 모두 나를 위로해 줄 수 있었고 내가 의지할 수 있는, 때론 내가 그들을 위로해주기도 할, 매우 절실한 친구들이었다네. 하지만, 거기엔 우리가 어쩔 수 없는 이념이 존재했네. 질서와 자유가 존재했어. 사람들은 서로 싸웠네. 질서와 자유에는 공존할 수 없는 대립이 존재했기 때문이지. 사람들은 싸우고 피를 흘렸는데, 특히 신뢰와 신념이 강한 이들은 더욱 헌신적으로 그들이 추구하는 길을 향해 나아갔지. 난 질서를 위해 힘을 썼네. 비록 자유주의자였지만 질서없이는 평화는 존재하기 어렵다고 생각했기 때문이지. 그런데 나의 그 절실한 친구들 중 몇몇은 나와 반대였지. 마음 아팠지만, 신념과 우정은 타협이 불가능했어. 그리곤 서로의 투쟁 속에 우리는 우리가 추구하고자 했던 것이 무엇이었는지 잊어버렸지. 우리가 추구하는 삶은 어느덧 존재하지 않았고 내일엔 싸움만이 존재했네. 내일은 더 이상 경이롭지가 않았어. 내가 꿈꾸던 이야기도 타락했지. 전투 속에서 난 내 자신을 잃어버렸네. 감성도 잃었고 암울하기만 했지. 결과적으로, 세속을 벗어나고 싶었네. 정취를 남기지 않고 끊임없이 발걸음을 옮긴다면, 어쩌면 난 그 때야 비로소 노래를 할 수 있을 거라고 확신했지. 어느날 뜨거운 태양으로 뉴젤름의 모래사장이 반짝이던 때, 나는 무언가를 느꼈지. 우리는 태양이 밝고 뜨겁다고 하지만 그 이면에 존재하는 본질을 직접 느낀 사람은 아무도 없지 않는가? 세상 사람들 모두가 어쩌면 그것과 같다고 생각했네. 나의 삶 역시 어쩌면 피상적인 이야기로만 수두룩 하다는 것을. 그리고 내 여행은 그 태양의 이면을 확인하기 위해 시작되었지."

나의 이야기는 진솔했고 그 친구는 조용히 내 이야기를 들어주었다. 그 때 그는 결코 소년이 아니었다. 그 친구는 평소보다 진지하고 사려깊어 보였지. 사실 그 때 그 친구도 나에게 그의 이야기를 들려주었지만 안타깝게도, 난 술에 취한 나머지 그 부분의 기억을 제대로 하지 못한다.

이 후 우리는 한 동안 말동무가 되어 같이 여행을 떠났지만 결국 헤어질 수 밖에 없었다. 그 친구의 꿈은 목장을 운영하는 것이었기 때문이었다. 수 백년도 더 된 나무들이 우거진 숲 속을 지나 어느 고원지대에 도달했을 때 그는 말했다.

"이곳이야. 내가 찾던 곳은. 난 여기서 평생을 살겠어. 양과 토끼를 키우고 그들의 털로 실과 천을 만들거야."

이별은 슬펐지만, 난 과감하게 그를 뒤로 한 채 발걸음을 옮겼다.

"언젠간 이 곳에 돌아오면 너의 천으로 만든 멋진 옷을 선물해 줘."

마지막으로, 우리는 우리가 즐겨 연주하던 화음과 춤을 즐겼다.

결국, 난 길고 긴 모험을 다시 홀로 떠났다. 떠나면서 난 생각했다. 어쩌면 먼 훗날, 그 친구는 나처럼 안락한 그 곳을 떠나 먼 길을 여행할 것이라는 것을. 하지만 난 그가 경험해보지 않은 그 곳 생활을 하면서 느끼고 깨닫는 것이 많을 거라고 믿었다.

긴 시간이 흘러 앙상한 나무가 메마른 손을 흔들 때, 외딴 오솔길을 홀로 지나던 난 차가운 겨울 바람에 몸서리를 쳤다. 그리곤 문뜩 어떤 멜로디를 흥얼거렸다. 그건 내가, 해변에서 자유와 안락함을 느꼈고 라마를 타고 길을 떠났으며 어린 친구를 만났던 시절을 상기시켜주었다. 황금빛 해변에서 연주했던 그 음악. 여행을 하는 동안 내가, 우리가 함께 연주했던 바로 그 화음. 그 시절을 회상했고 그 녀석이 그리웠다.

소침해진 채, 한동안 멍하니 나뭇가지를 올려다 보다가 저밀어오는 가슴을 주최하지 못하고 난 무릎을 꿇고 괴로워 했다. 그 영롱한 기억들은 나의 머릿 속에서 사라지지 않았고 오히려 나를 붙잩은 채 벗어날 수 없듯이 과거의 순간으로 잡아당기는 기분이었다. 무엇보다도 괴로웠던 이유는, 사실 황금 빛 해변도, 이 층 테라스도, 라마도, 그 친구도 모두가 거짓이었고 그건 그저 내 작은 기억 속에 어렴풋이 남은 환상에 불과했으며 과거로 돌아갈 수 없다는 사실 때문이었다. 솔직히 말하면, 내가 그토록 그리워했던 것은 붙잡을 수 없고 돌아갈 수도 없는 허망한 허구일 뿐이었다.

나는 그렇게 오솔길 한 가운데 쓰러지듯이 괴로워하며 기침을 해댔다. 매우 외로웠다. 나는 그 소년이 머무는 고원지대의 목장으로 당장이라도 달려가고 싶은 심정이었지만, 차갑고 싸늘한 매서운 겨울 공기는 매정하기만 했고 내 주위엔 그 누구도 존재하지 않았다. 돌아갈 수 있는 뉴젤름이나 소년이 살고 있을 고원지대는 애초에 존재하지 않았다. 난 망연자실 하듯 자리에 주저앉아 하늘을 쳐다보았다. 하늘에서는 새하얀 눈이 내 머리 위로 내려앉고 있었다. 생각해보니, 그래도 나의 기억 중에 유일한 진실이 하나 있었으니 그건 바로 류트의 선율이었다. 그나마 내가 그 추억을 상기할 수 있는 유일한 열쇠임에 틀림없었다. 순간, 정신을 차린 난 그 선율을 악보에 재빨리 옮겨적었다. 잊혀진다는 사실에, 내 기억에서 다시 사라질까봐. 그리고 그 선율의 제목을 “Coin Song" 이라고 이름 지었다.




저작자 표시
신고

Contribution of a series of EFL(Enlightenment Foundation Libraries) articles.

Korean Title: 월간 마이크로소프트웨어 (마소인터렉티브)
Publication Date: 2012.07 ~ 2012.12

Enlightenment started as a project to build a Window Manager for X11. That was way back in 1996. It has grown much since. It still produces this Window Manager, but it has evolved to also cover Mobile, Wearable and TV UI needs for projects such as Tizen as well as traditional the “desktop” UI. In the process of developing a Window Manager, A set of libraries were developed in order to achieve that goal. These libraries are known collectively as EFL. They cover a range of functionality from main-loop, to graphics, scene graphs, networking, widgets, data storage, IPC and much more.







저작자 표시
신고


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. 윈도우 생성 결과 화면


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


저작자 표시
신고

Translation project

Korean Title: 예제로 배우는 CUDA 프로그래밍 (BJ퍼블릭)
Publication Date: 2011.10.26
Original book: Cuda by Example (Nvidia)

“This book is required reading for anyone working with accelerator-based computing systems."

Cuda is a computing architecture designed to facilitate the development of parallel programs. In conjunction with a comprehensive software platform, the Cuda architecture enables programmers to draw on the immense power of graphics processing units (GPUs) when building high-performance applications. GPUs, of course, have long been available for demanding graphics and game applications. Cuda now brings this valuable resource to programmers working on applications in other domains, including science, engineering, and finance. No knowledge of graphics programming is required? Just the ability to program in a modestly extended version of C.




저작자 표시
신고