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