2640221ce0bd40617ac7348dfd68859d5aa097fd
[dana/xcompmgr.git] / xcompmgr.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <math.h>
4 #include <X11/Xlib.h>
5 #include <X11/Xutil.h>
6 #include <X11/extensions/Xcomposite.h>
7 #include <X11/extensions/Xdamage.h>
8 #include <X11/extensions/Xrender.h>
9
10 typedef struct _win {
11     struct _win         *next;
12     Window              id;
13     XWindowAttributes   a;
14     int                 damaged;
15     int                 mode;
16     Damage              damage;
17     Picture             picture;
18     XserverRegion       borderSize;
19     XserverRegion       extents;
20     Picture             shadow;
21     int                 shadow_dx;
22     int                 shadow_dy;
23     int                 shadow_width;
24     int                 shadow_height;
25
26     /* for drawing translucent windows */
27     XserverRegion       borderClip;
28     struct _win         *prev_trans;
29 } win;
30
31 win *list;
32
33 Display         *dpy;
34 int             scr;
35 Window          root;
36 Picture         rootPicture;
37 Picture         transPicture;
38 XserverRegion   allDamage;
39
40 #define WINDOW_PLAIN    0
41 #define WINDOW_DROP     1
42 #define WINDOW_TRANS    2
43 #define TRANS_OPACITY   0.75
44 #define SHADOW_RADIUS   15
45 #define SHADOW_OPACITY  0.75
46 #define SHADOW_OFFSET_X (-SHADOW_RADIUS)
47 #define SHADOW_OFFSET_Y (-SHADOW_RADIUS)
48
49
50 double
51 gaussian (double r, double x, double y)
52 {
53     return ((1 / (sqrt (2 * M_PI * r))) *
54             exp ((- (x * x + y * y)) / (2 * r * r)));
55 }
56
57 typedef struct _conv {
58     int     size;
59     double  *data;
60 } conv;
61
62 conv *
63 make_gaussian_map (Display *dpy, double r)
64 {
65     conv            *c;
66     int             size = ((int) ceil ((r * 3)) + 1) & ~1;
67     int             center = size / 2;
68     int             x, y;
69     double          t;
70     double          g;
71     
72     c = malloc (sizeof (conv) + size * size * sizeof (double));
73     c->size = size;
74     c->data = (double *) (c + 1);
75     for (y = 0; y < size; y++)
76         for (x = 0; x < size; x++)
77         {
78             g = gaussian (r, (double) (x - center), (double) (y - center));
79             t += g;
80             c->data[y * size + x] = g;
81         }
82     printf ("gaussian total %f\n", t);
83     for (y = 0; y < size; y++)
84         for (x = 0; x < size; x++)
85         {
86             c->data[y*size + x] /= t;
87         }
88     return c;
89 }
90
91 /*
92  * A picture will help
93  *
94  *      -center   0                width  width+center
95  *  -center +-----+-------------------+-----+
96  *          |     |                   |     |
97  *          |     |                   |     |
98  *        0 +-----+-------------------+-----+
99  *          |     |                   |     |
100  *          |     |                   |     |
101  *          |     |                   |     |
102  *   height +-----+-------------------+-----+
103  *          |     |                   |     |
104  * height+  |     |                   |     |
105  *  center  +-----+-------------------+-----+
106  */
107  
108 unsigned int
109 sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
110 {
111     int     fx, fy;
112     int     sx, sy;
113     double  *g_data;
114     double  *g_line = map->data;
115     int     g_size = map->size;
116     int     center = g_size / 2;
117     int     fx_start, fx_end;
118     int     fy_start, fy_end;
119     double  v;
120     
121     /*
122      * Compute set of filter values which are "in range",
123      * that's the set with:
124      *  0 <= x + (fx-center) && x + (fx-center) < width &&
125      *  0 <= y + (fy-center) && y + (fy-center) < height
126      *
127      *  0 <= x + (fx - center)  x + fx - center < width
128      *  center - x <= fx        fx < width + center - x
129      */
130
131     fx_start = center - x;
132     if (fx_start < 0)
133         fx_start = 0;
134     fx_end = width + center - x;
135     if (fx_end > g_size)
136         fx_end = g_size;
137
138     fy_start = center - y;
139     if (fy_start < 0)
140         fy_start = 0;
141     fy_end = height + center - y;
142     if (fy_end > g_size)
143         fy_end = g_size;
144
145     g_line = g_line + fy_start * g_size + fx_start;
146     
147     v = 0;
148     for (fy = fy_start; fy < fy_end; fy++)
149     {
150         g_data = g_line;
151         g_line += g_size;
152         
153         for (fx = fx_start; fx < fx_end; fx++)
154             v += *g_data++;
155     }
156     if (v > 1)
157         v = 1;
158     
159     return ((unsigned int) (v * opacity * 255.0)) << 24;
160 }
161
162 XImage *
163 make_shadow (Display *dpy, double opacity, double r, int width, int height)
164 {
165     conv            *map = make_gaussian_map (dpy, r);
166     XImage          *ximage;
167     double          *gdata = map->data;
168     unsigned int    *data;
169     int             gsize = map->size;
170     int             ylimit, xlimit;
171     int             swidth = width + gsize;
172     int             sheight = height + gsize;
173     int             center = gsize / 2;
174     int             x, y;
175     int             fx, fy;
176     int             sx, sy;
177     unsigned int    d;
178     double          v;
179     unsigned char   c;
180     
181     data = malloc (swidth * sheight * sizeof (int));
182     ximage = XCreateImage (dpy,
183                            DefaultVisual(dpy, DefaultScreen(dpy)),
184                            32,
185                            ZPixmap,
186                            0,
187                            (char *) data,
188                            swidth, sheight, 32, swidth * sizeof (int));
189     /*
190      * Build the gaussian in sections
191      */
192
193     /*
194      * corners
195      */
196     ylimit = gsize;
197     if (ylimit > sheight / 2)
198         ylimit = (sheight + 1) / 2;
199     xlimit = gsize;
200     if (xlimit > swidth / 2)
201         xlimit = (swidth + 1) / 2;
202
203     for (y = 0; y < ylimit; y++)
204         for (x = 0; x < xlimit; x++)
205         {
206             d = sum_gaussian (map, opacity, x - center, y - center, width, height);
207             data[y * swidth + x] = d;
208             data[(sheight - y - 1) * swidth + x] = d;
209             data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d;
210             data[y * swidth + (swidth - x - 1)] = d;
211         }
212
213     /*
214      * top/bottom
215      */
216     for (y = 0; y < ylimit; y++)
217     {
218         d = sum_gaussian (map, opacity, center, y - center, width, height);
219         for (x = gsize; x < swidth - gsize; x++)
220         {
221             data[y * swidth + x] = d;
222             data[(sheight - y - 1) * swidth + x] = d;
223         }
224     }
225
226     /*
227      * sides
228      */
229     
230     for (x = 0; x < xlimit; x++)
231     {
232         d = sum_gaussian (map, opacity, x - center, center, width, height);
233         for (y = gsize; y < sheight - gsize; y++)
234         {
235             data[y * swidth + x] = d;
236             data[y * swidth + (swidth - x - 1)] = d;
237         }
238     }
239
240     /*
241      * center
242      */
243
244     d = sum_gaussian (map, opacity, center, center, width, height);
245     for (y = ylimit; y < sheight - ylimit; y++)
246         for (x = xlimit; x < swidth - xlimit; x++)
247             data[y * swidth + x] = d;
248
249     free (map);
250     return ximage;
251 }
252
253 Picture
254 shadow_picture (Display *dpy, double opacity, double r, int width, int height, int *wp, int *hp)
255 {
256     XImage  *shadowImage = make_shadow (dpy, opacity, r, width, height);
257     Pixmap  shadowPixmap = XCreatePixmap (dpy, root, 
258                                           shadowImage->width,
259                                           shadowImage->height,
260                                           32);
261     Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
262                                                   XRenderFindStandardFormat (dpy, PictStandardARGB32),
263                                                   0, 0);
264     GC      gc = XCreateGC (dpy, shadowPixmap, 0, 0);
265     
266     XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, 
267                shadowImage->width,
268                shadowImage->height);
269     *wp = shadowImage->width;
270     *hp = shadowImage->height;
271     XFreeGC (dpy, gc);
272     XDestroyImage (shadowImage);
273     XFreePixmap (dpy, shadowPixmap);
274     return shadowPicture;
275 }
276
277 win *
278 find_win (Display *dpy, Window id)
279 {
280     win *w;
281
282     for (w = list; w; w = w->next)
283         if (w->id == id)
284             return w;
285     return 0;
286 }
287
288 void
289 paint_root (Display *dpy)
290 {
291     XRenderColor    c;
292     
293     c.red = c.green = c.blue = 0x8080;
294     c.alpha = 0xffff;
295     XRenderFillRectangle (dpy, PictOpSrc, rootPicture, &c, 
296                           0, 0, 32767, 32767);
297 }
298
299 XserverRegion
300 win_extents (Display *dpy, win *w)
301 {
302     XRectangle      r;
303     
304     if (!w->shadow)
305     {
306         double  opacity = SHADOW_OPACITY;
307         if (w->mode == WINDOW_TRANS)
308             opacity = opacity * TRANS_OPACITY;
309         w->shadow = shadow_picture (dpy, opacity, SHADOW_RADIUS, 
310                                     w->a.width, w->a.height,
311                                     &w->shadow_width, &w->shadow_height);
312         w->shadow_dx = SHADOW_OFFSET_X;
313         w->shadow_dy = SHADOW_OFFSET_Y;
314     }
315     r.x = w->a.x + w->a.border_width + w->shadow_dx;
316     r.y = w->a.y + w->a.border_width + w->shadow_dy;
317     r.width = w->shadow_width;
318     r.height = w->shadow_height;
319     return XFixesCreateRegion (dpy, &r, 1);
320 }
321
322 XserverRegion
323 border_size (Display *dpy, win *w)
324 {
325     XserverRegion   border;
326     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
327     /* translate this */
328     XFixesUnionRegion (dpy, border, border, w->a.x, w->a.y, None, 0, 0);
329     return border;
330 }
331
332 void
333 paint_all (Display *dpy, XserverRegion region)
334 {
335     win *w;
336     win *t = 0;
337     
338     for (w = list; w; w = w->next)
339     {
340         Picture mask;
341         
342         if (w->a.map_state != IsViewable)
343             continue;
344         if (w->borderSize)
345             XFixesDestroyRegion (dpy, w->borderSize);
346         w->borderSize = border_size (dpy, w);
347         if (w->extents)
348             XFixesDestroyRegion (dpy, w->extents);
349         w->extents = win_extents (dpy, w);
350         if (w->mode != WINDOW_TRANS)
351         {
352             XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
353             XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0);
354             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootPicture,
355                               0, 0, 0, 0, 
356                               w->a.x + w->a.border_width,
357                               w->a.y + w->a.border_width,
358                               w->a.width,
359                               w->a.height);
360         }
361         w->borderClip = XFixesCreateRegion (dpy, 0, 0);
362         XFixesUnionRegion (dpy, w->borderClip, region, 0, 0, None, 0, 0);
363         w->prev_trans = t;
364         t = w;
365     }
366     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
367     paint_root (dpy);
368     for (w = t; w; w = w->prev_trans)
369     {
370         XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, w->borderClip);
371         if (w->shadow)
372         {
373             XRenderComposite (dpy, PictOpOver, w->shadow, None, rootPicture,
374                               0, 0, 0, 0,
375                               w->a.x + w->a.border_width + w->shadow_dx,
376                               w->a.y + w->a.border_width + w->shadow_dy,
377                               w->shadow_width, w->shadow_height);
378         }
379         if (w->mode == WINDOW_TRANS)
380             XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootPicture,
381                               0, 0, 0, 0, 
382                               w->a.x + w->a.border_width,
383                               w->a.y + w->a.border_width,
384                               w->a.width,
385                               w->a.height);
386         XFixesDestroyRegion (dpy, w->borderClip);
387         w->borderClip = None;
388     }
389     XFixesDestroyRegion (dpy, region);
390 }
391
392 void
393 add_damage (Display *dpy, XserverRegion damage)
394 {
395     if (allDamage)
396     {
397         XFixesUnionRegion (dpy, allDamage, allDamage, 0, 0, damage, 0, 0);
398         XFixesDestroyRegion (dpy, damage);
399     }
400     else
401         allDamage = damage;
402 }
403
404 void
405 repair_win (Display *dpy, Window id)
406 {
407     win             *w = find_win (dpy, id);
408     XserverRegion   parts;
409
410     if (!w)
411         return;
412 /*    printf ("repair 0x%x\n", w->id); */
413     parts = XFixesCreateRegion (dpy, 0, 0);
414     /* translate region */
415     XDamageSubtract (dpy, w->damage, None, parts);
416     XFixesUnionRegion (dpy, parts, parts, w->a.x, w->a.y, None, 0, 0);
417     add_damage (dpy, parts);
418 }
419
420 void
421 map_win (Display *dpy, Window id)
422 {
423     win             *w = find_win (dpy, id);
424     XserverRegion   region;
425
426     if (!w)
427         return;
428     w->a.map_state = IsViewable;
429     w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
430     region = win_extents (dpy, w);
431     add_damage (dpy, region);
432 }
433
434 void
435 unmap_win (Display *dpy, Window id)
436 {
437     win *w = find_win (dpy, id);
438
439     if (!w)
440         return;
441     w->a.map_state = IsUnmapped;
442     if (w->damage != None)
443     {
444         XDamageDestroy (dpy, w->damage);
445         w->damage = None;
446     }
447     if (w->extents != None)
448     {
449         add_damage (dpy, w->extents);    /* destroys region */
450         w->extents = None;
451     }
452 }
453
454 void
455 add_win (Display *dpy, Window id, Window prev)
456 {
457     win *new = malloc (sizeof (win));
458     win **p;
459     XWindowAttributes a;
460     XRenderPictureAttributes pa;
461     
462     if (!new)
463         return;
464     if (prev)
465     {
466         for (p = &list; *p; p = &(*p)->next)
467             if ((*p)->id == prev)
468                 break;
469     }
470     else
471         p = &list;
472     new->id = id;
473     if (!XGetWindowAttributes (dpy, id, &new->a))
474     {
475         free (new);
476         return;
477     }
478     new->damaged = 0;
479     new->damage = None;
480     pa.subwindow_mode = IncludeInferiors;
481     new->picture = XRenderCreatePicture (dpy, id,
482                                          XRenderFindVisualFormat (dpy, 
483                                                                   new->a.visual),
484                                          CPSubwindowMode,
485                                          &pa);
486                                          
487     new->shadow = None;
488     new->borderSize = None;
489     new->extents = None;
490     if (new->a.override_redirect)
491         new->mode = WINDOW_TRANS;
492     else
493         new->mode = WINDOW_DROP;
494     new->next = *p;
495     *p = new;
496     if (new->a.map_state == IsViewable)
497         map_win (dpy, id);
498 }
499
500 void
501 configure_win (Display *dpy, XConfigureEvent *ce)
502 {
503     win             *w = find_win (dpy, ce->window);
504     Window          above;
505     XserverRegion   damage = None;
506     
507     if (!w)
508         return;
509     if (w->a.map_state == IsViewable)
510     {
511         damage = XFixesCreateRegion (dpy, 0, 0);
512         if (w->extents != None) 
513             XFixesUnionRegion (dpy, damage, w->extents, 0, 0, None, 0, 0);
514     }
515     w->a.x = ce->x;
516     w->a.y = ce->y;
517     if (w->a.width != ce->width || w->a.height != ce->height)
518         if (w->shadow)
519         {
520             XRenderFreePicture (dpy, w->shadow);
521             w->shadow = None;
522         }
523     w->a.width = ce->width;
524     w->a.height = ce->height;
525     w->a.border_width = ce->border_width;
526     w->a.override_redirect = ce->override_redirect;
527     if (w->next)
528         above = w->next->id;
529     else
530         above = None;
531     if (above != ce->above)
532     {
533         win **prev;
534
535         /* unhook */
536         for (prev = &list; *prev; prev = &(*prev)->next)
537             if ((*prev) == w)
538                 break;
539         *prev = w->next;
540         
541         /* rehook */
542         for (prev = &list; *prev; prev = &(*prev)->next)
543         {
544             if ((*prev)->id == ce->above)
545                 break;
546         }
547         w->next = *prev;
548         *prev = w;
549     }
550     if (damage)
551     {
552         XserverRegion   border = border_size (dpy, w);
553         XFixesUnionRegion (dpy, damage, damage, 0, 0, border, 0, 0);
554         add_damage (dpy, damage);
555     }
556 }
557
558 void
559 destroy_win (Display *dpy, Window id, Bool gone)
560 {
561     win **prev, *w;
562
563     for (prev = &list; w = *prev; prev = &w->next)
564         if (w->id == id)
565         {
566             if (!gone)
567             {
568                 unmap_win (dpy, id);
569                 XRenderFreePicture (dpy, w->picture);
570             }
571             *prev = w->next;
572             free (w);
573             break;
574         }
575 }
576
577 void
578 dump_win (win *w)
579 {
580     printf ("\t%08x: %d x %d + %d + %d (%d)\n", w->id,
581             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
582 }
583
584 void
585 dump_wins (void)
586 {
587     win *w;
588
589     printf ("windows:\n");
590     for (w = list; w; w = w->next)
591         dump_win (w);
592 }
593
594 void
595 damage_win (Display *dpy, XDamageNotifyEvent *de)
596 {
597     repair_win (dpy, de->drawable);
598 }
599
600 int
601 error (Display *dpy, XErrorEvent *ev)
602 {
603     printf ("error %d request %d minor %d\n",
604             ev->error_code, ev->request_code, ev->minor_code);
605 }
606
607 void
608 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
609 {
610     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
611     
612     add_damage (dpy, region);
613 }
614
615 main ()
616 {
617     XEvent          ev;
618     int             event_base, error_base;
619     Window          root_return, parent_return;
620     Window          *children;
621     Pixmap          transPixmap;
622     unsigned int    nchildren;
623     int             i;
624     int             damage_event, damage_error;
625     int             xfixes_event, xfixes_error;
626     XRenderPictureAttributes    pa;
627     XRenderColor                c;
628     XRectangle      *expose_rects = 0;
629     GC              gc;
630     int             size_expose = 0;
631     int             n_expose = 0;
632
633     dpy = XOpenDisplay (0);
634     if (!dpy)
635     {
636         fprintf (stderr, "Can't open display\n");
637         exit (1);
638     }
639     XSetErrorHandler (error);
640     scr = DefaultScreen (dpy);
641     root = RootWindow (dpy, scr);
642     pa.subwindow_mode = IncludeInferiors;
643     transPixmap = XCreatePixmap (dpy, root, 1, 1, 8);
644     pa.repeat = True;
645     transPicture = XRenderCreatePicture (dpy, transPixmap,
646                                          XRenderFindStandardFormat (dpy, PictStandardA8),
647                                          CPRepeat,
648                                          &pa);
649     c.red = c.green = c.blue = 0;
650     c.alpha = 0xc0c0;
651     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
652     
653     rootPicture = XRenderCreatePicture (dpy, root, 
654                                         XRenderFindVisualFormat (dpy,
655                                                                  DefaultVisual (dpy, scr)),
656                                         CPSubwindowMode,
657                                         &pa);
658     if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
659     {
660         fprintf (stderr, "No composite extension\n");
661         exit (1);
662     }
663     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
664     {
665         fprintf (stderr, "No damage extension\n");
666         exit (1);
667     }
668     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
669     {
670         fprintf (stderr, "No XFixes extension\n");
671         exit (1);
672     }
673     allDamage = None;
674     XGrabServer (dpy);
675     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
676     paint_root (dpy);
677     XSelectInput (dpy, root, SubstructureNotifyMask|ExposureMask);
678     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
679     for (i = 0; i < nchildren; i++)
680         add_win (dpy, children[i], i ? children[i-1] : None);
681     XFree (children);
682     XUngrabServer (dpy);
683     for (;;)
684     {
685 /*      dump_wins (); */
686         do {
687             XNextEvent (dpy, &ev);
688 /*          printf ("event %d\n", ev.type); */
689             switch (ev.type) {
690             case CreateNotify:
691                 add_win (dpy, ev.xcreatewindow.window, 0);
692                 break;
693             case ConfigureNotify:
694                 configure_win (dpy, &ev.xconfigure);
695                 break;
696             case DestroyNotify:
697                 destroy_win (dpy, ev.xdestroywindow.window, True);
698                 break;
699             case MapNotify:
700                 map_win (dpy, ev.xmap.window);
701                 break;
702             case UnmapNotify:
703                 unmap_win (dpy, ev.xunmap.window);
704                 break;
705             case ReparentNotify:
706                 if (ev.xreparent.parent == root)
707                     add_win (dpy, ev.xreparent.window, 0);
708                 else
709                     destroy_win (dpy, ev.xreparent.window, False);
710                 break;
711             case Expose:
712                 if (ev.xexpose.window == root)
713                 {
714                     int more = ev.xexpose.count + 1;
715                     if (n_expose == size_expose)
716                     {
717                         if (expose_rects)
718                         {
719                             expose_rects = realloc (expose_rects, 
720                                                     (size_expose + more) * 
721                                                     sizeof (XRectangle));
722                             size_expose += more;
723                         }
724                         else
725                         {
726                             expose_rects = malloc (more * sizeof (XRectangle));
727                             size_expose = more;
728                         }
729                     }
730                     expose_rects[n_expose].x = ev.xexpose.x;
731                     expose_rects[n_expose].y = ev.xexpose.y;
732                     expose_rects[n_expose].width = ev.xexpose.width;
733                     expose_rects[n_expose].height = ev.xexpose.height;
734                     n_expose++;
735                     if (ev.xexpose.count == 0)
736                     {
737                         expose_root (dpy, root, expose_rects, n_expose);
738                         n_expose = 0;
739                     }
740                 }
741                 break;
742             default:
743                 if (ev.type == damage_event + XDamageNotify)
744                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
745                 break;
746             }
747         } while (XEventsQueued (dpy, QueuedAfterReading));
748         if (allDamage)
749         {
750             paint_all (dpy, allDamage);
751             allDamage = None;
752         }
753     }
754 }