Previous Next Table of Contents

2. X 프로그래밍 기초 : Xlib

X 윈도우 프로그래밍에 도전하시는 여러분을 환영합니다. 저도 아는 것은 많이 없지만 여러분을 조금이라도 도울 수 있다면, 그리고 저의 강좌를 계기로 더욱 더 많은 수준높은 X 윈도우 프로그래밍 강좌들이 생겨나길 기대하면서 X 윈도우 프로그래밍 기초과정을 시작할까 합니다. 어렵더라도 포기하지 마시고 끝까지 저와 함께 가셔서 당당한 X 윈도우 프로그래머로 우뚝 서십시요. 그리고 리눅스를 빛내줍니다.

2.1 X 윈도우 프로그래밍에 들어가기 앞서

여러분의 선택은 정말로 탁월합니다. X 윈도우 시스템은 MS 윈도우즈 95와 같은 엉터리에다 단명하는 체제와는 전혀 다른 수준의 안정되고 표준적인 시스템입니다. 여러분께서 미래를 생각하신다면, 바로 X 윈도우 프로그래밍을 선택하십시요.

자, 우선 점검해야 할 것들이 있습니다. 여러분의 시스템이 X 프로그래밍을 할 준비가 되어 있는지 알아봐야 하지 않을까요? 이 글을 읽는 분들은 최소한 X 윈도우가 작동하고 계신 선택받은(?) 분들일 것이고, 남은 것은 여러분이 슬랙웨어 설치시 X 프로그래밍을 할 수 있는 헤더 파일 등을 제대로 설치하셨는지 확인해 보시기 바랍니다. /usr/include/X11 ㅤ그리고 /usr/X11/lib 에 화일들이 있는지 말입니다.

모두 이상없다고요? 네, 그러면 시작합시다.

2.2 X 윈도우 프로그래밍이란 ?

X 윈도우 프로그램이란 X 윈도우의 '자원(Resource)'을 이용하는 프로그램입니다. X의 자원들은 대표적으로 창(Window), 색상(Color Map), 폰트(font) 등이 있습니다. 이러한 자원들을 X 윈도우 서버(Server)라고 하는 녀석이 전권을 가지고 관리하고 있습니다. 우리의 역할은 이미 정해져 있는 '규칙'에 따라서, 자원을 관리하고 있는 서버에 연결하여, 서버더러 우리가 원하는 자원을 서비스해달라고 요청하는 일입니다. 바로 우리는 서버라는 놈과 어떻게 연결할 것인지, 그리고 어떻게 서버더러 이것 저것 우리가 원하는 것을 달라고 하는지를 배우는 것을 목표로 하게 됩니다.

이러한 프로그램을 클라이언트(Client)라고 하지요. 서버/클라이언트 개념이라고 해서 항상 프로그램을 "무엇이 무엇에 연결하여 원하는 일을 한다"라는 사고방식 하에 모든 프로그램을 짜게 됩니다. 이는 더 나아가 세계 저편에 있는 서버에 우리집의 클라이언트가 연결을 하여 서비스를 받는다는 "네트워크 지향적"이고 미래 지향적인 프로그래밍이 됩니다. 인터넷 시대를 살고 있는 우리에게는 아주 중요한 개념이라고 할 수 있습니다. 여러분께서 저와 함께 X 프로그래밍의 세계를 탐험하시다 보면 아주 자연스럽게 멋진 네트워크 프로그램에 도전하실 수 있을 겁니다.

우리는 단지 정해진 규칙에 따라 서버에 연결하고, 서비스를 받고, 연결을 해제 하는 것을 배우는 것일 뿐이라는 사실을 항상 명심해주세요!

2.3 알아두어야 할 사실

여러분 모두 당장에 실제적인 X 윈도우 프로그래밍을 하고 싶으실 겁니다. 하지만, 이러한 사실들은 알고 들어가셔야 더욱 더 빨리 프로그래밍에 익숙해지실 수 있다고 생각합니다.

cd /usr/X11/lib 해보세요. 그리고 ls ... lib라는 문자로 시작하는 많은 화일들이 있는 것을 확인하실 수 있을 겁니다. 이 녀석들 '라이브러리'는 우리를 항상 도와주는 놈들입니다. 이들이 없으면 아무 것도 할 수가 없습니다. 이 녀석들을 여러분께 소개시켜 드리고자 합니다.

나머지 친구들은 소개시켜 드리기 힘들군요. 아직 때가 되지 않아서... 나중에 소개시켜 드릴 기회가 올 지...

2.4 자, 이제 시작해볼까요?

클라이언트가 제일 먼저 해야 할 일은 무엇일까요? ... 서버에 접속하는 일입니다.

과연 나는 어떤 서버를 선택할 것인가? 하는 문제를 해결해야 합니다. 보통 도스, 윈도우즈 환경에 익숙하신 분들은 좀 생소한 개념이지요. 하지만 요즘은 인터넷을 많이 하시기 때문에 이해하시기 어렵지는 않을 겁니다. 리눅스 X 윈도우 환경에서는 아주 간단한 문장들로 여러분 옆에 있는 컴퓨터 말고도 세상 저편에 있는 컴퓨터의 모니터에다 하나의 창을 띄우는 일 정도는 너무도 쉽게 할 수 있거든요.

vi, emacs와 같은 에디터를 이용해서 다음과 같은 내용이 들어 있는 화일을 하나 만들어 보세요.

 #include <X11/Xlib.h>

 main()
 {
        Display *d ;

        d = XOpenDisplay ("localhost:0.0") ;

        XCloseDisplay (d) ;
 }

만약 위의 내용을 ex01.c라는 이름으로 저장하셨다면,

cc -o ex01 ex01.c -lX11 해보십시요.

컴파일이 잘됩니까? 안된다구요? 왜 그럴까요? 힘든 영어지만 에러 사유를 잘 보시면 아마도 헤더파일을 못찾는다고 그러지 않나요?

그러면 다음과 같이 INCLUDE 디렉토리를 지정해주십시요.

cc -o ex01 ex01.c -I/usr/include/X11 -lX11

컴파일은 되도 링크가 안되다면 라이브러리 디렉토리도 지정해주세요. -L/usr/X11/lib 를 명령행에 추가시키시면 됩니다.

제대로 컴파일이 이루어졌다면, 실행을 시켜보죠. ex10 엔터!

여러분께서 실망하시는 것을 느낄 수 있습니다. :) 이게 뭐냐?

하지만! 이 프로그램은 X 윈도우 프로그래밍의 거의 모든 것을 보여주는 것이나 다름없는 아주 중요한 프로그램입니다. 저는 그렇게 우기고 싶네요. :)

이 엉터리(?) X 윈도우 프로그램에 대한 설명을 드리고자 합니다.

첫줄 INCLUDE 문장은 XOpenDisplay, XCloseDisplay, Display 변수가 선언되어 있는 헤더화일 Xlib.h를 포함시키는 문장입니다. 다음으로 당연히 main 함수가 정의되어야겠죠?

