Detect ARGB windows, don't give them shadows, compose them correctly.
[dana/xcompmgr.git] / xcompmgr.c
index 8aa2c94..e06d08c 100644 (file)
@@ -1,6 +1,33 @@
+/*
+ * $Id$
+ *
+ * Copyright © 2003 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  Keith Packard makes no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
+ *
+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
 #include <stdlib.h>
 #include <stdio.h>
+#include <math.h>
+#include <sys/poll.h>
 #include <X11/Xlib.h>
+#include <X11/Xutil.h>
 #include <X11/extensions/Xcomposite.h>
 #include <X11/extensions/Xdamage.h>
 #include <X11/extensions/Xrender.h>
@@ -14,6 +41,12 @@ typedef struct _win {
     Damage             damage;
     Picture            picture;
     XserverRegion      borderSize;
+    XserverRegion      extents;
+    Picture            shadow;
+    int                        shadow_dx;
+    int                        shadow_dy;
+    int                        shadow_width;
+    int                        shadow_height;
 
     /* for drawing translucent windows */
     XserverRegion      borderClip;
@@ -26,12 +59,251 @@ Display            *dpy;
 int            scr;
 Window         root;
 Picture                rootPicture;
+Picture                rootBuffer;
 Picture                transPicture;
+Picture                rootTile;
 XserverRegion  allDamage;
