oops. Left in an XSynchronize call.
[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     {
333         memcpy (&pixmap, prop, 4);
334         XFree (prop);
335         fill = False;
336     }
337     else
338     {
339         pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
340         fill = True;
341     }
342     pa.repeat = True;
343     picture = XRenderCreatePicture (dpy, pixmap,
344                                     XRenderFindVisualFormat (dpy,
345                                                              DefaultVisual (dpy, scr)),
346                                     CPRepeat, &pa);
347     if (fill)
348     {
349         XRenderColor    c;
350         
351         c.red = c.green = c.blue = 0x8080;
352         c.alpha = 0xffff;
353         XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 
354                               0, 0, 1, 1);
355     }
356     return picture;
357 }
358
359 void
360 paint_root (Display *dpy)
361 {
362     if (!rootTile)
363         rootTile = root_tile (dpy);
364     
365     XRenderComposite (dpy, PictOpSrc,
366                       rootTile, None, rootBuffer,
367                       0, 0, 0, 0, 0, 0, root_width, root_height);
368 }
369
370 XserverRegion
371 win_extents (Display *dpy, win *w)
372 {
373     XRectangle      r;
374     
375     if (!w->shadow)
376     {
377         double  opacity = SHADOW_OPACITY;
378         if (w->mode == WINDOW_TRANS)
379             opacity = opacity * TRANS_OPACITY;
380         w->shadow = shadow_picture (dpy, opacity, SHADOW_RADIUS, 
381                                     w->a.width, w->a.height,
382                                     &w->shadow_width, &w->shadow_height);
383         w->shadow_dx = SHADOW_OFFSET_X;
384         w->shadow_dy = SHADOW_OFFSET_Y;
385     }
386     r.x = w->a.x + w->a.border_width + w->shadow_dx;
387     r.y = w->a.y + w->a.border_width + w->shadow_dy;
388     r.width = w->shadow_width;
389     r.height = w->shadow_height;
390     return XFixesCreateRegion (dpy, &r, 1);
391 }
392
393 XserverRegion
394 border_size (Display *dpy, win *w)
395 {
396     XserverRegion   border;
397     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
398     /* translate this */
399     XFixesUnionRegion (dpy, border, border, w->a.x, w->a.y, None, 0, 0);
400     return border;
401 }
402
403 void
404 paint_all (Display *dpy, XserverRegion region)
405 {
406     win *w;
407     win *t = 0;
408     
409     if (!region)
410     {
411         XRectangle  r;
412         r.x = 0;
413         r.y = 0;
414         r.width = root_width;
415         r.height = root_height;
416         region = XFixesCreateRegion (dpy, &r, 1);
417     }
418     if (!rootBuffer)
419     {
420         Pixmap  rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
421                                             DefaultDepth (dpy, scr));
422         rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
423                                            XRenderFindVisualFormat (dpy,
424                                                                     DefaultVisual (dpy, scr)),
425                                            0, 0);
426         XFreePixmap (dpy, rootPixmap);
427     }
428     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
429     for (w = list; w; w = w->next)
430     {
431         Picture mask;
432         
433         if (w->a.map_state != IsViewable)
434             continue;
435         if (!w->picture)
436             continue;
437         
438         if (w->borderSize)
439             XFixesDestroyRegion (dpy, w->borderSize);
440         w->borderSize = border_size (dpy, w);
441         if (w->extents)
442             XFixesDestroyRegion (dpy, w->extents);
443         w->extents = win_extents (dpy, w);
444         if (w->mode != WINDOW_TRANS)
445         {
446             XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
447             XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0);
448             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
449                               0, 0, 0, 0, 
450                               w->a.x + w->a.border_width,
451                               w->a.y + w->a.border_width,
452                               w->a.width,
453                               w->a.height);
454         }
455         w->borderClip = XFixesCreateRegion (dpy, 0, 0);
456         XFixesUnionRegion (dpy, w->borderClip, region, 0, 0, None, 0, 0);
457         w->prev_trans = t;
458         t = w;
459     }
460     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
461     paint_root (dpy);
462     for (w = t; w; w = w->prev_trans)
463     {
464         XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
465         if (w->shadow)
466         {
467             XRenderComposite (dpy, PictOpOver, w->shadow, None, rootBuffer,
468                               0, 0, 0, 0,
469                               w->a.x + w->a.border_width + w->shadow_dx,
470                               w->a.y + w->a.border_width + w->shadow_dy,
471                               w->shadow_width, w->shadow_height);
472         }
473         if (w->mode == WINDOW_TRANS)
474             XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootBuffer,
475                               0, 0, 0, 0, 
476                               w->a.x + w->a.border_width,
477                               w->a.y + w->a.border_width,
478                               w->a.width,
479                               w->a.height);
480         XFixesDestroyRegion (dpy, w->borderClip);
481         w->borderClip = None;
482     }
483     XFixesDestroyRegion (dpy, region);
484     XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
485     XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
486                       0, 0, 0, 0, 0, 0, root_width, root_height);
487 }
488
489 void
490 add_damage (Display *dpy, XserverRegion damage)
491 {
492     if (allDamage)
493     {
494         XFixesUnionRegion (dpy, allDamage, allDamage, 0, 0, damage, 0, 0);
495         XFixesDestroyRegion (dpy, damage);
496     }
497     else
498         allDamage = damage;
499 }
500
501 void
502 repair_win (Display *dpy, Window id)
503 {
504     win             *w = find_win (dpy, id);
505     XserverRegion   parts;
506
507     if (!w)
508         return;
509 /*    printf ("repair 0x%x\n", w->id); */
510     parts = XFixesCreateRegion (dpy, 0, 0);
511     /* translate region */
512     XDamageSubtract (dpy, w->damage, None, parts);
513     XFixesUnionRegion (dpy, parts, parts, w->a.x, w->a.y, None, 0, 0);
514     add_damage (dpy, parts);
515 }
516
517 void
518 map_win (Display *dpy, Window id)
519 {
520     win             *w = find_win (dpy, id);
521     XserverRegion   region;
522
523     if (!w)
524         return;
525     w->a.map_state = IsViewable;
526     if (w->picture)
527     {
528         w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
529         region = win_extents (dpy, w);
530         add_damage (dpy, region);
531     }
532 }
533
534 void
535 unmap_win (Display *dpy, Window id)
536 {
537     win *w = find_win (dpy, id);
538
539     if (!w)
540         return;
541     w->a.map_state = IsUnmapped;
542     if (w->damage != None)
543     {
544         XDamageDestroy (dpy, w->damage);
545         w->damage = None;
546     }
547     if (w->extents != None)
548     {
549         add_damage (dpy, w->extents);    /* destroys region */
550         w->extents = None;
551     }
552 }
553
554 void
555 add_win (Display *dpy, Window id, Window prev)
556 {
557     win *new = malloc (sizeof (win));
558     win **p;
559     XWindowAttributes a;
560     XRenderPictureAttributes pa;
561     
562     if (!new)
563         return;
564     if (prev)
565     {
566         for (p = &list; *p; p = &(*p)->next)
567             if ((*p)->id == prev)
568                 break;
569     }
570     else
571         p = &list;
572     new->id = id;
573     if (!XGetWindowAttributes (dpy, id, &new->a))
574     {
575         free (new);
576         return;
577     }
578     new->damaged = 0;
579     new->damage = None;
580     pa.subwindow_mode = IncludeInferiors;
581     if (new->a.class == InputOnly)
582         new->picture = 0;
583     else
584     {
585         new->picture = XRenderCreatePicture (dpy, id,
586                                              XRenderFindVisualFormat (dpy, 
587                                                                       new->a.visual),
588                                              CPSubwindowMode,
589                                              &pa);
590     }
591                                          
592     new->shadow = None;
593     new->borderSize = None;
594     new->extents = None;
595     if (new->a.override_redirect)
596         new->mode = WINDOW_TRANS;
597     else
598         new->mode = WINDOW_DROP;
599     new->next = *p;
600     *p = new;
601     if (new->a.map_state == IsViewable)
602         map_win (dpy, id);
603 }
604
605 void
606 configure_win (Display *dpy, XConfigureEvent *ce)
607 {
608     win             *w = find_win (dpy, ce->window);
609     Window          above;
610     XserverRegion   damage = None;
611     
612     if (!w)
613     {
614         if (ce->window == root)
615         {
616             if (rootBuffer)
617             {
618                 XRenderFreePicture (dpy, rootBuffer);
619                 rootBuffer = None;
620             }
621             root_width = ce->width;
622             root_height = ce->height;
623         }
624         return;
625     }
626     if (w->a.map_state == IsViewable)
627     {
628         damage = XFixesCreateRegion (dpy, 0, 0);
629         if (w->extents != None) 
630             XFixesUnionRegion (dpy, damage, w->extents, 0, 0, None, 0, 0);
631     }
632     w->a.x = ce->x;
633     w->a.y = ce->y;
634     if (w->a.width != ce->width || w->a.height != ce->height)
635         if (w->shadow)
636         {
637             XRenderFreePicture (dpy, w->shadow);
638             w->shadow = None;
639         }
640     w->a.width = ce->width;
641     w->a.height = ce->height;
642     w->a.border_width = ce->border_width;
643     w->a.override_redirect = ce->override_redirect;
644     if (w->next)
645         above = w->next->id;
646     else
647         above = None;
648     if (above != ce->above)
649     {
650         win **prev;
651
652         /* unhook */
653         for (prev = &list; *prev; prev = &(*prev)->next)
654             if ((*prev) == w)
655                 break;
656         *prev = w->next;
657         
658         /* rehook */
659         for (prev = &list; *prev; prev = &(*prev)->next)
660         {
661             if ((*prev)->id == ce->above)
662                 break;
663         }
664         w->next = *prev;
665         *prev = w;
666     }
667     if (damage)
668     {
669         XserverRegion   border = border_size (dpy, w);
670         XFixesUnionRegion (dpy, damage, damage, 0, 0, border, 0, 0);
671         add_damage (dpy, damage);
672     }
673 }
674
675 void
676 destroy_win (Display *dpy, Window id, Bool gone)
677 {
678     win **prev, *w;
679
680     for (prev = &list; w = *prev; prev = &w->next)
681         if (w->id == id)
682         {
683             if (!gone)
684             {
685                 unmap_win (dpy, id);
686                 if (w->picture)
687                     XRenderFreePicture (dpy, w->picture);
688             }
689             *prev = w->next;
690             free (w);
691             break;
692         }
693 }
694
695 void
696 dump_win (win *w)
697 {
698     printf ("\t%08x: %d x %d + %d + %d (%d)\n", w->id,
699             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
700 }
701
702 void
703 dump_wins (void)
704 {
705     win *w;
706
707     printf ("windows:\n");
708     for (w = list; w; w = w->next)
709         dump_win (w);
710 }
711
712 void
713 damage_win (Display *dpy, XDamageNotifyEvent *de)
714 {
715     repair_win (dpy, de->drawable);
716 }
717
718 int
719 error (Display *dpy, XErrorEvent *ev)
720 {
721     printf ("error %d request %d minor %d\n",
722             ev->error_code, ev->request_code, ev->minor_code);
723 }
724
725 void
726 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
727 {
728     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
729     
730     add_damage (dpy, region);
731 }
732
733 main ()
734 {
735     XEvent          ev;
736     int             event_base, error_base;
737     Window          root_return, parent_return;
738     Window          *children;
739     Pixmap          transPixmap;
740     unsigned int    nchildren;
741     int             i;
742     int             damage_event, damage_error;
743     int             xfixes_event, xfixes_error;
744     XRenderPictureAttributes    pa;
745     XRenderColor                c;
746     XRectangle      *expose_rects = 0;
747     GC              gc;
748     int             size_expose = 0;
749     int             n_expose = 0;
750     struct pollfd   ufd;
751     int             n;
752
753     dpy = XOpenDisplay (0);
754     if (!dpy)
755     {
756         fprintf (stderr, "Can't open display\n");
757         exit (1);
758     }
759     XSetErrorHandler (error);
760     scr = DefaultScreen (dpy);
761     root = RootWindow (dpy, scr);
762     pa.subwindow_mode = IncludeInferiors;
763     transPixmap = XCreatePixmap (dpy, root, 1, 1, 8);
764     pa.repeat = True;
765     transPicture = XRenderCreatePicture (dpy, transPixmap,
766                                          XRenderFindStandardFormat (dpy, PictStandardA8),
767                                          CPRepeat,
768                                          &pa);
769     c.red = c.green = c.blue = 0;
770     c.alpha = 0xc0c0;
771     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
772     
773     root_width = DisplayWidth (dpy, scr);
774     root_height = DisplayHeight (dpy, scr);
775     
776     rootPicture = XRenderCreatePicture (dpy, root, 
777                                         XRenderFindVisualFormat (dpy,
778                                                                  DefaultVisual (dpy, scr)),
779                                         CPSubwindowMode,
780                                         &pa);
781     if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
782     {
783         fprintf (stderr, "No composite extension\n");
784         exit (1);
785     }
786     printf ("Composite error %d\n", error_base);
787     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
788     {
789         fprintf (stderr, "No damage extension\n");
790         exit (1);
791     }
792     printf ("Damage error %d\n", damage_error);
793     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
794     {
795         fprintf (stderr, "No XFixes extension\n");
796         exit (1);
797     }
798     printf ("XFixes error %d\n", xfixes_error);
799     allDamage = None;
800     XGrabServer (dpy);
801     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
802     paint_all (dpy, None);
803     XSelectInput (dpy, root, SubstructureNotifyMask|ExposureMask|StructureNotifyMask);
804     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
805     for (i = 0; i < nchildren; i++)
806         add_win (dpy, children[i], i ? children[i-1] : None);
807     XFree (children);
808     XUngrabServer (dpy);
809     for (;;)
810     {
811 /*      dump_wins (); */
812         do {
813             XNextEvent (dpy, &ev);
814 /*          printf ("event %d\n", ev.type); */
815             switch (ev.type) {
816             case CreateNotify:
817                 add_win (dpy, ev.xcreatewindow.window, 0);
818                 break;
819             case ConfigureNotify:
820                 configure_win (dpy, &ev.xconfigure);
821                 break;
822             case DestroyNotify:
823                 destroy_win (dpy, ev.xdestroywindow.window, True);
824                 break;
825             case MapNotify:
826                 map_win (dpy, ev.xmap.window);
827                 break;
828             case UnmapNotify:
829                 unmap_win (dpy, ev.xunmap.window);
830                 break;
831             case ReparentNotify:
832                 if (ev.xreparent.parent == root)
833                     add_win (dpy, ev.xreparent.window, 0);
834                 else
835                     destroy_win (dpy, ev.xreparent.window, False);
836                 break;
837             case Expose:
838                 if (ev.xexpose.window == root)
839                 {
840                     int more = ev.xexpose.count + 1;
841                     if (n_expose == size_expose)
842                     {
843                         if (expose_rects)
844                         {
845                             expose_rects = realloc (expose_rects, 
846                                                     (size_expose + more) * 
847                                                     sizeof (XRectangle));
848                             size_expose += more;
849                         }
850                         else
851                         {
852                             expose_rects = malloc (more * sizeof (XRectangle));
853                             size_expose = more;
854                         }
855                     }
856                     expose_rects[n_expose].x = ev.xexpose.x;
857                     expose_rects[n_expose].y = ev.xexpose.y;
858                     expose_rects[n_expose].width = ev.xexpose.width;
859                     expose_rects[n_expose].height = ev.xexpose.height;
860                     n_expose++;
861                     if (ev.xexpose.count == 0)
862                     {
863                         expose_root (dpy, root, expose_rects, n_expose);
864                         n_expose = 0;
865                     }
866                 }
867                 break;
868             default:
869                 if (ev.type == damage_event + XDamageNotify)
870                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
871                 break;
872             }
873         } while (XEventsQueued (dpy, QueuedAfterReading));
874         ufd.fd = ConnectionNumber (dpy);
875         ufd.events = POLLIN;
876         n = poll (&ufd, 1, 30);
877         if (n > 0 && (ufd.revents & POLLIN) && XEventsQueued (dpy, QueuedAfterReading))
878             continue;
879         if (allDamage)
880         {
881             paint_all (dpy, allDamage);
882             allDamage = None;
883         }
884     }
885 }