Display형 포인터 d 를 선언합니다.

Display란?

디스플레이가 무엇인지 잘 아실 겁니다. 여러분께서 바로 눈 앞에 두고 계신 것이 모니터라고 하는 '디스플레이'입니다. 그런데 여기서 Display라고 하는 것은 X 윈도우 시스템에서 약간 확장된 의미로 생각합니다.

정의 : Display란 하나의 서버가 관리하고 있는 1개 이상의 스크린(Screen), 1개의 키보드, 마우스 등등으로 구성되어 있는 집합체이며 워크스테이션(Workstation) 이라고 부르기도 한다.

이러한 디스플레이는 보통 모니터 하나 전체를 나타내는 스크린을 하나 갖는 경우가 보통이지만 특수한 경우 2개 이상의 스크린을 갖는 경우도 있을 수 있습니다. 하나의 컴퓨터에 두 개의 모니터를 쓰는 "듀얼 모니터" 시스템도 종종 볼 수 있습니다. 그 경우 하드웨어가 받쳐준다면, X 시스템은 두 개의 화면을 각각 이용할 수도 있습니다. 이런 프로그램 하나 만들면 정말 재미있겠죠? X 윈도우 시스템에서는 너무도 쉽게 이루어집니다.

XOpenDisplay 함수

자, 이제 우리가 원하는 디스플레이 서버에 접속을 해봅시다.

XOpenDisplay ("localhost:0.0");

이 문장은 localhost의 0번 디스플레이(즉, 첫번째 디스플레이)의 0번 스크린(즉, 첫번째 스크린)에 접속하라는 뜻입니다. localhost는 바로 여러분 컴퓨터를 지칭하는 호스트명입니다. 이것을 만약 다른 인터넷 호스트 주소로 써준다면, 어떻겠니까? 여러분의 컴퓨터가 네트워크에 물려있고, 상대방 호스트를 사용할 권한이 주어져 있다면, 우리 프로그램은 저 멀리에 있는 호스트 서버와 연결을 하게 됩니다. 아니, 이렇게 간단할 수가!!! 인터넷 프로그램이라는 것이 별 것 아니군요.

localhost:0.0 과 같은 표현식을 눈여겨 보아주십시요. 명령행에서 한 번 set 이라고 쳐보세요. 아마 내용이 많아서 more 를 해주셔야 할 겁니다. 한 번 그 내용을 주루룩 살펴보시면 DISPLAY=:0.0이라는 표현식을 찾으실 수 있을 겁니다. 그 다음으로 HOSTDISPLAY=freeyong:0.0 이런 표현도 보실 수 있을 겁니다. 물론 freeyong과 같은 부분은 여러분마다 다르지요. 여러분께서 지정해주신 이름이 나타날 겁니다. localhost:0.0 을 freeyong:0.0등으로 바꾸어 주셔도 됩니다. 하지만 일부러 아무 의미도 없는 이름을 주어보십시요. 컴파일하고 실행을 시켜 보시면 그 결과를 아실 수 있을 겁니다. 꼭! 확인해보세요. 보통은 표현식 부분에 NULL을 주면 현재 DISPLAY라는 환경변수의 값을 읽어서 서버로 사용합니다.

XCloseDisplay 함수

접속해서 한 일은 하나도 없지만, 이제 접속을 마쳐야 하지 않을까요? 그 함수가 바로 XCloseDisplay 함수입니다.

오늘은 아주 아주 간단하지만, 또한 아주 아주 근본적인 프로그램을 하나 짜보았습니다. 모든 X 윈도우 프로그램은 기본적으로 예제 1 프로그램과 그 형식이 같습니다. 서버에 연결하고 서비스 받은 후, 접속을 해제합니다. 모든 유용한 작업은 XOpenDisplay 함수와 XCloseDisplay 함수 사이에 적어주시면 됩니다.

이 글을 다 읽으신 후, 꼭 Xlib.h 화일의 내용을 살펴보십시요. 예를 들어 Display 형 변수는 어떻게 선언, 정의되어 있는지 보시면, 정말로 많은 정보를지니고 있는 구조체(struct)라는 것을 확인하실 수 있습니다.

2.5 간단한 창 하나 만들어 봅시다.

앞에서 만든 원초적(?) 프로그램은 단지 서버와 접속하자 마자 접속을 끊어버리는 경우였습니다. 그럼, 이제는 서버에 접속하고 나서 아주 간단한 일을 하나 시켜보도록 합시다. 바로 창 하나를 만들어서 표시해보는 겁니다.

 #include <X11/Xlib.h>

 main()
 {
        Display *d ;
        Window   w, root ;

        d = XOpenDisplay(NULL) ;

        root = XDefaultRootWindow (d);
        w = XCreateSimpleWindow ( d, root, 50, 50, 400, 300,
                                  2, BlackPixel (d,0), WhitePixel(d,0) );
        XMapWindow (d, w);
        XFlush (d);

        getchar();

        XCloseDisplay (d);
 }

이번 예제에서는 Window 라는 새로운 자료형과 XDefaultRootWindow(), XCreateSimpleWindow(), XMapWindow(), XFlush() 등의 새로운 함수들이 나오는군요. 앗! BlackPixel(), WhitePixel()과 같은 매크로도 보이는군요. 하지만 이 매크로들은 나중에 설명드리기로 하겠습니다. 하루에 너무 많은 것을 배우면 머리가 아프잖아요?

Window 형 변수

우리가 창을 만들고 싶을 때는 창 하나마다 위의 예에서 나온 Window 형 변수 하나가 필요하다고 생각하시면 됩니다. Window 형 변수 하나는 그 창에 대한 모든 정보를 지니고 있는 복잡한 구조체 정도라고 생각하시면 됩니다. 예상할 수 있는 것으로는 창의 크기, 위치, 색상 등이 있습니다.

창 생성, 표시 ...

지금부터 설명드리는 기본적인 절차를 꼭 머리 속에 넣어두세요. 어떤 창을 만들든지 우리는 우선적으로 그 창에 대한 정보를 저장하고 있을 변수 하나를 이미 만들어놓야야 합니다. 즉 Window 형 변수 하나를. 그 다음 서버더러 창을 하나 만들겠다고 서비스 요청을 합니다. (조금 뒤에 설명드리는 함수를 통해) 이변이 없는 한 서버는 창이라는 자원 하나를 서버 측(!!!)에 만들어 놓습니다. 그리고 창 자원에 대한 자원 고유번호(Resource ID)를 아까 클라이언트 쪽에서 만들어 놓은 변수에 반환해줍니다. 이 변수는 바로 클라이언트와 서버 측에 만들어진 한 창에 대한 연결통로 역할을 해줍니다. 무지 중요하죠?