+int            root_height, root_width;
+
+#define BACKGROUND_PROP        "_XROOTPMAP_ID"
+
+#define WINDOW_SOLID   0
+#define WINDOW_TRANS   1
+#define WINDOW_ARGB    2
+
+#define TRANS_OPACITY  0.75
+#define SHADOW_RADIUS  15
+#define SHADOW_OPACITY 0.75
+#define SHADOW_OFFSET_X        (-SHADOW_RADIUS)
+#define SHADOW_OFFSET_Y        (-SHADOW_RADIUS)
+
+
+double
+gaussian (double r, double x, double y)
+{
+    return ((1 / (sqrt (2 * M_PI * r))) *
+           exp ((- (x * x + y * y)) / (2 * r * r)));
+}
+
+typedef struct _conv {
+    int            size;
+    double  *data;
+} conv;
+
+conv *
+make_gaussian_map (Display *dpy, double r)
+{
+    conv           *c;
+    int                    size = ((int) ceil ((r * 3)) + 1) & ~1;
+    int                    center = size / 2;
+    int                    x, y;
+    double         t;
+    double         g;
+    
+    c = malloc (sizeof (conv) + size * size * sizeof (double));
+    c->size = size;
+    c->data = (double *) (c + 1);
+    for (y = 0; y < size; y++)
+       for (x = 0; x < size; x++)
+       {
+           g = gaussian (r, (double) (x - center), (double) (y - center));
+           t += g;
+           c->data[y * size + x] = g;
+       }
+    printf ("gaussian total %f\n", t);
+    for (y = 0; y < size; y++)
+       for (x = 0; x < size; x++)
+       {
+           c->data[y*size + x] /= t;
+       }
+    return c;
+}
+
+/*
+ * A picture will help
+ *
+ *     -center   0                width  width+center
+ *  -center +-----+-------------------+-----+
+ *         |     |                   |     |
+ *         |     |                   |     |
+ *        0 +-----+-------------------+-----+
+ *         |     |                   |     |
+ *         |     |                   |     |
+ *         |     |                   |     |
+ *   height +-----+-------------------+-----+
+ *         |     |                   |     |
+ * height+  |     |                   |     |
+ *  center  +-----+-------------------+-----+
+ */
+unsigned int
+sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
+{
+    int            fx, fy;
+    int            sx, sy;
+    double  *g_data;
+    double  *g_line = map->data;
+    int            g_size = map->size;
+    int            center = g_size / 2;
+    int            fx_start, fx_end;
+    int            fy_start, fy_end;
+    double  v;
+    
+    /*
+     * Compute set of filter values which are "in range",
+     * that's the set with:
+     * 0 <= x + (fx-center) && x + (fx-center) < width &&
+     *  0 <= y + (fy-center) && y + (fy-center) < height
+     *
+     *  0 <= x + (fx - center) x + fx - center < width
+     *  center - x <= fx       fx < width + center - x
+     */
+
+    fx_start = center - x;
+    if (fx_start < 0)
+       fx_start = 0;
+    fx_end = width + center - x;
+    if (fx_end > g_size)
+       fx_end = g_size;
+
+    fy_start = center - y;
+    if (fy_start < 0)
+       fy_start = 0;
+    fy_end = height + center - y;
+    if (fy_end > g_size)
+       fy_end = g_size;
+
+    g_line = g_line + fy_start * g_size + fx_start;
+    
+    v = 0;
+    for (fy = fy_start; fy < fy_end; fy++)
+    {
+       g_data = g_line;
+       g_line += g_size;
+       
+       for (fx = fx_start; fx < fx_end; fx++)
+           v += *g_data++;
+    }
+    if (v > 1)
+       v = 1;
+    
+    return ((unsigned int) (v * opacity * 255.0)) << 24;
+}
+
+XImage *
+make_shadow (Display *dpy, double opacity, double r, int width, int height)
+{
+    conv           *map = make_gaussian_map (dpy, r);
+    XImage         *ximage;
+    double         *gdata = map->data;
+    unsigned int    *data;
+    int                    gsize = map->size;
+    int                    ylimit, xlimit;
+    int                    swidth = width + gsize;
+    int                    sheight = height + gsize;
+    int                    center = gsize / 2;
+    int                    x, y;
+    int                    fx, fy;
+    int                    sx, sy;
+    unsigned int    d;
+    double         v;
+    unsigned char   c;
+    
+    data = malloc (swidth * sheight * sizeof (int));
+    ximage = XCreateImage (dpy,
+                          DefaultVisual(dpy, DefaultScreen(dpy)),
+                          32,
+                          ZPixmap,
+                          0,
+                          (char *) data,
+                          swidth, sheight, 32, swidth * sizeof (int));
+    /*
+     * Build the gaussian in sections
+     */
+
+    /*
+     * corners
+     */
+    ylimit = gsize;
+    if (ylimit > sheight / 2)
+       ylimit = (sheight + 1) / 2;
+    xlimit = gsize;
+    if (xlimit > swidth / 2)
+       xlimit = (swidth + 1) / 2;
 
-#define WINDOW_PLAIN   0
-#define WINDOW_DROP    1
-#define WINDOW_TRANS   2
+    for (y = 0; y < ylimit; y++)
+       for (x = 0; x < xlimit; x++)
+       {
+           d = sum_gaussian (map, opacity, x - center, y - center, width, height);
+           data[y * swidth + x] = d;
+           data[(sheight - y - 1) * swidth + x] = d;
+           data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d;
+           data[y * swidth + (swidth - x - 1)] = d;
+       }
+
+    /*
+     * top/bottom
+     */
+    for (y = 0; y < ylimit; y++)
+    {
+       d = sum_gaussian (map, opacity, center, y - center, width, height);
+       for (x = gsize; x < swidth - gsize; x++)
+       {
+           data[y * swidth + x] = d;
+           data[(sheight - y - 1) * swidth + x] = d;
+       }
+    }
+
+    /*
+     * sides
+     */
+    
+    for (x = 0; x < xlimit; x++)
+    {
+       d = sum_gaussian (map, opacity, x - center, center, width, height);
+       for (y = gsize; y < sheight - gsize; y++)
+       {
+           data[y * swidth + x] = d;
+           data[y * swidth + (swidth - x - 1)] = d;
+       }
+    }
+
+    /*
+     * center
+     */
+
+    d = sum_gaussian (map, opacity, center, center, width, height);
+    for (y = ylimit; y < sheight - ylimit; y++)
+       for (x = xlimit; x < swidth - xlimit; x++)
+           data[y * swidth + x] = d;
+
+    free (map);
+    return ximage;
+}
+
+Picture
+shadow_picture (Display *dpy, double opacity, double r, int width, int height, int *wp, int *hp)
+{
+    XImage  *shadowImage = make_shadow (dpy, opacity, r, width, height);
+    Pixmap  shadowPixmap = XCreatePixmap (dpy, root, 
+                                         shadowImage->width,
+                                         shadowImage->height,
+                                         32);
+    Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
+                                                 XRenderFindStandardFormat (dpy, PictStandardARGB32),
+                                                 0, 0);
+    GC     gc = XCreateGC (dpy, shadowPixmap, 0, 0);
+    
+    XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 
+              shadowImage->width,
+              shadowImage->height);
+    *wp = shadowImage->width;
+    *hp = shadowImage->height;
+    XFreeGC (dpy, gc);
+    XDestroyImage (shadowImage);
+    XFreePixmap (dpy, shadowPixmap);
+    return shadowPicture;
+}
 
 win *
 find_win (Display *dpy, Window id)
