Previous Next Table of Contents

7. X 프로그래밍 기초 : 이벤트처리

지금까지는 그 자체로는 실질적인 프로그램을 만들기에는 불가능한 가장 기본적인 개념들만을 익혀왔지만, 이제부터는 좀 달라질 것입니다. 여러분은 이제 본격적인 프로그래밍에 들어가실 준비가 되었습니다.

오늘 배울 내용은 "GUI 프로그래밍"에서 가장 핵심적인 개념인 "이벤트 event"에 대한 것입니다. 이미 윈도그 프로그래밍 같은 것을 해본 적이 있는 분들은, 그리고 특히 옛날 도그 시절부터 C 로 프로그래밍을 해보신 분들은 지금부터 말씀드리는 몇 가지 얘기에 대하여 금방 이해하실 수 있을 겁니다. 전혀 모르시는 초보분의 경우에는 일단 모든 것을 이해할 수는 없으므로 단지 윈도우 프로그래밍은 모두들 "이벤트" 처리 방식의 독특한 프로그래밍 스타일을 갖는다는 문구 하나만 기억해 주시기 바랍니다.

7.1 이벤트 처리 방식과 순차적인 처리 방식 차이점

우선 순차적인 처리 방식에 대해서 알아보도록 하겠습니다.

C 를 보통 절차언어라고 부릅니다. 모든 일에는 순서가 있다! 가장 인간적인 사고 방식을 모델로 하고 있고 결국 어떤 언어든 간에 인간의 논리는 항상 원인 --< 결과의 순서를 가지고 생각하는 방식의 변화, 철학의 변화없이는 앞으로도 항상 남아있을 것입니다. 여러분이 지금 하고 있는 모든 일이 다 절차를 갖습니다. 그리고 그것은 항상 모든 프로그래밍의 기본이며 나중에 이벤트 처리 방식이라 할 지라도 스케일을 작게 보면 결국에는 자그마한 순차 처리 방식들의 결합이라고 생각할 수도 있습니다.

보통 셸 스크립트와 같이 단 한 번의 입력을 받고 단 한 번의 처리로 끝나는 프로그램들이 전형적인 예입니다.

그에 반해서 이벤트 처리 방식은 "수많은 버튼, 언제 클릭할 지 모르는 변덕장이 마우스"라는 것을 특징으로 합니다. 윈도우 시스템이 되면서부터 프로그래머들은 정말로 많은 고민을 하기 시작했지요. 도대체 순서라는 것을 발견하기가 힘들다는 것이 문제입니다. 어떤 버튼을 누른 다음 어떤 버튼을 누를 것인지는 예측할 수가 없 습니다. 순진한 사용자는 아무렇게 마음내키는 대로 버튼을 눌러댑니다. 도대체 마우스가 어디로 움직일 지도 예견할 수가 없습니다.

일이 이러하기 때문에 X 윈도우 프로그래밍 방식은 아주 판이하게 다른 어떤 일정 한 패턴을 갖습니다. 사실 나중에 보시면 아시겠지만 이벤트 처리방식이라는 것도 아주 쉽습니다. 그리고 대형 프로그램을 만들 때 좋지요. 그리고 이벤트 처리방식은 그 무엇보다도 C++ 과 같은 오브젝트 오리엔티디드 프로그래밍 언어와 제일 잘 맞습니다. 하지만 X 윈도우는 아직 C 프로그래밍 언어로 되어 있습니다. 하지만 C++ 로 짜도 상관없고 어떻게 보면 C++ 로 짜는 것이 더욱 알맞는지도 모릅니다. 실제 X 프로그래밍을 하다보면 OOP 비슷한 스타일을 많이 사용하니까요.

이벤트 처리 방식의 프로그래밍은 보통 이런 식으로 생각합니다.

