Track changes to _XROOTPMAP_ID property
[dana/xcompmgr.git] / xcompmgr.c
1 /*
2  * $Id$
3  *
4  * Copyright © 2003 Keith Packard
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <math.h>
28 #include <sys/poll.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/extensions/Xcomposite.h>
32 #include <X11/extensions/Xdamage.h>
33 #include <X11/extensions/Xrender.h>
34
35 typedef struct _win {
36     struct _win         *next;
37     Window              id;
38     XWindowAttributes   a;
39     int                 damaged;
40     int                 mode;
41     Damage              damage;
42     Picture             picture;
43     XserverRegion       borderSize;
44     XserverRegion       extents;
45     Picture             shadow;
46     int                 shadow_dx;
47     int                 shadow_dy;
48     int                 shadow_width;
49     int                 shadow_height;
50
51     /* for drawing translucent windows */
52     XserverRegion       borderClip;
53     struct _win         *prev_trans;
54 } win;
55
56 win *list;
57
58 Display         *dpy;
59 int             scr;
60 Window          root;
61 Picture         rootPicture;
62 Picture         rootBuffer;
63 Picture         transPicture;
64 Picture         rootTile;
65 XserverRegion   allDamage;
66 int             root_height, root_width;
67
68 #define BACKGROUND_PROP "_XROOTPMAP_ID"
69 #define WINDOW_PLAIN    0
70 #define WINDOW_DROP     1
71 #define WINDOW_TRANS    2
72 #define TRANS_OPACITY   0.75
73 #define SHADOW_RADIUS   15
74 #define SHADOW_OPACITY  0.75
75 #define SHADOW_OFFSET_X (-SHADOW_RADIUS)
76 #define SHADOW_OFFSET_Y (-SHADOW_RADIUS)
77
78
79 double
80 gaussian (double r, double x, double y)
81 {
82     return ((1 / (sqrt (2 * M_PI * r))) *
83             exp ((- (x * x + y * y)) / (2 * r * r)));
84 }
85
86 typedef struct _conv {
87     int     size;
88     double  *data;
89 } conv;
90
91 conv *
92 make_gaussian_map (Display *dpy, double r)
93 {
94     conv            *c;
95     int             size = ((int) ceil ((r * 3)) + 1) & ~1;
96     int             center = size / 2;
97     int             x, y;
98     double          t;
99     double          g;
100     
101     c = malloc (sizeof (conv) + size * size * sizeof (double));
102     c->size = size;
103     c->data = (double *) (c + 1);
104     for (y = 0; y < size; y++)
105         for (x = 0; x < size; x++)
106         {
107             g = gaussian (r, (double) (x - center), (double) (y - center));
108             t += g;
109             c->data[y * size + x] = g;
110         }
111     printf ("gaussian total %f\n", t);
112     for (y = 0; y < size; y++)
113         for (x = 0; x < size; x++)
114         {
115             c->data[y*size + x] /= t;
116         }
117     return c;
118 }
119
120 /*
121  * A picture will help
122  *
123  *      -center   0                width  width+center
124  *  -center +-----+-------------------+-----+
125  *          |     |                   |     |
126  *          |     |                   |     |
127  *        0 +-----+-------------------+-----+
128  *          |     |                   |     |
129  *          |     |                   |     |
130  *          |     |                   |     |
131  *   height +-----+-------------------+-----+
132  *          |     |                   |     |
133  * height+  |     |                   |     |
134  *  center  +-----+-------------------+-----+
135  */
136  
137 unsigned int
138 sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
139 {
140     int     fx, fy;
141     int     sx, sy;
142     double  *g_data;
143     double  *g_line = map->data;
144     int     g_size = map->size;
145     int     center = g_size / 2;
146     int     fx_start, fx_end;
147     int     fy_start, fy_end;
148     double  v;
149     
150     /*
151      * Compute set of filter values which are "in range",
152      * that's the set with:
153      *  0 <= x + (fx-center) && x + (fx-center) < width &&
154      *  0 <= y + (fy-center) && y + (fy-center) < height
155      *
156      *  0 <= x + (fx - center)  x + fx - center < width
157      *  center - x <= fx        fx < width + center - x
158      */
159
160     fx_start = center - x;
161     if (fx_start < 0)
162         fx_start = 0;
163     fx_end = width + center - x;
164     if (fx_end > g_size)
165         fx_end = g_size;
166
167     fy_start = center - y;
168     if (fy_start < 0)
169         fy_start = 0;
170     fy_end = height + center - y;
171     if (fy_end > g_size)
172         fy_end = g_size;
173
174     g_line = g_line + fy_start * g_size + fx_start;
175     
176     v = 0;
177     for (fy = fy_start; fy < fy_end; fy++)
178     {
179         g_data = g_line;
180         g_line += g_size;
181         
182         for (fx = fx_start; fx < fx_end; fx++)
183             v += *g_data++;
184     }
185     if (v > 1)
186         v = 1;
187     
188     return ((unsigned int) (v * opacity * 255.0)) << 24;
189 }
190
191 XImage *
192 make_shadow (Display *dpy, double opacity, double r, int width, int height)
193 {
194     conv            *map = make_gaussian_map (dpy, r);
195     XImage          *ximage;
196     double          *gdata = map->data;
197     unsigned int    *data;
198     int             gsize = map->size;
199     int             ylimit, xlimit;
200     int             swidth = width + gsize;
201     int             sheight = height + gsize;
202     int             center = gsize / 2;
203     int             x, y;
204     int             fx, fy;
205     int             sx, sy;
206     unsigned int    d;
207     double          v;
208     unsigned char   c;
209     
210     data = malloc (swidth * sheight * sizeof (int));
211     ximage = XCreateImage (dpy,
212                            DefaultVisual(dpy, DefaultScreen(dpy)),
213                            32,
214                            ZPixmap,
215                            0,
216                            (char *) data,
217                            swidth, sheight, 32, swidth * sizeof (int));
218     /*
219      * Build the gaussian in sections
220      */
221
222     /*
223      * corners
224      */
225     ylimit = gsize;
226     if (ylimit > sheight / 2)
227         ylimit = (sheight + 1) / 2;
228     xlimit = gsize;
229     if (xlimit > swidth / 2)
230         xlimit = (swidth + 1) / 2;
231
232     for (y = 0; y < ylimit; y++)
233         for (x = 0; x < xlimit; x++)
234         {
235             d = sum_gaussian (map, opacity, x - center, y - center, width, height);
236             data[y * swidth + x] = d;
237             data[(sheight - y - 1) * swidth + x] = d;
238             data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d;
239             data[y * swidth + (swidth - x - 1)] = d;
240         }
241
242     /*
243      * top/bottom
244      */
245     for (y = 0; y < ylimit; y++)
246     {
247         d = sum_gaussian (map, opacity, center, y - center, width, height);
248         for (x = gsize; x < swidth - gsize; x++)
249         {
250             data[y * swidth + x] = d;
251             data[(sheight - y - 1) * swidth + x] = d;
252         }
253     }
254
255     /*
256      * sides
257      */
258     
259     for (x = 0; x < xlimit; x++)
260     {
261         d = sum_gaussian (map, opacity, x - center, center, width, height);
262         for (y = gsize; y < sheight - gsize; y++)
263         {
264             data[y * swidth + x] = d;
265             data[y * swidth + (swidth - x - 1)] = d;
266         }
267     }
268
269     /*
270      * center
271      */
272
273     d = sum_gaussian (map, opacity, center, center, width, height);
274     for (y = ylimit; y < sheight - ylimit; y++)
275         for (x = xlimit; x < swidth - xlimit; x++)
276             data[y * swidth + x] = d;
277
278     free (map);
279     return ximage;
280 }
281
282 Picture
283 shadow_picture (Display *dpy, double opacity, double r, int width, int height, int *wp, int *hp)
284 {
285     XImage  *shadowImage = make_shadow (dpy, opacity, r, width, height);
286     Pixmap  shadowPixmap = XCreatePixmap (dpy, root, 
287                                           shadowImage->width,
288                                           shadowImage->height,
289                                           32);
290     Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
291                                                   XRenderFindStandardFormat (dpy, PictStandardARGB32),
292                                                   0, 0);
293     GC      gc = XCreateGC (dpy, shadowPixmap, 0, 0);
294     
295     XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 
296                shadowImage->width,
297                shadowImage->height);
298     *wp = shadowImage->width;
299     *hp = shadowImage->height;
300     XFreeGC (dpy, gc);
301     XDestroyImage (shadowImage);
302     XFreePixmap (dpy, shadowPixmap);
303     return shadowPicture;
304 }
305
306 win *
307 find_win (Display *dpy, Window id)
308 {
309     win *w;
310
311     for (w = list; w; w = w->next)
312         if (w->id == id)
313             return w;
314     return 0;
315 }
316
317 Picture
318 root_tile (Display *dpy)
319 {
320     Picture         picture;
321     Atom            actual_type;
322     Pixmap          pixmap;
323     int             actual_format;
324     unsigned long   nitems;
325     unsigned long   bytes_after;
326     unsigned char   *prop;
327     Bool            fill;
328     XRenderPictureAttributes    pa;
329
330     if (XGetWindowProperty (dpy, root, XInternAtom (dpy, BACKGROUND_PROP, False),
331                             0, 4, False, AnyPropertyType,
332                             &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success &&
333         actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1)
334     {
335         memcpy (&pixmap, prop, 4);
336         XFree (prop);
337         fill = False;
338     }
339     else
340     {
341         pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
342         fill = True;
343     }
344     pa.repeat = True;
345     picture = XRenderCreatePicture (dpy, pixmap,
346                                     XRenderFindVisualFormat (dpy,
347                                                              DefaultVisual (dpy, scr)),
348                                     CPRepeat, &pa);
349     if (fill)
350     {
351         XRenderColor    c;
352         
353         c.red = c.green = c.blue = 0x8080;
354         c.alpha = 0xffff;
355         XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 
356                               0, 0, 1, 1);
357     }
358     return picture;
359 }
360
361 void
362 paint_root (Display *dpy)
363 {
364     if (!rootTile)
365         rootTile = root_tile (dpy);
366     
367     XRenderComposite (dpy, PictOpSrc,
368                       rootTile, None, rootBuffer,
369                       0, 0, 0, 0, 0, 0, root_width, root_height);
370 }
371
372 XserverRegion
373 win_extents (Display *dpy, win *w)
374 {
375     XRectangle      r;
376     
377     if (!w->shadow)
378     {
379         double  opacity = SHADOW_OPACITY;
380         if (w->mode == WINDOW_TRANS)
381             opacity = opacity * TRANS_OPACITY;
382         w->shadow = shadow_picture (dpy, opacity, SHADOW_RADIUS, 
383                                     w->a.width, w->a.height,
384                                     &w->shadow_width, &w->shadow_height);
385         w->shadow_dx = SHADOW_OFFSET_X;
386         w->shadow_dy = SHADOW_OFFSET_Y;
387     }
388     r.x = w->a.x + w->a.border_width + w->shadow_dx;
389     r.y = w->a.y + w->a.border_width + w->shadow_dy;
390     r.width = w->shadow_width;
391     r.height = w->shadow_height;
392     return XFixesCreateRegion (dpy, &r, 1);
393 }
394
395 XserverRegion
396 border_size (Display *dpy, win *w)
397 {
398     XserverRegion   border;
399     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
400     /* translate this */
401     XFixesUnionRegion (dpy, border, border, w->a.x, w->a.y, None, 0, 0);
402     return border;
403 }
404
405 void
406 paint_all (Display *dpy, XserverRegion region)
407 {
408     win *w;
409     win *t = 0;
410     
411     if (!region)
412     {
413         XRectangle  r;
414         r.x = 0;
415         r.y = 0;
416         r.width = root_width;
417         r.height = root_height;
418         region = XFixesCreateRegion (dpy, &r, 1);
419     }
420     if (!rootBuffer)
421     {
422         Pixmap  rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
423                                             DefaultDepth (dpy, scr));
424         rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
425                                            XRenderFindVisualFormat (dpy,
426                                                                     DefaultVisual (dpy, scr)),
427                                            0, 0);
428         XFreePixmap (dpy, rootPixmap);
429     }
430     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
431     for (w = list; w; w = w->next)
432     {
433         Picture mask;
434         
435         if (w->a.map_state != IsViewable)
436             continue;
437         if (!w->picture)
438             continue;
439         
440         if (w->borderSize)
441             XFixesDestroyRegion (dpy, w->borderSize);
442         w->borderSize = border_size (dpy, w);
443         if (w->extents)
444             XFixesDestroyRegion (dpy, w->extents);
445         w->extents = win_extents (dpy, w);
446         if (w->mode != WINDOW_TRANS)
447         {
448             XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
449             XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0);
450             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
451                               0, 0, 0, 0, 
452                               w->a.x + w->a.border_width,
453                               w->a.y + w->a.border_width,
454                               w->a.width,
455                               w->a.height);
456         }
457         w->borderClip = XFixesCreateRegion (dpy, 0, 0);
458         XFixesUnionRegion (dpy, w->borderClip, region, 0, 0, None, 0, 0);
459         w->prev_trans = t;
460         t = w;
461     }
462     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
463     paint_root (dpy);
464     for (w = t; w; w = w->prev_trans)
465     {
466         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
467         if (w->shadow)
468         {
469             XRenderComposite (dpy, PictOpOver, w->shadow, None, rootBuffer,
470                               0, 0, 0, 0,
471                               w->a.x + w->a.border_width + w->shadow_dx,
472                               w->a.y + w->a.border_width + w->shadow_dy,
473                               w->shadow_width, w->shadow_height);
474         }
475         if (w->mode == WINDOW_TRANS)
476             XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootBuffer,
477                               0, 0, 0, 0, 
478                               w->a.x + w->a.border_width,
479                               w->a.y + w->a.border_width,
480                               w->a.width,
481                               w->a.height);
482         XFixesDestroyRegion (dpy, w->borderClip);
483         w->borderClip = None;
484     }
485     XFixesDestroyRegion (dpy, region);
486     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
487     XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
488                       0, 0, 0, 0, 0, 0, root_width, root_height);
489 }
490
491 void
492 add_damage (Display *dpy, XserverRegion damage)
493 {
494     if (allDamage)
495     {
496         XFixesUnionRegion (dpy, allDamage, allDamage, 0, 0, damage, 0, 0);
497         XFixesDestroyRegion (dpy, damage);
498     }
499     else
500         allDamage = damage;
501 }
502
503 void
504 repair_win (Display *dpy, Window id)
505 {
506     win             *w = find_win (dpy, id);
507     XserverRegion   parts;
508
509     if (!w)
510         return;
511 /*    printf ("repair 0x%x\n", w->id); */
512     parts = XFixesCreateRegion (dpy, 0, 0);
513     /* translate region */
514     XDamageSubtract (dpy, w->damage, None, parts);
515     XFixesUnionRegion (dpy, parts, parts, w->a.x, w->a.y, None, 0, 0);
516     add_damage (dpy, parts);
517 }
518
519 void
520 map_win (Display *dpy, Window id)
521 {
522     win             *w = find_win (dpy, id);
523     XserverRegion   region;
524
525     if (!w)
526         return;
527     w->a.map_state = IsViewable;
528     if (w->picture)
529     {
530         w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
531         region = win_extents (dpy, w);
532         add_damage (dpy, region);
533     }
534 }
535
536 void
537 unmap_win (Display *dpy, Window id)
538 {
539     win *w = find_win (dpy, id);
540
541     if (!w)
542         return;
543     w->a.map_state = IsUnmapped;
544     if (w->damage != None)
545     {
546         XDamageDestroy (dpy, w->damage);
547         w->damage = None;
548     }
549     if (w->extents != None)
550     {
551         add_damage (dpy, w->extents);    /* destroys region */
552         w->extents = None;
553     }
554 }
555
556 void
557 add_win (Display *dpy, Window id, Window prev)
558 {
559     win *new = malloc (sizeof (win));
560     win **p;
561     XWindowAttributes a;
562     XRenderPictureAttributes pa;
563     
564     if (!new)
565         return;
566     if (prev)
567     {
568         for (p = &list; *p; p = &(*p)->next)
569             if ((*p)->id == prev)
570                 break;
571     }
572     else
573         p = &list;
574     new->id = id;
575     if (!XGetWindowAttributes (dpy, id, &new->a))
576     {
577         free (new);
578         return;
579     }
580     new->damaged = 0;
581     new->damage = None;
582     pa.subwindow_mode = IncludeInferiors;
583     if (new->a.class == InputOnly)
584         new->picture = 0;
585     else
586     {
587         new->picture = XRenderCreatePicture (dpy, id,
588                                              XRenderFindVisualFormat (dpy, 
589                                                                       new->a.visual),
590                                              CPSubwindowMode,
591                                              &pa);
592     }
593                                          
594     new->shadow = None;
595     new->borderSize = None;
596     new->extents = None;
597     if (new->a.override_redirect)
598         new->mode = WINDOW_TRANS;
599     else
600         new->mode = WINDOW_DROP;
601     new->next = *p;
602     *p = new;
603     if (new->a.map_state == IsViewable)
604         map_win (dpy, id);
605 }
606
607 void
608 configure_win (Display *dpy, XConfigureEvent *ce)
609 {
610     win             *w = find_win (dpy, ce->window);
611     Window          above;
612     XserverRegion   damage = None;
613     
614     if (!w)
615     {
616         if (ce->window == root)
617         {
618             if (rootBuffer)
619             {
620                 XRenderFreePicture (dpy, rootBuffer);
621                 rootBuffer = None;
622             }
623             root_width = ce->width;
624             root_height = ce->height;
625         }
626         return;
627     }
628     if (w->a.map_state == IsViewable)
629     {
630         damage = XFixesCreateRegion (dpy, 0, 0);
631         if (w->extents != None) 
632             XFixesUnionRegion (dpy, damage, w->extents, 0, 0, None, 0, 0);
633     }
634     w->a.x = ce->x;
635     w->a.y = ce->y;
636     if (w->a.width != ce->width || w->a.height != ce->height)
637         if (w->shadow)
638         {
639             XRenderFreePicture (dpy, w->shadow);
640             w->shadow = None;
641         }
642     w->a.width = ce->width;
643     w->a.height = ce->height;
644     w->a.border_width = ce->border_width;
645     w->a.override_redirect = ce->override_redirect;
646     if (w->next)
647         above = w->next->id;
648     else
649         above = None;
650     if (above != ce->above)
651     {
652         win **prev;
653
654         /* unhook */
655         for (prev = &list; *prev; prev = &(*prev)->next)
656             if ((*prev) == w)
657                 break;
658         *prev = w->next;
659         
660         /* rehook */
661         for (prev = &list; *prev; prev = &(*prev)->next)
662         {
663             if ((*prev)->id == ce->above)
664                 break;
665         }
666         w->next = *prev;
667         *prev = w;
668     }
669     if (damage)
670     {
671         XserverRegion   extents = win_extents (dpy, w);
672         XFixesUnionRegion (dpy, damage, damage, 0, 0, extents, 0, 0);
673         XFixesDestroyRegion (dpy, extents);
674         add_damage (dpy, damage);
675     }
676 }
677
678 void
679 destroy_win (Display *dpy, Window id, Bool gone)
680 {
681     win **prev, *w;
682
683     for (prev = &list; w = *prev; prev = &w->next)
684         if (w->id == id)
685         {
686             if (!gone)
687             {
688                 unmap_win (dpy, id);
689                 if (w->picture)
690                     XRenderFreePicture (dpy, w->picture);
691             }
692             *prev = w->next;
693             free (w);
694             break;
695         }
696 }
697
698 void
699 dump_win (win *w)
700 {
701     printf ("\t%08x: %d x %d + %d + %d (%d)\n", w->id,
702             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
703 }
704
705 void
706 dump_wins (void)
707 {
708     win *w;
709
710     printf ("windows:\n");
711     for (w = list; w; w = w->next)
712         dump_win (w);
713 }
714
715 void
716 damage_win (Display *dpy, XDamageNotifyEvent *de)
717 {
718     repair_win (dpy, de->drawable);
719 }
720
721 int
722 error (Display *dpy, XErrorEvent *ev)
723 {
724     printf ("error %d request %d minor %d\n",
725             ev->error_code, ev->request_code, ev->minor_code);
726 }
727
728 void
729 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
730 {
731     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
732     
733     add_damage (dpy, region);
734 }
735
736 int
737 time_in_millis ()
738 {
739     struct timeval  tp;
740
741     gettimeofday (&tp, 0);
742     return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
743 }
744
745 #define INTERVAL    30
746
747 main ()
748 {
749     XEvent          ev;
750     int             event_base, error_base;
751     Window          root_return, parent_return;
752     Window          *children;
753     Pixmap          transPixmap;
754     unsigned int    nchildren;
755     int             i;
756     int             damage_event, damage_error;
757     int             xfixes_event, xfixes_error;
758     XRenderPictureAttributes    pa;
759     XRenderColor                c;
760     XRectangle      *expose_rects = 0;
761     GC              gc;
762     int             size_expose = 0;
763     int             n_expose = 0;
764     struct pollfd   ufd;
765     int             n;
766     int             last_update;
767     int             now;
768     int             timeout;
769
770     dpy = XOpenDisplay (0);
771     if (!dpy)
772     {
773         fprintf (stderr, "Can't open display\n");
774         exit (1);
775     }
776     XSetErrorHandler (error);
777     scr = DefaultScreen (dpy);
778     root = RootWindow (dpy, scr);
779     pa.subwindow_mode = IncludeInferiors;
780     transPixmap = XCreatePixmap (dpy, root, 1, 1, 8);
781     pa.repeat = True;
782     transPicture = XRenderCreatePicture (dpy, transPixmap,
783                                          XRenderFindStandardFormat (dpy, PictStandardA8),
784                                          CPRepeat,
785                                          &pa);
786     c.red = c.green = c.blue = 0;
787     c.alpha = 0xc0c0;
788     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
789     
790     root_width = DisplayWidth (dpy, scr);
791     root_height = DisplayHeight (dpy, scr);
792     
793     rootPicture = XRenderCreatePicture (dpy, root, 
794                                         XRenderFindVisualFormat (dpy,
795                                                                  DefaultVisual (dpy, scr)),
796                                         CPSubwindowMode,
797                                         &pa);
798     if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
799     {
800         fprintf (stderr, "No composite extension\n");
801         exit (1);
802     }
803     printf ("Composite error %d\n", error_base);
804     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
805     {
806         fprintf (stderr, "No damage extension\n");
807         exit (1);
808     }
809     printf ("Damage error %d\n", damage_error);
810     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
811     {
812         fprintf (stderr, "No XFixes extension\n");
813         exit (1);
814     }
815     printf ("XFixes error %d\n", xfixes_error);
816     allDamage = None;
817     XGrabServer (dpy);
818     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
819     paint_all (dpy, None);
820     XSelectInput (dpy, root, 
821                   SubstructureNotifyMask|
822                   ExposureMask|
823                   StructureNotifyMask|
824                   PropertyChangeMask);
825     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
826     for (i = 0; i < nchildren; i++)
827         add_win (dpy, children[i], i ? children[i-1] : None);
828     XFree (children);
829     XUngrabServer (dpy);
830     last_update = time_in_millis ();
831     for (;;)
832     {
833 /*      dump_wins (); */
834         do {
835             XNextEvent (dpy, &ev);
836 /*          printf ("event %d\n", ev.type); */
837             switch (ev.type) {
838             case CreateNotify:
839                 add_win (dpy, ev.xcreatewindow.window, 0);
840                 break;
841             case ConfigureNotify:
842                 configure_win (dpy, &ev.xconfigure);
843                 break;
844             case DestroyNotify:
845                 destroy_win (dpy, ev.xdestroywindow.window, True);
846                 break;
847             case MapNotify:
848                 map_win (dpy, ev.xmap.window);
849                 break;
850             case UnmapNotify:
851                 unmap_win (dpy, ev.xunmap.window);
852                 break;
853             case ReparentNotify:
854                 if (ev.xreparent.parent == root)
855                     add_win (dpy, ev.xreparent.window, 0);
856                 else
857                     destroy_win (dpy, ev.xreparent.window, False);
858                 break;
859             case Expose:
860                 if (ev.xexpose.window == root)
861                 {
862                     int more = ev.xexpose.count + 1;
863                     if (n_expose == size_expose)
864                     {
865                         if (expose_rects)
866                         {
867                             expose_rects = realloc (expose_rects, 
868                                                     (size_expose + more) * 
869                                                     sizeof (XRectangle));
870                             size_expose += more;
871                         }
872                         else
873                         {
874                             expose_rects = malloc (more * sizeof (XRectangle));
875                             size_expose = more;
876                         }
877                     }
878                     expose_rects[n_expose].x = ev.xexpose.x;
879                     expose_rects[n_expose].y = ev.xexpose.y;
880                     expose_rects[n_expose].width = ev.xexpose.width;
881                     expose_rects[n_expose].height = ev.xexpose.height;
882                     n_expose++;
883                     if (ev.xexpose.count == 0)
884                     {
885                         expose_root (dpy, root, expose_rects, n_expose);
886                         n_expose = 0;
887                     }
888                 }
889                 break;
890             case PropertyNotify:
891                 if (ev.xproperty.atom == XInternAtom (dpy, BACKGROUND_PROP, False))
892                 {
893                     if (rootTile)
894                     {
895                         XClearArea (dpy, root, 0, 0, 0, 0, True);
896                         XRenderFreePicture (dpy, rootTile);
897                         rootTile = None;
898                     }
899                 }
900                 break;
901             default:
902                 if (ev.type == damage_event + XDamageNotify)
903                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
904                 break;
905             }
906         } while (XEventsQueued (dpy, QueuedAfterReading));
907         now = time_in_millis ();
908         timeout = INTERVAL - (now - last_update);
909         if (timeout > 0)
910         {
911             ufd.fd = ConnectionNumber (dpy);
912             ufd.events = POLLIN;
913             n = poll (&ufd, 1, timeout);
914             if (n > 0 && (ufd.revents & POLLIN) && XEventsQueued (dpy, QueuedAfterReading))
915                 continue;
916         }
917         last_update = time_in_millis();
918         if (allDamage)
919         {
920             paint_all (dpy, allDamage);
921             allDamage = None;
922         }
923     }
924 }