[wiki:Home 대문] / [wiki:CategoryProgramming 프로그래밍] / [wiki:XWindowProgramming X window programming] ---- == [wiki:XWindowProgramming X window programming] == * 작성자 조재혁([mailto:minzkn@minzkn.com]) * 고친과정 2012년 4월 3일 : 처음씀 ||[[TableOfContents]] || === 시작하기전에 === 저도 이제 X window관련 프로그래밍을 공부하려고 합니다. 후배들에게 조금이나마 도움이 될까 해서 공부하면서 얻은 여러정보를 공유해볼까 합니다. X window 환경에서 개발하는 방법에는 Xlib, Gtk+, QT, ... 등의 개발선택권이 주어집니다. 여기서 다루는 내용은 Xlib에 국한되어 범위를 한정하겠습니다. [attachment:306px-X-client-libraries.svg.png 이미지 출처 : http://en.wikipedia.org/wiki/Xlib] 본 문서는 [^http://www.x.org/wiki/Releases/7.6 X11R7.6] 을 기준으로 작성되었으나 일부 패키지는 기준버젼이 다를 수 있습니다. === 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 에서 프로그래밍을 한다는 것의 기본 정의가 될것 같습니다. === X window개발 환경 준비 === Xlib는 상당히 많은 소스들로 구성되어 있습니다. 우리는 기본적으로 일반 배포판에서 libX11 또는 Xlib 와 비슷한 이름의 패키지를 설치했거나 설치할수 있을겁니다. 또한 개발요소에 따라서 추가적으로 수많은 패키지들을 설치해야 할겁니다. * 우분투(Ubuntu) 리눅스 배포판의 경우 {{{ $ sudo apr-get install build-essential $ sudo apr-get install libX11-dev }}} * 젠투(Gentoo) 리눅스 배포판의 경우 {{{ $ emerge libX11 }}} Xlib개발환경의 설치를 배포판에서 간단하게 지원하는 경우가 거의 대부분이라서 크게 개발환경 구축이 어렵지는 않을겁니다. 하지만 저는 이러한 배포판에 의존하여 개발하는 방법보다는 근본적으로 필요한 패키지들을 직접 이해하면서 빌드해서 설치해보고자 합니다. 물론 이런 사항이 반드시 이해가 필요한것은 아니지만 어느정도 빌드관계를 이해하는데 도움이 될수 있다고 생각합니다. 여기서는 최소한의 개발환경을 빌드하는것으로 소개를 마치겠지만 실제로 보다 많은 의존패키지들이 존재합니다. 그 많은 패키지들을 직접 빌드하는게 쉽지 않으며 저 역시 권해드리지 않습니다. 다만 임베디드(Embedded)개발환경을 구축하려고 하시는 분들은 많지 않겠지만 직접 빌드해야만 할겁니다. 조금이나마 그러한 분들께 도움이 되었으면 하는 마음에서 살짝 정리합니다. ==== 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을 통해서 다운로드 받으실수 있을겁니다. 소스들이 너무 많아서 사실상 완벽하게 정리하기는 쉽지 않네요. 어쨋건 위의 의존관계에 명시된 모든 패키지들을 싹싹 뒤져서 다운로드 받아야 합니다. * [^http://www.x.org/releases/] * [^http://www.freedesktop.org/wiki/Software/pkg-config] * [^http://dri.freedesktop.org/libdrm/] * [^http://www.gnu.org/software/] * [^http://pixman.org/] * [^http://sourceforge.net/projects/giflib/] * [^http://www.ijg.org/] * [^http://www.xmlsoft.org/] * [^http://www.zlib.net/] * [^http://www.bzip.org/] * [^http://www.openssl.org/] * [^http://www.kernel.org/pub/linux/utils/util-linux/] OR [^http://userweb.kernel.org/~kzak/util-linux-ng/] * [^http://www.gnu.org/software/gettext/] * [^http://www.freedesktop.org/wiki/Software/xapps] Target 환경으로의 빌드를 위해서는 Cross compile이 필수적입니다. 때문에 이를 잘 설명하기 위해서 다음과 같이 환경변수를 정의하겠습니다. 꼭 환경변수로 표시된 부분은 적절히 수정하시어 자신의 환경에 맞는 값으로 대체하세요. * TARGET_ROOT="/" : Target 실행환경의 root directory. 즉, Target board에서 최상위 디렉토리를 말합니다. (통상적으로 "/"가 될겁니다.) * TARGET_STAGE_ROOT="/tmp/target-rootfs" : 임시 설치경로 * HOST_STAGE_ROOT="/tmp/host-rootfs" : Build시에 필요한 Host용 실행파일 및 기타 필요한 파일들이 설치될 임시경로 * CROSS_COMPILE="arm-linux-" : Cross compiler의 명령어 prefix * AS="$(CROSS_COMPILE)as" : Cross assembler * CC="$(CROSS_COMPILE)gcc" : Cross C compiler * CXX="$(CROSS_COMPILE)g++" : Cross C++ compiler * CPP="$(CC) -E" : Cross C preprocessor * CXXCPP="$(CXX) -E" : Cross C++ preprocessor * LD="$(CROSS_COMPILE)ld" : Cross linker * AR="$(CROSS_COMPILE)ar" : Cross archive * RANLIB="$(CROSS_COMPILE)ranlib" : Cross generate index to archive * STRIP="$(CROSS_COMPILE)strip" : Cross discard symbols from object files * NM="$(CROSS_COMPILE)nm" : Cross list symbols from object files ==== Xorg server ==== TODO: 언젠가 정리할 수 있을까요??? {{{ [xorg-libraries] ... [Direct Rendering Infrastructure] libdrm MesaLib : needed some host util (flex, bison, makedepend) [xorg-server] xorg-server }}} ==== 예제 ==== * 불타오르는 화면 (필요한 라이브러리 : libX11, libXrender) {{{#!plain /* Copyright (C) HWPORT.COM All rights reserved. Author: JAEHYUK CHO */ #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 #include #include #include #include #include #include #include #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 */ }}} === 참고자료 === * 기본참고자료 * [^http://www.x.org/] : Xorg home page * [^http://www.linuxfromscratch.org/blfs/view/svn/x/installing.html] : X window를 직접 빌드해보고 싶으신 분들께 추천하는 문서 * [^http://en.wikibooks.org/wiki/X_Window_Programming] * [^http://tronche.com/gui/x/xlib/] * [^http://en.wikipedia.org/wiki/Xlib] * [^http://en.wikipedia.org/wiki/X_Window_System] * [^http://www.x.org/docs/X11/xlib.pdf] : Xlib programming에 대한 필수적인 기본 지침서 * [^http://wiki.kldp.org/wiki.php/LinuxdocSgml/X-Window-Programming-KLDP] : 이제 막 Xlib를 이용한 programming을 시작하신분들께 좋은 문서 * [^http://www.freedesktop.org/wiki/Software/Xserver/InstallGuide] * [^http://xorg.freedesktop.org/wiki/DeveloperStart] * Direct Rendering Infrastructure, OpenGL, Mesa * [^http://dri.freedesktop.org/wiki/] * [^http://mesa3d.org/] * Xlib에서의 Transparency window * [^http://hirntier.blogspot.com/2009/11/x11-grabbing-howto.html] * [^https://gist.github.com/901523] * [^http://www.eterm.org/docs/view.php?doc=ref#trans] * [^https://gist.github.com/903479] : Xlib transparent window with OpenGL support * [^http://lists.kde.org/?l=kde-devel&m=124264501607353] : ARGB visual 을 검색하는 방법 * [^http://en.gentoo-wiki.com/wiki/X.Org/Transparency]