버튼을 몇 개 만든다. 각 버튼이 눌려지면 수행될 독특한 함수들을 지정해준다. 그리고 나서 우리는 버튼이 눌려지기만을 기다린다. 그리고 버튼이 눌려질 때마다 예를 들어 1 번 버튼은 원을, 2 번 버튼은 사각형을, 그리고 3 번 버튼은 직선을 그리도록 한다. 즉 어떤 객체를 먼저 만들고 나서 그것에다가 특정 기능을 부여하는 방식입니다. 이런 프로그래밍 방식이기 때문에 비주얼 C 니 비주얼 베이식이니 하는 것이 가능한 것입니다. 기억하십시요. 먼저 어떤 버튼을 만듭니다. 그리고 그 버튼에다가 원하는 기능을 하나씩 부여합니다. 눌려지면 실행을 합니다. 예를 들어 "Exit" 버튼을 만들면 우리는 프로그램을 종료하면 됩니다.

7.2 복잡한 일은 X 서버가 처리합니다. 우리는 단지...

자신이 프로그래밍하는 플랫포옴을 믿는 일은 참으로 중요합니다. 윈도그95 프로그래머들은 윈도그95를 믿고, X 프로그래머들은 X 윈도우를 믿습니다. 전자는 M$가 세계 시장에서 이긴다는 것을 믿고 후자은 X 윈도우 그 자체의 능력과 가능성을 믿습니다. 윈도그95 자체를 믿지는 않겠지요? :)

X 윈도우가 뜨지 마자 정신없이 돌아다니는 마우스, 또는 언제 타이핑될 지 모르는 키보드, 이 모든 것에 대해서 여러분은 걱정할 필요가 없습니다. 마우스가 어디로 움 직이고 있는지 그리고 키보드 입력을 받으면 어떻게 할 것인지에 대해서는 X 서버가 담당합니다. 그는 마치 여러분에게 카드 패를 돌리는 딜러와도 같습니다. 여러분이 만든 프로그램은 함께 실행되고 있는 그 많은 창들과 공존합니다. 도대체 어느 창에서 마우스 클릭이 벌어졌는지에 대해서 걱정할 필요없습니다. 여러분의 창에서 클릭이 이루어지면 X 서버가 알아서 여러분의 프로그램에게 "이벤트"를 보냅니다.

심지어 나는 키보드 입력은 필요없다. 단지 마우스만으로 작동하는 프로그램이므로 "키보드 입력이라는 이벤트"는 통보하지 말아달라! 고 요청하면 X 서버는 알아서 키보드 입력은 여러분에게 주지 않습니다.

여러분은 while 문장 하나를 가지고 무한루프를 돌면서 만약 이벤트가 생긴다면 그것이 마우스 클릭인지 아니면 키보드 입력인지, 마우스 클릭이면 몇 번째 마우스 버튼인지 몇 개를 동시에 눌렀는지 등을 확인하고 알맞는 행동을 취해주면 됩니다. 개념상 프로그래밍은 쉽습니다. 여러분 프로그램의 성공은 오로지 여러분의 창조력에 달린 것이지 사실 윈도그95인지 X 윈도우인지는 상관없습니다. 심지어 꼭 X 윈도우 프로그래밍을 C 언어로 하란 법도 없습니다. Tcl/Tk 와 같은 멋진 스크립팅 언어들도 많고 그 강력함을 입증받고 있습니다. 단지 C 프로그래밍 언어로 X 윈도우 프로그래밍을 한다는 것은 여러분에게 아주 탄탄한 기초를 마련해주고 다른 어떤 프로그래밍도 쉽게 할 수 있는 자신감을 준다는 것을 말씀드리고 싶습니다.

7.3 일반적인 코드 형태를 보고 눈에 익힙시다.

이 소스는 완벽한 소스가 아니라 그냥 개념적인 소스일 뿐입니다.

 Display display;

 XEvent xe;                             /* 이벤트 처리 구조체 */
 Window window1;


 /* X 서버랑 접속을 합니다. XOpenDisplay 함수 사용 */

 /* 간단한 창 하나를 만들어둡니다. */

 /* 원하는 이벤트만을 서버에게 등록합니다 */
 XSelectInput( display, window1, StructureNotifyMask | ExposureMask 
                                | ButtonPressMask );

 XMapWindow( display, window1 );

 /* 여기서부터 본격적인 프로그램의 로직이 전개됩니다 */

 while(1)       /* WHILE 문의 시작 */
 {
        /* 이벤트가 생기기를 기다리고 있습니다 */
        XNextEvent( display, &xe );

        /* 이벤트가 생기면 다음 줄을 실행합니다. 이벤트가 어떤 종류의 이벤
           트인지를 식별해내고 그에 알맞는 행동을 합니다                   */

        switch ( xe.type )      {

            case Expose:
                 printf("Expose 이벤트 발생\n");
                 break;

            case ButtonPress:
                 printf("버튼이 눌려졌습니다.\n");
                 break;

            case ConfigureNotify:
                 printf("창의 상태가 바뀌었습니다.\n");
                 break

            case ClientMessage:
                 printf("다른 클라이언트가 메세지를 보내왔습니다\n");
                 break;

            default:
                 printf("발생한 이벤트를 무시합니다\n");
                 break;
        }
 }              /* WHILE 문의 끝 */

