b9f11a3bd6f4a03e0ea7396d993f6e0b7f29c3c9
[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 <pthread.h>
40 #include <sys/types.h>
41 #include <signal.h>
42 #include <getopt.h>
43 #include <X11/Xlib.h>
44 #include <X11/Xutil.h>
45 #include <X11/Xatom.h>
46 #include <X11/extensions/Xcomposite.h>
47 #include <X11/extensions/Xdamage.h>
48 #include <X11/extensions/Xrender.h>
49
50 #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2
51 #define HAS_NAME_WINDOW_PIXMAP 1
52 #endif
53
54 #define CAN_DO_USABLE 0
55
56 typedef struct _ignore {
57     struct _ignore      *next;
58     unsigned long       sequence;
59 } ignore;
60
61 typedef struct _win {
62     struct _win         *next;
63     Window              id;
64 #if HAS_NAME_WINDOW_PIXMAP
65     Pixmap              pixmap;
66     pthread_t           pixmap_thread;
67     int                 pixmap_nthreads;
68     pthread_mutex_t     pixmap_lock;
69 #endif
70     XWindowAttributes   a;
71 #if CAN_DO_USABLE
72     Bool                usable;             /* mapped and all damaged at one point */
73     XRectangle          damage_bounds;      /* bounds of damage */
74 #endif
75     int                 mode;
76     int                 damaged;
77     Damage              damage;
78     Picture             picture;
79     Picture             alphaPict;
80     Picture             shadowPict;
81     XserverRegion       borderSize;
82     XserverRegion       extents;
83     Picture             shadow;
84     int                 shadow_dx;
85     int                 shadow_dy;
86     int                 shadow_width;
87     int                 shadow_height;
88     unsigned int        opacity;
89     Atom                windowType;
90     unsigned long       damage_sequence;    /* sequence when damage was created */
91     Bool                unmapped_configure;
92
93     /* for drawing translucent windows */
94     XserverRegion       borderClip;
95     struct _win         *prev_trans;
96 } win;
97
98 typedef struct _conv {
99     int     size;
100     double  *data;
101 } conv;
102
103 typedef struct _fade {
104     struct _fade        *next;
105     win                 *w;
106     double              cur;
107     double              finish;
108     double              step;
109     void                (*callback) (Display *dpy, win *w);
110     Display             *dpy;
111 } fade;
112
113 typedef struct _threaddata {
114     Display             *dpy;
115     win                 *w;
116     XserverRegion        extents;
117     pthread_t            prev_thread;
118 } threaddata;
119
120 win             *list;
121 fade            *fades;
122 Display         *dpy;
123 int             scr;
124 Window          root;
125 Picture         rootPicture;
126 Picture         rootBuffer;
127 Picture         blackPicture;
128 Picture         transBlackPicture;
129 Picture         rootTile;
130 XserverRegion   allDamage;
131 pthread_mutex_t damage_lock = PTHREAD_MUTEX_INITIALIZER;
132 Bool            clipChanged;
133 #if HAS_NAME_WINDOW_PIXMAP
134 Bool            hasNamePixmap;
135 #endif
136 int             root_height, root_width;
137 ignore          *ignore_head, **ignore_tail = &ignore_head;
138 int             xfixes_event, xfixes_error;
139 int             damage_event, damage_error;
140 int             composite_event, composite_error;
141 int             render_event, render_error;
142 Bool            synchronize;
143 int             composite_opcode;
144
145 /* find these once and be done with it */
146 Atom            opacityAtom;
147 Atom            winTypeAtom;
148 Atom            winDesktopAtom;
149 Atom            winDockAtom;
150 Atom            winToolbarAtom;
151 Atom            winMenuAtom;
152 Atom            winUtilAtom;
153 Atom            winSplashAtom;
154 Atom            winDialogAtom;
155 Atom            winNormalAtom;
156
157 /* opacity property name; sometime soon I'll write up an EWMH spec for it */
158 #define OPACITY_PROP    "_NET_WM_WINDOW_OPACITY"
159
160 #define TRANSLUCENT     0xe0000000
161 #define OPAQUE          0xffffffff
162
163 conv            *gaussianMap;
164
165 #define WINDOW_SOLID    0
166 #define WINDOW_TRANS    1
167 #define WINDOW_ARGB     2
168
169 #define TRANS_OPACITY   0.75
170
171 #define DEBUG_REPAINT 0
172 #define DEBUG_EVENTS 0
173 #define MONITOR_REPAINT 0
174
175 #define SHADOWS         1
176 #define SHARP_SHADOW    0
177
178 typedef enum _compMode {
179     CompSimple,         /* looks like a regular X server */
180     CompServerShadows,  /* use window alpha for shadow; sharp, but precise */
181     CompClientShadows,  /* use window extents for shadow, blurred */
182 } CompMode;
183
184 static void
185 determine_mode(Display *dpy, win *w);
186     
187 static double
188 get_opacity_percent(Display *dpy, win *w, double def);
189
190 static XserverRegion
191 win_extents (Display *dpy, win *w);
192
193 CompMode    compMode = CompSimple;
194
195 int         shadowRadius = 12;
196 int         shadowOffsetX = -15;
197 int         shadowOffsetY = -15;
198 double      shadowOpacity = .75;
199
200 double  fade_in_step =  0.028;
201 double  fade_out_step = 0.03;
202 int     fade_delta =    10;
203 int     fade_time =     0;
204 Bool    fadeWindows = False;
205 Bool    excludeDockShadows = False;
206 Bool    fadeTrans = False;
207
208 Bool    autoRedirect = False;
209
210 /* For shadow precomputation */
211 int            Gsize = -1;
212 unsigned char *shadowCorner = NULL;
213 unsigned char *shadowTop = NULL;
214
215 int
216 get_time_in_milliseconds ()
217 {
218     struct timeval  tv;
219
220     gettimeofday (&tv, NULL);
221     return tv.tv_sec * 1000 + tv.tv_usec / 1000;
222 }
223
224 fade *
225 find_fade (win *w)
226 {
227     fade    *f;
228     
229     for (f = fades; f; f = f->next)
230     {
231         if (f->w == w)
232             return f;
233     }
234     return 0;
235 }
236
237 void
238 dequeue_fade (Display *dpy, fade *f)
239 {
240     fade    **prev;
241
242     for (prev = &fades; *prev; prev = &(*prev)->next)
243         if (*prev == f)
244         {
245             *prev = f->next;
246             if (f->callback)
247                 (*f->callback) (dpy, f->w);
248             free (f);
249             break;
250         }
251 }
252
253 void
254 cleanup_fade (Display *dpy, win *w)
255 {
256     fade *f = find_fade (w);
257     if (f)
258         dequeue_fade (dpy, f);
259 }
260
261 void
262 enqueue_fade (Display *dpy, fade *f)
263 {
264     if (!fades)
265         fade_time = get_time_in_milliseconds () + fade_delta;
266     f->next = fades;
267     fades = f;
268 }
269
270 static void
271 set_fade (Display *dpy, win *w, double start, double finish, double step,
272           void (*callback) (Display *dpy, win *w),
273           Bool exec_callback, Bool override)
274 {
275     fade    *f;
276
277     f = find_fade (w);
278     if (!f)
279     {
280         f = malloc (sizeof (fade));
281         f->next = 0;
282         f->w = w;
283         f->cur = start;
284         enqueue_fade (dpy, f);
285     }
286     else if(!override)
287         return;
288     else
289     {
290         if (exec_callback)
291             if (f->callback)
292                 (*f->callback)(dpy, f->w);
293     }
294
295     if (finish < 0)
296         finish = 0;
297     if (finish > 1)
298         finish = 1;
299     f->finish = finish;
300     if (f->cur < finish)
301         f->step = step;
302     else if (f->cur > finish)
303         f->step = -step;
304     f->callback = callback;
305     w->opacity = f->cur * OPAQUE;
306 #if 0
307     printf ("set_fade start %g step %g\n", f->cur, f->step);
308 #endif
309     determine_mode (dpy, w);
310     if (w->shadow)
311     {
312         XRenderFreePicture (dpy, w->shadow);
313         w->shadow = None;
314         w->extents = win_extents (dpy, w);
315     }
316 }
317
318 int
319 fade_timeout (void)
320 {
321     int now;
322     int delta;
323     if (!fades)
324         return -1;
325     now = get_time_in_milliseconds();
326     delta = fade_time - now;
327     if (delta < 0)
328         delta = 0;
329 /*    printf ("timeout %d\n", delta); */
330     return delta;
331 }
332
333 void
334 run_fades (Display *dpy)
335 {
336     int     now = get_time_in_milliseconds();
337     fade    *next = fades;
338     int     steps;
339     Bool    need_dequeue;
340
341 #if 0
342     printf ("run fades\n");
343 #endif
344     if (fade_time - now > 0)
345         return;
346     steps = 1 + (now - fade_time) / fade_delta;
347
348     while (next)
349     {
350         fade *f = next;
351         win *w = f->w;
352         next = f->next;
353         f->cur += f->step * steps;
354         if (f->cur >= 1)
355             f->cur = 1;
356         else if (f->cur < 0)
357             f->cur = 0;
358 #if 0
359         printf ("opacity now %g\n", f->cur);
360 #endif
361         w->opacity = f->cur * OPAQUE;
362         need_dequeue = False;
363         if (f->step > 0)
364         {
365             if (f->cur >= f->finish)
366             {
367                 w->opacity = f->finish*OPAQUE;
368                 need_dequeue = True;
369             }
370         }
371         else
372         {
373             if (f->cur <= f->finish)
374             {
375                 w->opacity = f->finish*OPAQUE;
376                 need_dequeue = True;
377             }
378         }
379         determine_mode (dpy, w);
380         if (w->shadow)
381         {
382             XRenderFreePicture (dpy, w->shadow);
383             w->shadow = None;
384             w->extents = win_extents(dpy, w);
385         }
386         /* Must do this last as it might destroy f->w in callbacks */
387         if (need_dequeue)
388                 dequeue_fade (dpy, f);
389     }
390     fade_time = now + fade_delta;
391 }
392
393 static double
394 gaussian (double r, double x, double y)
395 {
396     return ((1 / (sqrt (2 * M_PI * r))) *
397             exp ((- (x * x + y * y)) / (2 * r * r)));
398 }
399
400
401 static conv *
402 make_gaussian_map (Display *dpy, double r)
403 {
404     conv            *c;
405     int             size = ((int) ceil ((r * 3)) + 1) & ~1;
406     int             center = size / 2;
407     int             x, y;
408     double          t;
409     double          g;
410     
411     c = malloc (sizeof (conv) + size * size * sizeof (double));
412     c->size = size;
413     c->data = (double *) (c + 1);
414     t = 0.0;
415     for (y = 0; y < size; y++)
416         for (x = 0; x < size; x++)
417         {
418             g = gaussian (r, (double) (x - center), (double) (y - center));
419             t += g;
420             c->data[y * size + x] = g;
421         }
422 /*    printf ("gaussian total %f\n", t); */
423     for (y = 0; y < size; y++)
424         for (x = 0; x < size; x++)
425         {
426             c->data[y*size + x] /= t;
427         }
428     return c;
429 }
430
431 /*
432  * A picture will help
433  *
434  *      -center   0                width  width+center
435  *  -center +-----+-------------------+-----+
436  *          |     |                   |     |
437  *          |     |                   |     |
438  *        0 +-----+-------------------+-----+
439  *          |     |                   |     |
440  *          |     |                   |     |
441  *          |     |                   |     |
442  *   height +-----+-------------------+-----+
443  *          |     |                   |     |
444  * height+  |     |                   |     |
445  *  center  +-----+-------------------+-----+
446  */
447  
448 static unsigned char
449 sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
450 {
451     int     fx, fy;
452     double  *g_data;
453     double  *g_line = map->data;
454     int     g_size = map->size;
455     int     center = g_size / 2;
456     int     fx_start, fx_end;
457     int     fy_start, fy_end;
458     double  v;
459     
460     /*
461      * Compute set of filter values which are "in range",
462      * that's the set with:
463      *  0 <= x + (fx-center) && x + (fx-center) < width &&
464      *  0 <= y + (fy-center) && y + (fy-center) < height
465      *
466      *  0 <= x + (fx - center)  x + fx - center < width
467      *  center - x <= fx        fx < width + center - x
468      */
469
470     fx_start = center - x;
471     if (fx_start < 0)
472         fx_start = 0;
473     fx_end = width + center - x;
474     if (fx_end > g_size)
475         fx_end = g_size;
476
477     fy_start = center - y;
478     if (fy_start < 0)
479         fy_start = 0;
480     fy_end = height + center - y;
481     if (fy_end > g_size)
482         fy_end = g_size;
483
484     g_line = g_line + fy_start * g_size + fx_start;
485     
486     v = 0;
487     for (fy = fy_start; fy < fy_end; fy++)
488     {
489         g_data = g_line;
490         g_line += g_size;
491         
492         for (fx = fx_start; fx < fx_end; fx++)
493             v += *g_data++;
494     }
495     if (v > 1)
496         v = 1;
497     
498     return ((unsigned char) (v * opacity * 255.0));
499 }
500
501 /* precompute shadow corners and sides to save time for large windows */
502 static void
503 presum_gaussian (conv *map)
504 {
505     int center = map->size/2;
506     int opacity, x, y;
507
508     Gsize = map->size;
509
510     if (shadowCorner)
511         free ((void *)shadowCorner);
512     if (shadowTop)
513         free ((void *)shadowTop);
514
515     shadowCorner = (unsigned char *)(malloc ((Gsize + 1) * (Gsize + 1) * 26));
516     shadowTop = (unsigned char *)(malloc ((Gsize + 1) * 26));
517     
518     for (x = 0; x <= Gsize; x++)
519     {
520         shadowTop[25 * (Gsize + 1) + x] = sum_gaussian (map, 1, x - center, center, Gsize * 2, Gsize * 2);
521         for(opacity = 0; opacity < 25; opacity++)
522             shadowTop[opacity * (Gsize + 1) + x] = shadowTop[25 * (Gsize + 1) + x] * opacity / 25;
523         for(y = 0; y <= x; y++)
524         {
525             shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]
526                 = sum_gaussian (map, 1, x - center, y - center, Gsize * 2, Gsize * 2);
527             shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y]
528                 = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x];
529             for(opacity = 0; opacity < 25; opacity++)
530                 shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]
531                     = shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y]
532                     = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] * opacity / 25;
533         }
534     }
535 }
536
537 static XImage *
538 make_shadow (Display *dpy, double opacity, int width, int height)
539 {
540     XImage          *ximage;
541     unsigned char   *data;
542     int             gsize = gaussianMap->size;
543     int             ylimit, xlimit;
544     int             swidth = width + gsize;
545     int             sheight = height + gsize;
546     int             center = gsize / 2;
547     int             x, y;
548     unsigned char   d;
549     int             x_diff;
550     int             opacity_int = (int)(opacity * 25);
551     data = malloc (swidth * sheight * sizeof (unsigned char));
552     if (!data)
553         return 0;
554     ximage = XCreateImage (dpy,
555                            DefaultVisual(dpy, DefaultScreen(dpy)),
556                            8,
557                            ZPixmap,
558                            0,
559                            (char *) data,
560                            swidth, sheight, 8, swidth * sizeof (unsigned char));
561     if (!ximage)
562     {
563         free (data);
564         return 0;
565     }
566     /*
567      * Build the gaussian in sections
568      */
569
570     /*
571      * center (fill the complete data array)
572      */
573     if (Gsize > 0)
574         d = shadowTop[opacity_int * (Gsize + 1) + Gsize];
575     else
576         d = sum_gaussian (gaussianMap, opacity, center, center, width, height);
577     memset(data, d, sheight * swidth);
578     
579     /*
580      * corners
581      */
582     ylimit = gsize;
583     if (ylimit > sheight / 2)
584         ylimit = (sheight + 1) / 2;
585     xlimit = gsize;
586     if (xlimit > swidth / 2)
587         xlimit = (swidth + 1) / 2;
588
589     for (y = 0; y < ylimit; y++)
590         for (x = 0; x < xlimit; x++)
591         {
592             if (xlimit == Gsize && ylimit == Gsize)
593                 d = shadowCorner[opacity_int * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x];
594             else
595                 d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height);
596             data[y * swidth + x] = d;
597             data[(sheight - y - 1) * swidth + x] = d;
598             data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d;
599             data[y * swidth + (swidth - x - 1)] = d;
600         }
601
602     /*
603      * top/bottom
604      */
605     x_diff = swidth - (gsize * 2);
606     if (x_diff > 0 && ylimit > 0)
607     {
608         for (y = 0; y < ylimit; y++)
609         {
610             if (ylimit == Gsize)
611                 d = shadowTop[opacity_int * (Gsize + 1) + y];
612             else
613                 d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height);
614             memset (&data[y * swidth + gsize], d, x_diff);
615             memset (&data[(sheight - y - 1) * swidth + gsize], d, x_diff);
616         }
617     }
618
619     /*
620      * sides
621      */
622     
623     for (x = 0; x < xlimit; x++)
624     {
625         if (xlimit == Gsize)
626             d = shadowTop[opacity_int * (Gsize + 1) + x];
627         else
628             d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height);
629         for (y = gsize; y < sheight - gsize; y++)
630         {
631             data[y * swidth + x] = d;
632             data[y * swidth + (swidth - x - 1)] = d;
633         }
634     }
635
636     return ximage;
637 }
638
639 static Picture
640 shadow_picture (Display *dpy, double opacity, Picture alpha_pict, int width, int height, int *wp, int *hp)
641 {
642     XImage  *shadowImage;
643     Pixmap  shadowPixmap;
644     Picture shadowPicture;
645     GC      gc;
646     
647     shadowImage = make_shadow (dpy, opacity, width, height);
648     if (!shadowImage)
649         return None;
650     shadowPixmap = XCreatePixmap (dpy, root, 
651                                   shadowImage->width,
652                                   shadowImage->height,
653                                   8);
654     if (!shadowPixmap)
655     {
656         XDestroyImage (shadowImage);
657         return None;
658     }
659
660     shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
661                                           XRenderFindStandardFormat (dpy, PictStandardA8),
662                                           0, 0);
663     if (!shadowPicture)
664     {
665         XDestroyImage (shadowImage);
666         XFreePixmap (dpy, shadowPixmap);
667         return None;
668     }
669
670     gc = XCreateGC (dpy, shadowPixmap, 0, 0);
671     if (!gc)
672     {
673         XDestroyImage (shadowImage);
674         XFreePixmap (dpy, shadowPixmap);
675         XRenderFreePicture (dpy, shadowPicture);
676         return None;
677     }
678     
679     XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 
680                shadowImage->width,
681                shadowImage->height);
682     *wp = shadowImage->width;
683     *hp = shadowImage->height;
684     XFreeGC (dpy, gc);
685     XDestroyImage (shadowImage);
686     XFreePixmap (dpy, shadowPixmap);
687     return shadowPicture;
688 }
689
690 Picture
691 solid_picture (Display *dpy, Bool argb, double a, double r, double g, double b)
692 {
693     Pixmap                      pixmap;
694     Picture                     picture;
695     XRenderPictureAttributes    pa;
696     XRenderColor                c;
697
698     pixmap = XCreatePixmap (dpy, root, 1, 1, argb ? 32 : 8);
699     if (!pixmap)
700         return None;
701
702     pa.repeat = True;
703     picture = XRenderCreatePicture (dpy, pixmap,
704                                     XRenderFindStandardFormat (dpy, argb ? PictStandardARGB32 : PictStandardA8),
705                                     CPRepeat,
706                                     &pa);
707     if (!picture)
708     {
709         XFreePixmap (dpy, pixmap);
710         return None;
711     }
712
713     c.alpha = a * 0xffff;
714     c.red = r * 0xffff;
715     c.green = g * 0xffff;
716     c.blue = b * 0xffff;
717     XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 0, 0, 1, 1);
718     XFreePixmap (dpy, pixmap);
719     return picture;
720 }
721
722 void
723 discard_ignore (Display *dpy, unsigned long sequence)
724 {
725     while (ignore_head)
726     {
727         if ((long) (sequence - ignore_head->sequence) > 0)
728         {
729             ignore  *next = ignore_head->next;
730             free (ignore_head);
731             ignore_head = next;
732             if (!ignore_head)
733                 ignore_tail = &ignore_head;
734         }
735         else
736             break;
737     }
738 }
739
740 void
741 set_ignore (Display *dpy, unsigned long sequence)
742 {
743     ignore  *i = malloc (sizeof (ignore));
744     if (!i)
745         return;
746     i->sequence = sequence;
747     i->next = 0;
748     *ignore_tail = i;
749     ignore_tail = &i->next;
750 }
751
752 int
753 should_ignore (Display *dpy, unsigned long sequence)
754 {
755     discard_ignore (dpy, sequence);
756     return ignore_head && ignore_head->sequence == sequence;
757 }
758
759 static win *
760 find_win (Display *dpy, Window id)
761 {
762     win *w;
763
764     for (w = list; w; w = w->next)
765         if (w->id == id)
766             return w;
767     return 0;
768 }
769
770 static const char *backgroundProps[] = {
771     "_XROOTPMAP_ID",
772     "_XSETROOT_ID",
773     0,
774 };
775     
776 static Picture
777 root_tile (Display *dpy)
778 {
779     Picture         picture;
780     Atom            actual_type;
781     Pixmap          pixmap;
782     int             actual_format;
783     unsigned long   nitems;
784     unsigned long   bytes_after;
785     unsigned char   *prop;
786     Bool            fill;
787     XRenderPictureAttributes    pa;
788     int             p;
789
790     pixmap = None;
791     for (p = 0; backgroundProps[p]; p++)
792     {
793         if (XGetWindowProperty (dpy, root, XInternAtom (dpy, backgroundProps[p], False),
794                                 0, 4, False, AnyPropertyType,
795                                 &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success &&
796             actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1)
797         {
798             memcpy (&pixmap, prop, 4);
799             XFree (prop);
800             fill = False;
801             break;
802         }
803     }
804     if (!pixmap)
805     {
806         pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
807         fill = True;
808     }
809     pa.repeat = True;
810     picture = XRenderCreatePicture (dpy, pixmap,
811                                     XRenderFindVisualFormat (dpy,
812                                                              DefaultVisual (dpy, scr)),
813                                     CPRepeat, &pa);
814     if (fill)
815     {
816         XRenderColor    c;
817         
818         c.red = c.green = c.blue = 0x8080;
819         c.alpha = 0xffff;
820         XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 
821                               0, 0, 1, 1);
822     }
823     return picture;
824 }
825
826 static void
827 paint_root (Display *dpy)
828 {
829     if (!rootTile)
830         rootTile = root_tile (dpy);
831     
832     XRenderComposite (dpy, PictOpSrc,
833                       rootTile, None, rootBuffer,
834                       0, 0, 0, 0, 0, 0, root_width, root_height);
835 }
836
837 static XserverRegion
838 win_extents (Display *dpy, win *w)
839 {
840     XRectangle      r;
841     
842     r.x = w->a.x;
843     r.y = w->a.y;
844     r.width = w->a.width + w->a.border_width * 2;
845     r.height = w->a.height + w->a.border_width * 2;
846     if (compMode != CompSimple && !(w->windowType == winDockAtom && excludeDockShadows))
847     {
848         if (compMode == CompServerShadows || w->mode != WINDOW_ARGB)
849         {
850             XRectangle  sr;
851
852             if (compMode == CompServerShadows)
853             {
854                 w->shadow_dx = 2;
855                 w->shadow_dy = 7;
856                 w->shadow_width = w->a.width;
857                 w->shadow_height = w->a.height;
858             }
859             else
860             {
861                 w->shadow_dx = shadowOffsetX;
862                 w->shadow_dy = shadowOffsetY;
863                 if (!w->shadow)
864                 {
865                     double      opacity = shadowOpacity;
866                     if (w->mode == WINDOW_TRANS)
867                         opacity = opacity * ((double)w->opacity)/((double)OPAQUE);
868                     w->shadow = shadow_picture (dpy, opacity, w->alphaPict,
869                                                 w->a.width + w->a.border_width * 2,
870                                                 w->a.height + w->a.border_width * 2,
871                                                 &w->shadow_width, &w->shadow_height);
872                 }
873             }
874             sr.x = w->a.x + w->shadow_dx;
875             sr.y = w->a.y + w->shadow_dy;
876             sr.width = w->shadow_width;
877             sr.height = w->shadow_height;
878             if (sr.x < r.x)
879             {
880                 r.width = (r.x + r.width) - sr.x;
881                 r.x = sr.x;
882             }
883             if (sr.y < r.y)
884             {
885                 r.height = (r.y + r.height) - sr.y;
886                 r.y = sr.y;
887             }
888             if (sr.x + sr.width > r.x + r.width)
889                 r.width = sr.x + sr.width - r.x;
890             if (sr.y + sr.height > r.y + r.height)
891                 r.height = sr.y + sr.height - r.y;
892         }
893     }
894     return XFixesCreateRegion (dpy, &r, 1);
895 }
896
897 static XserverRegion
898 border_size (Display *dpy, win *w)
899 {
900     XserverRegion   border;
901     /*
902      * if window doesn't exist anymore,  this will generate an error
903      * as well as not generate a region.  Perhaps a better XFixes
904      * architecture would be to have a request that copies instead
905      * of creates, that way you'd just end up with an empty region
906      * instead of an invalid XID.
907      */
908     set_ignore (dpy, NextRequest (dpy));
909     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
910     /* translate this */
911     set_ignore (dpy, NextRequest (dpy));
912     XFixesTranslateRegion (dpy, border,
913                            w->a.x + w->a.border_width,
914                            w->a.y + w->a.border_width);
915     return border;
916 }
917
918 static void
919 add_damage (Display *dpy, XserverRegion damage)
920 {
921     pthread_mutex_lock (&damage_lock);
922     if (allDamage)
923     {
924         XFixesUnionRegion (dpy, allDamage, allDamage, damage);
925         XFixesDestroyRegion (dpy, damage);
926     }
927     else
928         allDamage = damage;
929     pthread_mutex_unlock (&damage_lock);
930 }
931
932 static void
933 repair_win (Display *dpy, win *w)
934 {
935     XserverRegion   parts;
936
937     if (!w->damaged)
938     {
939         parts = win_extents (dpy, w);
940         set_ignore (dpy, NextRequest (dpy));
941         XDamageSubtract (dpy, w->damage, None, None);
942     }
943     else
944     {
945         XserverRegion   o;
946         parts = XFixesCreateRegion (dpy, 0, 0);
947         set_ignore (dpy, NextRequest (dpy));
948         XDamageSubtract (dpy, w->damage, None, parts);
949         XFixesTranslateRegion (dpy, parts,
950                                w->a.x + w->a.border_width,
951                                w->a.y + w->a.border_width);
952         if (compMode == CompServerShadows)
953         {
954             o = XFixesCreateRegion (dpy, 0, 0);
955             XFixesCopyRegion (dpy, o, parts);
956             XFixesTranslateRegion (dpy, o, w->shadow_dx, w->shadow_dy);
957             XFixesUnionRegion (dpy, parts, parts, o);
958             XFixesDestroyRegion (dpy, o);
959         }
960     }
961     if (!w->picture)
962         printf("adding damage 0x%x 0x%x\n", w->id, w->picture);
963     add_damage (dpy, parts);
964     w->damaged = 1;
965 }
966
967 #if HAS_NAME_WINDOW_PIXMAP
968 static void*
969 get_pixmap_thread (void *vdata)
970 {
971     threaddata *d = vdata;
972     Pixmap pix;
973     Picture pic;
974
975     /* if another thread is already running for this window, then wait for it
976        to exit before start */
977     if (d->prev_thread != 0) {
978         void *ret;
979
980         pthread_join (d->prev_thread, &ret);
981     }
982
983     pix = None;
984     pic = None;
985
986     printf("getting pixmap for 0x%x\n", d->w->id);
987
988     /* this is the slow stuff */
989     XLockDisplay (d->dpy);
990     pix = XCompositeNameWindowPixmap (d->dpy, d->w->id);
991     if (pix) {
992         XRenderPictureAttributes        pa;
993         XRenderPictFormat               *format;
994
995         format = XRenderFindVisualFormat (d->dpy, d->w->a.visual);
996         pa.subwindow_mode = IncludeInferiors;
997         pic = XRenderCreatePicture (d->dpy, pix,
998                                     format,
999                                     CPSubwindowMode,
1000                                     &pa);
1001     }
1002     XUnlockDisplay (d->dpy);
1003
1004     /* we're done the slow stuff so lock and set things */
1005
1006     if (pic) {
1007         pthread_mutex_lock (&d->w->pixmap_lock);
1008
1009         if (d->w->pixmap)
1010         {
1011             XFreePixmap (dpy, d->w->pixmap);
1012             d->w->pixmap = None;
1013         }
1014
1015         if (d->w->picture)
1016         {
1017             XRenderFreePicture (dpy, d->w->picture);
1018             d->w->picture = None;
1019         }
1020
1021         d->w->pixmap = pix;
1022         d->w->picture = pic;
1023
1024         pthread_mutex_unlock (&d->w->pixmap_lock);
1025
1026         /* make the window redraw */
1027         //add_damage(d->dpy, d->extents);
1028         /* wake up the main loop */
1029         kill(getpid(), SIGURG);
1030     }
1031
1032     free(vdata);
1033
1034     pthread_mutex_lock (&d->w->pixmap_lock);
1035     d->w->pixmap_nthreads--;
1036     pthread_mutex_unlock (&d->w->pixmap_lock);
1037
1038     return NULL;
1039 }
1040 #endif
1041
1042 #if HAS_NAME_WINDOW_PIXMAP
1043 static void
1044 start_get_pixmap_thread (Display *dpy, win *w, XserverRegion extents)
1045 {
1046     if (hasNamePixmap) {
1047         threaddata  *data;
1048
1049         pthread_mutex_lock (&w->pixmap_lock);
1050         w->pixmap_nthreads++;
1051         pthread_mutex_unlock (&w->pixmap_lock);
1052
1053         data = malloc (sizeof (threaddata));
1054         data->dpy = dpy;
1055         data->w = w;
1056         data->extents = extents;
1057         data->prev_thread = w->pixmap_thread;
1058         if (pthread_create (&w->pixmap_thread, NULL, get_pixmap_thread, data))
1059         {
1060             /* error */
1061             pthread_mutex_lock (&w->pixmap_lock);
1062             w->pixmap_nthreads--;
1063             pthread_mutex_unlock (&w->pixmap_lock);
1064         }
1065     }
1066 }
1067 #endif
1068
1069 static Bool
1070 paint_all (Display *dpy, XserverRegion region)
1071 {
1072     win *w;
1073     win *t = 0;
1074     Bool pending = False;
1075     
1076     if (!region)
1077     {
1078         XRectangle  r;
1079         r.x = 0;
1080         r.y = 0;
1081         r.width = root_width;
1082         r.height = root_height;
1083         region = XFixesCreateRegion (dpy, &r, 1);
1084     }
1085 #if MONITOR_REPAINT
1086     rootBuffer = rootPicture;
1087 #else
1088     if (!rootBuffer)
1089     {
1090         Pixmap  rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
1091                                             DefaultDepth (dpy, scr));
1092         rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
1093                                            XRenderFindVisualFormat (dpy,
1094                                                                     DefaultVisual (dpy, scr)),
1095                                            0, 0);
1096         XFreePixmap (dpy, rootPixmap);
1097     }
1098 #endif
1099     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
1100 #if MONITOR_REPAINT
1101     XRenderComposite (dpy, PictOpSrc, blackPicture, None, rootPicture,
1102                       0, 0, 0, 0, 0, 0, root_width, root_height);
1103 #endif
1104 #if DEBUG_REPAINT
1105     printf ("paint:");
1106 #endif
1107     for (w = list; w; w = w->next)
1108     {
1109 #if CAN_DO_USABLE
1110         if (!w->usable)
1111             continue;
1112 #endif
1113         /* never painted, ignore it */
1114         if (!w->damaged)
1115             continue;
1116         /* if invisible, ignore it */
1117         if (w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1
1118             || w->a.x >= root_width || w->a.y >= root_height)
1119             continue;
1120
1121 #if HAS_NAME_WINDOW_PIXMAP
1122         pthread_mutex_lock (&w->pixmap_lock);
1123 #endif
1124         if (!w->picture)
1125         {
1126             XRenderPictureAttributes    pa;
1127             XRenderPictFormat           *format;
1128             
1129             format = XRenderFindVisualFormat (dpy, w->a.visual);
1130             pa.subwindow_mode = IncludeInferiors;
1131             w->picture = XRenderCreatePicture (dpy, w->id,
1132                                                format,
1133                                                CPSubwindowMode,
1134                                                &pa);
1135             printf("got temp picture for 0x%x\n", w->id);
1136
1137 #if HAS_NAME_WINDOW_PIXMAP
1138             pending = True;
1139 #endif
1140         }
1141 #if HAS_NAME_WINDOW_PIXMAP
1142         pthread_mutex_unlock (&w->pixmap_lock);
1143 #endif
1144
1145 #if DEBUG_REPAINT
1146         printf (" 0x%x", w->id);
1147 #endif
1148         if (clipChanged)
1149         {
1150             if (w->borderSize)
1151             {
1152                 set_ignore (dpy, NextRequest (dpy));
1153                 XFixesDestroyRegion (dpy, w->borderSize);
1154                 w->borderSize = None;
1155             }
1156             if (w->extents)
1157             {
1158                 XFixesDestroyRegion (dpy, w->extents);
1159                 w->extents = None;
1160             }
1161             if (w->borderClip)
1162             {
1163                 XFixesDestroyRegion (dpy, w->borderClip);
1164                 w->borderClip = None;
1165             }
1166         }
1167         if (!w->borderSize)
1168             w->borderSize = border_size (dpy, w);
1169         if (!w->extents)
1170             w->extents = win_extents (dpy, w);
1171         if (w->mode == WINDOW_SOLID)
1172         {
1173             int x, y, wid, hei;
1174 #if HAS_NAME_WINDOW_PIXMAP
1175             x = w->a.x;
1176             y = w->a.y;
1177             wid = w->a.width + w->a.border_width * 2;
1178             hei = w->a.height + w->a.border_width * 2;
1179 #else
1180             x = w->a.x + w->a.border_width;
1181             y = w->a.y + w->a.border_width;
1182             wid = w->a.width;
1183             hei = w->a.height;
1184 #endif
1185             XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
1186             set_ignore (dpy, NextRequest (dpy));
1187             XFixesSubtractRegion (dpy, region, region, w->borderSize);
1188             set_ignore (dpy, NextRequest (dpy));
1189             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
1190                               0, 0, 0, 0, 
1191                               x, y, wid, hei);
1192         }
1193         if (!w->borderClip)
1194         {
1195             w->borderClip = XFixesCreateRegion (dpy, 0, 0);
1196             XFixesCopyRegion (dpy, w->borderClip, region);
1197         }
1198         w->prev_trans = t;
1199         t = w;
1200     }
1201 #if DEBUG_REPAINT
1202     printf ("\n");
1203     fflush (stdout);
1204 #endif
1205     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
1206     paint_root (dpy);
1207     for (w = t; w; w = w->prev_trans)
1208     {
1209         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
1210         switch (compMode) {
1211         case CompSimple:
1212             break;
1213         case CompServerShadows:
1214             /* dont' bother drawing shadows on desktop windows */
1215             if (w->windowType == winDesktopAtom)
1216                 break;
1217             set_ignore (dpy, NextRequest (dpy));
1218             if (w->opacity != OPAQUE && !w->shadowPict)
1219                 w->shadowPict = solid_picture (dpy, True,
1220                                                (double) w->opacity / OPAQUE * 0.3,
1221                                                0, 0, 0);
1222             XRenderComposite (dpy, PictOpOver, 
1223                               w->shadowPict ? w->shadowPict : transBlackPicture,
1224                               w->picture, rootBuffer,
1225                               0, 0, 0, 0,
1226                               w->a.x + w->shadow_dx,
1227                               w->a.y + w->shadow_dy,
1228                               w->shadow_width, w->shadow_height);
1229             break;
1230         case CompClientShadows:
1231             /* don't bother drawing shadows on desktop windows */
1232             if (w->shadow && w->windowType != winDesktopAtom)
1233             {
1234                 XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer,
1235                                   0, 0, 0, 0,
1236                                   w->a.x + w->shadow_dx,
1237                                   w->a.y + w->shadow_dy,
1238                                   w->shadow_width, w->shadow_height);
1239             }
1240             break;
1241         }
1242         if (w->opacity != OPAQUE && !w->alphaPict)
1243             w->alphaPict = solid_picture (dpy, False, 
1244                                           (double) w->opacity / OPAQUE, 0, 0, 0);
1245         if (w->mode == WINDOW_TRANS)
1246         {
1247             int x, y, wid, hei;
1248 #if HAS_NAME_WINDOW_PIXMAP
1249             x = w->a.x;
1250             y = w->a.y;
1251             wid = w->a.width + w->a.border_width * 2;
1252             hei = w->a.height + w->a.border_width * 2;
1253 #else
1254             x = w->a.x + w->a.border_width;
1255             y = w->a.y + w->a.border_width;
1256             wid = w->a.width;
1257             hei = w->a.height;
1258 #endif
1259             set_ignore (dpy, NextRequest (dpy));
1260             XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer,
1261                               0, 0, 0, 0, 
1262                               x, y, wid, hei);
1263         }
1264         else if (w->mode == WINDOW_ARGB)
1265         {
1266             int x, y, wid, hei;
1267 #if HAS_NAME_WINDOW_PIXMAP
1268             x = w->a.x;
1269             y = w->a.y;
1270             wid = w->a.width + w->a.border_width * 2;
1271             hei = w->a.height + w->a.border_width * 2;
1272 #else
1273             x = w->a.x + w->a.border_width;
1274             y = w->a.y + w->a.border_width;
1275             wid = w->a.width;
1276             hei = w->a.height;
1277 #endif
1278             set_ignore (dpy, NextRequest (dpy));
1279             XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer,
1280                               0, 0, 0, 0, 
1281                               x, y, wid, hei);
1282         }
1283         XFixesDestroyRegion (dpy, w->borderClip);
1284         w->borderClip = None;
1285     }
1286     XFixesDestroyRegion (dpy, region);
1287     if (rootBuffer != rootPicture)
1288     {
1289         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
1290         XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
1291                           0, 0, 0, 0, 0, 0, root_width, root_height);
1292     }
1293
1294     return pending;
1295 }
1296
1297 static unsigned int
1298 get_opacity_prop (Display *dpy, win *w, unsigned int def);
1299
1300 static void
1301 map_win (Display *dpy, Window id, unsigned long sequence, Bool fade)
1302 {
1303     win         *w = find_win (dpy, id);
1304
1305     if (!w)
1306         return;
1307
1308     w->a.map_state = IsViewable;
1309     
1310     /* This needs to be here or else we lose transparency messages */
1311     XSelectInput (dpy, id, PropertyChangeMask);
1312
1313     /* This needs to be here since we don't get PropertyNotify when unmapped */
1314     w->opacity = get_opacity_prop (dpy, w, OPAQUE);
1315     determine_mode (dpy, w);
1316
1317 #if CAN_DO_USABLE
1318     w->damage_bounds.x = w->damage_bounds.y = 0;
1319     w->damage_bounds.width = w->damage_bounds.height = 0;
1320 #endif
1321     w->damaged = 0;
1322
1323     if (fade && fadeWindows)
1324         set_fade (dpy, w, 0, get_opacity_percent (dpy, w, 1.0), fade_in_step, 0, True, True);
1325
1326 #if HAS_NAME_WINDOW_PIXMAP
1327     start_get_pixmap_thread (dpy, w, 0);
1328 #endif
1329 }
1330
1331 static void
1332 finish_unmap_win (Display *dpy, win *w)
1333 {
1334     w->damaged = 0;
1335 #if CAN_DO_USABLE
1336     w->usable = False;
1337 #endif
1338     if (w->extents != None)
1339     {
1340         add_damage (dpy, w->extents);    /* destroys region */
1341         w->extents = None;
1342     }
1343
1344     if (w->unmapped_configure) {
1345 #if HAS_NAME_WINDOW_PIXMAP
1346         pthread_mutex_lock (&w->pixmap_lock);
1347         if (w->pixmap)
1348         {
1349             XFreePixmap (dpy, w->pixmap);
1350             w->pixmap = None;
1351             if (w->picture)
1352             {
1353                 XRenderFreePicture (dpy, w->picture);
1354                 w->picture = None;
1355             }
1356         }
1357         pthread_mutex_unlock (&w->pixmap_lock);
1358 #endif
1359     }
1360     
1361     /* don't care about properties anymore */
1362     set_ignore (dpy, NextRequest (dpy));
1363     XSelectInput(dpy, w->id, 0);
1364
1365     if (w->borderSize)
1366     {
1367         set_ignore (dpy, NextRequest (dpy));
1368         XFixesDestroyRegion (dpy, w->borderSize);
1369         w->borderSize = None;
1370     }
1371     if (w->shadow)
1372     {
1373         XRenderFreePicture (dpy, w->shadow);
1374         w->shadow = None;
1375     }
1376     if (w->borderClip)
1377     {
1378         XFixesDestroyRegion (dpy, w->borderClip);
1379         w->borderClip = None;
1380     }
1381
1382     clipChanged = True;
1383 }
1384
1385 #if HAS_NAME_WINDOW_PIXMAP
1386 static void
1387 unmap_callback (Display *dpy, win *w)
1388 {
1389     finish_unmap_win (dpy, w);
1390 }
1391 #endif
1392
1393 static void
1394 unmap_win (Display *dpy, Window id, Bool fade)
1395 {
1396     win *w = find_win (dpy, id);
1397     if (!w)
1398         return;
1399     w->a.map_state = IsUnmapped;
1400 #if HAS_NAME_WINDOW_PIXMAP
1401     if (w->pixmap && fade && fadeWindows)
1402         set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, unmap_callback, False, True);
1403     else
1404 #endif
1405         finish_unmap_win (dpy, w);
1406 }
1407
1408 /* Get the opacity prop from window
1409    not found: default
1410    otherwise the value
1411  */
1412 static unsigned int
1413 get_opacity_prop(Display *dpy, win *w, unsigned int def)
1414 {
1415     Atom actual;
1416     int format;
1417     unsigned long n, left;
1418
1419     unsigned char *data;
1420     int result = XGetWindowProperty(dpy, w->id, opacityAtom, 0L, 1L, False, 
1421                        XA_CARDINAL, &actual, &format, 
1422                                     &n, &left, &data);
1423     if (result == Success && data != NULL)
1424     {
1425         unsigned int i;
1426         memcpy (&i, data, sizeof (unsigned int));
1427         XFree( (void *) data);
1428         return i;
1429     }
1430     return def;
1431 }
1432
1433 /* Get the opacity property from the window in a percent format
1434    not found: default
1435    otherwise: the value
1436 */
1437 static double
1438 get_opacity_percent(Display *dpy, win *w, double def)
1439 {
1440     unsigned int opacity = get_opacity_prop (dpy, w, (unsigned int)(OPAQUE*def));
1441
1442     return opacity*1.0/OPAQUE;
1443 }
1444
1445 /* determine mode for window all in one place.
1446    Future might check for menu flag and other cool things
1447 */
1448
1449 static Atom
1450 get_wintype_prop(Display * dpy, Window w)
1451 {
1452     Atom actual;
1453     int format;
1454     unsigned long n, left;
1455
1456     unsigned char *data;
1457     int result = XGetWindowProperty (dpy, w, winTypeAtom, 0L, 1L, False,
1458                                      XA_ATOM, &actual, &format,
1459                                      &n, &left, &data);
1460
1461     if (result == Success && data != None)
1462     {
1463         Atom a;
1464         memcpy (&a, data, sizeof (Atom));
1465         XFree ( (void *) data);
1466         return a;
1467     }
1468     return winNormalAtom;
1469 }
1470
1471 static void
1472 determine_mode(Display *dpy, win *w)
1473 {
1474     int mode;
1475     XRenderPictFormat *format;
1476
1477     /* if trans prop == -1 fall back on  previous tests*/
1478
1479     if (w->alphaPict)
1480     {
1481         XRenderFreePicture (dpy, w->alphaPict);
1482         w->alphaPict = None;
1483     }
1484     if (w->shadowPict)
1485     {
1486         XRenderFreePicture (dpy, w->shadowPict);
1487         w->shadowPict = None;
1488     }
1489
1490     if (w->a.class == InputOnly)
1491     {
1492         format = 0;
1493     }
1494     else
1495     {
1496         format = XRenderFindVisualFormat (dpy, w->a.visual);
1497     }
1498
1499     if (format && format->type == PictTypeDirect && format->direct.alphaMask)
1500     {
1501         mode = WINDOW_ARGB;
1502     }
1503     else if (w->opacity != OPAQUE)
1504     {
1505         mode = WINDOW_TRANS;
1506     }
1507     else
1508     {
1509         mode = WINDOW_SOLID;
1510     }
1511     w->mode = mode;
1512     if (w->extents)
1513     {
1514         XserverRegion damage;
1515         damage = XFixesCreateRegion (dpy, 0, 0);
1516         XFixesCopyRegion (dpy, damage, w->extents);
1517         add_damage (dpy, damage);
1518     }
1519 }
1520
1521 static Atom
1522 determine_wintype (Display *dpy, Window w)
1523 {
1524     Window       root_return, parent_return;
1525     Window      *children = NULL;
1526     unsigned int nchildren, i;
1527     Atom         type;
1528
1529     type = get_wintype_prop (dpy, w);
1530     if (type != winNormalAtom)
1531         return type;
1532
1533     if (!XQueryTree (dpy, w, &root_return, &parent_return, &children,
1534                             &nchildren))
1535     {
1536         /* XQueryTree failed. */
1537         if (children)
1538             XFree ((void *)children);
1539         return winNormalAtom;
1540     }
1541
1542     for (i = 0;i < nchildren;i++)
1543     {
1544         type = determine_wintype (dpy, children[i]);
1545         if (type != winNormalAtom)
1546             return type;
1547     }
1548
1549     if (children)
1550         XFree ((void *)children);
1551
1552     return winNormalAtom;
1553 }
1554
1555 static void
1556 add_win (Display *dpy, Window id, Window prev)
1557 {
1558     win                         *new = malloc (sizeof (win));
1559     win                         **p;
1560     
1561     if (!new)
1562         return;
1563     if (prev)
1564     {
1565         for (p = &list; *p; p = &(*p)->next)
1566             if ((*p)->id == prev)
1567                 break;
1568     }
1569     else
1570         p = &list;
1571     new->id = id;
1572     set_ignore (dpy, NextRequest (dpy));
1573     if (!XGetWindowAttributes (dpy, id, &new->a))
1574     {
1575         free (new);
1576         return;
1577     }
1578     new->damaged = 0;
1579 #if CAN_DO_USABLE
1580     new->usable = False;
1581 #endif
1582 #if HAS_NAME_WINDOW_PIXMAP
1583     new->pixmap = None;
1584     new->pixmap_thread = 0;
1585     new->pixmap_nthreads = 0;
1586     pthread_mutex_init(&new->pixmap_lock, NULL);
1587 #endif
1588     new->picture = None;
1589     if (new->a.class == InputOnly)
1590     {
1591         new->damage_sequence = 0;
1592         new->damage = None;
1593     }
1594     else
1595     {
1596         new->damage_sequence = NextRequest (dpy);
1597         new->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
1598     }
1599     new->alphaPict = None;
1600     new->shadowPict = None;
1601     new->borderSize = None;
1602     new->extents = None;
1603     new->shadow = None;
1604     new->shadow_dx = 0;
1605     new->shadow_dy = 0;
1606     new->shadow_width = 0;
1607     new->shadow_height = 0;
1608     new->opacity = OPAQUE;
1609     new->unmapped_configure = False;
1610
1611     new->borderClip = None;
1612     new->prev_trans = 0;
1613
1614     new->windowType = determine_wintype (dpy, new->id);
1615     
1616     new->next = *p;
1617     *p = new;
1618     if (new->a.map_state == IsViewable)
1619         map_win (dpy, id, new->damage_sequence - 1, True);
1620 }
1621
1622 void
1623 restack_win (Display *dpy, win *w, Window new_above)
1624 {
1625     Window  old_above;
1626     
1627     if (w->next)
1628         old_above = w->next->id;
1629     else
1630         old_above = None;
1631     if (old_above != new_above)
1632     {
1633         win **prev;
1634
1635         /* unhook */
1636         for (prev = &list; *prev; prev = &(*prev)->next)
1637             if ((*prev) == w)
1638                 break;
1639         *prev = w->next;
1640         
1641         /* rehook */
1642         for (prev = &list; *prev; prev = &(*prev)->next)
1643         {
1644             if ((*prev)->id == new_above)
1645                 break;
1646         }
1647         w->next = *prev;
1648         *prev = w;
1649     }
1650 }
1651
1652 static void
1653 configure_win (Display *dpy, XConfigureEvent *ce)
1654 {
1655     win             *w = find_win (dpy, ce->window);
1656     XserverRegion   damage = None;
1657     
1658     if (!w)
1659     {
1660         if (ce->window == root)
1661         {
1662             if (rootBuffer)
1663             {
1664                 XRenderFreePicture (dpy, rootBuffer);
1665                 rootBuffer = None;
1666             }
1667             root_width = ce->width;
1668             root_height = ce->height;
1669         }
1670         return;
1671     }
1672 #if CAN_DO_USABLE
1673     if (w->usable)
1674 #endif
1675     {
1676         damage = XFixesCreateRegion (dpy, 0, 0);
1677         if (w->extents != None) 
1678             XFixesCopyRegion (dpy, damage, w->extents);
1679     }
1680     w->a.x = ce->x;
1681     w->a.y = ce->y;
1682     /* Only destroy the pixmap if the window is mapped */
1683     if (w->a.width != ce->width || w->a.height != ce->height)
1684     {
1685         if (w->a.map_state == IsUnmapped)
1686             w->unmapped_configure = True;
1687         else {
1688 #if HAS_NAME_WINDOW_PIXMAP
1689             start_get_pixmap_thread (dpy, w, 0);
1690 #endif
1691             if (w->shadow)
1692             {
1693                 XRenderFreePicture (dpy, w->shadow);
1694                 w->shadow = None;
1695             }
1696         }
1697     }
1698     w->a.width = ce->width;
1699     w->a.height = ce->height;
1700     w->a.border_width = ce->border_width;
1701     w->a.override_redirect = ce->override_redirect;
1702     restack_win (dpy, w, ce->above);
1703     if (w->a.map_state != IsUnmapped && damage)
1704     {
1705         XserverRegion   extents = win_extents (dpy, w);
1706         XFixesUnionRegion (dpy, damage, damage, extents);
1707         XFixesDestroyRegion (dpy, extents);
1708         add_damage (dpy, damage);
1709     }
1710     clipChanged = True;
1711 }
1712
1713 static void
1714 circulate_win (Display *dpy, XCirculateEvent *ce)
1715 {
1716     win     *w = find_win (dpy, ce->window);
1717     Window  new_above;
1718
1719     if (!w)
1720         return;
1721
1722     if (ce->place == PlaceOnTop)
1723         new_above = list->id;
1724     else
1725         new_above = None;
1726     restack_win (dpy, w, new_above);
1727     clipChanged = True;
1728 }
1729
1730 static void
1731 finish_destroy_win (Display *dpy, Window id)
1732 {
1733     win **prev, *w;
1734     void *ret;
1735
1736     for (prev = &list; (w = *prev); prev = &w->next)
1737         if (w->id == id)
1738         {
1739             /* join before freeing stuff so nothing leaks */
1740             if (w->pixmap_nthreads > 0)
1741                 pthread_join (w->pixmap_thread, &ret);
1742
1743             finish_unmap_win (dpy, w);
1744             *prev = w->next;
1745 #if HAS_NAME_WINDOW_PIXMAP
1746             if (w->pixmap)
1747             {
1748                 XFreePixmap (dpy, w->pixmap);
1749                 w->pixmap = None;
1750             }
1751 #endif
1752             if (w->picture)
1753             {
1754                 set_ignore (dpy, NextRequest (dpy));
1755                 XRenderFreePicture (dpy, w->picture);
1756                 w->picture = None;
1757             }
1758             if (w->alphaPict)
1759             {
1760                 XRenderFreePicture (dpy, w->alphaPict);
1761                 w->alphaPict = None;
1762             }
1763             if (w->shadowPict)
1764             {
1765                 XRenderFreePicture (dpy, w->shadowPict);
1766                 w->shadowPict = None;
1767             }
1768             if (w->damage != None)
1769             {
1770                 set_ignore (dpy, NextRequest (dpy));
1771                 XDamageDestroy (dpy, w->damage);
1772                 w->damage = None;
1773             }
1774             cleanup_fade (dpy, w);
1775             pthread_mutex_destroy(&w->pixmap_lock);
1776             free (w);
1777             break;
1778         }
1779 }
1780
1781 #if HAS_NAME_WINDOW_PIXMAP
1782 static void
1783 destroy_callback (Display *dpy, win *w)
1784 {
1785     finish_destroy_win (dpy, w->id);
1786 }
1787 #endif
1788
1789 static void
1790 destroy_win (Display *dpy, Window id, Bool fade)
1791 {
1792     win *w = find_win (dpy, id);
1793 #if HAS_NAME_WINDOW_PIXMAP
1794     if (w && w->pixmap && fade && fadeWindows)
1795         set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step,
1796                   destroy_callback, False, (w->a.map_state != IsUnmapped));
1797     else
1798 #endif
1799     {
1800         finish_destroy_win (dpy, id);
1801     }
1802 }
1803
1804 /*
1805 static void
1806 dump_win (win *w)
1807 {
1808     printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id,
1809             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
1810 }
1811
1812
1813 static void
1814 dump_wins (void)
1815 {
1816     win *w;
1817
1818     printf ("windows:\n");
1819     for (w = list; w; w = w->next)
1820         dump_win (w);
1821 }
1822 */
1823
1824 static void
1825 damage_win (Display *dpy, XDamageNotifyEvent *de)
1826 {
1827     win *w = find_win (dpy, de->drawable);
1828
1829     if (!w)
1830         return;
1831 #if CAN_DO_USABLE
1832     if (!w->usable)
1833     {
1834         if (w->damage_bounds.width == 0 || w->damage_bounds.height == 0)
1835         {
1836             w->damage_bounds = de->area;
1837         }
1838         else
1839         {
1840             if (de->area.x < w->damage_bounds.x)
1841             {
1842                 w->damage_bounds.width += (w->damage_bounds.x - de->area.x);
1843                 w->damage_bounds.x = de->area.x;
1844             }
1845             if (de->area.y < w->damage_bounds.y)
1846             {
1847                 w->damage_bounds.height += (w->damage_bounds.y - de->area.y);
1848                 w->damage_bounds.y = de->area.y;
1849             }
1850             if (de->area.x + de->area.width > w->damage_bounds.x + w->damage_bounds.width)
1851                 w->damage_bounds.width = de->area.x + de->area.width - w->damage_bounds.x;
1852             if (de->area.y + de->area.height > w->damage_bounds.y + w->damage_bounds.height)
1853                 w->damage_bounds.height = de->area.y + de->area.height - w->damage_bounds.y;
1854         }
1855 #if 0
1856         printf ("unusable damage %d, %d: %d x %d bounds %d, %d: %d x %d\n",
1857                 de->area.x,
1858                 de->area.y,
1859                 de->area.width,
1860                 de->area.height,
1861                 w->damage_bounds.x,
1862                 w->damage_bounds.y,
1863                 w->damage_bounds.width,
1864                 w->damage_bounds.height);
1865 #endif
1866         if (w->damage_bounds.x <= 0 && 
1867             w->damage_bounds.y <= 0 &&
1868             w->a.width <= w->damage_bounds.x + w->damage_bounds.width &&
1869             w->a.height <= w->damage_bounds.y + w->damage_bounds.height)
1870         {
1871             clipChanged = True;
1872             if (fadeWindows)
1873                 set_fade (dpy, w, 0, get_opacity_percent (dpy, w, 1.0), fade_in_step, 0, True, True);
1874             w->usable = True;
1875         }
1876     }
1877     if (w->usable)
1878 #endif
1879         repair_win (dpy, w);
1880 }
1881
1882 static int
1883 error (Display *dpy, XErrorEvent *ev)
1884 {
1885     int     o;
1886     const char    *name = 0;
1887     
1888     if (should_ignore (dpy, ev->serial))
1889         return 0;
1890     
1891     if (ev->request_code == composite_opcode &&
1892         ev->minor_code == X_CompositeRedirectSubwindows)
1893     {
1894         fprintf (stderr, "Another composite manager is already running\n");
1895         exit (1);
1896     }
1897     
1898     o = ev->error_code - xfixes_error;
1899     switch (o) {
1900     case BadRegion: name = "BadRegion"; break;
1901     default: break;
1902     }
1903     o = ev->error_code - damage_error;
1904     switch (o) {
1905     case BadDamage: name = "BadDamage"; break;
1906     default: break;
1907     }
1908     o = ev->error_code - render_error;
1909     switch (o) {
1910     case BadPictFormat: name ="BadPictFormat"; break;
1911     case BadPicture: name ="BadPicture"; break;
1912     case BadPictOp: name ="BadPictOp"; break;
1913     case BadGlyphSet: name ="BadGlyphSet"; break;
1914     case BadGlyph: name ="BadGlyph"; break;
1915     default: break;
1916     }
1917         
1918     printf ("error %d request %d minor %d serial %lu\n",
1919             ev->error_code, ev->request_code, ev->minor_code, ev->serial);
1920
1921 /*    abort ();     this is just annoying to most people */
1922     return 0;
1923 }
1924
1925 static void
1926 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
1927 {
1928     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
1929     
1930     add_damage (dpy, region);
1931 }
1932
1933 #if DEBUG_EVENTS
1934 static int
1935 ev_serial (XEvent *ev)
1936 {
1937     if (ev->type & 0x7f != KeymapNotify)
1938         return ev->xany.serial;
1939     return NextRequest (ev->xany.display);
1940 }
1941
1942 static char *
1943 ev_name (XEvent *ev)
1944 {
1945     static char buf[128];
1946     switch (ev->type & 0x7f) {
1947     case Expose:
1948         return "Expose";
1949     case MapNotify:
1950         return "Map";
1951     case UnmapNotify:
1952         return "Unmap";
1953     case ReparentNotify:
1954         return "Reparent";
1955     case CirculateNotify:
1956         return "Circulate";
1957     default:
1958         if (ev->type == damage_event + XDamageNotify)
1959             return "Damage";
1960         sprintf (buf, "Event %d", ev->type);
1961         return buf;
1962     }
1963 }
1964
1965 static Window
1966 ev_window (XEvent *ev)
1967 {
1968     switch (ev->type) {
1969     case Expose:
1970         return ev->xexpose.window;
1971     case MapNotify:
1972         return ev->xmap.window;
1973     case UnmapNotify:
1974         return ev->xunmap.window;
1975     case ReparentNotify:
1976         return ev->xreparent.window;
1977     case CirculateNotify:
1978         return ev->xcirculate.window;
1979     default:
1980         if (ev->type == damage_event + XDamageNotify)
1981             return ((XDamageNotifyEvent *) ev)->drawable;
1982         return 0;
1983     }
1984 }
1985 #endif
1986
1987 void
1988 usage (char *program)
1989 {
1990     fprintf (stderr, "%s v1.1.3\n", program);
1991     fprintf (stderr, "usage: %s [options]\n", program);
1992     fprintf (stderr, "Options\n");
1993     fprintf (stderr, "   -d display\n      Specifies which display should be managed.\n");
1994     fprintf (stderr, "   -r radius\n      Specifies the blur radius for client-side shadows. (default 12)\n");
1995     fprintf (stderr, "   -o opacity\n      Specifies the translucency for client-side shadows. (default .75)\n");
1996     fprintf (stderr, "   -l left-offset\n      Specifies the left offset for client-side shadows. (default -15)\n");
1997     fprintf (stderr, "   -t top-offset\n      Specifies the top offset for clinet-side shadows. (default -15)\n");
1998     fprintf (stderr, "   -I fade-in-step\n      Specifies the opacity change between steps while fading in. (default 0.028)\n");
1999     fprintf (stderr, "   -O fade-out-step\n      Specifies the opacity change between steps while fading out. (default 0.03)\n");
2000     fprintf (stderr, "   -D fade-delta-time\n      Specifies the time between steps in a fade in milliseconds. (default 10)\n");
2001     fprintf (stderr, "   -a\n      Use automatic server-side compositing. Faster, but no special effects.\n");
2002     fprintf (stderr, "   -c\n      Draw client-side shadows with fuzzy edges.\n");
2003     fprintf (stderr, "   -C\n      Avoid drawing shadows on dock/panel windows.\n");
2004     fprintf (stderr, "   -f\n      Fade windows in/out when opening/closing.\n");
2005     fprintf (stderr, "   -F\n      Fade windows during opacity changes.\n");
2006     fprintf (stderr, "   -n\n      Normal client-side compositing with transparency support\n");
2007     fprintf (stderr, "   -s\n      Draw server-side shadows with sharp edges.\n");
2008     fprintf (stderr, "   -S\n      Enable synchronous operation (for debugging).\n");
2009     exit (1);
2010 }
2011
2012 static void
2013 register_cm (void)
2014 {
2015     Window w;
2016     Atom a;
2017
2018     w = XCreateSimpleWindow (dpy, RootWindow (dpy, 0), 0, 0, 1, 1, 0, None,
2019                              None);
2020
2021     Xutf8SetWMProperties (dpy, w, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL,
2022                           NULL);
2023
2024     /* FIXME: Don't hard code the screen number */
2025     a = XInternAtom (dpy, "_NET_WM_CM_S0", False);
2026
2027     XSetSelectionOwner (dpy, a, w, 0);
2028 }
2029
2030 int
2031 main (int argc, char **argv)
2032 {
2033     XEvent          ev;
2034     Window          root_return, parent_return;
2035     Window          *children;
2036     unsigned int    nchildren;
2037     int             i;
2038     XRenderPictureAttributes    pa;
2039     XRectangle      *expose_rects = 0;
2040     int             size_expose = 0;
2041     int             n_expose = 0;
2042     struct pollfd   ufd;
2043     int             p;
2044     int             composite_major, composite_minor;
2045     char            *display = 0;
2046     int             o;
2047
2048     while ((o = getopt (argc, argv, "D:I:O:d:r:o:l:t:scnfFCaS")) != -1)
2049     {
2050         switch (o) {
2051         case 'd':
2052             display = optarg;
2053             break;
2054         case 'D':
2055             fade_delta = atoi (optarg);
2056             if (fade_delta < 1)
2057                 fade_delta = 10;
2058             break;
2059         case 'I':
2060             fade_in_step = atof (optarg);
2061             if (fade_in_step <= 0)
2062                 fade_in_step = 0.01;
2063             break;
2064         case 'O':
2065             fade_out_step = atof (optarg);
2066             if (fade_out_step <= 0)
2067                 fade_out_step = 0.01;
2068             break;
2069         case 's':
2070             compMode = CompServerShadows;
2071             break;
2072         case 'c':
2073             compMode = CompClientShadows;
2074             break;
2075         case 'C':
2076             excludeDockShadows = True;
2077             break;
2078         case 'n':
2079             compMode = CompSimple;
2080             break;
2081         case 'f':
2082             fadeWindows = True;
2083             break;
2084         case 'F':
2085             fadeTrans = True;
2086             break;
2087         case 'a':
2088             autoRedirect = True;
2089             break;
2090         case 'S':
2091             synchronize = True;
2092             break;
2093         case 'r':
2094             shadowRadius = atoi (optarg);
2095             break;
2096         case 'o':
2097             shadowOpacity = atof (optarg);
2098             break;
2099         case 'l':
2100             shadowOffsetX = atoi (optarg);
2101             break;
2102         case 't':
2103             shadowOffsetY = atoi (optarg);
2104             break;
2105         default:
2106             usage (argv[0]);
2107             break;
2108         }
2109     }
2110
2111     XInitThreads();
2112     dpy = XOpenDisplay (display);
2113     if (!dpy)
2114     {
2115         fprintf (stderr, "Can't open display\n");
2116         exit (1);
2117     }
2118     XSetErrorHandler (error);
2119     if (synchronize)
2120         XSynchronize (dpy, 1);
2121     scr = DefaultScreen (dpy);
2122     root = RootWindow (dpy, scr);
2123
2124     if (!XRenderQueryExtension (dpy, &render_event, &render_error))
2125     {
2126         fprintf (stderr, "No render extension\n");
2127         exit (1);
2128     }
2129     if (!XQueryExtension (dpy, COMPOSITE_NAME, &composite_opcode,
2130                           &composite_event, &composite_error))
2131     {
2132         fprintf (stderr, "No composite extension\n");
2133         exit (1);
2134     }
2135     XCompositeQueryVersion (dpy, &composite_major, &composite_minor);
2136 #if HAS_NAME_WINDOW_PIXMAP
2137     if (composite_major > 0 || composite_minor >= 2)
2138         hasNamePixmap = True;
2139 #endif
2140
2141     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
2142     {
2143         fprintf (stderr, "No damage extension\n");
2144         exit (1);
2145     }
2146     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
2147     {
2148         fprintf (stderr, "No XFixes extension\n");
2149         exit (1);
2150     }
2151
2152     register_cm();
2153
2154     /* get atoms */
2155     opacityAtom = XInternAtom (dpy, OPACITY_PROP, False);
2156     winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", False);
2157     winDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
2158     winDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
2159     winToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
2160     winMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", False);
2161     winUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False);
2162     winSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False);
2163     winDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
2164     winNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
2165
2166     pa.subwindow_mode = IncludeInferiors;
2167
2168     if (compMode == CompClientShadows)
2169     {
2170         gaussianMap = make_gaussian_map(dpy, shadowRadius);
2171         presum_gaussian (gaussianMap);
2172     }
2173
2174     root_width = DisplayWidth (dpy, scr);
2175     root_height = DisplayHeight (dpy, scr);
2176
2177     rootPicture = XRenderCreatePicture (dpy, root, 
2178                                         XRenderFindVisualFormat (dpy,
2179                                                                  DefaultVisual (dpy, scr)),
2180                                         CPSubwindowMode,
2181                                         &pa);
2182     blackPicture = solid_picture (dpy, True, 1, 0, 0, 0);
2183     if (compMode == CompServerShadows)
2184         transBlackPicture = solid_picture (dpy, True, 0.3, 0, 0, 0);
2185     allDamage = None;
2186     clipChanged = True;
2187     XGrabServer (dpy);
2188     if (autoRedirect)
2189         XCompositeRedirectSubwindows (dpy, root, CompositeRedirectAutomatic);
2190     else
2191     {
2192         XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
2193         XSelectInput (dpy, root, 
2194                       SubstructureNotifyMask|
2195                       ExposureMask|
2196                       StructureNotifyMask|
2197                       PropertyChangeMask);
2198         XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
2199         for (i = 0; i < nchildren; i++)
2200             add_win (dpy, children[i], i ? children[i-1] : None);
2201         XFree (children);
2202     }
2203     XUngrabServer (dpy);
2204     ufd.fd = ConnectionNumber (dpy);
2205     ufd.events = POLLIN;
2206     if (!autoRedirect)
2207         paint_all (dpy, None);
2208     for (;;)
2209     {
2210         /*      dump_wins (); */
2211         do {
2212             if (autoRedirect)
2213                 XFlush (dpy);
2214             if (!QLength (dpy))
2215             {
2216                  if (poll (&ufd, 1, fade_timeout()) == 0)
2217                  {
2218                     run_fades (dpy);
2219                     break;
2220                  }
2221             }
2222
2223             XNextEvent (dpy, &ev);
2224             if ((ev.type & 0x7f) != KeymapNotify)
2225                 discard_ignore (dpy, ev.xany.serial);
2226 #if DEBUG_EVENTS
2227             printf ("event %10.10s serial 0x%08x window 0x%08x\n",
2228                     ev_name(&ev), ev_serial (&ev), ev_window (&ev));
2229 #endif
2230             if (!autoRedirect) switch (ev.type) {
2231             case CreateNotify:
2232                 add_win (dpy, ev.xcreatewindow.window, 0);
2233                 break;
2234             case ConfigureNotify:
2235                 configure_win (dpy, &ev.xconfigure);
2236                 break;
2237             case DestroyNotify:
2238                 destroy_win (dpy, ev.xdestroywindow.window, True);
2239                 break;
2240             case MapNotify:
2241                 map_win (dpy, ev.xmap.window, ev.xmap.serial, True);
2242                 break;
2243             case UnmapNotify:
2244                 unmap_win (dpy, ev.xunmap.window, True);
2245                 break;
2246             case ReparentNotify:
2247                 if (ev.xreparent.parent == root)
2248                     add_win (dpy, ev.xreparent.window, 0);
2249                 else
2250                     destroy_win (dpy, ev.xreparent.window, True);
2251                 break;
2252             case CirculateNotify:
2253                 circulate_win (dpy, &ev.xcirculate);
2254                 break;
2255             case Expose:
2256                 if (ev.xexpose.window == root)
2257                 {
2258                     int more = ev.xexpose.count + 1;
2259                     if (n_expose == size_expose)
2260                     {
2261                         if (expose_rects)
2262                         {
2263                             expose_rects = realloc (expose_rects, 
2264                                                     (size_expose + more) * 
2265                                                     sizeof (XRectangle));
2266                             size_expose += more;
2267                         }
2268                         else
2269                         {
2270                             expose_rects = malloc (more * sizeof (XRectangle));
2271                             size_expose = more;
2272                         }
2273                     }
2274                     expose_rects[n_expose].x = ev.xexpose.x;
2275                     expose_rects[n_expose].y = ev.xexpose.y;
2276                     expose_rects[n_expose].width = ev.xexpose.width;
2277                     expose_rects[n_expose].height = ev.xexpose.height;
2278                     n_expose++;
2279                     if (ev.xexpose.count == 0)
2280                     {
2281                         expose_root (dpy, root, expose_rects, n_expose);
2282                         n_expose = 0;
2283                     }
2284                 }
2285                 break;
2286             case PropertyNotify:
2287                 for (p = 0; backgroundProps[p]; p++)
2288                 {
2289                     if (ev.xproperty.atom == XInternAtom (dpy, backgroundProps[p], False))
2290                     {
2291                         if (rootTile)
2292                         {
2293                             XClearArea (dpy, root, 0, 0, 0, 0, True);
2294                             XRenderFreePicture (dpy, rootTile);
2295                             rootTile = None;
2296                             break;
2297                         }
2298                     }
2299                 }
2300                 /* check if Trans property was changed */
2301                 if (ev.xproperty.atom == opacityAtom)
2302                 {
2303                     /* reset mode and redraw window */
2304                     win * w = find_win(dpy, ev.xproperty.window);
2305                     if (w)
2306                     {
2307                         if (fadeTrans)
2308                             set_fade (dpy, w, w->opacity*1.0/OPAQUE, get_opacity_percent (dpy, w, 1.0),
2309                                       fade_out_step, 0, True, False);
2310                         else
2311                         {
2312                         w->opacity = get_opacity_prop(dpy, w, OPAQUE);
2313                         determine_mode(dpy, w);
2314                             if (w->shadow)
2315                             {
2316                                 XRenderFreePicture (dpy, w->shadow);
2317                                 w->shadow = None;
2318                                 w->extents = win_extents (dpy, w);
2319                             }
2320                         }
2321                     }
2322                 }
2323                 break;
2324             default:
2325                 if (ev.type == damage_event + XDamageNotify)
2326                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
2327                 break;
2328             }
2329         } while (QLength (dpy));
2330         pthread_mutex_lock (&damage_lock);
2331         if (allDamage && !autoRedirect)
2332         {
2333             static int  paint;
2334             if (!paint_all (dpy, allDamage))
2335                 allDamage = None;
2336             paint++;
2337             XSync (dpy, False);
2338             clipChanged = False;
2339         }
2340         pthread_mutex_unlock (&damage_lock);
2341     }
2342 }