이렇게 서버 측에다 창 자원 하나를 만들어놓고, 그 다음에는 여러분이 원하는 때에 그 창을 화면에 표시해달라고 요청하면 서버가 알아서 그 창을 표시해줍니다. 물론 요청에 따라 화면에서 사라지게 만들 수도 있습니다.

또는 현재 나타내져 있는 창에 대하여 배경색과 테두리색을 바꾼다든지 하는 수정 작업 또한 아까의 Window 형 변수를 통해서 행할 수 있습니다.

우리는 앞으로 모든 자원에 대하여 서버 측에 자원을 만들어 놓고 우리 클라이언트 프로그램에서는 단지 그 자원에 대한 ID만을 변수에다 저장해놓고 있습니다. 그리고 원하는 때에 그 자원을 표시한다든지, 수정한다든지, 또는 없앤다든지 하는 작업을 행합니다. 그리고 나중에 정말 필요가 없어진 자원에 대해서는 서버 측에다 삭제하라고 통고합니다.

제가 이렇게 길게 설명드린 개념을 꼭 숙지해주십시요. 어떻게 보면 우리가 앞으로 하는 일의 거의 대부분을 말씀드린 것이나 다름없습니다.

XCreateSimpleWindow 함수에 대하여...

함수 이름 그대로 해석해보면 "간단한 창을 생성한다"이지 않습니까? 우리가 영어 문화권에 살고 있다면, 이것만큼 식은 죽 먹기가 어디 있겠습니까 만은... 간단한 창을 만드는 함수에 대하여 알아보도록 하지요.

 함수의 원형(Prototype)

 Window XCreateSimpleWindow (   Display*        display,
                                Window          parent,
                                int             x,
                                int             y,
                                unsigned int    width,
                                unsigned int    height,
                                unsigned int    border_width,
                                unsigned long   border_color,
                                unsigned long   background_color );

함수 한 번 정말 거창하군요. 앞으로 나오는 함수들 대부분이 거의 이렇다고 생각 하셔도 무방합니다. 정말로 많은 정보를 전달해주어야 하는 것을 보실 수 있을 겁니다. 짐 캐리처럼 숨을 크게 들이쉰 후, 다다다 쉴 새 없이 얘기해보자면, 1번째 Display 형 포인터는 XOpenDisplay()에서 접속한 디스플레이를 가리키는 포인터이며, 2번째 Window는 현재 만들고자 하는 창의 소속되는 부모 창이며, 3번째, 4번째는 각각 x, y 좌표를 뜻하고, 5번째는 픽셀 단위의 가로폭, 6번째는 픽셀 단위의 세로폭, 그리고 7번째는 테두리의 두께, 마지막으로 8번째와 9번째는 테두리 색과 창의 기본적인 배경색을 카리키는 unsigned long 형 수치값입니다.

보통 하나의 디스플레이에서 대부분의 일을 해치우니까 1번째 인수가 상당히 귀찮을 수도 있지만, 생각해보세요, 여러분의 클라이언트 프로그램이 다중 디스플레이 접속 프로그램이 될 수도 있지 않습니까? 그러면, 여기 저기 두 개 이상의 디스플레이를 간단하게 제어할 수 있습니다. 바로 요 첫번째 인수를 가지고 말입니다. 2번째, 부모 창은 무엇인가? --- X 윈도우의 모든 창은 항상 어떤 창에 속해 있거나, 어떤 창들을 자기 자식(Child)으로 갖습니다. 아주 근본적으로는 X 윈도우가 뜨자마자 생기는 창은 바로 테두리도 없는 창, 루트(root)창입니다. 바로 이 창에 모든 창들이 속하는 것입니다. 어떤 프로그램이든 처음 창은 바로 루트창의 자식창이 됩니다. 그 창은 다시 자기 자식창들을 얼마든지 가질 수 있지요. 어찌 되었든간에 새로 생성되는 창은 족보(?)를 가져야 합니다. 자기 부모창은 알아야 하지 않겠어요? 여기서 잠깐 XDefaultRootWindow()라는 함수에 대하여 알아봅시다.

 함수의 원형

 Window XDefaultRootWindow ( Display *display );

이 녀석의 기능은 Display 형 포인터가 가리키는 서버의 기본(Default) 루트창의 창 ID를 알아다가 ID를 Window 형 변수에다 되돌려 주는 역할입니다.

우리가 만들고자 하는 창은 직접적으로 루트창의 바로 아래 자식창이 되므로, 위에서 나온 XCreateSimpleWindow()의 두번째에서 부모창 ID를 지정해주어야 하기 때문에 필요한 함수입니다.

나머지는 자명하므로 8번째, 9번째 색상 지정 부분에 대한 설명만 드립니다.

X 윈도우에서 색상은 또한 서버가 제공하는 하나의 자원으로서 서버가 관리를 하고 있습니다. 따라서 우리가 생각하는 것 만큼 쉽게 색상을 쓰거나 할 수는 없습니다. 혹시 여러분은 XV 와 같은 그래픽 프로그램을 쓰면서 그래픽 화면의 질이 어떨 때는 다르게 나타나는 것을 경험해보신 적 있습니까? 그 경우엔 XV가 원하는 만큼의 색상 자원을 서버에게서 공급받지 못하기 때문입니다. 어떤 이유에서든...

예에서 드러나듯 그냥 검정과 흰색을 지칭하는 쉬운 숫자를 쓰는 게 아니라, BlackPixel(), WhitePixel()이라는 매크로를 썼습니다. 이번 강좌에서는 색상을 쓰는 과정이 그렇게 간단하지는 않다는 사실만 기억해주시기 바랍니다. 조만간에 색상에 대한 얘기가 이어집니다. 그 때까지만 참아주십시요.

XMapWindow()

이 녀석은 무엇인가 하면, 위에서 서버측에 만들라고 통보한 창을 실제로 나타내라는 지시를 서버에 보내는 녀석입니다. 창의 생성과 표시는 별개의 과정이지요.

 함수의 원형

 XMapWindow ( Display *display, Window w );

특정 디스플레이 서버에서 w라는 창을 화면에 실제로 표시합니다. 여기서 영어 Map은 동사로서 "지도로 그리다","배치하다"의 뜻입니다. 제가 다니는 지질과학과에서는 지질도 그리는 것을 매핑(Mapping)한다라고 말합니다.

XFlush()

아니, 이건 또 뭘까요? 플러쉬(flush)는 화장실에서 볼 일을 다 본 후, 변기의 물을 내리는 것을 말합니다. 한꺼번에 물을 좍 흘려보내는 그런 일입니다. X 윈도우에 뭐 그런 일이 필요하냐구요? 거참 이상하군요.

