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