#include #include #include #include #include #include #include #include #include typedef struct _Data Data; typedef void (* BindTexImageFunc) (Display *display, GLXDrawable drawable, int buffer, int *attribList); typedef void (* ReleaseTexImageFunc) (Display *display, GLXDrawable drawable, int buffer); struct _Data { Display *dpy; Window w; GLXContext ctx; GLXPixmap glx_pixmap; Pixmap win_pixmap; Window sub_win; BindTexImageFunc bind_tex_image; ReleaseTexImageFunc release_tex_image; GLuint rgb_tex_num, alpha_tex_num; }; #define TEX_WIDTH 2 #define TEX_HEIGHT 2 #define PIXMAP_WIDTH 128 #define PIXMAP_HEIGHT 128 /* 4x4 pure alpha texture with all full alpha except bottom left pixel */ static const GLubyte alpha_tex_data[] = { 0x80, 0xff, 0xff, 0xff }; static void redraw (Data *data) { GLfloat tex_coords[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; GLfloat vertex_coords[] = { -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0 }; if (data->glx_pixmap != None) { /* Bind the pixmap to the rgb texture in the first layer */ glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, data->rgb_tex_num); data->bind_tex_image (data->dpy, data->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL); } glClear (GL_COLOR_BUFFER_BIT); glColor4f (1.0f, 1.0f, 1.0f, 1.0f); glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer (2, GL_FLOAT, 0, vertex_coords); glClientActiveTexture (GL_TEXTURE0); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glTexCoordPointer (2, GL_FLOAT, 0, tex_coords); glClientActiveTexture (GL_TEXTURE1); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glTexCoordPointer (2, GL_FLOAT, 0, tex_coords); glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); glXSwapBuffers (data->dpy, data->w); if (data->glx_pixmap != None) data->release_tex_image (data->dpy, data->glx_pixmap, GLX_FRONT_LEFT_EXT); } static GLuint make_tex (const GLubyte *data, int width, int height, GLenum format, GLenum internal_format) { GLuint tex_num; glPixelStorei (GL_UNPACK_ALIGNMENT, 1); glGenTextures (1, &tex_num); glBindTexture (GL_TEXTURE_2D, tex_num); glTexImage2D (GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glEnable (GL_TEXTURE_2D); return tex_num; } static Window make_window (Data *data, unsigned int width, unsigned int height) { int attrib[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER, None }; int scrnum = 0; XSetWindowAttributes attr; unsigned long mask; Window root; Window win; XVisualInfo *visinfo; root = RootWindow (data->dpy, scrnum); if (!(visinfo = glXChooseVisual (data->dpy, scrnum, attrib))) { printf ("Error: couldn't get an RGB, Double-buffered visual\n"); exit (1); } if (!(data->ctx = glXCreateContext (data->dpy, visinfo, NULL, True))) { printf ("Error: glXCreateContext failed\n"); exit (1); } /* window attributes */ attr.background_pixel = 0; attr.border_pixel = 0; attr.colormap = XCreateColormap (data->dpy, root, visinfo->visual, AllocNone); attr.event_mask = StructureNotifyMask | ExposureMask | PointerMotionMask | KeyPressMask; mask = CWBorderPixel | CWColormap | CWEventMask; win = XCreateWindow (data->dpy, root, 0, 0, width, height, 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr); if (!win) { printf ("Error: XCreateWindow failed\n"); exit (1); } glXMakeCurrent (data->dpy, win, data->ctx); data->rgb_tex_num = make_tex (NULL, PIXMAP_WIDTH, PIXMAP_HEIGHT, GL_RGB, GL_RGB); glActiveTexture (GL_TEXTURE1); data->alpha_tex_num = make_tex (alpha_tex_data, TEX_WIDTH, TEX_HEIGHT, GL_ALPHA, GL_ALPHA); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); /* Replace the RGB from layer 1 with the RGB from layer 0 */ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); if (getenv ("WORKAROUND")) { /* workaround */ /* Use the alpha from layer 1 modulated with the alpha from the primary color */ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR); glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); } else { /* not workaround */ /* Modulate the alpha in layer 1 with the alpha from the previous layer */ glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); } glViewport (0, 0, width, height); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_BLEND); return win; } static Pixmap composite_window (Display *display, Window win) { XCompositeRedirectWindow (display, win, CompositeRedirectAutomatic); return XCompositeNameWindowPixmap (display, win); } static void uncomposite_window (Display *display, Window win, Pixmap pixmap) { XFreePixmap (display, pixmap); XCompositeUnredirectWindow (display, win, CompositeRedirectAutomatic); } static GLXPixmap create_glx_pixmap (Display *display, Drawable draw) { static const int fb_config_attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 0, GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, GLX_BIND_TO_TEXTURE_RGB_EXT, 1, None }; static const int pixmap_attribs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT, None }; GLXFBConfig *fb_configs; int n_fb_configs = 0; GLXPixmap glx_pixmap = None; fb_configs = glXChooseFBConfig (display, DefaultScreen (display), fb_config_attribs, &n_fb_configs); if (fb_configs == NULL || n_fb_configs < 1) fprintf (stderr, "No valid FB config found\n"); else glx_pixmap = glXCreatePixmap (display, fb_configs[n_fb_configs - 1], draw, pixmap_attribs); if (fb_configs) XFree (fb_configs); return glx_pixmap; } static void draw_to_window (Display *display, Window win) { GC white_gc, black_gc; XGCValues gc_values; unsigned long white, black; white = WhitePixel (display, DefaultScreen (display)); black = BlackPixel (display, DefaultScreen (display)); gc_values.foreground = white; gc_values.background = white; white_gc = XCreateGC (display, win, GCForeground | GCBackground, &gc_values); gc_values.foreground = black; gc_values.background = black; black_gc = XCreateGC (display, win, GCForeground | GCBackground, &gc_values); XFillRectangle (display, win, white_gc, 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT); XDrawArc (display, win, black_gc, PIXMAP_WIDTH / 4, PIXMAP_HEIGHT / 4, PIXMAP_WIDTH / 2, PIXMAP_HEIGHT / 2, 0, 360 * 64); XFreeGC (display, white_gc); XFreeGC (display, black_gc); } static void event_loop (Data *data) { while (1) { XEvent event; XNextEvent (data->dpy, &event); if (event.type == KeyPress) { KeySym sym = XKeycodeToKeysym (data->dpy, event.xkey.keycode, 0); if (sym == XK_Escape || sym == XK_q || sym == XK_Q) break; } else if (event.type == MapNotify) { if (event.xany.window == data->sub_win && data->win_pixmap == None) { data->win_pixmap = composite_window (data->dpy, data->sub_win); data->glx_pixmap = create_glx_pixmap (data->dpy, data->win_pixmap); draw_to_window (data->dpy, data->sub_win); XClearArea (data->dpy, data->w, 0, 0, 0, 0, True); } } else if (event.type == Expose) redraw (data); } } static Window create_x_window (Display *display) { Window win; unsigned long white; white = WhitePixel (display, DefaultScreen (display)); win = XCreateSimpleWindow (display, DefaultRootWindow (display), 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT, 0, white, white); XSelectInput (display, win, StructureNotifyMask); return win; } int main (int argc, char *argv[]) { Data data; data.win_pixmap = None; data.glx_pixmap = None; data.dpy = XOpenDisplay (NULL); if (!data.dpy) { printf ("Error: XOpenDisplay failed\n"); return 1; } data.w = make_window (&data, 640, 480); data.sub_win = create_x_window (data.dpy); XMapWindow (data.dpy, data.sub_win); data.bind_tex_image = (BindTexImageFunc) glXGetProcAddress ((GLubyte *) "glXBindTexImageEXT"); data.release_tex_image = (ReleaseTexImageFunc) glXGetProcAddress ((GLubyte *) "glXReleaseTexImageEXT"); if (data.bind_tex_image == NULL || data.release_tex_image == NULL) { fprintf (stderr, "Missing TFP extension\n"); return 1; } /* init GL state */ glClearColor (0.0, 0.0, 1.0, 1.0); XMapWindow (data.dpy, data.w); event_loop (&data); glXMakeCurrent (data.dpy, None, NULL); glXDestroyContext (data.dpy, data.ctx); XDestroyWindow (data.dpy, data.w); if (data.glx_pixmap != None) glXDestroyGLXPixmap (data.dpy, data.glx_pixmap); if (data.win_pixmap != None) uncomposite_window (data.dpy, data.sub_win, data.win_pixmap); XDestroyWindow (data.dpy, data.sub_win); XCloseDisplay (data.dpy); return 0; }