위에서 보여드린 패턴을 머리 속에 항상 있어야 합니다. 버스를 타고 가면서도 그리고 화장실에서 볼 일을 볼 때도 음악을 듣고 있어도 여자 친구를 만나서 두 눈을 바라보고 있으면서도... 잠결에도...

무엇이든 전형적인 패턴이라는 것이 있습니다. 그러한 패턴을 눈치채시는 것이 가장 중요합니다. 그럼 이제 차근차근 알아가보도록 합시다.

7.4 패턴 분석

제일 먼저 눈에 띄는 것은 바로 XEvent 라는 새로운 자료형입니다. C 프로그램이든 뭐든 간에 프로그래밍의 기본 요소는 "자료형과 알고리즘"입니다. 후자는 여러분의 몫이라 치고 일단 자기가 사용하는 자료형에 대해서 익숙해져 있지 않거나 대충 알고 넘어가게 된다면 프로그래밍의 반에 익숙하지 않고 반을 대충 해결한 것과 동일 합니다. 자료형에 대해서 즉시 알아보도록 합시다.

/*
 * this union is defined so Xlib can always use the same sized
 * event structure internally, to avoid memory fragmentation.
 */
typedef union _XEvent {
        int type;               /* must not be changed; first element */
        XAnyEvent xany;
        XKeyEvent xkey;
        XButtonEvent xbutton;
        XMotionEvent xmotion;
        XCrossingEvent xcrossing;
        XFocusChangeEvent xfocus;
        XExposeEvent xexpose;
        XGraphicsExposeEvent xgraphicsexpose;
        XNoExposeEvent xnoexpose;
        XVisibilityEvent xvisibility;
        XCreateWindowEvent xcreatewindow;
        XDestroyWindowEvent xdestroywindow;
        XUnmapEvent xunmap;
        XMapEvent xmap;
        XMapRequestEvent xmaprequest;
        XReparentEvent xreparent;
        XConfigureEvent xconfigure;
        XGravityEvent xgravity;
        XResizeRequestEvent xresizerequest;
        XConfigureRequestEvent xconfigurerequest;
        XCirculateEvent xcirculate;
        XCirculateRequestEvent xcirculaterequest;
        XPropertyEvent xproperty;
        XSelectionClearEvent xselectionclear;
        XSelectionRequestEvent xselectionrequest;
        XSelectionEvent xselection;
        XColormapEvent xcolormap;
        XClientMessageEvent xclient;
        XMappingEvent xmapping;
        XErrorEvent xerror;
        XKeymapEvent xkeymap;
        long pad[24];
} XEvent;
XEvent 는 <X11/Xlib.h>에 정의되어 있습니다. 지금 당장 Xlib.h 헤더화일을 편집기로 열어서 내용을 확인하십시요. 헤더 화일의 내용은 언젠가는 한 번 몽땅 읽어봐야 할 때가 오겠지요?

보시다시피 XEvent 는 공용체( 유니언 union )입니다. 공용체가 무엇인지 모르신다면 일단 X 윈도우 프로그래밍은 접어두시고 지금 당장 가장 쉬운 C 프로그래밍 책에서 공용체에 대해서 공부하시기 바랍니다.

XEvent 공용체의 구조는 전체적으로 다음과 같습니다.

typedef union _XEvent {
        int type;               /* 꼭! 첫번째 멤버여야 한다. */
        ...
        X*Event <변수명>
        ...
        long pad[24];
} XEvent;

