super fading
[dana/dcompmgr.git] / render.c
1 #include "efence.h"
2
3 #include "render.h"
4 #include "screen.h"
5 #include "window.h"
6 #include "display.h"
7 #include "list.h"
8 #include <stdio.h>
9 #include <assert.h>
10 #include <stdlib.h>
11 #include <xcb/render.h>
12
13 static int plugin_id;
14
15 typedef struct {
16     void (*screen_paint)(d_screen_t *sc);
17     void (*screen_root_pixmap_change)(d_screen_t *sc);
18     void (*window_show)(d_window_t *w);
19     void (*window_zombie_dead)(d_window_t *w);
20     void (*window_resize)(d_window_t *w);
21     void (*window_opacity_change)(d_window_t *w);
22
23     xcb_render_pictformat_t root_format;
24     xcb_render_pictformat_t argb32_format;
25     xcb_render_query_pict_formats_reply_t *pict_formats;
26     xcb_render_picture_t overlay_picture;
27     xcb_render_picture_t overlay_buffer;
28     xcb_render_picture_t root_picture;
29     xcb_render_picture_t solid_bg;
30
31     xcb_xfixes_region_t all_region;
32     xcb_xfixes_region_t paint_region;
33     xcb_xfixes_region_t shadow_region;
34
35     uint16_t shadowalpha;
36     int xshadowoff;
37     int yshadowoff;
38 } data_t;
39
40 typedef struct {
41     xcb_render_picture_t picture;
42     xcb_render_picture_t shadow_picture;
43     xcb_xfixes_region_t paint_clip;
44 } window_data_t;
45
46 static void render_paint(d_screen_t *sc);
47 static void render_root_pixmap_change(d_screen_t *sc);
48 static void paint_root(d_screen_t *sc, data_t *d);
49 static void paint_window(d_window_t *window, data_t *d, window_data_t *wd,
50                          gboolean opaque, int x, int y, int width,
51                          int height, int bwidth);
52 static void paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
53                          int x, int y, int width, int height, int bwidth);
54 static void render_update_shadow_picture(d_window_t *w, data_t *d,
55                                          window_data_t *wd);
56 static void render_update_picture(d_window_t *w, data_t *d, window_data_t *wd);
57 static void render_update_root_picture(d_screen_t *sc, data_t *d);
58 static void render_free_picture(d_window_t *w, window_data_t *wd);
59 static xcb_render_pictformat_t find_visual_format(data_t *d,
60                                                   xcb_visualid_t visual);
61 static xcb_render_pictformat_t find_argb32_format(data_t *d);
62 static xcb_render_picture_t solid_picture(data_t *d, d_screen_t *sc,
63                                           uint16_t a, uint16_t r,
64                                           uint16_t g, uint16_t b);
65
66 static void render_window_show(d_window_t *window);
67 static void render_window_zombie_dead(d_window_t *window);
68 static void render_window_resize(d_window_t *window);
69 static void render_window_opacity_change(d_window_t *w);
70
71 void
72 render_init(d_screen_t *sc, int id)
73 {
74     xcb_render_query_pict_formats_cookie_t ck;
75     xcb_pixmap_t px;
76     xcb_rectangle_t rect;
77
78     plugin_id = id;
79
80     data_t *d = malloc(sizeof(data_t));
81     d->screen_paint = sc->screen_paint;
82     d->screen_root_pixmap_change = sc->screen_root_pixmap_change;
83     d->window_show = sc->window_show;
84     d->window_zombie_dead = sc->window_zombie_dead;
85     d->window_resize = sc->window_resize;
86     d->window_opacity_change = sc->window_opacity_change;
87     screen_add_plugin_data(sc, plugin_id, d);
88
89     sc->screen_paint = render_paint;
90     sc->screen_root_pixmap_change = render_root_pixmap_change;
91     sc->window_show = render_window_show;
92     sc->window_zombie_dead = render_window_zombie_dead;
93     sc->window_resize = render_window_resize;
94     sc->window_opacity_change = render_window_opacity_change;
95
96     ck = xcb_render_query_pict_formats_unchecked(sc->dpy->conn);
97     d->pict_formats = xcb_render_query_pict_formats_reply(sc->dpy->conn, ck,
98                                                           NULL);
99
100     d->root_format = find_visual_format(d, sc->super.root_visual);
101     d->argb32_format = find_argb32_format(d);
102     d->root_picture = XCB_NONE;
103
104     d->overlay_picture = xcb_generate_id(sc->dpy->conn);
105     xcb_render_create_picture(sc->dpy->conn,
106                               d->overlay_picture, sc->overlay, d->root_format,
107                               0, NULL);
108
109     /* make the double buffer */
110     px = xcb_generate_id(sc->dpy->conn);
111     xcb_create_pixmap(sc->dpy->conn, sc->super.root_depth, px,
112                       sc->super.root, sc->super.width_in_pixels,
113                       sc->super.height_in_pixels);
114     d->overlay_buffer = xcb_generate_id(sc->dpy->conn);
115     xcb_render_create_picture(sc->dpy->conn, d->overlay_buffer, px,
116                               d->root_format, 0, 0);
117     xcb_free_pixmap(sc->dpy->conn, px);
118
119     d->solid_bg = solid_picture(d, sc, 0xffff, 0x6060, 02020, 0x3030);
120
121     d->all_region = xcb_generate_id(sc->dpy->conn);
122     d->paint_region = xcb_generate_id(sc->dpy->conn);
123     d->shadow_region = xcb_generate_id(sc->dpy->conn);
124     rect.x = rect.y = 0;
125     rect.width = sc->super.width_in_pixels;
126     rect.height = sc->super.height_in_pixels;
127     xcb_xfixes_create_region(sc->dpy->conn, d->all_region, 1, &rect);
128     xcb_xfixes_create_region(sc->dpy->conn, d->paint_region, 1, &rect);
129     xcb_xfixes_create_region(sc->dpy->conn, d->shadow_region, 1, &rect);
130
131     d->shadowalpha = 0x3333; /* 20% */
132     d->xshadowoff = 2;
133     d->yshadowoff = 2;
134 }
135
136 void
137 render_free(d_screen_t *sc)
138 {
139     data_t *d = screen_find_plugin_data(sc, plugin_id);
140     free(d->pict_formats);
141     xcb_render_free_picture(sc->dpy->conn, d->solid_bg);
142     if (d->root_picture)
143         xcb_render_free_picture(sc->dpy->conn, d->root_picture);
144     xcb_render_free_picture(sc->dpy->conn, d->overlay_picture);
145     xcb_render_free_picture(sc->dpy->conn, d->overlay_buffer);
146     xcb_xfixes_destroy_region(sc->dpy->conn, d->all_region);
147     xcb_xfixes_destroy_region(sc->dpy->conn, d->paint_region);
148     xcb_xfixes_destroy_region(sc->dpy->conn, d->shadow_region);
149     free(d);
150     screen_remove_plugin_data(sc, plugin_id);
151 }
152
153 int
154 render_next_timeout(struct d_screen *sc, struct timeval *tv)
155 {
156     (void)sc;
157     (void)tv;
158     return FALSE;
159 }
160
161 void
162 render_timeout(struct d_screen *sc, const struct timeval *now)
163 {
164     (void)sc; (void)now;
165 }
166
167 void
168 render_window_free(d_window_t *w, window_data_t *wd)
169 {
170     render_free_picture(w, wd);
171     if (wd->shadow_picture) {
172         xcb_render_free_picture(w->sc->dpy->conn, wd->shadow_picture);
173         wd->shadow_picture = XCB_NONE;
174     }
175     free(wd);
176 }
177
178 static void
179 render_window_show(d_window_t *w)
180 {
181     data_t *d;
182     window_data_t *wd;
183     xcb_rectangle_t rect;
184
185     d = screen_find_plugin_data(w->sc, plugin_id);
186
187     /* pass it on */
188     d->window_show(w);
189
190     wd = window_find_plugin_data(w, plugin_id);
191     if (wd)
192         render_window_free(w, wd);
193    
194     wd = malloc(sizeof(window_data_t));
195     wd->picture = XCB_NONE;
196
197     wd->paint_clip = xcb_generate_id(w->sc->dpy->conn);
198     rect.x = rect.y = 0;
199     rect.width = rect.height = 1;
200     xcb_xfixes_create_region(w->sc->dpy->conn, wd->paint_clip, 1, &rect);
201
202     render_update_shadow_picture(w, d, wd);
203
204     window_add_plugin_data(w, plugin_id, wd);
205 }
206
207 static void
208 render_window_zombie_dead(d_window_t *w)
209 {
210     data_t *d;
211     window_data_t *wd;
212
213     d = screen_find_plugin_data(w->sc, plugin_id);
214     wd = window_find_plugin_data(w, plugin_id);
215     if (wd) {
216         render_window_free(w, wd);
217         xcb_xfixes_destroy_region(w->sc->dpy->conn, wd->paint_clip);
218         window_remove_plugin_data(w, plugin_id);
219     }
220
221     /* pass it on */
222     d->window_zombie_dead(w);
223 }
224
225 static xcb_render_picture_t
226 solid_picture(data_t *d, d_screen_t *sc, uint16_t a, uint16_t r,
227               uint16_t g, uint16_t b)
228 {
229     xcb_pixmap_t pixmap;
230     xcb_render_picture_t picture;
231     xcb_render_color_t   c;
232     const uint32_t vals = XCB_RENDER_REPEAT_NORMAL;
233     const xcb_rectangle_t rect = { 0, 0, 1, 1 };
234
235     pixmap = xcb_generate_id(sc->dpy->conn);
236     picture = xcb_generate_id(sc->dpy->conn);
237
238     xcb_create_pixmap(sc->dpy->conn, 32, pixmap, sc->super.root, 1, 1);
239     xcb_render_create_picture(sc->dpy->conn, picture, pixmap, d->argb32_format,
240                               XCB_RENDER_CP_REPEAT, &vals);
241
242     c.alpha = a;
243     c.red   = r;
244     c.green = g;
245     c.blue  = b;
246
247     xcb_render_fill_rectangles(sc->dpy->conn, XCB_RENDER_PICT_OP_SRC,
248                                picture, c, 1, &rect);
249     xcb_free_pixmap(sc->dpy->conn, pixmap);
250
251     return picture;
252 }
253
254 static xcb_render_pictformat_t
255 find_argb32_format(data_t *d)
256 {
257     xcb_render_pictforminfo_iterator_t it;
258
259     for (it = xcb_render_query_pict_formats_formats_iterator(d->pict_formats);
260          it.rem; xcb_render_pictforminfo_next(&it))
261     {
262         xcb_render_pictforminfo_t *format = it.data;
263         if (format->type == XCB_RENDER_PICT_TYPE_DIRECT) {
264             if (format->depth              == 32   &&
265                 format->direct.alpha_mask  == 0xff &&
266                 format->direct.red_mask    == 0xff &&
267                 format->direct.green_mask  == 0xff &&
268                 format->direct.blue_mask   == 0xff &&
269                 format->direct.alpha_shift == 24   &&
270                 format->direct.red_shift   == 16   &&
271                 format->direct.green_shift == 8    &&
272                 format->direct.blue_shift  == 0)
273                 return format->id;
274         }
275     }
276     return XCB_NONE;
277 }
278
279 static xcb_render_pictformat_t
280 find_visual_format(data_t *d, xcb_visualid_t visual)
281 {
282     xcb_render_pictscreen_iterator_t si;
283     xcb_render_pictdepth_iterator_t di;
284     xcb_render_pictvisual_iterator_t vi;
285
286     if (!visual) return XCB_NONE;
287
288     /* go through all the screens */
289     si = xcb_render_query_pict_formats_screens_iterator(d->pict_formats);
290     for (; si.rem; xcb_render_pictscreen_next(&si)) {
291         di = xcb_render_pictscreen_depths_iterator(si.data);
292         for (; di.rem; xcb_render_pictdepth_next(&di)) {
293             vi = xcb_render_pictdepth_visuals_iterator(di.data);
294             for (; vi.rem; xcb_render_pictvisual_next(&vi)) {
295                 if (vi.data->visual == visual)
296                     return vi.data->format;
297             }
298         }
299     }
300     return XCB_NONE;
301 }
302
303 static void
304 render_free_picture(d_window_t *w, window_data_t *wd)
305 {
306     /* this might cause an error, oh well */
307     if (wd->picture) {
308         xcb_render_free_picture(w->sc->dpy->conn, wd->picture);
309         wd->picture = XCB_NONE;
310     }
311 }
312
313 static void
314 render_update_shadow_picture(d_window_t *w, data_t *d, window_data_t *wd)
315 {
316     if (wd->shadow_picture)
317         xcb_render_free_picture(w->sc->dpy->conn, wd->shadow_picture);
318     wd->shadow_picture = solid_picture(d, w->sc,
319                                        d->shadowalpha *
320                                        window_get_opacity(w) / 0xffff,
321                                        0, 0, 0);
322 }
323
324 static void
325 render_update_root_picture(d_screen_t *sc, data_t *d)
326 {
327     xcb_pixmap_t px;
328
329     px = screen_get_root_pixmap(sc);
330     if (px) {
331         d->root_picture = xcb_generate_id(sc->dpy->conn);
332         xcb_render_create_picture(sc->dpy->conn,
333                                   d->root_picture, px,
334                                   d->root_format, 0, NULL);
335     }
336 }
337
338 static void
339 render_update_picture(d_window_t *w, data_t *d, window_data_t *wd)
340 {
341     xcb_pixmap_t px;
342
343     px = window_get_pixmap(w);
344     //printf("got pixmap 0x%x\n", px);
345     if (px) {
346         xcb_render_pictformat_t format;
347         const uint32_t vals = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS;
348
349         render_free_picture(w, wd);
350
351         wd->picture = xcb_generate_id(w->sc->dpy->conn);
352         format = find_visual_format(d, window_get_visual(w));
353         /* we don't need to check this.  if it fails, we'll just be drawing
354            an invalid picture and creating some X errors but that's no big
355            deal really */
356         xcb_render_create_picture(w->sc->dpy->conn,
357                                   wd->picture, px, format,
358                                   XCB_RENDER_CP_SUBWINDOW_MODE,
359                                   &vals);
360     }
361 }
362
363 static void
364 render_window_resize(d_window_t *w)
365 {
366     data_t *d;
367     window_data_t *wd;
368
369     d = screen_find_plugin_data(w->sc, plugin_id);
370     wd = window_find_plugin_data(w, plugin_id);
371
372     /* pass it on */
373     d->window_resize(w);
374
375     assert(wd != NULL);
376     render_free_picture(w, wd);
377 }
378
379 static void
380 render_window_opacity_change(d_window_t *w)
381 {
382     data_t *d;
383     window_data_t *wd;
384
385     d = screen_find_plugin_data(w->sc, plugin_id);
386     wd = window_find_plugin_data(w, plugin_id);
387
388     /* pass it on */
389     d->window_opacity_change(w);
390
391     assert(wd != NULL);
392     render_update_shadow_picture(w, d, wd);
393 }
394
395 static void
396 render_root_pixmap_change(d_screen_t *sc)
397 {
398     data_t *d;
399
400     d = screen_find_plugin_data(sc, plugin_id);
401     if (d->root_picture) {
402         xcb_render_free_picture(sc->dpy->conn, d->root_picture);
403         d->root_picture = XCB_NONE;
404     }
405
406     /* pass it on */
407     d->screen_root_pixmap_change(sc);
408 }
409
410 static void
411 render_paint(d_screen_t *sc)
412 {
413     data_t *d = screen_find_plugin_data(sc, plugin_id);
414     d_list_it_t *it;
415
416     xcb_xfixes_copy_region(sc->dpy->conn, d->all_region, d->paint_region);
417
418     //printf("-- painting --\n");
419     for (it = list_top(sc->stacking); it; it = it->next) {
420         d_window_t *w = it->data;
421         int x, y, width, height, bwidth;
422
423         window_get_area(w, &x, &y, &width, &height, &bwidth);
424
425         if (!window_is_input_only(w) &&
426             (window_is_mapped(w) || window_is_zombie(w)) &&
427             x < sc->super.width_in_pixels &&
428             y < sc->super.height_in_pixels &&
429             x + width > 0 && y + height > 0)
430         {
431             gboolean opaque = !(window_is_argb(w) ||
432                                 window_get_opacity(w) < 0xffff);
433             window_data_t *wd;
434
435             wd = window_find_plugin_data(w, plugin_id);
436
437             if (opaque) {
438                 paint_window(w, d, wd, opaque, x, y, width, height, bwidth);
439
440                 /* remove this window from the paint region, as nothing is
441                    above it, so nothing should draw to this space again */
442                 xcb_xfixes_subtract_region(sc->dpy->conn, d->paint_region,
443                                            window_get_region(w),
444                                            d->paint_region);
445                 xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
446                                                    d->overlay_buffer,
447                                                    d->paint_region,
448                                                    0, 0);
449             }
450
451             /* save the clip region, when drawing windows (and shadows)
452                below this window, they should use this clip region */
453             xcb_xfixes_copy_region(sc->dpy->conn, d->paint_region,
454                                    wd->paint_clip);
455         }
456     }
457
458     paint_root(sc, d);
459
460     xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
461                                        d->overlay_buffer,
462                                        d->paint_region,
463                                        0, 0);
464
465     for (it = list_bottom(sc->stacking); it; it = it->prev) {
466         d_window_t *w = it->data;
467         int x, y, width, height, bwidth;
468
469         window_get_area(w, &x, &y, &width, &height, &bwidth);
470
471         if (!window_is_input_only(w) &&
472             (window_is_mapped(w) || window_is_zombie(w)) &&
473             x < sc->super.width_in_pixels &&
474             y < sc->super.height_in_pixels &&
475             (x + width > 0 || x + width + d->xshadowoff > 0) &&
476             (y + height > 0 || y + height + d->yshadowoff > 0))
477         {
478             window_data_t *wd;
479             gboolean opaque = !(window_is_argb(w) ||
480                                 window_get_opacity(w) < 0xffff);
481
482             wd = window_find_plugin_data(w, plugin_id);
483
484             /* shape the shadow to the window */
485             xcb_xfixes_copy_region(sc->dpy->conn, window_get_region(w),
486                                    d->shadow_region);
487             xcb_xfixes_translate_region(sc->dpy->conn, d->shadow_region,
488                                         d->xshadowoff, d->yshadowoff);
489             xcb_xfixes_intersect_region(sc->dpy->conn,
490                                         wd->paint_clip, d->shadow_region,
491                                         d->shadow_region);
492             xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
493                                                d->overlay_buffer,
494                                                d->shadow_region,
495                                                0, 0);
496             paint_shadow(w, d, wd, x, y, width, height, bwidth);
497
498             if (!opaque) {
499                 /* use the clip region of the highest opaque window seen so
500                    far, as nothing should be able to draw on top of that region
501                 */
502                 xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
503                                                    d->overlay_buffer,
504                                                    wd->paint_clip,
505                                                    0, 0);
506                 paint_window(w, d, wd, opaque, x, y, width, height, bwidth);
507             }
508         }
509     }
510
511     xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
512                                        d->overlay_buffer,
513                                        d->all_region,
514                                        0, 0);
515
516     /* copy the double buffer to the overlay window */
517     xcb_render_composite(sc->dpy->conn,
518                          XCB_RENDER_PICT_OP_SRC,
519                          d->overlay_buffer,
520                          XCB_NONE,
521                          d->overlay_picture,
522                          0, 0, 0, 0,
523                          0, 0,
524                          sc->super.width_in_pixels,
525                          sc->super.height_in_pixels);
526
527     /* call the function we replaced in the chain */
528     d->screen_paint(sc);
529 }
530
531 static void
532 paint_root(d_screen_t *sc, data_t *d)
533 {
534     xcb_render_picture_t src;
535     int op;
536
537     if (!d->root_picture)
538         render_update_root_picture(sc, d);
539
540     if (d->root_picture) {
541         src = d->root_picture;
542         op = XCB_RENDER_PICT_OP_SRC;
543     }
544     else {
545         src = d->solid_bg;
546         op = XCB_RENDER_PICT_OP_CLEAR;
547     }
548
549     xcb_render_composite(sc->dpy->conn,
550                          op,
551                          src,
552                          XCB_NONE,
553                          d->overlay_buffer,
554                          0, 0, 0, 0,
555                          0, 0,
556                          sc->super.width_in_pixels,
557                          sc->super.height_in_pixels);
558 }
559
560 static void
561 paint_window(d_window_t *w, data_t *d, window_data_t *wd, gboolean opaque,
562              int x, int y, int width, int height, int bwidth)
563 {
564     xcb_render_picture_t alphamap;
565
566     if (!wd->picture)
567         render_update_picture(w, d, wd);
568
569     //printf("-- paint window 0x%x picture 0x%x --\n", w->id, wd->picture);
570     if (wd->picture) {
571         int op;
572
573         op = !opaque ?
574             XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC;
575
576         if (opaque)
577             alphamap = XCB_NONE;
578         else
579             alphamap = solid_picture(d, w->sc, window_get_opacity(w), 0, 0, 0);
580
581         xcb_render_composite(w->sc->dpy->conn,
582                              op,
583                              wd->picture,
584                              alphamap,
585                              d->overlay_buffer,
586                              0, 0, 0, 0,
587                              x, y, width + bwidth*2, height + bwidth *2);
588     }
589 }
590
591 static void
592 paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
593              int x, int y, int width, int height, int bwidth)
594 {
595     xcb_render_composite(w->sc->dpy->conn,
596                          XCB_RENDER_PICT_OP_OVER,
597                          wd->shadow_picture,
598                          wd->picture,
599                          d->overlay_buffer,
600                          0, 0, 0, 0,
601                          x+d->xshadowoff, y+d->yshadowoff,
602                          width + bwidth*2, height + bwidth *2);
603
604 }