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