C 프로그램을 공부해보시면, printf와 같은 많은 출력문들이 실행 직후에 출력이 이루어지는 것은 아니라는 사실을 아실 겁니다. 실제는 매번 출력 명령을 받을 때 마다 출력을 하는 것보다는 한꺼번에 버퍼에 모아놓고 때가 되면 단 한 번의 실행으로 효율성을 높이는 기술을 채택합니다. X 윈도우도 또한 그렇습니다. 그 수많은 클라이언트들로부터의 서비스 요청을 그 때 그 때 실행하게 되면 능률이 떨어지게 되므로, 서버는 버퍼를 마련하고 그 버퍼가 차거나 버퍼를 비우라는 명시적인 요구가 있을 때만 실행을 하게 됩니다.

이번 예는 X 윈도우 서버에게는 콧방귀도 뀔 필요가 업을 만큼, 자잘한 요구이기 때문에 그런 명령을 내린다고 해서 버퍼가 찰 리 만무합니다. 그러니, 강제로 '변기 물을 쫙 내려야겠죠?'

 함수의 원형

 XFlush ( Display *display );

컴파일

위 예제를 ex02.c라고 저장하셨다면,

 cc -o ex02 ex02.c -lX11 -I/usr/X11/include -L/usr/X11/lib

보통 -I, -L 옵션은 주지 않으셔도 되지만 제대로 헤더화일과 라이브러리를 찾지 못할 때는 명시적으로 주시면 됩니다.

실행

자, 이제 X 터미널 상에서 실행을 해볼까요? 우리가 예상했던 것과는 좀 다를 것입니다. 왜냐구요? 여러분께서 fvwm과 같은 윈도우 관리자를 쓰고 계시면, 우리가 의도하지는 않았지만 그 놈이 우리가 만들고자 하는 창의 모양에 개입하고 들어오기 때문입니다. 이 부분에 대한 설명 또한 다음 번으로 미루기로 하고 단지 창이 만들어졌다는 것만 확인하고 넘어가죠. :)

종료는 실행시킨 터미널 창에서 엔터키를 한 번 눌러주시면 됩니다.

아참! 예제에서 왜 getchar()를 썼는지 한 번 생각해보세요. 그리고, 위에서 엔터 키가 아니라 스페이스바 같은 것을 누르면 왜 종료하지 않는지도 생각해보세요. 아래에서 그 답을 드리겠습니다.

2.6 창 안에 또 창을 만들어 봅시다.

바로 앞에서 아주 간단한 창 하나를 만들어 보았지요. 이번에는 그 창 안에다 다른 작은 창들을 여러 개 만들어 보기로 하겠습니다.

 #include <X11/Xlib.h>

 main()
 {
        Display *d ;
        Window root, p, w1, w2, w3 ; 
        unsigned long Black, White ;

        d = XOpenDisplay (NULL);

        /* 주 프로그램 부분 시작 */
        root = DefaultRootWindow(d);
        Black = BlackPixel(d,0);
        White = WhitePixel(d,0);

        p  = XCreateSimpleWindow (d, root, 100, 100,
                                        600, 400, 2, Black, White );
        w1 = XCreateSimpleWindow (d, p, 50, 50,
                                        200, 150, 2, Black, White );
        w2 = XCreateSimpleWindow (d, p, 200, 100,
                                        200, 150, 2, Black, White );
        w3 = XCreateSimpleWindow (d, p, 350, 200,
                                        200, 150, 2, Black, White );
        XMapWindow(d, p);
        XMapWindow(d, w1);
        XMapWindow(d, w2);
        XMapWindow(d, w3);

        XFlush(d);
        sleep(3);

        XDestroySubwindows(d, p);
        XDestoryWindow(d, p);
        /* 주 프로그램 부분 끝 */

        XCloseDisplay (d);
}

이제는 예제 프로그램이 점점 복잡해지는 것 같고, 실제 프로그램 같아지는군요. 하지만 기본 뼈대는 변함이 없습니다! 제가 /* */로 표시한 부분만이 늘었을 뿐, 우리는 항상 1. 서버에 접속, 2. 서버에 요청, 3. 서버와 접속 해제 라는 기본적인 등식을 머리 속에 두고 있어야 합니다.

모든 창은 자식창을 여러 개 가질 수 있다.

모든 창의 부모창은 결국 루트창입니다. 그 창 안에서 모든 자식창들이 생성되고 표시됩니다. 또 그 자식창들은 바로 똑같은 과정을 통해서 자기 자신의 자식창들을 가질 수 있습니다. 마지막 단계의 자식창들도 또한 마찬가지로... 그러한 원리를 이번 예에서 확인하실 수 있습니다. 산아제한 같은 건 필요없겠죠? :)

Window 형 변수 root, p, w1, w2, w3 이렇게 다섯개를 선언해두었습니다. root는 DefaultRootWindow()라는 매크로를 통해서 현재 루트창의 ID를 기억하고 있습니다. 첫번째 XCreateSimpleWindow 함수를 통해서 루트창의 자식창으로서 p 창을 만들었습니다. 두번째 같은 함수를 통해서 이번에는 위에서 만들어 놓은 p 창을 부모창으로 하는 자식창 w1 을 만들었습니다. 같은 과정을 통해서 p 창의 자식창들 w2, w3를 서버 측에 만들어 놓았습니다. 귀찮으시더라도 창생성 함수들에 주어진 XY 좌표, 창 크기를 보시고 창이 어떻게 나타날 것인지를 예측해보십시요.

<< 잠깐 생각중... >>

컴파일 방법은 이전과 동일합니다.

이번 예제에서는 Black, White라는 unsigned long 형 변수 두 개를 선언해놓고는 전번 예제에서도 선보인 적이 있는 BlackPixel(), WhitePixel()이라는 매크로를 써서 각각 검정과 흰색을 나타내는 수치를 저장하고 있습니다. 창 생성함수가 여러 번 쓰였으므로 그 때마다 색상 지정부분에 매크로를 써주기 보다는 변수에 저장해두고 그 변수의 저장값을 사용하고자 했습니다. 별 건 아니죠.

자, 서버측에 만들어 놓은 창들을 나타내 봐야겠죠? :)

XMapWindow 함수를 통해서 디스플레이 d 에 자식창 p 를 표현합니다. 그 다음 3개의 문장도 마찬가지입니다. 디스플레이 d 에 w1, w2, w3를 표현합니다. w1, w2, w3는 p 창의 자식창들로 등록,생성되어 있기 때문에 p 창 안에 표현됩니다. 그리고, 말씀은 안드렸지만 자식창들의 좌표계는 바로 전 부모창의 좌표계를 기준으로 합니다. 부모창의 왼쪽 윗구석이 자식창들의 원점(0,0)이 되는 것이지요.

자식창들을 한꺼번에 나타내자.