공용체의 크기는 공용체의 멤버 중 가장 큰 자료형의 크기와 같습니다. 마지막 멤버를 보면 XEvent 는 long 형 자료 24 개 정도의 크기를 갖는 것을 알 수 있습니다. 그리고 아마도 제일 작은 자료형은 int type 이겠죠?

중간에 무수히 많이 들어가 있는 X*Event 자료형을 한 번 봅시다. XAnyEvent, XKeyEvent, XButtonEvent, XMotionEvent, XExposeEvent 등등의 X 로 시작하고 Event로 끝나는 이름의 구조체가 있습니다. 그 구조체 자료형들 또한 모두 Xlib.h 에 정의되어 있습니다. 한 번 살펴보죠.

typedef struct {
        int type;             /* <-- 주목! */
        unsigned long serial; /* # of last request processed by server */
        Bool send_event;      /* true if this came from a SendEvent request */
        Display *display;/* Display the event was read from */
        Window window;  /* window on which event was requested in event mask */
} XAnyEvent;

typedef struct {
        int type;             /* <-- 주목! */
        unsigned long serial; /* # of last request processed by server */
        Bool send_event;      /* true if this came from a SendEvent request */
        Display *display;     /* Display the event was read from */
        Window window;        /* "event" window it is reported relative to */
        Window root;          /* root window that the event occured on */
        Window subwindow;     /* child window */
        Time time;            /* milliseconds */
        int x, y;             /* pointer x, y coordinates in event window */
        int x_root, y_root;   /* coordinates relative to root */
        unsigned int state;   /* key or button mask */
        unsigned int keycode; /* detail */
        Bool same_screen;     /* same screen flag */
} XKeyEvent;

typedef struct {
        int type;             /* <-- 주목! */ 
        unsigned long serial; /* # of last request processed by server */
        Bool send_event;      /* true if this came from a SendEvent request */
        Display *display;     /* Display the event was read from */
        Window window;        /* "event" window it is reported relative to */
        Window root;          /* root window that the event occured on */
        Window subwindow;     /* child window */
        Time time;            /* milliseconds */
        int x, y;             /* pointer x, y coordinates in event window */
        int x_root, y_root;   /* coordinates relative to root */
        unsigned int state;   /* key or button mask */
        unsigned int button;  /* detail */
        Bool same_screen;     /* same screen flag */
} XButtonEvent;

typedef struct {
        int type;             /* <-- 주목! */
        unsigned long serial; /* # of last request processed by server */
        Bool send_event;      /* true if this came from a SendEvent request */
        Display *display;     /* Display the event was read from */
        Window window;        /* "event" window reported relative to */
        Window root;          /* root window that the event occured on */
        Window subwindow;     /* child window */
        Time time;            /* milliseconds */
        int x, y;             /* pointer x, y coordinates in event window */
        int x_root, y_root;   /* coordinates relative to root */
        unsigned int state;   /* key or button mask */
        char is_hint;         /* detail */
        Bool same_screen;     /* same screen flag */
} XMotionEvent;

typedef struct {
        int type;             /* <-- 주목! */
        unsigned long serial; /* # of last request processed by server */
        Bool send_event;      /* true if this came from a SendEvent request */
        Display *display;     /* Display the event was read from */
        Window window;
        int x, y;
        int width, height;
        int count;            /* if non-zero, at least this many more */
} XExposeEvent;

여기서는 몇 개의 구조체만을 예로 들었을 뿐입니다. 옆에 약간의 설명이 들어있으니 그것을 참고하시면 됩니다. 모두들 구조체인데 구조체 멤버들을 서로 비교해가면서 보십시요. 여기서 여러분 스스로 관찰을 해내면 X 프로그래밍은 재미있습니다.

XAnyEvent 의 내용은 한 번 머리 속에 넣고 다른 X*Event 구조체 멤버들을 살펴보십시요. 그러면 여지없이 모든 구조체에는 XAnyEvent 의 내용이 모두 들어 있다는 것을 알 수 있습니다.

7.5 이벤트 자료형 사용법

