Initial revision
[dana/xcompmgr.git] / xcompmgr.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <X11/Xlib.h>
4 #include <X11/extensions/Xcomposite.h>
5 #include <X11/extensions/Xdamage.h>
6 #include <X11/extensions/Xrender.h>
7
8 typedef struct _win {
9     struct _win         *next;
10     Window              id;
11     XWindowAttributes   a;
12     int                 damaged;
13     int                 mode;
14     Damage              damage;
15     Picture             picture;
16     XserverRegion       borderSize;
17
18     /* for drawing translucent windows */
19     XserverRegion       borderClip;
20     struct _win         *prev_trans;
21 } win;
22
23 win *list;
24
25 Display         *dpy;
26 int             scr;
27 Window          root;
28 Picture         rootPicture;
29 Picture         transPicture;
30 XserverRegion   allDamage;
31
32 #define WINDOW_PLAIN    0
33 #define WINDOW_DROP     1
34 #define WINDOW_TRANS    2
35
36 win *
37 find_win (Display *dpy, Window id)
38 {
39     win *w;
40
41     for (w = list; w; w = w->next)
42         if (w->id == id)
43             return w;
44     return 0;
45 }
46
47 void
48 paint_root (Display *dpy)
49 {
50     XRenderColor    c;
51     
52     c.red = c.green = c.blue = 0x8080;
53     c.alpha = 0xffff;
54     XRenderFillRectangle (dpy, PictOpSrc, rootPicture, &c, 
55                           0, 0, 32767, 32767);
56 }
57
58 XserverRegion
59 border_size (Display *dpy, win *w)
60 {
61     XserverRegion   border;
62     border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
63     /* translate this */
64     XFixesUnionRegion (dpy, border, border, w->a.x, w->a.y, None, 0, 0);
65     return border;
66 }
67
68 void
69 paint_all (Display *dpy, XserverRegion region)
70 {
71     win *w;
72     win *t = 0;
73     
74     for (w = list; w; w = w->next)
75     {
76         Picture mask;
77         
78         if (w->a.map_state != IsViewable)
79             continue;
80         if (w->mode == WINDOW_TRANS)
81         {
82             w->borderClip = XFixesCreateRegion (dpy, 0, 0);
83             XFixesUnionRegion (dpy, w->borderClip, region, 0, 0, None, 0, 0);
84             w->prev_trans = t;
85             t = w;
86         }
87         else
88         {
89             XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
90             if (w->borderSize)
91                 XFixesDestroyRegion (dpy, w->borderSize);
92             w->borderSize = border_size (dpy, w);
93             XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0);
94             XRenderComposite (dpy, PictOpSrc, w->picture, None, rootPicture,
95                               0, 0, 0, 0, 
96                               w->a.x + w->a.border_width,
97                               w->a.y + w->a.border_width,
98                               w->a.width,
99                               w->a.height);
100         }
101     }
102     XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
103     paint_root (dpy);
104     for (w = t; w; w = w->prev_trans)
105     {
106         XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, w->borderClip);
107         XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootPicture,
108                               0, 0, 0, 0, 
109                               w->a.x + w->a.border_width,
110                               w->a.y + w->a.border_width,
111                               w->a.width,
112                               w->a.height);
113         XFixesDestroyRegion (dpy, w->borderClip);
114         w->borderClip = None;
115     }
116     XFixesDestroyRegion (dpy, region);
117 }
118
119 void
120 add_damage (Display *dpy, XserverRegion damage)
121 {
122     if (allDamage)
123     {
124         XFixesUnionRegion (dpy, allDamage, allDamage, 0, 0, damage, 0, 0);
125         XFixesDestroyRegion (dpy, damage);
126     }
127     else
128         allDamage = damage;
129 }
130
131 void
132 repair_win (Display *dpy, Window id)
133 {
134     win             *w = find_win (dpy, id);
135     XserverRegion   parts;
136
137     if (!w)
138         return;
139 /*    printf ("repair 0x%x\n", w->id); */
140     parts = XFixesCreateRegion (dpy, 0, 0);
141     /* translate region */
142     XDamageSubtract (dpy, w->damage, None, parts);
143     XFixesUnionRegion (dpy, parts, parts, w->a.x, w->a.y, None, 0, 0);
144     add_damage (dpy, parts);
145 }
146
147 void
148 map_win (Display *dpy, Window id)
149 {
150     win             *w = find_win (dpy, id);
151     XserverRegion   region;
152
153     if (!w)
154         return;
155     w->a.map_state = IsViewable;
156     w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
157     region = border_size (dpy, w);
158     add_damage (dpy, region);
159 }
160
161 void
162 unmap_win (Display *dpy, Window id)
163 {
164     win *w = find_win (dpy, id);
165
166     if (!w)
167         return;
168     w->a.map_state = IsUnmapped;
169     if (w->damage != None)
170     {
171         XDamageDestroy (dpy, w->damage);
172         w->damage = None;
173     }
174     if (w->borderSize != None)
175     {
176         add_damage (dpy, w->borderSize);    /* destroys region */
177         w->borderSize = None;
178     }
179 }
180
181 void
182 add_win (Display *dpy, Window id, Window prev)
183 {
184     win *new = malloc (sizeof (win));
185     win **p;
186     XWindowAttributes a;
187     XRenderPictureAttributes pa;
188     
189     if (!new)
190         return;
191     if (prev)
192     {
193         for (p = &list; *p; p = &(*p)->next)
194             if ((*p)->id == prev)
195                 break;
196     }
197     else
198         p = &list;
199     new->id = id;
200     if (!XGetWindowAttributes (dpy, id, &new->a))
201     {
202         free (new);
203         return;
204     }
205     new->damaged = 0;
206     new->damage = None;
207     pa.subwindow_mode = IncludeInferiors;
208     new->picture = XRenderCreatePicture (dpy, id,
209                                          XRenderFindVisualFormat (dpy, 
210                                                                   new->a.visual),
211                                          CPSubwindowMode,
212                                          &pa);
213                                          
214     new->borderSize = None;
215     if (new->a.override_redirect)
216         new->mode = WINDOW_TRANS;
217     else
218         new->mode = WINDOW_DROP;
219     new->next = *p;
220     *p = new;
221     if (new->a.map_state == IsViewable)
222         map_win (dpy, id);
223 }
224
225 void
226 configure_win (Display *dpy, XConfigureEvent *ce)
227 {
228     win             *w = find_win (dpy, ce->window);
229     Window          above;
230     XserverRegion   damage = None;
231     
232     if (!w)
233         return;
234     if (w->a.map_state == IsViewable)
235     {
236         damage = XFixesCreateRegion (dpy, 0, 0);
237         if (w->borderSize != None)      
238             XFixesUnionRegion (dpy, damage, w->borderSize, 0, 0, None, 0, 0);
239     }
240     w->a.x = ce->x;
241     w->a.y = ce->y;
242     w->a.width = ce->width;
243     w->a.height = ce->height;
244     w->a.border_width = ce->border_width;
245     w->a.override_redirect = ce->override_redirect;
246     if (w->next)
247         above = w->next->id;
248     else
249         above = None;
250     if (above != ce->above)
251     {
252         win **prev;
253
254         /* unhook */
255         for (prev = &list; *prev; prev = &(*prev)->next)
256             if ((*prev) == w)
257                 break;
258         *prev = w->next;
259         
260         /* rehook */
261         for (prev = &list; *prev; prev = &(*prev)->next)
262         {
263             if ((*prev)->id == ce->above)
264                 break;
265         }
266         w->next = *prev;
267         *prev = w;
268     }
269     if (damage)
270     {
271         XserverRegion   border = border_size (dpy, w);
272         XFixesUnionRegion (dpy, damage, damage, 0, 0, border, 0, 0);
273         add_damage (dpy, damage);
274     }
275 }
276
277 void
278 destroy_win (Display *dpy, Window id, Bool gone)
279 {
280     win **prev, *w;
281
282     for (prev = &list; w = *prev; prev = &w->next)
283         if (w->id == id)
284         {
285             if (!gone)
286             {
287                 unmap_win (dpy, id);
288                 XRenderFreePicture (dpy, w->picture);
289             }
290             *prev = w->next;
291             free (w);
292             break;
293         }
294 }
295
296 void
297 dump_win (win *w)
298 {
299     printf ("\t%08x: %d x %d + %d + %d (%d)\n", w->id,
300             w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
301 }
302
303 void
304 dump_wins (void)
305 {
306     win *w;
307
308     printf ("windows:\n");
309     for (w = list; w; w = w->next)
310         dump_win (w);
311 }
312
313 void
314 damage_win (Display *dpy, XDamageNotifyEvent *de)
315 {
316     repair_win (dpy, de->drawable);
317 }
318
319 int
320 error (Display *dpy, XErrorEvent *ev)
321 {
322     printf ("error %d request %d minor %d\n",
323             ev->error_code, ev->request_code, ev->minor_code);
324 }
325
326 void
327 expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
328 {
329     XserverRegion  region = XFixesCreateRegion (dpy, rects, nrects);
330     
331     add_damage (dpy, region);
332 }
333
334 main ()
335 {
336     XEvent          ev;
337     int             event_base, error_base;
338     Window          root_return, parent_return;
339     Window          *children;
340     Pixmap          transPixmap;
341     unsigned int    nchildren;
342     int             i;
343     int             damage_event, damage_error;
344     int             xfixes_event, xfixes_error;
345     XRenderPictureAttributes    pa;
346     XRenderColor                c;
347     XRectangle      *expose_rects = 0;
348     int             size_expose = 0;
349     int             n_expose = 0;
350
351     dpy = XOpenDisplay (0);
352     if (!dpy)
353     {
354         fprintf (stderr, "Can't open display\n");
355         exit (1);
356     }
357     XSetErrorHandler (error);
358     scr = DefaultScreen (dpy);
359     root = RootWindow (dpy, scr);
360     pa.subwindow_mode = IncludeInferiors;
361     transPixmap = XCreatePixmap (dpy, root, 1, 1, 8);
362     pa.repeat = True;
363     transPicture = XRenderCreatePicture (dpy, transPixmap,
364                                          XRenderFindStandardFormat (dpy, PictStandardA8),
365                                          CPRepeat,
366                                          &pa);
367     c.red = c.green = c.blue = 0;
368     c.alpha = 0xc0c0;
369     XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1);
370     
371     rootPicture = XRenderCreatePicture (dpy, root, 
372                                         XRenderFindVisualFormat (dpy,
373                                                                  DefaultVisual (dpy, scr)),
374                                         CPSubwindowMode,
375                                         &pa);
376     if (!XCompositeQueryExtension (dpy, &event_base, &error_base))
377     {
378         fprintf (stderr, "No composite extension\n");
379         exit (1);
380     }
381     if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
382     {
383         fprintf (stderr, "No damage extension\n");
384         exit (1);
385     }
386     if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
387     {
388         fprintf (stderr, "No XFixes extension\n");
389         exit (1);
390     }
391     allDamage = None;
392     XGrabServer (dpy);
393     XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
394     paint_root (dpy);
395     XSelectInput (dpy, root, SubstructureNotifyMask|ExposureMask);
396     XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
397     for (i = 0; i < nchildren; i++)
398         add_win (dpy, children[i], i ? children[i-1] : None);
399     XFree (children);
400     XUngrabServer (dpy);
401     for (;;)
402     {
403 /*      dump_wins (); */
404         do {
405             XNextEvent (dpy, &ev);
406 /*          printf ("event %d\n", ev.type); */
407             switch (ev.type) {
408             case CreateNotify:
409                 add_win (dpy, ev.xcreatewindow.window, 0);
410                 break;
411             case ConfigureNotify:
412                 configure_win (dpy, &ev.xconfigure);
413                 break;
414             case DestroyNotify:
415                 destroy_win (dpy, ev.xdestroywindow.window, True);
416                 break;
417             case MapNotify:
418                 map_win (dpy, ev.xmap.window);
419                 break;
420             case UnmapNotify:
421                 unmap_win (dpy, ev.xunmap.window);
422                 break;
423             case ReparentNotify:
424                 if (ev.xreparent.parent == root)
425                     add_win (dpy, ev.xreparent.window, 0);
426                 else
427                     destroy_win (dpy, ev.xreparent.window, False);
428                 break;
429             case Expose:
430                 if (ev.xexpose.window == root)
431                 {
432                     int more = ev.xexpose.count + 1;
433                     if (n_expose == size_expose)
434                     {
435                         if (expose_rects)
436                         {
437                             expose_rects = realloc (expose_rects, 
438                                                     (size_expose + more) * 
439                                                     sizeof (XRectangle));
440                             size_expose += more;
441                         }
442                         else
443                         {
444                             expose_rects = malloc (more * sizeof (XRectangle));
445                             size_expose = more;
446                         }
447                     }
448                     expose_rects[n_expose].x = ev.xexpose.x;
449                     expose_rects[n_expose].y = ev.xexpose.y;
450                     expose_rects[n_expose].width = ev.xexpose.width;
451                     expose_rects[n_expose].height = ev.xexpose.height;
452                     n_expose++;
453                     if (ev.xexpose.count == 0)
454                     {
455                         expose_root (dpy, root, expose_rects, n_expose);
456                         n_expose = 0;
457                     }
458                 }
459                 break;
460             default:
461                 if (ev.type == damage_event + XDamageNotify)
462                     damage_win (dpy, (XDamageNotifyEvent *) &ev);
463                 break;
464             }
465         } while (XEventsQueued (dpy, QueuedAfterReading));
466         if (allDamage)
467         {
468             paint_all (dpy, allDamage);
469             allDamage = None;
470         }
471     }
472 }