사실 마지막 3개의 문장은 약간 소비적인 문장입니다. 창의 생성과 표시라는 과정은 별개의 과정이므로, 일단 사용을 위해서 몇 개를 생성시켜놓은 후, 그 때 그 때 원하는 자식창들만 표시할 때는 XMapWindow 함수를 써야 하겠지만, 많은 경우 한꺼번에 자식창들을 몽땅 표시하고자 할 때가 많고, 그 경우에는 참 번거롭죠? 그래서 여기서 새로운 함수 하나를 소개시켜 드리고자 합니다.

 함수의 원형

 XMapSubwindows ( Display * d, Window w );

디스플레이 d 에서 창 w 의 모든 자식창들을 일괄적으로 화면에 표시합니다. 편리를 위해 제공되는 함수이지요.

앞으로도 많은 함수들이 기능은 거의 같으면서 이렇게 사소하게 다른 여러 가지들로 마련되어 있다는 것을 보실 수 있습니다.

쓸모없는 자원은 없애자.

 함수의 원형 : XDestroyWindow ( Display *d, Window p );
 함수의 원형 : XDestroySubwindows ( Display *d, Window p );

창을 파괴해버리라고(Destory) 요청하는 함수입니다. 더 이상 표시할 필요가 없어진 창들은 자원절약을 위해서라도(메모리를 차지하고 있으니까요) 제 때 없애주는 것이 필요합니다. 위에서보면 두 종류의 유사한 함수가 있는데, 첫번째 것은 지정된 하나의 창을 없애는 것이고, 두번째는 그 창이 포함하고 있는 모든 자식창들을 찾아서 없애주는 것입니다. 물론 자식창들 중에서 선별해서 없애려고 하신다면 당연히 하나씩 없애는 함수인 첫번째 것을 쓰실 수 밖에 없습니다.

바로 전 강의 질문에 대한 답

키보드 입력 또한 버퍼방식이므로 스페이스바를 몇 번 쳐보았자 실제로 프로그램 에는 전달이 되지 않다가 리턴키를 받으면 일시에 플러쉬됩니다.

오늘 강의를 마치면서...

정말 보잘 것 없는 프로그램 예라고 생각하실 지 모르겠지만, 제가 보기에는 X 윈도우 프로그래밍에서 창을 표시할 줄 아신다면 거의 반은 배우신 것이라고 확실히 말씀드릴 수 있습니다. 더욱 고차원적으로 버튼, 대화상자, 메뉴 등의 그래픽 인터페이스도 결국엔 자그마한 창들의 결합일 뿐입니다. 나중에 손쉽게 바로 우리 곁에 있는 Athena Widget 이라고 하는 편리한 인터페이스 보따리를 사용해보게 될 텐데요, 아마도 여러분 스스로 정말 색다른 인터페이스를 만들고 싶다는 생각이 들게 되실 겁니다. 엄청난 인터페이스의 자유! 이것은 X 윈도우가 다른 어떤 GUI 시스템 보다도 개방적임을 증명해보이게 될 겁니다. 기대해주세요.

이 짤막한 예를, 이 예에서 보이는 순서를 꼭 이해하시고 머리 속에 항상 기억해 주십시요. 아주 중요한 패턴 중에 하나입니다.

2.7 이제는 흑백이 아니라 칼라 창을 만들어봅시다.

오늘은 어떻게 색상(Color)을 서버로부터 얻어내고 사용할 수 있는지에 대해서 알아보기로 하겠습니다. 그 동안 계속 미뤄왔던 일이기도 하구요...

지금까지 아무 설명없이 저는 BlackPixel(), WhitePixel()등의 매크로를 예제에서 써왔습니다. 이름이 나타내듯 검정색/백색을 구하여 사용할 수 있었지요. 이제는 이것 말고도 우리가 원하는 색들을 일반적으로 어떻게 사용하는지에 대해서 알아봅니다.

색상 또한 서버가 관리하고 있는 자원의 하나입니다. 따라서 창을 만들어 달라고 서버에 요청하고, 표시하라고 요청하는 작업들과 마찬가지의 과정을 거치게 됩니다.

서버는 컬러맵(Color Map)이라고 하는 것을 가지고 있습니다. 색상을 나타내는 지도 또는 도표를 뜻하지요. 이것이 어떤 의미를 가지는지 잠깐 알아보겠습니다.

컬러맵이란?

컴퓨터가 사용하고 있는 출력장치 중에 비트맵 디스플레이에 속하는 CRT라는 것이 있습니다. 바로 여러분께서 눈 앞에 두고 계신 모니터이지요. 이 모니터의 원리는 여러분 모두 아시다시피 빛의 3원색에 대응하는 3개의 전자총이 모니터 표면에 발라져 있는 RGB 형광물질에 알맞게 비춰짐으로써 우리가 원하는 모든 색을 얻는 것입니다. 우리가 노랑/보라/자주색 등을 원할 때는 빛의 3원색을 알맞게 써서 전자총을 발사하지요. 그런데, 바로 여기서 노랑/보라/자주색 등을 구현할 때 과연 노랑은 RGB 를 각각 어떤 농도로 섞어야 하는가? 보라/자주색은 어떠한가를 저장하고 있는 표가 필요한데요, 바로 그것이 컬러맵입니다.

서버는 인간이 아니라 기계이기 때문에 우리가 yellow라고 말하면 어떤 것인지 전혀 알 방법이 없습니다. 단지 자기가 가지고 있는 컬러맵에서 yellow에 해당되는 것을 찾아서 모니터에게 적당한 양의 RGB 조합을 전달할 뿐입니다. 어떻습니까? 컬러맵이라는 것이 정말 중요한 위치를 차지하고 있지요? 이것이 엉망이 되면 어떻게 될까요? 컴퓨터가 정신못차리고 노랑을 원할 때 분홍을 표시할 지도 모릅니다.

서버는 기본적인 컬러맵을 가지고 있습니다.

원하는 색을 선택하기 위해 거치는 과정

앞으로 색상을 이용하는 어떤 작업도 다음과 같은 과정을 거쳐야 합니다.

  1. 기본 컬러맵(Default ColorMap)의 ID를 알아낸다.
  2. 위 기본 컬러맵으로부터 원하는 색의 픽셀값(Pixel Value)를 알아낸다.

 #include <X11/Xlib.h>

 main()
 {
        Display *d;
        Window Root, w;
        /* 색상값을 알아낼 때 쓸 변수들 */
        Colormap CMap;
        XColor color, color_exact;
        unsigned long Black, Blue;

        d = XOpenDisplay ( NULL );

        /* 원하는 색상 검정/파랑을 얻는 부분 */
        CMap = XDefaultColormap ( d, 0 );
        XAllocNamedColor ( d, CMap, "black", &color, &color_exact );
        Black = color.pixel;
        XAllocNamedColor ( d, CMap, "blue",  &color, &color_exact );
        Blue  = color.pixel;

        Root = DefaultRootWindow ( d );
        w = XCreateSimpleWindow ( d, Root, 100, 100, 600, 400,
                                        2, Black, Blue );

        XMapWindow ( d, w );
        XFlush ( d );
        sleep(10);

        XDestroyWindow ( d, w );
        XCloseDisplay ( d );
 }

