1.1. 시작하기전에

저도 이제 X window관련 프로그래밍을 공부하려고 합니다. 후배들에게 조금이나마 도움이 될까 해서 공부하면서 얻은 여러정보를 공유해볼까 합니다.

X window 환경에서 개발하는 방법에는 Xlib, Gtk+, QT, ... 등의 개발선택권이 주어집니다. 여기서 다루는 내용은 Xlib에 국한되어 범위를 한정하겠습니다.
이미지 출처 : http://en.wikipedia.org/wiki/Xlib
[PNG image (4.2 KB)]


본 문서는 [http]X11R7.6[] 을 기준으로 작성되었으나 일부 패키지는 기준버젼이 다를 수 있습니다.

1.2. X window에서 프로그래밍을 한다는 것은?

X window는 기본적으로 사용자와 직.간접적으로 소통하기 위한 요소들중에 "화면의 영역 (window)", "그리기 요소: 점(pixel)/선(line)/면(rectangle)/원(circle)/스케일(scale)/회전(rotate)...", "색상 (color)", "문자를 나타내기 위한 폰트 (font)", "입력: 키보드/마우스/터치스크린/..." 등의 관련된 자원(resource)들을 서버(server)라는 것을 통해서 집중하여 관리합니다. 우리는 이것을 클라이언트(client)의 입장에서 서버(server)와 통신하여 자원을 얻어 이용하는 일을 작성하게 될겁니다.

즉, X window programming이란?
"누가(client) 어디로(server) 접속하여 자원(resource)을 이용하도록 구현한다"
라고 정의되는 것이 X window 에서 프로그래밍을 한다는 것의 기본 정의가 될것 같습니다.

1.3. X window개발 환경 준비

Xlib는 상당히 많은 소스들로 구성되어 있습니다. 우리는 기본적으로 일반 배포판에서 libX11 또는 Xlib 와 비슷한 이름의 패키지를 설치했거나 설치할수 있을겁니다. 또한 개발요소에 따라서 추가적으로 수많은 패키지들을 설치해야 할겁니다.

  • 우분투(Ubuntu) 리눅스 배포판의 경우
    $ sudo apr-get install build-essential
    $ sudo apr-get install libX11-dev
    


  • 젠투(Gentoo) 리눅스 배포판의 경우
    $ emerge libX11
    


  • Xlib개발환경의 설치를 배포판에서 간단하게 지원하는 경우가 거의 대부분이라서 크게 개발환경 구축이 어렵지는 않을겁니다.

    하지만 저는 이러한 배포판에 의존하여 개발하는 방법보다는 근본적으로 필요한 패키지들을 직접 이해하면서 빌드해서 설치해보고자 합니다. 물론 이런 사항이 반드시 이해가 필요한것은 아니지만 어느정도 빌드관계를 이해하는데 도움이 될수 있다고 생각합니다. 여기서는 최소한의 개발환경을 빌드하는것으로 소개를 마치겠지만 실제로 보다 많은 의존패키지들이 존재합니다. 그 많은 패키지들을 직접 빌드하는게 쉽지 않으며 저 역시 권해드리지 않습니다. 다만 임베디드(Embedded)개발환경을 구축하려고 하시는 분들은 많지 않겠지만 직접 빌드해야만 할겁니다. 조금이나마 그러한 분들께 도움이 되었으면 하는 마음에서 살짝 정리합니다.

1.3.1. Xorg libraries

