make the render code use the window's opacity
[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_hide)(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_hide(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_hide = sc->window_hide;
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_hide = render_window_hide;
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 void
154 render_window_free(d_window_t *w, window_data_t *wd)
155 {
156     render_free_picture(w, wd);
157     if (wd->shadow_picture) {
158         xcb_render_free_picture(w->sc->dpy->conn, wd->shadow_picture);
159         wd->shadow_picture = XCB_NONE;
160     }
161     free(wd);
162 }
163
164 static void
165 render_window_show(d_window_t *w)
166 {
167     data_t *d;
168     window_data_t *wd;
169     xcb_rectangle_t rect;
170
171     d = screen_find_plugin_data(w->sc, plugin_id);
172
173     /* pass it on */
174     d->window_show(w);
175
176     wd = window_find_plugin_data(w, plugin_id);
177     if (wd)
178         render_window_free(w, wd);
179    
180     wd = malloc(sizeof(window_data_t));
181     wd->picture = XCB_NONE;
182
183     wd->paint_clip = xcb_generate_id(w->sc->dpy->conn);
184     rect.x = rect.y = 0;
185     rect.width = rect.height = 1;
186     xcb_xfixes_create_region(w->sc->dpy->conn, wd->paint_clip, 1, &rect);
187
188     render_update_shadow_picture(w, d, wd);
189
190     window_add_plugin_data(w, plugin_id, wd);
191
192     window_ref(w);
193 }
194
195 static void
196 render_window_hide(d_window_t *w)
197 {
198     data_t *d;
199     window_data_t *wd;
200
201     d = screen_find_plugin_data(w->sc, plugin_id);
202     wd = window_find_plugin_data(w, plugin_id);
203     if (wd) {
204         render_window_free(w, wd);
205         xcb_xfixes_destroy_region(w->sc->dpy->conn, wd->paint_clip);
206         window_remove_plugin_data(w, plugin_id);
207     }
208
209     /* pass it on */
210     d->window_hide(w);
211 }
212
213 static xcb_render_picture_t
214 solid_picture(data_t *d, d_screen_t *sc, uint16_t a, uint16_t r,
215               uint16_t g, uint16_t b)
216 {
217     xcb_pixmap_t pixmap;
218     xcb_render_picture_t picture;
219     xcb_render_color_t   c;
220     const uint32_t vals = XCB_RENDER_REPEAT_NORMAL;
221     const xcb_rectangle_t rect = { 0, 0, 1, 1 };
222
223     pixmap = xcb_generate_id(sc->dpy->conn);
224     picture = xcb_generate_id(sc->dpy->conn);
225
226     xcb_create_pixmap(sc->dpy->conn, 32, pixmap, sc->super.root, 1, 1);
227     xcb_render_create_picture(sc->dpy->conn, picture, pixmap, d->argb32_format,
228                               XCB_RENDER_CP_REPEAT, &vals);
229
230     c.alpha = a;
231     c.red   = r;
232     c.green = g;
233     c.blue  = b;
234
235     xcb_render_fill_rectangles(sc->dpy->conn, XCB_RENDER_PICT_OP_SRC,
236                                picture, c, 1, &rect);
237     xcb_free_pixmap(sc->dpy->conn, pixmap);
238
239     return picture;
240 }
241
242 static xcb_render_pictformat_t
243 find_argb32_format(data_t *d)
244 {
245     xcb_render_pictforminfo_iterator_t it;
246
247     for (it = xcb_render_query_pict_formats_formats_iterator(d->pict_formats);
248          it.rem; xcb_render_pictforminfo_next(&it))
249     {
250         xcb_render_pictforminfo_t *format = it.data;
251         if (format->type == XCB_RENDER_PICT_TYPE_DIRECT) {
252             if (format->depth              == 32   &&
253                 format->direct.alpha_mask  == 0xff &&
254                 format->direct.red_mask    == 0xff &&
255                 format->direct.green_mask  == 0xff &&
256                 format->direct.blue_mask   == 0xff &&
257                 format->direct.alpha_shift == 24   &&
258                 format->direct.red_shift   == 16   &&
259                 format->direct.green_shift == 8    &&
260                 format->direct.blue_shift  == 0)
261                 return format->id;
262         }
263     }
264     return XCB_NONE;
265 }
266
267 static xcb_render_pictformat_t
268 find_visual_format(data_t *d, xcb_visualid_t visual)
269 {
270     xcb_render_pictscreen_iterator_t si;
271     xcb_render_pictdepth_iterator_t di;
272     xcb_render_pictvisual_iterator_t vi;
273
274     if (!visual) return XCB_NONE;
275
276     /* go through all the screens */
277     si = xcb_render_query_pict_formats_screens_iterator(d->pict_formats);
278     for (; si.rem; xcb_render_pictscreen_next(&si)) {
279         di = xcb_render_pictscreen_depths_iterator(si.data);
280         for (; di.rem; xcb_render_pictdepth_next(&di)) {
281             vi = xcb_render_pictdepth_visuals_iterator(di.data);
282             for (; vi.rem; xcb_render_pictvisual_next(&vi)) {
283                 if (vi.data->visual == visual)
284                     return vi.data->format;
285             }
286         }
287     }
288     return XCB_NONE;
289 }
290
291 static void
292 render_free_picture(d_window_t *w, window_data_t *wd)
293 {
294     /* this might cause an error, oh well */
295     if (wd->picture) {
296         xcb_render_free_picture(w->sc->dpy->conn, wd->picture);
297         wd->picture = XCB_NONE;
298     }
299 }
300
301 static void
302 render_update_shadow_picture(d_window_t *w, data_t *d, window_data_t *wd)
303 {
304     if (wd->shadow_picture)
305         xcb_render_free_picture(w->sc->dpy->conn, wd->shadow_picture);
306     wd->shadow_picture = solid_picture(d, w->sc,
307                                        d->shadowalpha *
308                                        window_get_opacity(w) / 0xffff,
309                                        0, 0, 0);
310 }
311
312 static void
313 render_update_root_picture(d_screen_t *sc, data_t *d)
314 {
315     xcb_pixmap_t px;
316
317     px = screen_get_root_pixmap(sc);
318     if (px) {
319         d->root_picture = xcb_generate_id(sc->dpy->conn);
320         xcb_render_create_picture(sc->dpy->conn,
321                                   d->root_picture, px,
322                                   d->root_format, 0, NULL);
323     }
324 }
325
326 static void
327 render_update_picture(d_window_t *w, data_t *d, window_data_t *wd)
328 {
329     xcb_pixmap_t px;
330
331     px = window_get_pixmap(w);
332     //printf("got pixmap 0x%x\n", px);
333     if (px) {
334         xcb_render_pictformat_t format;
335         const uint32_t vals = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS;
336
337         render_free_picture(w, wd);
338
339         wd->picture = xcb_generate_id(w->sc->dpy->conn);
340         format = find_visual_format(d, window_get_visual(w));
341         /* we don't need to check this.  if it fails, we'll just be drawing
342            an invalid picture and creating some X errors but that's no big
343            deal really */
344         xcb_render_create_picture(w->sc->dpy->conn,
345                                   wd->picture, px, format,
346                                   XCB_RENDER_CP_SUBWINDOW_MODE,
347                                   &vals);
348     }
349 }
350
351 static void
352 render_window_resize(d_window_t *w)
353 {
354     data_t *d;
355     window_data_t *wd;
356
357     d = screen_find_plugin_data(w->sc, plugin_id);
358     wd = window_find_plugin_data(w, plugin_id);
359
360     /* pass it on */
361     d->window_resize(w);
362
363     assert(wd != NULL);
364     render_free_picture(w, wd);
365 }
366
367 static void
368 render_window_opacity_change(d_window_t *w)
369 {
370     data_t *d;
371     window_data_t *wd;
372
373     d = screen_find_plugin_data(w->sc, plugin_id);
374     wd = window_find_plugin_data(w, plugin_id);
375
376     /* pass it on */
377     d->window_opacity_change(w);
378
379     assert(wd != NULL);
380     render_update_shadow_picture(w, d, wd);
381 }
382
383 static void
384 render_root_pixmap_change(d_screen_t *sc)
385 {
386     data_t *d;
387
388     d = screen_find_plugin_data(sc, plugin_id);
389     if (d->root_picture) {
390         xcb_render_free_picture(sc->dpy->conn, d->root_picture);
391         d->root_picture = XCB_NONE;
392     }
393
394     /* pass it on */
395     d->screen_root_pixmap_change(sc);
396 }
397
398 static void
399 render_paint(d_screen_t *sc)
400 {
401     data_t *d = screen_find_plugin_data(sc, plugin_id);
402     d_list_it_t *it;
403
404     xcb_xfixes_copy_region(sc->dpy->conn, d->all_region, d->paint_region);
405
406     //printf("-- painting --\n");
407     for (it = list_top(sc->stacking); it; it = it->next) {
408         d_window_t *w = it->data;
409         int x, y, width, height, bwidth;
410
411         window_get_area(w, &x, &y, &width, &height, &bwidth);
412
413         if (!window_is_input_only(w) && window_is_mapped(w) &&
414             x < sc->super.width_in_pixels &&
415             y < sc->super.height_in_pixels &&
416             x + width > 0 && y + height > 0)
417         {
418             gboolean opaque = !(window_is_argb(w) ||
419                                 window_get_opacity(w) < 0xffff);
420             window_data_t *wd;
421
422             wd = window_find_plugin_data(w, plugin_id);
423
424             if (opaque) {
425                 paint_window(w, d, wd, opaque, x, y, width, height, bwidth);
426
427                 /* remove this window from the paint region, as nothing is
428                    above it, so nothing should draw to this space again */
429                 xcb_xfixes_subtract_region(sc->dpy->conn, d->paint_region,
430                                            window_get_region(w),
431                                            d->paint_region);
432                 xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
433                                                    d->overlay_buffer,
434                                                    d->paint_region,
435                                                    0, 0);
436             }
437
438             /* save the clip region, when drawing windows (and shadows)
439                below this window, they should use this clip region */
440             xcb_xfixes_copy_region(sc->dpy->conn, d->paint_region,
441                                    wd->paint_clip);
442         }
443     }
444
445     paint_root(sc, d);
446
447     xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
448                                        d->overlay_buffer,
449                                        d->paint_region,
450                                        0, 0);
451
452     for (it = list_bottom(sc->stacking); it; it = it->prev) {
453         d_window_t *w = it->data;
454         int x, y, width, height, bwidth;
455
456         window_get_area(w, &x, &y, &width, &height, &bwidth);
457
458         if (!window_is_input_only(w) && window_is_mapped(w) &&
459             x < sc->super.width_in_pixels &&
460             y < sc->super.height_in_pixels &&
461             (x + width > 0 || x + width + d->xshadowoff > 0) &&
462             (y + height > 0 || y + height + d->yshadowoff > 0))
463         {
464             window_data_t *wd;
465             gboolean opaque = !(window_is_argb(w) ||
466                                 window_get_opacity(w) < 0xffff);
467
468             wd = window_find_plugin_data(w, plugin_id);
469
470             /* shape the shadow to the window */
471             xcb_xfixes_copy_region(sc->dpy->conn, window_get_region(w),
472                                    d->shadow_region);
473             xcb_xfixes_translate_region(sc->dpy->conn, d->shadow_region,
474                                         d->xshadowoff, d->yshadowoff);
475             xcb_xfixes_intersect_region(sc->dpy->conn,
476                                         wd->paint_clip, d->shadow_region,
477                                         d->shadow_region);
478             xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
479                                                d->overlay_buffer,
480                                                d->shadow_region,
481                                                0, 0);
482             paint_shadow(w, d, wd, x, y, width, height, bwidth);
483
484             if (!opaque) {
485                 /* use the clip region of the highest opaque window seen so
486                    far, as nothing should be able to draw on top of that region
487                 */
488                 xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
489                                                    d->overlay_buffer,
490                                                    wd->paint_clip,
491                                                    0, 0);
492                 paint_window(w, d, wd, opaque, x, y, width, height, bwidth);
493             }
494         }
495     }
496
497     xcb_xfixes_set_picture_clip_region(sc->dpy->conn,
498                                        d->overlay_buffer,
499                                        d->all_region,
500                                        0, 0);
501
502     /* copy the double buffer to the overlay window */
503     xcb_render_composite(sc->dpy->conn,
504                          XCB_RENDER_PICT_OP_SRC,
505                          d->overlay_buffer,
506                          XCB_NONE,
507                          d->overlay_picture,
508                          0, 0, 0, 0,
509                          0, 0,
510                          sc->super.width_in_pixels,
511                          sc->super.height_in_pixels);
512
513     /* call the function we replaced in the chain */
514     d->screen_paint(sc);
515 }
516
517 static void
518 paint_root(d_screen_t *sc, data_t *d)
519 {
520     xcb_render_picture_t src;
521     int op;
522
523     if (!d->root_picture)
524         render_update_root_picture(sc, d);
525
526     if (d->root_picture) {
527         src = d->root_picture;
528         op = XCB_RENDER_PICT_OP_SRC;
529     }
530     else {
531         src = d->solid_bg;
532         op = XCB_RENDER_PICT_OP_CLEAR;
533     }
534
535     xcb_render_composite(sc->dpy->conn,
536                          op,
537                          src,
538                          XCB_NONE,
539                          d->overlay_buffer,
540                          0, 0, 0, 0,
541                          0, 0,
542                          sc->super.width_in_pixels,
543                          sc->super.height_in_pixels);
544 }
545
546 static void
547 paint_window(d_window_t *w, data_t *d, window_data_t *wd, gboolean opaque,
548              int x, int y, int width, int height, int bwidth)
549 {
550     xcb_render_picture_t alphamap;
551
552     if (!wd->picture)
553         render_update_picture(w, d, wd);
554
555     //printf("-- paint window 0x%x picture 0x%x --\n", w->id, wd->picture);
556     if (wd->picture) {
557         int op;
558
559         op = !opaque ?
560             XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC;
561
562         if (opaque)
563             alphamap = XCB_NONE;
564         else
565             alphamap = solid_picture(d, w->sc, window_get_opacity(w), 0, 0, 0);
566
567         xcb_render_composite(w->sc->dpy->conn,
568                              op,
569                              wd->picture,
570                              alphamap,
571                              d->overlay_buffer,
572                              0, 0, 0, 0,
573                              x, y, width + bwidth*2, height + bwidth *2);
574     }
575 }
576
577 static void
578 paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
579              int x, int y, int width, int height, int bwidth)
580 {
581     xcb_render_composite(w->sc->dpy->conn,
582                          XCB_RENDER_PICT_OP_OVER,
583                          wd->shadow_picture,
584                          wd->picture,
585                          d->overlay_buffer,
586                          0, 0, 0, 0,
587                          x+d->xshadowoff, y+d->yshadowoff,
588                          width + bwidth*2, height + bwidth *2);
589
590 }