위의 예를 살펴보시면, 색상값(unsigned long형 값) 하나 얻는게 그렇게 쉬운 것은 아니라는 사실을 발견하셨을 겁니다.

맨 먼저 해야할 일은 컬러맵의 ID를 서버로부터 알아내는 것입니다. Colormap 형 변수 CMap을 하나 선언해두었습니다. 그리고 나서 XDefaultColormap() 함수를 써서 현재의 서버, 현재의 기본 화면(0번 화면)의 기본 컬러맵 ID를 반환받습니다.

 함수의 원형 
 
 Colormap XDefaultColormap ( Display *d, int screen_no );

이제 컬러맵을 알아냈으니, 우리가 원하는 색의 픽셀값을 알아내봅시다. 이 때에는 XColor 형 변수 두 개가 필요합니다. 그리고 XAllocnamedColor 함수를 써서 서버더러 함수에 주어진 컬러맵에서 우리가 원하는 색상이름을 찾아서 그 색상 이름에 해당하는 정보를 XColor형 변수에 저장하도록 요청합니다.

 함수의 원형

  Status XAllocNamedColor( Display *d,
                           Colormap cmap,
                           _XConst char* color_name,
                           XColor *screen_def_return,
                           XColor *exact_def_return );

여기서 나오는 새로운 자료형에 대해서 지금 모두 아실 필요는 없습니다. 아셔야 한다고 생각할 때 꼭 자세히 설명드리겠습니다. 함수의 이름 자체가 상당히 길고 설명적이기 때문에 그 내용을 알아보기 좋습니다. 물론 타이핑하기는 여간 힘든게 아니죠? 이 함수는 이름에서도 드러나듯이 Named Color 즉 이름이 붙어 있는 색상에 대하여, 우리가 이 함수에 주소로 전달해주는 XColor 형 변수 두 개에 알맞는 정보를 할당(Allocation)해줍니다. 하나는 스크린 기본값으로서 하드웨어적으로 가장 근사하게 실현시킬 수 있는 정보가 들어가고, 나머지 하나는 지정한 색의 정확한 정보가 들어간다고 합니다. 우리는 전자만 사용하도록 하겠습니다. 쓸만한 그래픽 프로그램을 만들기 위해서는 아주 정확하게 알아야 하겠지만 지금 제가 하고자 하는 강의는 재빨리 X 프로그래밍 전반에 대하여 훑어보는 수준이니 이해하세요.

으잉? 그런데 이름이 있는 색상이라니... 하실 분들이 계실 것 같군요. 전번 fvwm 강의 중 색상설정 강의 때 showrgb 라는 명령을 기억하십니까? 쉘 상태에서 showrgb 해보십시요. 그러면 현재 서버가 이해할 수 있는 이름붙은 색상들 목록이 RGB 값과 함께 주루룩 나타나게 될 겁니다. 바로 그 목록에 있는 것들만을 말합니다. 우리가 알고 있는 red, yellow, blue, green 등이 바로 그것입니다.

이변이 없는 한, 실패하지 않을테니 사실은 int 형 변수인 Status에는 0이 아닌 True 값이 반환되어 옵니다. 만약에 문제가 발생했다면 False, 0 입니다.

이제는 정보를 지니고 있는 XColor 형 변수를 써서 결국에 우리가 알고 싶었던 값을 알아내봅시다. XColor 형 변수는 Xlib.h 파일에 정의되어 있는 구조체 변수로서 몇 개의 색상에 대한 정보를 지니고 있다고 보시면 됩니다. 우리가 사용할것은 그 중에 unsigned long 형 멤버인 pixel 멤버입니다. 그 값을 Black 이나 Blue 변수에 저장시켜 놓으면 되는 것이지요.

요약

색상은 서버가 관리하는 자원입니다. 이를 사용하기 위해서는 XDefaultColormap 함수를 써서 기본적인 컬러맵 ID를 알아낸 후, XAllocNamedColor 함수를 통해서 특정 이름의 색상에 대한 XColor 정보를 알아냅니다. 그리고 나서 XColor 구조체의 pixel 멤버값을 읽어내시면 됩니다.

그런데, 색상 하나의 픽셀값을 알기 위해서 항상 이렇게 많은 과정을 거쳐야 하다니, 좀 너무한 것 같죠? 그렇다면, 여러분께서 색상이름을 전달해주면, 그 색상에 관한 픽셀값을 반환해주는 사용자 정의 함수를 작성하시면 될 겁니다. 꼭 한 번 만들어보세요. 책을 가지고 계신 분들은 아실 지도 모르겠네요. :)

2.8 만들어 놓은 창 가지고 놀기

오늘은 우리가 이미 만들어 놓은 창들에 대해서 이리저리 마음껏 놀아보겠습니다. 창의 색깔을 바꾼다든지, 창의 크기를 바꾼다든지, 없앴다가 다시 나타나게 한다든지 ... 뭐 이런 놀이를 하려고 합니다.

이미 만들어 놓은 창 색깔 바꾸기

일단 창을 만드실 때 배경색과 테두리색을 결정해놓기는 했지만, 사람이란게 마음이 달라질 때도 있는 것 아니겠어요? 자, 이미 표시되어 있는 창을 그대로 놔둔 채 색깔만 한 번 바꾸어 봅시다. 여러분이 마음에 드는 색을 골라보세요. 바로 전 시간에 말씀드린 색상 정보 알아내기는 기억하고 계시죠?

  1. 테두리(Border)색 변화
       XSetWindowBorder ( Display *d, Window w, unsigned long border_pixel );
    
  2. 배경(Background)색 변화
     XSetWindowBackground ( Display *d, Window w, unsigned long background_pixel );
    

설명이 필요한가요? 인수로 주어지는 Window 형 변수는 우리가 색을 변화시키고자 하는 대상 창을 나타내는 변수를 쓰면 되고요, 픽셀값은 여러분께서 원하시는 색의 픽셀값을 저번 강의에서처럼 구하셔서 전달하시면 되고...

자, 예제 3 번의 10초 간 지연 함수(sleep) 뒤에다 여러분께서 한번 이 함수들을 이용해서 색상을 바꾸어보십시요. 잊지 마실 것은 XFlush를 해주셔야 한다는 것입니다. XFlush를 해주시고 나서 또 다시 sleep 함수를 쓰셔야 그 결과를 확인하실 수있겠지요?

<< 생각 중... >>

여러분, 좀 어려운가요? 그러면 제가 해본 결과를 보여드리겠습니다.

예제 3 에 추가 sleep(10); 문장 다음부터입니다.

 XAllocNamedColor( d, CMap, "green", &color, &ExactColor );
 XSetWindowBorder( d, w, color.pixel );

 XAllocNamedColor( d, CMap, "peachpuff", &color, &ExactColor );
 XSetWindowBackground( d, w, color.pixel );

 /* ??? */

 XFlush ( d );

 sleep(5);
