Made gussian_map get called just once rather than N times
[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 typedef struct _conv {
57     int     size;
58     double  *data;
59 } conv;
60
61 win             *list;
62 Display         *dpy;
63 int             scr;
64 Window          root;
65 Picture         rootPicture;
66 Picture         rootBuffer;
67 Picture         transPicture;
68 Picture         blackPicture;
69 Picture         rootTile;
70 XserverRegion   allDamage;
71 int             root_height, root_width;
72 conv            *gussianMap;
73
74 #define BACKGROUND_PROP "_XROOTPMAP_ID"
75
76 #define WINDOW_SOLID    0
77 #define WINDOW_TRANS    1
78 #define WINDOW_ARGB     2
79
80 #define TRANS_OPACITY   0.75
81 #define SHADOW_RADIUS   15
82 #define SHADOW_OPACITY  0.75
83 #define SHADOW_OFFSET_X (-SHADOW_RADIUS)
84 #define SHADOW_OFFSET_Y (-SHADOW_RADIUS)
85
86
87 double
88 gaussian (double r, double x, double y)
89 {
90     return ((1 / (sqrt (2 * M_PI * r))) *
91             exp ((- (x * x + y * y)) / (2 * r * r)));
92 }
93
94
95 conv *
96 make_gaussian_map (Display *dpy, double r)
97 {
98     conv            *c;
99     int             size = ((int) ceil ((r * 3)) + 1) & ~1;
100     int             center = size / 2;
101     int             x, y;
102     double          t;
103     double          g;
104     
105     c = malloc (sizeof (conv) + size * size * sizeof (double));
106     c->size = size;
107     c->data = (double *) (c + 1);
108     for (y = 0; y < size; y++)
109         for (x = 0; x < size; x++)
110         {
111             g = gaussian (r, (double) (x - center), (double) (y - center));
112             t += g;
113             c->data[y * size + x] = g;
114         }
115 /*    printf ("gaussian total %f\n", t); */
116     for (y = 0; y < size; y++)
117         for (x = 0; x < size; x++)
118         {
119             c->data[y*size + x] /= t;
120         }
121     return c;
122 }
123
124 /*
125  * A picture will help
126  *
127  *      -center   0                width  width+center
128  *  -center +-----+-------------------+-----+
129  *          |     |                   |     |
130  *          |     |                   |     |
131  *        0 +-----+-------------------+-----+
132  *          |     |                   |     |
133  *          |     |                   |     |
134  *          |     |                   |     |
135  *   height +-----+-------------------+-----+
136  *          |     |                   |     |
137  * height+  |     |                   |     |
138  *  center  +-----+-------------------+-----+
139  */
140  
141 unsigned char
142 sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
143 {
144     int     fx, fy;
145     int     sx, sy;
146     double  *g_data;
147     double  *g_line = map->data;
148     int     g_size = map->size;
149     int     center = g_size / 2;
150     int     fx_start, fx_end;
151     int     fy_start, fy_end;
152     double  v;
153     
154     /*
155      * Compute set of filter values which are "in range",
156      * that's the set with:
157      *  0 <= x + (fx-center) && x + (fx-center) < width &&
158      *  0 <= y + (fy-center) && y + (fy-center) < height
159      *
160      *  0 <= x + (fx - center)  x + fx - center < width
161      *  center - x <= fx        fx < width + center - x
162      */
163
164     fx_start = center - x;
165     if (fx_start < 0)
166         fx_start = 0;
167     fx_end = width + center - x;
168     if (fx_end > g_size)
169         fx_end = g_size;
170
171     fy_start = center - y;
172     if (fy_start < 0)
173         fy_start = 0;
174     fy_end = height + center - y;
175     if (fy_end > g_size)
176         fy_end = g_size;
177
178     g_line = g_line + fy_start * g_size + fx_start;
179     
180     v = 0;
181     for (fy = fy_start; fy < fy_end; fy++)
182     {
183         g_data = g_line;
184         g_line += g_size;
185         
186         for (fx = fx_start; fx < fx_end; fx++)
187             v += *g_data++;
188     }
189     if (v > 1)
190         v = 1;
191     
192     return ((unsigned int) (v * opacity * 255.0));
193 }
194
195 XImage *
196 make_shadow (Display *dpy, double opacity, int width, int height)
197 {
198     XImage          *ximage;
199     double          *gdata = gussianMap->data;
200     unsigned char   *data;
201     int             gsize = gussianMap->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 (gussianMap, 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 (gussianMap, 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 (gussianMap, 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 (gussianMap, 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     return ximage;
282 }
283
284 Picture
285 shadow_picture (Display *dpy, double opacity, int width, int height, int *wp, int *hp)
286 {
287     XImage  *shadowImage = make_shadow (dpy, opacity, width, height);
288     Pixmap  shadowPixmap = XCreatePixmap (dpy, root, 
289                                           shadowImage->width,
290                                           shadowImage->height,
291                                           8);
292     Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
293                                                   XRenderFindStandardFormat (dpy, PictStandardA8),
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, 
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, blackPicture, w->shadow, 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    0
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     Pixmap          blackPixmap;
780     unsigned int    nchildren;
781     int             i;
782     int             damage_event, damage_error;
783     int             xfixes_event, xfixes_error;
784     XRenderPictureAttributes    pa;
785     XRenderColor                c;
786     XRectangle      *expose_rects = 0;
787     GC              gc;
788     int             size_expose = 0;
789     int             n_expose = 0;
790     struct pollfd   ufd;
791     int             n;
792     int             last_update;
793     int             now;
794 #if INTERVAL
795     int             timeout;
796 #endif
797
798     dpy = XOpenDisplay (0);
799     if (!dpy)
800     {
801         fprintf (stderr, "Can't open display\n");
802         exit (1);
803     }
804     XSetErrorHandler (error);
805     scr = DefaultScreen (dpy);
806     root = RootWindow (dpy, scr);
807     pa.subwindow_mode = IncludeInferiors;
808
809     gussianMap = make_gaussian_map(dpy, SHADOW_RADIUS);
810
811     transPixmap = XCreatePixmap (dpy, root, 1, 1, 8);
812     pa.repeat = True;
813     transPicture = XRenderCreatePicture (dpy, transPixmap,
814                                          XRenderFindStandardFormat (dpy, PictStandardA8),
815                                          CPRepeat,
816                                          &pa);
817     c.red = c.green = c.blue = 0;
818     c.alpha = 0xc0c0;
819     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
820     
821     root_width = DisplayWidth (dpy, scr);
822     root_height = DisplayHeight (dpy, scr);
823     
824     rootPicture = XRenderCreatePicture (dpy, root, 
825                                         XRenderFindVisualFormat (dpy,
826                                                                  DefaultVisual (dpy, scr)),
827                                         CPSubwindowMode,
828                                         &pa);
829     blackPixmap = XCreatePixmap (dpy, root, 1, 1, 32);
830     pa.repeat = True;
831     blackPicture = XRenderCreatePicture (dpy, blackPixmap,
832                                          XRenderFindStandardFormat (dpy, PictStandardARGB32),
833                                          CPRepeat,
834                                          &pa);
835     c.red = c.green = c.blue = 0;
836     c.alpha = 0xffff;
837     XRenderFillRectangle (dpy, PictOpSrc, blackPicture, &c, 0, 0, 1, 1);
838     if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
839     {
840         fprintf (stderr, "No composite extension\n");
841         exit (1);
842     }
843     printf ("Composite error %d\n", error_base);
844     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
845     {
846         fprintf (stderr, "No damage extension\n");
847         exit (1);
848     }
849     printf ("Damage error %d\n", damage_error);
850     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
851     {
852         fprintf (stderr, "No XFixes extension\n");
853         exit (1);
854     }
855     printf ("XFixes error %d\n", xfixes_error);
856     allDamage = None;
857     XGrabServer (dpy);
858     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
859     XSelectInput (dpy, root, 
860                   SubstructureNotifyMask|
861                   ExposureMask|
862                   StructureNotifyMask|
863                   PropertyChangeMask);
864     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
865     for (i = 0; i < nchildren; i++)
866         add_win (dpy, children[i], i ? children[i-1] : None);
867     XFree (children);
868     XUngrabServer (dpy);
869     paint_all (dpy, None);
870 #if INTERVAL
871     last_update = time_in_millis ();
872 #endif
873     for (;;)
874     {
875 #if INTERVAL
876         int busy_start = 0;
877 #endif
878 /*      dump_wins (); */
879         do {
880             XNextEvent (dpy, &ev);
881 #if INTERVAL
882             if (!busy_start)
883                 busy_start = time_in_millis();
884 #endif
885 /*          printf ("event %d\n", ev.type); */
886             switch (ev.type) {
887             case CreateNotify:
888                 add_win (dpy, ev.xcreatewindow.window, 0);
889                 break;
890             case ConfigureNotify:
891                 configure_win (dpy, &ev.xconfigure);
892                 break;
893             case DestroyNotify:
894                 destroy_win (dpy, ev.xdestroywindow.window, True);
895                 break;
896             case MapNotify:
897                 map_win (dpy, ev.xmap.window);
898                 break;
899             case UnmapNotify:
900                 unmap_win (dpy, ev.xunmap.window);
901                 break;
902             case ReparentNotify:
903                 if (ev.xreparent.parent == root)
904                     add_win (dpy, ev.xreparent.window, 0);
905                 else
906                     destroy_win (dpy, ev.xreparent.window, False);
907                 break;
908             case Expose:
909                 if (ev.xexpose.window == root)
910                 {
911                     int more = ev.xexpose.count + 1;
912                     if (n_expose == size_expose)
913                     {
914                         if (expose_rects)
915                         {
916                             expose_rects = realloc (expose_rects, 
917                                                     (size_expose + more) * 
918                                                     sizeof (XRectangle));
919                             size_expose += more;
920                         }
921                         else
922                         {
923                             expose_rects = malloc (more * sizeof (XRectangle));
924                             size_expose = more;
925                         }
926                     }
927                     expose_rects[n_expose].x = ev.xexpose.x;
928                     expose_rects[n_expose].y = ev.xexpose.y;
929                     expose_rects[n_expose].width = ev.xexpose.width;
930                     expose_rects[n_expose].height = ev.xexpose.height;
931                     n_expose++;
932                     if (ev.xexpose.count == 0)
933                     {
934                         expose_root (dpy, root, expose_rects, n_expose);
935                         n_expose = 0;
936                     }
937                 }
938                 break;
939             case PropertyNotify:
940                 if (ev.xproperty.atom == XInternAtom (dpy, BACKGROUND_PROP, False))
941                 {
942                     if (rootTile)
943                     {
944                         XClearArea (dpy, root, 0, 0, 0, 0, True);
945                         XRenderFreePicture (dpy, rootTile);
946                         rootTile = None;
947                     }
948                 }
949                 break;
950             default:
951                 if (ev.type == damage_event + XDamageNotify)
952                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
953                 break;
954             }
955         } while (XEventsQueued (dpy, QueuedAfterReading));
956 #if INTERVAL
957         now = time_in_millis ();
958 /*      printf ("\t\tbusy %d\n", now - busy_start); */
959         timeout = INTERVAL - (now - last_update);
960         if (timeout > 0)
961         {
962             ufd.fd = ConnectionNumber (dpy);
963             ufd.events = POLLIN;
964             n = poll (&ufd, 1, timeout);
965             if (n > 0 && (ufd.revents & POLLIN) && XEventsQueued (dpy, QueuedAfterReading))
966                 continue;
967         }
968 #endif
969         if (allDamage)
970         {
971 #if INTERVAL
972             int old_update = last_update;
973             last_update = time_in_millis();
974 /*          printf ("delta %d\n", last_update - old_update); */
975 #endif
976             paint_all (dpy, allDamage);
977             allDamage = None;
978         }
979     }
980 }