@@ -44,15 +316,92 @@ find_win (Display *dpy, Window id)
     return 0;
 }
 
+Picture
+root_tile (Display *dpy)
+{
+    Picture        picture;
+    Atom           actual_type;
+    Pixmap         pixmap;
+    int                    actual_format;
+    unsigned long   nitems;
+    unsigned long   bytes_after;
+    unsigned char   *prop;
+    Bool           fill;
+    XRenderPictureAttributes   pa;
+
+    if (XGetWindowProperty (dpy, root, XInternAtom (dpy, BACKGROUND_PROP, False),
+                           0, 4, False, AnyPropertyType,
+                           &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success &&
+       actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1)
+    {
+       memcpy (&pixmap, prop, 4);
+       XFree (prop);
+       fill = False;
+    }
+    else
+    {
+       pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
+       fill = True;
+    }
+    pa.repeat = True;
+    picture = XRenderCreatePicture (dpy, pixmap,
+                                   XRenderFindVisualFormat (dpy,
+                                                            DefaultVisual (dpy, scr)),
+                                   CPRepeat, &pa);
+    if (fill)
+    {
+       XRenderColor    c;
+       
+       c.red = c.green = c.blue = 0x8080;
+       c.alpha = 0xffff;
+       XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 
+                             0, 0, 1, 1);
+    }
+    return picture;
+}
+
 void
 paint_root (Display *dpy)
 {
-    XRenderColor    c;
+    if (!rootTile)
+       rootTile = root_tile (dpy);
     
-    c.red = c.green = c.blue = 0x8080;
-    c.alpha = 0xffff;
-    XRenderFillRectangle (dpy, PictOpSrc, rootPicture, &c, 
-                         0, 0, 32767, 32767);
+    XRenderComposite (dpy, PictOpSrc,
+                     rootTile, None, rootBuffer,
+                     0, 0, 0, 0, 0, 0, root_width, root_height);
+}
+
+XserverRegion
+win_extents (Display *dpy, win *w)
+{
+    XRectangle     r;
+    
+    if (w->mode == WINDOW_ARGB)
+    {
+       r.x = w->a.x;
+       r.y = w->a.y;
+       r.width = w->a.width + w->a.border_width * 2;
+       r.height = w->a.height + w->a.border_width * 2;
+    }
+    else
+    {
+       if (!w->shadow)
+       {
+           double      opacity = SHADOW_OPACITY;
+           if (w->mode == WINDOW_TRANS)
+               opacity = opacity * TRANS_OPACITY;
+           w->shadow = shadow_picture (dpy, opacity, SHADOW_RADIUS, 
+                                       w->a.width, w->a.height,
+                                       &w->shadow_width, &w->shadow_height);
+           w->shadow_dx = SHADOW_OFFSET_X;
+           w->shadow_dy = SHADOW_OFFSET_Y;
+       }
+       r.x = w->a.x + w->a.border_width + w->shadow_dx;
+       r.y = w->a.y + w->a.border_width + w->shadow_dy;
+       r.width = w->shadow_width;
+       r.height = w->shadow_height;
+    }
+    return XFixesCreateRegion (dpy, &r, 1);
 }
 
 XserverRegion
