Add fade-in/fade-out support (-f option). Clean up leak of solid alpha
[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 <unistd.h>
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41 #include <X11/Xatom.h>
42 #include <X11/extensions/Xcomposite.h>
43 #include <X11/extensions/Xdamage.h>
44 #include <X11/extensions/Xrender.h>
45
46 #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2
47 #define HAS_NAME_WINDOW_PIXMAP 1
48 #endif
49
50 typedef struct _ignore {
51     struct _ignore      *next;
52     unsigned long       sequence;
53 } ignore;
54
55 typedef struct _win {
56     struct _win         *next;
57     Window              id;
58 #if HAS_NAME_WINDOW_PIXMAP
59     Pixmap              pixmap;
60 #endif
61     XWindowAttributes   a;
62     int                 mode;
63     int                 damaged;
64     Damage              damage;
65     Picture             picture;
66     Picture             alphaPict;
67     Picture             shadowPict;
68     XserverRegion       borderSize;
69     XserverRegion       extents;
70     Picture             shadow;
71     int                 shadow_dx;
72     int                 shadow_dy;
73     int                 shadow_width;
74     int                 shadow_height;
75     unsigned int        opacity;
76
77     unsigned long       damage_sequence;    /* sequence when damage was created */
78
79     /* for drawing translucent windows */
80     XserverRegion       borderClip;
81     struct _win         *prev_trans;
82 } win;
83
84 typedef struct _conv {
85     int     size;
86     double  *data;
87 } conv;
88
89 typedef struct _fade {
90     struct _fade        *next;
91     win                 *w;
92     double              cur;
93     double              step;
94     void                (*callback) (Display *dpy, win *w, Bool gone);
95     Display             *dpy;
96     Bool                gone;
97 } fade;
98
99 win             *list;
100 fade            *fades;
101 Display         *dpy;
102 int             scr;
103 Window          root;
104 Picture         rootPicture;
105 Picture         rootBuffer;
106 Picture         blackPicture;
107 Picture         transBlackPicture;
108 Picture         rootTile;
109 XserverRegion   allDamage;
110 Bool            clipChanged;
111 #if HAS_NAME_WINDOW_PIXMAP
112 Bool            hasNamePixmap;
113 #endif
114 int             root_height, root_width;
115 ignore          *ignore_head, **ignore_tail = &ignore_head;
116 int             xfixes_event, xfixes_error;
117 int             damage_event, damage_error;
118 int             composite_event, composite_error;
119 int             render_event, render_error;
120
121 /* find these once and be done with it */
122 Atom            opacityAtom;
123
124 /* opacity property name; sometime soon I'll write up an EWMH spec for it */
125 #define OPACITY_PROP    "_NET_WM_WINDOW_OPACITY"
126
127 #define TRANSLUCENT     0xe0000000
128 #define OPAQUE          0xffffffff
129
130 conv            *gaussianMap;
131
132 #define WINDOW_SOLID    0
133 #define WINDOW_TRANS    1
134 #define WINDOW_ARGB     2
135
136 #define TRANS_OPACITY   0.75
137
138 #define DEBUG_REPAINT 0
139 #define DEBUG_EVENTS 0
140 #define MONITOR_REPAINT 0
141
142 #define SHADOWS         1
143 #define SHARP_SHADOW    0
144
145 typedef enum _compMode {
146     CompSimple,         /* looks like a regular X server */
147     CompServerShadows,  /* use window alpha for shadow; sharp, but precise */
148     CompClientShadows,  /* use window extents for shadow, blurred */
149 } CompMode;
150
151 static void
152 determine_mode(Display *dpy, win *w);
153     
154 CompMode    compMode = CompSimple;
155
156 int         shadowRadius = 12;
157
158 double  fade_step =     0.05;
159 int     fade_delta =    10;
160 int     fade_time =     0;
161 Bool    fadeWindows;
162
163 int
164 get_time_in_milliseconds ()
165 {
166     struct timeval  tv;
167     struct timezone tz;
168
169     gettimeofday (&tv, &tz);
170     return tv.tv_sec * 1000 + tv.tv_usec / 1000;
171 }
172
173 fade *
174 find_fade (win *w)
175 {
176     fade    *f;
177     
178     for (f = fades; f; f = f->next)
179     {
180         if (f->w == w)
181             return f;
182     }
183     return 0;
184 }
185
186 void
187 dequeue_fade (Display *dpy, fade *f)
188 {
189     fade    **prev;
190
191     for (prev = &fades; *prev; prev = &(*prev)->next)
192         if (*prev == f)
193         {
194             *prev = f->next;
195             if (f->callback)
196                 (*f->callback) (dpy, f->w, f->gone);
197             free (f);
198             break;
199         }
200 }
201
202 void
203 cleanup_fade (Display *dpy, win *w)
204 {
205     fade *f = find_fade (w);
206     if (f)
207         dequeue_fade (dpy, f);
208 }
209
210 void
211 enqueue_fade (Display *dpy, fade *f)
212 {
213     if (!fades)
214         fade_time = get_time_in_milliseconds () + fade_delta;
215     f->next = fades;
216     fades = f;
217 }
218
219 static void
220 set_fade (Display *dpy, win *w, Bool in, 
221           void (*callback) (Display *dpy, win *w, Bool gone),
222           Bool gone)
223 {
224     fade    *f;
225
226     f = find_fade (w);
227     if (!f)
228     {
229         f = malloc (sizeof (fade));
230         f->next = 0;
231         f->w = w;
232         if (in)
233             f->cur = 0;
234         else
235             f->cur = 1;
236         enqueue_fade (dpy, f);
237     }
238     if (in)
239         f->step = fade_step;
240     else
241         f->step = -fade_step;
242     f->callback = callback;
243     f->gone = gone;
244     w->opacity = f->cur * OPAQUE;
245 #if 0
246     printf ("set_fade start %g step %g\n", f->cur, f->step);
247 #endif
248     determine_mode (dpy, w);
249 }
250
251 int
252 fade_timeout (void)
253 {
254     int now;
255     int delta;
256     if (!fades)
257         return -1;
258     now = get_time_in_milliseconds();
259     delta = fade_time - now;
260     if (delta < 0)
261         delta = 0;
262 /*    printf ("timeout %d\n", delta); */
263     return delta;
264 }
265
266 void
267 run_fades (Display *dpy)
268 {
269     int     now = get_time_in_milliseconds();
270     fade    *f, *next;
271     int     steps;
272
273 #if 0
274     printf ("run fades\n");
275 #endif
276     if (fade_time - now > 0)
277         return;
278     steps = 1 + (now - fade_time) / fade_delta;
279     for (next = fades; f = next; )
280     {
281         win *w = f->w;
282         next = f->next;
283         f->cur += f->step * steps;
284         if (f->cur >= 1)
285             f->cur = 1;
286         else if (f->cur < 0)
287             f->cur = 0;
288 #if 0
289         printf ("opacity now %g\n", f->cur);
290 #endif
291         w->opacity = f->cur * OPAQUE;
292         if (f->step > 0)
293         {
294             if (f->cur >= 1)
295                 dequeue_fade (dpy, f);
296         }
297         else
298         {
299             if (f->cur <= 0)
300                 dequeue_fade (dpy, f);
301         }
302         determine_mode (dpy, w);
303     }
304     fade_time = now + fade_delta;
305 }
306
307 #define SHADOW_OPACITY  0.75
308 #define SHADOW_OFFSET_X (-shadowRadius * 5 / 4)
309 #define SHADOW_OFFSET_Y (-shadowRadius * 5 / 4)
310
311 static double
312 gaussian (double r, double x, double y)
313 {
314     return ((1 / (sqrt (2 * M_PI * r))) *
315             exp ((- (x * x + y * y)) / (2 * r * r)));
316 }
317
318
319 static conv *
320 make_gaussian_map (Display *dpy, double r)
321 {
322     conv            *c;
323     int             size = ((int) ceil ((r * 3)) + 1) & ~1;
324     int             center = size / 2;
325     int             x, y;
326     double          t;
327     double          g;
328     
329     c = malloc (sizeof (conv) + size * size * sizeof (double));
330     c->size = size;
331     c->data = (double *) (c + 1);
332     t = 0.0;
333     for (y = 0; y < size; y++)
334         for (x = 0; x < size; x++)
335         {
336             g = gaussian (r, (double) (x - center), (double) (y - center));
337             t += g;
338             c->data[y * size + x] = g;
339         }
340 /*    printf ("gaussian total %f\n", t); */
341     for (y = 0; y < size; y++)
342         for (x = 0; x < size; x++)
343         {
344             c->data[y*size + x] /= t;
345         }
346     return c;
347 }
348
349 /*
350  * A picture will help
351  *
352  *      -center   0                width  width+center
353  *  -center +-----+-------------------+-----+
354  *          |     |                   |     |
355  *          |     |                   |     |
356  *        0 +-----+-------------------+-----+
357  *          |     |                   |     |
358  *          |     |                   |     |
359  *          |     |                   |     |
360  *   height +-----+-------------------+-----+
361  *          |     |                   |     |
362  * height+  |     |                   |     |
363  *  center  +-----+-------------------+-----+
364  */
365  
366 static unsigned char
367 sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
368 {
369     int     fx, fy;
370     double  *g_data;
371     double  *g_line = map->data;
372     int     g_size = map->size;
373     int     center = g_size / 2;
374     int     fx_start, fx_end;
375     int     fy_start, fy_end;
376     double  v;
377     
378     /*
379      * Compute set of filter values which are "in range",
380      * that's the set with:
381      *  0 <= x + (fx-center) && x + (fx-center) < width &&
382      *  0 <= y + (fy-center) && y + (fy-center) < height
383      *
384      *  0 <= x + (fx - center)  x + fx - center < width
385      *  center - x <= fx        fx < width + center - x
386      */
387
388     fx_start = center - x;
389     if (fx_start < 0)
390         fx_start = 0;
391     fx_end = width + center - x;
392     if (fx_end > g_size)
393         fx_end = g_size;
394
395     fy_start = center - y;
396     if (fy_start < 0)
397         fy_start = 0;
398     fy_end = height + center - y;
399     if (fy_end > g_size)
400         fy_end = g_size;
401
402     g_line = g_line + fy_start * g_size + fx_start;
403     
404     v = 0;
405     for (fy = fy_start; fy < fy_end; fy++)
406     {
407         g_data = g_line;
408         g_line += g_size;
409         
410         for (fx = fx_start; fx < fx_end; fx++)
411             v += *g_data++;
412     }
413     if (v > 1)
414         v = 1;
415     
416     return ((unsigned char) (v * opacity * 255.0));
417 }
418
419 static XImage *
420 make_shadow (Display *dpy, double opacity, int width, int height)
421 {
422     XImage          *ximage;
423     unsigned char   *data;
424     int             gsize = gaussianMap->size;
425     int             ylimit, xlimit;
426     int             swidth = width + gsize;
427     int             sheight = height + gsize;
428     int             center = gsize / 2;
429     int             x, y;
430     unsigned char   d;
431     int             x_diff;
432     
433     data = malloc (swidth * sheight * sizeof (unsigned char));
434     if (!data)
435         return 0;
436     ximage = XCreateImage (dpy,
437                            DefaultVisual(dpy, DefaultScreen(dpy)),
438                            8,
439                            ZPixmap,
440                            0,
441                            (char *) data,
442                            swidth, sheight, 8, swidth * sizeof (unsigned char));
443     if (!ximage)
444     {
445         free (data);
446         return 0;
447     }
448     /*
449      * Build the gaussian in sections
450      */
451
452     /*
453      * center (fill the complete data array)
454      */
455
456     d = sum_gaussian (gaussianMap, opacity, center, center, width, height);
457     memset(data, d, sheight * swidth);
458     
459     /*
460      * corners
461      */
462     ylimit = gsize;
463     if (ylimit > sheight / 2)
464         ylimit = (sheight + 1) / 2;
465     xlimit = gsize;
466     if (xlimit > swidth / 2)
467         xlimit = (swidth + 1) / 2;
468
469     for (y = 0; y < ylimit; y++)
470         for (x = 0; x < xlimit; x++)
471         {
472             d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height);
473             data[y * swidth + x] = d;
474             data[(sheight - y - 1) * swidth + x] = d;
475             data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d;
476             data[y * swidth + (swidth - x - 1)] = d;
477         }
478
479     /*
480      * top/bottom
481      */
482     x_diff = swidth - (gsize * 2);
483     if (x_diff > 0 && ylimit > 0)
484     {
485         for (y = 0; y < ylimit; y++)
486         {
487             d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height);
488             memset (&data[y * swidth + gsize], d, x_diff);
489             memset (&data[(sheight - y - 1) * swidth + gsize], d, x_diff);
490         }
491     }
492
493     /*
494      * sides
495      */
496     
497     for (x = 0; x < xlimit; x++)
498     {
499         d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height);
500         for (y = gsize; y < sheight - gsize; y++)
501         {
502             data[y * swidth + x] = d;
503             data[y * swidth + (swidth - x - 1)] = d;
504         }
505     }
506
507     return ximage;
508 }
509
510 static Picture
511 shadow_picture (Display *dpy, double opacity, int width, int height, int *wp, int *hp)
512 {
513     XImage  *shadowImage;
514     Pixmap  shadowPixmap;
515     Picture shadowPicture;
516     GC      gc;
517     
518     shadowImage = make_shadow (dpy, opacity, width, height);
519     if (!shadowImage)
520         return None;
521     shadowPixmap = XCreatePixmap (dpy, root, 
522                                   shadowImage->width,
523                                   shadowImage->height,
524                                   8);
525     shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
526                                           XRenderFindStandardFormat (dpy, PictStandardA8),
527                                           0, 0);
528     gc = XCreateGC (dpy, shadowPixmap, 0, 0);
529     
530     XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 
531                shadowImage->width,
532                shadowImage->height);
533     *wp = shadowImage->width;
534     *hp = shadowImage->height;
535     XFreeGC (dpy, gc);
536     XDestroyImage (shadowImage);
537     XFreePixmap (dpy, shadowPixmap);
538     return shadowPicture;
539 }
540
541 Picture
542 solid_picture (Display *dpy, Bool argb, double a, double r, double g, double b)
543 {
544     Pixmap                      pixmap;
545     Picture                     picture;
546     XRenderPictureAttributes    pa;
547     XRenderColor                c;
548
549     pixmap = XCreatePixmap (dpy, root, 1, 1, argb ? 32 : 8);
550     pa.repeat = True;
551     picture = XRenderCreatePicture (dpy, pixmap,
552                                     XRenderFindStandardFormat (dpy, argb ? PictStandardARGB32 : PictStandardA8),
553                                     CPRepeat,
554                                     &pa);
555     c.alpha = a * 0xffff;
556     c.red = r * 0xffff;
557     c.green = g * 0xffff;
558     c.blue = b * 0xffff;
559     XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 0, 0, 1, 1);
560     XFreePixmap (dpy, pixmap);
561     return picture;
562 }
563
564 void
565 discard_ignore (Display *dpy, unsigned long sequence)
566 {
567     while (ignore_head)
568     {
569         if ((long) (sequence - ignore_head->sequence) > 0)
570         {
571             ignore  *next = ignore_head->next;
572             free (ignore_head);
573             ignore_head = next;
574             if (!ignore_head)
575                 ignore_tail = &ignore_head;
576         }
577         else
578             break;
579     }
580 }
581
582 void
583 set_ignore (Display *dpy, unsigned long sequence)
584 {
585     ignore  *i = malloc (sizeof (ignore));
586     if (!i)
587         return;
588     i->sequence = sequence;
589     i->next = 0;
590     *ignore_tail = i;
591     ignore_tail = &i->next;
592 }
593
594 int
595 should_ignore (Display *dpy, unsigned long sequence)
596 {
597     discard_ignore (dpy, sequence);
598     return ignore_head && ignore_head->sequence == sequence;
599 }
600
601 static win *
602 find_win (Display *dpy, Window id)
603 {
604     win *w;
605
606     for (w = list; w; w = w->next)
607         if (w->id == id)
608             return w;
609     return 0;
610 }
611
612 static char *backgroundProps[] = {
613     "_XROOTPMAP_ID",
614     "_XSETROOT_ID",
615     0,
616 };
617     
618 static Picture
619 root_tile (Display *dpy)
620 {
621     Picture         picture;
622     Atom            actual_type;
623     Pixmap          pixmap;
624     int             actual_format;
625     unsigned long   nitems;
626     unsigned long   bytes_after;
627     unsigned char   *prop;
628     Bool            fill;
629     XRenderPictureAttributes    pa;
630     int             p;
631
632     pixmap = None;
633     for (p = 0; backgroundProps[p]; p++)
634     {
635         if (XGetWindowProperty (dpy, root, XInternAtom (dpy, backgroundProps[p], False),
636                                 0, 4, False, AnyPropertyType,
637                                 &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success &&
638             actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1)
639         {
640             memcpy (&pixmap, prop, 4);
641             XFree (prop);
642             fill = False;
643             break;
644         }
645     }
646     if (!pixmap)
647     {
648         pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
649         fill = True;
650     }
651     pa.repeat = True;
652     picture = XRenderCreatePicture (dpy, pixmap,
653                                     XRenderFindVisualFormat (dpy,
654                                                              DefaultVisual (dpy, scr)),
655                                     CPRepeat, &pa);
656     if (fill)
657     {
658         XRenderColor    c;
659         
660         c.red = c.green = c.blue = 0x8080;
661         c.alpha = 0xffff;
662         XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 
663                               0, 0, 1, 1);
664     }
665     return picture;
666 }
667
668 static void
669 paint_root (Display *dpy)
670 {
671     if (!rootTile)
672         rootTile = root_tile (dpy);
673     
674     XRenderComposite (dpy, PictOpSrc,
675                       rootTile, None, rootBuffer,
676                       0, 0, 0, 0, 0, 0, root_width, root_height);
677 }
678
679 static XserverRegion
680 win_extents (Display *dpy, win *w)
681 {
682     XRectangle      r;
683     
684     r.x = w->a.x;
685     r.y = w->a.y;
686     r.width = w->a.width + w->a.border_width * 2;
687     r.height = w->a.height + w->a.border_width * 2;
688     if (compMode != CompSimple)
689     {
690         if (compMode == CompServerShadows || w->mode != WINDOW_ARGB)
691         {
692             XRectangle  sr;
693
694             if (compMode == CompServerShadows)
695             {
696                 w->shadow_dx = 2;
697                 w->shadow_dy = 7;
698                 w->shadow_width = w->a.width;
699                 w->shadow_height = w->a.height;
700             }
701             else
702             {
703                 w->shadow_dx = SHADOW_OFFSET_X;
704                 w->shadow_dy = SHADOW_OFFSET_Y;
705                 if (!w->shadow)
706                 {
707                     double      opacity = SHADOW_OPACITY;
708                     if (w->mode == WINDOW_TRANS)
709                         opacity = opacity * TRANS_OPACITY;
710                     w->shadow = shadow_picture (dpy, opacity,
711                                                 w->a.width + w->a.border_width * 2,
712                                                 w->a.height + w->a.border_width * 2,
713                                                 &w->shadow_width, &w->shadow_height);
714                 }
715             }
716             sr.x = w->a.x + w->shadow_dx;
717             sr.y = w->a.y + w->shadow_dy;
718             sr.width = w->shadow_width;
719             sr.height = w->shadow_height;
720             if (sr.x < r.x)
721             {
722                 r.width = (r.x + r.width) - sr.x;
723                 r.x = sr.x;
724             }
725             if (sr.y < r.y)
726             {
727                 r.height = (r.y + r.height) - sr.y;
728                 r.y = sr.y;
729             }
730             if (sr.x + sr.width > r.x + r.width)
731                 r.width = sr.x + sr.width - r.x;
732             if (sr.y + sr.height > r.y + r.height)
733                 r.height = sr.y + sr.height - r.y;
734         }
735     }
736     return XFixesCreateRegion (dpy, &r, 1);
737 }
738
739 static XserverRegion
740 border_size (Display *dpy, win *w)
741 {
742     XserverRegion   border;
743     /*
744      * if window doesn't exist anymore,  this will generate an error
745      * as well as not generate a region.  Perhaps a better XFixes
746      * architecture would be to have a request that copies instead
747      * of creates, that way you'd just end up with an empty region
748      * instead of an invalid XID.
749      */
750     set_ignore (dpy, NextRequest (dpy));
751     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
752     /* translate this */
753     set_ignore (dpy, NextRequest (dpy));
754     XFixesTranslateRegion (dpy, border,
755                            w->a.x + w->a.border_width,
756                            w->a.y + w->a.border_width);
757     return border;
758 }
759
760 static void
761 paint_all (Display *dpy, XserverRegion region)
762 {
763     win *w;
764     win *t = 0;
765     
766     if (!region)
767     {
768         XRectangle  r;
769         r.x = 0;
770         r.y = 0;
771         r.width = root_width;
772         r.height = root_height;
773         region = XFixesCreateRegion (dpy, &r, 1);
774     }
775 #if MONITOR_REPAINT
776     rootBuffer = rootPicture;
777 #else
778     if (!rootBuffer)
779     {
780         Pixmap  rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
781                                             DefaultDepth (dpy, scr));
782         rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
783                                            XRenderFindVisualFormat (dpy,
784                                                                     DefaultVisual (dpy, scr)),
785                                            0, 0);
786         XFreePixmap (dpy, rootPixmap);
787     }
788 #endif
789     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
790 #if MONITOR_REPAINT
791     XRenderComposite (dpy, PictOpSrc, blackPicture, None, rootPicture,
792                       0, 0, 0, 0, 0, 0, root_width, root_height);
793 #endif
794 #if DEBUG_REPAINT
795     printf ("paint:");
796 #endif
797     for (w = list; w; w = w->next)
798     {
799         if (w->a.map_state != IsViewable)
800             continue;
801         /* never painted, ignore it */
802         if (!w->damaged)
803             continue;
804         if (!w->picture)
805         {
806             XRenderPictureAttributes    pa;
807             XRenderPictFormat           *format;
808             Drawable                    draw = w->id;
809             
810 #if HAS_NAME_WINDOW_PIXMAP
811             if (hasNamePixmap && !w->pixmap)
812                 w->pixmap = XCompositeNameWindowPixmap (dpy, w->id);
813             if (w->pixmap)
814                 draw = w->pixmap;
815 #endif
816             format = XRenderFindVisualFormat (dpy, w->a.visual);
817             pa.subwindow_mode = IncludeInferiors;
818             w->picture = XRenderCreatePicture (dpy, draw,
819                                                format,
820                                                CPSubwindowMode,
821                                                &pa);
822         }
823 #if DEBUG_REPAINT
824         printf (" 0x%x", w->id);
825 #endif
826         if (clipChanged)
827         {
828             if (w->borderSize)
829             {
830                 set_ignore (dpy, NextRequest (dpy));
831                 XFixesDestroyRegion (dpy, w->borderSize);
832                 w->borderSize = None;
833             }
834             if (w->extents)
835             {
836                 XFixesDestroyRegion (dpy, w->extents);
837                 w->extents = None;
838             }
839             if (w->borderClip)
840             {
841                 XFixesDestroyRegion (dpy, w->borderClip);
842                 w->borderClip = None;
843             }
844         }
845         if (!w->borderSize)
846             w->borderSize = border_size (dpy, w);
847         if (!w->extents)
848             w->extents = win_extents (dpy, w);
849         if (w->mode == WINDOW_SOLID)
850         {
851             XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
852             set_ignore (dpy, NextRequest (dpy));
853             XFixesSubtractRegion (dpy, region, region, w->borderSize);
854             set_ignore (dpy, NextRequest (dpy));
855             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
856                               0, 0, 0, 0, 
857                               w->a.x + w->a.border_width,
858                               w->a.y + w->a.border_width,
859                               w->a.width,
860                               w->a.height);
861         }
862         if (!w->borderClip)
863         {
864             w->borderClip = XFixesCreateRegion (dpy, 0, 0);
865             XFixesCopyRegion (dpy, w->borderClip, region);
866         }
867         w->prev_trans = t;
868         t = w;
869     }
870 #if DEBUG_REPAINT
871     printf ("\n");
872     fflush (stdout);
873 #endif
874     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
875     paint_root (dpy);
876     for (w = t; w; w = w->prev_trans)
877     {
878         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
879         switch (compMode) {
880         case CompSimple:
881             break;
882         case CompServerShadows:
883             set_ignore (dpy, NextRequest (dpy));
884             if (w->opacity != OPAQUE && !w->shadowPict)
885                 w->shadowPict = solid_picture (dpy, True,
886                                                (double) w->opacity / OPAQUE * 0.3,
887                                                0, 0, 0);
888             XRenderComposite (dpy, PictOpOver, 
889                               w->shadowPict ? w->shadowPict : transBlackPicture,
890                               w->picture, rootBuffer,
891                               0, 0, 0, 0,
892                               w->a.x + w->shadow_dx,
893                               w->a.y + w->shadow_dy,
894                               w->shadow_width, w->shadow_height);
895             break;
896         case CompClientShadows:
897             if (w->shadow)
898             {
899                 XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer,
900                                   0, 0, 0, 0,
901                                   w->a.x + w->shadow_dx,
902                                   w->a.y + w->shadow_dy,
903                                   w->shadow_width, w->shadow_height);
904             }
905             break;
906         }
907         if (w->opacity != OPAQUE && !w->alphaPict)
908             w->alphaPict = solid_picture (dpy, False, 
909                                           (double) w->opacity / OPAQUE, 0, 0, 0);
910         if (w->mode == WINDOW_TRANS)
911         {
912             set_ignore (dpy, NextRequest (dpy));
913             XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer,
914                               0, 0, 0, 0, 
915                               w->a.x + w->a.border_width,
916                               w->a.y + w->a.border_width,
917                               w->a.width,
918                               w->a.height);
919         }
920         else if (w->mode == WINDOW_ARGB)
921         {
922             set_ignore (dpy, NextRequest (dpy));
923             XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer,
924                               0, 0, 0, 0, 
925                               w->a.x + w->a.border_width,
926                               w->a.y + w->a.border_width,
927                               w->a.width,
928                               w->a.height);
929         }
930         XFixesDestroyRegion (dpy, w->borderClip);
931         w->borderClip = None;
932     }
933     XFixesDestroyRegion (dpy, region);
934     if (rootBuffer != rootPicture)
935     {
936         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
937         XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
938                           0, 0, 0, 0, 0, 0, root_width, root_height);
939     }
940 }
941
942 static void
943 add_damage (Display *dpy, XserverRegion damage)
944 {
945     if (allDamage)
946     {
947         XFixesUnionRegion (dpy, allDamage, allDamage, damage);
948         XFixesDestroyRegion (dpy, damage);
949     }
950     else
951         allDamage = damage;
952 }
953
954 static void
955 repair_win (Display *dpy, Window id)
956 {
957     win             *w = find_win (dpy, id);
958     XserverRegion   parts;
959
960     if (!w)
961         return;
962     if (!w->damaged)
963     {
964         parts = win_extents (dpy, w);
965         set_ignore (dpy, NextRequest (dpy));
966         XDamageSubtract (dpy, w->damage, None, None);
967     }
968     else
969     {
970         XserverRegion   o;
971         parts = XFixesCreateRegion (dpy, 0, 0);
972         set_ignore (dpy, NextRequest (dpy));
973         XDamageSubtract (dpy, w->damage, None, parts);
974         XFixesTranslateRegion (dpy, parts,
975                                w->a.x + w->a.border_width,
976                                w->a.y + w->a.border_width);
977         if (compMode == CompServerShadows)
978         {
979             o = XFixesCreateRegion (dpy, 0, 0);
980             XFixesCopyRegion (dpy, o, parts);
981             XFixesTranslateRegion (dpy, o, w->shadow_dx, w->shadow_dy);
982             XFixesUnionRegion (dpy, parts, parts, o);
983             XFixesDestroyRegion (dpy, o);
984         }
985     }
986     add_damage (dpy, parts);
987     w->damaged = 1;
988 }
989
990 static void
991 map_win (Display *dpy, Window id, unsigned long sequence, Bool fade)
992 {
993     win         *w = find_win (dpy, id);
994     Drawable    back;
995
996     if (!w)
997         return;
998     w->a.map_state = IsViewable;
999
1000     /* make sure we know if property was changed */
1001     XSelectInput(dpy, id, PropertyChangeMask);
1002
1003     w->damaged = 0;
1004     clipChanged = True;
1005     if (fade && fadeWindows)
1006         set_fade (dpy, w, True, 0, False);
1007 }
1008
1009 static void
1010 finish_unmap_win (Display *dpy, win *w)
1011 {
1012     w->a.map_state = IsUnmapped;
1013     w->damaged = 0;
1014     if (w->extents != None)
1015     {
1016         add_damage (dpy, w->extents);    /* destroys region */
1017         w->extents = None;
1018     }
1019     
1020 #if HAS_NAME_WINDOW_PIXMAP
1021     if (w->pixmap)
1022     {
1023         XFreePixmap (dpy, w->pixmap);
1024         w->pixmap = None;
1025     }
1026 #endif
1027
1028     if (w->picture)
1029     {
1030         set_ignore (dpy, NextRequest (dpy));
1031         XRenderFreePicture (dpy, w->picture);
1032         w->picture = None;
1033     }
1034
1035     /* don't care about properties anymore */
1036     set_ignore (dpy, NextRequest (dpy));
1037     XSelectInput(dpy, w->id, 0);
1038
1039     if (w->borderSize)
1040     {
1041         set_ignore (dpy, NextRequest (dpy));
1042         XFixesDestroyRegion (dpy, w->borderSize);
1043         w->borderSize = None;
1044     }
1045     if (w->shadow)
1046     {
1047         XRenderFreePicture (dpy, w->shadow);
1048         w->shadow = None;
1049     }
1050     if (w->borderClip)
1051     {
1052         XFixesDestroyRegion (dpy, w->borderClip);
1053         w->borderClip = None;
1054     }
1055
1056     clipChanged = True;
1057 }
1058
1059 #if HAS_NAME_WINDOW_PIXMAP
1060 static void
1061 unmap_callback (Display *dpy, win *w, Bool gone)
1062 {
1063     finish_unmap_win (dpy, w);
1064 }
1065 #endif
1066
1067 static void
1068 unmap_win (Display *dpy, Window id, Bool fade)
1069 {
1070     win *w = find_win (dpy, id);
1071     if (!w)
1072         return;
1073 #if HAS_NAME_WINDOW_PIXMAP
1074     if (w->pixmap && fade && fadeWindows)
1075         set_fade (dpy, w, False, unmap_callback, False);
1076     else
1077 #endif
1078         finish_unmap_win (dpy, w);
1079 }
1080
1081 /* Get the opacity prop from window
1082    not found: default
1083    otherwise the value
1084  */
1085 static unsigned int
1086 get_opacity_prop(Display *dpy, win *w, unsigned int def)
1087 {
1088     Atom actual;
1089     int format;
1090     unsigned long n, left;
1091
1092     char *data;
1093     XGetWindowProperty(dpy, w->id, opacityAtom, 0L, 1L, False, 
1094                        XA_CARDINAL, &actual, &format, 
1095                        &n, &left, (unsigned char **) &data);
1096     if (data != None)
1097     {
1098         unsigned int i;
1099         memcpy (&i, data, sizeof (unsigned int));
1100         XFree( (void *) data);
1101         return i;
1102     }
1103     return def;
1104 }
1105
1106 /* determine mode for window all in one place.
1107    Future might check for menu flag and other cool things
1108 */
1109
1110 static void
1111 determine_mode(Display *dpy, win *w)
1112 {
1113     int mode;
1114     XRenderPictFormat *format;
1115     unsigned int default_opacity;
1116
1117     /* if trans prop == -1 fall back on  previous tests*/
1118
1119     if (w->alphaPict)
1120     {
1121         XRenderFreePicture (dpy, w->alphaPict);
1122         w->alphaPict = None;
1123     }
1124     if (w->shadowPict)
1125     {
1126         XRenderFreePicture (dpy, w->shadowPict);
1127         w->shadowPict = None;
1128     }
1129
1130     if (w->a.class == InputOnly)
1131     {
1132         format = 0;
1133     }
1134     else
1135     {
1136         format = XRenderFindVisualFormat (dpy, w->a.visual);
1137     }
1138
1139     if (format && format->type == PictTypeDirect && format->direct.alphaMask)
1140     {
1141         mode = WINDOW_ARGB;
1142     }
1143     else if (w->opacity != OPAQUE)
1144     {
1145         mode = WINDOW_TRANS;
1146     }
1147     else
1148     {
1149         mode = WINDOW_SOLID;
1150     }
1151     w->mode = mode;
1152     if (w->extents)
1153     {
1154         XserverRegion damage;
1155         damage = XFixesCreateRegion (dpy, 0, 0);
1156         XFixesCopyRegion (dpy, damage, w->extents);
1157         add_damage (dpy, damage);
1158     }
1159 }
1160
1161 static void
1162 add_win (Display *dpy, Window id, Window prev)
1163 {
1164     win                         *new = malloc (sizeof (win));
1165     win                         **p;
1166     
1167     if (!new)
1168         return;
1169     if (prev)
1170     {
1171         for (p = &list; *p; p = &(*p)->next)
1172             if ((*p)->id == prev)
1173                 break;
1174     }
1175     else
1176         p = &list;
1177     new->id = id;
1178     set_ignore (dpy, NextRequest (dpy));
1179     if (!XGetWindowAttributes (dpy, id, &new->a))
1180     {
1181         free (new);
1182         return;
1183     }
1184     new->damaged = 0;
1185 #if HAS_NAME_WINDOW_PIXMAP
1186     new->pixmap = None;
1187 #endif
1188     new->picture = None;
1189     if (new->a.class == InputOnly)
1190     {
1191         new->damage_sequence = 0;
1192         new->damage = None;
1193     }
1194     else
1195     {
1196         new->damage_sequence = NextRequest (dpy);
1197         new->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
1198     }
1199     new->alphaPict = None;
1200     new->shadowPict = None;
1201     new->borderSize = None;
1202     new->extents = None;
1203     new->shadow = None;
1204     new->shadow_dx = 0;
1205     new->shadow_dy = 0;
1206     new->shadow_width = 0;
1207     new->shadow_height = 0;
1208     new->opacity = OPAQUE;
1209
1210     new->borderClip = None;
1211     new->prev_trans = 0;
1212
1213     /* moved mode setting to one place */
1214     new->opacity = get_opacity_prop(dpy, new, OPAQUE);
1215     determine_mode (dpy, new);
1216     
1217     new->next = *p;
1218     *p = new;
1219     if (new->a.map_state == IsViewable)
1220         map_win (dpy, id, new->damage_sequence - 1, False);
1221 }
1222
1223 void
1224 restack_win (Display *dpy, win *w, Window new_above)
1225 {
1226     Window  old_above;
1227     
1228     if (w->next)
1229         old_above = w->next->id;
1230     else
1231         old_above = None;
1232     if (old_above != new_above)
1233     {
1234         win **prev;
1235
1236         /* unhook */
1237         for (prev = &list; *prev; prev = &(*prev)->next)
1238             if ((*prev) == w)
1239                 break;
1240         *prev = w->next;
1241         
1242         /* rehook */
1243         for (prev = &list; *prev; prev = &(*prev)->next)
1244         {
1245             if ((*prev)->id == new_above)
1246                 break;
1247         }
1248         w->next = *prev;
1249         *prev = w;
1250     }
1251 }
1252
1253 static void
1254 configure_win (Display *dpy, XConfigureEvent *ce)
1255 {
1256     win             *w = find_win (dpy, ce->window);
1257     Window          above;
1258     XserverRegion   damage = None;
1259     
1260     if (!w)
1261     {
1262         if (ce->window == root)
1263         {
1264             if (rootBuffer)
1265             {
1266                 XRenderFreePicture (dpy, rootBuffer);
1267                 rootBuffer = None;
1268             }
1269             root_width = ce->width;
1270             root_height = ce->height;
1271         }
1272         return;
1273     }
1274     if (w->a.map_state == IsViewable)
1275     {
1276         damage = XFixesCreateRegion (dpy, 0, 0);
1277         if (w->extents != None) 
1278             XFixesCopyRegion (dpy, damage, w->extents);
1279     }
1280     w->a.x = ce->x;
1281     w->a.y = ce->y;
1282     if (w->a.width != ce->width || w->a.height != ce->height)
1283     {
1284 #if HAS_NAME_WINDOW_PIXMAP
1285         if (w->pixmap)
1286         {
1287             XFreePixmap (dpy, w->pixmap);
1288             w->pixmap = None;
1289             if (w->picture)
1290             {
1291                 XRenderFreePicture (dpy, w->picture);
1292                 w->picture = None;
1293             }
1294         }
1295 #endif
1296         if (w->shadow)
1297         {
1298             XRenderFreePicture (dpy, w->shadow);
1299             w->shadow = None;
1300         }
1301     }
1302     w->a.width = ce->width;
1303     w->a.height = ce->height;
1304     w->a.border_width = ce->border_width;
1305     w->a.override_redirect = ce->override_redirect;
1306     restack_win (dpy, w, ce->above);
1307     if (damage)
1308     {
1309         XserverRegion   extents = win_extents (dpy, w);
1310         XFixesUnionRegion (dpy, damage, damage, extents);
1311         XFixesDestroyRegion (dpy, extents);
1312         add_damage (dpy, damage);
1313     }
1314     clipChanged = True;
1315 }
1316
1317 static void
1318 circulate_win (Display *dpy, XCirculateEvent *ce)
1319 {
1320     win     *w = find_win (dpy, ce->window);
1321     Window  new_above;
1322
1323     if (ce->place == PlaceOnTop)
1324         new_above = list->id;
1325     else
1326         new_above = None;
1327     restack_win (dpy, w, new_above);
1328     clipChanged = True;
1329 }
1330
1331 static void
1332 finish_destroy_win (Display *dpy, Window id, Bool gone)
1333 {
1334     win **prev, *w;
1335
1336     for (prev = &list; (w = *prev); prev = &w->next)
1337         if (w->id == id)
1338         {
1339             if (!gone)
1340                 finish_unmap_win (dpy, w);
1341             *prev = w->next;
1342             if (w->picture)
1343             {
1344                 set_ignore (dpy, NextRequest (dpy));
1345                 XRenderFreePicture (dpy, w->picture);
1346             }
1347             if (w->alphaPict)
1348             {
1349                 XRenderFreePicture (dpy, w->alphaPict);
1350                 w->alphaPict = None;
1351             }
1352             if (w->shadowPict)
1353             {
1354                 XRenderFreePicture (dpy, w->shadowPict);
1355                 w->shadowPict = None;
1356             }
1357             if (w->damage != None)
1358             {
1359                 set_ignore (dpy, NextRequest (dpy));
1360                 XDamageDestroy (dpy, w->damage);
1361             }
1362             cleanup_fade (dpy, w);
1363             free (w);
1364             break;
1365         }
1366 }
1367
1368 #if HAS_NAME_WINDOW_PIXMAP
1369 static void
1370 destroy_callback (Display *dpy, win *w, Bool gone)
1371 {
1372     finish_destroy_win (dpy, w->id, gone);
1373 }
1374 #endif
1375
1376 static void
1377 destroy_win (Display *dpy, Window id, Bool gone, Bool fade)
1378 {
1379     win *w = find_win (dpy, id);
1380 #if HAS_NAME_WINDOW_PIXMAP
1381     if (w && w->pixmap && fade && fadeWindows)
1382         set_fade (dpy, w, False, destroy_callback, gone);
1383     else
1384 #endif
1385     {
1386         finish_destroy_win (dpy, id, gone);
1387     }
1388 }
1389
1390 /*
1391 static void
1392 dump_win (win *w)
1393 {
1394     printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id,
1395             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
1396 }
1397
1398
1399 static void
1400 dump_wins (void)
1401 {
1402     win *w;
1403
1404     printf ("windows:\n");
1405     for (w = list; w; w = w->next)
1406         dump_win (w);
1407 }
1408 */
1409
1410 static void
1411 damage_win (Display *dpy, XDamageNotifyEvent *de)
1412 {
1413     repair_win (dpy, de->drawable);
1414 }
1415
1416 static int
1417 error (Display *dpy, XErrorEvent *ev)
1418 {
1419     int     o;
1420     char    *name = 0;
1421     
1422     if (should_ignore (dpy, ev->serial))
1423         return 0;
1424     
1425     
1426     o = ev->error_code - xfixes_error;
1427     switch (o) {
1428     case BadRegion: name = "BadRegion"; break;
1429     default: break;
1430     }
1431     o = ev->error_code - damage_error;
1432     switch (o) {
1433     case BadDamage: name = "BadDamage"; break;
1434     default: break;
1435     }
1436     o = ev->error_code - render_error;
1437     switch (o) {
1438     case BadPictFormat: name ="BadPictFormat"; break;
1439     case BadPicture: name ="BadPicture"; break;
1440     case BadPictOp: name ="BadPictOp"; break;
1441     case BadGlyphSet: name ="BadGlyphSet"; break;
1442     case BadGlyph: name ="BadGlyph"; break;
1443     default: break;
1444     }
1445         
1446     printf ("error %d request %d minor %d serial %d\n",
1447             ev->error_code, ev->request_code, ev->minor_code, ev->serial);
1448
1449     abort ();
1450     return 0;
1451 }
1452
1453 static void
1454 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
1455 {
1456     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
1457     
1458     add_damage (dpy, region);
1459 }
1460
1461
1462 static int
1463 ev_serial (XEvent *ev)
1464 {
1465     if (ev->type & 0x7f != KeymapNotify)
1466         return ev->xany.serial;
1467     return NextRequest (ev->xany.display);
1468 }
1469
1470
1471 static char *
1472 ev_name (XEvent *ev)
1473 {
1474     static char buf[128];
1475     switch (ev->type & 0x7f) {
1476     case Expose:
1477         return "Expose";
1478     case MapNotify:
1479         return "Map";
1480     case UnmapNotify:
1481         return "Unmap";
1482     case ReparentNotify:
1483         return "Reparent";
1484     case CirculateNotify:
1485         return "Circulate";
1486     default:
1487         if (ev->type == damage_event + XDamageNotify)
1488             return "Damage";
1489         sprintf (buf, "Event %d", ev->type);
1490         return buf;
1491     }
1492 }
1493
1494 static Window
1495 ev_window (XEvent *ev)
1496 {
1497     switch (ev->type) {
1498     case Expose:
1499         return ev->xexpose.window;
1500     case MapNotify:
1501         return ev->xmap.window;
1502     case UnmapNotify:
1503         return ev->xunmap.window;
1504     case ReparentNotify:
1505         return ev->xreparent.window;
1506     case CirculateNotify:
1507         return ev->xcirculate.window;
1508     default:
1509         if (ev->type == damage_event + XDamageNotify)
1510             return ((XDamageNotifyEvent *) ev)->drawable;
1511         return 0;
1512     }
1513 }
1514
1515 void
1516 usage (char *program)
1517 {
1518     fprintf (stderr, "usage: %s [-d display] [-n] [-s] [-c]\n", program);
1519     exit (1);
1520 }
1521
1522 int
1523 main (int argc, char **argv)
1524 {
1525     XEvent          ev;
1526     Window          root_return, parent_return;
1527     Window          *children;
1528     Pixmap          transPixmap;
1529     Pixmap          blackPixmap;
1530     unsigned int    nchildren;
1531     int             i;
1532     XRenderPictureAttributes    pa;
1533     XRenderColor                c;
1534     XRectangle      *expose_rects = 0;
1535     int             size_expose = 0;
1536     int             n_expose = 0;
1537     struct pollfd   ufd;
1538     int             n;
1539     int             last_update;
1540     int             now;
1541     int             p;
1542     int             composite_major, composite_minor;
1543     char            *display = 0;
1544     int             o;
1545
1546     while ((o = getopt (argc, argv, "d:scnf")) != -1)
1547     {
1548         switch (o) {
1549         case 'd':
1550             display = optarg;
1551             break;
1552         case 's':
1553             compMode = CompServerShadows;
1554             break;
1555         case 'c':
1556             compMode = CompClientShadows;
1557             break;
1558         case 'n':
1559             compMode = CompSimple;
1560             break;
1561         case 'f':
1562             fadeWindows = True;
1563             break;
1564         default:
1565             usage (argv[0]);
1566             break;
1567         }
1568     }
1569     
1570     dpy = XOpenDisplay (display);
1571     if (!dpy)
1572     {
1573         fprintf (stderr, "Can't open display\n");
1574         exit (1);
1575     }
1576     XSetErrorHandler (error);
1577 #if 0
1578     XSynchronize (dpy, 1);
1579 #endif
1580     scr = DefaultScreen (dpy);
1581     root = RootWindow (dpy, scr);
1582
1583     if (!XRenderQueryExtension (dpy, &render_event, &render_error))
1584     {
1585         fprintf (stderr, "No render extension\n");
1586         exit (1);
1587     }
1588     if (!XCompositeQueryExtension (dpy, &composite_event, &composite_error))
1589     {
1590         fprintf (stderr, "No composite extension\n");
1591         exit (1);
1592     }
1593     XCompositeQueryVersion (dpy, &composite_major, &composite_minor);
1594 #if HAS_NAME_WINDOW_PIXMAP
1595     if (composite_major > 0 || composite_minor >= 2)
1596         hasNamePixmap = True;
1597 #endif
1598
1599     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
1600     {
1601         fprintf (stderr, "No damage extension\n");
1602         exit (1);
1603     }
1604     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
1605     {
1606         fprintf (stderr, "No XFixes extension\n");
1607         exit (1);
1608     }
1609     /* get atoms */
1610     opacityAtom = XInternAtom (dpy, OPACITY_PROP, False);
1611
1612     pa.subwindow_mode = IncludeInferiors;
1613
1614     if (compMode == CompClientShadows)
1615         gaussianMap = make_gaussian_map(dpy, shadowRadius);
1616
1617     root_width = DisplayWidth (dpy, scr);
1618     root_height = DisplayHeight (dpy, scr);
1619
1620     rootPicture = XRenderCreatePicture (dpy, root, 
1621                                         XRenderFindVisualFormat (dpy,
1622                                                                  DefaultVisual (dpy, scr)),
1623                                         CPSubwindowMode,
1624                                         &pa);
1625     blackPicture = solid_picture (dpy, True, 1, 0, 0, 0);
1626     if (compMode == CompServerShadows)
1627         transBlackPicture = solid_picture (dpy, True, 0.3, 0, 0, 0);
1628     allDamage = None;
1629     clipChanged = True;
1630     XGrabServer (dpy);
1631     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
1632     XSelectInput (dpy, root, 
1633                   SubstructureNotifyMask|
1634                   ExposureMask|
1635                   StructureNotifyMask|
1636                   PropertyChangeMask);
1637     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
1638     for (i = 0; i < nchildren; i++)
1639         add_win (dpy, children[i], i ? children[i-1] : None);
1640     XFree (children);
1641     XUngrabServer (dpy);
1642     ufd.fd = ConnectionNumber (dpy);
1643     ufd.events = POLLIN;
1644     paint_all (dpy, None);
1645     for (;;)
1646     {
1647         /*      dump_wins (); */
1648         do {
1649             if (!QLength (dpy))
1650             {
1651                  if (poll (&ufd, 1, fade_timeout()) == 0)
1652                  {
1653                     run_fades (dpy);
1654                     break;
1655                  }
1656             }
1657
1658             XNextEvent (dpy, &ev);
1659             if (ev.type & 0x7f != KeymapNotify)
1660                 discard_ignore (dpy, ev.xany.serial);
1661 #if DEBUG_EVENTS
1662             printf ("event %10.10s serial 0x%08x window 0x%08x\n",
1663                     ev_name(&ev), ev_serial (&ev), ev_window (&ev));
1664 #endif
1665             switch (ev.type) {
1666             case CreateNotify:
1667                 add_win (dpy, ev.xcreatewindow.window, 0);
1668                 break;
1669             case ConfigureNotify:
1670                 configure_win (dpy, &ev.xconfigure);
1671                 break;
1672             case DestroyNotify:
1673                 destroy_win (dpy, ev.xdestroywindow.window, True, True);
1674                 break;
1675             case MapNotify:
1676                 map_win (dpy, ev.xmap.window, ev.xmap.serial, True);
1677                 break;
1678             case UnmapNotify:
1679                 unmap_win (dpy, ev.xunmap.window, True);
1680                 break;
1681             case ReparentNotify:
1682                 if (ev.xreparent.parent == root)
1683                     add_win (dpy, ev.xreparent.window, 0);
1684                 else
1685                     destroy_win (dpy, ev.xreparent.window, False, True);
1686                 break;
1687             case CirculateNotify:
1688                 circulate_win (dpy, &ev.xcirculate);
1689                 break;
1690             case Expose:
1691                 if (ev.xexpose.window == root)
1692                 {
1693                     int more = ev.xexpose.count + 1;
1694                     if (n_expose == size_expose)
1695                     {
1696                         if (expose_rects)
1697                         {
1698                             expose_rects = realloc (expose_rects, 
1699                                                     (size_expose + more) * 
1700                                                     sizeof (XRectangle));
1701                             size_expose += more;
1702                         }
1703                         else
1704                         {
1705                             expose_rects = malloc (more * sizeof (XRectangle));
1706                             size_expose = more;
1707                         }
1708                     }
1709                     expose_rects[n_expose].x = ev.xexpose.x;
1710                     expose_rects[n_expose].y = ev.xexpose.y;
1711                     expose_rects[n_expose].width = ev.xexpose.width;
1712                     expose_rects[n_expose].height = ev.xexpose.height;
1713                     n_expose++;
1714                     if (ev.xexpose.count == 0)
1715                     {
1716                         expose_root (dpy, root, expose_rects, n_expose);
1717                         n_expose = 0;
1718                     }
1719                 }
1720                 break;
1721             case PropertyNotify:
1722                 for (p = 0; backgroundProps[p]; p++)
1723                 {
1724                     if (ev.xproperty.atom == XInternAtom (dpy, backgroundProps[p], False))
1725                     {
1726                         if (rootTile)
1727                         {
1728                             XClearArea (dpy, root, 0, 0, 0, 0, True);
1729                             XRenderFreePicture (dpy, rootTile);
1730                             rootTile = None;
1731                             break;
1732                         }
1733                     }
1734                 }
1735                 /* check if Trans property was changed */
1736                 if (ev.xproperty.atom == opacityAtom)
1737                 {
1738                     /* reset mode and redraw window */
1739                     win * w = find_win(dpy, ev.xproperty.window);
1740                     if (w)
1741                     {
1742                         w->opacity = get_opacity_prop(dpy, w, OPAQUE);
1743                         determine_mode(dpy, w);
1744                     }
1745                 }
1746                 break;
1747             default:
1748                 if (ev.type == damage_event + XDamageNotify)
1749                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
1750                 break;
1751             }
1752         } while (QLength (dpy));
1753         if (allDamage)
1754         {
1755             static int  paint;
1756             paint_all (dpy, allDamage);
1757             paint++;
1758             XSync (dpy, False);
1759             allDamage = None;
1760             clipChanged = False;
1761         }
1762     }
1763 }