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