Rename gussianMap to gaussianMap (who the heck is gus?) Allow for other
[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 <string.h>
28 #include <math.h>
29 #include <sys/poll.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/extensions/Xcomposite.h>
35 #include <X11/extensions/Xdamage.h>
36 #include <X11/extensions/Xrender.h>
37
38 typedef struct _win {
39     struct _win         *next;
40     Window              id;
41     XWindowAttributes   a;
42     int                 mode;
43     int                 damaged;
44     Damage              damage;
45     Picture             picture;
46     XserverRegion       borderSize;
47     XserverRegion       extents;
48     Picture             shadow;
49     int                 shadow_dx;
50     int                 shadow_dy;
51     int                 shadow_width;
52     int                 shadow_height;
53
54     unsigned long       damage_sequence;    /* sequence when damage was created */
55
56     /* for drawing translucent windows */
57     XserverRegion       borderClip;
58     struct _win         *prev_trans;
59 } win;
60
61 typedef struct _conv {
62     int     size;
63     double  *data;
64 } conv;
65
66 win             *list;
67 Display         *dpy;
68 int             scr;
69 Window          root;
70 Picture         rootPicture;
71 Picture         rootBuffer;
72 Picture         transPicture;
73 Picture         blackPicture;
74 Picture         rootTile;
75 XserverRegion   allDamage;
76 int             root_height, root_width;
77 conv            *gaussianMap;
78
79 #define WINDOW_SOLID    0
80 #define WINDOW_TRANS    1
81 #define WINDOW_ARGB     2
82
83 #define TRANS_OPACITY   0.75
84 #define SHADOW_RADIUS   15
85 #define SHADOW_OPACITY  0.75
86 #define SHADOW_OFFSET_X (-SHADOW_RADIUS)
87 #define SHADOW_OFFSET_Y (-SHADOW_RADIUS)
88
89 #define DEBUG 0
90 #define MONITOR_REPAINT 0
91
92 static double
93 gaussian (double r, double x, double y)
94 {
95     return ((1 / (sqrt (2 * M_PI * r))) *
96             exp ((- (x * x + y * y)) / (2 * r * r)));
97 }
98
99
100 static conv *
101 make_gaussian_map (Display *dpy, double r)
102 {
103     conv            *c;
104     int             size = ((int) ceil ((r * 3)) + 1) & ~1;
105     int             center = size / 2;
106     int             x, y;
107     double          t;
108     double          g;
109     
110     c = malloc (sizeof (conv) + size * size * sizeof (double));
111     c->size = size;
112     c->data = (double *) (c + 1);
113     for (y = 0; y < size; y++)
114         for (x = 0; x < size; x++)
115         {
116             g = gaussian (r, (double) (x - center), (double) (y - center));
117             t += g;
118             c->data[y * size + x] = g;
119         }
120 /*    printf ("gaussian total %f\n", t); */
121     for (y = 0; y < size; y++)
122         for (x = 0; x < size; x++)
123         {
124             c->data[y*size + x] /= t;
125         }
126     return c;
127 }
128
129 /*
130  * A picture will help
131  *
132  *      -center   0                width  width+center
133  *  -center +-----+-------------------+-----+
134  *          |     |                   |     |
135  *          |     |                   |     |
136  *        0 +-----+-------------------+-----+
137  *          |     |                   |     |
138  *          |     |                   |     |
139  *          |     |                   |     |
140  *   height +-----+-------------------+-----+
141  *          |     |                   |     |
142  * height+  |     |                   |     |
143  *  center  +-----+-------------------+-----+
144  */
145  
146 static unsigned char
147 sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
148 {
149     int     fx, fy;
150     double  *g_data;
151     double  *g_line = map->data;
152     int     g_size = map->size;
153     int     center = g_size / 2;
154     int     fx_start, fx_end;
155     int     fy_start, fy_end;
156     double  v;
157     
158     /*
159      * Compute set of filter values which are "in range",
160      * that's the set with:
161      *  0 <= x + (fx-center) && x + (fx-center) < width &&
162      *  0 <= y + (fy-center) && y + (fy-center) < height
163      *
164      *  0 <= x + (fx - center)  x + fx - center < width
165      *  center - x <= fx        fx < width + center - x
166      */
167
168     fx_start = center - x;
169     if (fx_start < 0)
170         fx_start = 0;
171     fx_end = width + center - x;
172     if (fx_end > g_size)
173         fx_end = g_size;
174
175     fy_start = center - y;
176     if (fy_start < 0)
177         fy_start = 0;
178     fy_end = height + center - y;
179     if (fy_end > g_size)
180         fy_end = g_size;
181
182     g_line = g_line + fy_start * g_size + fx_start;
183     
184     v = 0;
185     for (fy = fy_start; fy < fy_end; fy++)
186     {
187         g_data = g_line;
188         g_line += g_size;
189         
190         for (fx = fx_start; fx < fx_end; fx++)
191             v += *g_data++;
192     }
193     if (v > 1)
194         v = 1;
195     
196     return ((unsigned int) (v * opacity * 255.0));
197 }
198
199 static XImage *
200 make_shadow (Display *dpy, double opacity, int width, int height)
201 {
202     XImage          *ximage;
203     unsigned char   *data;
204     int             gsize = gaussianMap->size;
205     int             ylimit, xlimit;
206     int             swidth = width + gsize;
207     int             sheight = height + gsize;
208     int             center = gsize / 2;
209     int             x, y;
210     unsigned char   d;
211     
212     data = malloc (swidth * sheight * sizeof (unsigned char));
213     ximage = XCreateImage (dpy,
214                            DefaultVisual(dpy, DefaultScreen(dpy)),
215                            8,
216                            ZPixmap,
217                            0,
218                            (char *) data,
219                            swidth, sheight, 8, swidth * sizeof (unsigned char));
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 (gaussianMap, 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 (gaussianMap, 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 (gaussianMap, 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 (gaussianMap, 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     return ximage;
281 }
282
283 static Picture
284 shadow_picture (Display *dpy, double opacity, int width, int height, int *wp, int *hp)
285 {
286     XImage  *shadowImage = make_shadow (dpy, opacity, width, height);
287     Pixmap  shadowPixmap = XCreatePixmap (dpy, root, 
288                                           shadowImage->width,
289                                           shadowImage->height,
290                                           8);
291     Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
292                                                   XRenderFindStandardFormat (dpy, PictStandardA8),
293                                                   0, 0);
294     GC      gc = XCreateGC (dpy, shadowPixmap, 0, 0);
295     
296     XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 
297                shadowImage->width,
298                shadowImage->height);
299     *wp = shadowImage->width;
300     *hp = shadowImage->height;
301     XFreeGC (dpy, gc);
302     XDestroyImage (shadowImage);
303     XFreePixmap (dpy, shadowPixmap);
304     return shadowPicture;
305 }
306
307 static win *
308 find_win (Display *dpy, Window id)
309 {
310     win *w;
311
312     for (w = list; w; w = w->next)
313         if (w->id == id)
314             return w;
315     return 0;
316 }
317
318 static char *backgroundProps[] = {
319     "_XROOTPMAP_ID",
320     "_XSETROOT_ID",
321     0,
322 };
323     
324 static Picture
325 root_tile (Display *dpy)
326 {
327     Picture         picture;
328     Atom            actual_type;
329     Pixmap          pixmap;
330     int             actual_format;
331     unsigned long   nitems;
332     unsigned long   bytes_after;
333     unsigned char   *prop;
334     Bool            fill;
335     XRenderPictureAttributes    pa;
336     int             p;
337
338     for (p = 0; backgroundProps[p]; p++)
339     {
340         if (XGetWindowProperty (dpy, root, XInternAtom (dpy, backgroundProps[p], False),
341                                 0, 4, False, AnyPropertyType,
342                                 &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success &&
343             actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1)
344         {
345             memcpy (&pixmap, prop, 4);
346             XFree (prop);
347             fill = False;
348             break;
349         }
350     }
351     if (!backgroundProps[p])
352     {
353         pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
354         fill = True;
355     }
356     pa.repeat = True;
357     picture = XRenderCreatePicture (dpy, pixmap,
358                                     XRenderFindVisualFormat (dpy,
359                                                              DefaultVisual (dpy, scr)),
360                                     CPRepeat, &pa);
361     if (fill)
362     {
363         XRenderColor    c;
364         
365         c.red = c.green = c.blue = 0x8080;
366         c.alpha = 0xffff;
367         XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 
368                               0, 0, 1, 1);
369     }
370     return picture;
371 }
372
373 static void
374 paint_root (Display *dpy)
375 {
376     if (!rootTile)
377         rootTile = root_tile (dpy);
378     
379     XRenderComposite (dpy, PictOpSrc,
380                       rootTile, None, rootBuffer,
381                       0, 0, 0, 0, 0, 0, root_width, root_height);
382 }
383
384 static XserverRegion
385 win_extents (Display *dpy, win *w)
386 {
387     XRectangle      r;
388     
389     r.x = w->a.x;
390     r.y = w->a.y;
391     r.width = w->a.width + w->a.border_width * 2;
392     r.height = w->a.height + w->a.border_width * 2;
393     if (w->mode != WINDOW_ARGB)
394     {
395         XRectangle  sr;
396         
397         if (!w->shadow)
398         {
399             double      opacity = SHADOW_OPACITY;
400             if (w->mode == WINDOW_TRANS)
401                 opacity = opacity * TRANS_OPACITY;
402             w->shadow = shadow_picture (dpy, opacity,
403                                         w->a.width + w->a.border_width * 2,
404                                         w->a.height + w->a.border_width * 2,
405                                         &w->shadow_width, &w->shadow_height);
406             w->shadow_dx = SHADOW_OFFSET_X;
407             w->shadow_dy = SHADOW_OFFSET_Y;
408         }
409         sr.x = w->a.x + w->shadow_dx;
410         sr.y = w->a.y + w->shadow_dy;
411         sr.width = w->shadow_width;
412         sr.height = w->shadow_height;
413         if (sr.x < r.x)
414         {
415             r.width = (r.x + r.width) - sr.x;
416             r.x = sr.x;
417         }
418         if (sr.y < r.y)
419         {
420             r.height = (r.y + r.height) - sr.y;
421             r.y = sr.y;
422         }
423         if (sr.width > r.width)
424             r.width = sr.width;
425         if (sr.height > r.height)
426             r.height = sr.height;
427     }
428     return XFixesCreateRegion (dpy, &r, 1);
429 }
430
431 static XserverRegion
432 border_size (Display *dpy, win *w)
433 {
434     XserverRegion   border;
435     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
436     /* translate this */
437     XFixesTranslateRegion (dpy, border,
438                            w->a.x + w->a.border_width,
439                            w->a.y + w->a.border_width);
440     return border;
441 }
442
443 static void
444 paint_all (Display *dpy, XserverRegion region)
445 {
446     win *w;
447     win *t = 0;
448     
449     if (!region)
450     {
451         XRectangle  r;
452         r.x = 0;
453         r.y = 0;
454         r.width = root_width;
455         r.height = root_height;
456         region = XFixesCreateRegion (dpy, &r, 1);
457     }
458 #if MONITOR_REPAINT
459     rootBuffer = rootPicture;
460 #else
461     if (!rootBuffer)
462     {
463         Pixmap  rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
464                                             DefaultDepth (dpy, scr));
465         rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
466                                            XRenderFindVisualFormat (dpy,
467                                                                     DefaultVisual (dpy, scr)),
468                                            0, 0);
469         XFreePixmap (dpy, rootPixmap);
470     }
471 #endif
472     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
473 #if MONITOR_REPAINT
474     XRenderComposite (dpy, PictOpSrc, blackPicture, None, rootPicture,
475                       0, 0, 0, 0, 0, 0, root_width, root_height);
476 #endif
477 #if DEBUG
478     printf ("paint:");
479 #endif
480     for (w = list; w; w = w->next)
481     {
482         if (w->a.map_state != IsViewable)
483             continue;
484         /* never painted, ignore it */
485         if (!w->damaged)
486             continue;
487         if (!w->picture)
488             continue;
489 #if DEBUG
490         printf (" 0x%x", w->id);
491 #endif
492         if (w->borderSize)
493             XFixesDestroyRegion (dpy, w->borderSize);
494         w->borderSize = border_size (dpy, w);
495         if (w->extents)
496             XFixesDestroyRegion (dpy, w->extents);
497         w->extents = win_extents (dpy, w);
498         if (w->mode == WINDOW_SOLID)
499         {
500             XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
501             XFixesSubtractRegion (dpy, region, region, w->borderSize);
502             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
503                               0, 0, 0, 0, 
504                               w->a.x + w->a.border_width,
505                               w->a.y + w->a.border_width,
506                               w->a.width,
507                               w->a.height);
508         }
509         w->borderClip = XFixesCreateRegion (dpy, 0, 0);
510         XFixesCopyRegion (dpy, w->borderClip, region);
511         w->prev_trans = t;
512         t = w;
513     }
514 #if DEBUG
515     printf ("\n");
516 #endif
517     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
518     paint_root (dpy);
519     for (w = t; w; w = w->prev_trans)
520     {
521         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
522         if (w->shadow)
523         {
524             XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer,
525                               0, 0, 0, 0,
526                               w->a.x + w->shadow_dx,
527                               w->a.y + w->shadow_dy,
528                               w->shadow_width, w->shadow_height);
529         }
530         if (w->mode == WINDOW_TRANS)
531             XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootBuffer,
532                               0, 0, 0, 0, 
533                               w->a.x + w->a.border_width,
534                               w->a.y + w->a.border_width,
535                               w->a.width,
536                               w->a.height);
537         else if (w->mode == WINDOW_ARGB)
538             XRenderComposite (dpy, PictOpOver, w->picture, None, rootBuffer,
539                               0, 0, 0, 0, 
540                               w->a.x + w->a.border_width,
541                               w->a.y + w->a.border_width,
542                               w->a.width,
543                               w->a.height);
544         XFixesDestroyRegion (dpy, w->borderClip);
545         w->borderClip = None;
546     }
547     XFixesDestroyRegion (dpy, region);
548     if (rootBuffer != rootPicture)
549     {
550         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
551         XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
552                           0, 0, 0, 0, 0, 0, root_width, root_height);
553     }
554 }
555
556 static void
557 add_damage (Display *dpy, XserverRegion damage)
558 {
559     if (allDamage)
560     {
561         XFixesUnionRegion (dpy, allDamage, allDamage, damage);
562         XFixesDestroyRegion (dpy, damage);
563     }
564     else
565         allDamage = damage;
566 }
567
568 static void
569 repair_win (Display *dpy, Window id)
570 {
571     win             *w = find_win (dpy, id);
572     XserverRegion   parts;
573
574     if (!w)
575         return;
576     if (!w->damaged)
577     {
578         parts = win_extents (dpy, w);
579         XDamageSubtract (dpy, w->damage, None, None);
580     }
581     else
582     {
583         parts = XFixesCreateRegion (dpy, 0, 0);
584         XDamageSubtract (dpy, w->damage, None, parts);
585         XFixesTranslateRegion (dpy, parts,
586                                w->a.x + w->a.border_width,
587                                w->a.y + w->a.border_width);
588     }
589     add_damage (dpy, parts);
590     w->damaged = 1;
591 }
592
593 static void
594 map_win (Display *dpy, Window id, unsigned long sequence)
595 {
596     win             *w = find_win (dpy, id);
597
598     if (!w)
599         return;
600     w->a.map_state = IsViewable;
601     w->damaged = 0;
602 }
603
604 static void
605 unmap_win (Display *dpy, Window id)
606 {
607     win *w = find_win (dpy, id);
608
609     if (!w)
610         return;
611     w->a.map_state = IsUnmapped;
612     w->damaged = 0;
613     if (w->extents != None)
614     {
615         add_damage (dpy, w->extents);    /* destroys region */
616         w->extents = None;
617     }
618 }
619
620 static void
621 add_win (Display *dpy, Window id, Window prev)
622 {
623     win                         *new = malloc (sizeof (win));
624     win                         **p;
625     XRenderPictureAttributes    pa;
626     XRenderPictFormat           *format;
627     
628     if (!new)
629         return;
630     if (prev)
631     {
632         for (p = &list; *p; p = &(*p)->next)
633             if ((*p)->id == prev)
634                 break;
635     }
636     else
637         p = &list;
638     new->id = id;
639     if (!XGetWindowAttributes (dpy, id, &new->a))
640     {
641         free (new);
642         return;
643     }
644     new->damage = None;
645     new->damaged = 0;
646     new->damage = None;
647     pa.subwindow_mode = IncludeInferiors;
648     if (new->a.class == InputOnly)
649     {
650         new->picture = 0;
651         format = 0;
652         new->damage_sequence = 0;
653     }
654     else
655     {
656         format = XRenderFindVisualFormat (dpy, new->a.visual);
657         new->picture = XRenderCreatePicture (dpy, id,
658                                              format,
659                                              CPSubwindowMode,
660                                              &pa);
661         new->damage_sequence = NextRequest (dpy);
662         new->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
663     }
664                                          
665     new->shadow = None;
666     new->shadow_dx = 0;
667     new->shadow_dy = 0;
668     new->shadow_width = 0;
669     new->shadow_height = 0;
670     new->borderSize = None;
671     new->extents = None;
672     if (format && format->type == PictTypeDirect && format->direct.alphaMask)
673         new->mode = WINDOW_ARGB;
674     else if (new->a.override_redirect)
675         new->mode = WINDOW_TRANS;
676     else
677         new->mode = WINDOW_SOLID;
678     new->next = *p;
679     *p = new;
680     if (new->a.map_state == IsViewable)
681         map_win (dpy, id, new->damage_sequence - 1);
682 }
683
684 void
685 restack_win (Display *dpy, win *w, Window new_above)
686 {
687     Window  old_above;
688     
689     if (w->next)
690         old_above = w->next->id;
691     else
692         old_above = None;
693     if (old_above != new_above)
694     {
695         win **prev;
696
697         /* unhook */
698         for (prev = &list; *prev; prev = &(*prev)->next)
699             if ((*prev) == w)
700                 break;
701         *prev = w->next;
702         
703         /* rehook */
704         for (prev = &list; *prev; prev = &(*prev)->next)
705         {
706             if ((*prev)->id == new_above)
707                 break;
708         }
709         w->next = *prev;
710         *prev = w;
711     }
712 }
713
714 static void
715 configure_win (Display *dpy, XConfigureEvent *ce)
716 {
717     win             *w = find_win (dpy, ce->window);
718     Window          above;
719     XserverRegion   damage = None;
720     
721     if (!w)
722     {
723         if (ce->window == root)
724         {
725             if (rootBuffer)
726             {
727                 XRenderFreePicture (dpy, rootBuffer);
728                 rootBuffer = None;
729             }
730             root_width = ce->width;
731             root_height = ce->height;
732         }
733         return;
734     }
735     if (w->a.map_state == IsViewable)
736     {
737         damage = XFixesCreateRegion (dpy, 0, 0);
738         if (w->extents != None) 
739             XFixesCopyRegion (dpy, damage, w->extents);
740     }
741     w->a.x = ce->x;
742     w->a.y = ce->y;
743     if (w->a.width != ce->width || w->a.height != ce->height)
744         if (w->shadow)
745         {
746             XRenderFreePicture (dpy, w->shadow);
747             w->shadow = None;
748         }
749     w->a.width = ce->width;
750     w->a.height = ce->height;
751     w->a.border_width = ce->border_width;
752     w->a.override_redirect = ce->override_redirect;
753     restack_win (dpy, w, ce->above);
754     if (damage)
755     {
756         XserverRegion   extents = win_extents (dpy, w);
757         XFixesUnionRegion (dpy, damage, damage, extents);
758         XFixesDestroyRegion (dpy, extents);
759         add_damage (dpy, damage);
760     }
761 }
762
763 static void
764 circulate_win (Display *dpy, XCirculateEvent *ce)
765 {
766     win     *w = find_win (dpy, ce->window);
767     Window  new_above;
768
769     if (ce->place == PlaceOnTop)
770         new_above = list->id;
771     else
772         new_above = None;
773     restack_win (dpy, w, new_above);
774 }
775
776 static void
777 destroy_win (Display *dpy, Window id, Bool gone)
778 {
779     win **prev, *w;
780
781     for (prev = &list; (w = *prev); prev = &w->next)
782         if (w->id == id)
783         {
784             if (!gone)
785                 unmap_win (dpy, id);
786             *prev = w->next;
787             if (w->picture)
788                 XRenderFreePicture (dpy, w->picture);
789             if (w->damage != None)
790                 XDamageDestroy (dpy, w->damage);
791             free (w);
792             break;
793         }
794 }
795
796 /*
797 static void
798 dump_win (win *w)
799 {
800     printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id,
801             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
802 }
803
804
805 static void
806 dump_wins (void)
807 {
808     win *w;
809
810     printf ("windows:\n");
811     for (w = list; w; w = w->next)
812         dump_win (w);
813 }
814 */
815
816 static void
817 damage_win (Display *dpy, XDamageNotifyEvent *de)
818 {
819     repair_win (dpy, de->drawable);
820 }
821
822 static int
823 error (Display *dpy, XErrorEvent *ev)
824 {
825     printf ("error %d request %d minor %d\n",
826             ev->error_code, ev->request_code, ev->minor_code);
827
828     return 0;
829 }
830
831 static void
832 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
833 {
834     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
835     
836     add_damage (dpy, region);
837 }
838
839 #define INTERVAL    0
840
841 #if INTERVAL
842 static int
843 time_in_millis (void)
844 {
845     struct timeval  tp;
846
847     gettimeofday (&tp, 0);
848     return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
849 }
850 #endif
851
852 #define INTERVAL    0
853
854 static int
855 ev_serial (XEvent *ev)
856 {
857     if (ev->type & 0x7f != KeymapNotify)
858         return ev->xany.serial;
859     return NextRequest (ev->xany.display);
860 }
861
862 int                 damage_event, damage_error;
863
864 static char *
865 ev_name (XEvent *ev)
866 {
867     static char buf[128];
868     switch (ev->type & 0x7f) {
869     case Expose:
870         return "Expose";
871     case MapNotify:
872         return "Map";
873     case UnmapNotify:
874         return "Unmap";
875     case ReparentNotify:
876         return "Reparent";
877     case CirculateNotify:
878         return "Circulate";
879     default:
880         if (ev->type == damage_event + XDamageNotify)
881             return "Damage";
882         sprintf (buf, "Event %d", ev->type);
883         return buf;
884     }
885 }
886
887 static Window
888 ev_window (XEvent *ev)
889 {
890     switch (ev->type) {
891     case Expose:
892         return ev->xexpose.window;
893     case MapNotify:
894         return ev->xmap.window;
895     case UnmapNotify:
896         return ev->xunmap.window;
897     case ReparentNotify:
898         return ev->xreparent.window;
899     case CirculateNotify:
900         return ev->xcirculate.window;
901     default:
902         if (ev->type == damage_event + XDamageNotify)
903             return ((XDamageNotifyEvent *) ev)->drawable;
904         return 0;
905     }
906 }
907
908 int
909 main (int argc, char **argv)
910 {
911     XEvent          ev;
912     int             event_base, error_base;
913     Window          root_return, parent_return;
914     Window          *children;
915     Pixmap          transPixmap;
916     Pixmap          blackPixmap;
917     unsigned int    nchildren;
918     int             i;
919     int             xfixes_event, xfixes_error;
920     XRenderPictureAttributes    pa;
921     XRenderColor                c;
922     XRectangle      *expose_rects = 0;
923     int             size_expose = 0;
924     int             n_expose = 0;
925     struct pollfd   ufd;
926     int             n;
927     int             last_update;
928     int             now;
929     int             p;
930 #if INTERVAL
931     int             timeout;
932 #endif
933
934     dpy = XOpenDisplay (0);
935     if (!dpy)
936     {
937         fprintf (stderr, "Can't open display\n");
938         exit (1);
939     }
940     XSetErrorHandler (error);
941     scr = DefaultScreen (dpy);
942     root = RootWindow (dpy, scr);
943     pa.subwindow_mode = IncludeInferiors;
944
945     gaussianMap = make_gaussian_map(dpy, SHADOW_RADIUS);
946
947     transPixmap = XCreatePixmap (dpy, root, 1, 1, 8);
948     pa.repeat = True;
949     transPicture = XRenderCreatePicture (dpy, transPixmap,
950                                          XRenderFindStandardFormat (dpy, PictStandardA8),
951                                          CPRepeat,
952                                          &pa);
953     c.red = c.green = c.blue = 0;
954     c.alpha = 0xc0c0;
955     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
956     
957     root_width = DisplayWidth (dpy, scr);
958     root_height = DisplayHeight (dpy, scr);
959     
960     rootPicture = XRenderCreatePicture (dpy, root, 
961                                         XRenderFindVisualFormat (dpy,
962                                                                  DefaultVisual (dpy, scr)),
963                                         CPSubwindowMode,
964                                         &pa);
965     blackPixmap = XCreatePixmap (dpy, root, 1, 1, 32);
966     pa.repeat = True;
967     blackPicture = XRenderCreatePicture (dpy, blackPixmap,
968                                          XRenderFindStandardFormat (dpy, PictStandardARGB32),
969                                          CPRepeat,
970                                          &pa);
971     c.red = c.green = c.blue = 0;
972     c.alpha = 0xffff;
973     XRenderFillRectangle (dpy, PictOpSrc, blackPicture, &c, 0, 0, 1, 1);
974     if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
975     {
976         fprintf (stderr, "No composite extension\n");
977         exit (1);
978     }
979     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
980     {
981         fprintf (stderr, "No damage extension\n");
982         exit (1);
983     }
984     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
985     {
986         fprintf (stderr, "No XFixes extension\n");
987         exit (1);
988     }
989     allDamage = None;
990     XGrabServer (dpy);
991     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
992     XSelectInput (dpy, root, 
993                   SubstructureNotifyMask|
994                   ExposureMask|
995                   StructureNotifyMask|
996                   PropertyChangeMask);
997     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
998     for (i = 0; i < nchildren; i++)
999         add_win (dpy, children[i], i ? children[i-1] : None);
1000     XFree (children);
1001     XUngrabServer (dpy);
1002     paint_all (dpy, None);
1003 #if INTERVAL
1004     last_update = time_in_millis ();
1005 #endif
1006     for (;;)
1007     {
1008 #if INTERVAL
1009         int busy_start = 0;
1010 #endif
1011 /*      dump_wins (); */
1012         do {
1013             XNextEvent (dpy, &ev);
1014 #if INTERVAL
1015             if (!busy_start)
1016                 busy_start = time_in_millis();
1017 #endif
1018 #if DEBUG
1019             printf ("event %10.10s serial 0x%08x window 0x%08x\n",
1020                     ev_name(&ev), ev_serial (&ev), ev_window (&ev));
1021 #endif
1022             switch (ev.type) {
1023             case CreateNotify:
1024                 add_win (dpy, ev.xcreatewindow.window, 0);
1025                 break;
1026             case ConfigureNotify:
1027                 configure_win (dpy, &ev.xconfigure);
1028                 break;
1029             case DestroyNotify:
1030                 destroy_win (dpy, ev.xdestroywindow.window, True);
1031                 break;
1032             case MapNotify:
1033                 map_win (dpy, ev.xmap.window, ev.xmap.serial);
1034                 break;
1035             case UnmapNotify:
1036                 unmap_win (dpy, ev.xunmap.window);
1037                 break;
1038             case ReparentNotify:
1039                 if (ev.xreparent.parent == root)
1040                     add_win (dpy, ev.xreparent.window, 0);
1041                 else
1042                     destroy_win (dpy, ev.xreparent.window, False);
1043                 break;
1044             case CirculateNotify:
1045                 circulate_win (dpy, &ev.xcirculate);
1046                 break;
1047             case Expose:
1048                 if (ev.xexpose.window == root)
1049                 {
1050                     int more = ev.xexpose.count + 1;
1051                     if (n_expose == size_expose)
1052                     {
1053                         if (expose_rects)
1054                         {
1055                             expose_rects = realloc (expose_rects, 
1056                                                     (size_expose + more) * 
1057                                                     sizeof (XRectangle));
1058                             size_expose += more;
1059                         }
1060                         else
1061                         {
1062                             expose_rects = malloc (more * sizeof (XRectangle));
1063                             size_expose = more;
1064                         }
1065                     }
1066                     expose_rects[n_expose].x = ev.xexpose.x;
1067                     expose_rects[n_expose].y = ev.xexpose.y;
1068                     expose_rects[n_expose].width = ev.xexpose.width;
1069                     expose_rects[n_expose].height = ev.xexpose.height;
1070                     n_expose++;
1071                     if (ev.xexpose.count == 0)
1072                     {
1073                         expose_root (dpy, root, expose_rects, n_expose);
1074                         n_expose = 0;
1075                     }
1076                 }
1077                 break;
1078             case PropertyNotify:
1079                 for (p = 0; backgroundProps[p]; p++)
1080                 {
1081                     if (ev.xproperty.atom == XInternAtom (dpy, backgroundProps[p], False))
1082                     {
1083                         if (rootTile)
1084                         {
1085                             XClearArea (dpy, root, 0, 0, 0, 0, True);
1086                             XRenderFreePicture (dpy, rootTile);
1087                             rootTile = None;
1088                             break;
1089                         }
1090                     }
1091                 }
1092                 break;
1093             default:
1094                 if (ev.type == damage_event + XDamageNotify)
1095                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
1096                 break;
1097             }
1098         } while (QLength (dpy));
1099 #if INTERVAL
1100         now = time_in_millis ();
1101 /*      printf ("\t\tbusy %d\n", now - busy_start); */
1102         timeout = INTERVAL - (now - last_update);
1103         if (timeout > 0)
1104         {
1105             ufd.fd = ConnectionNumber (dpy);
1106             ufd.events = POLLIN;
1107             n = poll (&ufd, 1, timeout);
1108             if (n > 0 && (ufd.revents & POLLIN) && XEventsQueued (dpy, QueuedAfterReading))
1109                 continue;
1110         }
1111 #endif
1112         if (allDamage)
1113         {
1114 #if INTERVAL
1115             int old_update = last_update;
1116             last_update = time_in_millis();
1117 /*          printf ("delta %d\n", last_update - old_update); */
1118 #endif
1119             paint_all (dpy, allDamage);
1120             allDamage = None;
1121         }
1122     }
1123 }