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