모든 이벤트 구조체들을 대표하고 있는 공용체 XEvent 를 프로그램에서 일단 사용합니다. X 서버가 우리들 프로그램에게 어떤 이벤트가 발생했다고 하면 바로 XEvent를 보내줍니다. 그러면 우리는 우선! XEvent 의 가장 기초적인 자료형인 int type의 내용을 살펴봅니다. 그 type 값을 가지고 이 이벤트가 과연 마우스과 관련된 것인지 아니면 키보드와 관련된 것인지 등을 판별합니다. 판별이 되면 그에 알맞는 자료형 즉 XEvent의 공용체 멤버들을 적절하게 사용합니다. 마우스와 관련되면 XEvent 에서 마우스와 관련된 멤버인 XButtonEvent, XMotionEvent 같은 것을 이용하여 이벤트를 해석합니다. 키보드에 관련되어 있다면 이번에는 그 자료형들 대신에 XKeyEvent 를 사용합니다.

각 구조체는 또 다시 자기에게 알맞는 구조체 멤버들을 가지고 있습니다. 그것을 보고 해석하면 됩니다. 만약 마우스에 관련되었다면 unsigned int button 멤버를 보고 어떤 버튼인지를 해석합니다. 만약 키보드에 관련되었다면 unsigned int keycode 를 보고 해석합니다.

그러한 과정을 바로 switch ( xe.type ) 문장에서 하고 있는 것입니다. case 문에 나타난 Expose, ButtonPress 등은 매크로입니다. X.h 에 정의되어 있습니다.

        #define KeyPress                2
        #define KeyRelease              3
        #define ButtonPress             4
        #define ButtonRelease           5
        #define MotionNotify            6
        #define EnterNotify             7
        #define LeaveNotify             8
        #define FocusIn                 9
        #define FocusOut                10
        #define KeymapNotify            11
        #define Expose                  12
        #define GraphicsExpose          13
        #define NoExpose                14
        #define VisibilityNotify        15

7.6 창마다 자기가 원하는 이벤트만을 예약하기

예제 소스를 보시면 다음과 같은 부분이 있습니다.

  XSelectInput( display, window1, StructureNotifyMask | ExposureMask 
                                  | ButtonPressMask );

  XMapWindow( display, window1 );

XSelectInput이라는 함수가 보이는군요. 새로운 함수니까 잠시 눈에 익혀두어야겠죠? man 명령을 떠올리셔야 합니다. man XSelectInput

 NAME
       XSelectInput - 이벤트 입력을 선택합니다.

 SYNTAX
       XSelectInput(display, w, event_mask)
             Display *display;
             Window w;
             long event_mask;

 ARGUMENTS
       display    X 서버와의 접속 ID
       event_mask 이벤트 매스크
       w          이벤트 선택을 적용할 창 ID

매스크(Mask)라고 하는 기법 또한 아주 중요한 C 프로그래밍 실전 기법입니다. 모르시는 분은 당장 C 프로그래밍 책을 보십시요. 제가 생각엔 원판 커니건 & 리치의 아주 얇지만 그 어떤 C 프로그래밍 해설책보다도 뛰어난 "C 프로그래밍 랭귀지" 라는 책을 권합니다. 항상 가지고 다니기에 참 좋습니다. 쓸데 없이 쉽게 쓰지도 어렵게 쓰지도 않은 명저이지요.

위에서보면 우리는 각 창마다 XSelectInput 이라는 함수를 통해서 자기가 관심갖는 이벤트만을 등록할 수 있다는 것을 알 수 있습니다. 예를 들어 OK 나 CANCEL 버튼을 표시하는 창의 입장에서 키보드 입력이나 마우스의 이동 같은 것은 전혀 필요없겠지요? 버튼이 눌렸나 안눌렸나만 확인하면 됩니다.

 StructureNotifyMask | ExposureMask | ButtonPressMask

비트연산 OR 또한 아주 많이 접하는 기술입니다.

 #define NoEventMask                     0L
 #define KeyPressMask                    (1L<<0)  
 #define KeyReleaseMask                  (1L<<1)  
 #define ButtonPressMask                 (1L<<2)  
 #define ButtonReleaseMask               (1L<<3)  
 #define EnterWindowMask                 (1L<<4)  
 #define LeaveWindowMask                 (1L<<5)  
 #define PointerMotionMask               (1L<<6)  
 #define PointerMotionHintMask           (1L<<7)  
 #define Button1MotionMask               (1L<<8)  
 #define Button2MotionMask               (1L<<9)  
 #define Button3MotionMask               (1L<<10) 
 #define Button4MotionMask               (1L<<11) 
 #define Button5MotionMask               (1L<<12) 
 #define ButtonMotionMask                (1L<<13) 

