6e6309efe17a8ea05d4c9582f32a1d0217e30a60
[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 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <math.h>
29 #include <sys/poll.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/extensions/Xcomposite.h>
35 #include <X11/extensions/Xdamage.h>
36 #include <X11/extensions/Xrender.h>
37
38 typedef struct _win {
39     struct _win         *next;
40     Window              id;
41     XWindowAttributes   a;
42     int                 damaged;
43     int                 mode;
44     Damage              damage;
45     Picture             picture;
46     XserverRegion       borderSize;
47     XserverRegion       extents;
48     Picture             shadow;
49     int                 shadow_dx;
50     int                 shadow_dy;
51     int                 shadow_width;
52     int                 shadow_height;
53
54     /* for drawing translucent windows */
55     XserverRegion       borderClip;
56     struct _win         *prev_trans;
57 } win;
58
59 typedef struct _conv {
60     int     size;
61     double  *data;
62 } conv;
63
64 win             *list;
65 Display         *dpy;
66 int             scr;
67 Window          root;
68 Picture         rootPicture;
69 Picture         rootBuffer;
70 Picture         transPicture;
71 Picture         blackPicture;
72 Picture         rootTile;
73 XserverRegion   allDamage;
74 int             root_height, root_width;
75 conv            *gussianMap;
76
77 #define BACKGROUND_PROP "_XROOTPMAP_ID"
78
79 #define WINDOW_SOLID    0
80 #define WINDOW_TRANS    1
81 #define WINDOW_ARGB     2
82
83 #define TRANS_OPACITY   0.75
84 #define SHADOW_RADIUS   15
85 #define SHADOW_OPACITY  0.75
86 #define SHADOW_OFFSET_X (-SHADOW_RADIUS)
87 #define SHADOW_OFFSET_Y (-SHADOW_RADIUS)
88
89
90 static double
91 gaussian (double r, double x, double y)
92 {
93     return ((1 / (sqrt (2 * M_PI * r))) *
94             exp ((- (x * x + y * y)) / (2 * r * r)));
95 }
96
97
98 static conv *
99 make_gaussian_map (Display *dpy, double r)
100 {
101     conv            *c;
102     int             size = ((int) ceil ((r * 3)) + 1) & ~1;
103     int             center = size / 2;
104     int             x, y;
105     double          t;
106     double          g;
107     
108     c = malloc (sizeof (conv) + size * size * sizeof (double));
109     c->size = size;
110     c->data = (double *) (c + 1);
111     for (y = 0; y < size; y++)
112         for (x = 0; x < size; x++)
113         {
114             g = gaussian (r, (double) (x - center), (double) (y - center));
115             t += g;
116             c->data[y * size + x] = g;
117         }
118 /*    printf ("gaussian total %f\n", t); */
119     for (y = 0; y < size; y++)
120         for (x = 0; x < size; x++)
121         {
122             c->data[y*size + x] /= t;
123         }
124     return c;
125 }
126
127 /*
128  * A picture will help
129  *
130  *      -center   0                width  width+center
131  *  -center +-----+-------------------+-----+
132  *          |     |                   |     |
133  *          |     |                   |     |
134  *        0 +-----+-------------------+-----+
135  *          |     |                   |     |
136  *          |     |                   |     |
137  *          |     |                   |     |
138  *   height +-----+-------------------+-----+
139  *          |     |                   |     |
140  * height+  |     |                   |     |
141  *  center  +-----+-------------------+-----+
142  */
143  
144 static unsigned char
145 sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
146 {
147     int     fx, fy;
148     double  *g_data;
149     double  *g_line = map->data;
150     int     g_size = map->size;
151     int     center = g_size / 2;
152     int     fx_start, fx_end;
153     int     fy_start, fy_end;
154     double  v;
155     
156     /*
157      * Compute set of filter values which are "in range",
158      * that's the set with:
159      *  0 <= x + (fx-center) && x + (fx-center) < width &&
160      *  0 <= y + (fy-center) && y + (fy-center) < height
161      *
162      *  0 <= x + (fx - center)  x + fx - center < width
163      *  center - x <= fx        fx < width + center - x
164      */
165
166     fx_start = center - x;
167     if (fx_start < 0)
168         fx_start = 0;
169     fx_end = width + center - x;
170     if (fx_end > g_size)
171         fx_end = g_size;
172
173     fy_start = center - y;
174     if (fy_start < 0)
175         fy_start = 0;
176     fy_end = height + center - y;
177     if (fy_end > g_size)
178         fy_end = g_size;
179
180     g_line = g_line + fy_start * g_size + fx_start;
181     
182     v = 0;
183     for (fy = fy_start; fy < fy_end; fy++)
184     {
185         g_data = g_line;
186         g_line += g_size;
187         
188         for (fx = fx_start; fx < fx_end; fx++)
189             v += *g_data++;
190     }
191     if (v > 1)
192         v = 1;
193     
194     return ((unsigned int) (v * opacity * 255.0));
195 }
196
197 static XImage *
198 make_shadow (Display *dpy, double opacity, int width, int height)
199 {
200     XImage          *ximage;
201     unsigned char   *data;
202     int             gsize = gussianMap->size;
203     int             ylimit, xlimit;
204     int             swidth = width + gsize;
205     int             sheight = height + gsize;
206     int             center = gsize / 2;
207     int             x, y;
208     unsigned char   d;
209     
210     data = malloc (swidth * sheight * sizeof (unsigned char));
211     ximage = XCreateImage (dpy,
212                            DefaultVisual(dpy, DefaultScreen(dpy)),
213                            8,
214                            ZPixmap,
215                            0,
216                            (char *) data,
217                            swidth, sheight, 8, swidth * sizeof (unsigned char));
218     /*
219      * Build the gaussian in sections
220      */
221
222     /*
223      * corners
224      */
225     ylimit = gsize;
226     if (ylimit > sheight / 2)
227         ylimit = (sheight + 1) / 2;
228     xlimit = gsize;
229     if (xlimit > swidth / 2)
230         xlimit = (swidth + 1) / 2;
231
232     for (y = 0; y < ylimit; y++)
233         for (x = 0; x < xlimit; x++)
234         {
235             d = sum_gaussian (gussianMap, opacity, x - center, y - center, width, height);
236             data[y * swidth + x] = d;
237             data[(sheight - y - 1) * swidth + x] = d;
238             data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d;
239             data[y * swidth + (swidth - x - 1)] = d;
240         }
241
242     /*
243      * top/bottom
244      */
245     for (y = 0; y < ylimit; y++)
246     {
247         d = sum_gaussian (gussianMap, opacity, center, y - center, width, height);
248         for (x = gsize; x < swidth - gsize; x++)
249         {
250             data[y * swidth + x] = d;
251             data[(sheight - y - 1) * swidth + x] = d;
252         }
253     }
254
255     /*
256      * sides
257      */
258     
259     for (x = 0; x < xlimit; x++)
260     {
261         d = sum_gaussian (gussianMap, opacity, x - center, center, width, height);
262         for (y = gsize; y < sheight - gsize; y++)
263         {
264             data[y * swidth + x] = d;
265             data[y * swidth + (swidth - x - 1)] = d;
266         }
267     }
268
269     /*
270      * center
271      */
272
273     d = sum_gaussian (gussianMap, opacity, center, center, width, height);
274     for (y = ylimit; y < sheight - ylimit; y++)
275         for (x = xlimit; x < swidth - xlimit; x++)
276             data[y * swidth + x] = d;
277
278     return ximage;
279 }
280
281 static Picture
282 shadow_picture (Display *dpy, double opacity, int width, int height, int *wp, int *hp)
283 {
284     XImage  *shadowImage = make_shadow (dpy, opacity, width, height);
285     Pixmap  shadowPixmap = XCreatePixmap (dpy, root, 
286                                           shadowImage->width,
287                                           shadowImage->height,
288                                           8);
289     Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
290                                                   XRenderFindStandardFormat (dpy, PictStandardA8),
291                                                   0, 0);
292     GC      gc = XCreateGC (dpy, shadowPixmap, 0, 0);
293     
294     XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 
295                shadowImage->width,
296                shadowImage->height);
297     *wp = shadowImage->width;
298     *hp = shadowImage->height;
299     XFreeGC (dpy, gc);
300     XDestroyImage (shadowImage);
301     XFreePixmap (dpy, shadowPixmap);
302     return shadowPicture;
303 }
304
305 static win *
306 find_win (Display *dpy, Window id)
307 {
308     win *w;
309
310     for (w = list; w; w = w->next)
311         if (w->id == id)
312             return w;
313     return 0;
314 }
315
316 static Picture
317 root_tile (Display *dpy)
318 {
319     Picture         picture;
320     Atom            actual_type;
321     Pixmap          pixmap;
322     int             actual_format;
323     unsigned long   nitems;
324     unsigned long   bytes_after;
325     unsigned char   *prop;
326     Bool            fill;
327     XRenderPictureAttributes    pa;
328
329     if (XGetWindowProperty (dpy, root, XInternAtom (dpy, BACKGROUND_PROP, False),
330                             0, 4, False, AnyPropertyType,
331                             &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success &&
332         actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1)
333     {
334         memcpy (&pixmap, prop, 4);
335         XFree (prop);
336         fill = False;
337     }
338     else
339     {
340         pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
341         fill = True;
342     }
343     pa.repeat = True;
344     picture = XRenderCreatePicture (dpy, pixmap,
345                                     XRenderFindVisualFormat (dpy,
346                                                              DefaultVisual (dpy, scr)),
347                                     CPRepeat, &pa);
348     if (fill)
349     {
350         XRenderColor    c;
351         
352         c.red = c.green = c.blue = 0x8080;
353         c.alpha = 0xffff;
354         XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 
355                               0, 0, 1, 1);
356     }
357     return picture;
358 }
359
360 static void
361 paint_root (Display *dpy)
362 {
363     if (!rootTile)
364         rootTile = root_tile (dpy);
365     
366     XRenderComposite (dpy, PictOpSrc,
367                       rootTile, None, rootBuffer,
368                       0, 0, 0, 0, 0, 0, root_width, root_height);
369 }
370
371 static XserverRegion
372 win_extents (Display *dpy, win *w)
373 {
374     XRectangle      r;
375     
376     if (w->mode == WINDOW_ARGB)
377     {
378         r.x = w->a.x;
379         r.y = w->a.y;
380         r.width = w->a.width + w->a.border_width * 2;
381         r.height = w->a.height + w->a.border_width * 2;
382     }
383     else
384     {
385         if (!w->shadow)
386         {
387             double      opacity = SHADOW_OPACITY;
388             if (w->mode == WINDOW_TRANS)
389                 opacity = opacity * TRANS_OPACITY;
390             w->shadow = shadow_picture (dpy, opacity, 
391                                         w->a.width, w->a.height,
392                                         &w->shadow_width, &w->shadow_height);
393             w->shadow_dx = SHADOW_OFFSET_X;
394             w->shadow_dy = SHADOW_OFFSET_Y;
395         }
396         r.x = w->a.x + w->a.border_width + w->shadow_dx;
397         r.y = w->a.y + w->a.border_width + w->shadow_dy;
398         r.width = w->shadow_width;
399         r.height = w->shadow_height;
400     }
401     return XFixesCreateRegion (dpy, &r, 1);
402 }
403
404 static XserverRegion
405 border_size (Display *dpy, win *w)
406 {
407     XserverRegion   border;
408     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
409     XFixesTranslateRegion (dpy, border, w->a.x, w->a.y);
410     return border;
411 }
412
413 static void
414 paint_all (Display *dpy, XserverRegion region)
415 {
416     win *w;
417     win *t = 0;
418     
419     if (!region)
420     {
421         XRectangle  r;
422         r.x = 0;
423         r.y = 0;
424         r.width = root_width;
425         r.height = root_height;
426         region = XFixesCreateRegion (dpy, &r, 1);
427     }
428     if (!rootBuffer)
429     {
430         Pixmap  rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
431                                             DefaultDepth (dpy, scr));
432         rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
433                                            XRenderFindVisualFormat (dpy,
434                                                                     DefaultVisual (dpy, scr)),
435                                            0, 0);
436         XFreePixmap (dpy, rootPixmap);
437     }
438     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
439     for (w = list; w; w = w->next)
440     {
441         if (w->a.map_state != IsViewable)
442             continue;
443         if (!w->picture)
444             continue;
445         
446         if (w->borderSize)
447             XFixesDestroyRegion (dpy, w->borderSize);
448         w->borderSize = border_size (dpy, w);
449         if (w->extents)
450             XFixesDestroyRegion (dpy, w->extents);
451         w->extents = win_extents (dpy, w);
452         if (w->mode == WINDOW_SOLID)
453         {
454             XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
455             XFixesSubtractRegion (dpy, region, region, w->borderSize);
456             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
457                               0, 0, 0, 0, 
458                               w->a.x + w->a.border_width,
459                               w->a.y + w->a.border_width,
460                               w->a.width,
461                               w->a.height);
462         }
463         w->borderClip = XFixesCreateRegion (dpy, 0, 0);
464         XFixesCopyRegion (dpy, w->borderClip, region);
465         w->prev_trans = t;
466         t = w;
467     }
468     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
469     paint_root (dpy);
470     for (w = t; w; w = w->prev_trans)
471     {
472         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
473         if (w->shadow)
474         {
475             XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer,
476                               0, 0, 0, 0,
477                               w->a.x + w->a.border_width + w->shadow_dx,
478                               w->a.y + w->a.border_width + w->shadow_dy,
479                               w->shadow_width, w->shadow_height);
480         }
481         if (w->mode == WINDOW_TRANS)
482             XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootBuffer,
483                               0, 0, 0, 0, 
484                               w->a.x + w->a.border_width,
485                               w->a.y + w->a.border_width,
486                               w->a.width,
487                               w->a.height);
488         else if (w->mode == WINDOW_ARGB)
489             XRenderComposite (dpy, PictOpOver, w->picture, None, rootBuffer,
490                               0, 0, 0, 0, 
491                               w->a.x + w->a.border_width,
492                               w->a.y + w->a.border_width,
493                               w->a.width,
494                               w->a.height);
495         XFixesDestroyRegion (dpy, w->borderClip);
496         w->borderClip = None;
497     }
498     XFixesDestroyRegion (dpy, region);
499     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
500     XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
501                       0, 0, 0, 0, 0, 0, root_width, root_height);
502 }
503
504 static void
505 add_damage (Display *dpy, XserverRegion damage)
506 {
507     if (allDamage)
508     {
509         XFixesUnionRegion (dpy, allDamage, allDamage, damage);
510         XFixesDestroyRegion (dpy, damage);
511     }
512     else
513         allDamage = damage;
514 }
515
516 static void
517 repair_win (Display *dpy, Window id)
518 {
519     win             *w = find_win (dpy, id);
520     XserverRegion   parts;
521
522     if (!w)
523         return;
524 /*    printf ("repair 0x%x\n", w->id); */
525     parts = XFixesCreateRegion (dpy, 0, 0);
526     XDamageSubtract (dpy, w->damage, None, parts);
527     XFixesTranslateRegion (dpy, parts, w->a.x, w->a.y);
528     add_damage (dpy, parts);
529 }
530
531 static void
532 map_win (Display *dpy, Window id)
533 {
534     win             *w = find_win (dpy, id);
535     XserverRegion   region;
536
537     if (!w)
538         return;
539     w->a.map_state = IsViewable;
540     if (w->picture)
541     {
542         w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
543         region = win_extents (dpy, w);
544         add_damage (dpy, region);
545     }
546 }
547
548 static void
549 unmap_win (Display *dpy, Window id)
550 {
551     win *w = find_win (dpy, id);
552
553     if (!w)
554         return;
555     w->a.map_state = IsUnmapped;
556     if (w->damage != None)
557     {
558         XDamageDestroy (dpy, w->damage);
559         w->damage = None;
560     }
561     if (w->extents != None)
562     {
563         add_damage (dpy, w->extents);    /* destroys region */
564         w->extents = None;
565     }
566 }
567
568 static void
569 add_win (Display *dpy, Window id, Window prev)
570 {
571     win                         *new = malloc (sizeof (win));
572     win                         **p;
573     XRenderPictureAttributes    pa;
574     XRenderPictFormat           *format;
575     
576     if (!new)
577         return;
578     if (prev)
579     {
580         for (p = &list; *p; p = &(*p)->next)
581             if ((*p)->id == prev)
582                 break;
583     }
584     else
585         p = &list;
586     new->id = id;
587     if (!XGetWindowAttributes (dpy, id, &new->a))
588     {
589         free (new);
590         return;
591     }
592     new->damaged = 0;
593     new->damage = None;
594     pa.subwindow_mode = IncludeInferiors;
595     if (new->a.class == InputOnly)
596     {
597         new->picture = 0;
598         format = 0;
599     }
600     else
601     {
602         format = XRenderFindVisualFormat (dpy, new->a.visual);
603         new->picture = XRenderCreatePicture (dpy, id,
604                                              format,
605                                              CPSubwindowMode,
606                                              &pa);
607     }
608                                          
609     new->shadow = None;
610     new->borderSize = None;
611     new->extents = None;
612     if (format && format->type == PictTypeDirect && format->direct.alphaMask)
613         new->mode = WINDOW_ARGB;
614     else if (new->a.override_redirect)
615         new->mode = WINDOW_TRANS;
616     else
617         new->mode = WINDOW_SOLID;
618     new->next = *p;
619     *p = new;
620     if (new->a.map_state == IsViewable)
621         map_win (dpy, id);
622 }
623
624 static void
625 configure_win (Display *dpy, XConfigureEvent *ce)
626 {
627     win             *w = find_win (dpy, ce->window);
628     Window          above;
629     XserverRegion   damage = None;
630     
631     if (!w)
632     {
633         if (ce->window == root)
634         {
635             if (rootBuffer)
636             {
637                 XRenderFreePicture (dpy, rootBuffer);
638                 rootBuffer = None;
639             }
640             root_width = ce->width;
641             root_height = ce->height;
642         }
643         return;
644     }
645     if (w->a.map_state == IsViewable)
646     {
647         damage = XFixesCreateRegion (dpy, 0, 0);
648         if (w->extents != None) 
649             XFixesCopyRegion (dpy, damage, w->extents);
650     }
651     w->a.x = ce->x;
652     w->a.y = ce->y;
653     if (w->a.width != ce->width || w->a.height != ce->height)
654         if (w->shadow)
655         {
656             XRenderFreePicture (dpy, w->shadow);
657             w->shadow = None;
658         }
659     w->a.width = ce->width;
660     w->a.height = ce->height;
661     w->a.border_width = ce->border_width;
662     w->a.override_redirect = ce->override_redirect;
663     if (w->next)
664         above = w->next->id;
665     else
666         above = None;
667     if (above != ce->above)
668     {
669         win **prev;
670
671         /* unhook */
672         for (prev = &list; *prev; prev = &(*prev)->next)
673             if ((*prev) == w)
674                 break;
675         *prev = w->next;
676         
677         /* rehook */
678         for (prev = &list; *prev; prev = &(*prev)->next)
679         {
680             if ((*prev)->id == ce->above)
681                 break;
682         }
683         w->next = *prev;
684         *prev = w;
685     }
686     if (damage)
687     {
688         XserverRegion   extents = win_extents (dpy, w);
689         XFixesUnionRegion (dpy, damage, damage, extents);
690         XFixesDestroyRegion (dpy, extents);
691         add_damage (dpy, damage);
692     }
693 }
694
695 static void
696 destroy_win (Display *dpy, Window id, Bool gone)
697 {
698     win **prev, *w;
699
700     for (prev = &list; (w = *prev); prev = &w->next)
701         if (w->id == id)
702         {
703             if (!gone)
704             {
705                 unmap_win (dpy, id);
706                 if (w->picture)
707                     XRenderFreePicture (dpy, w->picture);
708             }
709             *prev = w->next;
710             free (w);
711             break;
712         }
713 }
714
715 /*
716 static void
717 dump_win (win *w)
718 {
719     printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id,
720             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
721 }
722
723
724 static void
725 dump_wins (void)
726 {
727     win *w;
728
729     printf ("windows:\n");
730     for (w = list; w; w = w->next)
731         dump_win (w);
732 }
733 */
734
735 static void
736 damage_win (Display *dpy, XDamageNotifyEvent *de)
737 {
738     repair_win (dpy, de->drawable);
739 }
740
741 static int
742 error (Display *dpy, XErrorEvent *ev)
743 {
744     printf ("error %d request %d minor %d\n",
745             ev->error_code, ev->request_code, ev->minor_code);
746
747     return 0;
748 }
749
750 static void
751 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
752 {
753     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
754     
755     add_damage (dpy, region);
756 }
757
758 #define INTERVAL    0
759
760 #if INTERVAL
761 static int
762 time_in_millis (void)
763 {
764     struct timeval  tp;
765
766     gettimeofday (&tp, 0);
767     return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
768 }
769 #endif
770
771 int
772 main (void)
773 {
774     XEvent          ev;
775     int             event_base, error_base;
776     Window          root_return, parent_return;
777     Window          *children;
778     Pixmap          transPixmap;
779     Pixmap          blackPixmap;
780     unsigned int    nchildren;
781     int             i;
782     int             damage_event, damage_error;
783     int             xfixes_event, xfixes_error;
784     XRenderPictureAttributes    pa;
785     XRenderColor                c;
786     XRectangle      *expose_rects = 0;
787     int             size_expose = 0;
788     int             n_expose = 0;
789 #if INTERVAL
790     int             timeout;
791 #endif
792
793     dpy = XOpenDisplay (0);
794     if (!dpy)
795     {
796         fprintf (stderr, "Can't open display\n");
797         exit (1);
798     }
799     XSetErrorHandler (error);
800     scr = DefaultScreen (dpy);
801     root = RootWindow (dpy, scr);
802     pa.subwindow_mode = IncludeInferiors;
803
804     gussianMap = make_gaussian_map(dpy, SHADOW_RADIUS);
805
806     transPixmap = XCreatePixmap (dpy, root, 1, 1, 8);
807     pa.repeat = True;
808     transPicture = XRenderCreatePicture (dpy, transPixmap,
809                                          XRenderFindStandardFormat (dpy, PictStandardA8),
810                                          CPRepeat,
811                                          &pa);
812     c.red = c.green = c.blue = 0;
813     c.alpha = 0xc0c0;
814     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
815     
816     root_width = DisplayWidth (dpy, scr);
817     root_height = DisplayHeight (dpy, scr);
818     
819     rootPicture = XRenderCreatePicture (dpy, root, 
820                                         XRenderFindVisualFormat (dpy,
821                                                                  DefaultVisual (dpy, scr)),
822                                         CPSubwindowMode,
823                                         &pa);
824     blackPixmap = XCreatePixmap (dpy, root, 1, 1, 32);
825     pa.repeat = True;
826     blackPicture = XRenderCreatePicture (dpy, blackPixmap,
827                                          XRenderFindStandardFormat (dpy, PictStandardARGB32),
828                                          CPRepeat,
829                                          &pa);
830     c.red = c.green = c.blue = 0;
831     c.alpha = 0xffff;
832     XRenderFillRectangle (dpy, PictOpSrc, blackPicture, &c, 0, 0, 1, 1);
833     if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
834     {
835         fprintf (stderr, "No composite extension\n");
836         exit (1);
837     }
838     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
839     {
840         fprintf (stderr, "No damage extension\n");
841         exit (1);
842     }
843     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
844     {
845         fprintf (stderr, "No XFixes extension\n");
846         exit (1);
847     }
848     allDamage = None;
849     XGrabServer (dpy);
850     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
851     XSelectInput (dpy, root, 
852                   SubstructureNotifyMask|
853                   ExposureMask|
854                   StructureNotifyMask|
855                   PropertyChangeMask);
856     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
857     for (i = 0; i < nchildren; i++)
858         add_win (dpy, children[i], i ? children[i-1] : None);
859     XFree (children);
860     XUngrabServer (dpy);
861     paint_all (dpy, None);
862 #if INTERVAL
863     last_update = time_in_millis ();
864 #endif
865     for (;;)
866     {
867 #if INTERVAL
868         int busy_start = 0;
869 #endif
870 /*      dump_wins (); */
871         do {
872             XNextEvent (dpy, &ev);
873 #if INTERVAL
874             if (!busy_start)
875                 busy_start = time_in_millis();
876 #endif
877 /*          printf ("event %d\n", ev.type); */
878             switch (ev.type) {
879             case CreateNotify:
880                 add_win (dpy, ev.xcreatewindow.window, 0);
881                 break;
882             case ConfigureNotify:
883                 configure_win (dpy, &ev.xconfigure);
884                 break;
885             case DestroyNotify:
886                 destroy_win (dpy, ev.xdestroywindow.window, True);
887                 break;
888             case MapNotify:
889                 map_win (dpy, ev.xmap.window);
890                 break;
891             case UnmapNotify:
892                 unmap_win (dpy, ev.xunmap.window);
893                 break;
894             case ReparentNotify:
895                 if (ev.xreparent.parent == root)
896                     add_win (dpy, ev.xreparent.window, 0);
897                 else
898                     destroy_win (dpy, ev.xreparent.window, False);
899                 break;
900             case Expose:
901                 if (ev.xexpose.window == root)
902                 {
903                     int more = ev.xexpose.count + 1;
904                     if (n_expose == size_expose)
905                     {
906                         if (expose_rects)
907                         {
908                             expose_rects = realloc (expose_rects, 
909                                                     (size_expose + more) * 
910                                                     sizeof (XRectangle));
911                             size_expose += more;
912                         }
913                         else
914                         {
915                             expose_rects = malloc (more * sizeof (XRectangle));
916                             size_expose = more;
917                         }
918                     }
919                     expose_rects[n_expose].x = ev.xexpose.x;
920                     expose_rects[n_expose].y = ev.xexpose.y;
921                     expose_rects[n_expose].width = ev.xexpose.width;
922                     expose_rects[n_expose].height = ev.xexpose.height;
923                     n_expose++;
924                     if (ev.xexpose.count == 0)
925                     {
926                         expose_root (dpy, root, expose_rects, n_expose);
927                         n_expose = 0;
928                     }
929                 }
930                 break;
931             case PropertyNotify:
932                 if (ev.xproperty.atom == XInternAtom (dpy, BACKGROUND_PROP, False))
933                 {
934                     if (rootTile)
935                     {
936                         XClearArea (dpy, root, 0, 0, 0, 0, True);
937                         XRenderFreePicture (dpy, rootTile);
938                         rootTile = None;
939                     }
940                 }
941                 break;
942             default:
943                 if (ev.type == damage_event + XDamageNotify)
944                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
945                 break;
946             }
947         } while (XEventsQueued (dpy, QueuedAfterReading));
948 #if INTERVAL
949         now = time_in_millis ();
950 /*      printf ("\t\tbusy %d\n", now - busy_start); */
951         timeout = INTERVAL - (now - last_update);
952         if (timeout > 0)
953         {
954             ufd.fd = ConnectionNumber (dpy);
955             ufd.events = POLLIN;
956             n = poll (&ufd, 1, timeout);
957             if (n > 0 && (ufd.revents & POLLIN) && XEventsQueued (dpy, QueuedAfterReading))
958                 continue;
959         }
960 #endif
961         if (allDamage)
962         {
963 #if INTERVAL
964             int old_update = last_update;
965             last_update = time_in_millis();
966 /*          printf ("delta %d\n", last_update - old_update); */
967 #endif
968             paint_all (dpy, allDamage);
969             allDamage = None;
970         }
971     }
972 }