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

Reply via email to