여러분은 (1L>>2) 라는 표현에 익숙해지셔야 합니다. 앞으로도 자주 나오고 기본적인 실전 테크닉이니까요.

그럼 간단한 예를 들어볼까요?

간단한 예

간단한 버튼을 하나 만들어서 클릭하면 Hello, Linuxers 라는 문자열을 출력한 후 종료하는 예를 보여드리겠습니다.

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>

int main( int argc, char* argv[] )
{
    Display *dpy;
    Window  w;
    Font    f;
    GC      gc;
    XEvent  xe;

    dpy = XOpenDisplay( NULL);
    w   = XCreateSimpleWindow( dpy, RootWindow( dpy, 0 ), 50, 50,
                                    100, 50, 5, BlackPixel( dpy, 0 ),
                                    WhitePixel( dpy, 0 ) );
    gc = XCreateGC( dpy, w, 0L, ( XGCValues * ) NULL );
    f  = XLoadFont( dpy, "fixed" );
    XSetFont( dpy, gc, f );
    XSetLineAttributes( dpy, gc, 5, LineSolid, 
                         CapRound, JoinRound );

    XSelectInput( dpy, w, ExposureMask | ButtonPressMask | EnterWindowMask 
                           | LeaveWindowMask );

    XMapWindow( dpy, w );

    XFlush( dpy );

    while ( True )
    {
            XNextEvent( dpy, &xe );

            switch ( xe.type )      {
            case Expose:
                printf("Expose 이벤트가 발생하였습니다.\n");
                XSetForeground( dpy, gc, BlackPixel( dpy, 0 ) )
                XDrawString( dpy, w, gc, 40, 30, "O K", 3 );
                break;
            case ButtonPress:
                printf("버튼을 누르셨습니다!\n");
                exit(1);
                break;
            case EnterNotify:
                printf("마우스가 창 안으로 들어왔습니다.\n");
                XSetForeground( dpy, gc, BlackPixel( dpy, 0 ) );
                XDrawRectangle( dpy, w, gc, 10, 10, 80, 30 );
                break;
            case LeaveNotify:
                printf("마우스가 창 밖으로 나갔습니다.\n");
                XSetForeground( dpy, gc, WhitePixel( dpy, 0 ) );
                XDrawRectangle( dpy, w, gc, 10, 10, 80, 30 );
                break;
            default:
                printf("모르는 이벤트입니다. 무시합니다.\n");
                break;
            }
    }
        return 0;
}

위 예제를 event.example.c 라는 이름으로 저장하고 다음과 같이 해줍니다.

 gcc -o event.example event.example.c -lX11 -L/usr/X11/lib

실행은 한텀 창과 같이 한글이 출력될 수 있는 곳에서 실행해주십시요.

 ./event.example

예제에 대한 설명