@@ -71,40 +420,79 @@ paint_all (Display *dpy, XserverRegion region)
     win        *w;
     win        *t = 0;
     
+    if (!region)
+    {
+       XRectangle  r;
+       r.x = 0;
+       r.y = 0;
+       r.width = root_width;
+       r.height = root_height;
+       region = XFixesCreateRegion (dpy, &r, 1);
+    }
+    if (!rootBuffer)
+    {
+       Pixmap  rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
+                                           DefaultDepth (dpy, scr));
+       rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
+                                          XRenderFindVisualFormat (dpy,
+                                                                   DefaultVisual (dpy, scr)),
+                                          0, 0);
+       XFreePixmap (dpy, rootPixmap);
+    }
+    XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
     for (w = list; w; w = w->next)
     {
        Picture mask;
        
        if (w->a.map_state != IsViewable)
            continue;
-       if (w->mode == WINDOW_TRANS)
-       {
-           w->borderClip = XFixesCreateRegion (dpy, 0, 0);
-           XFixesUnionRegion (dpy, w->borderClip, region, 0, 0, None, 0, 0);
-           w->prev_trans = t;
-           t = w;
-       }
-       else
+       if (!w->picture)
+           continue;
+       
+       if (w->borderSize)
+           XFixesDestroyRegion (dpy, w->borderSize);
+       w->borderSize = border_size (dpy, w);
+       if (w->extents)
+           XFixesDestroyRegion (dpy, w->extents);
+       w->extents = win_extents (dpy, w);
+       if (w->mode == WINDOW_SOLID)
        {
-           XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
-           if (w->borderSize)
-               XFixesDestroyRegion (dpy, w->borderSize);
-           w->borderSize = border_size (dpy, w);
+           XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
            XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0);
-           XRenderComposite (dpy, PictOpSrc, w->picture, None, rootPicture,
+           XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
                              0, 0, 0, 0, 
                              w->a.x + w->a.border_width,
                              w->a.y + w->a.border_width,
                              w->a.width,
                              w->a.height);
        }
+       w->borderClip = XFixesCreateRegion (dpy, 0, 0);
+       XFixesUnionRegion (dpy, w->borderClip, region, 0, 0, None, 0, 0);
+       w->prev_trans = t;
+       t = w;
     }
-    XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
+    XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
     paint_root (dpy);
     for (w = t; w; w = w->prev_trans)
     {
-       XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, w->borderClip);
-       XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootPicture,
+       XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
+       if (w->shadow)
+       {
+           XRenderComposite (dpy, PictOpOver, w->shadow, None, rootBuffer,
+                             0, 0, 0, 0,
+                             w->a.x + w->a.border_width + w->shadow_dx,
+                             w->a.y + w->a.border_width + w->shadow_dy,
+                             w->shadow_width, w->shadow_height);
+       }
+       if (w->mode == WINDOW_TRANS)
+           XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootBuffer,
+                             0, 0, 0, 0, 
+                             w->a.x + w->a.border_width,
+                             w->a.y + w->a.border_width,
+                             w->a.width,
+                             w->a.height);
+       else if (w->mode == WINDOW_ARGB)
+           XRenderComposite (dpy, PictOpOver, w->picture, None, rootBuffer,
                              0, 0, 0, 0, 
                              w->a.x + w->a.border_width,
                              w->a.y + w->a.border_width,
@@ -114,6 +502,9 @@ paint_all (Display *dpy, XserverRegion region)
        w->borderClip = None;
     }
     XFixesDestroyRegion (dpy, region);
+    XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
+    XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
+                     0, 0, 0, 0, 0, 0, root_width, root_height);
 }
 
 void
@@ -153,9 +544,12 @@ map_win (Display *dpy, Window id)
     if (!w)
        return;
     w->a.map_state = IsViewable;
