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