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