I attach a simple GL based compositor that runs with the nVidia drivers for Xorg 7.0 and 7.1. These drivers support accelerated indirect GL, so this compositor does not need aiglx or Xgl. By keeping things simple, I have managed to identify and dodge some bugs in the nVidia Xcomposite support. I am currently testing the compositor with the ATI drivers.
The compositor deliberately offers no eye candy (apart from alpha support) and should be completely invisible to the user when working correctly. I intend to concentrate on maximising the performance of this compositor. It uses Xcomposite 0.3 overlays under Xorg 7.1. Hopefully, the lessons learnt will be of use to compiz and metacity, since for 99% of the time their intended behaviour will be similarly unobtrusive. The compositor relies on glTexSubImage copies on damaged areas as a replacement for the absent glBindTexImage. The fastest speeds are obtained when the glTexSubImage copies are applied to a GLXPixmap reference to the Xcomposite Pixmap. However, this approach crashes when windows are unmapped or sized due to a bug in the nVidia drivers. The crashes may be avoided by discarding the GLXPixmap references immediately after the glTexSubImage copies, but this is horrendously slow. I have raised this issue with James and he has added it to nVidia bug tracking system. I understand from Soren that there are problems with GLXPixmap references under the ATI drivers as well and once I have tested the compositor against their drivers I may be able to raise the same issue with them. The fastest means of avoiding the crashes is to perform an XCopyArea on the damaged areas into a separate "copy buffer" Pixmap and then perform the glTexSubImage copies out of this Pixmap. I have yet to devise any faster solution that would avoid crashes from non override_redirect windows. Unless I use a copy buffer Pixmap on my nVidia 6150 GPU then I find that GLX client windows freeze under glcompmgr (this problem occurs with rawhide Xorg 7.0 and Xorg 7.1 in both the x86_64 and i386 builds). I understand from James that he does not encounter these freezes on his platform. Therefore, I would be interested in learning of any other platforms that are effected by this problem and in any ideas about what might be causing it. My intial throught was that the nVidia indirect GLX implementation might be having problems with shared drawables when an indirect glTexSubImage request is received from the compositor context between certain indirect render requests from the client context (eg glBegin / glEnd). However, James has dismissed this particular explanation on the grounds that GLX should not suffer synchronisation problems providing the compositor has a server grab (as it does in the attached code). The sources should build reasonably easily against the nVidia drivers. The code does not try anything too special with GLX/GL, so it should build and work on other GLX platforms as well. The X setup will no doubt need some tweaking to get things working. The setup up for the nvidia drivers is as follows: Xorg 7.1 command line: Xorg -ignoreABI # ABI out of sync on Xorg 7.1 xorg.conf requirements: Section "Screen" Option "HWCursor" "On" # glcompmgr broken with SW cursor Option "AddARGBGLXVisuals" "True" # nVidia alpha voodoo Option "RenderAccel" "Off" # nVidia broken on Xorg 7.1 EndSection Section "Extensions" Option "Composite" "Enable" EndSection I am interested in any problems that anyone finds in building or running this code on any platform. Felix
/* * glcompmgr - a simple accelerated GL compositor focusing on speed. * * Requires: * Accelerated compositing (Xorg >= 7.0 and accelerated GLX drivers) * * Supports: * Any window manager. * Alpha blending, opacity property, shaped windows. * Xcomposite 0.3 overlays (Xorg >= 7.1). * * Trialed on: * nVidia 6150 GPU 1.0.87.62 drivers: * Xorg 7.0 (Fedora 5 x86_64) * Xorg 7.1 (Fedora Rawhide x86_64 & i686 / Xorg vanilla x86_64) * * Does NOT require: * GLX_EXT_texture_from_drawable, XRender * * Does NOT provide: * Eye candy. * * Make on nvidia GLX with: * gcc -I/usr/include/nvidia -L/usr/lib64/nvidia -L/usr/lib/nvidia \ * -lXcomposite -lXdamage -lX11 -lGL \ * `pkg-config --libs --cflags glib-2.0` glcompmgr.c * * Issues: * 1) Bug work arounds included for GLXPixmap failure on window unmap/resize. * a) COPY_BUFFER = 1 * Fastest performing fix. (glxgears speed around 60% of non-comp) * b) REFRESH_DRAWABLES = 1 * Slower, but simpler fix (glxgears speed around 20% of non-comp) * * Bug does not impede performance significantly. texture_from_drawable * extension will improve performance, but not hugely. * * 2) Some platforms encounter GL client freezes when COPY_BUFFER = 0. * Problem consistent over all tested GPU 6150 platforms, but not * present on some other systems. Cause currently unknown. * * 3) Bug work around for broken constant alpha blending requires a clumsy * hack that clears the source alpha on opacity property windows. * Bug that corrupts alpha channel during a non-alpha pixmap bit blit * means that this costly clear is needed again in every frame. * * 5) Does not draw the root pixmap (unecessary on modern desktops). * * ToDo: * Update frame by drawing a minimal set of textured polygons over the * previous frame. Texture mapping can take 10 times longer than bit blit, * so sticking to damaged and exposed zones makes a lot of sense. * * Author: Felix Bellaby * * Licence: GPL. * The code draws on Keith Packard's xcompmgr which is * Copyright (c) 2003 Keith Packard. * Any mistakes are mine. */ #define COPY_BUFFER 1 #define REFRESH_DRAWABLES 1 #define DEBUG 0 #if DEBUG #define dprintf printf #else #define dprintf(...) #endif #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <X11/Xlib.h> #include <X11/Xatom.h> #include <X11/extensions/Xcomposite.h> #include <X11/extensions/Xdamage.h> #include <X11/extensions/shape.h> #include <GL/gl.h> #include <GL/glx.h> #include <glib.h> #define HAVE_OVERLAYS (XCOMPOSITE_MAJOR >=0 && XCOMPOSITE_MINOR >= 3) int damage_notify; int shape_notify; int composite_request; Atom opacityAtom; #define OPACITY_PROP "_NET_WM_WINDOW_OPACITY" #define OPAQUE 0xffffffff; Display* display; Window root; int root_height; int root_width; GLXFBConfig fbcmirror; int redraw; int have_overlays; Pixmap xrootbuffer; GC gcroot; #if COPY_BUFFER /* The CopyBuffers provide safe GLXPixmap drawables when all else fails */ typedef struct _copy_buffer { GLXDrawable drawable; Pixmap pixmap; GC gc; int w, h, d; } CopyBuffer; GSList* copy_buffers; void copy_buffer_resize (CopyBuffer* copy_buffer, int w, int h) { if (copy_buffer->w >= w && copy_buffer->h >= h) return; if (copy_buffer->w < w) copy_buffer->w = w; if (copy_buffer->h < h) copy_buffer->h = h; if (copy_buffer->pixmap) { XFreePixmap (display, copy_buffer->pixmap); copy_buffer->pixmap = None; } if (copy_buffer->gc) { XFreeGC (display, copy_buffer->gc); copy_buffer->gc = None; } if (copy_buffer->drawable) { glXDestroyPixmap (display, copy_buffer->drawable); copy_buffer->drawable = None; } } CopyBuffer * copy_buffer_new (int w, int h, int d) { CopyBuffer *copy_buffer; if (!(copy_buffer = (CopyBuffer*)g_new (CopyBuffer, 1))) return NULL; copy_buffer->w = w; copy_buffer->h = h; copy_buffer->d = d; copy_buffer->pixmap = None; copy_buffer->gc = None; copy_buffer->drawable = None; return copy_buffer; } GLXDrawable copy_buffer_get_drawable (CopyBuffer *copy_buffer) { XGCValues gcv; gcv.graphics_exposures = False; if (!copy_buffer->pixmap && !(copy_buffer->pixmap = XCreatePixmap (display, root, copy_buffer->w, copy_buffer->h, copy_buffer->d))) return None; if (!copy_buffer->gc && !(copy_buffer->gc = XCreateGC (display, copy_buffer->pixmap, GCGraphicsExposures, &gcv))) return None; if (!copy_buffer->drawable) copy_buffer->drawable = glXCreatePixmap (display, fbcmirror, copy_buffer->pixmap, NULL); return copy_buffer->drawable; } void copy_buffer_destroy (CopyBuffer *copy_buffer) { if (copy_buffer->drawable) glXDestroyPixmap (display, copy_buffer->drawable); if (copy_buffer->pixmap) XFreePixmap (display, copy_buffer->pixmap); if (copy_buffer->gc) XFreeGC (display, copy_buffer->gc); g_free (copy_buffer); } #endif /* Quads are GL coordinate based replacements for XRectangles */ typedef struct _Quad { int x, y, w, h; } Quad; Quad * quad_new_from_area (int x, int y, int w, int h) { Quad *quad; if (!(quad = (Quad*)g_new (Quad, 1))) return NULL; quad->x = x; quad->y = y; quad->w = w; quad->h = h; return quad; } Quad * quad_new_from_quad (Quad* quad) { return (Quad*)g_memdup (quad, sizeof(Quad)); } void quad_destroy (Quad* quad) { g_free (quad); } /* Zones are simplified client-side replacements for X server Regions. */ typedef GSList Zone; Zone* zone_add_quad (Zone* zone, Quad* quad) { if (!quad) return zone; return g_slist_prepend (zone, quad); } Zone* zone_new_from_quad (Quad* quad) { return zone_add_quad (NULL, quad); } Zone* zone_new_from_zone (Zone* zone) { Zone* copy = NULL; Quad* quad; for (; zone; zone = g_slist_next (zone)) copy = zone_add_quad (copy, quad_new_from_quad (zone->data)); return copy; } void zone_destroy (Zone* zone) { while (zone) { quad_destroy (zone->data); zone = g_slist_remove (zone, zone->data); } } /* Mirrors are the compositor replacements for X Windows */ typedef struct _Mirror { Window window; Pixmap pixmap; Damage damage; #if COPY_BUFFER CopyBuffer * copy_buffer; #else GLXDrawable drawable; #endif GLuint texture; int x, y, z, w, h; int mapped; int shaped; int has_output; int has_alpha; float opacity; Zone * draw_zone; Zone * damage_zone; int b; /* coord conversion only */ int override_redirect; /* unused */ } Mirror; GSList* mirrors; void mirror_dump (Mirror* mirror, char* note) { dprintf ("Win %07x Dam %07x Pix %07x DRW %07x (%d x %d) Map %d:\t%s", mirror->window, mirror->damage, mirror->pixmap, #if COPY_BUFFER mirror->copy_buffer->drawable, #else mirror->drawable, #endif mirror->w, mirror->h, mirror->mapped, note ? note : ""); } gint match_id (gconstpointer a, gconstpointer b) { Mirror *mirror = (Mirror*)a; XID id = (XID)b; return !((mirror->window == id) || (mirror->has_output && ((mirror->pixmap == id) || #if COPY_BUFFER (mirror->copy_buffer->pixmap == id) || (mirror->copy_buffer->drawable == id) || #else (mirror->drawable == id) || #endif (mirror->damage == id)))); } /* * Stand alone compositors only learn that windows have been destroyed after * the event. This error handler protects against the BadWindow errors that * inevitably occur when requests are made on destroyed windows. * * Note that window manager integrated compositors face the same problem * with override_redirect windows, unless they decide to manage them. */ int error_handler (Display* display, XErrorEvent *event) { char name[50]; GSList* list; if (event->request_code == composite_request && event->minor_code == X_CompositeRedirectSubwindows) { fprintf (stderr, "Another composite manager is already running\n"); exit (1); } XGetErrorText (display, event->error_code, name, 50); dprintf ("%-50s request #%6d (%3d, %d)\n", name, event->serial, event->request_code, event->minor_code); list = g_slist_find_custom (mirrors, (gconstpointer)event->resourceid, match_id); if (list) mirror_dump ((Mirror*)list->data, "Xerror\n\n"); else dprintf ("Unrecognised X ID %07x\n", event->resourceid); return 0; } GLXDrawable mirror_get_drawable (Mirror* mirror) { #if COPY_BUFFER return copy_buffer_get_drawable (mirror->copy_buffer); #else if (!mirror->drawable) mirror->drawable = glXCreatePixmap (display, fbcmirror, mirror->pixmap, NULL); return mirror->drawable; #endif } void mirror_cleanup_resources (Mirror* mirror) { if (!mirror) return; #if ! COPY_BUFFER if (mirror->drawable) { glXDestroyPixmap (display, mirror->drawable); mirror->drawable = None; } #endif if (mirror->texture) { glDeleteTextures (1, &mirror->texture); mirror->texture = None; } if (mirror->pixmap) { XFreePixmap (display, mirror->pixmap); mirror->pixmap = None; } } void mirror_damage (Mirror* mirror, XRectangle *area) { if (!mirror || !mirror->damage) return; XDamageSubtract (display, mirror->damage, None, None); mirror->damage_zone = zone_add_quad (mirror->damage_zone, quad_new_from_area (area->x + mirror->b, (mirror->h - area->height) - (area->y + mirror->b), area->width, area->height)); if (mirror->mapped && mirror->damage_zone) redraw = 1; } void mirror_shape (Mirror* mirror, int shape_height) { Quad *quad; XRectangle *area; int i, n, o; int is_shaped, bx, by, bw, bh, cs, cx, cy, cw, ch; if (!mirror || !mirror->has_output) return; zone_destroy (mirror->draw_zone); mirror->draw_zone = NULL; mirror->shaped = 0; if (XShapeQueryExtents (display, mirror->window, &is_shaped, &bx, &by, &bw, &bh, &cs, &cx, &cy, &cw, &ch) && is_shaped) { if ((area = XShapeGetRectangles (display, mirror->window, ShapeBounding, &n, &o))) { mirror->shaped = 1; for (i = 0; i < n; i++) mirror->draw_zone = zone_add_quad (mirror->draw_zone, quad_new_from_area (area[i].x + mirror->b, (shape_height - area[i].height) - (area[i].y + mirror->b), area[i].width, area[i].height)); XFree (area); } } if (!mirror->draw_zone) mirror->draw_zone = zone_new_from_quad (quad_new_from_area (0, 0, mirror->w, mirror->h)); if (mirror->mapped) redraw = 1; } void mirror_map (Mirror* mirror) { if (!mirror || !mirror->has_output) return; if (!mirror->pixmap) mirror->pixmap = XCompositeNameWindowPixmap (display, mirror->window); mirror->mapped = 1; XSelectInput (display, mirror->window, PropertyChangeMask); XShapeSelectInput (display, mirror->window, ShapeNotifyMask); } void mirror_unmap (Mirror* mirror) { if (!mirror || !mirror->has_output) return; mirror->mapped = 0; mirror_cleanup_resources (mirror); zone_destroy (mirror->damage_zone); mirror->damage_zone = NULL; } void mirror_alpha (Mirror *mirror) { Atom actual; int format; unsigned long n, left; unsigned char *data; GSList *list; Quad *quad; if (!mirror || !mirror->has_output || mirror->has_alpha) return; if (XGetWindowProperty(display, mirror->window, opacityAtom, 0L, 1L, 0, XA_CARDINAL, &actual, &format, &n, &left, &data) == Success && data != NULL) { mirror->opacity = (float)*((unsigned int *)data) / OPAQUE; XFree ((void *) data); } else mirror->opacity = 1.0; zone_destroy (mirror->damage_zone); mirror->damage_zone = zone_new_from_zone (mirror->draw_zone); if (mirror->mapped) redraw = 1; } Mirror* mirror_new (Window window) { Mirror *mirror; XWindowAttributes attr; XRectangle *area; int i, n, o; GSList * list; Quad *quad; if (!(mirror = g_new (Mirror, 1))) return NULL; if (!XGetWindowAttributes (display, window, &attr)) return NULL; mirror->window = window; mirror->pixmap = None; mirror->damage = None; mirror->texture = None; mirror->shaped = 0; mirror->b = attr.border_width; mirror->w = attr.width + 2 * mirror->b; mirror->h = attr.height + 2 * mirror->b; mirror->x = attr.x - mirror->b; mirror->y = (root_height - mirror->h) - (attr.y - mirror->b); mirror->override_redirect = attr.override_redirect; mirror->z = 0; mirror->draw_zone = NULL; mirror->damage_zone = NULL; mirror->mapped = (attr.map_state == IsViewable); mirror->has_alpha = attr.depth > 3 * attr.visual->bits_per_rgb; mirror->has_output = (attr.class == InputOutput); if (!mirror->has_output) return mirror; #if COPY_BUFFER for (list = copy_buffers; list; list = g_slist_next (list)) { CopyBuffer* copy_buffer = list->data; if (copy_buffer->d == attr.depth) { mirror->copy_buffer = copy_buffer; copy_buffer_resize (mirror->copy_buffer, mirror->w, mirror->h); break; } } if (!list) { mirror->copy_buffer = copy_buffer_new (mirror->w, mirror->h, attr.depth); copy_buffers = g_slist_append (copy_buffers, mirror->copy_buffer); } #else mirror->drawable = None; #endif mirror->damage = XDamageCreate (display, window, XDamageReportDeltaRectangles); mirror_shape (mirror, mirror->h); mirror_alpha (mirror); if (mirror->mapped) mirror_map (mirror); return mirror; } void mirror_destroy (Mirror* mirror) { mirror_cleanup_resources (mirror); zone_destroy (mirror->damage_zone); zone_destroy (mirror->draw_zone); g_free (mirror); } void mirror_configure (Mirror* mirror, XConfigureEvent *ce) { int w = ce->width + 2 * ce->border_width; int h = ce->height + 2 * ce->border_width; GSList *list; Quad *quad; if (!mirror) return; if (mirror->w != w || mirror->h != h) { mirror->w = w; mirror->h = h; if (mirror->has_output) { mirror_cleanup_resources (mirror); mirror->pixmap = XCompositeNameWindowPixmap (display, mirror->window); #if COPY_BUFFER copy_buffer_resize (mirror->copy_buffer, mirror->w, mirror->h); #endif if (!mirror->shaped) { zone_destroy (mirror->draw_zone); mirror->draw_zone = zone_new_from_quad (quad_new_from_area (0, 0, mirror->w, mirror->h)); } zone_destroy (mirror->damage_zone); mirror->damage_zone = zone_new_from_zone (mirror->draw_zone); } } mirror->b = ce->border_width; mirror->x = ce->x - mirror->b; mirror->y = (root_height - mirror->h) - (ce->y - mirror->b); mirror->override_redirect = ce->override_redirect; if (mirror->has_output && mirror->mapped) redraw = 1; } #if COPY_BUFFER void mirror_copy_quad (Mirror *mirror, Quad* quad) { XCopyArea (display, mirror->pixmap, mirror->copy_buffer->pixmap, mirror->copy_buffer->gc, quad->x, (mirror->h - quad->h) - quad->y, quad->w, quad->h, quad->x, (mirror->copy_buffer->h - quad->h) - quad->y); } void mirror_copy_zone (Mirror *mirror, Zone* zone) { glXWaitGL (); for (;zone; zone = g_slist_next (zone)) mirror_copy_quad (mirror, zone->data); glXWaitX (); } #endif void mirror_bind_texture (Mirror* mirror, GLXContext context) { GSList *list; GLXDrawable glxmirror; glxmirror = mirror_get_drawable (mirror); glXMakeContextCurrent (display, glxmirror, glxmirror, context); glReadBuffer (GL_FRONT); glDrawBuffer (GL_FRONT); #if COPY_BUFFER if (mirror->texture) mirror_copy_zone (mirror, mirror->damage_zone); else mirror_copy_zone (mirror, mirror->draw_zone); #endif if (!mirror->has_alpha && mirror->opacity < 1.0) { glColorMask (0, 0, 0, 1); glClearColor (0, 0, 0, mirror->opacity); glClear (GL_COLOR_BUFFER_BIT); glColorMask (1, 1, 1, 1); } #if ! REFRESH_DRAWABLES && ! COPY_BUFFER XGrabServer (display); glXWaitX(); #endif if (mirror->texture) { glBindTexture (GL_TEXTURE_RECTANGLE_ARB, mirror->texture); for (list = mirror->damage_zone; list; list = g_slist_next (list)) { Quad *quad = list->data; glCopyTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, quad->x, quad->y, quad->x, quad->y, quad->w, quad->h); } } else { glGenTextures (1, &mirror->texture); glBindTexture (GL_TEXTURE_RECTANGLE_ARB, mirror->texture); glCopyTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 0, 0, mirror->w, mirror->h, 0); } #if ! REFRESH_DRAWABLES && ! COPY_BUFFER XUngrabServer (display); #endif } void extent_clip (int x, int y, int w, int h) { double n1[] = { 1, 0, 0, -x }; double n2[] = { 0, 1, 0, -y }; double n3[] = { -1, 0, 0, w + x }; double n4[] = { 0, -1, 0, h + y }; glClipPlane (GL_CLIP_PLANE0, n1); glClipPlane (GL_CLIP_PLANE1, n2); glClipPlane (GL_CLIP_PLANE2, n3); glClipPlane (GL_CLIP_PLANE3, n4); glEnable (GL_CLIP_PLANE0); glEnable (GL_CLIP_PLANE1); glEnable (GL_CLIP_PLANE2); glEnable (GL_CLIP_PLANE3); } void extent_unclip (void) { glDisable (GL_CLIP_PLANE0); glDisable (GL_CLIP_PLANE1); glDisable (GL_CLIP_PLANE2); glDisable (GL_CLIP_PLANE3); } void quad_draw (Quad* quad) { glTexCoord2i (quad->x, quad->y); glVertex2i (quad->x, quad->y); glTexCoord2i (quad->x + quad->w, quad->y); glVertex2i (quad->x + quad->w, quad->y); glTexCoord2i (quad->x + quad->w, quad->y + quad->h); glVertex2i (quad->x + quad->w, quad->y + quad->h); glTexCoord2i (quad->x, quad->y + quad->h); glVertex2i (quad->x, quad->y + quad->h); } void mirror_draw (Mirror* mirror, GLXDrawable glxroot, GLXContext context) { GSList* list; #if REFRESH_DRAWABLES && ! COPY_BUFFER XGrabServer (display); glXWaitX (); #endif mirror_bind_texture (mirror, context); glXMakeContextCurrent (display, glxroot, glxroot, context); glDrawBuffer (GL_BACK); glPushMatrix (); glTranslatef (mirror->x, mirror->y, mirror->z); glEnable (GL_TEXTURE_RECTANGLE_ARB); extent_clip (0, 0, mirror->w, mirror->h); glBegin(GL_QUADS); for (list = mirror->draw_zone; list; list = g_slist_next (list)) quad_draw (list->data); glEnd(); extent_unclip (); glPopMatrix (); glDisable (GL_TEXTURE_RECTANGLE_ARB); glBindTexture (GL_TEXTURE_RECTANGLE_ARB, 0); #if REFRESH_DRAWABLES && ! COPY_BUFFER glXDestroyPixmap (display, mirror->drawable); mirror->drawable = None; XUngrabServer (display); #endif zone_destroy (mirror->damage_zone); mirror->damage_zone = NULL; glXWaitGL(); } void draw (GLXDrawable glxroot, GLXContext context) { GSList *all = mirrors; GSList *transparents = NULL; int z = 0; glClearColor (0.0f, 0.0f, 0.0f, 1.0f); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); for (; all; all = g_slist_next (all)) { Mirror *mirror = (Mirror*)all->data; int width, height; if (!mirror->has_output || !mirror->mapped) continue; if (mirror->x + mirror->w < 1 || mirror->y + mirror->h < 1 || mirror->x >= root_width || mirror->y >= root_height) continue; mirror->z = z--; if (mirror->has_alpha || mirror->opacity < 1.0) { transparents = g_slist_prepend (transparents, mirror); continue; } /* Opaque windows - top down to clip by depth */ mirror_draw (mirror, glxroot, context); } while (transparents) { Mirror *mirror = (Mirror*)transparents->data; /* Would prefer (broken) GL_CONSTANT_ALPHA for opacity prop windows: */ glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); mirror_draw (mirror, glxroot, context); glDisable (GL_BLEND); transparents = g_slist_remove (transparents, mirror); } #if HAVE_OVERLAYS if (have_overlays) glXSwapBuffers (display, glxroot); else #endif XCopyArea (display, xrootbuffer, root, gcroot, 0, 0, root_width, root_height, 0, 0); } gint match_window (gconstpointer a, gconstpointer b) { Mirror *mirror = (Mirror*)a; Window window = (Window)b; return !(mirror->window == window); } Mirror * mirror_from_window (Window window) { GSList* list = g_slist_find_custom (mirrors, (gconstpointer)window, match_window); if (list) return (Mirror*)list->data; else return NULL; } void process_event (XEvent *event) { Window window; Mirror *mirror; GSList *list; int type = event->type; switch (type) { case CreateNotify: window = event->xcreatewindow.window; break; case ConfigureNotify: window = event->xconfigure.window; break; case DestroyNotify: window = event->xdestroywindow.window; break; case MapNotify: window = event->xmap.window; break; case UnmapNotify: window = event->xunmap.window; break; case ReparentNotify: window = event->xreparent.window; if (event->xreparent.parent == root) type = CreateNotify; else type = DestroyNotify; break; case CirculateNotify: window = event->xcirculate.window; break; case PropertyNotify: window = event->xproperty.window; break; default: if (type == damage_notify) window = ((XDamageNotifyEvent *)event)->drawable; else if (type == shape_notify) window = ((XShapeEvent *)event)->window; else return; } if (type == CreateNotify) mirror = mirror_new (window); else mirror = mirror_from_window (window); if (!mirror) return; if (type != damage_notify) { dprintf ("Event %d, # %d \n", type, ((XAnyEvent*)event)->serial); mirror_dump (mirror, "XEvent\n"); } switch (type) { case CreateNotify: mirrors = g_slist_prepend (mirrors, mirror); break; case DestroyNotify: mirrors = g_slist_remove (mirrors, mirror); mirror_destroy (mirror); break; case ConfigureNotify: mirror_configure (mirror, &event->xconfigure); window = event->xconfigure.above; list = g_slist_find_custom (mirrors, (gconstpointer)window, match_window); if (!list) printf ("Unidentified above window\n"); if (list) mirrors = g_slist_insert_before (g_slist_remove (mirrors, mirror), list, mirror); else mirrors = g_slist_prepend (g_slist_remove (mirrors, mirror), mirror); break; case MapNotify: mirror_map (mirror); break; case UnmapNotify: mirror_unmap (mirror); break; case CirculateNotify: if (event->xcirculate.place == PlaceOnTop) mirrors = g_slist_prepend (g_slist_remove (mirrors, mirror), mirror); else mirrors = g_slist_append (g_slist_remove (mirrors, mirror), mirror); break; case PropertyNotify: if (event->xproperty.atom == opacityAtom) mirror_alpha (mirror); break; default: if (type == damage_notify) mirror_damage (mirror, &(((XDamageNotifyEvent *)event)->area)); else if (type == shape_notify) if (((XShapeEvent*)event)->kind == ShapeBounding) mirror_shape (mirror, ((XShapeEvent*)event)->height); } } int loop; static void hang_up (int signal) { loop = 0; } void event_loop (GLXDrawable glxroot, GLXContext context) { XEvent event; signal (SIGHUP, hang_up); signal (SIGINT, hang_up); loop = 1; while (loop) { if (redraw) { draw (glxroot, context); redraw = 0; } do { XNextEvent (display, &event); process_event (&event); } while (QLength (display)); } } int main (int argc, char **argv) { int screen; XVisualInfo *visinfo; XEvent event; XSetWindowAttributes attr; Window overlay; Window overlay_root; GLXFBConfig *fbconfigs; GLXFBConfig fbcroot; GLXDrawable glxroot; GLXContext context; int i, n; int fbc_attr_mirror[] = { GLX_DOUBLEBUFFER, False, GLX_DEPTH_SIZE, 0, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 1, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT, None }; int query_error, query_event; int composite_major, composite_minor; if (!(display = XOpenDisplay (NULL))) return 1; screen = DefaultScreen (display); root = RootWindow (display, screen); root_width = DisplayWidth (display, screen); root_height = DisplayHeight (display, screen); XSetErrorHandler (error_handler); if (!glXQueryExtension (display, &query_error, &query_event)) return 1; if (!XQueryExtension (display, COMPOSITE_NAME, &composite_request, &query_event, &query_error)) return 1; if (!XDamageQueryExtension (display, &damage_notify, &query_error)) return 1; if (!XShapeQueryExtension (display, &shape_notify, &query_error)) return 1; if (!XFixesQueryExtension (display, &query_event, &query_error)) return 1; XCompositeQueryVersion (display, &composite_major, &composite_minor); if (composite_major == 0 && composite_minor < 2) return 1; have_overlays = (composite_major >= 0 || composite_minor >= 3); #if HAVE_OVERLAYS if (have_overlays) { int fbc_attr_root[] = { GLX_DOUBLEBUFFER, True, GLX_DEPTH_SIZE, 16, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 1, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT, None }; XSetWindowAttributes attr; if (!(fbconfigs = glXChooseFBConfig(display, DefaultScreen (display), fbc_attr_root, &n))) return 1; fbcroot = fbconfigs[0]; visinfo = glXGetVisualFromFBConfig (display, fbcroot); if (!(overlay = XCompositeGetOverlayWindow (display, root))) return 1; attr.colormap = XCreateColormap (display, root, visinfo->visual, AllocNone); overlay_root = XCreateWindow (display, overlay, 0, 0, root_width, root_height, 0, visinfo->depth, InputOutput, visinfo->visual, CWColormap, &attr); XMapWindow (display, overlay_root); XRaiseWindow (display, overlay_root); XShapeCombineRectangles(display, overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted); XShapeCombineRectangles(display, overlay_root, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted); if (!(glxroot = glXCreateWindow (display, fbcroot, overlay_root, NULL))) return 1; XFree (visinfo); } else #endif { XGCValues gc_attr; int fbc_attr_root[] = { GLX_DOUBLEBUFFER, False, GLX_DEPTH_SIZE, 16, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 1, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT, None }; gc_attr.subwindow_mode = IncludeInferiors; gcroot = XCreateGC (display, root, GCSubwindowMode, &gc_attr); if (!(xrootbuffer = XCreatePixmap (display, root, root_width, root_height, DefaultDepth (display, screen)))) return 1; if (!(fbconfigs = glXChooseFBConfig(display, screen, fbc_attr_root, &n))) return 1; fbcroot = fbconfigs[0]; glxroot = glXCreatePixmap (display, fbcroot, xrootbuffer, NULL); overlay_root = overlay = None; } if (!(context = glXCreateNewContext (display, fbcroot, GLX_RGBA_TYPE, NULL, GL_FALSE))) return 1; if (!(fbconfigs = glXChooseFBConfig(display, screen, fbc_attr_mirror, &n))) return 1; fbcmirror = fbconfigs[0]; opacityAtom = XInternAtom (display, OPACITY_PROP, 0); XGrabServer (display); { Window root_return, parent_return, last; Window *children; Mirror *mirror; XCompositeRedirectSubwindows (display, root, CompositeRedirectManual); XSelectInput (display, root, SubstructureNotifyMask); XQueryTree (display, root, &root_return, &parent_return, &children, &n); /* Should add another window to draw root, but it is rarely visible */ for (mirrors = NULL, i = 0; i < n; i++) if ((mirror = mirror_new (children[i]))) mirrors = g_slist_prepend (mirrors, mirror); XFree (children); } XUngrabServer (display); glXMakeContextCurrent (display, glxroot, glxroot, context); glMatrixMode (GL_PROJECTION); glLoadIdentity (); glOrtho (0, root_width, 0, root_height, 0, 65535); glEnable (GL_DEPTH_TEST); redraw = 1; event_loop (glxroot, context); while (mirrors) { Mirror *mirror = mirrors->data; mirrors = g_slist_remove (mirrors, mirror); mirror_destroy (mirror); } #if COPY_BUFFER while (copy_buffers) { CopyBuffer* copy_buffer = copy_buffers->data; copy_buffers = g_slist_remove (copy_buffers, copy_buffer); copy_buffer_destroy (copy_buffer); } #endif #if HAVE_OVERLAYS if (have_overlays) { glXDestroyWindow (display, glxroot); XDestroyWindow (display, overlay_root); XCompositeReleaseOverlayWindow (display, overlay); } else #endif { glXDestroyPixmap (display, glxroot); XFreeGC (display, gcroot); XFreePixmap (display, xrootbuffer); } glXDestroyContext (display, context); XFree (fbconfigs); }
_______________________________________________ compiz mailing list compiz@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/compiz