-    w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
-    region = border_size (dpy, w);
-    add_damage (dpy, region);
+    if (w->picture)
+    {
+       w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
+       region = win_extents (dpy, w);
+       add_damage (dpy, region);
+    }
 }
 
 void
@@ -171,20 +565,21 @@ unmap_win (Display *dpy, Window id)
        XDamageDestroy (dpy, w->damage);
        w->damage = None;
     }
-    if (w->borderSize != None)
+    if (w->extents != None)
     {
-       add_damage (dpy, w->borderSize);    /* destroys region */
-       w->borderSize = None;
+       add_damage (dpy, w->extents);    /* destroys region */
+       w->extents = None;
     }
 }
 
 void
 add_win (Display *dpy, Window id, Window prev)
 {
-    win        *new = malloc (sizeof (win));
-    win        **p;
-    XWindowAttributes a;
-    XRenderPictureAttributes pa;
+    win                                *new = malloc (sizeof (win));
+    win                                **p;
+    XWindowAttributes          a;
+    XRenderPictureAttributes   pa;
+    XRenderPictFormat          *format;
     
     if (!new)
        return;
@@ -205,17 +600,29 @@ add_win (Display *dpy, Window id, Window prev)
     new->damaged = 0;
     new->damage = None;
     pa.subwindow_mode = IncludeInferiors;
-    new->picture = XRenderCreatePicture (dpy, id,
-                                        XRenderFindVisualFormat (dpy, 
-                                                                 new->a.visual),
-                                        CPSubwindowMode,
-                                        &pa);
+    if (new->a.class == InputOnly)
+    {
+       new->picture = 0;
+       format = 0;
+    }
+    else
+    {
+       format = XRenderFindVisualFormat (dpy, new->a.visual);
+       new->picture = XRenderCreatePicture (dpy, id,
+                                            format,
+                                            CPSubwindowMode,
+                                            &pa);
+    }
                                         
+    new->shadow = None;
     new->borderSize = None;
-    if (new->a.override_redirect)
+    new->extents = None;
+    if (format && format->type == PictTypeDirect && format->direct.alphaMask)
+       new->mode = WINDOW_ARGB;
+    else if (new->a.override_redirect)
        new->mode = WINDOW_TRANS;
     else
-       new->mode = WINDOW_DROP;
+       new->mode = WINDOW_SOLID;
     new->next = *p;
     *p = new;
     if (new->a.map_state == IsViewable)
@@ -230,15 +637,33 @@ configure_win (Display *dpy, XConfigureEvent *ce)
     XserverRegion   damage = None;
     
     if (!w)
+    {
+       if (ce->window == root)
+       {
+           if (rootBuffer)
+           {
+               XRenderFreePicture (dpy, rootBuffer);
+               rootBuffer = None;
+           }
+           root_width = ce->width;
+           root_height = ce->height;
+       }
        return;
+    }
     if (w->a.map_state == IsViewable)
     {
        damage = XFixesCreateRegion (dpy, 0, 0);
-       if (w->borderSize != None)      
-           XFixesUnionRegion (dpy, damage, w->borderSize, 0, 0, None, 0, 0);
+       if (w->extents != None) 
+           XFixesUnionRegion (dpy, damage, w->extents, 0, 0, None, 0, 0);
     }
     w->a.x = ce->x;
     w->a.y = ce->y;
+    if (w->a.width != ce->width || w->a.height != ce->height)
+       if (w->shadow)
+       {
+           XRenderFreePicture (dpy, w->shadow);
+           w->shadow = None;
+       }
     w->a.width = ce->width;
     w->a.height = ce->height;
     w->a.border_width = ce->border_width;
@@ -268,8 +693,9 @@ configure_win (Display *dpy, XConfigureEvent *ce)
     }
     if (damage)
     {
-       XserverRegion   border = border_size (dpy, w);
-       XFixesUnionRegion (dpy, damage, damage, 0, 0, border, 0, 0);
+       XserverRegion   extents = win_extents (dpy, w);
+       XFixesUnionRegion (dpy, damage, damage, 0, 0, extents, 0, 0);
+       XFixesDestroyRegion (dpy, extents);
        add_damage (dpy, damage);
     }
 }