등등...

설명을 드리자면, 전반부에서는 green 색의 픽셀값을 알아내서 테두리색을 변화시켰고, 후반부에서는 peachpuff 색으로 배경색을 바꾸라고 지시했습니다. 그리고 꼭 물내리는 것 잊지 마십시요.(flush)

하지만 이렇게 한다고 해서 원하는 결과를 얻을 수 있는 것은 아닙니다. 아직 부족한 것이 하나 있습니다. 그것은 다음과 같은 함수입니다.

3. 창 배경을 지우는 함수, 아니 다시 칠하는 함수!!!

 
 XClearWindow ( Display *d, Window w );

함수의 이름에서 보이듯, 창을 지우는, 정확히 말해서 창의 배경을 지우는 함수입니다. 더욱 상세히 말씀드리자면, 현재의 배경을 지우고, 현재 창의 정보 중 배경색에 해당하는 색으로 다시 칠해주는 역할을 합니다. 배경을 바꾸시고자 할 때는 한 번 창을 지워주시던가 아니면 그와 똑같은 효과를 갖는 일을 해주시면 됩니다. 그것이 무엇이냐구요? 창을 UnMap 했다가 다시 Map하시면 됩니다.

따라서 제가 위에서 /* ??? */ 라고 한 부분에 다음과 같이 써넣어주십시요.

 XClearWindow ( d , w );

창의 테두리와 배경색을 바꾸는 행위는 창 조작에 있어서 아주 기본적인 행위임은 물론이거니와 아주 중요한 행위이기도 합니다. 여러분이 X 윈도우 프로그램에서 매일 보시는 버튼들을 보세요. 포인터를 가져다 대면 테두리나 그 자체 색이 변하는 것을 보실 수 있습니다. 결국에는 그 버튼도 하나 또는 그 이상의 창으로 이루어져 있는 것에 불과합니다. 메뉴도 그러하고요. X 윈도우는 그야말로 아주 쬐끄만 창부터 시작해서 터미널 창과 같은 큰 창들을 포함하고 있습니다.

창에 대한 여러 정보를 알아냅시다.

많은 분들께서 geometry라는 단어를 보신 적이 있을 겁니다. Geometry라 함은 창에 대한 다음과 같은 정보를 말합니다. 창의 좌표(물론 창의 왼쪽 윗구석 좌표를 말하겠죠?), 폭과 높이, 테두리 두께 등.

사각형 창의 기본 요소들이라고 할 수 있는 것이죠. 창 전반에 대한 정보를 가져다 주는 역할을 하는 함수 하나를 먼저 소개하겠습니다.

 함수의 원형 : <X11/Xlib.h>에 선언

  Status XGetGeometry ( Display *display,
                        Window drawable_object,
                        Window root_ID,
                        int *x,
                        int *y,
                        unsigned *width,
                        unsigned *height,
                        unsigned *border_width,
                        unsigned *depth
                       )

함수에 대한 설명 :

Geometry 정보를 가져오는(Get) 함수입니다. 첫번째 Display 형 포인터는 현재 작업 중인 디스플레이 ID이고, 두번째 Window 형 변수는 바로 우리가 geometry 정보를 알아내고자 하는 대상 창의 ID입니다. 그 다음 변수들은 하나같이 포인터 변수들입니다. 여러분도 C 프로그래밍을 해보셔서 아시겠지만, 어떤 함수든 반환값은 하나 밖에 없습니다. 하지만 그 함수를 통해 여러 개의 반환값을 가지고자 할 때 쓰는 기술이 바로 그 함수에게 주소 지정 방식의 호출(Call by Reference), 즉 포인터로 변수를 전달해주는 방식입니다. 그 함수에서 그 변수의 내용에 알맞는 값을 써주면 되니까요. 그렇습니다. 세번째 변수들부터는 우리가 알고 싶은 정보들을 가지고 올 변수들입니다. 세번째 Window형 변수는 두번째 인수에서 주어진 ID를 갖는 창이 속한 ROOT 창의 ID를 반환합니다. 저로서는 아직도 왜 세번째 인수를 주어야 하는지 그 필요성을 이해할 수는 없지만, 혼동하셔서는 안되는 것은 어떤 창이 속하는 자신의 부모창 ID가 반환되는 것이 아니라 절대적으로 ROOT 창의 ID가 반환된다는 사실입니다. 네번째 인수는 x 좌표, 다섯번째 인수는 y 좌표, 그 다음은 폭, 높이, 테두리 두께입니다. 그리고 마지막은 Depth 즉 깊이를 말하는데, 이것은 현재 창에서 색깔을 구현하는데 있어 몇 비트를 사용하고 있는가를 말해줍니다. 이 숫자가 8이면 8 비트 칼라(8bpp) 즉 256 칼라가 되겠죠? 16 bpp는 65536 칼라, 24 bpp는 16만 7천 칼라(16,777,216)를 말합니다.

자, 웃기는 사실은 위에서 알아낼 수 있는 x, y 좌표는 그 창이 속한 부모창의 좌표계를 기준으로 한다는 사실입니다. 만약 루트창이 A 창을 포함하고, A 창이 다시 B 창을 포함한다고 합시다. B 창에 대하여 XGetGeometry 하시면, 세번째 인수에는 ROOT창의 ID가, x, y 변수에는 A 창의 왼쪽 윗구석을 (0,0)으로 하는 좌표계를 쓰는 상대적인 B 창의 좌표계가 반환되어 옵니다.

덧붙이는 말 한 마디 :

마지막으로 덧붙이고자 하는 사실은 이렇습니다. 여러분께서 Xlib.h 화일에서 XGetGeometry 함수를 찾아서 실제 내용을 보시면 두번째 변수의 자료형은 Window 형이 아니라 실제로는 Drawable 즉 무엇인가를 그릴 수 있는 대상으로 되어 있다는 것을 발견하실 수 있습니다. 그것에 대한 설명은 다시 뒤로 미룹니다. 어찌 되었든 Drawable 형 자료에는 Window 형이 포함되어 있습니다.

이제는 창의 geometry를 바꾸어 봅시다.

창의 geometry를 바꾸는 함수들을 여기에 소개합니다.

 
 함수의 원형 : <X11/Xlib.h>에 선언

 XMoveWindow ( Display *display, Window w, int x, int y );
 XResizeWindow ( Display *display, Window w,
                        unsigned int width, unsigned int height );
 XSetWindowBorderWidth ( Display *display, Window w,
                                unsigned int border_width );

첫번째 함수는 창의 위치를 주어진 x, y 좌표값에 따라 이동시키는 녀석입니다. 물론 좌표는 부모창 좌표계를 기준으로 합니다. 두번째 함수는 Resize 즉 크기 변화시키는 함수로서 주어진 폭&높이 값에 따라 크기를 변화시킵니다. 세번째는 무슨 일을 하는지 설명해드려야 하나요? 네, 맞습니다. 테두리 폭을 변화시킵니다.

