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