@@ -285,7 +711,8 @@ destroy_win (Display *dpy, Window id, Bool gone)
            if (!gone)
            {
                unmap_win (dpy, id);
-               XRenderFreePicture (dpy, w->picture);
+               if (w->picture)
+                   XRenderFreePicture (dpy, w->picture);
            }
            *prev = w->next;
            free (w);
@@ -331,6 +758,17 @@ expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
     add_damage (dpy, region);
 }
 
+int
+time_in_millis ()
+{
+    struct timeval  tp;
+
+    gettimeofday (&tp, 0);
+    return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
+}
+
+#define INTERVAL    30
+
 main ()
 {
     XEvent         ev;
@@ -345,8 +783,14 @@ main ()
     XRenderPictureAttributes   pa;
     XRenderColor               c;
     XRectangle     *expose_rects = 0;
+    GC             gc;
     int                    size_expose = 0;
     int                    n_expose = 0;
+    struct pollfd   ufd;
+    int                    n;
+    int                    last_update;
+    int                    now;
+    int                    timeout;
 
     dpy = XOpenDisplay (0);
     if (!dpy)
@@ -368,6 +812,9 @@ main ()
     c.alpha = 0xc0c0;
     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
     
+    root_width = DisplayWidth (dpy, scr);
+    root_height = DisplayHeight (dpy, scr);
+    
     rootPicture = XRenderCreatePicture (dpy, root, 
                                        XRenderFindVisualFormat (dpy,
                                                                 DefaultVisual (dpy, scr)),
@@ -378,26 +825,34 @@ main ()
        fprintf (stderr, "No composite extension\n");
        exit (1);
     }
+    printf ("Composite error %d\n", error_base);
     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
     {
        fprintf (stderr, "No damage extension\n");
        exit (1);
     }
+    printf ("Damage error %d\n", damage_error);
     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
     {
        fprintf (stderr, "No XFixes extension\n");
        exit (1);
     }
+    printf ("XFixes error %d\n", xfixes_error);
     allDamage = None;
     XGrabServer (dpy);
     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
-    paint_root (dpy);
-    XSelectInput (dpy, root, SubstructureNotifyMask|ExposureMask);
+    XSelectInput (dpy, root, 
+                 SubstructureNotifyMask|
+                 ExposureMask|
+                 StructureNotifyMask|
+                 PropertyChangeMask);
     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
     for (i = 0; i < nchildren; i++)
        add_win (dpy, children[i], i ? children[i-1] : None);
     XFree (children);
     XUngrabServer (dpy);
+    paint_all (dpy, None);
+    last_update = time_in_millis ();
     for (;;)
     {
 /*     dump_wins (); */
@@ -457,12 +912,34 @@ main ()
                    }
                }
                break;
+           case PropertyNotify:
+               if (ev.xproperty.atom == XInternAtom (dpy, BACKGROUND_PROP, False))
+               {
+                   if (rootTile)
+                   {
+                       XClearArea (dpy, root, 0, 0, 0, 0, True);
+                       XRenderFreePicture (dpy, rootTile);
+                       rootTile = None;
+                   }
+               }
+               break;
            default:
                if (ev.type == damage_event + XDamageNotify)
                    damage_win (dpy, (XDamageNotifyEvent *) &ev);
                break;
            }
        } while (XEventsQueued (dpy, QueuedAfterReading));
+       now = time_in_millis ();
+       timeout = INTERVAL - (now - last_update);
+       if (timeout > 0)
+       {
+           ufd.fd = ConnectionNumber (dpy);
+           ufd.events = POLLIN;
+           n = poll (&ufd, 1, timeout);
+           if (n > 0 && (ufd.revents & POLLIN) && XEventsQueued (dpy, QueuedAfterReading))
+               continue;
+       }
+       last_update = time_in_millis();
        if (allDamage)
        {
            paint_all (dpy, allDamage);