오늘의 모든 내용을 담고 있는 예제를 적어드리고 오늘 강의를 마칠까 합니다. 한 번 여러분들께서 분석해보세요.

/* 창의 색상 변화와 Geometry 변화 */

#include <X11/Xlib.h>

/* 함수 선언 */
unsigned long UsrColorPixel( Display*, char* );

int main()
{
        Display *d;
        Window w0, w1, w2; /* One TopLevel Window & Two Child Windows */
        unsigned long black_pixel;
        int w_X1, w_Y1, w_X2, w_Y2;    /* 두 자식창의 위치 좌표 */
        unsigned int width, height, I; /* 자식창의 폭 & 높이 */ 

        /* 서버와 접속하기 그리고 변수들을 초기화 */
        d = XOpenDisplay ( NULL );

        black_pixel = BlackPixel ( d, 0 );
        width = 200; height = 100;
        w_X1 = 10; w_Y1 = 10; w_X2 = width - 10; w_Y2 = height - 10;

        /* 자, 시작해볼까요? */
        printf( "I will make windows.\n" );
        sleep( 2 );


        /* 창 하나 그리고 자식창 2개 생성 */
        w0 = XCreateSimpleWindow ( d, DefaultRootWindow( d ),
                                  100, 100, width*2, height*2, 1,
                                  black_pixel, WhitePixel( d, 0 ) );
        w1 = XCreateSimpleWindow ( d, w0, w_X1, w_Y1, width, height, 1,
                                  black_pixel, UsrColorPixel( d, "magenta" ) );
        w2 = XCreateSimpleWindow ( d, w0, w_X2, w_Y2, width, height, 3,
                                  black_pixel, UsrColorPixel( d, "blue" ) );

        /* 창과 자식창을 화면상에 표시 */
        XMapWindow( d, w0 );
        XMapSubwindows( d, w0 );
        XFlush( d );

        printf( "Unmap & Map.\n" );
        sleep( 3 );



        /* 창 하나를 UNMAP, MAP */
        XUnmapWindow( d, w1 ); XFlush( d );
        sleep( 1 );
        XMapWindow( d, w1 ); XFlush ( d );
        printf( "I will change the color of windows.\n" );
        sleep( 3 );



        /* 창 하나의 색상을 변경 */
        XSetWindowBorder( d, w2, UsrColorPixel( d, "red" ) );
        XSetWindowBackground( d, w2, UsrColorPixel( d, "green" ) );
        XClearWindow( d, w2 );
        XFlush( d ); sleep( 1 );
        XSetWindowBackground( d, w0, UsrColorPixel( d, "yellow" ) );
        XClearWindow( d, w0 );
        XFlush( d );
        printf( "I will move windows.\n" );
        sleep( 3 );



        /* 창 하나씩 이동 */ 
        for ( ; w_X1 < width - 10 ; )
        {
                XMoveWindow( d, w1, w_X1++, w_Y1 );
                XFlush( d );
        }
        for ( ;  w_X1 > 10 ; )
        {
                XMoveWindow( d, w1, w_X1--, w_Y1 );
                XFlush( d );
        }
        for ( ;  w_Y2 > 10 ; )
        {
                XMoveWindow( d, w2, w_X2, w_Y2-- );
                XFlush( d );
        }
        for ( ;  w_Y2 < height - 10 ; )
        {
                XMoveWindow( d, w2, w_X2, w_Y2++ );
                XFlush( d );
        }
        printf( "I will change the size of windows.\n" );
        sleep( 3 );



        /* 창의 크기를 확장/축소 */
        XResizeWindow ( d, w1, width + 100, height + 50 );
        XMoveResizeWindow ( d, w2, w_X2 + 50, w_Y1 + 20, 
                                width - 100, height - 50 );
        /* XMoveResize !!! */
        XFlush ( d );
        printf( "At last, I will change the width of borders.\n" );
        sleep( 3 );



        /* 창의 테두리 확장 */
        for ( I = 1 ; I < 20 ; I++ )
        {
                XSetWindowBorderWidth ( d, w2, I );
                XFlush( d );
        }
        printf( "Jobs done. Merci.\n" );
        sleep( 3 );



        /* 창 파괴 & 서버와의 접속 해제 */
        XUnmapWindow( d, w0 );
        XUnmapSubwindows( d, w0 );
        XDestroySubwindows( d, w0 );
        XDestroyWindow( d, w0 );

        XCloseDisplay( d );


        return 0; /* 성공적으로 프로그램을 수행 */
}


/*

  UsrColorPixel() : 주어진 이름의 색상에 대한 기본 컬러맵의 픽셀값 반환

*/

unsigned long UsrColorPixel( display, name )
Display *display;
char *name;
{
        Colormap cmap;
        XColor c0, c1;

        cmap = DefaultColormap( display, 0 );

        XAllocNamedColor( display, cmap, name, &c0, &c1 );
        /* 여기서 우리는 c1 을 아직 이용하지 않습니다. */

        return ( c0.pixel );
}

여기까지가 예제 6 입니다. 마지막에 사용자 정의함수 UsrColorPixel()은 바로 전 시간에 제가 문제로 내드렸던 것에 대한 답 중 하나입니다. 여러분들께서 각자 자신만의 정의함수를 만들어 놓으셨겠지요? 저랑 비교해 보십시요.

이 예제를 보시면서 한 줄 한 줄이 어떤 일을 하게 될 것인지 미리 머리 속에서 그려보시고, 번거로우시더라도 꼭 예제를 자기 손으로 타이핑하시면서( 상당한 노가다죠? :) ) 함수 하나하나를 익히시기 바랍니다. 모든 함수들이 왜 그렇게 씌여져야 하는가, 왜 그렇게 밖에 만들지 못했는가에 대해서도 생각해주시면 더욱 좋구요. 그 다음, 이 예제 속에는 제가 설명드리지 않은 함수가 있습니다. 그 함수는 여러분께서 힘들이지 않고 그 의미와 사용법을 아실 수 있을 거라고 생각합니다.

여러분도 잘 아실 겁니다. 프로그래밍은 짜증날 정도로 많은 실수들의 연발 속에서 그 실력이 늘어간다는 사실, 그리고 엉뚱한 실수 속에서 중요한 문제들을 파악해 나갈 수 있다는 사실 말입니다. 다음과 같은 문장이 예제에 있죠?

 XSetWindowBackground( d, w0, UsrColorPixel( d, "yellow" ) );

요 문장을 /* */으로 주석문 처리하시거나 지우신 다음 컴파일하고 실행시켜 보세요. 자, 어떤 일이 일어납니까?

X 윈도우 프로그래밍 별 것 아닙니다. 창을 가지고 노는 장난이라고나 할까요?


Previous Next Table of Contents