X window의 client에 해당하는 library를 빌드해보기 위해서는 우선 빌드의존관계를 따져보고 빌드순서를 정해야 겠지요?
[xorg-utils] (http://www.freedesktop.org/wiki/Software/pkg-config, http://www.x.org/releases/X11R7.6/src/util/)
    autoconf (v2.59 이상)
    automake (v1.9.x)
    libtool (v1.5)
    pkg-config (v0.9.0 이상)
    util-macros

[xorg-protocol-headers] (http://www.x.org/releases/X11R7.6/src/proto/)
    bigreqsproto
    compositeproto
    damageproto
    dmxproto
    dri2proto
    fixesproto
    fontsproto
    glproto
    inputproto
    kbproto
    printproto
    randrproto
    recordproto
    renderproto
    resourceproto
    scrnsaverproto
    videoproto
    xcmiscproto
    xextproto
    xf86bigfontproto
    xf86dgaproto
    xf86driproto
    xf86vidmodeproto
    xineramaproto
    xproto

[xorg-libraries] (http://www.x.org/releases/X11R7.6/src/lib/, http://www.x.org/releases/X11R7.6/src/xcb/, ...)
    libXdmcp
    libXau
    libpthread-stubs
    libxml2
    libxslt
    xcb-proto
    libxcb
    xtrans
    libX11
    libXext
    libFS
    libICE
    libSM
    libXScrnSaver
    libXt
    libXmu
    libXp
    libXpm
    libXaw
    libXfixes
    libXcomposite
    libXrender
    libXcursor
    libXdamage
    zlib
    libfontenc
    bzip2
    freetype
    libXfont
    expat
    fontconfig
    libXft
    libXi
    libXinerama
    libXrandr
    libXres
    libXtst
    libXv
    libXvMC
    libXxf86dga
    libXxf86vm
    libdmx
    libpciaccess
    libxkbfile


관련된 소스파일들은 모두 아래의 URL을 통해서 다운로드 받으실수 있을겁니다. 소스들이 너무 많아서 사실상 완벽하게 정리하기는 쉽지 않네요. 어쨋건 위의 의존관계에 명시된 모든 패키지들을 싹싹 뒤져서 다운로드 받아야 합니다.

1.3.2. Xorg server

TODO: 언젠가 정리할 수 있을까요???
[xorg-libraries]
    ...

[Direct Rendering Infrastructure]
    libdrm
    MesaLib : needed some host util (flex, bison, makedepend)

[xorg-server]
    xorg-server


1.3.3. 예제

  • 불타오르는 화면 (필요한 라이브러리 : libX11, libXrender)
    /*
        Copyright (C) HWPORT.COM
        All rights reserved.
        Author: JAEHYUK CHO <mailto:minzkn@minzkn.com>
    */
    
    #if !defined(__def_hwport_source_template_main_c__)
    # define __def_hwport_source_template_main_c__ "template_main.c"
    
    #if !defined(_ISOC99_SOURCE)
    # define _ISOC99_SOURCE (1L)
    #endif
    
    #if !defined(_GNU_SOURCE)
    # define _GNU_SOURCE (1L)
    #endif
    
    #include <sys/types.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    
    #include <X11/extensions/Xrender.h>
    
    #include <X11/Xos.h>
    
    #define def_xfire_draw_method (1)
    
    typedef enum {
        xfire_fire0_window = 0,
        xfire_fire1_window,
        xfire_fire2_window,
        xfire_fire3_window,
        xfire_max_window
    }__xfire_window_t;
    #define xfire_window_t __xfire_window_t
    #define def_xfire_window_names {"root", "background", (const char *)0}
    
    typedef struct {
        Window m_root_window;
        int m_x, m_y;
        unsigned int m_width, m_height, m_border_width, m_depth;
    }__xfire_geometry_t;
    #define xfire_geometry_t __xfire_geometry_t
    
    typedef struct {
        const char *m_display_name;
        Display *m_display;
        Screen *m_screen;
    
        int m_visualinfo_count, m_select_visualinfo;
        XVisualInfo *m_visualinfo;
    
        long m_event_mask;
    
        Colormap m_colormap;
        XSetWindowAttributes m_window_attributes[xfire_max_window];
        Window m_window[xfire_max_window];
        xfire_geometry_t m_geometry[xfire_max_window];
        GC m_gc[xfire_max_window];
        XImage *m_ximage[xfire_max_window];
    
        XEvent m_event;
    
    #if def_xfire_draw_method == (1)
        /* fire info */
        unsigned char *m_fire_map[xfire_max_window];
        unsigned int m_fire_color_table[xfire_max_window][256];
    #endif
    }__xfire_t;
    #define xfire_t __xfire_t
    
    static int xfire_select_visual(xfire_t *s_xfire);
    
    int main(int s_argc, char **s_argv);
    
    static int xfire_select_visual(xfire_t *s_xfire)
    {
        int s_event_base, s_error_base;
    
        XVisualInfo s_template_visualinfo;
        XRenderPictFormat *s_format;
    
        if(XRenderQueryExtension(s_xfire->m_display, &s_event_base, &s_error_base) == 0) {
            (void)fprintf(stderr, "XRenderQueryExtension failed !\n");
            return(-1);
        }
    
        (void)memset((void *)(&s_template_visualinfo), 0, sizeof(s_template_visualinfo));
        s_template_visualinfo.screen = XScreenNumberOfScreen(s_xfire->m_screen);
        s_template_visualinfo.depth = 32;
    #if defined(__cplusplus)
        s_template_visualinfo.c_class = TrueColor;
    #else
        s_template_visualinfo.class = TrueColor;
    #endif
    
        s_xfire->m_visualinfo = XGetVisualInfo(
            s_xfire->m_display,
            VisualScreenMask | VisualDepthMask | VisualClassMask,
            (XVisualInfo *)(&s_template_visualinfo),
            (int *)(&s_xfire->m_visualinfo_count)
        );
        if(s_xfire->m_visualinfo == ((XVisualInfo *)0)) {
            (void)fprintf(stderr, "XGetVisualInfo failed !\n");
            return(-1);
        }
    
        for(s_xfire->m_select_visualinfo = 0;s_xfire->m_select_visualinfo < s_xfire->m_visualinfo_count;s_xfire->m_select_visualinfo++) {
            s_format = XRenderFindVisualFormat(s_xfire->m_display, s_xfire->m_visualinfo[s_xfire->m_select_visualinfo].visual);
            if(s_format == ((XRenderPictFormat *)0)) { continue; }
    #if 1L /* ARGB (alpha supported visual) */
            if((s_format->type == PictTypeDirect) && (s_format->direct.alphaMask != 0)) { break; }
    #else /* RGB */
            if(s_format->type == PictTypeDirect) { break; }
    #endif
        }
        if(s_xfire->m_select_visualinfo >= s_xfire->m_visualinfo_count) {
            (void)fprintf(stderr, "not found visual !\n");
            return(-1);
        }
    
        return(0);
    }
    
    int main(int s_argc, char **s_argv)
    {
        xfire_t s_xfire_local, *s_xfire;
        int s_window_index;
        int s_is_break, s_tick;
    
        (void)fprintf(stdout, "Initiailizing...\n");
    
        s_xfire = (xfire_t *)memset((void *)(&s_xfire_local), 0, sizeof(s_xfire_local));
    
        if(s_argc >= 2) { s_xfire->m_display_name = (const char *)s_argv[1]; }
        else {
            s_xfire->m_display_name = (const char *)getenv("DISPLAY");
            if(s_xfire->m_display_name == ((const char *)0)) {
                static const char cg_default_display_name[] = {":0"};
                s_xfire->m_display_name = (const char *)(&cg_default_display_name[0]);
            }
        }
        (void)fprintf(stdout, "display name is \"%s\"\n", s_xfire->m_display_name);
    
        s_xfire->m_display = XOpenDisplay(s_xfire->m_display_name);
        if(s_xfire->m_display == ((Display *)0)) { (void)fprintf(stderr, "XOpenDisplay failed !\n"); goto l_end; }
    
        s_xfire->m_screen = XDefaultScreenOfDisplay(s_xfire->m_display);
        if(s_xfire->m_screen == ((Screen *)0)) { (void)fprintf(stderr, "XDefaultScreenOfDisplay failed !\n"); goto l_close_display; }
    
        if(xfire_select_visual(s_xfire) == (-1)) { (void)fprintf(stderr, "xfire_select_visual failed !\n"); goto l_close_display; }
    
        s_xfire->m_event_mask = NoEventMask | ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | SubstructureNotifyMask;
    
        s_xfire->m_colormap = XCreateColormap(s_xfire->m_display, XDefaultRootWindow(s_xfire->m_display), s_xfire->m_visualinfo[s_xfire->m_select_visualinfo].visual, AllocNone);
        for(s_window_index = 0;s_window_index < xfire_max_window;s_window_index++) {
            s_xfire->m_window_attributes[s_window_index].background_pixmap = None;
            s_xfire->m_window_attributes[s_window_index].background_pixel = 0xe0000000 | 0x00000000;
            s_xfire->m_window_attributes[s_window_index].border_pixmap = CopyFromParent;
            s_xfire->m_window_attributes[s_window_index].border_pixel = 0xff000000 | 0x00ffffff;
            s_xfire->m_window_attributes[s_window_index].bit_gravity = ForgetGravity;
            s_xfire->m_window_attributes[s_window_index].win_gravity = NorthWestGravity;
            s_xfire->m_window_attributes[s_window_index].backing_store = NotUseful;
            s_xfire->m_window_attributes[s_window_index].backing_planes = 1ul;
            s_xfire->m_window_attributes[s_window_index].backing_pixel = 0xff000000 | 0x00000000;
            s_xfire->m_window_attributes[s_window_index].save_under = False;
            s_xfire->m_window_attributes[s_window_index].event_mask = s_xfire->m_event_mask;
            s_xfire->m_window_attributes[s_window_index].do_not_propagate_mask = NoEventMask;
            s_xfire->m_window_attributes[s_window_index].override_redirect = True;
            s_xfire->m_window_attributes[s_window_index].colormap = s_xfire->m_colormap;
            s_xfire->m_window_attributes[s_window_index].cursor = (Cursor)None;
            s_xfire->m_window[s_window_index] = (Window)None;
            s_xfire->m_gc[s_window_index] = (GC)None;
            s_xfire->m_ximage[s_window_index] = (XImage *)0;
    
            s_xfire->m_window[s_window_index] = XCreateWindow(
                s_xfire->m_display,
                XDefaultRootWindow(s_xfire->m_display),
                0 + (s_window_index * 100), /* x */
                0 + (s_window_index * 100), /* y */
                ((unsigned int)XWidthOfScreen(s_xfire->m_screen)) - ((xfire_max_window - 1) * 100), /* w */
                ((unsigned int)XHeightOfScreen(s_xfire->m_screen)) - ((xfire_max_window - 1) * 100), /* h */
                1, /* border_width */
                s_xfire->m_visualinfo[s_xfire->m_select_visualinfo].depth,
                InputOutput, /* class: InputOutput or InputOnly */
                s_xfire->m_visualinfo[s_xfire->m_select_visualinfo].visual,
                CWBackPixel | CWBorderPixel | CWBitGravity | CWWinGravity | CWBackingStore | CWSaveUnder | CWEventMask | CWDontPropagate | CWOverrideRedirect | CWColormap | CWCursor,
                (XSetWindowAttributes *)(&s_xfire->m_window_attributes[s_window_index])
            );
            (void)XStoreName(s_xfire->m_display, s_xfire->m_window[s_window_index], "fire");
            if(s_xfire->m_window[s_window_index] == ((Window)None)) { continue; }
    
            (void)XMapWindow(s_xfire->m_display, s_xfire->m_window[s_window_index]);
    
            (void)XGetGeometry(
                s_xfire->m_display,
                s_xfire->m_window[s_window_index],
                (Window *)(&s_xfire->m_geometry[s_window_index].m_root_window),
                (int *)(&s_xfire->m_geometry[s_window_index].m_x),
                (int *)(&s_xfire->m_geometry[s_window_index].m_y),
                (unsigned int *)(&s_xfire->m_geometry[s_window_index].m_width),
                (unsigned int *)(&s_xfire->m_geometry[s_window_index].m_height),
                (unsigned int *)(&s_xfire->m_geometry[s_window_index].m_border_width),
                (unsigned int *)(&s_xfire->m_geometry[s_window_index].m_depth)
            );
    
            s_xfire->m_ximage[s_window_index] = XGetImage(
                s_xfire->m_display,
                s_xfire->m_window[s_window_index],
                0, 0,
                s_xfire->m_geometry[s_window_index].m_width,
                s_xfire->m_geometry[s_window_index].m_height,
                XAllPlanes(),
                ZPixmap);
            if(s_xfire->m_ximage[s_window_index] == ((XImage *)0)) { (void)fprintf(stderr, "XGetImage failed ! (window_index is %d)\n", s_window_index); }
    
            s_xfire->m_gc[s_window_index] = XCreateGC(s_xfire->m_display, s_xfire->m_window[s_window_index], 0ul, (XGCValues *)0);
    
    #if def_xfire_draw_method == (1)
            s_xfire->m_fire_map[s_window_index] = (unsigned char *)malloc(((size_t)s_xfire->m_ximage[s_window_index]->width) * ((size_t)s_xfire->m_ximage[s_window_index]->height) * sizeof(unsigned char));
            if(s_xfire->m_fire_map[s_window_index] != ((unsigned char *)0)) {
                unsigned int s_color_index;
                unsigned int s_level, s_max_level, s_min_level;
    
                (void)memset((void *)s_xfire->m_fire_map[s_window_index], 0, ((size_t)s_xfire->m_ximage[s_window_index]->width) * ((size_t)s_xfire->m_ximage[s_window_index]->height) * sizeof(unsigned char));
    
                s_min_level = 0x00u;
                s_max_level = 0xffu;
                for(s_color_index = 0u;s_color_index < 64u;s_color_index++) {
                    s_level = ((s_color_index + 1u) << 2) - 1u;
                    s_xfire->m_fire_color_table[s_window_index][s_color_index + 0] = (s_level << 16) | (s_min_level << 8) | (s_min_level << 0);
                    s_xfire->m_fire_color_table[s_window_index][s_color_index + 64] = (s_max_level << 16) | (s_level << 8) | (s_min_level << 0);
                    s_xfire->m_fire_color_table[s_window_index][s_color_index + 128] = (s_max_level << 16) | (s_max_level << 8) | (s_level << 0);
                    s_xfire->m_fire_color_table[s_window_index][s_color_index + 192] = (s_max_level << 16) | (s_max_level << 8) | (s_max_level << 0);
                }
            }
    #endif
        }
        (void)XSync(s_xfire->m_display, False);
    
        for(s_is_break = 0, s_tick = 0;s_is_break == 0;s_tick++) {
            while(XPending(s_xfire->m_display) > 0) {
                if(XCheckMaskEvent(s_xfire->m_display, s_xfire->m_event_mask, (XEvent *)(&s_xfire->m_event)) == True) {
                    if(s_xfire->m_event.type == Expose) {
                    }
                    else if(s_xfire->m_event.type == KeyPress) {
                        (void)fprintf(stdout, "KeyPress event. (%04XH)\n", (unsigned int)s_xfire->m_event.xkey.keycode);
                    }
                    else if(s_xfire->m_event.type == KeyRelease) {
                        (void)fprintf(stdout, "KeyRelease event. (%04XH)\n", (unsigned int)s_xfire->m_event.xkey.keycode);
                    }
                    else if(s_xfire->m_event.type == ButtonPress) {
                        (void)fprintf(stdout, "ButtonPress event.\n");
                    }
                    else if(s_xfire->m_event.type == ButtonRelease) {
                        (void)fprintf(stdout, "ButtonRelease event.\n");
                        s_is_break = 1;
                    }
                    else if(s_xfire->m_event.type == EnterNotify) {
                        (void)fprintf(stdout, "EnterNotify event.\n");
                    }
                    else if(s_xfire->m_event.type == LeaveNotify) {
                        (void)fprintf(stdout, "LeaveNotify event.\n");
                    }
                    else {
                        (void)fprintf(stdout, "Ignore event. (%d)\n", (int)s_xfire->m_event.type);
                    }
                }
            }
    
    #if def_xfire_draw_method == (1)
            for(s_window_index = 0;s_window_index < xfire_max_window;s_window_index++) {
                size_t s_entry1, s_entry2;
                int s_rand_count;
                unsigned int *s_map;
    
                s_entry1 = ((size_t)s_xfire->m_ximage[s_window_index]->width) * (((size_t)s_xfire->m_ximage[s_window_index]->height) - ((size_t)1u));
                for(s_rand_count = 0;s_rand_count < ((int)(s_xfire->m_ximage[s_window_index]->width >> 3));s_rand_count++) {
                    s_xfire->m_fire_map[s_window_index][s_entry1 + (((size_t)rand()) % ((size_t)s_xfire->m_ximage[s_window_index]->width))] = (unsigned char)((rand() & 0xbf) + 0x40);
                }
    
                s_entry1 -= (size_t)2u;
                s_entry2 = s_entry1 + (((size_t)s_xfire->m_ximage[s_window_index]->width) - ((size_t)1u));
                s_map = (unsigned int *)s_xfire->m_ximage[s_window_index]->data;
                do {
                    s_xfire->m_fire_map[s_window_index][s_entry1] = (unsigned char)((
                        ((unsigned int)s_xfire->m_fire_map[s_window_index][s_entry1]) +
                        ((unsigned int)s_xfire->m_fire_map[s_window_index][s_entry2 + 0]) +
                        ((unsigned int)s_xfire->m_fire_map[s_window_index][s_entry2 + 1]) +
                        ((unsigned int)s_xfire->m_fire_map[s_window_index][s_entry2 + 2])) >> 2);
    
                    if(s_xfire->m_fire_map[s_window_index][s_entry1] > ((unsigned char)0u)) { --s_xfire->m_fire_map[s_window_index][s_entry1]; }
    
                    s_map[s_entry1] = s_xfire->m_fire_color_table[s_window_index][s_xfire->m_fire_map[s_window_index][s_entry1]] | 0x7f000000;
    
                    --s_entry1;
                    --s_entry2;
                }while(s_entry1 > ((size_t)0u));
    
                (void)XPutImage(
                    s_xfire->m_display,
                    s_xfire->m_window[s_window_index],
                    s_xfire->m_gc[s_window_index],
                    s_xfire->m_ximage[s_window_index],
                    0, 0,
                    0, 0,
                    (unsigned int)s_xfire->m_ximage[s_window_index]->width,
                    (unsigned int)s_xfire->m_ximage[s_window_index]->height
                );
            }
            (void)XSync(s_xfire->m_display, False);
    #else
            usleep(10000);
    #endif
        }
    
        for(s_window_index = 0;s_window_index < xfire_max_window;s_window_index++) {
    #if def_xfire_draw_method == (1)
            if(s_xfire->m_fire_map[s_window_index] != ((unsigned char *)0)) {
                free((void *)s_xfire->m_fire_map[s_window_index]);
            }
    #endif
    
            if(s_xfire->m_ximage[s_window_index] != ((XImage *)0)) {
                (void)XDestroyImage(s_xfire->m_ximage[s_window_index]);
            }
    
            if(s_xfire->m_gc[s_window_index] != ((GC)None)) {
                (void)XFreeGC(s_xfire->m_display, s_xfire->m_gc[s_window_index]);
            }
    
            if(s_xfire->m_window[s_window_index] != ((Window)None)) {
                (void)XDestroySubwindows(s_xfire->m_display, s_xfire->m_window[s_window_index]);
                (void)XDestroyWindow(s_xfire->m_display, s_xfire->m_window[s_window_index]);
            }
        }
    
        (void)XFreeColormap(s_xfire->m_display, s_xfire->m_colormap);
    
    l_close_display:;
        (void)XFlush(s_xfire->m_display);
        (void)XCloseDisplay(s_xfire->m_display);
    
    l_end:;
        (void)fprintf(stdout, "End.\n");
    
        return(0);
    }
    
    #endif
    
    /* vim: set expandtab: */
    /* End of source */
    



/*
[ FrontPage | PrintView | RawView | RSS ]

Copyright ⓒ MINZKN.COM
All Rights Reserved.

MINZKN
*/