XSelectInput 함수가 나오기 전까지 나오는 모든 것들은 이미 우리가 잘 알고 있는 내용입니다. 서버와 접속을 하고 창을 하나 만듭니다. 폰트 출력과 사각형 그리기를 할 것이므로 GC 를 만들어두는 것도 잊지 않습니다. 시스템의 기본인 fixed 폰트를 로딩하고 GC 세팅도 그럭저럭 해둡니다.

  1. 창을 MAP하기 전에 우선 원하는 이벤트를 골라둔다. XSelectInput 함수를 통해서 우리는 원하는 창에다가 우리가 관심을 갖는 이벤트 만을 등록해둔다. 그러면 나머지 이벤트들은 그냥 무시되버리고 만다. 여기서 우리가 원하는 것은 일단 아주아주 중요한 이벤트의 하나인 Expose 이벤트, 마우스 버튼이 클릭되었는가를 알아보는 이벤트 그리고 창 속으로 마우스 포인터가 들어갔는가, 나왔는가를 알려주는 이벤트를 설정하였습니다.
      ExposureMask | ButtonPressMask | EnterWindowMask | LeaveWindowMask
    
  2. Expose 이벤트의 중요성 Expose는 영어의 뜻 그대로 "밖으로 드러나다"라는 의미입니다. 윈도우 시스템에서 고려해줘야 할 것 중 하나는 한 화면을 동시에 많은 창들이 공유하고 있다는 사실입니다. 창들이 이리저리 서로를 가리기도 하고 다시 나타나기도 하며 아이콘으로 되었다가 다시 원상태로 돌아오기도 합니다. 크기가 늘기도 하지요. 특히나 다른 창에 가렸다가 다시 나타나게 될 때는 남들에 의해서 가려졌던 부분들을 다시 그려줘야 합니다. 그런데 여러분이 Xt/모티프 같은 수준에서는 느끼지 못 하셨을지 모르겠지만 Xlib 은 저수준이므로 여러분 스스로가 남들에 의해 가려졌다가 다시 나타날 때 지워졌던 부분을 복구해주어야 합니다. 실제로 모티프 같은 것들은 이미 Xlib 의 함수를 써서 여러분 몰래 다시 그려주는 작업을 대신 해주고 있을 뿐입니다. Expose 이벤트는 여러분의 창이 맨 처음 XMapWindow 함수에 의해서 뿅! 하고 나타날 때 처음으로 발생합니다. 대부분의 프로그램들은 창이 맨 처음 뿅! 하고 나타날 때 자기 자신을 여러 모로 치장합니다. 또한 남들에 의해 가려졌다가 다시 뿅! 하고 여러분에게 나타낼 때는 적절하게 지워진 부분을 복구해주어야 합니다. 여기서는 아주 간단한 방식으로 처리했습니다. Expose 이벤트가 발생할 때마다 다시 O K 라는 글씨를 써주는 것이지요.
  3. while ( True ) 문장을 통해서 이벤트 처리 무한 루프를 돕니다. XNextEvent 함수를 사용하면 여러분이 원하는 이벤트 중 하나가 생길 때까지 기다립니다. 이벤트가 안생기면 가만히 있습니다. 그러다가 생기면 파라미터로 주어진 XEvent 공용체에다가 이벤트 정보를 적어서 보내줍니다.

  switch ( xe.type )

자, 이벤트라는 것은 종류가 많기 때문에 그것이 마우스와 관련된 것인지 키보드와 관련된 것인지를 알아야 할 필요가 있습니다. 그것에 맞게 적절하게 처리해야 하니까요.

  case Expose:

     Expose 이벤트 발생시에는 적절하게 자기 자신을 그려주거나 복구
     해주어야 합니다. XDrawString 함수로 "O K"라고 써줍니다.
     먼저 XSetForeground 함수로 전경색을 검정으로 해줍니다.

  case ButtonPress:

     버튼이 눌리면( 어떤 버튼이든 상관하지 않고 ) exit(1) 종료합니다.

  case EnterNotify:

     마우스가 창 안으로 들어가면 XDrawRectangle 함수로 사각형을 테
     두리에 그려줍니다. 여러분의 마우스가 들어왔다는 것을 확실하게
     보여주기 위해서입니다.

  case LeaveNotify:

     마우스가 나가면 나갔다는 것을 보여주기 위해 전경색을 하얀색으
     로 즉, 배경색과 같게 해서 사각형을 그려줍니다. 그러면 지워지는
     효과가 나겠지요?

  default:

     알 수 없는 이벤트는 무시합니다.

위에서 예로 든 것은 여러분이 아주 쉽게 접할 수 있는 버튼 하나를 아주 저수준에서 해결한 것입니다. 보통 버튼을 클릭하기 위하여 마우스를 가져다 대면 버튼 전체의 색상이 변한다든지 하는 것을 볼 수 있습니다. 바로 이벤트에 맞게 자기 모양을 그리면 되는 것입니다. 원리는 아주 간단하지요?

버튼 하나를 만들어 보았습니다. 기존의 라이브러리만 쓰는 것이 아니라 여러분이 저수준으로 아주 재미있는 행동을 하는 버튼을 만들 수도 있습니다. 다른 사람은 흉내낼 수 없는 재미있는 일을 하는 것, 이것은 저수준 함수 Xlib 을 배우지 않고 는 이룰 수 없는 일입니다.


Previous Next Table of Contents