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