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